naiveproxy/src/build/rust/rust_target.gni
2022-05-29 18:11:59 +08:00

414 lines
14 KiB
Plaintext

# Copyright 2021 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import("//build/config/rust.gni")
import("//build/rust/rust_autocxx.gni")
import("//build/rust/rust_unit_test.gni")
# Creates a Rust target (rlib, executable, proc macro etc.) with
# ability to understand some handy variables such as "edition" and
# "features" and also to build any associated unit tests.
#
# Normally, you should not use this directly. Use either
# - cargo_crate.gni - for 3p crates only
# - rust_static_library.gni - for 1p Rust code
# - mixed_static_library.gni - for 1p C++ and Rust code together.
#
# Because the common use of this is rust_static_library, all the documentation
# for the supported options is given in rust_static_library.gni. Please refer
# over there.
#
# If you're using rust_target directly, you will also need to specify:
# target_type
# executable, rust_library etc. per GN norms
#
# support_use_from_cpp (bool)
# Whether both C++ and Rust may link against this. If so, the Rust standard
# library will be explicitly included for C++ to link against. This is
# always true if cxx_bindings is non-empty, in which case [auto]cxx
# will also be implicitly depended on.
#
# There is one area where this differs from `rust_static_library`: configs.
# Here, you must specify `executable_configs` or `library_configs`
# depending on the type of thing you're generating. This is so that
# different defaults can be provided.
template("rust_target") {
# Only one of `crate_root` or `generate_crate_root` can be specified, or
# neither.
assert(!defined(invoker.crate_root) ||
!(defined(invoker.generate_crate_root) && invoker.generate_crate_root))
_target_name = target_name
_crate_name = target_name
if (defined(invoker.crate_name)) {
_crate_name = invoker.crate_name
}
if (defined(invoker.output_dir) && invoker.output_dir != "") {
_out_dir = invoker.output_dir
} else {
_out_dir = target_out_dir
}
if (defined(invoker.generate_crate_root) && invoker.generate_crate_root) {
generated_file("${_target_name}_crate_root") {
outputs = [ "${target_gen_dir}/${target_name}.rs" ]
contents = [
"// Generated crate root for ${_target_name}.",
"// @generated",
"",
]
foreach(rs, invoker.sources) {
rs_path_from_root = rebase_path(rs, target_gen_dir)
contents += [ "#[path = \"${rs_path_from_root}\"]" ]
# Drop the file extension from the module name.
rs_modname = string_replace(rs, ".rs", "")
# Replace invalid "/" chars in the source file path.
rs_modname = string_replace(rs_modname, "/", "_")
# Since source files are specified relative to the BUILD.gn they may
# also have ".." path components.
rs_modname = string_replace(rs_modname, "..", "dotdot")
contents += [
"mod ${rs_modname};",
"",
]
}
}
_crate_root =
string_join("", get_target_outputs(":${_target_name}_crate_root"))
} else if (defined(invoker.crate_root)) {
_crate_root = invoker.crate_root
} else if (invoker.target_type == "executable") {
_crate_root = "src/main.rs"
} else {
_crate_root = "src/lib.rs"
}
_testonly = false
if (defined(invoker.testonly)) {
_testonly = invoker.testonly
}
if (defined(invoker.visibility)) {
_visibility = invoker.visibility
}
_rustflags = []
if (defined(invoker.rustflags)) {
_rustflags += invoker.rustflags
}
if (defined(invoker.features)) {
foreach(i, invoker.features) {
_rustflags += [ "--cfg=feature=\"${i}\"" ]
}
}
_edition = "2021"
if (defined(invoker.edition)) {
_edition = invoker.edition
}
_configs = [ string_join("",
[
"//build/rust:edition_",
_edition,
]) ]
if (invoker.target_type == "executable") {
if (defined(invoker.executable_configs)) {
_configs += invoker.executable_configs
}
} else {
if (defined(invoker.library_configs)) {
_configs += invoker.library_configs
}
}
_forward_to_host_toolchain = false
if (invoker.target_type == "rust_proc_macro") {
# TODO(crbug.com/gn/104): GN rust_proc_macro targets are missing this
# command line flag, for the proc_macro crate which is provided by rustc for
# compiling proc-macros.
_rustflags += [
"--extern",
"proc_macro",
]
if (current_toolchain != host_toolchain) {
_forward_to_host_toolchain = true
}
_main_target_suffix = "${target_name}__proc_macro"
} else {
_main_target_suffix = "__rlib"
}
_deps = []
if (defined(invoker.deps)) {
_deps += invoker.deps
}
_public_deps = []
if (defined(invoker.public_deps)) {
_public_deps += invoker.public_deps
}
_build_unit_tests = false
if (defined(invoker.build_native_rust_unit_tests)) {
_build_unit_tests =
invoker.build_native_rust_unit_tests && can_build_rust_unit_tests
}
# Declares that the Rust crate generates bindings between C++ and Rust via the
# Cxx/autocxx crates. It may generate C++ headers and/or use the cxx crate macros
# to generate Rust code internally, depending on what bindings are declared. If
# set, it's a set of rust files that include Cxx or autocxx bindings declarations.
_cxx_bindings = []
if (defined(invoker.cxx_bindings)) {
_cxx_bindings = invoker.cxx_bindings
}
_rustenv = []
if (defined(invoker.rustenv)) {
_rustenv += invoker.rustenv
}
foreach(_autocxx_generated, _cxx_bindings) {
# TODO(crbug.com/1306841): Currently we support only a single
# include_cpp! macro per target and this ugly environment
# variable is necessary. Meanwhile, this path must match that of
# out_gen0_rs in rust_autocxx.gni.
_rustenv += [ "AUTOCXX_RS_FILE=" + rebase_path(
"$target_gen_dir/$_autocxx_generated/gen0.include.rs") ]
}
# Normally we generate a C++ bindings group only if there are C++ bindings,
# since it's not useful otherwise. But mixed targets want to be able to depend
# on the Rust side regardless, or other Rust targets that provide interop
# without using Cxx to generate bindings (such as via #[no_mangle] functions).
_support_use_from_cpp = _cxx_bindings != []
if (defined(invoker.support_use_from_cpp) && invoker.support_use_from_cpp) {
_support_use_from_cpp = true
}
if (defined(invoker.mutually_dependent_target)) {
_mutually_dependent_target = invoker.mutually_dependent_target
_mutually_dependent_public_deps = invoker.mutually_dependent_public_deps
} else {
assert(!defined(_mutually_dependent_public_deps))
}
# TODO(danakj): This could be a hash generated from the input crate, such as
# from its path, in which case the BUILD.gn would not need to specify
# anything. But GN doesn't give us a hash function to make that easy.
_metadata = "0"
if (defined(invoker.epoch)) {
_metadata = invoker.epoch
}
# We require that all source files are listed, even though this is
# not a requirement for rustc. The reason is to ensure that tools
# such as `gn deps` give the correct answer, and thus we trigger
# the right test suites etc. on code change.
# TODO(crbug.com/1256930) - verify this is correct
assert(defined(invoker.sources), "sources must be listed")
if (_forward_to_host_toolchain) {
# Redirect to the host toolchain.
group(_target_name) {
testonly = _testonly
if (defined(_visibility)) {
visibility = _visibility
}
public_deps =
[ ":${_target_name}${_main_target_suffix}($host_toolchain)" ]
}
not_needed(invoker, "*")
not_needed([
"_build_unit_tests",
"_crate_root",
"_crate_name",
"_cxx_bindings",
"_deps",
"_metadata",
"_mutually_dependent_public_deps",
"_mutually_dependent_target",
"_out_dir",
"_public_deps",
"_rustenv",
"_support_use_from_cpp",
"_test_deps",
"_testonly",
"_visibility",
"proc_macro_target",
])
} else {
group(_target_name) {
testonly = _testonly
if (defined(_visibility)) {
visibility = _visibility
}
# Both the C++ bindings (if present) and the Rust crate should be treated
# like direct dependencies, so we expose them both in public_deps.
public_deps = [ ":${_target_name}${_main_target_suffix}" ]
if (_support_use_from_cpp) {
if (_cxx_bindings != []) {
public_deps += [ ":${_target_name}_autocxx_generated" ]
# Additionally, C++ bindings generated by Cxx can include C++ types
# that come from the Cxx library, such as `rust::Str`. So any C++
# target that depends on a rust target directly may need access to Cxx
# as well, which means it must appear in public_deps.
public_deps += [ "//build/rust:cxx_cppdeps" ]
} else {
# If we're supporting use from C++ but not using Cxx bindings, then we
# just need to make sure C++ links in the Rust stdlib.
deps = [ "//build/rust/std" ]
}
} else {
# Mixed targets (which define the below) always enable use from C++.
assert(!defined(_mutually_dependent_target))
}
}
_rust_deps = _deps
_rust_public_deps = _public_deps
_autocxx_deps = _deps + _public_deps
# In a mixed target, the C++ bindings may include headers from the C++
# part of the mixed target. Those headers rely on being used with the
# correct dependencies present.
if (defined(_mutually_dependent_public_deps)) {
_autocxx_deps += _mutually_dependent_public_deps
}
# The Rust target (and unit tests) need the autocxx & cxx crates when using
# them to generate bindings - both are in autocxx_rustdeps. We also need
# to depend upon the autocxx code generation step, since when we build
# our Rust code, a procedural macro will look for a file generated by that
# codegen step.
if (_cxx_bindings != []) {
_rust_deps += [ "//build/rust:autocxx_rustdeps" ]
# Generated bindings are a public dep so that the unit tests can include
# them.
_rust_public_deps += [ ":${_target_name}_autocxx_generated" ]
}
# You must go through the groups above to get to these targets.
_visibility = []
_visibility = [ ":${_target_name}" ]
target(invoker.target_type, "${_target_name}${_main_target_suffix}") {
forward_variables_from(invoker,
"*",
TESTONLY_AND_VISIBILITY + [
"features",
"deps",
"public_deps",
"rustflags",
"rustenv",
"configs",
"output_dir",
"unit_test_target",
])
testonly = _testonly
visibility = _visibility
crate_name = _crate_name
crate_root = _crate_root
configs = []
configs = _configs
deps = _rust_deps
public_deps = _rust_public_deps
rustflags = _rustflags
rustflags += [ string_join("",
[
"-Cmetadata=",
_metadata,
]) ]
rustenv = _rustenv
rustenv += [ "OUT_DIR=" + rebase_path(_out_dir) ]
# The Rust tool() declarations, like C++ ones, use the output_name and
# output_dir, so that GN targets can override these if needed. Here we
# give them their default values, or allow them to be overridden.
output_dir = _out_dir
if (!defined(output_name) || output_name == "") {
output_name = crate_name
}
}
if (_cxx_bindings != []) {
rust_autocxx("${_target_name}_autocxx_generated") {
testonly = _testonly
visibility = [ ":${_target_name}${_main_target_suffix}" ]
if (defined(_visibility)) {
visibility += _visibility
}
sources = _cxx_bindings
deps = _autocxx_deps
configs = _configs
if (is_component_build) {
# In a component_build the cxx bindings may be linked into a shared
# library at any point up the dependency tree, so always export.
export_symbols = true
} else if (invoker.target_type == "shared_library") {
export_symbols = true
} else {
export_symbols = false
}
}
} else {
not_needed([
"_autocxx_deps",
"_mutually_dependent_public_deps",
])
}
if (_build_unit_tests) {
_unit_test_target = "${_target_name}_unittests"
if (defined(invoker.unit_test_target)) {
_unit_test_target = invoker.unit_test_target
}
rust_unit_test(_unit_test_target) {
forward_variables_from(invoker, [ "sources" ])
testonly = true
crate_root = _crate_root
rustflags = _rustflags
output_dir = _out_dir
deps = _rust_deps + _public_deps
if (defined(invoker.test_deps)) {
deps += invoker.test_deps
}
if (defined(_mutually_dependent_target)) {
# This routes through the C++ mixed target rule to get back to the
# Rust $_target_name.
public_deps = [ _mutually_dependent_target ]
} else {
public_deps = [ ":${_target_name}" ]
}
if (defined(invoker.executable_configs)) {
configs = []
configs = invoker.executable_configs
}
rustenv = _rustenv
}
} else {
not_needed([
"_crate_root",
"_crate_name",
"_metadata",
"_mutually_dependent_target",
])
}
}
}
set_defaults("rust_target") {
executable_configs = default_executable_configs
library_configs = default_compiler_configs
}