Building

This page is about calling C++ from Rust with rust_api_from_cpp. If you want to call Rust from C++, see C++ bindings for Rust libraries instead.

Using rust_api_from_cpp in your build

rust_api_from_cpp is available in Bazel, through the rules_crubit module. See Bazel for setup instructions. Bazel builds the rs_bindings_from_cc tool for you, including the LLVM libraries it depends on, so there is nothing else to install.

CMake and Cargo integrations for rust_api_from_cpp are planned

Build System Integrations for the current status of every build system.

Building rs_bindings_from_cc yourself

If you're integrating Crubit into another build system, you can build the rs_bindings_from_cc binary yourself and run it the way the Bazel rules do.

rs_bindings_from_cc is a Rust program that links against Clang's C++ libraries, plus Abseil and Protobuf. It is built against a specific llvm-project commit, pinned in MODULE.bazel and updated regularly by CI. Open-source CI covers Linux x86_64.

There are two ways to build it: with Bazel (which builds LLVM from source for you) or with Cargo against your own LLVM, Abseil, and Protobuf installation.

Bazel

This is how Crubit's CI builds the tool. Bazel downloads the Rust and Clang toolchains, and it builds LLVM from source at the commit pinned in MODULE.bazel. Because it builds LLVM, the first build takes a long time.

Prerequisites: bazelisk, python3, and the development headers for zlib, libxml2, and zstd in /usr/include. On Debian or Ubuntu, install them with apt install zlib1g-dev libxml2-dev libzstd-dev.

git clone https://github.com/google/crubit.git
cd crubit
bazelisk build //rs_bindings_from_cc:rs_bindings_from_cc_main

To build against a different llvm-project commit, set the CRUBIT_LLVM_COMMIT environment variable to the full commit SHA.

Cargo, with your own LLVM, Abseil, and Protobuf

This option is for packagers and for projects that already build LLVM, such as Chromium. CI doesn't test it.

Prerequisites

  • LLVM and Clang libraries, built and installed from the llvm-project commit pinned in MODULE.bazel (or one close to it).
    • They must be built with compression support (zlib), which is the default build config.
  • Abseil libraries, built and installed.
  • Protobuf libraries and the protoc compiler (v27.1 or later), installed. Crubit's CI uses Protobuf 36.1.
  • zlib (for example, libz.so) in the system include and lib paths.
  • A recent Rust toolchain. In a Crubit checkout, rustup automatically selects the nightly toolchain from rust-toolchain.toml.

Linux-specific setup:

# Choice of compiler is optional.
export CC=/path/to/clang
export CXX=/path/to/clang++

# We must use `lld` linker via clang. It must be in the PATH.
export PATH="$PATH:/dir/containing/lld"
export RUSTFLAGS="$RUSTFLAGS -Clinker=/path/to/clang"
export RUSTFLAGS="$RUSTFLAGS -Clink-arg=-fuse-ld=lld"

# If you want to use a sysroot.
# SYSROOT_FLAG=--sysroot=$SYSROOT
# export CXXFLAGS="$CXXFLAGS $SYSROOT_FLAG"
# export RUSTFLAGS="$RUSTFLAGS -Clink-arg=$SYSROOT_FLAG"

macOS-specific setup (untested):

export CC=clang
export CXX=clang++
export RUSTFLAGS="$RUSTFLAGS -Clinker=clang"
export RUSTFLAGS="$RUSTFLAGS -Clink-arg=-fuse-ld=lld"

# Point to the Xcode sysroot.
export CXXFLAGS="$CXXFLAGS -isysroot $(xcrun --show-sdk-path)"
export RUSTFLAGS="$RUSTFLAGS -Clink-arg=-isysroot -Clink-arg=$(xcrun --show-sdk-path)"

Windows-specific setup:

  • Windows is currently unsupported, and the APIs generated by Crubit may not compile and will change over time.
  • All commands must be run from a development shell, where MSVC environment variables are set up.
# We use clang compiler (clang-cl); MSVC may work too but is unsupported.
export CC=clang-cl
export CXX=clang-cl
# We must use lld to link, which is spelt lld-link. So user-specified linker
# flags must be in MSVC format.
export RUSTFLAGS="$RUSTFLAGS -Clinker=/path/to/lld-link"

# LLVM was built with Zlib support. Point Crubit to the same library.
export CXXFLAGS="$CXXFLAGS /I/path/to/zlib"
export RUSTFLAGS="$RUSTFLAGS -Clink-arg=/LIBPATH:/path/to/zlib"

# Avoid deprecation warnings.
export CXXFLAGS="$CXXFLAGS /D_CRT_SECURE_NO_DEPRECATE"

# If LLVM (-DCMAKE_MSVC_RUNTIME_LIBRARY) and Abseil (-DABSL_MSVC_STATIC_RUNTIME)
# are built against static CRT, then Rust needs to match, or vice-versa.
# export RUSTFLAGS="$RUSTFLAGS -Ctarget-feature=+crt-static"

Run the build step via cargo:

# Pre-generate the C++ headers for Crubit's own .proto files. This avoids
# races during the parallel Cargo build.
python3 cargo/build/generate_proto_headers.py \
    --protoc=/path/to/protoc --out_dir=/path/to/generated_headers
export PROTOC=/path/to/protoc

# Paths for Crubit's cargo to use. Most accept a comma-separated list.
## This path contains clang/ and llvm/ dirs with their respective headers.
export CLANG_INCLUDE_PATH=/path/to/llvm/and/clang/headers
## This path contains libLLVM*.a and libclang*.a.  On Windows it may also hold the
## import libraries of LLVM's DLLs (LLVM-C.lib and friends); those are recognised by
## their contents and skipped, so the whole `lib` dir can be given as-is.
export CLANG_LIB_STATIC_PATH=/path/to/llvm/and/clang/libs
## This path contains absl/ dir with all the includes.
export ABSL_INCLUDE_PATH=/path/to/absl/include/dir
## This path contains libabsl_*
export ABSL_LIB_STATIC_PATH=/path/to/absl/libs
## These paths contain google/protobuf/ dir with all the includes, and the
## headers generated above.
export PROTOBUF_INCLUDE_PATH=/path/to/protobuf/include/dir,/path/to/generated_headers
## This path contains libprotobuf.a and libutf8_validity.a.  Other archives that a
## protobuf install tree holds (libutf8_range.a, libprotobuf-lite.a, libprotoc.a,
## libupb.a) are ignored; the first two would collide with the ones above.
export PROTOBUF_LIB_STATIC_PATH=/path/to/protobuf/libs

# Tell Crubit which LLVM it is compiled against, as the commit date (YYYYMMDD) of
# your llvm-project checkout.
LLVM_DATE=$(python3 cargo/build/get_llvm_commit_date.py <llvm_commit_sha>)
export CXXFLAGS="$CXXFLAGS -DCRUBIT_LLVM_DEV_DATE=${LLVM_DATE}"

cargo build --locked --release -p rs_bindings_from_cc

Cargo build of Crubit explains each of these variables in more detail.

If you need to build LLVM and Clang yourself, here is an example:

git clone https://github.com/llvm/llvm-project
cd llvm-project
CC=clang CXX=clang++ cmake -S llvm -B build -DLLVM_ENABLE_PROJECTS='clang' -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=install
cmake --build build -j
# wait...
cmake --install build