blob: 857a16fe4d0d93c316660a148849feead4ca1a91 [file] [view]
# `cpp_api_from_rust`
## Introduction
`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.
This document provides instructions for using `cpp_api_from_rust` in Chromium.
See [`//docs/rust/crubit/README.md`](./README.md) for general information about
Crubit.
[TOC]
## Enabling `cpp_api_from_rust` for a `rust_static_library` crate
Example:
```rust
// build/rust/tests/test_cpp_api_from_rust/lib.rs:
pub fn mul_two_ints_via_rust(x: i32, y: i32) -> i32 {
x * y
}
```
```gn
# 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. Note that `check_includes_strict = true` and `public` are set so
that C++ targets depending on `:my_cpp_code` do not have to wait for the Rust
compilation (see [Crubit and build performance](build_performance.md)):
```
# My BUILD.gn:
source_set("my_cpp_code") {
# Enforces that `my_cpp_code.h` does not #include headers from private
# `deps`, which also stops GN from forwarding the bindings-related
# order-only dependencies to C++ targets that depend on `:my_cpp_code`.
check_includes_strict = true
public = [ "my_cpp_code.h" ]
sources = [ "my_cpp_code.cc" ]
# ...
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::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.:
```gn
# 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:
```sh
$ 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: ...
```
## 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:
```rust
// 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)
}
```
```gn
# 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"
bindings_deps = [ "//some/other/lib:other_lib_bindings" ]
}
}
```
Note how `other_lib_bindings` are listed in `bindings_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 `bindings_deps` attribute of `cpp_api_from_rust`.
### Bindings dependencies for C++ types
If public APIs of the Rust crate use C++ types (from Rust bindings generated by
[`rust_api_from_cpp`](rust_api_from_cpp.md)), then `bindings_deps` should list
the `rust_api_from_cpp` target. The C++ bindings will then refer to the
original C++ types. See `rust_lib_using_cpp_type` in
`build/rust/tests/test_rust_api_from_cpp/round_trip/BUILD.gn` for an example.
### 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 = { bindings_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 `#include`d 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
The sections below should help diagnose and fix some issues you may encounter.
If your issue is not covered, then please see
[`docs/rust/crubit/README.md`](./README.md) for instructions
how to report a new bug.
### 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
C++ targets that depend on the bindings (directly or transitively) have to wait
for the Rust compilation, unless the target that depends on the bindings sets
`check_includes_strict = true` (with the bindings in private `deps`) or
isolates the `.cc` files in a `source_set` with `public = []`. Most bindings
end up being transitively used by many C++ targets, so please follow
[Crubit and build performance](build_performance.md) when adding a
dependency on the bindings.
### 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).