cpp_api_from_rust

cpp_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.

Availability

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:

    • Make a policy decision to only support clients that have Crubit available, and/or help their clients set up Crubit support.
    • Enable Crubit in their build system by providing //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.

Other docs

  • Generic, Chromium-agnostic documentation of Crubit can be found at https://crubit.rs.
    • Note that some examples are Bazel-specific, but most of the documentation should still apply to Chromium.
    • Note that the documentation covers both cpp_api_from_rust (with some Chromium support - see “availability” above) and rust_api_from_cpp (with no Chromium support at this point).
  • Google-internal Crubit documentation can be found at go/crubit
    • This is mostly the same content as above, but is mentioned here because it includes a few extra things like document freshness and owner metadata, link to a Google-internal chatroom, etc.)
  • Crubit's Discord server can be joined using the following invite link: https://discord.gg/nHq5fdADKV
  • TODO: Cover Crubit in Chromium/FFI chapter of Comprehensive Rust course

Known issues

  • https://crbug.com/545486505: Crubit link failure: lld-link: error: undefined symbol: ___crubit_thunk_foo_bar_baz
    • cpp_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.
    • A workaround is to define and call out-of-line functions. See for example https://crbug.com/545486505#comment2
  • https://crbug.com/549864599: cpp_api_from_rust is not supported in the host toolchain (e.g. when generating bindings for host-side build tools).

Using cpp_api_from_rust in Chromium

Enabling cpp_api_from_rust for a rust_static_library crate

Example:

// 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);
}

Enabling cpp_api_from_rust for a third_party/rust crate

Set 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);
  // ...
}

Inspecting the generated bindings

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>

Specifying binding dependencies

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.

Bindings dependencies for 1st-party Rust libraries

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.

Bindings dependencies for //third_party/rust libraries

3rd-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.

Bindings dependencies for Rust standard library

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 namespace
  • alloc crate => rs_alloc namespace
  • core crate => rs_core namespace

The 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.gn overrides 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 actual third_party/crubit/support directory in the root of the Chromium repo.

Troubleshooting

APIs missing from the generated bindings

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.

No --crate-header was specified for this crate

If 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.

Build performance

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.