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