tor-browser

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

rust_target.gni (18021B)


      1 # Copyright 2021 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/rust.gni")
      6 import("//chromium/build/rust/rust_unit_test.gni")
      7 
      8 # The //build directory is re-used for non-Chromium projects. Do not support
      9 # cxx bindings in such contexts by default, because //third_party may be
     10 # missing. Projects that wish to use cxx bindings must explicitly set the
     11 # enable_rust_cxx GN arg to true.
     12 if (enable_rust_cxx) {
     13   import("//third_party/rust/cxx/chromium_integration/rust_cxx.gni")
     14 }
     15 
     16 # Creates a Rust target (rlib, executable, proc macro etc.) with ability to
     17 # understand some handy variables such as "edition" and "features" and also to
     18 # build any associated unit tests.
     19 #
     20 # `bindgen_deps` field exists only for 3P cargo sys crates that uses
     21 # `rust_bindgen_generator` templates. For 1P code, `rust_bindgen` should
     22 # be used and should go directly in the `deps` field.
     23 #
     24 # Normally, you should not use this directly. Use either
     25 # - cargo_crate.gni - for 3p crates only
     26 # - rust_static_library.gni - for 1p Rust code
     27 #
     28 # Because the common use of this is rust_static_library, all the documentation
     29 # for the supported options is given in rust_static_library.gni. Please refer
     30 # over there.
     31 #
     32 # If you're using rust_target directly, you will also need to specify:
     33 # target_type executable, rust_library etc. per GN norms
     34 #
     35 # There is one area where this differs from `rust_static_library`: configs.
     36 # Here, you must specify `executable_configs` or `library_configs` depending on
     37 # the type of thing you're generating. This is so that different defaults can
     38 # be provided.
     39 
     40 template("rust_target") {
     41   _target_name = target_name
     42 
     43   # NOTE: TargetName=>CrateName mangling algorithm should be updated
     44   # simultaneously in 3 places: here, //build/rust/rust_static_library.gni,
     45   # //build/rust/chromium_prelude/import_attribute.rs
     46   if (defined(invoker.crate_name)) {
     47     _crate_name = invoker.crate_name
     48   } else {
     49     # Not using `get_label_info(..., "label_no_toolchain")` to consistently
     50     # use `//foo/bar:baz` instead of the alternative shorter `//foo/bar` form.
     51     _dir = get_label_info(":${_target_name}", "dir")
     52     _dir = string_replace(_dir, "//", "")
     53     _crate_name = "${_dir}:${_target_name}"
     54 
     55     # The `string_replace` calls below replicate the escaping algorithm
     56     # from the `escape_non_identifier_chars` function in
     57     # //build/rust/chromium_prelude/import_attribute.rs.  Note that the
     58     # ordering of `match` branches within the Rust function doesn't matter,
     59     # but the ordering of `string_replace` calls *does* matter - the escape
     60     # character `_` needs to be handled first to meet the injectivity
     61     # requirement (otherwise we would get `/` => `_s` => `_us` and the same
     62     # result for `_s` => `_us`).
     63     _crate_name = string_replace(_crate_name, "_", "_u")
     64     _crate_name = string_replace(_crate_name, "/", "_s")
     65     _crate_name = string_replace(_crate_name, ":", "_c")
     66     _crate_name = string_replace(_crate_name, "-", "_d")
     67   }
     68   _generate_crate_root =
     69       defined(invoker.generate_crate_root) && invoker.generate_crate_root
     70 
     71   # Only one of `crate_root` or `generate_crate_root` can be specified, or
     72   # neither.
     73   assert(!defined(invoker.crate_root) || !_generate_crate_root)
     74 
     75   # This is where the OUT_DIR environment variable points to when running a
     76   # build script and when compiling the build target, for consuming generated
     77   # files.
     78   _env_out_dir = "$target_gen_dir/$_target_name"
     79 
     80   _allow_unsafe = false
     81   if (defined(invoker.allow_unsafe)) {
     82     _allow_unsafe = invoker.allow_unsafe
     83   }
     84 
     85   if (_generate_crate_root) {
     86     generated_file("${_target_name}_crate_root") {
     87       outputs = [ "${target_gen_dir}/${target_name}.rs" ]
     88       contents = [
     89         "// Generated crate root for ${_target_name}.",
     90         "// @generated",
     91         "",
     92       ]
     93       foreach(rs, invoker.sources) {
     94         rs_path_from_root = rebase_path(rs, target_gen_dir)
     95         contents += [ "#[path = \"${rs_path_from_root}\"]" ]
     96 
     97         # Drop the file extension from the module name.
     98         rs_modname = string_replace(rs, ".rs", "")
     99 
    100         # Replace invalid "/" chars in the source file path.
    101         rs_modname = string_replace(rs_modname, "/", "_")
    102 
    103         # Since source files are specified relative to the BUILD.gn they may
    104         # also have ".." path components.
    105         rs_modname = string_replace(rs_modname, "..", "dotdot")
    106         contents += [
    107           "mod ${rs_modname};",
    108           "",
    109         ]
    110       }
    111     }
    112     _generated_crate_root = get_target_outputs(":${_target_name}_crate_root")
    113     _crate_root = _generated_crate_root[0]
    114   } else if (defined(invoker.crate_root)) {
    115     _crate_root = invoker.crate_root
    116   } else if (invoker.target_type == "executable") {
    117     _crate_root = "src/main.rs"
    118   } else {
    119     _crate_root = "src/lib.rs"
    120   }
    121 
    122   _testonly = false
    123   if (defined(invoker.testonly)) {
    124     _testonly = invoker.testonly
    125   }
    126   if (defined(invoker.visibility)) {
    127     _visibility = invoker.visibility
    128   }
    129 
    130   _rustflags = []
    131   if (defined(invoker.rustflags)) {
    132     _rustflags += invoker.rustflags
    133   }
    134   if (defined(invoker.features)) {
    135     foreach(i, invoker.features) {
    136       _rustflags += [ "--cfg=feature=\"${i}\"" ]
    137     }
    138   }
    139   _edition = "2021"
    140   if (defined(invoker.edition)) {
    141     _edition = invoker.edition
    142   }
    143 
    144   assert(!defined(configs))
    145   _configs = [ "//chromium/build/rust:edition_${_edition}" ]
    146   _test_configs = []
    147   if (invoker.target_type == "executable") {
    148     _configs += invoker.executable_configs
    149   } else if (invoker.target_type == "rust_proc_macro") {
    150     _configs += invoker.proc_macro_configs
    151     _test_configs += [ "//chromium/build/rust:proc_macro_extern" ]
    152   } else if (invoker.target_type == "shared_library") {
    153     _configs += invoker.shared_library_configs
    154   } else {
    155     _configs += invoker.library_configs
    156   }
    157 
    158   if (invoker.target_type == "rust_proc_macro") {
    159     _main_target_suffix = "__proc_macro"
    160   } else if (invoker.target_type == "shared_library") {
    161     _main_target_suffix = "__proc_macro"
    162   } else {
    163     _main_target_suffix = ""
    164   }
    165 
    166   _deps = []
    167   if (defined(invoker.deps)) {
    168     _deps += invoker.deps
    169   }
    170   if (defined(invoker.bindgen_deps)) {
    171     _bindgen_inputs = []
    172 
    173     # This iteration assumes that no targets have the same name which is
    174     # very rare to happen and if it does. An error will be thrown as we
    175     # try to create two targets with the same name, the error might not
    176     # be descriptive enough so maybe adding a check action would be better.
    177     foreach(bindgen_dep, invoker.bindgen_deps) {
    178       _copy_target_name =
    179           target_name + "_" + get_label_info(bindgen_dep, "name") + "_copy"
    180       copy(_copy_target_name) {
    181         _bindgen_output_files = get_target_outputs(bindgen_dep)
    182 
    183         # `rust_bindgen_generator` promises that the first output file is always .rs.
    184         sources = [ _bindgen_output_files[0] ]
    185         outputs = [ "$_env_out_dir/{{source_name_part}}.rs" ]
    186         deps = [ bindgen_dep ]
    187       }
    188 
    189       # The bindgen-generated rs files are inputs to this library for the library
    190       # to `include!` them.
    191       # The output of the copy action is always a single file so just copy everything.
    192       _bindgen_inputs += get_target_outputs(":$_copy_target_name")
    193 
    194       # Depend on the bindgen generation to make the above `_bindgen_inputs`.
    195       _deps += [ ":$_copy_target_name" ]
    196     }
    197   }
    198   _public_deps = []
    199   if (defined(invoker.public_deps)) {
    200     _public_deps += invoker.public_deps
    201   }
    202   if (defined(invoker.aliased_deps)) {
    203     _aliased_deps = invoker.aliased_deps
    204   } else {
    205     _aliased_deps = {
    206     }
    207   }
    208 
    209   _build_unit_tests = false
    210   if (defined(invoker.build_native_rust_unit_tests)) {
    211     _build_unit_tests =
    212         invoker.build_native_rust_unit_tests && can_build_rust_unit_tests
    213   }
    214 
    215   # Declares that the Rust crate generates bindings between C++ and Rust via the
    216   # Cxx crate. It may generate C++ headers and/or use the cxx crate macros to
    217   # generate Rust code internally, depending on what bindings are declared. If
    218   # set, it's a set of rust files that include Cxx bindings declarations.
    219   _cxx_bindings = []
    220   assert(!defined(invoker.cxx_bindings) || enable_rust_cxx,
    221          "cxx bindings are not supported when building rust targets " +
    222              "outside the Chromium build.")
    223   if (defined(invoker.cxx_bindings)) {
    224     _cxx_bindings = invoker.cxx_bindings
    225   }
    226   _rustenv = [ "OUT_DIR=" +
    227                rebase_path(_env_out_dir, get_path_info(_crate_root, "dir")) ]
    228   if (defined(invoker.rustenv)) {
    229     _rustenv += invoker.rustenv
    230   }
    231 
    232   # We require that all source files are listed, even though this is
    233   # not a requirement for rustc. The reason is to ensure that tools
    234   # such as `gn deps` give the correct answer, and thus we trigger
    235   # the right test suites etc. on code change.
    236   # TODO(crbug.com/40200431) - verify this is correct
    237   assert(defined(invoker.sources), "sources must be listed")
    238 
    239   if (invoker.target_type == "rust_proc_macro" &&
    240       !toolchain_for_rust_host_build_tools) {
    241     # Redirect to the proc macro toolchain, which uses prebuilt stdlib libraries
    242     # that are not built with panic=abort.
    243     group(_target_name) {
    244       testonly = _testonly
    245       if (defined(_visibility)) {
    246         visibility = _visibility
    247       }
    248       public_deps =
    249           [ ":${_target_name}${_main_target_suffix}($rust_macro_toolchain)" ]
    250     }
    251 
    252     not_needed(invoker, "*")
    253     not_needed([
    254                  "_aliased_deps",
    255                  "_allow_unsafe",
    256                  "_build_unit_tests",
    257                  "_crate_root",
    258                  "_crate_name",
    259                  "_cxx_bindings",
    260                  "_deps",
    261                  "_rustc_metadata",
    262                  "_out_dir",
    263                  "_public_deps",
    264                  "_rustenv",
    265                  "_rustflags",
    266                  "_support_use_from_cpp",
    267                  "_testonly",
    268                ])
    269   } else {
    270     # These are dependencies that must be included into the C++ target that
    271     # depends on this Rust one, and into the Rust target itself, respectively.
    272     #
    273     # For an rlib or exe, it's enough to add all these as dependencies of the
    274     # Rust target alone, and they will get included into the final link step.
    275     #
    276     # But when then Rust target is a shared library, the C++ target needs to
    277     # link the C++ thunks that are used to call the cxx bridge functions. And
    278     # Cxx library itself needs to be in both.
    279     _cxx_generated_deps_for_cpp = []
    280     _cxx_generated_deps_for_rust = []
    281     if (_cxx_bindings != []) {
    282       _cxx_generated_deps_for_cpp += [
    283         # The Cxx-generated thunks, which have the public C++ names and bounce
    284         # over to the Rust code.
    285         ":${_target_name}_cxx_generated",
    286 
    287         # Additionally, C++ bindings generated by Cxx can include C++ types
    288         # that come from the Cxx library, such as `rust::Str`. The header and
    289         # implementation of these types are provided in the cxx_cppdeps target.
    290         # The C++ targets depending on this Rust target need the headers, while
    291         # the Rust target needs the implementation.
    292         "//chromium/build/rust:cxx_cppdeps",
    293       ]
    294       _cxx_generated_deps_for_rust = [
    295         # The implementation of the Cxx library needs to be in the Rust target.
    296         "//chromium/build/rust:cxx_cppdeps",
    297       ]
    298     }
    299 
    300     # Proc macros and shared libraries have a group for the target name and
    301     # redirect to a suffixed target for the actual library.
    302     if (_main_target_suffix != "") {
    303       group(_target_name) {
    304         testonly = _testonly
    305         if (defined(_visibility)) {
    306           visibility = _visibility
    307         }
    308         public_deps = [ ":${_target_name}${_main_target_suffix}" ]
    309         public_deps += _cxx_generated_deps_for_cpp
    310       }
    311     }
    312 
    313     _rustc_metadata = ""
    314     if (defined(invoker.rustc_metadata)) {
    315       _rustc_metadata = invoker.rustc_metadata
    316     }
    317 
    318     _rust_deps = _deps
    319     _rust_aliased_deps = _aliased_deps
    320     _rust_public_deps = _public_deps
    321     _cxx_deps = _deps
    322 
    323     # Include the `chromium` crate in all first-party code. Third-party code
    324     # (and the `chromium` crate itself) opts out by setting
    325     # `no_chromium_prelude`.
    326     if (!defined(invoker.no_chromium_prelude) || !invoker.no_chromium_prelude) {
    327       if (enable_chromium_prelude) {
    328         _rust_deps += [ "//chromium/build/rust/chromium_prelude" ]
    329       }
    330     }
    331 
    332     if (_cxx_bindings != []) {
    333       # The Rust target (and unit tests) need the Cxx crate when using it to
    334       # generate bindings.
    335       _rust_deps += [ "//chromium/build/rust:cxx_rustdeps" ]
    336     }
    337 
    338     if (!defined(invoker.no_std) || !invoker.no_std) {
    339       _rust_deps += [ "//chromium/build/rust/std" ]
    340     }
    341 
    342     if (_build_unit_tests) {
    343       _unit_test_target = "${_target_name}_unittests"
    344       if (defined(invoker.unit_test_target)) {
    345         _unit_test_target = invoker.unit_test_target
    346       }
    347 
    348       rust_unit_test(_unit_test_target) {
    349         testonly = true
    350         crate_name = _unit_test_target
    351         crate_root = _crate_root
    352         sources = invoker.sources + [ crate_root ]
    353         rustflags = _rustflags
    354         env_out_dir = _env_out_dir
    355         if (defined(invoker.unit_test_output_dir)) {
    356           output_dir = invoker.unit_test_output_dir
    357         }
    358         deps = _rust_deps + _public_deps
    359         aliased_deps = _rust_aliased_deps
    360         public_deps = [ ":${_target_name}" ]
    361         if (defined(invoker.test_deps)) {
    362           deps += invoker.test_deps
    363         }
    364         inputs = []
    365         if (defined(invoker.inputs)) {
    366           inputs += invoker.inputs
    367         }
    368         if (defined(_bindgen_inputs)) {
    369           inputs += _bindgen_inputs
    370         }
    371         if (defined(invoker.test_inputs)) {
    372           inputs += invoker.test_inputs
    373         }
    374         if (defined(invoker.executable_configs)) {
    375           configs = []
    376           configs += invoker.executable_configs
    377         }
    378         configs += _test_configs
    379         rustenv = _rustenv
    380 
    381         if (!_allow_unsafe) {
    382           configs += [ "//chromium/build/rust:forbid_unsafe" ]
    383         }
    384       }
    385     } else {
    386       not_needed([
    387                    "_crate_root",
    388                    "_crate_name",
    389                    "_rustc_metadata",
    390                    "_test_configs",
    391                  ])
    392       not_needed(invoker, [ "executable_configs" ])
    393     }
    394 
    395     target(invoker.target_type, "${_target_name}${_main_target_suffix}") {
    396       forward_variables_from(invoker,
    397                              "*",
    398                              TESTONLY_AND_VISIBILITY + [
    399                                    "features",
    400                                    "deps",
    401                                    "aliased_deps",
    402                                    "public_deps",
    403                                    "rustflags",
    404                                    "rustenv",
    405                                    "configs",
    406                                    "unit_test_output_dir",
    407                                    "unit_test_target",
    408                                    "test_inputs",
    409                                  ])
    410 
    411       if (_main_target_suffix != "") {
    412         # There's a group that depends on this target, and dependencies must
    413         # be through that group.
    414         visibility = [ ":$_target_name" ]
    415         not_needed([ "_visibility" ])
    416       } else if (defined(_visibility)) {
    417         visibility = _visibility
    418       }
    419 
    420       testonly = _testonly
    421       crate_name = _crate_name
    422       crate_root = _crate_root
    423       configs = []
    424       configs = _configs
    425       deps = _rust_deps + _cxx_generated_deps_for_rust
    426       aliased_deps = _rust_aliased_deps
    427       public_deps = _rust_public_deps
    428       if (_main_target_suffix == "") {
    429         # When these are not provided by a wrapper group target, they are added
    430         # to the Rust target itself.
    431         public_deps += _cxx_generated_deps_for_cpp
    432       }
    433       rustflags = _rustflags
    434       if (_rustc_metadata != "") {
    435         rustflags += [ "-Cmetadata=${_rustc_metadata}" ]
    436       }
    437       rustenv = _rustenv
    438 
    439       if (_generate_crate_root) {
    440         deps += [ ":${_target_name}_crate_root" ]
    441         sources += [ _crate_root ]
    442       }
    443 
    444       if (!defined(inputs)) {
    445         inputs = []
    446       }
    447 
    448       if (defined(_bindgen_inputs)) {
    449         inputs += _bindgen_inputs
    450       }
    451 
    452       if (!defined(output_name)) {
    453         # Note that file names of libraries must start with the crate name in
    454         # order for the compiler to find transitive dependencies in the
    455         # directory search paths (since they are not all explicitly specified).
    456         #
    457         # For bin targets, we expect the target name to be unique, and the name
    458         # of the exe should not add magic stuff to it. And bin crates can not be
    459         # transitive dependencies.
    460         if (invoker.target_type == "executable") {
    461           output_name = _target_name
    462         } else {
    463           # TODO(danakj): Since the crate name includes the whole path for 1p
    464           # libraries, we could move the output_dir to `root_out_dir` here for
    465           # them, which would make for shorter file paths. But we need to not
    466           # do the same for 3p crates or those with a `crate_name` set
    467           # explicitly.
    468           output_name = _crate_name
    469         }
    470       }
    471 
    472       if (!_allow_unsafe) {
    473         configs += [ "//chromium/build/rust:forbid_unsafe" ]
    474       }
    475     }
    476 
    477     if (_cxx_bindings != []) {
    478       rust_cxx("${_target_name}_cxx_generated") {
    479         testonly = _testonly
    480         visibility = [ ":${_target_name}" ]
    481         if (defined(_visibility)) {
    482           visibility += _visibility
    483         }
    484         sources = _cxx_bindings
    485         deps = _cxx_deps + _public_deps
    486         configs = _configs
    487 
    488         # In a component_build the cxx bindings may be linked into a shared
    489         # library at any point up the dependency tree, so always export.
    490         export_symbols = is_component_build
    491       }
    492     } else {
    493       not_needed([ "_cxx_deps" ])
    494     }
    495   }
    496 }