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>.jldiscover(..., 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) -> StringScan 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)RepliBuild.build — Function
build(toml_path="replibuild.toml"; clean=false)Compile C++ project → library (.so/.dylib/.dll)
What it does:
- Compiles your C++ code to LLVM IR
- Links and optimizes IR
- Generates library file
- 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")RepliBuild.wrap — Function
wrap(toml_path="replibuild.toml"; headers=String[])Generate Julia wrapper from compiled library
What it does:
- Loads metadata from build (DWARF + symbols)
- Generates Julia module with ccall wrappers
- Creates type definitions from C++ structs
- With
[compile] aot_thunks = true(C++), compiles the thunks the wrapper binds intolib<name>_thunks.<ext>beside the library - 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
builddoes 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"])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")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")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 registerREPLIBUILD_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) -> RegistryEntryRecord 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;usestill 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.
RepliBuild.use — Function
use(name::String; force_rebuild=false, verbose=true) -> ModuleLoad 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].nameRepliBuild.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 keywordRepliBuild.list_registry — Function
list_registry()Print all registered packages in the global RepliBuild registry.
RepliBuild.unregister — Function
unregister(name::String)Remove a package from the global registry.
RepliBuild.scaffold_package — Function
scaffold_package(name::String; path::String=".") -> StringGenerate 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")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) -> StringEXPERIMENTAL. 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-3ccallonly, 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 itsextern "C"surface; the generated wrapper for the C++ API proper is unusable. If the library ships a C API variant, ingest that withlanguage = :cinstead.
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/.dllheaders: header search dirs for type extraction (recommended)extra_link_libs: additional-llibraries the wrapper needs at load timename: project name (default: derived from library basename)project_dir: where to write replibuild.toml (default: cwd)language::cor:cpp— drives wrapper generator selectionbuild: also runbuild()after scaffoldingwrap: also runwrap()(requiresbuild=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)Environment
RepliBuild.check_environment — Function
check_environment(; verbose=true, throw_on_error=false) -> ToolchainStatusRun 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 availablestatus.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.
| Name | What it answers |
|---|---|
struct_size(name) | Byte size of a wrapped struct |
member_offset(name, member) | Byte offset of a member |
LIBRARY_PATH | The library the wrapper calls (the copy beside the wrapper first, else the build-time path) |
LIBRARY_SHA / BUILD_TARGET / BUILD_GENERATOR | SHA-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_TIER | What 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.