tor-browser

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

cargo_crate.gni (20193B)


      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/rust/rust_executable.gni")
      6 import("//chromium/build/rust/rust_macro.gni")
      7 import("//chromium/build/rust/rust_static_library.gni")
      8 
      9 # This template allows for building Cargo crates within gn.
     10 #
     11 # It is intended for use with pre-existing (third party) code and
     12 # is none too efficient. (It will stall the build pipeline whilst
     13 # it runs build scripts to work out what flags are needed). First
     14 # party code should directly use first-class gn targets, such as
     15 # //build/rust/rust_static_library.gni or similar.
     16 #
     17 # Because it's intended for third-party code, it automatically
     18 # defaults to //build/config/compiler:no_chromium_code which
     19 # suppresses some warnings. If you *do* use this for first party
     20 # code, you should remove that config and add the equivalent
     21 # //build/config/compiler:chromium_code config.
     22 #
     23 # Arguments:
     24 #  sources
     25 #  crate_root
     26 #  deps
     27 #  aliased_deps
     28 #  features
     29 #  build_native_rust_unit_tests
     30 #  edition
     31 #  crate_name
     32 #    All just as in rust_static_library.gni
     33 #  library_configs/executable_configs
     34 #    All just as in rust_target.gni
     35 #
     36 #  epoch (optional)
     37 #    The major version of the library, which is used to differentiate between
     38 #    multiple versions of the same library name. This includes all leading 0s
     39 #    and the first non-zero value in the crate's version. This should be left
     40 #    as the default, which is "0", for first-party code unless there are
     41 #    multiple versions of a crate present. For third-party code, the version
     42 #    epoch (matching the directory it is found in) should be specified.
     43 #
     44 #    Examples:
     45 #      1.0.2 => epoch = "1"
     46 #      4.2.0 => epoch = "4"
     47 #      0.2.7 => epoch = "0.2"
     48 #      0.0.3 => epoch = "0.0.3"
     49 #
     50 #  dev_deps
     51 #    Same meaning as test_deps in rust_static_library.gni, but called
     52 #    dev_deps to match Cargo.toml better.
     53 #
     54 #  build_root (optional)
     55 #    Filename of build.rs build script.
     56 #
     57 #  build_deps (optional)
     58 #    Build script dependencies
     59 #
     60 #  build_sources (optional)
     61 #    List of sources for build script. Must be specified if
     62 #    build_root is specified.
     63 #
     64 #  build_script_outputs (optional)
     65 #    List of .rs files generated by the build script, if any.
     66 #    Fine to leave undefined even if you have a build script.
     67 #    This doesn't directly correspond to any Cargo variable,
     68 #    but unfortunately is necessary for gn to build its dependency
     69 #    trees automatically.
     70 #    Many build scripts just output --cfg directives, in which case
     71 #    no source code is generated and this can remain empty.
     72 #
     73 #  build_script_inputs (optional)
     74 #    If the build script reads any files generated by build_deps,
     75 #    as opposed to merely linking against them, add a list of such
     76 #    files here. Again, this doesn't correspond to a Cargo variable
     77 #    but is necessary for gn.
     78 #
     79 #  native_libs (optional)
     80 #    Paths to library files that need to be in the linking search path when
     81 #    depending on the crate's library, as it links against them via #[link]
     82 #    directives.
     83 #
     84 #  crate_type "bin", "proc-macro" or "rlib" (optional)
     85 #    Whether to build an executable. The default is "rlib".
     86 #    At present others are not supported.
     87 #
     88 #  cargo_pkg_authors
     89 #  cargo_pkg_version
     90 #  cargo_pkg_name
     91 #  cargo_pkg_description
     92 #    Strings as found within 'version' and similar fields within Cargo.toml.
     93 #    Converted to environment variables passed to rustc, in case the crate
     94 #    uses clap `crate_version!` or `crate_authors!` macros (fairly common in
     95 #    command line tool help)
     96 
     97 template("cargo_crate") {
     98   _orig_target_name = target_name
     99 
    100   _crate_name = _orig_target_name
    101   if (defined(invoker.crate_name)) {
    102     _crate_name = invoker.crate_name
    103   }
    104 
    105   # Construct metadata from the crate epoch or an explicitly provided metadata
    106   # field.
    107   _rustc_metadata = ""
    108   if (defined(invoker.rustc_metadata)) {
    109     _rustc_metadata = invoker.rustc_metadata
    110   } else if (defined(invoker.epoch)) {
    111     _rustc_metadata = "${_crate_name}-${invoker.epoch}"
    112   }
    113 
    114   _epochlabel = "vunknown"
    115   if (defined(invoker.epoch)) {
    116     _tempepoch = string_replace(invoker.epoch, ".", "_")
    117     _epochlabel = "v${_tempepoch}"
    118   }
    119 
    120   # Executables need to have unique names. Work out a prefix.
    121   if (defined(invoker.build_root)) {
    122     # This name includes the target name to ensure it's unique for each possible
    123     # build target in the same BUILD.gn file.
    124     _build_script_name =
    125         "${_crate_name}_${target_name}_${_epochlabel}_build_script"
    126 
    127     # Where the OUT_DIR will point when running the build script exe, and
    128     # compiling the crate library/binaries. This directory must include the
    129     # target name to avoid collisions between multiple GN targets that exist
    130     # in the same BUILD.gn.
    131     _build_script_env_out_dir = "$target_gen_dir/$target_name"
    132   }
    133 
    134   _rustenv = []
    135   if (defined(invoker.rustenv)) {
    136     _rustenv = invoker.rustenv
    137   }
    138   if (defined(invoker.cargo_pkg_authors)) {
    139     _rustenv += [ "CARGO_PKG_AUTHORS=${invoker.cargo_pkg_authors}" ]
    140   }
    141   if (defined(invoker.cargo_pkg_version)) {
    142     _rustenv += [ "CARGO_PKG_VERSION=${invoker.cargo_pkg_version}" ]
    143   }
    144   if (defined(invoker.cargo_pkg_name)) {
    145     _rustenv += [ "CARGO_PKG_NAME=${invoker.cargo_pkg_name}" ]
    146   }
    147   if (defined(invoker.cargo_pkg_description)) {
    148     _rustenv += [ "CARGO_PKG_DESCRIPTION=${invoker.cargo_pkg_description}" ]
    149   }
    150 
    151   # Try to determine the CARGO_MANIFEST_DIR, preferring the directory
    152   # with build.rs and otherwise assuming that the target contains a
    153   # `crate/` subdirectory.
    154   if (defined(invoker.build_root)) {
    155     manifest_dir = "."
    156   } else {
    157     build_gn_dir = get_label_info(target_name, "dir")
    158     manifest_dir = rebase_path(build_gn_dir + "/crate", root_build_dir)
    159   }
    160   _rustenv += [ "CARGO_MANIFEST_DIR=${manifest_dir}" ]
    161 
    162   # cargo_crate() should set library_configs, executable_configs,
    163   # proc_macro_configs. Not configs.
    164   assert(!defined(invoker.configs))
    165 
    166   # Work out what we're building.
    167   _crate_type = "rlib"
    168   if (defined(invoker.crate_type)) {
    169     _crate_type = invoker.crate_type
    170   }
    171   if (_crate_type == "bin") {
    172     _target_type = "rust_executable"
    173     assert(!defined(invoker.epoch))
    174     _configs = invoker.executable_configs
    175   } else if (_crate_type == "proc-macro") {
    176     _target_type = "rust_macro"
    177     _configs = invoker.proc_macro_configs
    178   } else {
    179     assert(_crate_type == "rlib")
    180     _target_type = "rust_static_library"
    181     _configs = invoker.library_configs
    182   }
    183 
    184   if (defined(invoker.output_name)) {
    185     _output_name = invoker.output_name
    186     not_needed([ "_epochlabel" ])
    187   } else if (_crate_type == "proc-macro") {
    188     # Proc macros are output to a top level directory (for the host toolchain).
    189     # Since multiple versions of the same crate may coexist, the output file
    190     # name must include the epoch to disambiguate.
    191     _output_name = "${_crate_name}_${_epochlabel}"
    192   } else if (_crate_type != "bin") {
    193     # Note that file names of libraries must start with the crate name in
    194     # order for the compiler to find transitive dependencies in the
    195     # directory search paths (since they are not all explicitly specified).
    196     #
    197     # For bin targets, we expect the target name to be unique, and the name
    198     # of the exe should not add magic stuff to it. And bin crates can not be
    199     # transitive dependencies.
    200     _output_name = "${_crate_name}_${_orig_target_name}"
    201     not_needed([ "_epochlabel" ])
    202   } else {
    203     not_needed([ "_epochlabel" ])
    204   }
    205 
    206   _testonly = false
    207   if (defined(invoker.testonly)) {
    208     _testonly = invoker.testonly
    209   }
    210 
    211   if (defined(invoker.native_libs)) {
    212     _native_libs_action = "copy_${target_name}_native_libs"
    213     _native_libs_config = "config_${target_name}_native_libs"
    214     _native_libs_dir =
    215         "${root_out_dir}/rustlib/${_crate_name}_${target_name}_${_epochlabel}"
    216 
    217     copy(_native_libs_action) {
    218       testonly = _testonly
    219       visibility = [ ":$target_name" ]
    220       sources = invoker.native_libs
    221       outputs = [ "${_native_libs_dir}/{{source_file_part}}" ]
    222     }
    223     config(_native_libs_config) {
    224       lib_dirs = [ "${_native_libs_dir}" ]
    225     }
    226   }
    227 
    228   # The main target, either a Rust source set or an executable.
    229   target(_target_type, target_name) {
    230     forward_variables_from(invoker,
    231                            "*",
    232                            TESTONLY_AND_VISIBILITY + [
    233                                  "build_root",
    234                                  "build_deps",
    235                                  "build_sources",
    236                                  "build_script_inputs",
    237                                  "build_script_outputs",
    238                                  "epoch",
    239                                  "unit_test_target",
    240                                  "configs",
    241                                  "executable_configs",
    242                                  "library_configs",
    243                                  "proc_macro_configs",
    244                                  "rustenv",
    245                                  "dev_deps",
    246                                  "native_libs_dir",
    247                                ])
    248 
    249     testonly = _testonly
    250     if (defined(invoker.visibility)) {
    251       visibility = invoker.visibility
    252     }
    253     crate_name = _crate_name
    254 
    255     if (defined(_output_name)) {
    256       output_name = _output_name
    257     }
    258 
    259     # Don't import the `chromium` crate into third-party code.
    260     no_chromium_prelude = true
    261 
    262     rustc_metadata = _rustc_metadata
    263 
    264     # TODO(crbug.com/40259764): don't default to true. This requires changes to
    265     # third_party.toml and gnrt when generating third-party build targets.
    266     allow_unsafe = true
    267 
    268     configs = []
    269     configs = _configs
    270     if (_crate_type == "rlib") {
    271       # Forward configs for unit tests.
    272       executable_configs = invoker.executable_configs
    273     }
    274 
    275     if (!defined(rustflags)) {
    276       rustflags = []
    277     }
    278     rustenv = _rustenv
    279 
    280     if (!defined(build_native_rust_unit_tests)) {
    281       build_native_rust_unit_tests = _crate_type != "proc-macro"
    282     }
    283     if (build_native_rust_unit_tests) {
    284       # Unit tests in a proc-macro crate type don't make sense, you can't
    285       # compile executables against the `proc_macro` crate.
    286       assert(_crate_type != "proc-macro")
    287     }
    288 
    289     # The unit tests for each target, if generated, should be unique as well.
    290     # a) It needs to be unique even if multiple build targets have the same
    291     #    `crate_name`, but different target names.
    292     # b) It needs to be unique even if multiple build targets have the same
    293     #    `crate_name` and target name, but different epochs.
    294     _unit_test_unique_target_name = ""
    295     if (_crate_name != _orig_target_name) {
    296       _unit_test_unique_target_name = "${_orig_target_name}_"
    297     }
    298     _unit_test_unique_epoch = ""
    299     if (defined(invoker.epoch)) {
    300       _epoch_str = string_replace(invoker.epoch, ".", "_")
    301       _unit_test_unique_epoch = "v${_epoch_str}_"
    302     }
    303     if (defined(output_dir) && output_dir != "") {
    304       unit_test_output_dir = output_dir
    305     }
    306     unit_test_target = "${_unit_test_unique_target_name}${_crate_name}_${_unit_test_unique_epoch}unittests"
    307 
    308     if ((!defined(output_dir) || output_dir == "") && _crate_type == "rlib") {
    309       # Cargo crate rlibs can be compiled differently for tests, and must not
    310       # collide with the production outputs. This does *not* override the
    311       # unit_test_output_dir, which is set above, as that target is not an rlib.
    312       output_dir = "$target_out_dir/$_orig_target_name"
    313     }
    314 
    315     if (defined(invoker.dev_deps)) {
    316       test_deps = invoker.dev_deps
    317     }
    318 
    319     if (defined(invoker.build_root)) {
    320       # Uh-oh, we have a build script
    321       if (!defined(deps)) {
    322         deps = []
    323       }
    324       if (!defined(sources)) {
    325         sources = []
    326       }
    327       if (!defined(inputs)) {
    328         inputs = []
    329       }
    330 
    331       # This... is a bit weird. We generate a file called cargo_flags.rs which
    332       # does not actually contain Rust code, but instead some flags to add
    333       # to the rustc command line. We need it to end in a .rs extension so that
    334       # we can include it in the 'sources' line and thus have dependency
    335       # calculation done correctly. data_deps won't work because targets don't
    336       # require them to be present until runtime.
    337       flags_file = "$_build_script_env_out_dir/cargo_flags.rs"
    338       rustflags += [ "@" + rebase_path(flags_file, root_build_dir) ]
    339       sources += [ flags_file ]
    340       if (defined(invoker.build_script_outputs)) {
    341         # Build scripts may output arbitrary files. They are usually included in
    342         # the main Rust target using include! or include_str! and therefore the
    343         # filename may be .rs or may be arbitrary. We want to educate ninja
    344         # about the dependency either way.
    345         foreach(extra_source,
    346                 filter_include(invoker.build_script_outputs, [ "*.rs" ])) {
    347           sources += [ "$_build_script_env_out_dir/$extra_source" ]
    348         }
    349         foreach(extra_source,
    350                 filter_exclude(invoker.build_script_outputs, [ "*.rs" ])) {
    351           inputs += [ "$_build_script_env_out_dir/$extra_source" ]
    352         }
    353       }
    354       deps += [ ":${_build_script_name}_output" ]
    355       if (defined(_native_libs_action)) {
    356         deps += [ ":${_native_libs_action}" ]
    357         configs += [ ":${_native_libs_config}" ]
    358       }
    359     }
    360   }
    361 
    362   if (defined(invoker.build_root)) {
    363     action("${_build_script_name}_write_rustflags") {
    364       _rustflags_txt = "$_build_script_env_out_dir/rustflags.txt"
    365       outputs = [ _rustflags_txt ]
    366       script = rebase_path("//chromium/build/rust/write_rustflags.py")
    367       args = [
    368         "--output",
    369         rebase_path(_rustflags_txt, root_build_dir),
    370         "--",
    371         "{{rustflags}}",
    372       ]
    373 
    374       # The configs are required to get `{{rustflags}}` so that the build script
    375       # is compiled with the same flags as the library/binary will be. The build
    376       # script is an executable so it also gets the executable configs. If this
    377       # is ever a problem we can add a separate build_script_configs to the
    378       # cargo_crate template and just have it default to the same thing as
    379       # executable_configs.
    380       configs = invoker.executable_configs
    381     }
    382 
    383     # Extra targets required to make build script work
    384     action("${_build_script_name}_output") {
    385       script = rebase_path("//chromium/build/rust/run_build_script.py")
    386       _write_rustflags_outputs =
    387           get_target_outputs(":${_build_script_name}_write_rustflags")
    388       _rustflags_txt = _write_rustflags_outputs[0]
    389       inputs = [
    390         "//chromium/build/action_helpers.py",
    391         "//chromium/build/gn_helpers.py",
    392         _rustflags_txt,
    393       ]
    394       build_script_target = ":${_build_script_name}($rust_macro_toolchain)"
    395       deps = [
    396         ":${_build_script_name}_write_rustflags",
    397         build_script_target,
    398       ]
    399       testonly = _testonly
    400       if (defined(invoker.visibility)) {
    401         visibility = invoker.visibility
    402       }
    403 
    404       # The build script may be built with a different toolchain when
    405       # cross-compiling (the host toolchain) so we must find the path relative
    406       # to that.
    407       _build_script_root_out_dir =
    408           get_label_info(build_script_target, "root_out_dir")
    409       _build_script_exe = "$_build_script_root_out_dir/$_build_script_name"
    410 
    411       # The executable is always built with the `rust_macro_toolchain` which
    412       # targets the `host_os`. The rule here is on the `target_toolchain` which
    413       # can be different (e.g. compiling on Linux, targeting Windows).
    414       if (host_os == "win") {
    415         _build_script_exe = "${_build_script_exe}.exe"
    416       }
    417 
    418       _flags_file = "$_build_script_env_out_dir/cargo_flags.rs"
    419 
    420       inputs += [ _build_script_exe ]
    421       outputs = [ _flags_file ]
    422       args = [
    423         "--build-script",
    424         rebase_path(_build_script_exe, root_build_dir),
    425         "--output",
    426         rebase_path(_flags_file, root_build_dir),
    427         "--rust-prefix",
    428         rebase_path("${rust_sysroot}/bin", root_build_dir),
    429         "--out-dir",
    430         rebase_path(_build_script_env_out_dir, root_build_dir),
    431         "--src-dir",
    432         rebase_path(get_path_info(invoker.build_root, "dir"), root_build_dir),
    433         "--target",
    434         rust_abi_target,
    435         "--rustflags",
    436         rebase_path(_rustflags_txt, root_build_dir),
    437       ]
    438       if (cargo_target_abi != "") {
    439         args += [
    440           "--target-abi",
    441           cargo_target_abi,
    442         ]
    443       }
    444       if (defined(invoker.features)) {
    445         args += [ "--features" ]
    446         args += invoker.features
    447       }
    448       if (defined(invoker.build_script_outputs)) {
    449         args += [ "--generated-files" ]
    450         args += invoker.build_script_outputs
    451         foreach(generated_file, invoker.build_script_outputs) {
    452           outputs += [ "$_build_script_env_out_dir/$generated_file" ]
    453         }
    454       }
    455       if (_rustenv != []) {
    456         args += [ "--env" ]
    457         args += _rustenv
    458       }
    459       if (defined(invoker.build_script_inputs)) {
    460         inputs += invoker.build_script_inputs
    461       }
    462     }
    463 
    464     if (toolchain_for_rust_host_build_tools) {
    465       # The build script is only available to be built on the host, and we use
    466       # the rust_macro_toolchain for it to unblock building them while the
    467       # Chromium stdlib is still being compiled.
    468       rust_executable(_build_script_name) {
    469         crate_name = _build_script_name
    470         sources = invoker.build_sources
    471         crate_root = invoker.build_root
    472         testonly = _testonly
    473         if (defined(invoker.visibility)) {
    474           visibility = invoker.visibility
    475         }
    476         if (defined(invoker.build_deps)) {
    477           deps = invoker.build_deps
    478         }
    479         if (defined(invoker.build_script_inputs)) {
    480           inputs = invoker.build_script_inputs
    481         }
    482 
    483         # Don't import the `chromium` crate into third-party code.
    484         no_chromium_prelude = true
    485 
    486         # The ${_build_script_name}_output target looks for the exe in this
    487         # location. Due to how the Windows component build works, this has to
    488         # be $root_out_dir for all EXEs. In component build, C++ links to the
    489         # CRT as a DLL, and if Rust does not match, we can't link mixed target
    490         # Rust EXE/DLLs, as the headers in C++ said something different than
    491         # what Rust links. Since the CRT DLL is placed in the $root_out_dir,
    492         # an EXE can find it if it's also placed in that dir.
    493         output_dir = root_out_dir
    494         rustenv = _rustenv
    495         forward_variables_from(invoker,
    496                                [
    497                                  "features",
    498                                  "edition",
    499                                  "rustflags",
    500                                ])
    501         configs -= [
    502           "//chromium/build/config/compiler:chromium_code",
    503 
    504           # Avoid generating profiling data for build scripts.
    505           #
    506           # TODO(crbug.com/40261306): determine for sure whether to remove this
    507           # config. I'm not sure of the overlap between PGO instrumentation and
    508           # code coverage instrumentation, but we definitely don't want build
    509           # script coverage for PGO, while we might for test coverage metrics.
    510           #
    511           # If we do include build script output in test metrics, it could be
    512           # misleading: exercising some code from a build script doesn't give us
    513           # the same signal as an actual test.
    514           "//chromium/build/config/coverage:default_coverage",
    515         ]
    516         configs += [ "//chromium/build/config/compiler:no_chromium_code" ]
    517       }
    518     } else {
    519       not_needed(invoker,
    520                  [
    521                    "build_sources",
    522                    "build_deps",
    523                    "build_root",
    524                    "build_script_inputs",
    525                    "build_script_outputs",
    526                  ])
    527     }
    528   } else {
    529     not_needed([
    530                  "_name_specific_output_dir",
    531                  "_orig_target_name",
    532                ])
    533   }
    534 }
    535 
    536 set_defaults("cargo_crate") {
    537   library_configs = default_compiler_configs
    538   executable_configs = default_executable_configs
    539   proc_macro_configs = default_rust_proc_macro_configs
    540 }