RepliBuild.jl

ABI-aware C/C++ compiler bridge for Julia. Point it at source code, get type-safe Julia bindings — correct struct layouts, enum definitions, calling conventions, and virtual dispatch — without writing a single ccall by hand.

New to RepliBuild? Read Why RepliBuild for background on what problem it solves and how it compares to CxxWrap.jl, Clang.jl, and manual ccall.

Overview

RepliBuild compiles your C/C++ source with Clang, then combines multiple information sources to generate bindings that are correct by construction:

  • DWARF debug metadata — struct member offsets, sizes, function signatures, vtable layout, bitfield positions. This is the compiler's own record of what it produced — always accurate for the target platform.
  • Symbol tables (nm) — mangled C++ names and function addresses. The authoritative linking identity.
  • Clang.jl AST — enums the compiler optimized away, function pointer typedefs, macro definitions. Fills gaps where DWARF is incomplete.
  • Cross-verification — DWARF struct size is checked against Julia's alignment calculation. If they disagree, the struct is packed and gets routed to an MLIR thunk instead of ccall (which would silently misalign fields).

Functions are automatically routed to one of three calling tiers — Base.llvmcall with LTO bitcode, MLIR AOT thunks, or ccall — based on ABI complexity.

Three-tier dispatch

TierMechanismWhen selected
1Base.llvmcallPOD args, scalar/pointer return, LTO bitcode available
2MLIR AOT thunks (libJLCS.so)Packed structs, unions, large struct return, C++ virtual dispatch
3ccallFallback when bitcode is unavailable

Tier selection is automatic — the wrapper generator analyses each function signature against DWARF metadata and emits the appropriate calling convention.

Quick start

using RepliBuild

# Scan a C/C++ project, generate replibuild.toml, compile, and wrap
RepliBuild.discover("path/to/project", build=true, wrap=true)

# Load the generated module
include("path/to/project/julia/MyProject.jl")
using .MyProject

Or step by step:

toml = RepliBuild.discover("path/to/project")  # generates replibuild.toml
RepliBuild.build(toml)                          # Clang → LLVM IR → .so + DWARF metadata
RepliBuild.wrap(toml)                           # DWARF → Julia module in julia/

Package registry

RepliBuild.register("path/to/project/replibuild.toml")  # one-time registration
Lua = RepliBuild.use("lua")                              # build + wrap + load, cached
Lua.luaL_newstate()

What gets wrapped

  • Structs with correct field order, alignment padding, and topological sort for circular references
  • Enums via @enum with correct underlying types (Clang.jl AST walker)
  • Unions as NTuple{N,UInt8} with typed getter/setter accessors
  • Bitfields with bit-level extraction
  • Function pointers with DWARF signature parsing to @cfunction-compatible types
  • Variadic functions with typed overloads via [wrap.varargs] config
  • Macros with auto-generated typed shims via [wrap.macros] config
  • Multi-level pointers / referencesT**Ptr{Ptr{T}}, T&Ref{T}
  • C++ virtual methods via MLIR JIT thunks or static AOT thunks
  • Idiomatic wrappers — factory/destructor pairs → mutable struct with GC finalizers
  • Global variables via cglobal accessors
  • Templates — declare in [types].templates; RepliBuild forces DWARF emission

Example: wrapping a git dependency

# replibuild.toml
[dependencies.cjson]
type    = "git"
url     = "https://github.com/DaveGamble/cJSON"
tag     = "v1.7.18"
exclude = ["test", "fuzzing"]
RepliBuild.build("replibuild.toml")
RepliBuild.wrap("replibuild.toml")

include("julia/MyCjsonWrapper.jl")
using .MyCjsonWrapper

obj = cJSON_CreateObject()
cJSON_AddStringToObject(obj, "key", "value")

Configuration

The replibuild.toml file controls the entire build. Generated by discover(), hand-editable:

[project]
name = "MyEngine"

[compile]
flags    = ["-O3", "-std=c++17", "-fPIC"]
parallel = true

[link]
enable_lto         = false   # true → emit _lto.bc for Base.llvmcall (Tier 1)
optimization_level = "3"

[wrap]
language     = "cpp"         # "c" | "cpp" (auto-detected by discover())
use_clang_jl = true
aot_thunks   = false         # true → pre-compile MLIR thunks (Tier 2)

[types]
strictness = "warn"
templates  = ["std::vector<int>"]
template_headers = ["<vector>"]

[dependencies.mylib]
type = "git"
url  = "https://github.com/example/mylib"
tag  = "v1.0.0"

See the Configuration Reference for all available options.

Documentation