API Reference

The public API is small: a build lifecycle (discover → build → wrap), a local registry with a community Hub (register / use / search), an experimental ingest path for pre-built binaries, and an environment check. Everything else — the compiler, wrapper generators, DWARF parser, MLIR/JLCS dialect bindings — is internal and documented under Internals.

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 immediately.

Build lifecycle

The four-stage pipeline. discover writes a replibuild.toml, build compiles source to a .so + DWARF metadata, wrap emits the Julia module, clean removes generated artifacts. info reports status without touching anything.

toml = RepliBuild.discover("path/to/project")   # scan → replibuild.toml
RepliBuild.build(toml)                           # clang → LLVM IR → .so + compilation_metadata.json
RepliBuild.wrap(toml)                            # DWARF + symbols → julia/<Module>.jl

discover accepts build=true, wrap=true to run the whole pipeline in one call. Re-running discover(force=true) regenerates the config but preserves the hand-curated keys that cannot be derived from source — [types].templates / template_headers, [wrap].varargs / macros / shim_headers / cstring_owned / tier1, and [link].promote_statics — so a forced re-scan never eats your intent. Everything else is regenerated from the scan. See what discovery cannot know for the information you are expected to supply by hand. build(clean=true) forces a full rebuild past the cache.

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

Scan C++ project and generate replibuild.toml configuration file.

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

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 generated 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.buildFunction
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.wrapFunction
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. Saves to julia/ directory

Requirements:

  • Must run build() first
  • Metadata must exist in julia/compilation_metadata.json

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.cleanFunction
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.infoFunction
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

Package registry and the Hub

register records a project in the local registry (~/.replibuild/registry/). use is the one-call "give me a loaded module": it resolves dependencies, builds (or serves the cached build from ~/.replibuild/builds/<hash>/), wraps, and returns the loaded Julia module. On a local-registry miss, use/search fall through to the RepliBuild-Hub community registry.

Lua = RepliBuild.use("lua")          # build + wrap + load (cached), Hub on miss
Lua.luaL_newstate()

RepliBuild.search("xml")             # query the Hub by name/description/tags/language
RepliBuild.list_registry()           # what's registered locally

The build-cache key includes RepliBuild's own version and git revision, so upgrading the generator rebuilds each package once with current codegen instead of serving a stale wrapper. scaffold_package turns a registered project into a distributable, Pkg.add-able Julia package. Two environment overrides: REPLIBUILD_HOME relocates the registry, REPLIBUILD_HUB_URL points Hub operations at a private mirror.

RepliBuild.registerFunction
register(toml_path::String; name="", verified=false) -> RegistryEntry

Hash and store a replibuild.toml in the global registry (~/.replibuild/registry/). Name is inferred from [project].name if not provided. Called automatically by discover().

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

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

Example

Lua = RepliBuild.use("lua")
Lua.luaL_newstate()
source
RepliBuild.searchFunction
search(query::String="")

Search the RepliBuild Hub for available packages. Matches against names, descriptions, tags, and language. Call with no arguments to list everything.

RepliBuild.search()           # list all hub packages
RepliBuild.search("json")     # filter by keyword
source
RepliBuild.scaffold_packageFunction
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 systems RepliBuild's source pipeline can't reproduce (autotools, CMake code generators, configure scripts), build the .so yourself with -g and ingest it — RepliBuild skips compilation and runs only DWARF extraction + wrapper generation. Ingested libraries dispatch through Tier 3 (ccall) exclusively; the C++ API surface of an ingested binary is not supported (classes/methods/templates/virtual dispatch need the thunks only the source build produces — at best the extern "C" surface works). Prefer the source build; reach for ingest only when you must.

toml = RepliBuild.ingest("/path/to/libfoo.so",
                         headers=["/path/to/include"],
                         name="foo", language=:c,
                         build=true, wrap=true)
RepliBuild.ingestFunction
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 = :cppNOT 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

check_environment validates both toolchain buckets — the C bucket (JLL clang + Julia's resident libLLVM, no external install) and the C++/Tier-2 bucket (system LLVM/MLIR 21+, Clang, mlir-tblgen, CMake, and libJLCS.so) — and reports which dispatch tiers are available, with OS-specific install hints for anything missing.

RepliBuild.check_environmentFunction
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

What a generated wrapper exposes about itself

Beyond the wrapped API, every generated module carries facts about its own generation. None of these are exported — reach for them qualified:

NameWhat it answers
dispatch_tier(f)Which tier f actually dispatches through now: :tier1 / :tier2 / :tier3, :unknown (not wrapped), :mixed (methods on several tiers), :deferred (asked during precompilation — ask at runtime)
DISPATCH_TIERSymbol => Symbol — the tier the generator emitted for each function
struct_size(name)Byte size of a wrapped struct, from DWARF
member_offset(name, member)Byte offset of a member within it
STRUCT_SIZES / STRUCT_OFFSETSThe tables behind those two
TIER1_FUNCTIONS / TIER1_DECLARESC only: functions on a bitcode slice, and the symbols each slice declares
BUILD_ID / BUILD_TARGET / BUILD_GENERATORIdentity of the library and generator the wrapper came from

dispatch_tier and DISPATCH_TIER disagree exactly when a Tier-1 kernel demotes (missing slices/, unresolvable declare); the function reports what will actually run, the table what was intended. dispatch_tier forces the kernel to generate, so it is not read-only and refuses to answer during precompilation.

Debugging generated thunks

RepliBuild.Debug inspects what the Tier-2 pipeline emitted for a package — including one this process never built — without running anything:

D = RepliBuild.Debug
D.thunks(pkg)                       # thunk symbols available to ask about
D.mlir_body(pkg, symbol)            # the generated dialect for one thunk
D.disassemble(pkg; symbol = s)      # objdump -dS: ops interleaved with machine code
D.dwarf(pkg; section = "line")      # the address → MLIR-line table
D.walk(pkg, symbol)                 # the common combination, one call

The disassembly path needs ENV["REPLIBUILD_JIT_OBJDUMP"] = "1" set before the wrapper loads. The live-process counterpart — breaking inside the emitted MLIR under gdb — is documented in Debugging a thunk.