Devin Jeanpierre | 68c7b62 | 2023-03-10 14:15:37 -0800 | [diff] [blame] | 1 | # Rust bindings for C++ libraries |
Marcel Hlopko | 7ebafb2 | 2021-07-30 12:14:00 +0000 | [diff] [blame] | 2 | |
Marcel Hlopko | e8f1c4e | 2021-07-28 18:12:49 +0000 | [diff] [blame] | 3 | `:rs_bindings_from_cc` parses C++ headers and generates: |
| 4 | |
Devin Jeanpierre | 68c7b62 | 2023-03-10 14:15:37 -0800 | [diff] [blame] | 5 | * A Rust source file with bindings for the C++ API |
| 6 | * A C++ source file with the implementation of the bindings |
| 7 | |
| 8 | ## Architecture |
| 9 | |
| 10 | At a high level, Crubit parses C++ headers using Clang (with the |
Dmitri Gribenko | 6b84d33 | 2023-07-28 06:22:14 -0700 | [diff] [blame^] | 11 | [same configuration](../docs/bindings/overview/reproducible_builds.md) as when |
| 12 | used to compile C++). A distilled intermediate representation is passed to Rust, |
| 13 | which generates actual Rust and C++ source code for the bindings. |
Devin Jeanpierre | 68c7b62 | 2023-03-10 14:15:37 -0800 | [diff] [blame] | 14 | |
| 15 | Some key source files: |
| 16 | |
| 17 | * [`rs_bindings_from_cc.cc`](rs_bindings_from_cc.cc): The `main()` function |
| 18 | for bindings generation. |
| 19 | * [`importers/*`](importers/): The Clang-AST processing classes, which extract |
| 20 | the important details about the C++ AST. When implementing a new C++ feature |
| 21 | (e.g. supporting aliases, or typedefs), it must first be added here, |
| 22 | * [`ir.h`](ir.h) and [`ir.rs`](ir.rs): The intermediate representation, |
| 23 | produced by `importers/*.cc` and consumed by `src_code_gen.rs`. If source |
| 24 | code generation needs to understand something about the AST, it must be |
| 25 | present here. |
| 26 | * [`src_code_gen.rs`](src_code_gen.rs): The actual bindings code generation. |
| 27 | This is where the majority of decisions about source code generation go |
| 28 | (e.g. how to represent reference types, which traits to implement, etc.) |
| 29 | |
| 30 | In addition, the generated bindings can depend on runtime libraries, found in |
| 31 | [`crubit/support/`](../support/). For example, the Rust type for rvalue |
| 32 | references is found there. |
| 33 | |
| 34 | ## Manual testing |
| 35 | |
| 36 | ### Golden files |
| 37 | |
| 38 | The easiest way to see the output of Crubit on a C++ header is to add the header |
| 39 | to the [`golden`](test/golden) directory and regenerate the golden outputs: |
| 40 | |
| 41 | ```sh |
| 42 | rs_bindings_from_cc/test/golden/update.sh |
| 43 | ``` |
| 44 | |
| 45 | ### Running Crubit on a Bazel target |
| 46 | |
| 47 | To specifically see what bindings Crubit generates for a bazel target, one can |
| 48 | specifically invoke Crubit's aspect and inspect the generated output: |
| 49 | |
| 50 | ```sh |
| 51 | $ 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 |
| 52 | ``` |
| 53 | |
| 54 | The source files used for interop will be output into bazel-bin, and their paths |
| 55 | will be output to the terminal. |
| 56 | |
| 57 | ### `:test_wrapper` |
Marcel Hlopko | e8f1c4e | 2021-07-28 18:12:49 +0000 | [diff] [blame] | 58 | |
| 59 | For convenience, `:test_wrapper` is a shell script that passes all Clang command |
Marcel Hlopko | 9a94fc4 | 2022-04-06 23:35:36 -0700 | [diff] [blame] | 60 | line flags from the current Bazel C++ toolchain: |
Marcel Hlopko | e8f1c4e | 2021-07-28 18:12:49 +0000 | [diff] [blame] | 61 | |
| 62 | ``` |
Devin Jeanpierre | d7f4b3b | 2021-10-06 15:28:18 +0000 | [diff] [blame] | 63 | bazel run //rs_bindings_from_cc:test_wrapper -- --public_headers=hello_world.h |
Marcel Hlopko | e8f1c4e | 2021-07-28 18:12:49 +0000 | [diff] [blame] | 64 | ``` |
| 65 | |
| 66 | or: |
| 67 | |
| 68 | ``` |
| 69 | bazel build //rs_bindings_from_cc:test_wrapper |
Devin Jeanpierre | d7f4b3b | 2021-10-06 15:28:18 +0000 | [diff] [blame] | 70 | bazel-bin/rs_bindings_from_cc/test_wrapper --public_headers=hello_world.h |
Marcel Hlopko | e8f1c4e | 2021-07-28 18:12:49 +0000 | [diff] [blame] | 71 | ``` |
| 72 | |
Devin Jeanpierre | 68c7b62 | 2023-03-10 14:15:37 -0800 | [diff] [blame] | 73 | ## Testing Practices |
Marcel Hlopko | e8f1c4e | 2021-07-28 18:12:49 +0000 | [diff] [blame] | 74 | |
Marcel Hlopko | 5b8c112 | 2021-12-10 06:59:23 +0000 | [diff] [blame] | 75 | If possible follow these recommendations: |
Marcel Hlopko | e8f1c4e | 2021-07-28 18:12:49 +0000 | [diff] [blame] | 76 | |
Marcel Hlopko | 5b8c112 | 2021-12-10 06:59:23 +0000 | [diff] [blame] | 77 | * Unit tests for |
Kinuko Yasuda | abf4f3e | 2022-08-16 12:09:49 -0700 | [diff] [blame] | 78 | [`src_code_gen`](rs_bindings_from_cc/src_code_gen.rs) |
| 79 | should: |
| 80 | * be written in Rust |
Marcel Hlopko | 5b8c112 | 2021-12-10 06:59:23 +0000 | [diff] [blame] | 81 | * have snippets of C++ as input |
| 82 | * use |
Kinuko Yasuda | abf4f3e | 2022-08-16 12:09:49 -0700 | [diff] [blame] | 83 | [`assert_cc_matches!/assert_rs_matches!/assert_cc_not_matches!/assert_rs_not_matches!`](rs_bindings_from_cc/token_stream_matchers.rs) |
Marcel Hlopko | 5b8c112 | 2021-12-10 06:59:23 +0000 | [diff] [blame] | 84 | macros |
| 85 | * Unit tests for the |
Kinuko Yasuda | abf4f3e | 2022-08-16 12:09:49 -0700 | [diff] [blame] | 86 | [`importer`](rs_bindings_from_cc/importer.h) |
| 87 | should: |
| 88 | * be written in Rust |
| 89 | ([`ir_from_cc_test.rs`](rs_bindings_from_cc/ir_from_cc_test.rs)) |
Marcel Hlopko | 5b8c112 | 2021-12-10 06:59:23 +0000 | [diff] [blame] | 90 | so they cover both AST logic and IR serialization/deserialization, but |
| 91 | C++ tests (thanks to its nice matchers) are also OK at the moment |
Kinuko Yasuda | abf4f3e | 2022-08-16 12:09:49 -0700 | [diff] [blame] | 92 | ([`importer_test.cc`](rs_bindings_from_cc/importer_test.cc)) |
Marcel Hlopko | 5b8c112 | 2021-12-10 06:59:23 +0000 | [diff] [blame] | 93 | * have snippets of C++ as input |
| 94 | * make assertions on the content of the IR |
| 95 | * Write tests for the command line interface of interop tools in |
Kinuko Yasuda | abf4f3e | 2022-08-16 12:09:49 -0700 | [diff] [blame] | 96 | [`rs_bindings_from_cc_test.sh`](rs_bindings_from_cc/test/rs_bindings_from_cc_test.sh). |
Marcel Hlopko | 5b8c112 | 2021-12-10 06:59:23 +0000 | [diff] [blame] | 97 | * Write golden file tests (comparing both the C++ and Rust generated source |
| 98 | code against the checked-in files) in |
Kinuko Yasuda | abf4f3e | 2022-08-16 12:09:49 -0700 | [diff] [blame] | 99 | [`test/golden`](rs_bindings_from_cc/test/golden/). |
| 100 | * Run |
| 101 | [`rs_bindings_from_cc/test/golden/update.sh`](rs_bindings_from_cc/test/golden/update.sh) |
| 102 | to regenerate checked-in files. |
Googler | b2ef22d | 2022-01-04 11:17:22 +0000 | [diff] [blame] | 103 | * Write full executable end-to-end tests (verifying that interop tools and |
Marcel Hlopko | 9a94fc4 | 2022-04-06 23:35:36 -0700 | [diff] [blame] | 104 | Bazel rules generate outputs that can be built and executed) as small |
Marcel Hlopko | 5b8c112 | 2021-12-10 06:59:23 +0000 | [diff] [blame] | 105 | projects with a `rust_test` or `cc_test` on top in subpackages of `test`. |
Devin Jeanpierre | d9c382a | 2021-12-15 10:23:25 +0000 | [diff] [blame] | 106 | |
Devin Jeanpierre | 68c7b62 | 2023-03-10 14:15:37 -0800 | [diff] [blame] | 107 | To run individual rust tests with `bazel test` (like `bazel test |
| 108 | --test_filter=<test>` for gtest cases), give the test function name as |
| 109 | `--test_arg=<test>`. |
Kinuko Yasuda | ed9efd8 | 2022-08-18 13:55:01 -0700 | [diff] [blame] | 110 | |
Googler | b2ef22d | 2022-01-04 11:17:22 +0000 | [diff] [blame] | 111 | To get Rust backtraces for `rs_bindings_from_cc` when running end-to-end tests, |
| 112 | use `bazel test --action_env=RUST_BACKTRACE=1` to run the tests. |
| 113 | |
Marcel Hlopko | 253ceb4 | 2022-09-21 04:10:35 -0700 | [diff] [blame] | 114 | ## Debugging |
| 115 | |
| 116 | If you want to build the tool specially, for example using sanitizers, use the |
Devin Jeanpierre | 68c7b62 | 2023-03-10 14:15:37 -0800 | [diff] [blame] | 117 | script at |
| 118 | `rs_bindings_from_cc/generate_bindings_for_target_with_tool_flags.sh`, |
| 119 | for example: |
Marcel Hlopko | 253ceb4 | 2022-09-21 04:10:35 -0700 | [diff] [blame] | 120 | |
| 121 | ``` |
| 122 | rs_bindings_from_cc/generate_bindings_for_target_with_tool_flags.sh \ |
| 123 | //base \ |
| 124 | --config=asan |
| 125 | ``` |
| 126 | |
| 127 | If you want to build the tool specially and use it for generating bindings from |
| 128 | a golden file, use the `<header basename>_rs_test` target. For `types.h` the |
| 129 | command would be: |
| 130 | |
| 131 | ``` |
| 132 | rs_bindings_from_cc/generate_bindings_for_target_with_tool_flags.sh \ |
| 133 | //rs_bindings_from_cc/test/golden:types_rs_test \ |
| 134 | --config=asan |
| 135 | ``` |
| 136 | |
Devin Jeanpierre | 68c7b62 | 2023-03-10 14:15:37 -0800 | [diff] [blame] | 137 | If you want to see the Clang AST dump of some file (generated files work too), |
| 138 | run: |
Marcel Hlopko | 253ceb4 | 2022-09-21 04:10:35 -0700 | [diff] [blame] | 139 | |
| 140 | ``` |
| 141 | bazel build --per_file_copt=<PATH_TO_FILE>@-Xclang,-ast-dump,-fno-color-diagnostics <TARGET> > /tmp/output_file |
| 142 | ``` |
| 143 | |
Devin Jeanpierre | d9c382a | 2021-12-15 10:23:25 +0000 | [diff] [blame] | 144 | ## Contributing |
| 145 | |
Kinuko Yasuda | abf4f3e | 2022-08-16 12:09:49 -0700 | [diff] [blame] | 146 | Chat room (internal): https://chat.google.com/room/AAAAImO--WA |
Devin Jeanpierre | d9c382a | 2021-12-15 10:23:25 +0000 | [diff] [blame] | 147 | |
Kinuko Yasuda | abf4f3e | 2022-08-16 12:09:49 -0700 | [diff] [blame] | 148 | 20% starter projects list (internal): b/hotlists/3645339 |