cpp_api_from_rustcpp_api_from_rust (aka cc_bindings_from_rs) is a Crubit tool that takes a Rust crate as input and generates C++ APIs (a .h header) as output, enabling C++ to call Rust.
cpp_api_from_rust is fully supported by the Rust in Chrome team, with the following caveats:
Some directories cannot use Crubit: The Android project‘s Soong/bp build system does not support Crubit at this point. Consequently, Crubit cannot be used in //base, //net, or other directories that Cronet depends on. This is tracked in https://crbug.com/535682335. (Quick clarification: Chromium’s GN/ninja build system supports Crubit on all Chromium target platforms, including Android. For example, QR code generator in Chromium uses Crubit and ships to mobile and desktop targets.)
2nd-party project limitations: In principle, Crubit will work in projects like PDFium or V8 as well. However, such projects usually support non-Chromium clients or alternative toolchains that may lack Crubit support. In order to use Crubit in a 2nd-party project, that project must first:
//build_overrides/crubit.gni.Other notes:
cxx remains fully supported; there are no plans to migrate existing cxx::bridge code to Crubit.rust_api_from_cpp (calling C++ from Rust) is not yet supported, but integration work is ongoing.cpp_api_from_rust (with some Chromium support - see “availability” above) and rust_api_from_cpp (with no Chromium support at this point).lld-link: error: undefined symbol: ___crubit_thunk_foo_bar_bazcpp_api_from_rust-generated APIs cannot be called from another build component (another .so or .dll) than the one that contains the Crubit-generated source_set.cpp_api_from_rust is not supported in the host toolchain (e.g. when generating bindings for host-side build tools).cpp_api_from_rust in Chromiumcpp_api_from_rust for a rust_static_library crateExample:
// build/rust/tests/test_cpp_api_from_rust/lib.rs: pub fn mul_two_ints_via_rust(x: i32, y: i32) -> i32 { x * y }
# build/rust/tests/test_cpp_api_from_rust/BUILD.gn import("//build/rust/rust_static_library.gni") rust_static_library("rust_lib") { crate_root = "lib.rs" sources = [ crate_root ] cpp_api_from_rust = { target_name = "rust_lib_bindings" cpp_namespace = "rust_lib" } } source_set("unittests") { sources = [ "unittests.cc" ] deps = [ ":rust_lib_bindings", ] }
// build/rust/tests/test_cpp_api_from_rust/unittests.cc:
// `rust_lib` part of the `#include` path comes from the target name
// (i.e. from `rust_static_library("rust_lib")` above).
#include "build/rust/tests/test_cpp_api_from_rust/rust_lib.h"
void foo() {
auto product = rust_lib::mul_two_ints_via_rust(3, 4);
}
cpp_api_from_rust for a third_party/rust crateSet cpp_api_from_rust = true in gnrt_config.toml as follows:
[crate.qr_code.extra_kv] allow_unsafe = false cpp_api_from_rust = true
After modifying gnrt_config.toml you have to re-run tools/crates/run_gnrt.py gen to regenerate the crate's BUILD.gn file.
At this point you should be able to depend on the bindings and use them as follows:
# My BUILD.gn:
source_set("my_cpp_code") {
# ...
deps += [ "//third_party/rust/qr_code/v2:cpp_api_from_rust" ]
}
// my_cpp_code.cc
// The last `qr_code` part of the `#include` path comes from the `crate_name`
// attribute of the `//third_party/rust/qr_code/v2:lib` target.
#include "third_party/rust/qr_code/v2/qr_code.h"
void foo() {
// ...
rs_std::SliceRef<const uint8_t> rs_in(in);
auto result = ::qr_code::QrCode::new_(rs_in);
// ...
}
Let's assume that cpp_api_from_rust bindings are generated for //some/dir:some_target - e.g.:
# some/dir/BUILD.gn import("//build/rust/rust_static_library.gni") rust_static_library("some_target") { crate_root = "lib.rs" sources = [ crate_root ] cpp_api_from_rust = { target_name = "some_target_bindings" } }
The generated bindings can then be found and inspected in <out_dir>/gen/some/dir/some_target.h. For example:
$ cat out/rel/gen/build/rust/tests/test_cpp_api_from_rust/rust_lib.h | head -3 // Automatically @generated C++ bindings for the following Rust crate: // rust_lib_1dc874e1 // Features: <none>
If public APIs of a crate depend on types from another crate, then the dependency on the other crate needs to be explicitly specified in BUILD.gn.
1st-party Rust libraries can specify dependencies of their bindings as follows:
// build/rust/tests/test_cpp_api_from_rust/lib.rs: chromium::import! { "//build/rust/tests/test_cpp_api_from_rust:internal_helper"; "//build/rust/tests/test_cpp_api_from_rust:other_lib"; } pub fn create_multiplier(x: i32) -> other_lib::Multiplier { internal_helper::do_something(); other_lib::Multiplier::new(x) }
# build/rust/tests/test_cpp_api_from_rust/BUILD.gn import("//build/rust/rust_static_library.gni") rust_static_library("rust_lib") { crate_root = "lib.rs" sources = [ crate_root ] deps = [ ":other_lib", ":internal_helper", ] cpp_api_from_rust = { target_name = "rust_lib_bindings" cpp_namespace = "rust_lib" deps = [ "//some/other/lib:other_lib_bindings" ] } }
Note how other_lib_bindings are listed in deps of cpp_api_from_rust above.
Note that types from internal_helper are not used in public APIs of rust_lib and therefore internal_helper is not listed in deps attribute of cpp_api_from_rust.
//third_party/rust libraries3rd-party Rust crates can specify dependencies of their bindings with the following gnrt_config.toml entry:
[crate.my_crate_name.extra_kv]
allow_unsafe = false
cpp_api_from_rust = { deps = ["some_other_crate/v123"] }
After modifying gnrt_config.toml you have to re-run tools/crates/run_gnrt.py gen to regenerate the crate's BUILD.gn file.
C++ bindings for Rust standard library are automatically injected as a dependency of all other bindings. Therefore usually there is no need to explicitly depend on these bindings, but if needed other targets can depend on //build/rust/crubit.
C++ bindings for Rust standard library are placed in a C++ namespace that corresponds to the original Rust crate as follows:
std crate => rs_std namespacealloc crate => rs_alloc namespacecore crate => rs_core namespaceThe bindings can be #included from the following paths:
#include "third_party/crubit/support/rs_std/rs_std.h"#include "third_party/crubit/support/rs_std/rs_alloc.h"#include "third_party/crubit/support/rs_std/rs_core.h"Side-note: The auto-generated
build/rust/std/rules/BUILD.gnoverrides the include paths to make sure that Chromium can use the canonical paths (ones that are unified across other major Crubit clients). There is no actualthird_party/crubit/supportdirectory in the root of the Chromium repo.
If cpp_api_from_rust is unable to generate bindings for a given Rust API, then the generated .h file will contain a comment explaining why. The sections below describe a few errors that are somewhat related to how Chromium integrates Crubit into its build system.
--crate-header was specified for this crateIf you see an error like:
$ cat out/rel/gen/build/rust/tests/test_cpp_api_from_rust/rust_lib.h ... // Error generating bindings for `create_multiplier` defined at // ../../build/rust/tests/test_cpp_api_from_rust/lib.rs;l=22: Error formatting // function return type `other_lib::Multiplier`: Type `other_lib::Multiplier` // comes from the `other_lib_1dc874e1` crate, but no `--crate-header` was // specified for this crate ...
Then you want to read the “Specifying binding dependencies” section above.
Most Crubit users do not need to worry about this. But if the bindings are used by a widely used C++ target (for example by a component that thousands of C++ files depend on, directly or transitively), then see Crubit and build performance.