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.