tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

rs_bindings_from_cc.gni (10158B)


      1 # Copyright 2022 The Chromium Authors
      2 # Use of this source code is governed by a BSD-style license that can be
      3 # found in the LICENSE file.
      4 
      5 import("//chromium/build/config/clang/clang.gni")
      6 import("//chromium/build/config/rust.gni")
      7 import("//chromium/build/config/sysroot.gni")
      8 import("//chromium/build/rust/mixed_static_library.gni")
      9 
     10 # Template to generate and build Rust bindings for a set of C++ headers using
     11 # Crubit's `rs_bindings_from_cc` tool.
     12 #
     13 # This template expands to a `mixed_static_library` named "<target>_rs_api" and
     14 # containing the Rust side of the bindings (as well as internal C++ thunks
     15 # needed to support the bindings).
     16 #
     17 # The generated out/.../gen/.../<target>_rs_api.rs is machine-generated, but
     18 # should be fairly readable (inspecting it might be useful to discover the
     19 # imported bindings and their shape).
     20 #
     21 # Parameters:
     22 #
     23 #   bindings_target:
     24 #     The C++ target (e.g. a `source_set`) that Rust bindings should be
     25 #     generated for.
     26 #
     27 #   public_headers:
     28 #     The .h files to generate bindings for.
     29 #
     30 #     Implementation note: This doesn't just take *all* the headers of the
     31 #     `bindings_target`, because typically only a *subset* of headers provides
     32 #     the *public* API that bindings are needed for.
     33 #
     34 #     TODO(crbug.com/40226863): Internal headers should still to be included in
     35 #     the targets_and_args metadata...
     36 #
     37 #   deps:
     38 #     Other `rs_bindings_from_cc` targets that the bindings need to depend on
     39 #     (e.g. because APIs in the `public_headers` refer to `struct`s declared in
     40 #     those other targets. Note how in the usage example below bindings for
     41 #     `struct Goat` are provided by `goat_rs_api`, and that therefore the
     42 #     bindings for the `TeleportGoat` provided by `teleport_rs_api` depend on
     43 #     `goat_rs_api`).
     44 #
     45 #     Oftentimes `deps` can be a copy of the `public_deps` of the
     46 #     `bindings_target`, but depending on targets with the suffix "_rs_api".
     47 #     Still, there are scenarios where `deps` don't parallel *all* entries from
     48 #     `public_deps`:
     49 #       * `public_deps` that don't expose Rust APIs (i.e. there are no
     50 #         "..._rs_api" targets to depend on).
     51 #       * `public_deps` that Crubit bindings don't depend on (dependencies that
     52 #          don't provide re-exportable C++ APIs, or that only provide items
     53 #          that are ignored by Crubit - e.g. `#define`s).
     54 #
     55 # Usage example:
     56 #
     57 #   BUILD.gn:
     58 #       import("//chromium/build/rust/rs_bindings_from_cc.gni")
     59 #       import("//chromium/build/rust/rust_executable.gni")
     60 #
     61 #       rust_executable("my_target") {
     62 #         crate_root = "main.rs"
     63 #         sources = [ "main.rs" ]
     64 #         deps = [ ":teleport_rs_api" ]
     65 #       ]
     66 #
     67 #       # This will generate "teleport_rs_api" target that provides Rust
     68 #       # bindings for the "teleport.h" header from the ":teleport" source
     69 #       # set.
     70 #       rs_bindings_from_cc("teleport_rs_api") {
     71 #         bindings_target = ":teleport"
     72 #         public_headers = ["teleport.h"]
     73 #         deps = [ ":goat_rs_api" ]  # Parallel's `public_deps` of ":teleport".
     74 #       }
     75 #
     76 #       source_set("teleport") {
     77 #         sources = [ "teleport.h", ... ]
     78 #         public_deps = [ ":goat" ]
     79 #       }
     80 #
     81 #       rs_bindings_from_cc("goat_rs_api") {
     82 #         bindings_target = ":goat"
     83 #         public_headers = ["goat.h"]
     84 #       }
     85 #       source_set("goat") {
     86 #         sources = [ "goat.h", ... ]
     87 #       }
     88 #
     89 #   teleport.h:
     90 #     #include "goat.h"
     91 #     void TeleportGoat(const Goat& goat_to_teleport);
     92 #
     93 #   goat.h:
     94 #     struct Goat { ... };
     95 #
     96 #   main.rs:
     97 #     fn main() {
     98 #       let g: goat_rs_api::Goat = ...;
     99 #       teleport_rs_api::TeleportGoat(&g);
    100 #     }
    101 #
    102 # Debugging and implementation notes:
    103 #
    104 # - Consider running the build while CRUBIT_DEBUG environment variable is set.
    105 #   This will generate additional `.ir` file and log extra information from
    106 #   the `run_rs_bindings_from_cc.py` script (e.g. full cmdlines for invoking
    107 #   `rs_bindings_from_cc`).
    108 #
    109 template("rs_bindings_from_cc") {
    110   # Mandatory parameter: bindings_target.
    111   assert(defined(invoker.bindings_target),
    112          "Must specify the C target to make bindings for.")
    113   _bindings_target = invoker.bindings_target
    114 
    115   # Mandatory/unavoidable parameter: target_name
    116   _lib_target_name = target_name
    117   _base_target_name = get_label_info(_bindings_target, "name")
    118   assert(_lib_target_name == "${_base_target_name}_rs_api",
    119          "The convention is that bindings for `foo` are named `foo_rs_api`")
    120 
    121   # Mandatory parameter: public_headers.
    122   assert(defined(invoker.public_headers),
    123          "Must specify the public C headers to make bindings for.")
    124   _rebased_public_headers = []
    125   foreach(hdr, invoker.public_headers) {
    126     _rebased_public_headers += [ rebase_path(hdr) ]
    127   }
    128 
    129   # Optional parameter: testonly.
    130   _testonly = false
    131   if (defined(invoker.testonly)) {
    132     _testonly = invoker.testonly
    133   }
    134 
    135   # Optional parameter: visibility.
    136   if (defined(invoker.visibility)) {
    137     _visibility = invoker.visibility
    138   }
    139 
    140   # Optional parameter: deps.
    141   #
    142   # TODO(crbug.com/40226863): Can we somehow assert that `_deps` only contains
    143   # some "..._rs_api" targets crated via
    144   # `mixed_static_library($_lib_target_name)` below?  foreach(dep, _deps) {
    145   # assert something }
    146   _deps = []
    147   if (defined(invoker.deps)) {
    148     _deps = invoker.deps
    149   }
    150 
    151   # Various names and paths that are shared across multiple targets defined
    152   # in the template here.
    153   _gen_bindings_target_name = "${_lib_target_name}_gen_bindings"
    154   _gen_metadata_target_name = "${_lib_target_name}_gen_metadata"
    155   _metadata_target_name = "${_lib_target_name}_metadata"
    156   _metadata_path = "${target_gen_dir}/${_lib_target_name}_meta.json"
    157   _rs_out_path = "${target_gen_dir}/${_lib_target_name}.rs"
    158   _cc_out_path = "${target_gen_dir}/${_lib_target_name}_impl.cc"
    159 
    160   # Calculating the --targets_and_args snippet for the *current* target
    161   # and putting it into GN's `metadata`.
    162   group(_metadata_target_name) {
    163     testonly = _testonly
    164     visibility = [
    165       ":${_gen_metadata_target_name}",
    166       ":${_lib_target_name}",
    167     ]
    168     deps = []
    169 
    170     metadata = {
    171       # The data below corresponds to a single-target entry inside
    172       # `--targets_and_args` cmdline argument of `rs_bindings_from_cc`.
    173       crubit_target_and_args = [
    174         {
    175           # The `get_label_info` call below expands ":foo_rs_api" into
    176           # something like "//dir/bar/baz:foo_rs_api".  Crubit assumes that
    177           # there is a colon + uses the after-colon-suffix as the name of the
    178           # crate.
    179           t = get_label_info(":${_lib_target_name}", "label_no_toolchain")
    180           h = _rebased_public_headers
    181         },
    182       ]
    183     }
    184   }
    185 
    186   # Gathering --targets-and-args data from *all* transitive dependencies and
    187   # putting them into the file at `_metadata_path`.
    188   generated_file(_gen_metadata_target_name) {
    189     testonly = _testonly
    190     visibility = [ ":${_gen_bindings_target_name}" ]
    191 
    192     deps = [ ":${_metadata_target_name}" ]
    193     deps += _deps
    194 
    195     testonly = _testonly
    196     outputs = [ _metadata_path ]
    197     output_conversion = "json"
    198     data_keys = [ "crubit_target_and_args" ]
    199 
    200     # `walk_keys` are used to limit how deep the transitive dependency goes.
    201     # This is important, because Crubit doesn't care about all the `deps` or
    202     # `public_deps` of the `_bindings_target`.  (See also the doc comment about
    203     # `rs_bindings_from_cc.deps` parameter at the top of this file.)
    204     walk_keys = [ "crubit_metadata_deps" ]
    205   }
    206 
    207   # Exposing the generated Rust bindings.
    208   mixed_static_library(_lib_target_name) {
    209     testonly = _testonly
    210     if (defined(_visibility)) {
    211       visibility = _visibility
    212     }
    213 
    214     sources = [ _cc_out_path ]
    215     deps = _deps
    216     deps += [
    217       ":${_gen_bindings_target_name}",
    218       ":${_metadata_target_name}",
    219       "//third_party/crubit:deps_of_rs_api_impl",
    220       _bindings_target,
    221     ]
    222 
    223     # Chromium already covers `chromium/src/` and `out/Release/gen` in the
    224     # include path, but we need to explicitly add `out/Release` below.  This
    225     # is needed, because `--public_headers` passed to Crubit use paths relative
    226     # to the `out/Release` directory.  See also b/239238801.
    227     include_dirs = [ root_build_dir ]
    228 
    229     rs_sources = [ _rs_out_path ]
    230     rs_crate_name = _lib_target_name
    231     rs_crate_root = _rs_out_path
    232     rs_deps = _deps
    233     rs_deps += [
    234       ":${_gen_bindings_target_name}",
    235       "//third_party/crubit:deps_of_rs_api",
    236     ]
    237 
    238     metadata = {
    239       crubit_metadata_deps = _deps + [ ":${_metadata_target_name}" ]
    240     }
    241   }
    242 
    243   # Invoking Crubit's `rs_bindings_from_cc` tool to generate Rust bindings.
    244   action(_gen_bindings_target_name) {
    245     testonly = _testonly
    246     if (defined(_visibility)) {
    247       visibility = _visibility
    248     }
    249 
    250     script = "//chromium/build/rust/run_rs_bindings_from_cc.py"
    251     inputs = [ "//third_party/rust-toolchain/bin/rs_bindings_from_cc" ]
    252     sources = invoker.public_headers
    253     outputs = [
    254       _rs_out_path,
    255       _cc_out_path,
    256     ]
    257 
    258     deps = [ ":${_gen_metadata_target_name}" ]
    259     args = [
    260       # Target-specific outputs:
    261       "--rs_out",
    262       rebase_path(_rs_out_path),
    263       "--cc_out",
    264       rebase_path(_cc_out_path),
    265 
    266       # Target-specific inputs:
    267       "--public_headers",
    268       string_join(",", _rebased_public_headers),
    269       "--targets_and_args_from_gn",
    270       rebase_path(_metadata_path),
    271     ]
    272 
    273     # Several important compiler flags come from default_compiler_configs
    274     configs = default_compiler_configs
    275     if (defined(invoker.configs)) {
    276       configs += invoker.configs
    277     }
    278     args += [
    279       "--",
    280       "{{defines}}",
    281       "{{include_dirs}}",
    282       "{{cflags}}",
    283 
    284       # This path contains important C headers (e.g. stddef.h) and {{cflags}}
    285       # does not include it. Normally this path is implicitly added by clang but
    286       # it does not happen for libclang.
    287       #
    288       # Add it last so includes from deps and configs take precedence.
    289       "-isystem" + rebase_path(
    290               clang_base_path + "/lib/clang/" + clang_version + "/include",
    291               root_build_dir),
    292 
    293       # Passes C comments through as rustdoc attributes.
    294       "-fparse-all-comments",
    295     ]
    296   }
    297 }