API Reference

The public API is the verbs below. Everything else — compiler, generators, DWARF parser, MLIR bindings — is internal. Contributors: Developer.

Every entry point takes a path to a replibuild.toml (or a project directory that contains one) and is idempotent: unchanged inputs hit the content-hash cache and return.

Build lifecycle

toml = RepliBuild.discover("path/to/project")   # scan → replibuild.toml
RepliBuild.build(toml)                           # clang → .so
RepliBuild.wrap(toml)                            # .so → julia/<Module>.jl

discover(..., build=true, wrap=true) runs the whole pipeline; on a project that already has a replibuild.toml it keeps that file and runs build and wrap. discover(force=true) regenerates the config but preserves the hand-edited keys listed in Edit the TOML. build(clean=true) deletes build/, julia/ and .replibuild_cache/ first, then builds from nothing.

RepliBuild.discover — Function
discover(target_dir="."; force=false, build=false, wrap=false) -> String

Scan a C or C++ project and generate its replibuild.toml.

This is the entry point for new projects. Run this first to set up RepliBuild.

An existing replibuild.toml is kept, not rescanned, unless force=true; build and wrap still run against it. With build=true, wrap=true it builds and wraps the project where it lives, then registers it (register): the build is vendored system-wide and added to the Julia environment.

Arguments

  • target_dir: Project directory to scan (default: current directory)
  • force: Force rediscovery even if replibuild.toml exists (default: false)
  • build: Automatically run build() after discovery (default: false)
  • wrap: Automatically run wrap() after build (requires build=true, default: false)

Returns

Path to the replibuild.toml file

Workflow

Basic workflow (step-by-step):

# 1. Discover and create config
toml_path = RepliBuild.discover()

# 2. Build the library
RepliBuild.build(toml_path)

# 3. Generate Julia wrappers
RepliBuild.wrap(toml_path)

Chained workflow (automated):

# Discover → Build → Wrap (all at once)
toml_path = RepliBuild.discover(build=true, wrap=true)

# Or just discover and build
toml_path = RepliBuild.discover(build=true)

Examples

# Discover current directory
RepliBuild.discover()

# Discover another directory
RepliBuild.discover("path/to/cpp/project")

# Force regenerate config
RepliBuild.discover(force=true)

# Full automated pipeline
RepliBuild.discover(build=true, wrap=true)
source
RepliBuild.build — Function
build(toml_path="replibuild.toml"; clean=false)

Compile C++ project → library (.so/.dylib/.dll)

What it does:

  1. Compiles your C++ code to LLVM IR
  2. Links and optimizes IR
  3. Generates library file
  4. Extracts metadata (DWARF + symbols) for wrapping

What it does NOT do:

  • Does NOT generate Julia wrappers (use wrap() for that)

Arguments

  • toml_path: Path to replibuild.toml configuration file (default: "replibuild.toml")
  • clean: Clean before building (default: false)

Returns

Library path (String)

Examples

# Build using replibuild.toml in current directory
RepliBuild.build()

# Build with specific config file
RepliBuild.build("path/to/replibuild.toml")

# Clean build
RepliBuild.build(clean=true)

# Then generate Julia wrappers:
RepliBuild.wrap("replibuild.toml")
source
RepliBuild.wrap — Function
wrap(toml_path="replibuild.toml"; headers=String[])

Generate Julia wrapper from compiled library

What it does:

  1. Loads metadata from build (DWARF + symbols)
  2. Generates Julia module with ccall wrappers
  3. Creates type definitions from C++ structs
  4. With [compile] aot_thunks = true (C++), compiles the thunks the wrapper binds into lib<name>_thunks.<ext> beside the library
  5. Saves to julia/ directory

Requirements:

  • Must run build() first
  • Metadata must exist in julia/compilation_metadata.json
  • AOT thunks need the JLCS dialect library and clang++, as build does for C++

Arguments

  • toml_path: Path to replibuild.toml configuration file (default: "replibuild.toml")
  • headers: C++ headers for advanced wrapping (optional)

Returns

Path to generated Julia wrapper file

Examples

# Generate wrapper using replibuild.toml in current directory
RepliBuild.wrap()

# Generate wrapper with specific config file
RepliBuild.wrap("path/to/replibuild.toml")

# With headers for better type info
RepliBuild.wrap("replibuild.toml", headers=["mylib.h"])
source
RepliBuild.clean — Function
clean(toml_path="replibuild.toml")

Remove build artifacts (build/, julia/, caches)

Arguments

  • toml_path: Path to replibuild.toml configuration file (default: "replibuild.toml")

Examples

# Clean using replibuild.toml in current directory
RepliBuild.clean()

# Clean specific project
RepliBuild.clean("path/to/replibuild.toml")
source
RepliBuild.info — Function
info(toml_path="replibuild.toml")

Show project status (config, library, wrapper)

Arguments

  • toml_path: Path to replibuild.toml configuration file (default: "replibuild.toml")

Examples

# Show info for current directory
RepliBuild.info()

# Show info for specific project
RepliBuild.info("path/to/replibuild.toml")
source

Registry

A project builds where it lives. register builds and wraps it in place, vendors the build to ~/.replibuild/builds/<hash>/, and adds a Julia package around it to a Julia environment (env, default the @v#.# environment). use("name") loads the vendored build, or rebuilds the project in place after an edit. A fresh install has an empty registry — there is no Hub fallback.

RepliBuild.register("replibuild.toml")   # also: env=:active, env="path", env=nothing, build=false
RepliBuild.use("myproject")              # name from [project].name
using MyProject                          # from the Julia environment register added it to
RepliBuild.list_registry()
RepliBuild.search("xml")                 # browse Hub TOML names; does not register

REPLIBUILD_HOME relocates ~/.replibuild/. REPLIBUILD_JULIA_ENV (default, active, none, or a path) changes where register adds packages. REPLIBUILD_HUB_URL points search at a private index mirror. The vendored build's hash includes RepliBuild's version, so after an upgrade use wraps each registered package again, once; a fix in what build extracts also needs use(name; force_rebuild=true). See Registry.

RepliBuild.register — Function
register(toml_path::String; name="", build=true, env=:default) -> RegistryEntry

Record a project in the global registry and vendor its build, so it loads in any session by RepliBuild.use(name) and by using <Module>.

The project builds where it lives: register runs build and wrap on it (a cache hit when its build is current), copies the result to ~/.replibuild/builds/<hash>/, writes a Julia package around it in ~/.replibuild/packages/<Module>/, and adds that package to the Julia environment env:

  • :default — the @v#.# environment, which every session can load from;
  • :active — the active environment;
  • a path to an environment;
  • nothing — no environment; use still loads it.

ENV["REPLIBUILD_JULIA_ENV"] (default, active, none, or a path) changes the default. A package's own Project.toml is never changed. build=false records the project only, and use(name) builds it later. Name is inferred from [project].name if not provided. discover(path; build=true, wrap=true) ends by calling this.

source
RepliBuild.use — Function
use(name::String; force_rebuild=false, verbose=true) -> Module

Load a wrapper by local registry name. Resolves dependencies, checks environment, builds if needed, and returns the loaded Julia module.

The name must already be registered (register(toml), or discover(path; build=true, wrap=true)). There is no Hub fallback — a missing name errors.

use loads the build register vendored while the project's inputs still match it. After an edit it builds and wraps the project where it lives, vendors the new build, and loads that; a project that has moved still loads from its last vendored build. force_rebuild=true builds the project from nothing first (git dependencies are fetched again).

Call it at top level: the module is defined while your code runs, so calling into it from the function that called use needs Base.invokelatest.

Example

RepliBuild.register("path/to/replibuild.toml")   # builds in place, vendors, adds to the Julia env
M = RepliBuild.use("myproject")                  # [project].name
source
RepliBuild.search — Function
search(query::String="")

Browse the RepliBuild Hub catalog by name, description, tags, or language. Does not register or install anything — use only sees the local registry.

RepliBuild.search()           # list catalog names
RepliBuild.search("json")     # filter by keyword
source
RepliBuild.scaffold_package — Function
scaffold_package(name::String; path::String=".") -> String

Generate a standardized Julia package for distributing RepliBuild wrappers.

Creates a complete package with Project.toml, replibuild.toml, source stub, deps/build.jl hook, and test skeleton. Edit the replibuild.toml to point at your C/C++ source, then Pkg.build() compiles and wraps automatically.

Example

RepliBuild.scaffold_package("MyEigenWrapper")
source

Ingest (experimental, C only)

For C libraries whose build system the source pipeline cannot reproduce. Build the .so yourself with -g. C++ API surfaces are not supported.

toml = RepliBuild.ingest("/path/to/libfoo.so",
                         headers=["/path/to/include"],
                         name="foo", language=:c,
                         build=true, wrap=true)
RepliBuild.ingest — Function
ingest(library_path; headers=String[], extra_link_libs=String[],
                     name="", project_dir=".", language=:c, build=false, wrap=false) -> String

EXPERIMENTAL. Scaffold a replibuild.toml for ingest mode: wrap a pre-built .so (built by upstream's own build system) without recompiling. RepliBuild only runs DWARF metadata extraction + wrapper generation; the library must be built with -g.

Support matrix — the maintained, flagship path is the source-build pipeline (discover/build/wrap), where RepliBuild's own version-matched compilation guarantees the DWARF it consumes:

  • language = :c — works for plain-C ABIs (Tier-3 ccall only, no bitcode/thunks), but is best-effort: upstream's compiler and debug-info settings are outside RepliBuild's control, so extraction quality varies. Prefer the source build when the sources compile under one flag set.
  • language = :cpp — NOT supported. The C++ ABI surface (classes, methods, templates, virtual dispatch) requires the MLIR dialect to marshal calls and generate thunks, which only the source-build pipeline produces. Ingesting a C++ library can at best expose its extern "C" surface; the generated wrapper for the C++ API proper is unusable. If the library ships a C API variant, ingest that with language = :c instead.

This is the fallback for C libraries with elaborate build systems (autotools, CMake with code generators, configure scripts) that RepliBuild's source-build pipeline can't reproduce.

Arguments

  • library_path: path to the pre-built .so / .dylib / .dll
  • headers: header search dirs for type extraction (recommended)
  • extra_link_libs: additional -l libraries the wrapper needs at load time
  • name: project name (default: derived from library basename)
  • project_dir: where to write replibuild.toml (default: cwd)
  • language: :c or :cpp — drives wrapper generator selection
  • build: also run build() after scaffolding
  • wrap: also run wrap() (requires build=true)

Returns

Path to the generated replibuild.toml.

Example

toml = RepliBuild.ingest("/usr/lib/libsqlite3.so",
                         headers=["/usr/include"],
                         name="sqlite_ingest",
                         build=true, wrap=true)
source

Environment

RepliBuild.check_environment — Function
check_environment(; verbose=true, throw_on_error=false) -> ToolchainStatus

Run environment diagnostics to verify LLVM 21+, MLIR, CMake, and other toolchain requirements.

Prints a colorful report showing which tools are found, their versions, and installation instructions for anything missing. Use throw_on_error=true to abort on missing requirements.

Example

status = RepliBuild.check_environment()
status.ready          # true if Tier 3 (ccall) builds will work
status.tier2_ready    # true if MLIR JIT tier is also available
source

status.ready — C builds will work. status.tier2_ready — C++ and the hard ABI path will work. See Install.

What a generated wrapper exposes

Not exported. Qualified access only. Full usage: Call a wrapper.

NameWhat it answers
struct_size(name)Byte size of a wrapped struct
member_offset(name, member)Byte offset of a member
LIBRARY_PATHThe library the wrapper calls (the copy beside the wrapper first, else the build-time path)
LIBRARY_SHA / BUILD_TARGET / BUILD_GENERATORSHA-256 of the library it was generated against, the target triple, and the RepliBuild version that wrote it
dispatch_tier(f)Which path f will actually take (:tier1 / :tier2 / :tier3, or :unknown / :mixed / :deferred)
DISPATCH_TIERWhat the generator emitted

dispatch_tier and DISPATCH_TIER exist only in a wrapper that mixes tiers or has Tier 1 kernels; a single-tier wrapper names its tier in a comment instead.