CMake

CMake projects can call Rust from C++ with cpp_api_from_rust, through Corrosion, the standard way to combine CMake and Cargo. Corrosion builds your crate with Cargo, runs Crubit on it, and gives you a CMake target that contains the generated header and a static library with the Rust code.

Crubit's Corrosion support is in a fork of Corrosion while the upstream pull request is reviewed.

Rust bindings for C++ libraries (rust_api_from_cpp) aren't available in CMake yet.

There are two example projects, and CI builds both on every change to Crubit:

To run the minimal example, you need CMake 3.22 or later, Ninja, Clang, and rustup (it installs the Rust toolchain from Crubit's rust-toolchain.toml):

git clone https://github.com/google/crubit.git
cd crubit/examples/build_systems/cmake
cmake -B build -S . -G Ninja
cmake --build build
./build/main

Build configuration

Crubit uses Cargo to build your Rust library. Changes to your Cargo.toml are respected when the Crubit CMake targets are built. Your project needs to provide a library package, because Crubit needs it to produce an .rlib or .rmeta file.

On the CMake side, Corrosion has many options for tuning the Cargo build. See the Corrosion usage docs for details. Most of these options are passed through to the underlying Cargo build, with a few exceptions:

  • CRATES doesn‘t work, because cargo cpp_api_from_rust generates bindings for the single library package in the root manifest. Cargo workspaces aren’t supported yet.
  • CRATE_TYPES has no effect, because Crubit always imports a lib/rlib.
  • OVERRIDE_CRATE_TYPE has no effect, because Crubit must use --crate-type=lib.

The CMake target that Corrosion creates is an interface library. It contains the generated header and a static library with the Rust code and the generated FFI bindings.

Example setup

First, create a CMake project. See the CMake documentation for help. At a minimum, you need a directory (ours is named cmake) with a CMakeLists.txt at its root that contains:

cmake_minimum_required(VERSION 3.22)

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_C_COMPILER clang)
set(CMAKE_CXX_COMPILER clang++)

project(example_project
  VERSION 0.1.0
  LANGUAGES CXX)

Next, create a Cargo project with this shell command:

cmake/$ cargo new --lib rust_lib

C++ calls this code through Crubit, so the Cargo project must produce a normal Rust library. A binary, static library, or cdylib won't work.

Corrosion connects the two projects. In CMakeLists.txt, fetch Crubit and Corrosion, then turn on Crubit for the rust_lib package:

include(FetchContent)

FetchContent_Declare(
  crubit
  GIT_REPOSITORY https://github.com/google/crubit
  GIT_TAG <commit SHA>  # If omitted, Corrosion fetches Crubit's latest version.
)

FetchContent_Declare(
  Corrosion
  # NB: We temporarily require a fork of Corrosion while we upstream our changes.
  GIT_REPOSITORY https://github.com/thunderseethe/corrosion
  GIT_TAG main
)

FetchContent_MakeAvailable(Corrosion)

corrosion_experimental_crubit(rust_lib)
corrosion_import_crate(MANIFEST_PATH rust_lib/Cargo.toml)

rust_lib is the name of the library package in the Cargo project. By default, it‘s the same as the crate name. If you rename the package, update this line to use the package name, not the crate or directory name. Corrosion reads the project’s Cargo.toml to get the metadata it needs.

Corrosion creates a CMake library target named after the Rust package. C++ code uses that target to depend on the Rust code. For example, to make a CMake executable main depend on it, add the following to CMakeLists.txt:

add_executable(main main.cpp)
target_link_libraries(main PRIVATE rust_lib)

Crubit names the generated C++ header after the library package. In main.cpp, include crubit/rust_lib.h:

cs/file:examples/build_systems/cmake/main.cpp

Now C++ can call the Rust code. See C++ bindings for Rust libraries for what the generated API looks like. The CMake integration has the same limitations as Crubit's other build integrations.