| # 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_cxx.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 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}\"]" ] |
| rs_modname = string_replace(string_replace(rs, "/", "_"), ".rs", "") |
| 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 crate. 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 bindings declarations. |
| _cxx_bindings = [] |
| if (defined(invoker.cxx_bindings)) { |
| _cxx_bindings = invoker.cxx_bindings |
| } |
| |
| # 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", |
| "_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}_cxx" ] |
| |
| # 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 |
| _cxx_deps = _deps |
| |
| # The Rust target (and unit tests) need the Cxx crate when using it to |
| # generate bindings. |
| if (_cxx_bindings != []) { |
| _rust_deps += [ "//build/rust:cxx_rustdeps" ] |
| } |
| |
| # 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 = _public_deps |
| rustflags = _rustflags |
| rustflags += [ string_join("", |
| [ |
| "-Cmetadata=", |
| _metadata, |
| ]) ] |
| rustenv = [ "OUT_DIR=" + rebase_path(_out_dir) ] |
| if (defined(invoker.rustenv)) { |
| rustenv += invoker.rustenv |
| } |
| |
| # 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_cxx("${_target_name}_cxx") { |
| testonly = _testonly |
| visibility = _visibility |
| inputs = _cxx_bindings |
| deps = _cxx_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)) { |
| deps += _mutually_dependent_public_deps |
| } |
| 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([ |
| "_cxx_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 |
| } |
| } |
| } 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 |
| } |