blob: 6663e9028672c9f82c74a32d6bb7b1e3f7bd9f50 [file] [view] [edit]
# CMake
CMake projects can call Rust from C++ with
[`cpp_api_from_rust`](../rust/index.md), through
[Corrosion](https://github.com/corrosion-rs/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](https://github.com/thunderseethe/corrosion) while the
[upstream pull request](https://github.com/corrosion-rs/corrosion/pull/666) is
reviewed. <!-- TODO(b/574505417): Point at upstream Corrosion once merged. -->
Rust bindings for C++ libraries (`rust_api_from_cpp`) aren't available in CMake
yet. <!-- TODO(b/539627008) --> You can build the `rs_bindings_from_cc` tool
itself with CMake, though; see
[Building with CMake](cargo_build.md#building-with-cmake).
There are two example projects, and CI builds both on every change to Crubit:
* [`examples/build_systems/cmake`](https://github.com/google/crubit/tree/main/examples/build_systems/cmake):
the minimal setup walked through below.
* [`examples/build_systems/cmake_demo`](https://github.com/google/crubit/tree/main/examples/build_systems/cmake_demo):
a slightly larger demo that uses several crates.io dependencies.
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`):
```sh
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](https://corrosion-rs.github.io/corrosion/usage.html) 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. <!-- TODO(b/574506047) -->
* `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](https://cmake.org/cmake/help/latest/guide/tutorial/Getting%20Started%20with%20CMake.html)
for help. At a minimum, you need a directory (ours is named `cmake`) with a
`CMakeLists.txt` at its root that contains:
```cmake
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](https://doc.rust-lang.org/book/ch01-03-hello-cargo.html) with this
shell command:
```sh
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:
```cmake
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`:
```cmake
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`:
```live-snippet
cs/file:examples/build_systems/cmake/main.cpp
```
Now C++ can call the Rust code. See
[C++ bindings for Rust libraries](../rust/index.md) for what the generated API
looks like. The CMake integration has the same [limitations](limits.md) as
Crubit's other build integrations.