You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
423 lines
16 KiB
423 lines
16 KiB
# Copyright 2021 The Chromium Authors
|
|
# Use of this source code is governed by a BSD-style license that can be
|
|
# found in the LICENSE file.
|
|
|
|
import("//build/rust/rust_executable.gni")
|
|
import("//build/rust/rust_macro.gni")
|
|
import("//build/rust/rust_static_library.gni")
|
|
|
|
# This template allows for building Cargo crates within gn.
|
|
#
|
|
# It is intended for use with pre-existing (third party) code and
|
|
# is none too efficient. (It will stall the build pipeline whilst
|
|
# it runs build scripts to work out what flags are needed). First
|
|
# party code should directly use first-class gn targets, such as
|
|
# //build/rust/rust_static_library.gni or similar.
|
|
#
|
|
# Because it's intended for third-party code, it automatically
|
|
# defaults to //build/config/compiler:no_chromium_code which
|
|
# suppresses some warnings. If you *do* use this for first party
|
|
# code, you should remove that config and add the equivalent
|
|
# //build/config/compiler:chromium_code config.
|
|
#
|
|
# Arguments:
|
|
# sources
|
|
# crate_root
|
|
# deps
|
|
# aliased_deps
|
|
# features
|
|
# build_native_rust_unit_tests
|
|
# edition
|
|
# crate_name
|
|
# All just as in rust_static_library.gni
|
|
# library_configs/executable_configs
|
|
# All just as in rust_target.gni
|
|
#
|
|
# epoch (optional)
|
|
# The major version of the library, which is used to differentiate between
|
|
# multiple versions of the same library name. This includes all leading 0s
|
|
# and the first non-zero value in the crate's version. This should be left
|
|
# as the default, which is "0", for first-party code unless there are
|
|
# multiple versions of a crate present. For third-party code, the version
|
|
# epoch (matching the directory it is found in) should be specified.
|
|
#
|
|
# Examples:
|
|
# 1.0.2 => epoch = "1"
|
|
# 4.2.0 => epoch = "4"
|
|
# 0.2.7 => epoch = "0.2"
|
|
# 0.0.3 => epoch = "0.0.3"
|
|
#
|
|
# dev_deps
|
|
# Same meaning as test_deps in rust_static_library.gni, but called
|
|
# dev_deps to match Cargo.toml better.
|
|
#
|
|
# build_root (optional)
|
|
# Filename of build.rs build script.
|
|
#
|
|
# build_deps (optional)
|
|
# Build script dependencies
|
|
#
|
|
# build_sources (optional)
|
|
# List of sources for build script. Must be specified if
|
|
# build_root is specified.
|
|
#
|
|
# build_script_outputs (optional)
|
|
# List of .rs files generated by the build script, if any.
|
|
# Fine to leave undefined even if you have a build script.
|
|
# This doesn't directly correspond to any Cargo variable,
|
|
# but unfortunately is necessary for gn to build its dependency
|
|
# trees automatically.
|
|
# Many build scripts just output --cfg directives, in which case
|
|
# no source code is generated and this can remain empty.
|
|
#
|
|
# build_script_inputs (optional)
|
|
# If the build script reads any files generated by build_deps,
|
|
# as opposed to merely linking against them, add a list of such
|
|
# files here. Again, this doesn't correspond to a Cargo variable
|
|
# but is necessary for gn.
|
|
#
|
|
# crate_type "bin", "proc-macro" or "rlib" (optional)
|
|
# Whether to build an executable. The default is "rlib".
|
|
# At present others are not supported.
|
|
#
|
|
# cargo_pkg_authors
|
|
# cargo_pkg_version
|
|
# cargo_pkg_name
|
|
# cargo_pkg_description
|
|
# Strings as found within 'version' and similar fields within Cargo.toml.
|
|
# Converted to environment variables passed to rustc, in case the crate
|
|
# uses clap `crate_version!` or `crate_authors!` macros (fairly common in
|
|
# command line tool help)
|
|
|
|
template("cargo_crate") {
|
|
_orig_target_name = target_name
|
|
|
|
_crate_name = _orig_target_name
|
|
if (defined(invoker.crate_name)) {
|
|
_crate_name = invoker.crate_name
|
|
}
|
|
|
|
# Construct metadata from the crate epoch or an explicitly provided metadata
|
|
# field.
|
|
_rustc_metadata = ""
|
|
if (defined(invoker.rustc_metadata)) {
|
|
_rustc_metadata = invoker.rustc_metadata
|
|
} else if (defined(invoker.epoch)) {
|
|
_rustc_metadata = "${_crate_name}-${invoker.epoch}"
|
|
}
|
|
|
|
# Executables need to have unique names. Work out a prefix.
|
|
if (defined(invoker.build_root)) {
|
|
_epochlabel = "vunknown"
|
|
if (defined(invoker.epoch)) {
|
|
_tempepoch = string_replace(invoker.epoch, ".", "_")
|
|
_epochlabel = "v${_tempepoch}"
|
|
}
|
|
|
|
# This name includes the target name to ensure it's unique for each possible
|
|
# build target in the same BUILD.gn file.
|
|
_build_script_name =
|
|
"${_crate_name}_${target_name}_${_epochlabel}_build_script"
|
|
|
|
# Where the OUT_DIR will point when running the build script exe, and
|
|
# compiling the crate library/binaries. This directory must include the
|
|
# target name to avoid collisions between multiple GN targets that exist
|
|
# in the same BUILD.gn.
|
|
_build_script_env_out_dir = "$target_gen_dir/$target_name"
|
|
}
|
|
|
|
_rustenv = []
|
|
if (defined(invoker.rustenv)) {
|
|
_rustenv = invoker.rustenv
|
|
}
|
|
if (defined(invoker.cargo_pkg_authors)) {
|
|
_rustenv += [ "CARGO_PKG_AUTHORS=${invoker.cargo_pkg_authors}" ]
|
|
}
|
|
if (defined(invoker.cargo_pkg_version)) {
|
|
_rustenv += [ "CARGO_PKG_VERSION=${invoker.cargo_pkg_version}" ]
|
|
}
|
|
if (defined(invoker.cargo_pkg_name)) {
|
|
_rustenv += [ "CARGO_PKG_NAME=${invoker.cargo_pkg_name}" ]
|
|
}
|
|
if (defined(invoker.cargo_pkg_description)) {
|
|
_rustenv += [ "CARGO_PKG_DESCRIPTION=${invoker.cargo_pkg_description}" ]
|
|
}
|
|
|
|
# cargo_crate() should set library_configs, executable_configs,
|
|
# proc_macro_configs. Not configs.
|
|
assert(!defined(invoker.configs))
|
|
|
|
# Work out what we're building.
|
|
_crate_type = "rlib"
|
|
if (defined(invoker.crate_type)) {
|
|
_crate_type = invoker.crate_type
|
|
}
|
|
if (_crate_type == "bin") {
|
|
_target_type = "rust_executable"
|
|
assert(!defined(invoker.epoch))
|
|
if (defined(invoker.executable_configs)) {
|
|
_configs = invoker.executable_configs
|
|
}
|
|
} else if (_crate_type == "proc-macro") {
|
|
_target_type = "rust_macro"
|
|
if (defined(invoker.proc_macro_configs)) {
|
|
_configs = invoker.proc_macro_configs
|
|
}
|
|
} else {
|
|
assert(_crate_type == "rlib")
|
|
_target_type = "rust_static_library"
|
|
if (defined(invoker.library_configs)) {
|
|
_configs = invoker.library_configs
|
|
}
|
|
}
|
|
|
|
# The main target, either a Rust source set or an executable.
|
|
target(_target_type, target_name) {
|
|
forward_variables_from(invoker,
|
|
"*",
|
|
TESTONLY_AND_VISIBILITY + [
|
|
"build_root",
|
|
"build_deps",
|
|
"build_sources",
|
|
"build_script_inputs",
|
|
"build_script_outputs",
|
|
"epoch",
|
|
"unit_test_target",
|
|
"configs",
|
|
"executable_configs",
|
|
"library_configs",
|
|
"proc_macro_configs",
|
|
"rustenv",
|
|
"dev_deps",
|
|
])
|
|
forward_variables_from(invoker, TESTONLY_AND_VISIBILITY)
|
|
|
|
rustc_metadata = _rustc_metadata
|
|
|
|
# TODO(crbug.com/1422745): don't default to true. This requires changes to
|
|
# third_party.toml and gnrt when generating third-party build targets.
|
|
allow_unsafe = true
|
|
|
|
configs = []
|
|
if (defined(_configs)) {
|
|
configs += _configs
|
|
}
|
|
|
|
if (_crate_type == "rlib") {
|
|
# Forward configs for unit tests.
|
|
if (defined(invoker.executable_configs)) {
|
|
executable_configs = invoker.executable_configs
|
|
}
|
|
}
|
|
|
|
if (!defined(rustflags)) {
|
|
rustflags = []
|
|
}
|
|
rustenv = _rustenv
|
|
|
|
if (!defined(build_native_rust_unit_tests)) {
|
|
build_native_rust_unit_tests = _crate_type != "proc-macro"
|
|
}
|
|
if (build_native_rust_unit_tests) {
|
|
# Unit tests in a proc-macro crate type don't make sense, you can't
|
|
# compile executables against the `proc_macro` crate.
|
|
assert(_crate_type != "proc-macro")
|
|
}
|
|
|
|
# The unit tests for each target, if generated, should be unique as well.
|
|
# a) It needs to be unique even if multiple build targets have the same
|
|
# `crate_name`, but different target names.
|
|
# b) It needs to be unique even if multiple build targets have the same
|
|
# `crate_name` and target name, but different epochs.
|
|
_unit_test_unique_target_name = ""
|
|
if (_crate_name != _orig_target_name) {
|
|
_unit_test_unique_target_name = "${_orig_target_name}_"
|
|
}
|
|
_unit_test_unique_epoch = ""
|
|
if (defined(invoker.epoch)) {
|
|
_epoch_str = string_replace(invoker.epoch, ".", "_")
|
|
_unit_test_unique_epoch = "v${_epoch_str}_"
|
|
}
|
|
if (defined(output_dir) && output_dir != "") {
|
|
unit_test_output_dir = output_dir
|
|
}
|
|
unit_test_target = "${_unit_test_unique_target_name}${_crate_name}_${_unit_test_unique_epoch}unittests"
|
|
|
|
if ((!defined(output_dir) || output_dir == "") && _crate_type == "rlib") {
|
|
# Cargo crate rlibs can be compiled differently for tests, and must not
|
|
# collide with the production outputs. This does *not* override the
|
|
# unit_test_output_dir, which is set above, as that target is not an rlib.
|
|
output_dir = "$target_out_dir/$_orig_target_name"
|
|
}
|
|
|
|
if (defined(invoker.dev_deps)) {
|
|
test_deps = invoker.dev_deps
|
|
}
|
|
|
|
if (defined(invoker.build_root)) {
|
|
# Uh-oh, we have a build script
|
|
if (!defined(deps)) {
|
|
deps = []
|
|
}
|
|
if (!defined(sources)) {
|
|
sources = []
|
|
}
|
|
|
|
# This... is a bit weird. We generate a file called cargo_flags.rs which
|
|
# does not actually contain Rust code, but instead some flags to add
|
|
# to the rustc command line. We need it to end in a .rs extension so that
|
|
# we can include it in the 'sources' line and thus have dependency
|
|
# calculation done correctly. data_deps won't work because targets don't
|
|
# require them to be present until runtime.
|
|
flags_file = "$_build_script_env_out_dir/cargo_flags.rs"
|
|
rustflags += [ "@" + rebase_path(flags_file, root_build_dir) ]
|
|
sources += [ flags_file ]
|
|
if (defined(invoker.build_script_outputs)) {
|
|
# Build scripts may output arbitrary files. They are usually included in
|
|
# the main Rust target using include! or include_str! and therefore the
|
|
# filename may be .rs or may be arbitrary. We want to educate ninja
|
|
# about the dependency either way.
|
|
foreach(extra_source,
|
|
filter_include(invoker.build_script_outputs, [ "*.rs" ])) {
|
|
sources += [ "$_build_script_env_out_dir/$extra_source" ]
|
|
}
|
|
inputs = []
|
|
foreach(extra_source,
|
|
filter_exclude(invoker.build_script_outputs, [ "*.rs" ])) {
|
|
inputs += [ "$_build_script_env_out_dir/$extra_source" ]
|
|
}
|
|
}
|
|
deps += [ ":${_build_script_name}_output" ]
|
|
}
|
|
}
|
|
|
|
if (defined(invoker.build_root)) {
|
|
# Extra targets required to make build script work
|
|
action("${_build_script_name}_output") {
|
|
script = rebase_path("//build/rust/run_build_script.py")
|
|
build_script_target = ":${_build_script_name}($rust_macro_toolchain)"
|
|
deps = [ build_script_target ]
|
|
|
|
# The build script may be built with a different toolchain when
|
|
# cross-compiling (the host toolchain) so we must find the path relative
|
|
# to that.
|
|
_build_script_root_out_dir =
|
|
get_label_info(build_script_target, "root_out_dir")
|
|
_build_script_exe = "$_build_script_root_out_dir/$_build_script_name"
|
|
|
|
# The executable is always built with the `rust_macro_toolchain` which
|
|
# targets the `host_os`. The rule here is on the `target_toolchain` which
|
|
# can be different (e.g. compiling on Linux, targeting Windows).
|
|
if (host_os == "win") {
|
|
_build_script_exe = "${_build_script_exe}.exe"
|
|
}
|
|
|
|
_flags_file = "$_build_script_env_out_dir/cargo_flags.rs"
|
|
|
|
inputs = [ _build_script_exe ]
|
|
outputs = [ _flags_file ]
|
|
args = [
|
|
"--build-script",
|
|
rebase_path(_build_script_exe, root_build_dir),
|
|
"--output",
|
|
rebase_path(_flags_file, root_build_dir),
|
|
"--rust-prefix",
|
|
rebase_path("${rust_sysroot}/bin", root_build_dir),
|
|
"--out-dir",
|
|
rebase_path(_build_script_env_out_dir, root_build_dir),
|
|
"--src-dir",
|
|
rebase_path(get_path_info(invoker.build_root, "dir"), root_build_dir),
|
|
]
|
|
if (defined(rust_abi_target) && rust_abi_target != "") {
|
|
args += [
|
|
"--target",
|
|
rust_abi_target,
|
|
]
|
|
}
|
|
if (defined(invoker.features)) {
|
|
args += [ "--features" ]
|
|
args += invoker.features
|
|
}
|
|
if (defined(invoker.build_script_outputs)) {
|
|
args += [ "--generated-files" ]
|
|
args += invoker.build_script_outputs
|
|
foreach(generated_file, invoker.build_script_outputs) {
|
|
outputs += [ "$_build_script_env_out_dir/$generated_file" ]
|
|
}
|
|
}
|
|
if (_rustenv != []) {
|
|
args += [ "--env" ]
|
|
args += _rustenv
|
|
}
|
|
if (defined(invoker.build_script_inputs)) {
|
|
inputs += invoker.build_script_inputs
|
|
}
|
|
}
|
|
|
|
if (toolchain_for_rust_host_build_tools) {
|
|
# The build script is only available to be built on the host, and we use
|
|
# the rust_macro_toolchain for it to unblock building them while the
|
|
# Chromium stdlib is still being compiled.
|
|
rust_executable(_build_script_name) {
|
|
sources = invoker.build_sources
|
|
crate_root = invoker.build_root
|
|
if (defined(invoker.build_deps)) {
|
|
deps = invoker.build_deps
|
|
}
|
|
|
|
# The ${_build_script_name}_output target looks for the exe in this
|
|
# location. Due to how the Windows component build works, this has to
|
|
# be $root_out_dir for all EXEs. In component build, C++ links to the
|
|
# CRT as a DLL, and if Rust does not match, we can't link mixed target
|
|
# Rust EXE/DLLs, as the headers in C++ said something different than
|
|
# what Rust links. Since the CRT DLL is placed in the $root_out_dir,
|
|
# an EXE can find it if it's also placed in that dir.
|
|
output_dir = root_out_dir
|
|
rustenv = _rustenv
|
|
forward_variables_from(invoker,
|
|
[
|
|
"features",
|
|
"edition",
|
|
"rustflags",
|
|
])
|
|
configs -= [
|
|
"//build/config/compiler:chromium_code",
|
|
|
|
# Avoid generating profiling data for build scripts.
|
|
#
|
|
# TODO(crbug.com/1426472): determine for sure whether to remove this
|
|
# config. I'm not sure of the overlap between PGO instrumentation and
|
|
# code coverage instrumentation, but we definitely don't want build
|
|
# script coverage for PGO, while we might for test coverage metrics.
|
|
#
|
|
# If we do include build script output in test metrics, it could be
|
|
# misleading: exercising some code from a build script doesn't give us
|
|
# the same signal as an actual test.
|
|
"//build/config/coverage:default_coverage",
|
|
]
|
|
configs += [ "//build/config/compiler:no_chromium_code" ]
|
|
}
|
|
} else {
|
|
not_needed(invoker,
|
|
[
|
|
"build_sources",
|
|
"build_deps",
|
|
"build_root",
|
|
"build_script_inputs",
|
|
"build_script_outputs",
|
|
])
|
|
}
|
|
} else {
|
|
not_needed([
|
|
"_name_specific_output_dir",
|
|
"_orig_target_name",
|
|
])
|
|
}
|
|
}
|
|
|
|
set_defaults("cargo_crate") {
|
|
library_configs = default_compiler_configs
|
|
executable_configs = default_executable_configs
|
|
proc_macro_configs = default_rust_proc_macro_configs
|
|
}
|