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