xref: /aosp_15_r20/external/cronet/build/rust/std/BUILD.gn (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
5# This file provides the Rust standard library for GN targets.
6#
7# For Rust targets, it either copies a prebuilt stdlib or builds a stdlib, and
8# then points rustc to it with `--sysroot`.
9#
10# When linking it ensures the libraries (and their C library dependencies) are
11# part of the linker line. If Rust drives the linking, this is redundant but if
12# Clang drives the linking it is required.
13#
14# Part of the standard library provided here is "remap_alloc" which maps
15# allocator functions into the shim provided by PartitionAlloc so that Rust and
16# C++ use the same allocator backend.
17
18import("//build/config/compiler/compiler.gni")
19import("//build/config/coverage/coverage.gni")
20import("//build/config/rust.gni")
21import("//build/config/sanitizers/sanitizers.gni")
22
23if (toolchain_has_rust) {
24  # Equivalent of allocator symbols injected by rustc.
25  source_set("remap_alloc") {
26    sources = [
27      # `alias.*`, `compiler_specific.h`, and `immediate_crash.*` have been
28      # copied from `//base`.
29      # TODO(https://crbug.com/1475734): Avoid duplication / reuse code.
30      "alias.cc",
31      "alias.h",
32      "compiler_specific.h",
33      "immediate_crash.h",
34      "remap_alloc.cc",
35    ]
36  }
37
38  # List of Rust stdlib rlibs which are present in the official Rust toolchain
39  # we are using from the Android team. This is usually a version or two behind
40  # nightly. Generally this matches the toolchain we build ourselves, but if
41  # they differ, append or remove libraries based on the
42  # `use_chromium_rust_toolchain` GN variable.
43  #
44  # If the build fails due to missing symbols, it would be because of a missing
45  # library that needs to be added here in a newer stdlib.
46  stdlib_files = [
47    "std",  # List first because it makes depfiles more debuggable (see below)
48    "alloc",
49    "cfg_if",
50    "compiler_builtins",
51    "core",
52    "getopts",
53    "hashbrown",
54    "libc",
55    "panic_abort",
56    "panic_unwind",
57    "rustc_demangle",
58    "std_detect",
59    "test",
60    "unicode_width",
61    "unwind",
62  ]
63
64  if (!is_win) {
65    # These are no longer present in the Windows toolchain.
66    stdlib_files += [
67      "addr2line",
68      "adler",
69      "gimli",
70      "memchr",
71      "miniz_oxide",
72      "object",
73    ]
74  }
75
76  if (toolchain_for_rust_host_build_tools) {
77    # When building proc macros, include the proc_macro crate in what should be
78    # copied with find_stdlib. Otherwise it is not copied since it will be
79    # unused.
80    stdlib_files += [ "proc_macro" ]
81  }
82
83  # Different Rust toolchains may add or remove files relative to the above
84  # list. That can be specified in gn args for anyone using (for instance)
85  # nightly or some other experimental toolchain, prior to it becoming official.
86  stdlib_files -= removed_rust_stdlib_libs
87  stdlib_files += added_rust_stdlib_libs
88
89  # rlib files which are distributed alongside Rust's prebuilt stdlib, but we
90  # don't need to pass to the C++ linker because they're used for specialized
91  # purposes.
92  skip_stdlib_files = [
93    "profiler_builtins",
94    "rustc_std_workspace_alloc",
95    "rustc_std_workspace_core",
96    "rustc_std_workspace_std",
97  ]
98
99  config("stdlib_dependent_libs") {
100    # TODO(crbug.com/1434092): These should really be `libs`, however that
101    # breaks. Normally, we specify lib files with the `.lib` suffix but
102    # then when rustc links an EXE, it invokes lld-link with `.lib.lib`
103    # instead.
104    #
105    # Omitting the `.lib` suffix breaks linking as well, when clang drives
106    # the linking step of a C++ EXE that depends on Rust.
107    if (is_win) {
108      # The libc crate tries to link in the Windows CRT, but we specify the CRT
109      # library ourselves in //build/config/win:dynamic_crt and
110      # //build/config/win:static_crt because Rustc does not allow us to specify
111      # using the debug CRT: https://github.com/rust-lang/rust/issues/39016
112      #
113      # As such, we have disabled all #[link] directives from the libc crate,
114      # and we need to add any non-CRT libs here.
115      ldflags = [ "legacy_stdio_definitions.lib" ]
116    }
117  }
118  config("stdlib_public_dependent_libs") {
119    # TODO(crbug.com/1434092): These should really be `libs`, however that
120    # breaks. Normally, we specify lib files with the `.lib` suffix but
121    # then when rustc links an EXE, it invokes lld-link with `.lib.lib`
122    # instead.
123    #
124    # Omitting the `.lib` suffix breaks linking as well, when clang drives
125    # the linking step of a C++ EXE that depends on Rust.
126    if (is_win) {
127      # These libs provide functions that are used by the stdlib. Rust crates
128      # will try to link them in with #[link] directives. However these don't
129      # get propagated to the linker if Rust isn't driving the linking (a C++
130      # target that depends on a Rust rlib). So these need to be specified
131      # explicitly.
132      ldflags = [
133        "advapi32.lib",
134        "bcrypt.lib",
135        "kernel32.lib",
136        "ntdll.lib",
137        "synchronization.lib",
138        "userenv.lib",
139        "ws2_32.lib",
140      ]
141    }
142
143    # From rust/library/std/src/sys/unix/mod.rs.
144    # TODO(danakj): We should generate this list somehow when building or rolling
145    # the Rust toolchain?
146    if (is_android) {
147      libs = [ "dl" ]
148    } else if (target_os == "freebsd") {
149      libs = [
150        "execinfo",
151        "pthread",
152      ]
153    } else if (target_os == "netbsd") {
154      libs = [
155        "rt",
156        "pthread",
157      ]
158    } else if (is_mac) {
159      libs = [ "System" ]
160    } else if (is_ios) {
161      libs = [
162        "System",
163        "objc",
164      ]
165      frameworks = [
166        "Security.framework",
167        "Foundation.framework",
168      ]
169    } else if (is_fuchsia) {
170      libs = [
171        "zircon",
172        "fdio",
173      ]
174    }
175  }
176
177  # Construct sysroots for rustc invocations to better control what libraries
178  # are linked. We have two: one with copied prebuilt libraries, and one with
179  # our locally-built std. Both reside in root_out_dir: we must only have one of
180  # each per GN toolchain anyway.
181
182  sysroot_lib_subdir = "lib/rustlib/$rust_abi_target/lib"
183
184  if (!rust_prebuilt_stdlib) {
185    local_rustc_sysroot = "$root_out_dir/local_rustc_sysroot"
186
187    # All std targets starting with core build with our sysroot. It starts empty
188    # and is incrementally built. The directory must exist at the start.
189    generated_file("empty_sysroot_for_std_build") {
190      outputs = [ "$local_rustc_sysroot/$sysroot_lib_subdir/.empty" ]
191      contents = ""
192      visibility = [ ":*" ]
193    }
194
195    # Target to be depended on by std build targets. Creates the initially empty
196    # sysroot.
197    group("std_build_deps") {
198      deps = [ ":empty_sysroot_for_std_build" ]
199      public_configs = [ ":local_stdlib_sysroot" ]
200      visibility = [ "rules:*" ]
201    }
202
203    profiler_builtins_crates = [
204      "core",
205      "compiler_builtins",
206      "profiler_builtins",
207    ]
208
209    # When using instrumentation, profiler_builtins and its deps must be built
210    # before other std crates. Other crates depend on this target so they are
211    # built in the right order.
212    group("profiler_builtins_group") {
213      deps = []
214      foreach(libname, profiler_builtins_crates) {
215        deps += [ "rules:$libname" ]
216      }
217      visibility = [ "rules:*" ]
218    }
219
220    config("local_stdlib_sysroot") {
221      sysroot = rebase_path(local_rustc_sysroot, root_build_dir)
222      rustflags = [ "--sysroot=$sysroot" ]
223      visibility = [ ":*" ]
224    }
225
226    # When given -Zsanitize=..., rustc insists on passing a sanitizer runtime to
227    # the linker it invokes. Unfortunately, our C++ ldflags already tell the
228    # linker to link against a C++ sanitizer runtime - which contains the same
229    # symbols. So, make a blank library.
230    # The list of relevant sanitizers here is taken from
231    # https://github.com/rust-lang/rust/blob/7e7483d26e3cec7a44ef00cf7ae6c9c8c918bec6/compiler/rustc_codegen_ssa/src/back/link.rs#L1148
232    template("rustc_sanitizer_runtime") {
233      rt_name = target_name
234      not_needed([ "invoker" ])
235      static_library("sanitizer_rt_$rt_name") {
236        sources = []
237        output_name = "librustc-${rust_channel}_rt.$rt_name"
238        output_dir = "$local_rustc_sysroot/$sysroot_lib_subdir"
239        if (is_win) {
240          arflags = [ "/llvmlibempty" ]
241        }
242      }
243    }
244    rustc_sanitizer_runtimes = []
245    if (is_asan) {
246      rustc_sanitizer_runtime("asan") {
247      }
248      rustc_sanitizer_runtimes += [ ":sanitizer_rt_asan" ]
249    }
250    if (is_lsan) {
251      rustc_sanitizer_runtime("lsan") {
252      }
253      rustc_sanitizer_runtimes += [ ":sanitizer_rt_lsan" ]
254    }
255    if (is_msan) {
256      rustc_sanitizer_runtime("msan") {
257      }
258      rustc_sanitizer_runtimes += [ ":sanitizer_rt_msan" ]
259    }
260    if (is_tsan) {
261      rustc_sanitizer_runtime("tsan") {
262      }
263      rustc_sanitizer_runtimes += [ ":sanitizer_rt_tsan" ]
264    }
265    if (is_hwasan) {
266      rustc_sanitizer_runtime("hwasan") {
267      }
268      rustc_sanitizer_runtimes += [ ":sanitizer_rt_hwasan" ]
269    }
270
271    group("local_stdlib_libs") {
272      assert(toolchain_has_rust,
273             "Some C++ target is depending on Rust code even though " +
274                 "toolchain_has_rust=false. Usually this would mean" +
275                 "a NaCl target is depending on Rust, as there's no Rust " +
276                 "toolchain targetting NaCl.")
277      all_dependent_configs = [ ":stdlib_dependent_libs" ]
278      deps = []
279      foreach(libname, stdlib_files + skip_stdlib_files) {
280        deps += [ "rules:$libname" ]
281      }
282      deps += rustc_sanitizer_runtimes
283      visibility = [ ":*" ]
284    }
285
286    # Builds and links against the Rust stdlib. Used by targets for which
287    # linking is driven by C++.
288    group("std") {
289      all_dependent_configs = [
290        ":stdlib_public_dependent_libs",
291        ":local_stdlib_sysroot",
292      ]
293      public_deps = [
294        ":local_stdlib_libs",
295        ":remap_alloc",
296      ]
297    }
298  } else {
299    action("find_stdlib") {
300      # Collect prebuilt Rust libraries from toolchain package and copy to a
301      # known location.
302      #
303      # The Rust toolchain contains prebuilt rlibs for the standard library and
304      # its dependencies. However, they have unstable names: an unpredictable
305      # metadata hash is appended to the known crate name.
306      #
307      # We must depend on these rlibs explicitly when rustc is not in charge of
308      # linking. However, it is difficult to construct GN rules to do so when
309      # the names can't be known statically.
310      #
311      # This action copies the prebuilt rlibs to a known location, removing the
312      # metadata part of the name. In the process it verifies we have all the
313      # libraries we expect and none that we don't. A depfile is generated so
314      # this step is re-run when any libraries change. The action script
315      # additionally verifies rustc matches the expected version, which is
316      # unrelated but this is a convenient place to do so.
317      #
318      # The action refers to `stdlib_files`, `skip_stdlib_files`, and the
319      # associated //build/config/rust.gni vars `removed_rust_stdlib_libs` and
320      # `added_rust_stdlib_libs` for which rlib files to expect.
321      # `extra_sysroot_libs` is also used to copy non-std libs, if any.
322      script = "find_std_rlibs.py"
323      depfile = "$target_out_dir/stdlib.d"
324      out_libdir = rebase_path(target_out_dir, root_build_dir)
325      out_depfile = rebase_path(depfile, root_build_dir)
326
327      # For the rustc sysroot we must include even the rlibs we don't pass to
328      # the C++ linker.
329      all_stdlibs_to_copy = stdlib_files + skip_stdlib_files
330      args = [
331        "--rust-bin-dir",
332        rebase_path("${rust_sysroot}/bin", root_build_dir),
333        "--output",
334        out_libdir,
335        "--depfile",
336        out_depfile,
337
338        # Due to limitations in Ninja's handling of .d files, we have to pick
339        # *the first* of our outputs. To make diagnostics more obviously
340        # related to the Rust standard library, we ensure libstd.rlib is first.
341        "--depfile-target",
342        stdlib_files[0],
343
344        # Create a dependency on the rustc version so this action is re-run when
345        # it changes. This argument is not actually read by the script.
346        "--rustc-revision",
347        rustc_revision,
348      ]
349
350      if (extra_sysroot_libs != []) {
351        args += [
352          "--extra-libs",
353          string_join(",", extra_sysroot_libs),
354        ]
355      }
356
357      args += [
358        "--target",
359        rust_abi_target,
360      ]
361
362      outputs = []
363      foreach(lib, all_stdlibs_to_copy) {
364        outputs += [ "$target_out_dir/lib$lib.rlib" ]
365      }
366      foreach(lib, extra_sysroot_libs) {
367        outputs += [ "$target_out_dir/$lib" ]
368      }
369
370      visibility = [ ":*" ]
371    }
372
373    prebuilt_rustc_sysroot = "$root_out_dir/prebuilt_rustc_sysroot"
374    copy("prebuilt_rustc_copy_to_sysroot") {
375      assert(enable_rust,
376             "Some C++ target is including Rust code even though " +
377                 "enable_rust=false")
378      deps = [ ":find_stdlib" ]
379      sources = get_target_outputs(":find_stdlib")
380      outputs =
381          [ "$prebuilt_rustc_sysroot/$sysroot_lib_subdir/{{source_file_part}}" ]
382
383      visibility = [ ":*" ]
384    }
385
386    config("prebuilt_stdlib_sysroot") {
387      # Match the output directory of :prebuilt_rustc_copy_to_sysroot
388      sysroot = rebase_path(prebuilt_rustc_sysroot, root_build_dir)
389      rustflags = [ "--sysroot=$sysroot" ]
390      visibility = [ ":*" ]
391    }
392
393    config("prebuilt_stdlib_libs") {
394      ldflags = []
395      lib_dir = rebase_path("$prebuilt_rustc_sysroot/$sysroot_lib_subdir",
396                            root_build_dir)
397
398      # We're unable to make these files regular gn dependencies because
399      # they're prebuilt. Instead, we'll pass them in the ldflags. This doesn't
400      # work for all types of build because ldflags propagate differently from
401      # actual dependencies and therefore can end up in different targets from
402      # the remap_alloc.cc above. For example, in a component build, we might
403      # apply the remap_alloc.cc file and these ldlags to shared object A,
404      # while shared object B (that depends upon A) might get only the ldflags
405      # but not remap_alloc.cc, and thus the build will fail. There is
406      # currently no known solution to this for the prebuilt stdlib - this
407      # problem does not apply with configurations where we build the stdlib
408      # ourselves, which is what we'll use in production.
409      foreach(lib, stdlib_files) {
410        this_file = "$lib_dir/lib$lib.rlib"
411        ldflags += [ this_file ]
412      }
413      visibility = [ ":*" ]
414    }
415
416    group("std") {
417      all_dependent_configs = [
418        ":prebuilt_stdlib_libs",
419        ":stdlib_public_dependent_libs",
420      ]
421      deps = [ ":prebuilt_rustc_copy_to_sysroot" ]
422
423      # The host builds tools toolchain supports Rust only and does not use
424      # the allocator remapping to point it to PartitionAlloc.
425      if (!toolchain_for_rust_host_build_tools) {
426        deps += [ ":remap_alloc" ]
427      }
428    }
429  }
430}
431