xref: /aosp_15_r20/external/cronet/build/rust/rust_static_library.gni (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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.
4
5import("//build/rust/rust_target.gni")
6
7# Defines a Rust static library which can be used by downstream Rust or C++
8# targets. This is a single Rust compilation unit consisting of potentially
9# multiple .rs files.
10#
11# We term this 'rust_static_library' because it is used most analogously
12# to a C++ 'static_library' in Chromium. Like the C++ one, it can be compiled
13# independently into an intermediate linking target. The output contains the
14# object file(s) of the GN target's sources, and not its dependencies.
15#
16# Parameters
17#
18#   sources
19#     List of source files which this crate is allowed to compile, which is
20#     used to determine the impact of source code changes on other GN targets.
21#     This is not used by the Rust compiler, as it discovers source files by
22#     following `mod` declarations starting at the `crate_root`. The
23#     discovered source files must match this list. (This is not yet enforced,
24#     but will be.)
25#
26#   edition (optional)
27#     Edition of the Rust language to be used.
28#     Options are "2015", "2018" and "2021". Defaults to "2021".
29#
30#   allow_unsafe (optional)
31#     Set to true to allow unsafe code in this target. Defaults to false.
32#
33#   configs (optional)
34#     A list of config labels (in the GN meaning) applying to this target.
35#
36#   rustflags (optional)
37#     Explicit flags for rustc command line. (Use 'edition' or 'features'
38#     where possible).
39#
40#   deps (optional)
41#     List of GN targets on which this crate depends. These may be Rust
42#     or non-Rust targets.
43#
44#   public_deps (optional)
45#     List of GN targets on which this crate depends, and which are exported
46#     into the dependency list of any crate that depends on it. Dependency
47#     crates that appear in the public API should be included here.
48#
49#   test_deps (optional)
50#     List of GN targets on which this crate's tests depend, in addition
51#     to deps.
52#
53#   is_gtest_unittests (optional)
54#     Should only be set to true for rlibs of gtest unit tests. This ensures
55#     all objects in the rlib are linked into the final target, rather than
56#     pruning dead code, so that the tests themselves are not discarded by the
57#     linker.
58#
59#   mutually_dependent_target (optional)
60#   mutually_dependent_public_deps (optional)
61#     These is for use by the mixed_target() template.
62#
63#     If this Rust code is intrinsically paired with some C/C++ code,
64#     with bidirectional calls between the two, then this would
65#     be a circular dependency. GN does not allow circular dependencies,
66#     (other than for header files per allow_circular_includes_from).
67#     But this is common for a 'component' which has both Rust and C++
68#     code. You should structure things such that the C++ code depends
69#     on the Rust code in the normal way:
70#        static_library("cpp_stuff") {
71#          deps = [ "rust_stuff" ]
72#          # ..
73#        }
74#     but that the Rust target also notes the C++ target using this
75#     'mutually_dependent_target' parameter.
76#        rust_static_library("rust_stuff") {
77#          mutually_dependent_target = "cpp_stuff"
78#          mutually_dependent_public_deps = _cpp_stuff_public_deps
79#          # ..
80#        }
81#
82#     This causes the Rust unit tests, if generated, to depend on the mutually
83#     dependent target, since depending on the Rust code only would be
84#     insufficient. And it allows any C++ bindings generated from the Rust code
85#     to include headers from the mutually_dependent_target by depending on its
86#     public_deps.
87#
88#   build_native_rust_unit_tests (optional)
89#     Builds native unit tests (under #[cfg(test)]) written inside the Rust
90#     crate. This will create a `<name>_unittests` executable in the output
91#     directory when set to true.
92#
93#   unit_test_target (optional)
94#     Overrides the default name for the unit tests target
95#
96#   crate_root (optional)
97#     Location of the crate root.
98#     This defaults to `./src/lib.rs` and should only be changed when
99#     absolutely necessary (such as in the case of generated code).
100#
101#   features (optional)
102#     A list of conditional compilation flags to enable. This can be used
103#     to set features for crates built in-tree which are also published to
104#     crates.io. Each feature in the list will be passed to rustc as
105#     '--cfg feature=XXX'
106#
107#   cxx_bindings (optional)
108#     A list of Rust files which contain #[cxx::bridge] mods and should
109#     therefore have C++ bindings generated. See https://cxx.rs.
110#     This will automatically add appropriate dependencies: there's no
111#     need to depend on the cxx crate or any generated bindings.
112#
113#   data, testonly, visibility (optional)
114#     Same meaning as in other GN targets.
115#
116#   crate_name (optional)
117#     Discouraged - first-party code should prefer to use the auto-generated,
118#     target-based crate name which is guaranteed to be globally unique (and
119#     still ergonomic to import and use via the `chromium::import!` macro).
120#
121#   inputs (optional)
122#     Additional input files needed for compilation (such as `include!`ed files)
123#
124#   test_inputs (optional)
125#     Same as above but for the unit tests target
126#
127#   rustc_metadata (optional)
128#     Override the metadata identifying the target's version. This allows e.g.
129#     linking otherwise conflicting versions of the same Rust library. The
130#     metadata string will also be appended to the output library names.
131#     Should be used sparingly, since generally we should have one version and
132#     build of each target.
133#
134# Example of usage:
135#
136#   rust_static_library("foo_bar") {
137#     deps = [
138#       "//boo/public/rust/bar",
139#       "//third_party/rust/crates:argh",
140#       "//third_party/rust/crates:serde",
141#       "//third_party/rust/crates:slab",
142#     ]
143#     sources = [ "src/lib.rs" ]
144#   }
145#
146# This template is intended to serve the same purpose as 'rustc_library'
147# in Fuchsia.
148template("rust_static_library") {
149  _target_name = target_name
150
151  _configs = []
152  _all_dependent_configs = []
153
154  if (defined(invoker.configs)) {
155    _configs += invoker.configs
156  }
157
158  # TODO(dcheng): Is there any value in allowing rust_shared_library() to also
159  # set `is_gtest_unittest` to true?
160  if (defined(invoker.is_gtest_unittests) && invoker.is_gtest_unittests) {
161    _output_dir = rebase_path(target_out_dir, root_build_dir)
162
163    config("${_target_name}_alwayslink") {
164      # Targets using the #[gtest(...)] proc macro register their tests with
165      # static initializers. A rust library target generates a .rlib, which is
166      # a regular static library with extra metadata. However, if nothing
167      # outside the .rlib references functions in the .rlib—as is often the
168      # case with a target containing only tests—the linker will not pull in
169      # the .rlib into the final test binary at all, so the tests won't run.
170      #
171      # C++ works around this by using source sets and directly pass the .o
172      # files to the linker, but Rust does not have a parallel to this. Instead,
173      # force the linker to always include the whole archive.
174
175      # NOTE: TargetName=>CrateName mangling algorithm should be updated
176      # simultaneously in 3 places: here, //build/rust/rust_target.gni,
177      # //build/rust/chromium_prelude/import_attribute.rs
178      if (defined(invoker.crate_name)) {
179        _crate_name = invoker.crate_name
180      } else {
181        # Not using `get_label_info(..., "label_no_toolchain")` to consistently
182        # use `//foo/bar:baz` instead of the alternative shorter `//foo/bar` form.
183        _dir = get_label_info(":${_target_name}", "dir")
184        _dir = string_replace(_dir, "//", "")
185        _crate_name = "${_dir}:${_target_name}"
186
187        # The `string_replace` calls below replicate the escaping algorithm
188        # from the `escape_non_identifier_chars` function in
189        # //build/rust/chromium_prelude/import_attribute.rs.  Note that the
190        # ordering of `match` branches within the Rust function doesn't matter,
191        # but the ordering of `string_replace` calls *does* matter - the escape
192        # character `_` needs to be handled first to meet the injectivity
193        # requirement (otherwise we would get `/` => `_s` => `_us` and the same
194        # result for `_s` => `_us`).
195        _crate_name = string_replace(_crate_name, "_", "_u")
196        _crate_name = string_replace(_crate_name, "/", "_s")
197        _crate_name = string_replace(_crate_name, ":", "_c")
198        _crate_name = string_replace(_crate_name, "-", "_d")
199      }
200
201      _rlib_path = "${_output_dir}/lib${_crate_name}.rlib"
202      if (current_os == "aix") {
203        # The AIX linker does not implement an option for this.
204      } else if (is_win) {
205        ldflags = [ "/WHOLEARCHIVE:${_rlib_path}" ]
206      } else {
207        ldflags = [ "-LinkWrapper,add-whole-archive=${_rlib_path}" ]
208      }
209    }
210    _all_dependent_configs += [ ":${target_name}_alwayslink" ]
211    _configs += [ "//build/rust:is_gtest_unittests" ]
212  }
213
214  rust_target(_target_name) {
215    forward_variables_from(invoker,
216                           "*",
217                           TESTONLY_AND_VISIBILITY + [ "configs" ])
218    forward_variables_from(invoker, TESTONLY_AND_VISIBILITY)
219    library_configs = _configs
220    all_dependent_configs = _all_dependent_configs
221    target_type = "rust_library"
222  }
223}
224
225set_defaults("rust_static_library") {
226  configs = default_compiler_configs
227}
228