# 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_target.gni") # Defines a Rust static library which can be used by downstream Rust or C++ # targets. This is a single Rust compilation unit consisting of potentially # multiple .rs files. # # We term this 'rust_static_library' because it is used most analogously # to a C++ 'static_library' in Chromium. Like the C++ one, it can be compiled # independently into an intermediate linking target. The output contains the # object file(s) of the GN target's sources, and not its dependencies. # # Parameters # # sources # List of source files which this crate is allowed to compile, which is # used to determine the impact of source code changes on other GN targets. # This is not used by the Rust compiler, as it discovers source files by # following `mod` declarations starting at the `crate_root`. The # discovered source files must match this list. (This is not yet enforced, # but will be.) # # edition (optional) # Edition of the Rust language to be used. # Options are "2015", "2018" and "2021". Defaults to "2021". # # allow_unsafe (optional) # Set to true to allow unsafe code in this target. Defaults to false. # # configs (optional) # A list of config labels (in the GN meaning) applying to this target. # # rustflags (optional) # Explicit flags for rustc command line. (Use 'edition' or 'features' # where possible). # # deps (optional) # List of GN targets on which this crate depends. These may be Rust # or non-Rust targets. # # public_deps (optional) # List of GN targets on which this crate depends, and which are exported # into the dependency list of any crate that depends on it. Dependency # crates that appear in the public API should be included here. # # test_deps (optional) # List of GN targets on which this crate's tests depend, in addition # to deps. # # is_gtest_unittests (optional) # Should only be set to true for rlibs of gtest unit tests. This ensures # all objects in the rlib are linked into the final target, rather than # pruning dead code, so that the tests themselves are not discarded by the # linker. # # mutually_dependent_target (optional) # mutually_dependent_public_deps (optional) # These is for use by the mixed_target() template. # # If this Rust code is intrinsically paired with some C/C++ code, # with bidirectional calls between the two, then this would # be a circular dependency. GN does not allow circular dependencies, # (other than for header files per allow_circular_includes_from). # But this is common for a 'component' which has both Rust and C++ # code. You should structure things such that the C++ code depends # on the Rust code in the normal way: # static_library("cpp_stuff") { # deps = [ "rust_stuff" ] # # .. # } # but that the Rust target also notes the C++ target using this # 'mutually_dependent_target' parameter. # rust_static_library("rust_stuff") { # mutually_dependent_target = "cpp_stuff" # mutually_dependent_public_deps = _cpp_stuff_public_deps # # .. # } # # This causes the Rust unit tests, if generated, to depend on the mutually # dependent target, since depending on the Rust code only would be # insufficient. And it allows any C++ bindings generated from the Rust code # to include headers from the mutually_dependent_target by depending on its # public_deps. # # build_native_rust_unit_tests (optional) # Builds native unit tests (under #[cfg(test)]) written inside the Rust # crate. This will create a `_unittests` executable in the output # directory when set to true. # # unit_test_target (optional) # Overrides the default name for the unit tests target # # crate_root (optional) # Location of the crate root. # This defaults to `./src/lib.rs` and should only be changed when # absolutely necessary (such as in the case of generated code). # # features (optional) # A list of conditional compilation flags to enable. This can be used # to set features for crates built in-tree which are also published to # crates.io. Each feature in the list will be passed to rustc as # '--cfg feature=XXX' # # cxx_bindings (optional) # A list of Rust files which contain #[cxx::bridge] mods and should # therefore have C++ bindings generated. See https://cxx.rs. # This will automatically add appropriate dependencies: there's no # need to depend on the cxx crate or any generated bindings. # # data, testonly, visibility (optional) # Same meaning as in other GN targets. # # crate_name (optional) # Discouraged - first-party code should prefer to use the auto-generated, # target-based crate name which is guaranteed to be globally unique (and # still ergonomic to import and use via the `chromium::import!` macro). # # inputs (optional) # Additional input files needed for compilation (such as `include!`ed files) # # test_inputs (optional) # Same as above but for the unit tests target # # rustc_metadata (optional) # Override the metadata identifying the target's version. This allows e.g. # linking otherwise conflicting versions of the same Rust library. The # metadata string will also be appended to the output library names. # Should be used sparingly, since generally we should have one version and # build of each target. # # Example of usage: # # rust_static_library("foo_bar") { # deps = [ # "//boo/public/rust/bar", # "//third_party/rust/crates:argh", # "//third_party/rust/crates:serde", # "//third_party/rust/crates:slab", # ] # sources = [ "src/lib.rs" ] # } # # This template is intended to serve the same purpose as 'rustc_library' # in Fuchsia. template("rust_static_library") { _target_name = target_name _configs = [] _all_dependent_configs = [] if (defined(invoker.configs)) { _configs += invoker.configs } # TODO(dcheng): Is there any value in allowing rust_shared_library() to also # set `is_gtest_unittest` to true? if (defined(invoker.is_gtest_unittests) && invoker.is_gtest_unittests) { _output_dir = rebase_path(target_out_dir, root_build_dir) config("${_target_name}_alwayslink") { # Targets using the #[gtest(...)] proc macro register their tests with # static initializers. A rust library target generates a .rlib, which is # a regular static library with extra metadata. However, if nothing # outside the .rlib references functions in the .rlib—as is often the # case with a target containing only tests—the linker will not pull in # the .rlib into the final test binary at all, so the tests won't run. # # C++ works around this by using source sets and directly pass the .o # files to the linker, but Rust does not have a parallel to this. Instead, # force the linker to always include the whole archive. # NOTE: TargetName=>CrateName mangling algorithm should be updated # simultaneously in 3 places: here, //build/rust/rust_target.gni, # //build/rust/chromium_prelude/import_attribute.rs if (defined(invoker.crate_name)) { _crate_name = invoker.crate_name } else { # Not using `get_label_info(..., "label_no_toolchain")` to consistently # use `//foo/bar:baz` instead of the alternative shorter `//foo/bar` form. _dir = get_label_info(":${_target_name}", "dir") _dir = string_replace(_dir, "//", "") _crate_name = "${_dir}:${_target_name}" # The `string_replace` calls below replicate the escaping algorithm # from the `escape_non_identifier_chars` function in # //build/rust/chromium_prelude/import_attribute.rs. Note that the # ordering of `match` branches within the Rust function doesn't matter, # but the ordering of `string_replace` calls *does* matter - the escape # character `_` needs to be handled first to meet the injectivity # requirement (otherwise we would get `/` => `_s` => `_us` and the same # result for `_s` => `_us`). _crate_name = string_replace(_crate_name, "_", "_u") _crate_name = string_replace(_crate_name, "/", "_s") _crate_name = string_replace(_crate_name, ":", "_c") _crate_name = string_replace(_crate_name, "-", "_d") } _rlib_path = "${_output_dir}/lib${_crate_name}.rlib" if (current_os == "aix") { # The AIX linker does not implement an option for this. } else if (is_win) { ldflags = [ "/WHOLEARCHIVE:${_rlib_path}" ] } else { ldflags = [ "-LinkWrapper,add-whole-archive=${_rlib_path}" ] } } _all_dependent_configs += [ ":${target_name}_alwayslink" ] _configs += [ "//build/rust:is_gtest_unittests" ] } rust_target(_target_name) { forward_variables_from(invoker, "*", TESTONLY_AND_VISIBILITY + [ "configs" ]) forward_variables_from(invoker, TESTONLY_AND_VISIBILITY) library_configs = _configs all_dependent_configs = _all_dependent_configs target_type = "rust_library" } } set_defaults("rust_static_library") { configs = default_compiler_configs }