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 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: The main() function for bindings generation.
  • 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 and 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: 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/. 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 directory and regenerate the golden outputs:

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:

$ 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:

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