]>
Commit | Line | Data |
---|---|---|
a7813a04 XL |
1 | //! Implementation of compiling various phases of the compiler and standard |
2 | //! library. | |
3 | //! | |
4 | //! This module contains some of the real meat in the rustbuild build system | |
5 | //! which is where Cargo is used to compiler the standard library, libtest, and | |
6 | //! compiler. This module is also responsible for assembling the sysroot as it | |
7 | //! goes along from the output of the previous stage. | |
8 | ||
0531ce1d | 9 | use std::borrow::Cow; |
5869c6ff | 10 | use std::collections::HashSet; |
7cac9316 | 11 | use std::env; |
0731742a | 12 | use std::fs; |
7cac9316 | 13 | use std::io::prelude::*; |
dfeec247 | 14 | use std::io::BufReader; |
7453a54e | 15 | use std::path::{Path, PathBuf}; |
dfeec247 | 16 | use std::process::{exit, Command, Stdio}; |
7cac9316 | 17 | use std::str; |
7453a54e | 18 | |
e1599b0c | 19 | use build_helper::{output, t, up_to_date}; |
9e0c209e | 20 | use filetime::FileTime; |
48663c56 | 21 | use serde::Deserialize; |
7453a54e | 22 | |
e1599b0c | 23 | use crate::builder::Cargo; |
f035d41b XL |
24 | use crate::builder::{Builder, Kind, RunConfig, ShouldRun, Step}; |
25 | use crate::cache::{Interned, INTERNER}; | |
3dfed10e | 26 | use crate::config::TargetSelection; |
dfeec247 | 27 | use crate::dist; |
0731742a | 28 | use crate::native; |
f035d41b | 29 | use crate::tool::SourceType; |
6a06907d | 30 | use crate::util::{exe, is_debug_info, is_dylib, symlink_dir}; |
f035d41b | 31 | use crate::{Compiler, DependencyType, GitRepo, Mode}; |
3b2f2976 | 32 | |
83c7162d | 33 | #[derive(Debug, PartialOrd, Ord, Copy, Clone, PartialEq, Eq, Hash)] |
3b2f2976 | 34 | pub struct Std { |
3dfed10e | 35 | pub target: TargetSelection, |
3b2f2976 XL |
36 | pub compiler: Compiler, |
37 | } | |
38 | ||
39 | impl Step for Std { | |
40 | type Output = (); | |
41 | const DEFAULT: bool = true; | |
42 | ||
9fa01778 | 43 | fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { |
6a06907d XL |
44 | // When downloading stage1, the standard library has already been copied to the sysroot, so |
45 | // there's no need to rebuild it. | |
46 | let download_rustc = run.builder.config.download_rustc; | |
47 | run.all_krates("test").default_condition(!download_rustc) | |
3b2f2976 XL |
48 | } |
49 | ||
9fa01778 | 50 | fn make_run(run: RunConfig<'_>) { |
3b2f2976 | 51 | run.builder.ensure(Std { |
1b1a35ee | 52 | compiler: run.builder.compiler(run.builder.top_stage, run.build_triple()), |
3b2f2976 XL |
53 | target: run.target, |
54 | }); | |
55 | } | |
56 | ||
9fa01778 | 57 | /// Builds the standard library. |
3b2f2976 XL |
58 | /// |
59 | /// This will build the standard library for a particular stage of the build | |
60 | /// using the `compiler` targeting the `target` architecture. The artifacts | |
61 | /// created will also be linked into the sysroot directory. | |
9fa01778 | 62 | fn run(self, builder: &Builder<'_>) { |
3b2f2976 XL |
63 | let target = self.target; |
64 | let compiler = self.compiler; | |
65 | ||
6a06907d XL |
66 | // These artifacts were already copied (in `impl Step for Sysroot`). |
67 | // Don't recompile them. | |
cdc7bbd5 XL |
68 | // NOTE: the ABI of the beta compiler is different from the ABI of the downloaded compiler, |
69 | // so its artifacts can't be reused. | |
70 | if builder.config.download_rustc && compiler.stage != 0 { | |
6a06907d XL |
71 | return; |
72 | } | |
73 | ||
1b1a35ee XL |
74 | if builder.config.keep_stage.contains(&compiler.stage) |
75 | || builder.config.keep_stage_std.contains(&compiler.stage) | |
76 | { | |
8faf50e0 | 77 | builder.info("Warning: Using a potentially old libstd. This may not behave well."); |
dfeec247 | 78 | builder.ensure(StdLink { compiler, target_compiler: compiler, target }); |
8faf50e0 XL |
79 | return; |
80 | } | |
81 | ||
e74abb32 | 82 | let mut target_deps = builder.ensure(StartupObjects { compiler, target }); |
3b2f2976 | 83 | |
dc9dc135 XL |
84 | let compiler_to_use = builder.compiler_for(compiler.stage, compiler.host, target); |
85 | if compiler_to_use != compiler { | |
dfeec247 | 86 | builder.ensure(Std { compiler: compiler_to_use, target }); |
dc9dc135 | 87 | builder.info(&format!("Uplifting stage1 std ({} -> {})", compiler_to_use.host, target)); |
3b2f2976 XL |
88 | |
89 | // Even if we're not building std this stage, the new sysroot must | |
0731742a XL |
90 | // still contain the third party objects needed by various targets. |
91 | copy_third_party_objects(builder, &compiler, target); | |
f035d41b | 92 | copy_self_contained_objects(builder, &compiler, target); |
3b2f2976 XL |
93 | |
94 | builder.ensure(StdLink { | |
dc9dc135 | 95 | compiler: compiler_to_use, |
3b2f2976 XL |
96 | target_compiler: compiler, |
97 | target, | |
98 | }); | |
99 | return; | |
100 | } | |
101 | ||
f035d41b XL |
102 | target_deps.extend(copy_third_party_objects(builder, &compiler, target)); |
103 | target_deps.extend(copy_self_contained_objects(builder, &compiler, target)); | |
3b2f2976 | 104 | |
f035d41b | 105 | let mut cargo = builder.cargo(compiler, Mode::Std, SourceType::InTree, target, "build"); |
f9f354fc | 106 | std_cargo(builder, target, compiler.stage, &mut cargo); |
0531ce1d | 107 | |
dfeec247 XL |
108 | builder.info(&format!( |
109 | "Building stage{} std artifacts ({} -> {})", | |
110 | compiler.stage, &compiler.host, target | |
111 | )); | |
112 | run_cargo( | |
113 | builder, | |
114 | cargo, | |
115 | vec![], | |
116 | &libstd_stamp(builder, compiler, target), | |
117 | target_deps, | |
118 | false, | |
119 | ); | |
3b2f2976 XL |
120 | |
121 | builder.ensure(StdLink { | |
83c7162d | 122 | compiler: builder.compiler(compiler.stage, builder.config.build), |
3b2f2976 XL |
123 | target_compiler: compiler, |
124 | target, | |
125 | }); | |
126 | } | |
127 | } | |
128 | ||
f035d41b XL |
129 | fn copy_and_stamp( |
130 | builder: &Builder<'_>, | |
131 | libdir: &Path, | |
132 | sourcedir: &Path, | |
133 | name: &str, | |
134 | target_deps: &mut Vec<(PathBuf, DependencyType)>, | |
135 | dependency_type: DependencyType, | |
136 | ) { | |
137 | let target = libdir.join(name); | |
138 | builder.copy(&sourcedir.join(name), &target); | |
139 | ||
140 | target_deps.push((target, dependency_type)); | |
141 | } | |
142 | ||
60c5eb7d | 143 | /// Copies third party objects needed by various targets. |
dfeec247 XL |
144 | fn copy_third_party_objects( |
145 | builder: &Builder<'_>, | |
146 | compiler: &Compiler, | |
3dfed10e | 147 | target: TargetSelection, |
f035d41b | 148 | ) -> Vec<(PathBuf, DependencyType)> { |
e74abb32 XL |
149 | let mut target_deps = vec![]; |
150 | ||
f035d41b XL |
151 | // FIXME: remove this in 2021 |
152 | if target == "x86_64-fortanix-unknown-sgx" { | |
153 | if env::var_os("X86_FORTANIX_SGX_LIBS").is_some() { | |
154 | builder.info("Warning: X86_FORTANIX_SGX_LIBS environment variable is ignored, libunwind is now compiled as part of rustbuild"); | |
155 | } | |
156 | } | |
157 | ||
29967ef6 | 158 | if builder.config.sanitizers_enabled(target) && compiler.stage != 0 { |
f035d41b XL |
159 | // The sanitizers are only copied in stage1 or above, |
160 | // to avoid creating dependency on LLVM. | |
161 | target_deps.extend( | |
162 | copy_sanitizers(builder, &compiler, target) | |
163 | .into_iter() | |
164 | .map(|d| (d, DependencyType::Target)), | |
165 | ); | |
166 | } | |
167 | ||
168 | target_deps | |
169 | } | |
170 | ||
171 | /// Copies third party objects needed by various targets for self-contained linkage. | |
172 | fn copy_self_contained_objects( | |
173 | builder: &Builder<'_>, | |
174 | compiler: &Compiler, | |
3dfed10e | 175 | target: TargetSelection, |
f035d41b | 176 | ) -> Vec<(PathBuf, DependencyType)> { |
3dfed10e | 177 | let libdir_self_contained = builder.sysroot_libdir(*compiler, target).join("self-contained"); |
f035d41b XL |
178 | t!(fs::create_dir_all(&libdir_self_contained)); |
179 | let mut target_deps = vec![]; | |
e74abb32 | 180 | |
f9f354fc | 181 | // Copies the CRT objects. |
0731742a | 182 | // |
f9f354fc XL |
183 | // rustc historically provides a more self-contained installation for musl targets |
184 | // not requiring the presence of a native musl toolchain. For example, it can fall back | |
185 | // to using gcc from a glibc-targeting toolchain for linking. | |
186 | // To do that we have to distribute musl startup objects as a part of Rust toolchain | |
187 | // and link with them manually in the self-contained mode. | |
0731742a | 188 | if target.contains("musl") { |
6a06907d XL |
189 | let srcdir = builder.musl_libdir(target).unwrap_or_else(|| { |
190 | panic!("Target {:?} does not have a \"musl-libdir\" key", target.triple) | |
191 | }); | |
f9f354fc | 192 | for &obj in &["crt1.o", "Scrt1.o", "rcrt1.o", "crti.o", "crtn.o"] { |
f035d41b XL |
193 | copy_and_stamp( |
194 | builder, | |
195 | &libdir_self_contained, | |
196 | &srcdir, | |
197 | obj, | |
198 | &mut target_deps, | |
199 | DependencyType::TargetSelfContained, | |
200 | ); | |
0731742a | 201 | } |
cdc7bbd5 | 202 | let crt_path = builder.ensure(native::CrtBeginEnd { target }); |
6a06907d | 203 | for &obj in &["crtbegin.o", "crtbeginS.o", "crtend.o", "crtendS.o"] { |
cdc7bbd5 | 204 | let src = crt_path.join(obj); |
6a06907d XL |
205 | let target = libdir_self_contained.join(obj); |
206 | builder.copy(&src, &target); | |
207 | target_deps.push((target, DependencyType::TargetSelfContained)); | |
208 | } | |
532ac7d7 | 209 | } else if target.ends_with("-wasi") { |
6a06907d XL |
210 | let srcdir = builder |
211 | .wasi_root(target) | |
212 | .unwrap_or_else(|| { | |
213 | panic!("Target {:?} does not have a \"wasi-root\" key", target.triple) | |
214 | }) | |
215 | .join("lib/wasm32-wasi"); | |
216 | for &obj in &["crt1-command.o", "crt1-reactor.o"] { | |
5869c6ff XL |
217 | copy_and_stamp( |
218 | builder, | |
219 | &libdir_self_contained, | |
220 | &srcdir, | |
221 | obj, | |
222 | &mut target_deps, | |
223 | DependencyType::TargetSelfContained, | |
224 | ); | |
225 | } | |
f035d41b XL |
226 | } else if target.contains("windows-gnu") { |
227 | for obj in ["crt2.o", "dllcrt2.o"].iter() { | |
228 | let src = compiler_file(builder, builder.cc(target), target, obj); | |
229 | let target = libdir_self_contained.join(obj); | |
230 | builder.copy(&src, &target); | |
231 | target_deps.push((target, DependencyType::TargetSelfContained)); | |
232 | } | |
dfeec247 XL |
233 | } |
234 | ||
e74abb32 | 235 | target_deps |
3b2f2976 XL |
236 | } |
237 | ||
238 | /// Configure cargo to compile the standard library, adding appropriate env vars | |
239 | /// and such. | |
3dfed10e | 240 | pub fn std_cargo(builder: &Builder<'_>, target: TargetSelection, stage: u32, cargo: &mut Cargo) { |
041b39d2 | 241 | if let Some(target) = env::var_os("MACOSX_STD_DEPLOYMENT_TARGET") { |
8bb4bdeb XL |
242 | cargo.env("MACOSX_DEPLOYMENT_TARGET", target); |
243 | } | |
244 | ||
dc9dc135 XL |
245 | // Determine if we're going to compile in optimized C intrinsics to |
246 | // the `compiler-builtins` crate. These intrinsics live in LLVM's | |
247 | // `compiler-rt` repository, but our `src/llvm-project` submodule isn't | |
248 | // always checked out, so we need to conditionally look for this. (e.g. if | |
249 | // an external LLVM is used we skip the LLVM submodule checkout). | |
250 | // | |
251 | // Note that this shouldn't affect the correctness of `compiler-builtins`, | |
252 | // but only its speed. Some intrinsics in C haven't been translated to Rust | |
253 | // yet but that's pretty rare. Other intrinsics have optimized | |
254 | // implementations in C which have only had slower versions ported to Rust, | |
255 | // so we favor the C version where we can, but it's not critical. | |
256 | // | |
257 | // If `compiler-rt` is available ensure that the `c` feature of the | |
258 | // `compiler-builtins` crate is enabled and it's configured to learn where | |
259 | // `compiler-rt` is located. | |
260 | let compiler_builtins_root = builder.src.join("src/llvm-project/compiler-rt"); | |
261 | let compiler_builtins_c_feature = if compiler_builtins_root.exists() { | |
ba9703b0 XL |
262 | // Note that `libprofiler_builtins/build.rs` also computes this so if |
263 | // you're changing something here please also change that. | |
dc9dc135 | 264 | cargo.env("RUST_COMPILER_RT_ROOT", &compiler_builtins_root); |
29967ef6 | 265 | " compiler-builtins-c" |
dc9dc135 | 266 | } else { |
29967ef6 | 267 | "" |
dc9dc135 XL |
268 | }; |
269 | ||
83c7162d | 270 | if builder.no_std(target) == Some(true) { |
dc9dc135 | 271 | let mut features = "compiler-builtins-mem".to_string(); |
29967ef6 | 272 | features.push_str(compiler_builtins_c_feature); |
dc9dc135 | 273 | |
83c7162d | 274 | // for no-std targets we only compile a few no_std crates |
0731742a | 275 | cargo |
83c7162d | 276 | .args(&["-p", "alloc"]) |
83c7162d | 277 | .arg("--manifest-path") |
3dfed10e | 278 | .arg(builder.src.join("library/alloc/Cargo.toml")) |
0731742a | 279 | .arg("--features") |
29967ef6 | 280 | .arg(features); |
83c7162d | 281 | } else { |
29967ef6 XL |
282 | let mut features = builder.std_features(target); |
283 | features.push_str(compiler_builtins_c_feature); | |
8bb4bdeb | 284 | |
dfeec247 XL |
285 | cargo |
286 | .arg("--features") | |
287 | .arg(features) | |
83c7162d | 288 | .arg("--manifest-path") |
3dfed10e | 289 | .arg(builder.src.join("library/test/Cargo.toml")); |
7453a54e | 290 | |
e1599b0c XL |
291 | // Help the libc crate compile by assisting it in finding various |
292 | // sysroot native libraries. | |
83c7162d | 293 | if target.contains("musl") { |
f035d41b XL |
294 | if let Some(p) = builder.musl_libdir(target) { |
295 | let root = format!("native={}", p.to_str().unwrap()); | |
e1599b0c | 296 | cargo.rustflag("-L").rustflag(&root); |
83c7162d | 297 | } |
7453a54e | 298 | } |
532ac7d7 XL |
299 | |
300 | if target.ends_with("-wasi") { | |
301 | if let Some(p) = builder.wasi_root(target) { | |
e1599b0c XL |
302 | let root = format!("native={}/lib/wasm32-wasi", p.to_str().unwrap()); |
303 | cargo.rustflag("-L").rustflag(&root); | |
532ac7d7 XL |
304 | } |
305 | } | |
7453a54e | 306 | } |
f9f354fc XL |
307 | |
308 | // By default, rustc uses `-Cembed-bitcode=yes`, and Cargo overrides that | |
309 | // with `-Cembed-bitcode=no` for non-LTO builds. However, libstd must be | |
310 | // built with bitcode so that the produced rlibs can be used for both LTO | |
311 | // builds (which use bitcode) and non-LTO builds (which use object code). | |
312 | // So we override the override here! | |
313 | // | |
314 | // But we don't bother for the stage 0 compiler because it's never used | |
315 | // with LTO. | |
316 | if stage >= 1 { | |
317 | cargo.rustflag("-Cembed-bitcode=yes"); | |
318 | } | |
f035d41b XL |
319 | |
320 | // By default, rustc does not include unwind tables unless they are required | |
321 | // for a particular target. They are not required by RISC-V targets, but | |
322 | // compiling the standard library with them means that users can get | |
323 | // backtraces without having to recompile the standard library themselves. | |
324 | // | |
325 | // This choice was discussed in https://github.com/rust-lang/rust/pull/69890 | |
326 | if target.contains("riscv") { | |
327 | cargo.rustflag("-Cforce-unwind-tables=yes"); | |
328 | } | |
7453a54e SL |
329 | } |
330 | ||
3b2f2976 XL |
331 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] |
332 | struct StdLink { | |
333 | pub compiler: Compiler, | |
334 | pub target_compiler: Compiler, | |
3dfed10e | 335 | pub target: TargetSelection, |
54a0048b SL |
336 | } |
337 | ||
3b2f2976 XL |
338 | impl Step for StdLink { |
339 | type Output = (); | |
340 | ||
9fa01778 | 341 | fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { |
3b2f2976 XL |
342 | run.never() |
343 | } | |
344 | ||
345 | /// Link all libstd rlibs/dylibs into the sysroot location. | |
346 | /// | |
a1dfa0c6 | 347 | /// Links those artifacts generated by `compiler` to the `stage` compiler's |
3b2f2976 XL |
348 | /// sysroot for the specified `host` and `target`. |
349 | /// | |
350 | /// Note that this assumes that `compiler` has already generated the libstd | |
351 | /// libraries for `target`, and this method will find them in the relevant | |
352 | /// output directory. | |
9fa01778 | 353 | fn run(self, builder: &Builder<'_>) { |
3b2f2976 XL |
354 | let compiler = self.compiler; |
355 | let target_compiler = self.target_compiler; | |
356 | let target = self.target; | |
dfeec247 XL |
357 | builder.info(&format!( |
358 | "Copying stage{} std from stage{} ({} -> {} / {})", | |
359 | target_compiler.stage, compiler.stage, &compiler.host, target_compiler.host, target | |
360 | )); | |
3b2f2976 | 361 | let libdir = builder.sysroot_libdir(target_compiler, target); |
532ac7d7 XL |
362 | let hostdir = builder.sysroot_libdir(target_compiler, compiler.host); |
363 | add_to_sysroot(builder, &libdir, &hostdir, &libstd_stamp(builder, compiler, target)); | |
54a0048b | 364 | } |
7453a54e SL |
365 | } |
366 | ||
dfeec247 XL |
367 | /// Copies sanitizer runtime libraries into target libdir. |
368 | fn copy_sanitizers( | |
9fa01778 | 369 | builder: &Builder<'_>, |
dfeec247 | 370 | compiler: &Compiler, |
3dfed10e | 371 | target: TargetSelection, |
dfeec247 XL |
372 | ) -> Vec<PathBuf> { |
373 | let runtimes: Vec<native::SanitizerRuntime> = builder.ensure(native::Sanitizers { target }); | |
374 | ||
375 | if builder.config.dry_run { | |
376 | return Vec::new(); | |
377 | } | |
378 | ||
379 | let mut target_deps = Vec::new(); | |
380 | let libdir = builder.sysroot_libdir(*compiler, target); | |
381 | ||
382 | for runtime in &runtimes { | |
383 | let dst = libdir.join(&runtime.name); | |
384 | builder.copy(&runtime.path, &dst); | |
385 | ||
5869c6ff XL |
386 | if target == "x86_64-apple-darwin" || target == "aarch64-apple-darwin" { |
387 | // Update the library’s install name to reflect that it has has been renamed. | |
388 | apple_darwin_update_library_name(&dst, &format!("@rpath/{}", &runtime.name)); | |
389 | // Upon renaming the install name, the code signature of the file will invalidate, | |
390 | // so we will sign it again. | |
391 | apple_darwin_sign_file(&dst); | |
dfeec247 XL |
392 | } |
393 | ||
394 | target_deps.push(dst); | |
7cac9316 | 395 | } |
dfeec247 XL |
396 | |
397 | target_deps | |
7cac9316 XL |
398 | } |
399 | ||
5869c6ff XL |
400 | fn apple_darwin_update_library_name(library_path: &Path, new_name: &str) { |
401 | let status = Command::new("install_name_tool") | |
402 | .arg("-id") | |
403 | .arg(new_name) | |
404 | .arg(library_path) | |
405 | .status() | |
406 | .expect("failed to execute `install_name_tool`"); | |
407 | assert!(status.success()); | |
408 | } | |
409 | ||
410 | fn apple_darwin_sign_file(file_path: &Path) { | |
411 | let status = Command::new("codesign") | |
412 | .arg("-f") // Force to rewrite the existing signature | |
413 | .arg("-s") | |
414 | .arg("-") | |
415 | .arg(file_path) | |
416 | .status() | |
417 | .expect("failed to execute `codesign`"); | |
418 | assert!(status.success()); | |
419 | } | |
420 | ||
3b2f2976 XL |
421 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] |
422 | pub struct StartupObjects { | |
423 | pub compiler: Compiler, | |
3dfed10e | 424 | pub target: TargetSelection, |
3b2f2976 XL |
425 | } |
426 | ||
427 | impl Step for StartupObjects { | |
f035d41b | 428 | type Output = Vec<(PathBuf, DependencyType)>; |
3b2f2976 | 429 | |
9fa01778 | 430 | fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { |
3dfed10e | 431 | run.path("library/rtstartup") |
3b2f2976 XL |
432 | } |
433 | ||
9fa01778 | 434 | fn make_run(run: RunConfig<'_>) { |
3b2f2976 | 435 | run.builder.ensure(StartupObjects { |
1b1a35ee | 436 | compiler: run.builder.compiler(run.builder.top_stage, run.build_triple()), |
3b2f2976 XL |
437 | target: run.target, |
438 | }); | |
439 | } | |
440 | ||
9fa01778 | 441 | /// Builds and prepare startup objects like rsbegin.o and rsend.o |
3b2f2976 XL |
442 | /// |
443 | /// These are primarily used on Windows right now for linking executables/dlls. | |
444 | /// They don't require any library support as they're just plain old object | |
445 | /// files, so we just use the nightly snapshot compiler to always build them (as | |
446 | /// no other compilers are guaranteed to be available). | |
f035d41b | 447 | fn run(self, builder: &Builder<'_>) -> Vec<(PathBuf, DependencyType)> { |
3b2f2976 XL |
448 | let for_compiler = self.compiler; |
449 | let target = self.target; | |
416331ca | 450 | if !target.contains("windows-gnu") { |
dfeec247 | 451 | return vec![]; |
8bb4bdeb | 452 | } |
7453a54e | 453 | |
e74abb32 XL |
454 | let mut target_deps = vec![]; |
455 | ||
3dfed10e | 456 | let src_dir = &builder.src.join("library").join("rtstartup"); |
83c7162d | 457 | let dst_dir = &builder.native_dir(target).join("rtstartup"); |
3b2f2976 XL |
458 | let sysroot_dir = &builder.sysroot_libdir(for_compiler, target); |
459 | t!(fs::create_dir_all(dst_dir)); | |
460 | ||
461 | for file in &["rsbegin", "rsend"] { | |
462 | let src_file = &src_dir.join(file.to_string() + ".rs"); | |
463 | let dst_file = &dst_dir.join(file.to_string() + ".o"); | |
464 | if !up_to_date(src_file, dst_file) { | |
83c7162d | 465 | let mut cmd = Command::new(&builder.initial_rustc); |
cdc7bbd5 XL |
466 | cmd.env("RUSTC_BOOTSTRAP", "1"); |
467 | if !builder.local_rebuild { | |
468 | // a local_rebuild compiler already has stage1 features | |
469 | cmd.arg("--cfg").arg("bootstrap"); | |
470 | } | |
dfeec247 | 471 | builder.run( |
cdc7bbd5 | 472 | cmd.arg("--target") |
3dfed10e | 473 | .arg(target.rustc_target_arg()) |
dfeec247 XL |
474 | .arg("--emit=obj") |
475 | .arg("-o") | |
476 | .arg(dst_file) | |
477 | .arg(src_file), | |
478 | ); | |
3b2f2976 XL |
479 | } |
480 | ||
74b04a01 | 481 | let target = sysroot_dir.join((*file).to_string() + ".o"); |
e74abb32 | 482 | builder.copy(dst_file, &target); |
f035d41b | 483 | target_deps.push((target, DependencyType::Target)); |
3b2f2976 | 484 | } |
e74abb32 XL |
485 | |
486 | target_deps | |
7453a54e | 487 | } |
3b2f2976 XL |
488 | } |
489 | ||
83c7162d | 490 | #[derive(Debug, PartialOrd, Ord, Copy, Clone, PartialEq, Eq, Hash)] |
3b2f2976 | 491 | pub struct Rustc { |
3dfed10e | 492 | pub target: TargetSelection, |
83c7162d | 493 | pub compiler: Compiler, |
3b2f2976 XL |
494 | } |
495 | ||
496 | impl Step for Rustc { | |
497 | type Output = (); | |
498 | const ONLY_HOSTS: bool = true; | |
3dfed10e | 499 | const DEFAULT: bool = false; |
3b2f2976 | 500 | |
9fa01778 | 501 | fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { |
1b1a35ee | 502 | run.path("compiler/rustc") |
3b2f2976 XL |
503 | } |
504 | ||
9fa01778 | 505 | fn make_run(run: RunConfig<'_>) { |
3b2f2976 | 506 | run.builder.ensure(Rustc { |
1b1a35ee | 507 | compiler: run.builder.compiler(run.builder.top_stage, run.build_triple()), |
3b2f2976 XL |
508 | target: run.target, |
509 | }); | |
510 | } | |
511 | ||
9fa01778 | 512 | /// Builds the compiler. |
3b2f2976 XL |
513 | /// |
514 | /// This will build the compiler for a particular stage of the build using | |
515 | /// the `compiler` targeting the `target` architecture. The artifacts | |
516 | /// created will also be linked into the sysroot directory. | |
9fa01778 | 517 | fn run(self, builder: &Builder<'_>) { |
3b2f2976 XL |
518 | let compiler = self.compiler; |
519 | let target = self.target; | |
520 | ||
cdc7bbd5 XL |
521 | // NOTE: the ABI of the beta compiler is different from the ABI of the downloaded compiler, |
522 | // so its artifacts can't be reused. | |
523 | if builder.config.download_rustc && compiler.stage != 0 { | |
6a06907d XL |
524 | // Copy the existing artifacts instead of rebuilding them. |
525 | // NOTE: this path is only taken for tools linking to rustc-dev. | |
526 | builder.ensure(Sysroot { compiler }); | |
527 | return; | |
528 | } | |
529 | ||
e1599b0c | 530 | builder.ensure(Std { compiler, target }); |
3b2f2976 | 531 | |
8faf50e0 XL |
532 | if builder.config.keep_stage.contains(&compiler.stage) { |
533 | builder.info("Warning: Using a potentially old librustc. This may not behave well."); | |
1b1a35ee | 534 | builder.info("Warning: Use `--keep-stage-std` if you want to rebuild the compiler when it changes"); |
dfeec247 | 535 | builder.ensure(RustcLink { compiler, target_compiler: compiler, target }); |
8faf50e0 XL |
536 | return; |
537 | } | |
538 | ||
dc9dc135 XL |
539 | let compiler_to_use = builder.compiler_for(compiler.stage, compiler.host, target); |
540 | if compiler_to_use != compiler { | |
dfeec247 XL |
541 | builder.ensure(Rustc { compiler: compiler_to_use, target }); |
542 | builder | |
543 | .info(&format!("Uplifting stage1 rustc ({} -> {})", builder.config.build, target)); | |
3b2f2976 | 544 | builder.ensure(RustcLink { |
dc9dc135 | 545 | compiler: compiler_to_use, |
3b2f2976 XL |
546 | target_compiler: compiler, |
547 | target, | |
548 | }); | |
549 | return; | |
550 | } | |
551 | ||
532ac7d7 | 552 | // Ensure that build scripts and proc macros have a std / libproc_macro to link against. |
e1599b0c | 553 | builder.ensure(Std { |
83c7162d XL |
554 | compiler: builder.compiler(self.compiler.stage, builder.config.build), |
555 | target: builder.config.build, | |
3b2f2976 | 556 | }); |
0531ce1d | 557 | |
f035d41b | 558 | let mut cargo = builder.cargo(compiler, Mode::Rustc, SourceType::InTree, target, "build"); |
60c5eb7d | 559 | rustc_cargo(builder, &mut cargo, target); |
3b2f2976 | 560 | |
fc512014 XL |
561 | if builder.config.rust_profile_use.is_some() |
562 | && builder.config.rust_profile_generate.is_some() | |
563 | { | |
564 | panic!("Cannot use and generate PGO profiles at the same time"); | |
565 | } | |
566 | ||
567 | let is_collecting = if let Some(path) = &builder.config.rust_profile_generate { | |
568 | if compiler.stage == 1 { | |
569 | cargo.rustflag(&format!("-Cprofile-generate={}", path)); | |
570 | // Apparently necessary to avoid overflowing the counters during | |
571 | // a Cargo build profile | |
572 | cargo.rustflag("-Cllvm-args=-vp-counters-per-site=4"); | |
573 | true | |
574 | } else { | |
575 | false | |
576 | } | |
577 | } else if let Some(path) = &builder.config.rust_profile_use { | |
578 | if compiler.stage == 1 { | |
579 | cargo.rustflag(&format!("-Cprofile-use={}", path)); | |
580 | cargo.rustflag("-Cllvm-args=-pgo-warn-missing-function"); | |
581 | true | |
582 | } else { | |
583 | false | |
584 | } | |
585 | } else { | |
586 | false | |
587 | }; | |
588 | if is_collecting { | |
589 | // Ensure paths to Rust sources are relative, not absolute. | |
590 | cargo.rustflag(&format!( | |
591 | "-Cllvm-args=-static-func-strip-dirname-prefix={}", | |
592 | builder.config.src.components().count() | |
593 | )); | |
594 | } | |
595 | ||
dfeec247 XL |
596 | builder.info(&format!( |
597 | "Building stage{} compiler artifacts ({} -> {})", | |
598 | compiler.stage, &compiler.host, target | |
599 | )); | |
600 | run_cargo( | |
601 | builder, | |
602 | cargo, | |
603 | vec![], | |
604 | &librustc_stamp(builder, compiler, target), | |
605 | vec![], | |
606 | false, | |
607 | ); | |
3b2f2976 XL |
608 | |
609 | builder.ensure(RustcLink { | |
83c7162d | 610 | compiler: builder.compiler(compiler.stage, builder.config.build), |
3b2f2976 XL |
611 | target_compiler: compiler, |
612 | target, | |
613 | }); | |
614 | } | |
615 | } | |
616 | ||
3dfed10e | 617 | pub fn rustc_cargo(builder: &Builder<'_>, cargo: &mut Cargo, target: TargetSelection) { |
dfeec247 XL |
618 | cargo |
619 | .arg("--features") | |
620 | .arg(builder.rustc_features()) | |
621 | .arg("--manifest-path") | |
1b1a35ee | 622 | .arg(builder.src.join("compiler/rustc/Cargo.toml")); |
60c5eb7d | 623 | rustc_cargo_env(builder, cargo, target); |
2c00a5a8 | 624 | } |
7453a54e | 625 | |
3dfed10e | 626 | pub fn rustc_cargo_env(builder: &Builder<'_>, cargo: &mut Cargo, target: TargetSelection) { |
7453a54e SL |
627 | // Set some configuration variables picked up by build scripts and |
628 | // the compiler alike | |
dfeec247 XL |
629 | cargo |
630 | .env("CFG_RELEASE", builder.rust_release()) | |
631 | .env("CFG_RELEASE_CHANNEL", &builder.config.channel) | |
632 | .env("CFG_VERSION", builder.rust_version()) | |
633 | .env("CFG_PREFIX", builder.config.prefix.clone().unwrap_or_default()); | |
32a655c1 | 634 | |
74b04a01 | 635 | let libdir_relative = builder.config.libdir_relative().unwrap_or_else(|| Path::new("lib")); |
2c00a5a8 | 636 | cargo.env("CFG_LIBDIR_RELATIVE", libdir_relative); |
7453a54e | 637 | |
83c7162d | 638 | if let Some(ref ver_date) = builder.rust_info.commit_date() { |
7453a54e SL |
639 | cargo.env("CFG_VER_DATE", ver_date); |
640 | } | |
83c7162d | 641 | if let Some(ref ver_hash) = builder.rust_info.sha() { |
7453a54e SL |
642 | cargo.env("CFG_VER_HASH", ver_hash); |
643 | } | |
83c7162d | 644 | if !builder.unstable_features() { |
7453a54e SL |
645 | cargo.env("CFG_DISABLE_UNSTABLE_FEATURES", "1"); |
646 | } | |
83c7162d | 647 | if let Some(ref s) = builder.config.rustc_default_linker { |
7453a54e SL |
648 | cargo.env("CFG_DEFAULT_LINKER", s); |
649 | } | |
9fa01778 | 650 | if builder.config.rustc_parallel { |
e1599b0c | 651 | cargo.rustflag("--cfg=parallel_compiler"); |
ff7c6d11 | 652 | } |
0bf4aa26 XL |
653 | if builder.config.rust_verify_llvm_ir { |
654 | cargo.env("RUSTC_VERIFY_LLVM_IR", "1"); | |
655 | } | |
60c5eb7d XL |
656 | |
657 | // Pass down configuration from the LLVM build into the build of | |
1b1a35ee | 658 | // rustc_llvm and rustc_codegen_llvm. |
60c5eb7d XL |
659 | // |
660 | // Note that this is disabled if LLVM itself is disabled or we're in a check | |
f9f354fc XL |
661 | // build. If we are in a check build we still go ahead here presuming we've |
662 | // detected that LLVM is alreay built and good to go which helps prevent | |
663 | // busting caches (e.g. like #71152). | |
664 | if builder.config.llvm_enabled() | |
665 | && (builder.kind != Kind::Check | |
666 | || crate::native::prebuilt_llvm_config(builder, target).is_ok()) | |
667 | { | |
60c5eb7d XL |
668 | if builder.is_rust_llvm(target) { |
669 | cargo.env("LLVM_RUSTLLVM", "1"); | |
670 | } | |
671 | let llvm_config = builder.ensure(native::Llvm { target }); | |
672 | cargo.env("LLVM_CONFIG", &llvm_config); | |
673 | let target_config = builder.config.target_config.get(&target); | |
674 | if let Some(s) = target_config.and_then(|c| c.llvm_config.as_ref()) { | |
675 | cargo.env("CFG_LLVM_ROOT", s); | |
676 | } | |
1b1a35ee | 677 | // Some LLVM linker flags (-L and -l) may be needed to link rustc_llvm. |
60c5eb7d XL |
678 | if let Some(ref s) = builder.config.llvm_ldflags { |
679 | cargo.env("LLVM_LINKER_FLAGS", s); | |
680 | } | |
681 | // Building with a static libstdc++ is only supported on linux right now, | |
682 | // not for MSVC or macOS | |
dfeec247 XL |
683 | if builder.config.llvm_static_stdcpp |
684 | && !target.contains("freebsd") | |
685 | && !target.contains("msvc") | |
686 | && !target.contains("apple") | |
687 | { | |
688 | let file = compiler_file(builder, builder.cxx(target).unwrap(), target, "libstdc++.a"); | |
60c5eb7d XL |
689 | cargo.env("LLVM_STATIC_STDCPP", file); |
690 | } | |
1b1a35ee | 691 | if builder.config.llvm_link_shared { |
60c5eb7d XL |
692 | cargo.env("LLVM_LINK_SHARED", "1"); |
693 | } | |
694 | if builder.config.llvm_use_libcxx { | |
695 | cargo.env("LLVM_USE_LIBCXX", "1"); | |
696 | } | |
697 | if builder.config.llvm_optimize && !builder.config.llvm_release_debuginfo { | |
698 | cargo.env("LLVM_NDEBUG", "1"); | |
699 | } | |
700 | } | |
7453a54e SL |
701 | } |
702 | ||
3b2f2976 XL |
703 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] |
704 | struct RustcLink { | |
705 | pub compiler: Compiler, | |
706 | pub target_compiler: Compiler, | |
3dfed10e | 707 | pub target: TargetSelection, |
3b2f2976 XL |
708 | } |
709 | ||
710 | impl Step for RustcLink { | |
711 | type Output = (); | |
712 | ||
9fa01778 | 713 | fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { |
3b2f2976 XL |
714 | run.never() |
715 | } | |
716 | ||
717 | /// Same as `std_link`, only for librustc | |
9fa01778 | 718 | fn run(self, builder: &Builder<'_>) { |
3b2f2976 XL |
719 | let compiler = self.compiler; |
720 | let target_compiler = self.target_compiler; | |
721 | let target = self.target; | |
dfeec247 XL |
722 | builder.info(&format!( |
723 | "Copying stage{} rustc from stage{} ({} -> {} / {})", | |
724 | target_compiler.stage, compiler.stage, &compiler.host, target_compiler.host, target | |
725 | )); | |
532ac7d7 XL |
726 | add_to_sysroot( |
727 | builder, | |
728 | &builder.sysroot_libdir(target_compiler, target), | |
729 | &builder.sysroot_libdir(target_compiler, compiler.host), | |
dfeec247 | 730 | &librustc_stamp(builder, compiler, target), |
532ac7d7 | 731 | ); |
3b2f2976 | 732 | } |
7453a54e SL |
733 | } |
734 | ||
29967ef6 XL |
735 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] |
736 | pub struct CodegenBackend { | |
737 | pub target: TargetSelection, | |
738 | pub compiler: Compiler, | |
739 | pub backend: Interned<String>, | |
740 | } | |
741 | ||
742 | impl Step for CodegenBackend { | |
743 | type Output = (); | |
744 | const ONLY_HOSTS: bool = true; | |
745 | // Only the backends specified in the `codegen-backends` entry of `config.toml` are built. | |
746 | const DEFAULT: bool = true; | |
747 | ||
748 | fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { | |
749 | run.path("compiler/rustc_codegen_cranelift") | |
750 | } | |
751 | ||
752 | fn make_run(run: RunConfig<'_>) { | |
753 | for &backend in &run.builder.config.rust_codegen_backends { | |
754 | if backend == "llvm" { | |
755 | continue; // Already built as part of rustc | |
756 | } | |
757 | ||
758 | run.builder.ensure(CodegenBackend { | |
759 | target: run.target, | |
760 | compiler: run.builder.compiler(run.builder.top_stage, run.build_triple()), | |
761 | backend, | |
762 | }); | |
763 | } | |
764 | } | |
765 | ||
766 | fn run(self, builder: &Builder<'_>) { | |
767 | let compiler = self.compiler; | |
768 | let target = self.target; | |
769 | let backend = self.backend; | |
770 | ||
771 | builder.ensure(Rustc { compiler, target }); | |
772 | ||
773 | if builder.config.keep_stage.contains(&compiler.stage) { | |
774 | builder.info( | |
775 | "Warning: Using a potentially old codegen backend. \ | |
776 | This may not behave well.", | |
777 | ); | |
778 | // Codegen backends are linked separately from this step today, so we don't do | |
779 | // anything here. | |
780 | return; | |
781 | } | |
782 | ||
783 | let compiler_to_use = builder.compiler_for(compiler.stage, compiler.host, target); | |
784 | if compiler_to_use != compiler { | |
785 | builder.ensure(CodegenBackend { compiler: compiler_to_use, target, backend }); | |
786 | return; | |
787 | } | |
788 | ||
789 | let out_dir = builder.cargo_out(compiler, Mode::Codegen, target); | |
790 | ||
791 | let mut cargo = | |
792 | builder.cargo(compiler, Mode::Codegen, SourceType::Submodule, target, "build"); | |
793 | cargo | |
794 | .arg("--manifest-path") | |
795 | .arg(builder.src.join(format!("compiler/rustc_codegen_{}/Cargo.toml", backend))); | |
796 | rustc_cargo_env(builder, &mut cargo, target); | |
797 | ||
798 | let tmp_stamp = out_dir.join(".tmp.stamp"); | |
799 | ||
800 | let files = run_cargo(builder, cargo, vec![], &tmp_stamp, vec![], false); | |
801 | if builder.config.dry_run { | |
802 | return; | |
803 | } | |
804 | let mut files = files.into_iter().filter(|f| { | |
805 | let filename = f.file_name().unwrap().to_str().unwrap(); | |
806 | is_dylib(filename) && filename.contains("rustc_codegen_") | |
807 | }); | |
808 | let codegen_backend = match files.next() { | |
809 | Some(f) => f, | |
810 | None => panic!("no dylibs built for codegen backend?"), | |
811 | }; | |
812 | if let Some(f) = files.next() { | |
813 | panic!( | |
814 | "codegen backend built two dylibs:\n{}\n{}", | |
815 | codegen_backend.display(), | |
816 | f.display() | |
817 | ); | |
818 | } | |
819 | let stamp = codegen_backend_stamp(builder, compiler, target, backend); | |
820 | let codegen_backend = codegen_backend.to_str().unwrap(); | |
821 | t!(fs::write(&stamp, &codegen_backend)); | |
822 | } | |
823 | } | |
824 | ||
825 | /// Creates the `codegen-backends` folder for a compiler that's about to be | |
826 | /// assembled as a complete compiler. | |
827 | /// | |
828 | /// This will take the codegen artifacts produced by `compiler` and link them | |
829 | /// into an appropriate location for `target_compiler` to be a functional | |
830 | /// compiler. | |
831 | fn copy_codegen_backends_to_sysroot( | |
832 | builder: &Builder<'_>, | |
833 | compiler: Compiler, | |
834 | target_compiler: Compiler, | |
835 | ) { | |
836 | let target = target_compiler.host; | |
837 | ||
838 | // Note that this step is different than all the other `*Link` steps in | |
839 | // that it's not assembling a bunch of libraries but rather is primarily | |
840 | // moving the codegen backend into place. The codegen backend of rustc is | |
841 | // not linked into the main compiler by default but is rather dynamically | |
842 | // selected at runtime for inclusion. | |
843 | // | |
844 | // Here we're looking for the output dylib of the `CodegenBackend` step and | |
845 | // we're copying that into the `codegen-backends` folder. | |
846 | let dst = builder.sysroot_codegen_backends(target_compiler); | |
fc512014 | 847 | t!(fs::create_dir_all(&dst), dst); |
29967ef6 XL |
848 | |
849 | if builder.config.dry_run { | |
850 | return; | |
851 | } | |
852 | ||
853 | for backend in builder.config.rust_codegen_backends.iter() { | |
854 | if backend == "llvm" { | |
855 | continue; // Already built as part of rustc | |
856 | } | |
857 | ||
858 | let stamp = codegen_backend_stamp(builder, compiler, target, *backend); | |
859 | let dylib = t!(fs::read_to_string(&stamp)); | |
860 | let file = Path::new(&dylib); | |
861 | let filename = file.file_name().unwrap().to_str().unwrap(); | |
862 | // change `librustc_codegen_cranelift-xxxxxx.so` to | |
863 | // `librustc_codegen_cranelift-release.so` | |
864 | let target_filename = { | |
865 | let dash = filename.find('-').unwrap(); | |
866 | let dot = filename.find('.').unwrap(); | |
867 | format!("{}-{}{}", &filename[..dash], builder.rust_release(), &filename[dot..]) | |
868 | }; | |
869 | builder.copy(&file, &dst.join(target_filename)); | |
870 | } | |
871 | } | |
872 | ||
7453a54e SL |
873 | /// Cargo's output path for the standard library in a given stage, compiled |
874 | /// by a particular compiler for the specified target. | |
3dfed10e | 875 | pub fn libstd_stamp(builder: &Builder<'_>, compiler: Compiler, target: TargetSelection) -> PathBuf { |
94b46f34 | 876 | builder.cargo_out(compiler, Mode::Std, target).join(".libstd.stamp") |
7453a54e SL |
877 | } |
878 | ||
cc61c64b XL |
879 | /// Cargo's output path for librustc in a given stage, compiled by a particular |
880 | /// compiler for the specified target. | |
9fa01778 XL |
881 | pub fn librustc_stamp( |
882 | builder: &Builder<'_>, | |
883 | compiler: Compiler, | |
3dfed10e | 884 | target: TargetSelection, |
9fa01778 | 885 | ) -> PathBuf { |
94b46f34 | 886 | builder.cargo_out(compiler, Mode::Rustc, target).join(".librustc.stamp") |
cc61c64b XL |
887 | } |
888 | ||
29967ef6 XL |
889 | /// Cargo's output path for librustc_codegen_llvm in a given stage, compiled by a particular |
890 | /// compiler for the specified target and backend. | |
891 | fn codegen_backend_stamp( | |
892 | builder: &Builder<'_>, | |
893 | compiler: Compiler, | |
894 | target: TargetSelection, | |
895 | backend: Interned<String>, | |
896 | ) -> PathBuf { | |
897 | builder | |
898 | .cargo_out(compiler, Mode::Codegen, target) | |
899 | .join(format!(".librustc_codegen_{}.stamp", backend)) | |
900 | } | |
901 | ||
9fa01778 XL |
902 | pub fn compiler_file( |
903 | builder: &Builder<'_>, | |
904 | compiler: &Path, | |
3dfed10e | 905 | target: TargetSelection, |
9fa01778 XL |
906 | file: &str, |
907 | ) -> PathBuf { | |
2c00a5a8 | 908 | let mut cmd = Command::new(compiler); |
b7449926 | 909 | cmd.args(builder.cflags(target, GitRepo::Rustc)); |
2c00a5a8 XL |
910 | cmd.arg(format!("-print-file-name={}", file)); |
911 | let out = output(&mut cmd); | |
54a0048b | 912 | PathBuf::from(out.trim()) |
7453a54e SL |
913 | } |
914 | ||
3b2f2976 XL |
915 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] |
916 | pub struct Sysroot { | |
917 | pub compiler: Compiler, | |
32a655c1 SL |
918 | } |
919 | ||
3b2f2976 XL |
920 | impl Step for Sysroot { |
921 | type Output = Interned<PathBuf>; | |
922 | ||
9fa01778 | 923 | fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { |
3b2f2976 | 924 | run.never() |
7453a54e SL |
925 | } |
926 | ||
3b2f2976 XL |
927 | /// Returns the sysroot for the `compiler` specified that *this build system |
928 | /// generates*. | |
929 | /// | |
930 | /// That is, the sysroot for the stage0 compiler is not what the compiler | |
931 | /// thinks it is by default, but it's the same as the default for stages | |
932 | /// 1-3. | |
9fa01778 | 933 | fn run(self, builder: &Builder<'_>) -> Interned<PathBuf> { |
3b2f2976 XL |
934 | let compiler = self.compiler; |
935 | let sysroot = if compiler.stage == 0 { | |
3dfed10e | 936 | builder.out.join(&compiler.host.triple).join("stage0-sysroot") |
3b2f2976 | 937 | } else { |
3dfed10e | 938 | builder.out.join(&compiler.host.triple).join(format!("stage{}", compiler.stage)) |
3b2f2976 XL |
939 | }; |
940 | let _ = fs::remove_dir_all(&sysroot); | |
941 | t!(fs::create_dir_all(&sysroot)); | |
ba9703b0 | 942 | |
6a06907d | 943 | // If we're downloading a compiler from CI, we can use the same compiler for all stages other than 0. |
cdc7bbd5 | 944 | if builder.config.download_rustc && compiler.stage != 0 { |
6a06907d XL |
945 | assert_eq!( |
946 | builder.config.build, compiler.host, | |
947 | "Cross-compiling is not yet supported with `download-rustc`", | |
948 | ); | |
949 | // Copy the compiler into the correct sysroot. | |
cdc7bbd5 XL |
950 | let ci_rustc_dir = |
951 | builder.config.out.join(&*builder.config.build.triple).join("ci-rustc"); | |
952 | builder.cp_r(&ci_rustc_dir, &sysroot); | |
6a06907d XL |
953 | return INTERNER.intern_path(sysroot); |
954 | } | |
955 | ||
ba9703b0 XL |
956 | // Symlink the source root into the same location inside the sysroot, |
957 | // where `rust-src` component would go (`$sysroot/lib/rustlib/src/rust`), | |
958 | // so that any tools relying on `rust-src` also work for local builds, | |
959 | // and also for translating the virtual `/rustc/$hash` back to the real | |
960 | // directory (for running tests with `rust.remap-debuginfo = true`). | |
961 | let sysroot_lib_rustlib_src = sysroot.join("lib/rustlib/src"); | |
962 | t!(fs::create_dir_all(&sysroot_lib_rustlib_src)); | |
963 | let sysroot_lib_rustlib_src_rust = sysroot_lib_rustlib_src.join("rust"); | |
964 | if let Err(e) = symlink_dir(&builder.config, &builder.src, &sysroot_lib_rustlib_src_rust) { | |
965 | eprintln!( | |
966 | "warning: creating symbolic link `{}` to `{}` failed with {}", | |
967 | sysroot_lib_rustlib_src_rust.display(), | |
968 | builder.src.display(), | |
969 | e, | |
970 | ); | |
971 | if builder.config.rust_remap_debuginfo { | |
972 | eprintln!( | |
973 | "warning: some `src/test/ui` tests will fail when lacking `{}`", | |
974 | sysroot_lib_rustlib_src_rust.display(), | |
975 | ); | |
976 | } | |
977 | } | |
978 | ||
3b2f2976 XL |
979 | INTERNER.intern_path(sysroot) |
980 | } | |
981 | } | |
982 | ||
83c7162d | 983 | #[derive(Debug, Copy, PartialOrd, Ord, Clone, PartialEq, Eq, Hash)] |
3b2f2976 XL |
984 | pub struct Assemble { |
985 | /// The compiler which we will produce in this step. Assemble itself will | |
986 | /// take care of ensuring that the necessary prerequisites to do so exist, | |
987 | /// that is, this target can be a stage2 compiler and Assemble will build | |
988 | /// previous stages for you. | |
989 | pub target_compiler: Compiler, | |
990 | } | |
7453a54e | 991 | |
3b2f2976 XL |
992 | impl Step for Assemble { |
993 | type Output = Compiler; | |
7453a54e | 994 | |
9fa01778 | 995 | fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { |
8faf50e0 | 996 | run.never() |
3b2f2976 XL |
997 | } |
998 | ||
999 | /// Prepare a new compiler from the artifacts in `stage` | |
1000 | /// | |
1001 | /// This will assemble a compiler in `build/$host/stage$stage`. The compiler | |
83c7162d | 1002 | /// must have been previously produced by the `stage - 1` builder.build |
3b2f2976 | 1003 | /// compiler. |
9fa01778 | 1004 | fn run(self, builder: &Builder<'_>) -> Compiler { |
3b2f2976 XL |
1005 | let target_compiler = self.target_compiler; |
1006 | ||
1007 | if target_compiler.stage == 0 { | |
dfeec247 XL |
1008 | assert_eq!( |
1009 | builder.config.build, target_compiler.host, | |
1010 | "Cannot obtain compiler for non-native build triple at stage 0" | |
1011 | ); | |
3b2f2976 XL |
1012 | // The stage 0 compiler for the build triple is always pre-built. |
1013 | return target_compiler; | |
1014 | } | |
1015 | ||
1016 | // Get the compiler that we'll use to bootstrap ourselves. | |
2c00a5a8 XL |
1017 | // |
1018 | // Note that this is where the recursive nature of the bootstrap | |
1019 | // happens, as this will request the previous stage's compiler on | |
1020 | // downwards to stage 0. | |
1021 | // | |
1022 | // Also note that we're building a compiler for the host platform. We | |
1023 | // only assume that we can run `build` artifacts, which means that to | |
1024 | // produce some other architecture compiler we need to start from | |
1025 | // `build` to get there. | |
1026 | // | |
2c00a5a8 XL |
1027 | // FIXME: It may be faster if we build just a stage 1 compiler and then |
1028 | // use that to bootstrap this compiler forward. | |
dfeec247 | 1029 | let build_compiler = builder.compiler(target_compiler.stage - 1, builder.config.build); |
3b2f2976 | 1030 | |
6a06907d XL |
1031 | // If we're downloading a compiler from CI, we can use the same compiler for all stages other than 0. |
1032 | if builder.config.download_rustc { | |
1033 | builder.ensure(Sysroot { compiler: target_compiler }); | |
1034 | return target_compiler; | |
1035 | } | |
1036 | ||
3b2f2976 XL |
1037 | // Build the libraries for this compiler to link to (i.e., the libraries |
1038 | // it uses at runtime). NOTE: Crates the target compiler compiles don't | |
1039 | // link to these. (FIXME: Is that correct? It seems to be correct most | |
1040 | // of the time but I think we do link to these for stage2/bin compilers | |
1041 | // when not performing a full bootstrap). | |
dfeec247 | 1042 | builder.ensure(Rustc { compiler: build_compiler, target: target_compiler.host }); |
3b2f2976 | 1043 | |
29967ef6 XL |
1044 | for &backend in builder.config.rust_codegen_backends.iter() { |
1045 | if backend == "llvm" { | |
1046 | continue; // Already built as part of rustc | |
1047 | } | |
1048 | ||
1049 | builder.ensure(CodegenBackend { | |
1050 | compiler: build_compiler, | |
1051 | target: target_compiler.host, | |
1052 | backend, | |
1053 | }); | |
1054 | } | |
1055 | ||
83c7162d | 1056 | let lld_install = if builder.config.lld_enabled { |
dfeec247 | 1057 | Some(builder.ensure(native::Lld { target: target_compiler.host })) |
0531ce1d XL |
1058 | } else { |
1059 | None | |
1060 | }; | |
1061 | ||
3b2f2976 XL |
1062 | let stage = target_compiler.stage; |
1063 | let host = target_compiler.host; | |
83c7162d | 1064 | builder.info(&format!("Assembling stage{} compiler ({})", stage, host)); |
3b2f2976 XL |
1065 | |
1066 | // Link in all dylibs to the libdir | |
5869c6ff XL |
1067 | let stamp = librustc_stamp(builder, build_compiler, target_compiler.host); |
1068 | let proc_macros = builder | |
1069 | .read_stamp_file(&stamp) | |
1070 | .into_iter() | |
1071 | .filter_map(|(path, dependency_type)| { | |
1072 | if dependency_type == DependencyType::Host { | |
1073 | Some(path.file_name().unwrap().to_owned().into_string().unwrap()) | |
1074 | } else { | |
1075 | None | |
1076 | } | |
1077 | }) | |
1078 | .collect::<HashSet<_>>(); | |
1079 | ||
3b2f2976 | 1080 | let sysroot = builder.sysroot(target_compiler); |
532ac7d7 XL |
1081 | let rustc_libdir = builder.rustc_libdir(target_compiler); |
1082 | t!(fs::create_dir_all(&rustc_libdir)); | |
3b2f2976 | 1083 | let src_libdir = builder.sysroot_libdir(build_compiler, host); |
83c7162d | 1084 | for f in builder.read_dir(&src_libdir) { |
3b2f2976 | 1085 | let filename = f.file_name().into_string().unwrap(); |
6a06907d XL |
1086 | if (is_dylib(&filename) || is_debug_info(&filename)) && !proc_macros.contains(&filename) |
1087 | { | |
532ac7d7 | 1088 | builder.copy(&f.path(), &rustc_libdir.join(&filename)); |
3b2f2976 XL |
1089 | } |
1090 | } | |
1091 | ||
29967ef6 XL |
1092 | copy_codegen_backends_to_sysroot(builder, build_compiler, target_compiler); |
1093 | ||
fc512014 XL |
1094 | // We prepend this bin directory to the user PATH when linking Rust binaries. To |
1095 | // avoid shadowing the system LLD we rename the LLD we provide to `rust-lld`. | |
60c5eb7d | 1096 | let libdir = builder.sysroot_libdir(target_compiler, target_compiler.host); |
fc512014 XL |
1097 | let libdir_bin = libdir.parent().unwrap().join("bin"); |
1098 | t!(fs::create_dir_all(&libdir_bin)); | |
1099 | ||
0531ce1d | 1100 | if let Some(lld_install) = lld_install { |
3dfed10e XL |
1101 | let src_exe = exe("lld", target_compiler.host); |
1102 | let dst_exe = exe("rust-lld", target_compiler.host); | |
fc512014 XL |
1103 | builder.copy(&lld_install.join("bin").join(&src_exe), &libdir_bin.join(&dst_exe)); |
1104 | } | |
1105 | ||
5869c6ff XL |
1106 | // Similarly, copy `llvm-dwp` into libdir for Split DWARF. Only copy it when the LLVM |
1107 | // backend is used to avoid unnecessarily building LLVM and because LLVM is not checked | |
1108 | // out by default when the LLVM backend is not enabled. | |
1109 | if builder.config.rust_codegen_backends.contains(&INTERNER.intern_str("llvm")) { | |
fc512014 XL |
1110 | let src_exe = exe("llvm-dwp", target_compiler.host); |
1111 | let dst_exe = exe("rust-llvm-dwp", target_compiler.host); | |
1112 | let llvm_config_bin = builder.ensure(native::Llvm { target: target_compiler.host }); | |
1113 | if !builder.config.dry_run { | |
1114 | let llvm_bin_dir = output(Command::new(llvm_config_bin).arg("--bindir")); | |
1115 | let llvm_bin_dir = Path::new(llvm_bin_dir.trim()); | |
1116 | builder.copy(&llvm_bin_dir.join(&src_exe), &libdir_bin.join(&dst_exe)); | |
1117 | } | |
0531ce1d | 1118 | } |
3b2f2976 | 1119 | |
60c5eb7d XL |
1120 | // Ensure that `libLLVM.so` ends up in the newly build compiler directory, |
1121 | // so that it can be found when the newly built `rustc` is run. | |
f9f354fc XL |
1122 | dist::maybe_install_llvm_runtime(builder, target_compiler.host, &sysroot); |
1123 | dist::maybe_install_llvm_target(builder, target_compiler.host, &sysroot); | |
0731742a | 1124 | |
3b2f2976 | 1125 | // Link the compiler binary itself into place |
94b46f34 | 1126 | let out_dir = builder.cargo_out(build_compiler, Mode::Rustc, host); |
1b1a35ee | 1127 | let rustc = out_dir.join(exe("rustc-main", host)); |
3b2f2976 XL |
1128 | let bindir = sysroot.join("bin"); |
1129 | t!(fs::create_dir_all(&bindir)); | |
1130 | let compiler = builder.rustc(target_compiler); | |
83c7162d | 1131 | builder.copy(&rustc, &compiler); |
3b2f2976 XL |
1132 | |
1133 | target_compiler | |
7453a54e SL |
1134 | } |
1135 | } | |
1136 | ||
1137 | /// Link some files into a rustc sysroot. | |
1138 | /// | |
7cac9316 XL |
1139 | /// For a particular stage this will link the file listed in `stamp` into the |
1140 | /// `sysroot_dst` provided. | |
532ac7d7 XL |
1141 | pub fn add_to_sysroot( |
1142 | builder: &Builder<'_>, | |
1143 | sysroot_dst: &Path, | |
1144 | sysroot_host_dst: &Path, | |
dfeec247 | 1145 | stamp: &Path, |
532ac7d7 | 1146 | ) { |
f035d41b | 1147 | let self_contained_dst = &sysroot_dst.join("self-contained"); |
7cac9316 | 1148 | t!(fs::create_dir_all(&sysroot_dst)); |
532ac7d7 | 1149 | t!(fs::create_dir_all(&sysroot_host_dst)); |
f035d41b XL |
1150 | t!(fs::create_dir_all(&self_contained_dst)); |
1151 | for (path, dependency_type) in builder.read_stamp_file(stamp) { | |
1152 | let dst = match dependency_type { | |
1153 | DependencyType::Host => sysroot_host_dst, | |
1154 | DependencyType::Target => sysroot_dst, | |
1155 | DependencyType::TargetSelfContained => self_contained_dst, | |
1156 | }; | |
1157 | builder.copy(&path, &dst.join(path.file_name().unwrap())); | |
7453a54e SL |
1158 | } |
1159 | } | |
54a0048b | 1160 | |
dfeec247 XL |
1161 | pub fn run_cargo( |
1162 | builder: &Builder<'_>, | |
1163 | cargo: Cargo, | |
1164 | tail_args: Vec<String>, | |
1165 | stamp: &Path, | |
f035d41b | 1166 | additional_target_deps: Vec<(PathBuf, DependencyType)>, |
dfeec247 XL |
1167 | is_check: bool, |
1168 | ) -> Vec<PathBuf> { | |
83c7162d XL |
1169 | if builder.config.dry_run { |
1170 | return Vec::new(); | |
1171 | } | |
1172 | ||
7cac9316 XL |
1173 | // `target_root_dir` looks like $dir/$target/release |
1174 | let target_root_dir = stamp.parent().unwrap(); | |
1175 | // `target_deps_dir` looks like $dir/$target/release/deps | |
1176 | let target_deps_dir = target_root_dir.join("deps"); | |
1177 | // `host_root_dir` looks like $dir/release | |
dfeec247 XL |
1178 | let host_root_dir = target_root_dir |
1179 | .parent() | |
1180 | .unwrap() // chop off `release` | |
1181 | .parent() | |
1182 | .unwrap() // chop off `$target` | |
1183 | .join(target_root_dir.file_name().unwrap()); | |
7cac9316 XL |
1184 | |
1185 | // Spawn Cargo slurping up its JSON output. We'll start building up the | |
1186 | // `deps` array of all files it generated along with a `toplevel` array of | |
1187 | // files we need to probe for later. | |
1188 | let mut deps = Vec::new(); | |
1189 | let mut toplevel = Vec::new(); | |
dc9dc135 | 1190 | let ok = stream_cargo(builder, cargo, tail_args, &mut |msg| { |
532ac7d7 XL |
1191 | let (filenames, crate_types) = match msg { |
1192 | CargoMessage::CompilerArtifact { | |
1193 | filenames, | |
dfeec247 | 1194 | target: CargoTarget { crate_types }, |
532ac7d7 XL |
1195 | .. |
1196 | } => (filenames, crate_types), | |
0531ce1d | 1197 | _ => return, |
7cac9316 | 1198 | }; |
0531ce1d | 1199 | for filename in filenames { |
7cac9316 | 1200 | // Skip files like executables |
74b04a01 XL |
1201 | if !(filename.ends_with(".rlib") |
1202 | || filename.ends_with(".lib") | |
1203 | || filename.ends_with(".a") | |
6a06907d | 1204 | || is_debug_info(&filename) |
74b04a01 XL |
1205 | || is_dylib(&filename) |
1206 | || (is_check && filename.ends_with(".rmeta"))) | |
dfeec247 | 1207 | { |
8faf50e0 | 1208 | continue; |
7cac9316 XL |
1209 | } |
1210 | ||
0531ce1d | 1211 | let filename = Path::new(&*filename); |
7cac9316 XL |
1212 | |
1213 | // If this was an output file in the "host dir" we don't actually | |
532ac7d7 | 1214 | // worry about it, it's not relevant for us |
7cac9316 | 1215 | if filename.starts_with(&host_root_dir) { |
532ac7d7 XL |
1216 | // Unless it's a proc macro used in the compiler |
1217 | if crate_types.iter().any(|t| t == "proc-macro") { | |
f035d41b | 1218 | deps.push((filename.to_path_buf(), DependencyType::Host)); |
532ac7d7 | 1219 | } |
8faf50e0 | 1220 | continue; |
041b39d2 | 1221 | } |
7cac9316 XL |
1222 | |
1223 | // If this was output in the `deps` dir then this is a precise file | |
1224 | // name (hash included) so we start tracking it. | |
041b39d2 | 1225 | if filename.starts_with(&target_deps_dir) { |
f035d41b | 1226 | deps.push((filename.to_path_buf(), DependencyType::Target)); |
8faf50e0 | 1227 | continue; |
041b39d2 | 1228 | } |
7cac9316 XL |
1229 | |
1230 | // Otherwise this was a "top level artifact" which right now doesn't | |
1231 | // have a hash in the name, but there's a version of this file in | |
1232 | // the `deps` folder which *does* have a hash in the name. That's | |
1233 | // the one we'll want to we'll probe for it later. | |
abe05a73 XL |
1234 | // |
1235 | // We do not use `Path::file_stem` or `Path::extension` here, | |
1236 | // because some generated files may have multiple extensions e.g. | |
1237 | // `std-<hash>.dll.lib` on Windows. The aforementioned methods only | |
1238 | // split the file name by the last extension (`.lib`) while we need | |
1239 | // to split by all extensions (`.dll.lib`). | |
1240 | let expected_len = t!(filename.metadata()).len(); | |
1241 | let filename = filename.file_name().unwrap().to_str().unwrap(); | |
1242 | let mut parts = filename.splitn(2, '.'); | |
1243 | let file_stem = parts.next().unwrap().to_owned(); | |
1244 | let extension = parts.next().unwrap().to_owned(); | |
1245 | ||
1246 | toplevel.push((file_stem, extension, expected_len)); | |
7cac9316 | 1247 | } |
0531ce1d | 1248 | }); |
7cac9316 | 1249 | |
0531ce1d | 1250 | if !ok { |
a1dfa0c6 | 1251 | exit(1); |
7cac9316 XL |
1252 | } |
1253 | ||
1254 | // Ok now we need to actually find all the files listed in `toplevel`. We've | |
1255 | // got a list of prefix/extensions and we basically just need to find the | |
1256 | // most recent file in the `deps` folder corresponding to each one. | |
1257 | let contents = t!(target_deps_dir.read_dir()) | |
1258 | .map(|e| t!(e)) | |
1259 | .map(|e| (e.path(), e.file_name().into_string().unwrap(), t!(e.metadata()))) | |
1260 | .collect::<Vec<_>>(); | |
abe05a73 XL |
1261 | for (prefix, extension, expected_len) in toplevel { |
1262 | let candidates = contents.iter().filter(|&&(_, ref filename, ref meta)| { | |
f035d41b XL |
1263 | meta.len() == expected_len |
1264 | && filename | |
1265 | .strip_prefix(&prefix[..]) | |
1266 | .map(|s| s.starts_with('-') && s.ends_with(&extension[..])) | |
1267 | .unwrap_or(false) | |
7cac9316 | 1268 | }); |
dfeec247 XL |
1269 | let max = candidates |
1270 | .max_by_key(|&&(_, _, ref metadata)| FileTime::from_last_modification_time(metadata)); | |
7cac9316 XL |
1271 | let path_to_add = match max { |
1272 | Some(triple) => triple.0.to_str().unwrap(), | |
1273 | None => panic!("no output generated for {:?} {:?}", prefix, extension), | |
1274 | }; | |
1275 | if is_dylib(path_to_add) { | |
1276 | let candidate = format!("{}.lib", path_to_add); | |
1277 | let candidate = PathBuf::from(candidate); | |
1278 | if candidate.exists() { | |
f035d41b | 1279 | deps.push((candidate, DependencyType::Target)); |
7cac9316 XL |
1280 | } |
1281 | } | |
f035d41b | 1282 | deps.push((path_to_add.into(), DependencyType::Target)); |
7cac9316 XL |
1283 | } |
1284 | ||
f035d41b | 1285 | deps.extend(additional_target_deps); |
7cac9316 | 1286 | deps.sort(); |
7cac9316 | 1287 | let mut new_contents = Vec::new(); |
f035d41b XL |
1288 | for (dep, dependency_type) in deps.iter() { |
1289 | new_contents.extend(match *dependency_type { | |
1290 | DependencyType::Host => b"h", | |
1291 | DependencyType::Target => b"t", | |
1292 | DependencyType::TargetSelfContained => b"s", | |
1293 | }); | |
7cac9316 XL |
1294 | new_contents.extend(dep.to_str().unwrap().as_bytes()); |
1295 | new_contents.extend(b"\0"); | |
1296 | } | |
0731742a | 1297 | t!(fs::write(&stamp, &new_contents)); |
532ac7d7 | 1298 | deps.into_iter().map(|(d, _)| d).collect() |
9e0c209e | 1299 | } |
0531ce1d XL |
1300 | |
1301 | pub fn stream_cargo( | |
9fa01778 | 1302 | builder: &Builder<'_>, |
e1599b0c | 1303 | cargo: Cargo, |
dc9dc135 | 1304 | tail_args: Vec<String>, |
9fa01778 | 1305 | cb: &mut dyn FnMut(CargoMessage<'_>), |
0531ce1d | 1306 | ) -> bool { |
e1599b0c | 1307 | let mut cargo = Command::from(cargo); |
83c7162d XL |
1308 | if builder.config.dry_run { |
1309 | return true; | |
1310 | } | |
0531ce1d XL |
1311 | // Instruct Cargo to give us json messages on stdout, critically leaving |
1312 | // stderr as piped so we can get those pretty colors. | |
ba9703b0 XL |
1313 | let mut message_format = if builder.config.json_output { |
1314 | String::from("json") | |
1315 | } else { | |
1316 | String::from("json-render-diagnostics") | |
1317 | }; | |
dfeec247 | 1318 | if let Some(s) = &builder.config.rustc_error_format { |
e1599b0c XL |
1319 | message_format.push_str(",json-diagnostic-"); |
1320 | message_format.push_str(s); | |
1321 | } | |
1322 | cargo.arg("--message-format").arg(message_format).stdout(Stdio::piped()); | |
0531ce1d | 1323 | |
dc9dc135 XL |
1324 | for arg in tail_args { |
1325 | cargo.arg(arg); | |
1326 | } | |
1327 | ||
83c7162d | 1328 | builder.verbose(&format!("running: {:?}", cargo)); |
0531ce1d XL |
1329 | let mut child = match cargo.spawn() { |
1330 | Ok(child) => child, | |
1331 | Err(e) => panic!("failed to execute command: {:?}\nerror: {}", cargo, e), | |
1332 | }; | |
1333 | ||
1334 | // Spawn Cargo slurping up its JSON output. We'll start building up the | |
1335 | // `deps` array of all files it generated along with a `toplevel` array of | |
1336 | // files we need to probe for later. | |
1337 | let stdout = BufReader::new(child.stdout.take().unwrap()); | |
1338 | for line in stdout.lines() { | |
1339 | let line = t!(line); | |
9fa01778 | 1340 | match serde_json::from_str::<CargoMessage<'_>>(&line) { |
f035d41b XL |
1341 | Ok(msg) => { |
1342 | if builder.config.json_output { | |
1343 | // Forward JSON to stdout. | |
1344 | println!("{}", line); | |
1345 | } | |
1346 | cb(msg) | |
1347 | } | |
0531ce1d | 1348 | // If this was informational, just print it out and continue |
dfeec247 | 1349 | Err(_) => println!("{}", line), |
0531ce1d XL |
1350 | } |
1351 | } | |
1352 | ||
1353 | // Make sure Cargo actually succeeded after we read all of its stdout. | |
1354 | let status = t!(child.wait()); | |
1355 | if !status.success() { | |
dfeec247 XL |
1356 | eprintln!( |
1357 | "command did not execute successfully: {:?}\n\ | |
0531ce1d | 1358 | expected success, got: {}", |
dfeec247 XL |
1359 | cargo, status |
1360 | ); | |
0531ce1d XL |
1361 | } |
1362 | status.success() | |
1363 | } | |
1364 | ||
532ac7d7 XL |
1365 | #[derive(Deserialize)] |
1366 | pub struct CargoTarget<'a> { | |
1367 | crate_types: Vec<Cow<'a, str>>, | |
1368 | } | |
1369 | ||
0531ce1d XL |
1370 | #[derive(Deserialize)] |
1371 | #[serde(tag = "reason", rename_all = "kebab-case")] | |
1372 | pub enum CargoMessage<'a> { | |
1373 | CompilerArtifact { | |
1374 | package_id: Cow<'a, str>, | |
1375 | features: Vec<Cow<'a, str>>, | |
1376 | filenames: Vec<Cow<'a, str>>, | |
532ac7d7 | 1377 | target: CargoTarget<'a>, |
0531ce1d XL |
1378 | }, |
1379 | BuildScriptExecuted { | |
1380 | package_id: Cow<'a, str>, | |
dc9dc135 | 1381 | }, |
f9f354fc XL |
1382 | BuildFinished { |
1383 | success: bool, | |
1384 | }, | |
dc9dc135 | 1385 | } |