blob: 1d52b0ad8e911a08b2539bd0cabdd9d72ba1a4a2 [file] [log] [blame] [view]
# Rust bindings for C++ libraries
`:rs_bindings_from_cc` parses C++ headers and generates:
* A Rust source file with bindings for the C++ API
* A C++ source file with the implementation of the bindings
## Architecture
At a high level, Crubit parses C++ headers using Clang (with the
[same configuration](../docs/bindings/overview/reproducible_builds.md) as when
used to compile C++). A distilled intermediate representation is passed to Rust,
which generates actual Rust and C++ source code for the bindings.
Some key source files:
* [`rs_bindings_from_cc.cc`](rs_bindings_from_cc.cc): The `main()` function
for bindings generation.
* [`importers/*`](importers/): The Clang-AST processing classes, which extract
the important details about the C++ AST. When implementing a new C++ feature
(e.g. supporting aliases, or typedefs), it must first be added here,
* [`ir.h`](ir.h) and [`ir.rs`](ir.rs): The intermediate representation,
produced by `importers/*.cc` and consumed by `src_code_gen.rs`. If source
code generation needs to understand something about the AST, it must be
present here.
* [`src_code_gen.rs`](src_code_gen.rs): The actual bindings code generation.
This is where the majority of decisions about source code generation go
(e.g. how to represent reference types, which traits to implement, etc.)
In addition, the generated bindings can depend on runtime libraries, found in
[`crubit/support/`](../support/). For example, the Rust type for rvalue
references is found there.
## Manual testing
### Golden files
The easiest way to see the output of Crubit on a C++ header is to add the header
to the [`golden`](test/golden) directory and regenerate the golden outputs:
```sh
rs_bindings_from_cc/test/golden/update.sh
```
### Running Crubit on a Bazel target
To specifically see what bindings Crubit generates for a bazel target, one can
specifically invoke Crubit's aspect and inspect the generated output:
```sh
$ bazel build --aspects \
//rs_bindings_from_cc/bazel_support:rust_bindings_from_cc_aspect.bzl%rust_bindings_from_cc_aspect \
--output_groups=out \
//some/cc/library/target:here
```
The source files used for interop will be output into bazel-bin, and their paths
will be output to the terminal.
### `:test_wrapper`
For convenience, `:test_wrapper` is a shell script that passes all Clang command
line flags from the current Bazel C++ toolchain:
```
bazel run //rs_bindings_from_cc:test_wrapper -- --public_headers=hello_world.h
```
or:
```
bazel build //rs_bindings_from_cc:test_wrapper
bazel-bin/rs_bindings_from_cc/test_wrapper --public_headers=hello_world.h
```
## Testing Practices
If possible follow these recommendations:
* Unit tests for
[`src_code_gen`](rs_bindings_from_cc/src_code_gen.rs)
should:
* be written in Rust
* have snippets of C++ as input
* use
[`assert_cc_matches!/assert_rs_matches!/assert_cc_not_matches!/assert_rs_not_matches!`](rs_bindings_from_cc/token_stream_matchers.rs)
macros
* Unit tests for the
[`importer`](rs_bindings_from_cc/importer.h)
should:
* be written in Rust
([`ir_from_cc_test.rs`](rs_bindings_from_cc/ir_from_cc_test.rs))
so they cover both AST logic and IR serialization/deserialization, but
C++ tests (thanks to its nice matchers) are also OK at the moment
([`importer_test.cc`](rs_bindings_from_cc/importer_test.cc))
* have snippets of C++ as input
* make assertions on the content of the IR
* Write tests for the command line interface of interop tools in
[`rs_bindings_from_cc_test.sh`](rs_bindings_from_cc/test/rs_bindings_from_cc_test.sh).
* Write golden file tests (comparing both the C++ and Rust generated source
code against the checked-in files) in
[`test/golden`](rs_bindings_from_cc/test/golden/).
* Run
[`rs_bindings_from_cc/test/golden/update.sh`](rs_bindings_from_cc/test/golden/update.sh)
to regenerate checked-in files.
* Write full executable end-to-end tests (verifying that interop tools and
Bazel rules generate outputs that can be built and executed) as small
projects with a `rust_test` or `cc_test` on top in subpackages of `test`.
To run individual rust tests with `bazel test` (like `bazel test
--test_filter=<test>` for gtest cases), give the test function name as
`--test_arg=<test>`.
To get Rust backtraces for `rs_bindings_from_cc` when running end-to-end tests,
use `bazel test --action_env=RUST_BACKTRACE=1` to run the tests.
## Debugging
If you want to build the tool specially, for example using sanitizers, use the
script at
`rs_bindings_from_cc/generate_bindings_for_target_with_tool_flags.sh`,
for example:
```
rs_bindings_from_cc/generate_bindings_for_target_with_tool_flags.sh \
//base \
--config=asan
```
If you want to build the tool specially and use it for generating bindings from
a golden file, use the `<header basename>_rs_test` target. For `types.h` the
command would be:
```
rs_bindings_from_cc/generate_bindings_for_target_with_tool_flags.sh \
//rs_bindings_from_cc/test/golden:types_rs_test \
--config=asan
```
If you want to see the Clang AST dump of some file (generated files work too),
run:
```
bazel build --per_file_copt=<PATH_TO_FILE>@-Xclang,-ast-dump,-fno-color-diagnostics <TARGET> > /tmp/output_file
```
## Contributing
Chat room (internal): https://chat.google.com/room/AAAAImO--WA
20% starter projects list (internal): b/hotlists/3645339