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.
5 import("//build/config/rust.gni")
6 import("//build/rust/rust_unit_test.gni")
8 # The //build directory is re-used for non-Chromium products. We do not support
9 # cxx bindings in such contexts, because //third_party may be missing.
10 if (build_with_chromium) {
11 import("//third_party/rust/cxx/chromium_integration/rust_cxx.gni")
14 # Creates a Rust target (rlib, executable, proc macro etc.) with ability to
15 # understand some handy variables such as "edition" and "features" and also to
16 # build any associated unit tests.
18 # Normally, you should not use this directly. Use either
19 # - cargo_crate.gni - for 3p crates only
20 # - rust_static_library.gni - for 1p Rust code
22 # Because the common use of this is rust_static_library, all the documentation
23 # for the supported options is given in rust_static_library.gni. Please refer
26 # If you're using rust_target directly, you will also need to specify:
27 # target_type executable, rust_library etc. per GN norms
29 # There is one area where this differs from `rust_static_library`: configs.
30 # Here, you must specify `executable_configs` or `library_configs` depending on
31 # the type of thing you're generating. This is so that different defaults can
34 template("rust_target") {
35 _target_name = target_name
36 _crate_name = target_name
37 if (defined(invoker.crate_name)) {
38 _crate_name = invoker.crate_name
40 _generate_crate_root =
41 defined(invoker.generate_crate_root) && invoker.generate_crate_root
43 # Only one of `crate_root` or `generate_crate_root` can be specified, or
45 assert(!defined(invoker.crate_root) || !_generate_crate_root)
47 if (defined(invoker.output_dir) && invoker.output_dir != "") {
48 # This is where the build target (.exe, .rlib, etc) goes.
49 _output_dir = invoker.output_dir
52 # This is where the OUT_DIR environment variable points to when running a
53 # build script and when compiling the build target, for consuming generated
55 _env_out_dir = "$target_gen_dir/$_target_name"
58 if (defined(invoker.allow_unsafe)) {
59 _allow_unsafe = invoker.allow_unsafe
62 if (_generate_crate_root) {
63 generated_file("${_target_name}_crate_root") {
64 outputs = [ "${target_gen_dir}/${target_name}.rs" ]
66 "// Generated crate root for ${_target_name}.",
70 foreach(rs, invoker.sources) {
71 rs_path_from_root = rebase_path(rs, target_gen_dir)
72 contents += [ "#[path = \"${rs_path_from_root}\"]" ]
74 # Drop the file extension from the module name.
75 rs_modname = string_replace(rs, ".rs", "")
77 # Replace invalid "/" chars in the source file path.
78 rs_modname = string_replace(rs_modname, "/", "_")
80 # Since source files are specified relative to the BUILD.gn they may
81 # also have ".." path components.
82 rs_modname = string_replace(rs_modname, "..", "dotdot")
89 _generated_crate_root = get_target_outputs(":${_target_name}_crate_root")
90 _crate_root = _generated_crate_root[0]
91 } else if (defined(invoker.crate_root)) {
92 _crate_root = invoker.crate_root
93 } else if (invoker.target_type == "executable") {
94 _crate_root = "src/main.rs"
96 _crate_root = "src/lib.rs"
100 if (defined(invoker.testonly)) {
101 _testonly = invoker.testonly
103 if (defined(invoker.visibility)) {
104 _visibility = invoker.visibility
108 if (defined(invoker.rustflags)) {
109 _rustflags += invoker.rustflags
111 if (defined(invoker.features)) {
112 foreach(i, invoker.features) {
113 _rustflags += [ "--cfg=feature=\"${i}\"" ]
117 if (defined(invoker.edition)) {
118 _edition = invoker.edition
121 assert(!defined(configs))
122 _configs = [ "//build/rust:edition_${_edition}" ]
124 if (invoker.target_type == "executable") {
125 _configs += invoker.executable_configs
126 } else if (invoker.target_type == "rust_proc_macro") {
127 _configs += invoker.proc_macro_configs
128 _test_configs += [ "//build/rust:proc_macro_extern" ]
129 } else if (invoker.target_type == "shared_library") {
130 _configs += invoker.shared_library_configs
132 _configs += invoker.library_configs
135 if (invoker.target_type == "rust_proc_macro") {
136 _main_target_suffix = "${target_name}__proc_macro"
138 _main_target_suffix = "__rlib"
142 if (defined(invoker.deps)) {
143 _deps += invoker.deps
146 if (defined(invoker.public_deps)) {
147 _public_deps += invoker.public_deps
149 if (defined(invoker.aliased_deps)) {
150 _aliased_deps = invoker.aliased_deps
156 _is_data_dep = defined(invoker.is_data_dep) && invoker.is_data_dep
158 _build_unit_tests = false
159 if (defined(invoker.build_native_rust_unit_tests)) {
161 invoker.build_native_rust_unit_tests && can_build_rust_unit_tests
164 # Declares that the Rust crate generates bindings between C++ and Rust via the
165 # Cxx crate. It may generate C++ headers and/or use the cxx crate macros to
166 # generate Rust code internally, depending on what bindings are declared. If
167 # set, it's a set of rust files that include Cxx bindings declarations.
169 assert(!defined(invoker.cxx_bindings) || enable_cxx,
170 "cxx bindings are not supported when building rust targets " +
171 "outside the Chromium build.")
172 if (defined(invoker.cxx_bindings)) {
173 _cxx_bindings = invoker.cxx_bindings
175 _rustenv = [ "OUT_DIR=" +
176 rebase_path(_env_out_dir, get_path_info(_crate_root, "dir")) ]
177 if (defined(invoker.rustenv)) {
178 _rustenv += invoker.rustenv
181 # We require that all source files are listed, even though this is
182 # not a requirement for rustc. The reason is to ensure that tools
183 # such as `gn deps` give the correct answer, and thus we trigger
184 # the right test suites etc. on code change.
185 # TODO(crbug.com/1256930) - verify this is correct
186 assert(defined(invoker.sources), "sources must be listed")
188 if (invoker.target_type == "rust_proc_macro" &&
189 !toolchain_for_rust_host_build_tools) {
190 # Redirect to the proc macro toolchain, which uses prebuilt stdlib libraries
191 # that are not built with panic=abort.
192 group(_target_name) {
194 if (defined(_visibility)) {
195 visibility = _visibility
198 [ ":${_target_name}${_main_target_suffix}($rust_macro_toolchain)" ]
201 not_needed(invoker, "*")
216 "_support_use_from_cpp",
222 if (defined(invoker.rustc_metadata)) {
223 _rustc_metadata = invoker.rustc_metadata
226 # Add a metadata-influenced suffix to the output name for libraries only.
228 if (invoker.target_type == "rust_library" && _rustc_metadata != "") {
229 _output_suffix = "-${_rustc_metadata}"
232 group(_target_name) {
234 if (defined(_visibility)) {
235 visibility = _visibility
238 # Both the C++ bindings (if present) and the Rust crate should be treated
239 # like direct dependencies, so we expose them both in public_deps.
240 public_deps = [ ":${_target_name}${_main_target_suffix}" ]
242 # TODO(danakj): This would not be needed if we stopped forwarding through
243 # a group in the common (non-procmacro) case.
245 data_deps = [ ":${_target_name}${_main_target_suffix}" ]
248 if (_cxx_bindings != []) {
249 public_deps += [ ":${_target_name}_cxx_generated" ]
251 # Additionally, C++ bindings generated by Cxx can include C++ types
252 # that come from the Cxx library, such as `rust::Str`. So any C++
253 # target that depends on a rust target directly may need access to Cxx
254 # as well, which means it must appear in public_deps.
255 public_deps += [ "//build/rust:cxx_cppdeps" ]
256 } else if (!defined(invoker.no_std) || !invoker.no_std) {
257 # If C++ depends on and links in the library, we need to make sure C++
258 # links in the Rust stdlib. This is orthogonal to if the library exports
259 # bindings for C++ to use.
260 deps = [ "//build/rust/std:stdlib_for_clang" ]
265 _rust_aliased_deps = _aliased_deps
266 _rust_public_deps = _public_deps
269 # The Rust target (and unit tests) need the Cxx crate when using it to
271 if (_cxx_bindings != []) {
272 _rust_deps += [ "//build/rust:cxx_rustdeps" ]
274 # C++ targets can depend on the Rust target from the BUILD.gn file to
275 # access the headers generated from it
276 _rust_public_deps += [ ":${_target_name}_cxx_generated" ]
279 if (!defined(invoker.no_std) || !invoker.no_std) {
280 _rust_deps += [ "//build/rust/std:stdlib_for_rustc" ]
283 # You must go through the groups above to get to these targets.
285 _visibility = [ ":${_target_name}" ]
287 if (_build_unit_tests) {
288 _unit_test_target = "${_target_name}_unittests"
289 if (defined(invoker.unit_test_target)) {
290 _unit_test_target = invoker.unit_test_target
293 rust_unit_test(_unit_test_target) {
295 crate_root = _crate_root
296 sources = invoker.sources + [ crate_root ]
297 rustflags = _rustflags
298 env_out_dir = _env_out_dir
299 if (defined(invoker.unit_test_output_dir)) {
300 output_dir = invoker.unit_test_output_dir
302 deps = _rust_deps + _public_deps
303 aliased_deps = _rust_aliased_deps
304 public_deps = [ ":${_target_name}" ]
305 if (defined(invoker.test_deps)) {
306 deps += invoker.test_deps
309 if (defined(invoker.inputs)) {
310 inputs += invoker.inputs
312 if (defined(invoker.test_inputs)) {
313 inputs += invoker.test_inputs
315 if (defined(invoker.executable_configs)) {
317 configs += invoker.executable_configs
319 configs += _test_configs
322 if (!_allow_unsafe) {
323 configs += [ "//build/rust:forbid_unsafe" ]
333 not_needed(invoker, [ "executable_configs" ])
336 target(invoker.target_type, "${_target_name}${_main_target_suffix}") {
337 forward_variables_from(invoker,
339 TESTONLY_AND_VISIBILITY + [
347 "unit_test_output_dir",
353 visibility = _visibility
354 crate_name = _crate_name
355 crate_root = _crate_root
359 aliased_deps = _rust_aliased_deps
360 public_deps = _rust_public_deps
361 rustflags = _rustflags
362 if (_rustc_metadata != "") {
363 rustflags += [ "-Cmetadata=${_rustc_metadata}" ]
367 if (_generate_crate_root) {
368 deps += [ ":${_target_name}_crate_root" ]
369 sources += [ _crate_root ]
372 # The Rust tool() declarations, like C++ ones, use the output_name and
373 # output_dir, so that GN targets can override these if needed. Here we
374 # give them their default values, or allow them to be overridden.
375 if (defined(_output_dir)) {
376 output_dir = _output_dir
378 if (!defined(output_name) || output_name == "") {
379 output_name = "${crate_name}${_output_suffix}"
382 if (!_allow_unsafe) {
383 configs += [ "//build/rust:forbid_unsafe" ]
387 if (_cxx_bindings != []) {
388 rust_cxx("${_target_name}_cxx_generated") {
390 visibility = [ ":${_target_name}${_main_target_suffix}" ]
391 if (defined(_visibility)) {
392 visibility += _visibility
394 sources = _cxx_bindings
395 deps = _cxx_deps + _public_deps
398 if (is_component_build) {
399 # In a component_build the cxx bindings may be linked into a shared
400 # library at any point up the dependency tree, so always export.
401 export_symbols = true
402 } else if (invoker.target_type == "shared_library") {
403 export_symbols = true
405 export_symbols = false
409 not_needed([ "_cxx_deps" ])