]>
Commit | Line | Data |
---|---|---|
5bcae85e | 1 | //! Implementation of rustbuild, the Rust build system. |
a7813a04 | 2 | //! |
5bcae85e SL |
3 | //! This module, and its descendants, are the implementation of the Rust build |
4 | //! system. Most of this build system is backed by Cargo but the outer layer | |
5 | //! here serves as the ability to orchestrate calling Cargo, sequencing Cargo | |
476ff2be | 6 | //! builds, building artifacts like LLVM, etc. The goals of rustbuild are: |
5bcae85e | 7 | //! |
476ff2be SL |
8 | //! * To be an easily understandable, easily extensible, and maintainable build |
9 | //! system. | |
10 | //! * Leverage standard tools in the Rust ecosystem to build the compiler, aka | |
11 | //! crates.io and Cargo. | |
12 | //! * A standard interface to build across all platforms, including MSVC | |
13 | //! | |
14 | //! ## Architecture | |
15 | //! | |
3b2f2976 XL |
16 | //! The build system defers most of the complicated logic managing invocations |
17 | //! of rustc and rustdoc to Cargo itself. However, moving through various stages | |
18 | //! and copying artifacts is still necessary for it to do. Each time rustbuild | |
19 | //! is invoked, it will iterate through the list of predefined steps and execute | |
20 | //! each serially in turn if it matches the paths passed or is a default rule. | |
21 | //! For each step rustbuild relies on the step internally being incremental and | |
476ff2be SL |
22 | //! parallel. Note, though, that the `-j` parameter to rustbuild gets forwarded |
23 | //! to appropriate test harnesses and such. | |
24 | //! | |
25 | //! Most of the "meaty" steps that matter are backed by Cargo, which does indeed | |
26 | //! have its own parallelism and incremental management. Later steps, like | |
27 | //! tests, aren't incremental and simply run the entire suite currently. | |
3b2f2976 XL |
28 | //! However, compiletest itself tries to avoid running tests when the artifacts |
29 | //! that are involved (mainly the compiler) haven't changed. | |
476ff2be | 30 | //! |
0731742a | 31 | //! When you execute `x.py build`, the steps executed are: |
476ff2be SL |
32 | //! |
33 | //! * First, the python script is run. This will automatically download the | |
c295e0f8 | 34 | //! stage0 rustc and cargo according to `src/stage0.json`, or use the cached |
476ff2be SL |
35 | //! versions if they're available. These are then used to compile rustbuild |
36 | //! itself (using Cargo). Finally, control is then transferred to rustbuild. | |
37 | //! | |
38 | //! * Rustbuild takes over, performs sanity checks, probes the environment, | |
3b2f2976 XL |
39 | //! reads configuration, and starts executing steps as it reads the command |
40 | //! line arguments (paths) or going through the default rules. | |
476ff2be | 41 | //! |
3b2f2976 XL |
42 | //! The build output will be something like the following: |
43 | //! | |
44 | //! Building stage0 std artifacts | |
45 | //! Copying stage0 std | |
46 | //! Building stage0 test artifacts | |
47 | //! Copying stage0 test | |
48 | //! Building stage0 compiler artifacts | |
49 | //! Copying stage0 rustc | |
50 | //! Assembling stage1 compiler | |
51 | //! Building stage1 std artifacts | |
52 | //! Copying stage1 std | |
53 | //! Building stage1 test artifacts | |
54 | //! Copying stage1 test | |
55 | //! Building stage1 compiler artifacts | |
56 | //! Copying stage1 rustc | |
57 | //! Assembling stage2 compiler | |
58 | //! Uplifting stage1 std | |
59 | //! Uplifting stage1 test | |
60 | //! Uplifting stage1 rustc | |
61 | //! | |
62 | //! Let's disect that a little: | |
63 | //! | |
64 | //! ## Building stage0 {std,test,compiler} artifacts | |
65 | //! | |
66 | //! These steps use the provided (downloaded, usually) compiler to compile the | |
67 | //! local Rust source into libraries we can use. | |
68 | //! | |
69 | //! ## Copying stage0 {std,test,rustc} | |
70 | //! | |
71 | //! This copies the build output from Cargo into | |
9fa01778 | 72 | //! `build/$HOST/stage0-sysroot/lib/rustlib/$ARCH/lib`. FIXME: this step's |
3b2f2976 XL |
73 | //! documentation should be expanded -- the information already here may be |
74 | //! incorrect. | |
75 | //! | |
76 | //! ## Assembling stage1 compiler | |
77 | //! | |
78 | //! This copies the libraries we built in "building stage0 ... artifacts" into | |
79 | //! the stage1 compiler's lib directory. These are the host libraries that the | |
80 | //! compiler itself uses to run. These aren't actually used by artifacts the new | |
81 | //! compiler generates. This step also copies the rustc and rustdoc binaries we | |
82 | //! generated into build/$HOST/stage/bin. | |
83 | //! | |
84 | //! The stage1/bin/rustc is a fully functional compiler, but it doesn't yet have | |
85 | //! any libraries to link built binaries or libraries to. The next 3 steps will | |
86 | //! provide those libraries for it; they are mostly equivalent to constructing | |
87 | //! the stage1/bin compiler so we don't go through them individually. | |
88 | //! | |
89 | //! ## Uplifting stage1 {std,test,rustc} | |
90 | //! | |
91 | //! This step copies the libraries from the stage1 compiler sysroot into the | |
92 | //! stage2 compiler. This is done to avoid rebuilding the compiler; libraries | |
93 | //! we'd build in this step should be identical (in function, if not necessarily | |
94 | //! identical on disk) so there's no need to recompile the compiler again. Note | |
95 | //! that if you want to, you can enable the full-bootstrap option to change this | |
96 | //! behavior. | |
476ff2be SL |
97 | //! |
98 | //! Each step is driven by a separate Cargo project and rustbuild orchestrates | |
99 | //! copying files between steps and otherwise preparing for Cargo to run. | |
100 | //! | |
101 | //! ## Further information | |
102 | //! | |
103 | //! More documentation can be found in each respective module below, and you can | |
104 | //! also check out the `src/bootstrap/README.md` file for more information. | |
5bcae85e | 105 | |
dfeec247 XL |
106 | use std::cell::{Cell, RefCell}; |
107 | use std::collections::{HashMap, HashSet}; | |
7453a54e | 108 | use std::env; |
5099ac24 | 109 | use std::fs::{self, File}; |
064997fb | 110 | use std::io; |
dfeec247 | 111 | use std::path::{Path, PathBuf}; |
064997fb | 112 | use std::process::Command; |
83c7162d | 113 | use std::str; |
5bcae85e | 114 | |
83c7162d | 115 | use filetime::FileTime; |
5e7ed085 | 116 | use once_cell::sync::OnceCell; |
5bcae85e | 117 | |
5e7ed085 | 118 | use crate::builder::Kind; |
29967ef6 | 119 | use crate::config::{LlvmLibunwind, TargetSelection}; |
5e7ed085 | 120 | use crate::util::{ |
064997fb | 121 | check_run, exe, libdir, mtime, output, run, run_suppressed, try_run, try_run_suppressed, CiEnv, |
5e7ed085 | 122 | }; |
5bcae85e | 123 | |
dfeec247 XL |
124 | mod builder; |
125 | mod cache; | |
ea8adc8c | 126 | mod cc_detect; |
5bcae85e SL |
127 | mod channel; |
128 | mod check; | |
129 | mod clean; | |
130 | mod compile; | |
131 | mod config; | |
132 | mod dist; | |
133 | mod doc; | |
134 | mod flags; | |
dfeec247 | 135 | mod format; |
c30ab7b3 | 136 | mod install; |
dfeec247 | 137 | mod metadata; |
5bcae85e | 138 | mod native; |
ba9703b0 | 139 | mod run; |
5bcae85e | 140 | mod sanity; |
1b1a35ee | 141 | mod setup; |
fc512014 | 142 | mod tarball; |
dfeec247 | 143 | mod test; |
3b2f2976 | 144 | mod tool; |
ea8adc8c | 145 | mod toolstate; |
dfeec247 | 146 | pub mod util; |
5bcae85e | 147 | |
923072b8 FG |
148 | #[cfg(feature = "build-metrics")] |
149 | mod metrics; | |
150 | ||
5bcae85e SL |
151 | #[cfg(windows)] |
152 | mod job; | |
153 | ||
8faf50e0 | 154 | #[cfg(all(unix, not(target_os = "haiku")))] |
7cac9316 | 155 | mod job { |
0731742a | 156 | pub unsafe fn setup(build: &mut crate::Build) { |
7cac9316 XL |
157 | if build.config.low_priority { |
158 | libc::setpriority(libc::PRIO_PGRP as _, 0, 10); | |
159 | } | |
160 | } | |
161 | } | |
162 | ||
e74abb32 | 163 | #[cfg(any(target_os = "haiku", target_os = "hermit", not(any(unix, windows))))] |
5bcae85e | 164 | mod job { |
dfeec247 | 165 | pub unsafe fn setup(_build: &mut crate::Build) {} |
5bcae85e SL |
166 | } |
167 | ||
923072b8 | 168 | pub use crate::builder::PathSet; |
dfeec247 | 169 | use crate::cache::{Interned, INTERNER}; |
0731742a | 170 | pub use crate::config::Config; |
1b1a35ee | 171 | pub use crate::flags::Subcommand; |
5bcae85e | 172 | |
8faf50e0 | 173 | const LLVM_TOOLS: &[&str] = &[ |
29967ef6 XL |
174 | "llvm-cov", // used to generate coverage report |
175 | "llvm-nm", // used to inspect binaries; it shows symbol names, their sizes and visibility | |
176 | "llvm-objcopy", // used to transform ELFs into binary format which flashing tools consume | |
177 | "llvm-objdump", // used to disassemble programs | |
8faf50e0 | 178 | "llvm-profdata", // used to inspect and merge files generated by profiles |
29967ef6 XL |
179 | "llvm-readobj", // used to get information from ELFs/objects that the other tools don't provide |
180 | "llvm-size", // used to prints the size of the linker sections of a program | |
181 | "llvm-strip", // used to discard symbols from binary files to reduce their size | |
182 | "llvm-ar", // used for creating and modifying archive files | |
fc512014 | 183 | "llvm-as", // used to convert LLVM assembly to LLVM bitcode |
29967ef6 XL |
184 | "llvm-dis", // used to disassemble LLVM bitcode |
185 | "llc", // used to compile LLVM bytecode | |
186 | "opt", // used to optimize LLVM bytecode | |
8faf50e0 XL |
187 | ]; |
188 | ||
29967ef6 XL |
189 | pub const VERSION: usize = 2; |
190 | ||
5e7ed085 FG |
191 | /// Extra --check-cfg to add when building |
192 | /// (Mode restriction, config name, config values (if any)) | |
193 | const EXTRA_CHECK_CFGS: &[(Option<Mode>, &'static str, Option<&[&'static str]>)] = &[ | |
194 | (None, "bootstrap", None), | |
195 | (Some(Mode::Rustc), "parallel_compiler", None), | |
196 | (Some(Mode::ToolRustc), "parallel_compiler", None), | |
197 | (Some(Mode::Std), "stdarch_intel_sde", None), | |
198 | (Some(Mode::Std), "no_fp_fmt_parse", None), | |
199 | (Some(Mode::Std), "no_global_oom_handling", None), | |
200 | (Some(Mode::Std), "freebsd12", None), | |
201 | (Some(Mode::Std), "backtrace_in_libstd", None), | |
202 | /* Extra values not defined in the built-in targets yet, but used in std */ | |
203 | (Some(Mode::Std), "target_env", Some(&["libnx"])), | |
204 | (Some(Mode::Std), "target_os", Some(&["watchos"])), | |
205 | ( | |
206 | Some(Mode::Std), | |
207 | "target_arch", | |
208 | Some(&["asmjs", "spirv", "nvptx", "nvptx64", "le32", "xtensa"]), | |
209 | ), | |
210 | /* Extra names used by dependencies */ | |
211 | // FIXME: Used by rustfmt is their test but is invalid (neither cargo nor bootstrap ever set | |
212 | // this config) should probably by removed or use a allow attribute. | |
213 | (Some(Mode::ToolRustc), "release", None), | |
214 | // FIXME: Used by stdarch in their test, should use a allow attribute instead. | |
215 | (Some(Mode::Std), "dont_compile_me", None), | |
216 | // FIXME: Used by serde_json, but we should not be triggering on external dependencies. | |
217 | (Some(Mode::Rustc), "no_btreemap_remove_entry", None), | |
218 | (Some(Mode::ToolRustc), "no_btreemap_remove_entry", None), | |
219 | // FIXME: Used by crossbeam-utils, but we should not be triggering on external dependencies. | |
220 | (Some(Mode::Rustc), "crossbeam_loom", None), | |
221 | (Some(Mode::ToolRustc), "crossbeam_loom", None), | |
222 | // FIXME: Used by proc-macro2, but we should not be triggering on external dependencies. | |
223 | (Some(Mode::Rustc), "span_locations", None), | |
224 | (Some(Mode::ToolRustc), "span_locations", None), | |
225 | ]; | |
226 | ||
5bcae85e SL |
227 | /// A structure representing a Rust compiler. |
228 | /// | |
229 | /// Each compiler has a `stage` that it is associated with and a `host` that | |
230 | /// corresponds to the platform the compiler runs on. This structure is used as | |
231 | /// a parameter to many methods below. | |
83c7162d | 232 | #[derive(Eq, PartialOrd, Ord, PartialEq, Clone, Copy, Hash, Debug)] |
3b2f2976 | 233 | pub struct Compiler { |
5bcae85e | 234 | stage: u32, |
3dfed10e | 235 | host: TargetSelection, |
5bcae85e SL |
236 | } |
237 | ||
83c7162d XL |
238 | #[derive(PartialEq, Eq, Copy, Clone, Debug)] |
239 | pub enum DocTests { | |
dc9dc135 | 240 | /// Run normal tests and doc tests (default). |
83c7162d | 241 | Yes, |
dc9dc135 | 242 | /// Do not run any doc tests. |
83c7162d | 243 | No, |
dc9dc135 | 244 | /// Only run doc tests. |
83c7162d XL |
245 | Only, |
246 | } | |
247 | ||
b7449926 XL |
248 | pub enum GitRepo { |
249 | Rustc, | |
250 | Llvm, | |
251 | } | |
252 | ||
5bcae85e SL |
253 | /// Global configuration for the build system. |
254 | /// | |
255 | /// This structure transitively contains all configuration for the build system. | |
256 | /// All filesystem-encoded configuration is in `config`, all flags are in | |
257 | /// `flags`, and then parsed or probed information is listed in the keys below. | |
258 | /// | |
259 | /// This structure is a parameter of almost all methods in the build system, | |
260 | /// although most functions are implemented as free functions rather than | |
261 | /// methods specifically on this structure itself (to make it easier to | |
262 | /// organize). | |
263 | pub struct Build { | |
dc9dc135 | 264 | /// User-specified configuration from `config.toml`. |
5bcae85e SL |
265 | config: Config, |
266 | ||
1b1a35ee XL |
267 | // Version information |
268 | version: String, | |
269 | ||
dc9dc135 | 270 | // Properties derived from the above configuration |
5bcae85e SL |
271 | src: PathBuf, |
272 | out: PathBuf, | |
5e7ed085 | 273 | bootstrap_out: PathBuf, |
8bb4bdeb XL |
274 | rust_info: channel::GitInfo, |
275 | cargo_info: channel::GitInfo, | |
cc61c64b | 276 | rls_info: channel::GitInfo, |
f035d41b | 277 | rust_analyzer_info: channel::GitInfo, |
8faf50e0 | 278 | clippy_info: channel::GitInfo, |
0731742a | 279 | miri_info: channel::GitInfo, |
abe05a73 | 280 | rustfmt_info: channel::GitInfo, |
9fa01778 | 281 | in_tree_llvm_info: channel::GitInfo, |
5bcae85e | 282 | local_rebuild: bool, |
041b39d2 | 283 | fail_fast: bool, |
83c7162d | 284 | doc_tests: DocTests, |
041b39d2 XL |
285 | verbosity: usize, |
286 | ||
dc9dc135 | 287 | // Targets for which to build |
3dfed10e XL |
288 | build: TargetSelection, |
289 | hosts: Vec<TargetSelection>, | |
290 | targets: Vec<TargetSelection>, | |
041b39d2 | 291 | |
041b39d2 XL |
292 | initial_rustc: PathBuf, |
293 | initial_cargo: PathBuf, | |
74b04a01 | 294 | initial_lld: PathBuf, |
f9f354fc | 295 | initial_libdir: PathBuf, |
5bcae85e | 296 | |
5bcae85e | 297 | // Runtime state filled in later on |
abe05a73 | 298 | // C/C++ compilers and archiver for all targets |
3dfed10e XL |
299 | cc: HashMap<TargetSelection, cc::Tool>, |
300 | cxx: HashMap<TargetSelection, cc::Tool>, | |
301 | ar: HashMap<TargetSelection, PathBuf>, | |
302 | ranlib: HashMap<TargetSelection, PathBuf>, | |
dc9dc135 | 303 | // Miscellaneous |
04454e1e | 304 | // allow bidirectional lookups: both name -> path and path -> name |
3b2f2976 | 305 | crates: HashMap<Interned<String>, Crate>, |
04454e1e | 306 | crate_paths: HashMap<PathBuf, Interned<String>>, |
476ff2be | 307 | is_sudo: bool, |
7cac9316 | 308 | ci_env: CiEnv, |
ea8adc8c | 309 | delayed_failures: RefCell<Vec<String>>, |
ff7c6d11 | 310 | prerelease_version: Cell<Option<u32>>, |
dfeec247 | 311 | tool_artifacts: |
3dfed10e | 312 | RefCell<HashMap<TargetSelection, HashMap<String, (&'static str, PathBuf, Vec<String>)>>>, |
923072b8 FG |
313 | |
314 | #[cfg(feature = "build-metrics")] | |
315 | metrics: metrics::BuildMetrics, | |
c30ab7b3 SL |
316 | } |
317 | ||
318 | #[derive(Debug)] | |
319 | struct Crate { | |
3b2f2976 | 320 | name: Interned<String>, |
94b46f34 | 321 | deps: HashSet<Interned<String>>, |
c30ab7b3 | 322 | path: PathBuf, |
5bcae85e SL |
323 | } |
324 | ||
2c00a5a8 | 325 | impl Crate { |
2c00a5a8 | 326 | fn local_path(&self, build: &Build) -> PathBuf { |
2c00a5a8 XL |
327 | self.path.strip_prefix(&build.config.src).unwrap().into() |
328 | } | |
329 | } | |
330 | ||
f035d41b XL |
331 | /// When building Rust various objects are handled differently. |
332 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] | |
333 | pub enum DependencyType { | |
334 | /// Libraries originating from proc-macros. | |
335 | Host, | |
336 | /// Typical Rust libraries. | |
337 | Target, | |
338 | /// Non Rust libraries and objects shipped to ease usage of certain targets. | |
339 | TargetSelfContained, | |
340 | } | |
341 | ||
5bcae85e SL |
342 | /// The various "modes" of invoking Cargo. |
343 | /// | |
344 | /// These entries currently correspond to the various output directories of the | |
345 | /// build system, with each mod generating output in a different directory. | |
83c7162d | 346 | #[derive(Debug, Hash, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] |
5bcae85e | 347 | pub enum Mode { |
041b39d2 | 348 | /// Build the standard library, placing output in the "stageN-std" directory. |
94b46f34 | 349 | Std, |
5bcae85e | 350 | |
94b46f34 XL |
351 | /// Build librustc, and compiler libraries, placing output in the "stageN-rustc" directory. |
352 | Rustc, | |
5bcae85e | 353 | |
29967ef6 XL |
354 | /// Build a codegen backend for rustc, placing the output in the "stageN-codegen" directory. |
355 | Codegen, | |
356 | ||
f035d41b XL |
357 | /// Build a tool, placing output in the "stage0-bootstrap-tools" |
358 | /// directory. This is for miscellaneous sets of tools that are built | |
359 | /// using the bootstrap stage0 compiler in its entirety (target libraries | |
360 | /// and all). Typically these tools compile with stable Rust. | |
8faf50e0 XL |
361 | ToolBootstrap, |
362 | ||
f035d41b XL |
363 | /// Build a tool which uses the locally built std, placing output in the |
364 | /// "stageN-tools" directory. Its usage is quite rare, mainly used by | |
365 | /// compiletest which needs libtest. | |
94b46f34 | 366 | ToolStd, |
f035d41b XL |
367 | |
368 | /// Build a tool which uses the locally built rustc and the target std, | |
369 | /// placing the output in the "stageN-tools" directory. This is used for | |
370 | /// anything that needs a fully functional rustc, such as rustdoc, clippy, | |
371 | /// cargo, rls, rustfmt, miri, etc. | |
94b46f34 XL |
372 | ToolRustc, |
373 | } | |
374 | ||
375 | impl Mode { | |
376 | pub fn is_tool(&self) -> bool { | |
3dfed10e | 377 | matches!(self, Mode::ToolBootstrap | Mode::ToolRustc | Mode::ToolStd) |
94b46f34 | 378 | } |
29967ef6 XL |
379 | |
380 | pub fn must_support_dlopen(&self) -> bool { | |
381 | matches!(self, Mode::Std | Mode::Codegen) | |
382 | } | |
5bcae85e SL |
383 | } |
384 | ||
5099ac24 FG |
385 | pub enum CLang { |
386 | C, | |
387 | Cxx, | |
388 | } | |
389 | ||
5bcae85e SL |
390 | impl Build { |
391 | /// Creates a new set of build configuration from the `flags` on the command | |
392 | /// line and the filesystem `config`. | |
393 | /// | |
394 | /// By default all build output will be placed in the current directory. | |
3b2f2976 | 395 | pub fn new(config: Config) -> Build { |
3b2f2976 | 396 | let src = config.src.clone(); |
83c7162d | 397 | let out = config.out.clone(); |
5bcae85e | 398 | |
064997fb FG |
399 | #[cfg(unix)] |
400 | // keep this consistent with the equivalent check in x.py: | |
401 | // https://github.com/rust-lang/rust/blob/a8a33cf27166d3eabaffc58ed3799e054af3b0c6/src/bootstrap/bootstrap.py#L796-L797 | |
476ff2be | 402 | let is_sudo = match env::var_os("SUDO_USER") { |
064997fb FG |
403 | Some(_sudo_user) => { |
404 | let uid = unsafe { libc::getuid() }; | |
405 | uid == 0 | |
406 | } | |
476ff2be SL |
407 | None => false, |
408 | }; | |
064997fb FG |
409 | #[cfg(not(unix))] |
410 | let is_sudo = false; | |
532ac7d7 XL |
411 | |
412 | let ignore_git = config.ignore_git; | |
413 | let rust_info = channel::GitInfo::new(ignore_git, &src); | |
414 | let cargo_info = channel::GitInfo::new(ignore_git, &src.join("src/tools/cargo")); | |
415 | let rls_info = channel::GitInfo::new(ignore_git, &src.join("src/tools/rls")); | |
f035d41b XL |
416 | let rust_analyzer_info = |
417 | channel::GitInfo::new(ignore_git, &src.join("src/tools/rust-analyzer")); | |
532ac7d7 XL |
418 | let clippy_info = channel::GitInfo::new(ignore_git, &src.join("src/tools/clippy")); |
419 | let miri_info = channel::GitInfo::new(ignore_git, &src.join("src/tools/miri")); | |
420 | let rustfmt_info = channel::GitInfo::new(ignore_git, &src.join("src/tools/rustfmt")); | |
421 | ||
422 | // we always try to use git for LLVM builds | |
423 | let in_tree_llvm_info = channel::GitInfo::new(false, &src.join("src/llvm-project")); | |
476ff2be | 424 | |
f9f354fc XL |
425 | let initial_target_libdir_str = if config.dry_run { |
426 | "/dummy/lib/path/to/lib/".to_string() | |
427 | } else { | |
428 | output( | |
429 | Command::new(&config.initial_rustc) | |
430 | .arg("--target") | |
3dfed10e | 431 | .arg(config.build.rustc_target_arg()) |
f9f354fc XL |
432 | .arg("--print") |
433 | .arg("target-libdir"), | |
434 | ) | |
435 | }; | |
436 | let initial_target_dir = Path::new(&initial_target_libdir_str).parent().unwrap(); | |
437 | let initial_lld = initial_target_dir.join("bin").join("rust-lld"); | |
438 | ||
439 | let initial_sysroot = if config.dry_run { | |
440 | "/dummy".to_string() | |
441 | } else { | |
442 | output(Command::new(&config.initial_rustc).arg("--print").arg("sysroot")) | |
443 | }; | |
444 | let initial_libdir = initial_target_dir | |
445 | .parent() | |
446 | .unwrap() | |
447 | .parent() | |
448 | .unwrap() | |
449 | .strip_prefix(initial_sysroot.trim()) | |
450 | .unwrap() | |
451 | .to_path_buf(); | |
74b04a01 | 452 | |
1b1a35ee XL |
453 | let version = std::fs::read_to_string(src.join("src").join("version")) |
454 | .expect("failed to read src/version"); | |
455 | let version = version.trim(); | |
456 | ||
5e7ed085 FG |
457 | let bootstrap_out = if std::env::var("BOOTSTRAP_PYTHON").is_ok() { |
458 | out.join("bootstrap").join("debug") | |
459 | } else { | |
460 | let workspace_target_dir = std::env::var("CARGO_TARGET_DIR") | |
461 | .map(PathBuf::from) | |
462 | .unwrap_or_else(|_| src.join("target")); | |
463 | let bootstrap_out = workspace_target_dir.join("debug"); | |
04454e1e | 464 | if !bootstrap_out.join("rustc").exists() && !cfg!(test) { |
5e7ed085 FG |
465 | // this restriction can be lifted whenever https://github.com/rust-lang/rfcs/pull/3028 is implemented |
466 | panic!("run `cargo build --bins` before `cargo run`") | |
467 | } | |
468 | bootstrap_out | |
469 | }; | |
470 | ||
83c7162d | 471 | let mut build = Build { |
041b39d2 XL |
472 | initial_rustc: config.initial_rustc.clone(), |
473 | initial_cargo: config.initial_cargo.clone(), | |
74b04a01 | 474 | initial_lld, |
f9f354fc | 475 | initial_libdir, |
041b39d2 | 476 | local_rebuild: config.local_rebuild, |
3b2f2976 | 477 | fail_fast: config.cmd.fail_fast(), |
0531ce1d | 478 | doc_tests: config.cmd.doc_tests(), |
3b2f2976 | 479 | verbosity: config.verbose, |
041b39d2 | 480 | |
3b2f2976 XL |
481 | build: config.build, |
482 | hosts: config.hosts.clone(), | |
483 | targets: config.targets.clone(), | |
041b39d2 | 484 | |
3b2f2976 | 485 | config, |
1b1a35ee | 486 | version: version.to_string(), |
3b2f2976 XL |
487 | src, |
488 | out, | |
5e7ed085 | 489 | bootstrap_out, |
5bcae85e | 490 | |
3b2f2976 XL |
491 | rust_info, |
492 | cargo_info, | |
493 | rls_info, | |
f035d41b | 494 | rust_analyzer_info, |
8faf50e0 | 495 | clippy_info, |
0731742a | 496 | miri_info, |
abe05a73 | 497 | rustfmt_info, |
9fa01778 | 498 | in_tree_llvm_info, |
5bcae85e SL |
499 | cc: HashMap::new(), |
500 | cxx: HashMap::new(), | |
abe05a73 | 501 | ar: HashMap::new(), |
b7449926 | 502 | ranlib: HashMap::new(), |
c30ab7b3 | 503 | crates: HashMap::new(), |
04454e1e | 504 | crate_paths: HashMap::new(), |
3b2f2976 | 505 | is_sudo, |
7cac9316 | 506 | ci_env: CiEnv::current(), |
ea8adc8c | 507 | delayed_failures: RefCell::new(Vec::new()), |
ff7c6d11 | 508 | prerelease_version: Cell::new(None), |
0531ce1d | 509 | tool_artifacts: Default::default(), |
923072b8 FG |
510 | |
511 | #[cfg(feature = "build-metrics")] | |
512 | metrics: metrics::BuildMetrics::init(), | |
83c7162d XL |
513 | }; |
514 | ||
515 | build.verbose("finding compilers"); | |
516 | cc_detect::find(&mut build); | |
17df50a5 XL |
517 | // When running `setup`, the profile is about to change, so any requirements we have now may |
518 | // be different on the next invocation. Don't check for them until the next time x.py is | |
519 | // run. This is ok because `setup` never runs any build commands, so it won't fail if commands are missing. | |
520 | if !matches!(build.config.cmd, Subcommand::Setup { .. }) { | |
521 | build.verbose("running sanity check"); | |
522 | sanity::check(&mut build); | |
523 | } | |
83c7162d XL |
524 | |
525 | // If local-rust is the same major.minor as the current version, then force a | |
526 | // local-rebuild | |
dfeec247 XL |
527 | let local_version_verbose = |
528 | output(Command::new(&build.initial_rustc).arg("--version").arg("--verbose")); | |
83c7162d | 529 | let local_release = local_version_verbose |
dfeec247 | 530 | .lines() |
f035d41b | 531 | .filter_map(|x| x.strip_prefix("release:")) |
dfeec247 XL |
532 | .next() |
533 | .unwrap() | |
dfeec247 | 534 | .trim(); |
1b1a35ee | 535 | if local_release.split('.').take(2).eq(version.split('.').take(2)) { |
83c7162d XL |
536 | build.verbose(&format!("auto-detected local-rebuild {}", local_release)); |
537 | build.local_rebuild = true; | |
5bcae85e | 538 | } |
83c7162d | 539 | |
064997fb FG |
540 | // Make sure we update these before gathering metadata so we don't get an error about missing |
541 | // Cargo.toml files. | |
542 | let rust_submodules = [ | |
543 | "src/tools/rust-installer", | |
544 | "src/tools/cargo", | |
545 | "src/tools/rls", | |
546 | "src/tools/miri", | |
547 | "library/backtrace", | |
548 | "library/stdarch", | |
549 | ]; | |
550 | for s in rust_submodules { | |
551 | build.update_submodule(Path::new(s)); | |
552 | } | |
553 | ||
83c7162d XL |
554 | build.verbose("learning about cargo"); |
555 | metadata::build(&mut build); | |
556 | ||
557 | build | |
5bcae85e SL |
558 | } |
559 | ||
136023e0 XL |
560 | // modified from `check_submodule` and `update_submodule` in bootstrap.py |
561 | /// Given a path to the directory of a submodule, update it. | |
562 | /// | |
563 | /// `relative_path` should be relative to the root of the git repository, not an absolute path. | |
564 | pub(crate) fn update_submodule(&self, relative_path: &Path) { | |
565 | fn dir_is_empty(dir: &Path) -> bool { | |
566 | t!(std::fs::read_dir(dir)).next().is_none() | |
567 | } | |
568 | ||
569 | if !self.config.submodules(&self.rust_info) { | |
570 | return; | |
571 | } | |
572 | ||
573 | let absolute_path = self.config.src.join(relative_path); | |
574 | ||
575 | // NOTE: The check for the empty directory is here because when running x.py the first time, | |
576 | // the submodule won't be checked out. Check it out now so we can build it. | |
3c0e092e | 577 | if !channel::GitInfo::new(false, &absolute_path).is_git() && !dir_is_empty(&absolute_path) { |
136023e0 XL |
578 | return; |
579 | } | |
580 | ||
581 | // check_submodule | |
923072b8 FG |
582 | let checked_out_hash = |
583 | output(Command::new("git").args(&["rev-parse", "HEAD"]).current_dir(&absolute_path)); | |
584 | // update_submodules | |
585 | let recorded = output( | |
586 | Command::new("git") | |
587 | .args(&["ls-tree", "HEAD"]) | |
588 | .arg(relative_path) | |
589 | .current_dir(&self.config.src), | |
590 | ); | |
591 | let actual_hash = recorded | |
592 | .split_whitespace() | |
593 | .nth(2) | |
594 | .unwrap_or_else(|| panic!("unexpected output `{}`", recorded)); | |
595 | ||
596 | // update_submodule | |
597 | if actual_hash == checked_out_hash.trim_end() { | |
598 | // already checked out | |
599 | return; | |
136023e0 XL |
600 | } |
601 | ||
602 | println!("Updating submodule {}", relative_path.display()); | |
603 | self.run( | |
604 | Command::new("git") | |
605 | .args(&["submodule", "-q", "sync"]) | |
606 | .arg(relative_path) | |
607 | .current_dir(&self.config.src), | |
608 | ); | |
609 | ||
610 | // Try passing `--progress` to start, then run git again without if that fails. | |
611 | let update = |progress: bool| { | |
612 | let mut git = Command::new("git"); | |
5099ac24 | 613 | git.args(&["submodule", "update", "--init", "--recursive", "--depth=1"]); |
136023e0 XL |
614 | if progress { |
615 | git.arg("--progress"); | |
616 | } | |
617 | git.arg(relative_path).current_dir(&self.config.src); | |
618 | git | |
619 | }; | |
620 | // NOTE: doesn't use `try_run` because this shouldn't print an error if it fails. | |
621 | if !update(true).status().map_or(false, |status| status.success()) { | |
622 | self.run(&mut update(false)); | |
623 | } | |
624 | ||
625 | self.run(Command::new("git").args(&["reset", "-q", "--hard"]).current_dir(&absolute_path)); | |
626 | self.run(Command::new("git").args(&["clean", "-qdfx"]).current_dir(absolute_path)); | |
627 | } | |
628 | ||
629 | /// If any submodule has been initialized already, sync it unconditionally. | |
630 | /// This avoids contributors checking in a submodule change by accident. | |
631 | pub fn maybe_update_submodules(&self) { | |
136023e0 XL |
632 | // Avoid running git when there isn't a git checkout. |
633 | if !self.config.submodules(&self.rust_info) { | |
634 | return; | |
635 | } | |
636 | let output = output( | |
064997fb FG |
637 | self.config |
638 | .git() | |
136023e0 XL |
639 | .args(&["config", "--file"]) |
640 | .arg(&self.config.src.join(".gitmodules")) | |
641 | .args(&["--get-regexp", "path"]), | |
642 | ); | |
643 | for line in output.lines() { | |
644 | // Look for `submodule.$name.path = $path` | |
645 | // Sample output: `submodule.src/rust-installer.path src/tools/rust-installer` | |
646 | let submodule = Path::new(line.splitn(2, ' ').nth(1).unwrap()); | |
064997fb FG |
647 | // Don't update the submodule unless it's already been cloned. |
648 | if channel::GitInfo::new(false, submodule).is_git() { | |
136023e0 XL |
649 | self.update_submodule(submodule); |
650 | } | |
651 | } | |
652 | } | |
653 | ||
5bcae85e SL |
654 | /// Executes the entire build, as configured by the flags and configuration. |
655 | pub fn build(&mut self) { | |
5bcae85e | 656 | unsafe { |
7cac9316 | 657 | job::setup(self); |
5bcae85e SL |
658 | } |
659 | ||
136023e0 XL |
660 | self.maybe_update_submodules(); |
661 | ||
17df50a5 | 662 | if let Subcommand::Format { check, paths } = &self.config.cmd { |
923072b8 | 663 | return format::format(&builder::Builder::new(&self), *check, &paths); |
dfeec247 XL |
664 | } |
665 | ||
ea8adc8c XL |
666 | if let Subcommand::Clean { all } = self.config.cmd { |
667 | return clean::clean(self, all); | |
5bcae85e SL |
668 | } |
669 | ||
29967ef6 | 670 | if let Subcommand::Setup { profile } = &self.config.cmd { |
5e7ed085 | 671 | return setup::setup(&self.config, *profile); |
1b1a35ee XL |
672 | } |
673 | ||
83c7162d XL |
674 | { |
675 | let builder = builder::Builder::new(&self); | |
676 | if let Some(path) = builder.paths.get(0) { | |
677 | if path == Path::new("nonexistent/path/to/trigger/cargo/metadata") { | |
678 | return; | |
679 | } | |
680 | } | |
5bcae85e | 681 | } |
5bcae85e | 682 | |
83c7162d XL |
683 | if !self.config.dry_run { |
684 | { | |
685 | self.config.dry_run = true; | |
686 | let builder = builder::Builder::new(&self); | |
687 | builder.execute_cli(); | |
688 | } | |
689 | self.config.dry_run = false; | |
690 | let builder = builder::Builder::new(&self); | |
691 | builder.execute_cli(); | |
692 | } else { | |
693 | let builder = builder::Builder::new(&self); | |
74b04a01 | 694 | builder.execute_cli(); |
83c7162d | 695 | } |
ea8adc8c XL |
696 | |
697 | // Check for postponed failures from `test --no-fail-fast`. | |
698 | let failures = self.delayed_failures.borrow(); | |
699 | if failures.len() > 0 { | |
923072b8 | 700 | eprintln!("\n{} command(s) did not execute successfully:\n", failures.len()); |
ea8adc8c | 701 | for failure in failures.iter() { |
923072b8 | 702 | eprintln!(" - {}\n", failure); |
ea8adc8c | 703 | } |
064997fb | 704 | detail_exit(1); |
ea8adc8c | 705 | } |
923072b8 FG |
706 | |
707 | #[cfg(feature = "build-metrics")] | |
708 | self.metrics.persist(self); | |
5bcae85e SL |
709 | } |
710 | ||
5bcae85e SL |
711 | /// Clear out `dir` if `input` is newer. |
712 | /// | |
713 | /// After this executes, it will also ensure that `dir` exists. | |
abe05a73 | 714 | fn clear_if_dirty(&self, dir: &Path, input: &Path) -> bool { |
5bcae85e | 715 | let stamp = dir.join(".stamp"); |
abe05a73 | 716 | let mut cleared = false; |
5bcae85e SL |
717 | if mtime(&stamp) < mtime(input) { |
718 | self.verbose(&format!("Dirty - {}", dir.display())); | |
719 | let _ = fs::remove_dir_all(dir); | |
abe05a73 | 720 | cleared = true; |
9e0c209e | 721 | } else if stamp.exists() { |
abe05a73 | 722 | return cleared; |
5bcae85e SL |
723 | } |
724 | t!(fs::create_dir_all(dir)); | |
725 | t!(File::create(stamp)); | |
abe05a73 | 726 | cleared |
5bcae85e SL |
727 | } |
728 | ||
9fa01778 | 729 | /// Gets the space-separated set of activated features for the standard |
5bcae85e | 730 | /// library. |
29967ef6 | 731 | fn std_features(&self, target: TargetSelection) -> String { |
476ff2be | 732 | let mut features = "panic-unwind".to_string(); |
8bb4bdeb | 733 | |
923072b8 | 734 | match self.config.llvm_libunwind(target) { |
29967ef6 XL |
735 | LlvmLibunwind::InTree => features.push_str(" llvm-libunwind"), |
736 | LlvmLibunwind::System => features.push_str(" system-llvm-libunwind"), | |
737 | LlvmLibunwind::No => {} | |
532ac7d7 | 738 | } |
5bcae85e SL |
739 | if self.config.backtrace { |
740 | features.push_str(" backtrace"); | |
741 | } | |
29967ef6 | 742 | if self.config.profiler_enabled(target) { |
041b39d2 XL |
743 | features.push_str(" profiler"); |
744 | } | |
745 | features | |
5bcae85e SL |
746 | } |
747 | ||
9fa01778 | 748 | /// Gets the space-separated set of activated features for the compiler. |
5e7ed085 FG |
749 | fn rustc_features(&self, kind: Kind) -> String { |
750 | let mut features = vec![]; | |
a1dfa0c6 | 751 | if self.config.jemalloc { |
5e7ed085 | 752 | features.push("jemalloc"); |
5bcae85e | 753 | } |
5e7ed085 FG |
754 | if self.config.llvm_enabled() || kind == Kind::Check { |
755 | features.push("llvm"); | |
756 | } | |
757 | // keep in sync with `bootstrap/compile.rs:rustc_cargo_env` | |
758 | if self.config.rustc_parallel { | |
759 | features.push("rustc_use_parallel_compiler"); | |
60c5eb7d | 760 | } |
1b1a35ee XL |
761 | |
762 | // If debug logging is on, then we want the default for tracing: | |
763 | // https://github.com/tokio-rs/tracing/blob/3dd5c03d907afdf2c39444a29931833335171554/tracing/src/level_filters.rs#L26 | |
764 | // which is everything (including debug/trace/etc.) | |
765 | // if its unset, if debug_assertions is on, then debug_logging will also be on | |
766 | // as well as tracing *ignoring* this feature when debug_assertions is on | |
767 | if !self.config.rust_debug_logging { | |
5e7ed085 | 768 | features.push("max_level_info"); |
1b1a35ee XL |
769 | } |
770 | ||
5e7ed085 | 771 | features.join(" ") |
5bcae85e SL |
772 | } |
773 | ||
774 | /// Component directory that Cargo will produce output into (e.g. | |
775 | /// release/debug) | |
776 | fn cargo_dir(&self) -> &'static str { | |
dfeec247 | 777 | if self.config.rust_optimize { "release" } else { "debug" } |
5bcae85e SL |
778 | } |
779 | ||
abe05a73 | 780 | fn tools_dir(&self, compiler: Compiler) -> PathBuf { |
3dfed10e XL |
781 | let out = self |
782 | .out | |
783 | .join(&*compiler.host.triple) | |
784 | .join(format!("stage{}-tools-bin", compiler.stage)); | |
abe05a73 XL |
785 | t!(fs::create_dir_all(&out)); |
786 | out | |
787 | } | |
788 | ||
5bcae85e SL |
789 | /// Returns the root directory for all output generated in a particular |
790 | /// stage when running with a particular host compiler. | |
791 | /// | |
792 | /// The mode indicates what the root directory is for. | |
3b2f2976 | 793 | fn stage_out(&self, compiler: Compiler, mode: Mode) -> PathBuf { |
5bcae85e | 794 | let suffix = match mode { |
94b46f34 | 795 | Mode::Std => "-std", |
94b46f34 | 796 | Mode::Rustc => "-rustc", |
29967ef6 | 797 | Mode::Codegen => "-codegen", |
8faf50e0 | 798 | Mode::ToolBootstrap => "-bootstrap-tools", |
e1599b0c | 799 | Mode::ToolStd | Mode::ToolRustc => "-tools", |
5bcae85e | 800 | }; |
3dfed10e | 801 | self.out.join(&*compiler.host.triple).join(format!("stage{}{}", compiler.stage, suffix)) |
5bcae85e SL |
802 | } |
803 | ||
804 | /// Returns the root output directory for all Cargo output in a given stage, | |
3b2f2976 | 805 | /// running a particular compiler, whether or not we're building the |
5bcae85e | 806 | /// standard library, and targeting the specified architecture. |
3dfed10e XL |
807 | fn cargo_out(&self, compiler: Compiler, mode: Mode, target: TargetSelection) -> PathBuf { |
808 | self.stage_out(compiler, mode).join(&*target.triple).join(self.cargo_dir()) | |
5bcae85e SL |
809 | } |
810 | ||
811 | /// Root output directory for LLVM compiled for `target` | |
812 | /// | |
813 | /// Note that if LLVM is configured externally then the directory returned | |
814 | /// will likely be empty. | |
3dfed10e XL |
815 | fn llvm_out(&self, target: TargetSelection) -> PathBuf { |
816 | self.out.join(&*target.triple).join("llvm") | |
5bcae85e SL |
817 | } |
818 | ||
3dfed10e XL |
819 | fn lld_out(&self, target: TargetSelection) -> PathBuf { |
820 | self.out.join(&*target.triple).join("lld") | |
0531ce1d XL |
821 | } |
822 | ||
c30ab7b3 | 823 | /// Output directory for all documentation for a target |
3dfed10e XL |
824 | fn doc_out(&self, target: TargetSelection) -> PathBuf { |
825 | self.out.join(&*target.triple).join("doc") | |
c30ab7b3 SL |
826 | } |
827 | ||
6a06907d XL |
828 | fn test_out(&self, target: TargetSelection) -> PathBuf { |
829 | self.out.join(&*target.triple).join("test") | |
830 | } | |
831 | ||
0531ce1d | 832 | /// Output directory for all documentation for a target |
3dfed10e XL |
833 | fn compiler_doc_out(&self, target: TargetSelection) -> PathBuf { |
834 | self.out.join(&*target.triple).join("compiler-doc") | |
0531ce1d XL |
835 | } |
836 | ||
041b39d2 | 837 | /// Output directory for some generated md crate documentation for a target (temporary) |
3dfed10e XL |
838 | fn md_doc_out(&self, target: TargetSelection) -> Interned<PathBuf> { |
839 | INTERNER.intern_path(self.out.join(&*target.triple).join("md-doc")) | |
8bb4bdeb XL |
840 | } |
841 | ||
9fa01778 | 842 | /// Returns `true` if no custom `llvm-config` is set for the specified target. |
5bcae85e SL |
843 | /// |
844 | /// If no custom `llvm-config` was specified then Rust's llvm will be used. | |
3dfed10e | 845 | fn is_rust_llvm(&self, target: TargetSelection) -> bool { |
1b1a35ee XL |
846 | if self.config.llvm_from_ci && target == self.config.build { |
847 | return true; | |
848 | } | |
849 | ||
3b2f2976 | 850 | match self.config.target_config.get(&target) { |
5bcae85e | 851 | Some(ref c) => c.llvm_config.is_none(), |
dfeec247 | 852 | None => true, |
5bcae85e SL |
853 | } |
854 | } | |
855 | ||
5bcae85e | 856 | /// Returns the path to `FileCheck` binary for the specified target |
3dfed10e | 857 | fn llvm_filecheck(&self, target: TargetSelection) -> PathBuf { |
3b2f2976 | 858 | let target_config = self.config.target_config.get(&target); |
0bf4aa26 XL |
859 | if let Some(s) = target_config.and_then(|c| c.llvm_filecheck.as_ref()) { |
860 | s.to_path_buf() | |
861 | } else if let Some(s) = target_config.and_then(|c| c.llvm_config.as_ref()) { | |
32a655c1 | 862 | let llvm_bindir = output(Command::new(s).arg("--bindir")); |
3dfed10e | 863 | let filecheck = Path::new(llvm_bindir.trim()).join(exe("FileCheck", target)); |
0bf4aa26 XL |
864 | if filecheck.exists() { |
865 | filecheck | |
866 | } else { | |
867 | // On Fedora the system LLVM installs FileCheck in the | |
868 | // llvm subdirectory of the libdir. | |
869 | let llvm_libdir = output(Command::new(s).arg("--libdir")); | |
dfeec247 | 870 | let lib_filecheck = |
3dfed10e | 871 | Path::new(llvm_libdir.trim()).join("llvm").join(exe("FileCheck", target)); |
0bf4aa26 XL |
872 | if lib_filecheck.exists() { |
873 | lib_filecheck | |
874 | } else { | |
875 | // Return the most normal file name, even though | |
876 | // it doesn't exist, so that any error message | |
877 | // refers to that. | |
878 | filecheck | |
879 | } | |
880 | } | |
5bcae85e | 881 | } else { |
04454e1e FG |
882 | let base = self.llvm_out(target).join("build"); |
883 | let base = if !self.ninja() && target.contains("msvc") { | |
94b46f34 XL |
884 | if self.config.llvm_optimize { |
885 | if self.config.llvm_release_debuginfo { | |
886 | base.join("RelWithDebInfo") | |
887 | } else { | |
888 | base.join("Release") | |
889 | } | |
890 | } else { | |
891 | base.join("Debug") | |
892 | } | |
5bcae85e | 893 | } else { |
94b46f34 XL |
894 | base |
895 | }; | |
3dfed10e | 896 | base.join("bin").join(exe("FileCheck", target)) |
5bcae85e SL |
897 | } |
898 | } | |
899 | ||
8bb4bdeb | 900 | /// Directory for libraries built from C/C++ code and shared between stages. |
3dfed10e XL |
901 | fn native_dir(&self, target: TargetSelection) -> PathBuf { |
902 | self.out.join(&*target.triple).join("native") | |
8bb4bdeb XL |
903 | } |
904 | ||
5bcae85e SL |
905 | /// Root output directory for rust_test_helpers library compiled for |
906 | /// `target` | |
3dfed10e | 907 | fn test_helpers_out(&self, target: TargetSelection) -> PathBuf { |
8bb4bdeb | 908 | self.native_dir(target).join("rust-test-helpers") |
5bcae85e SL |
909 | } |
910 | ||
476ff2be SL |
911 | /// Adds the `RUST_TEST_THREADS` env var if necessary |
912 | fn add_rust_test_threads(&self, cmd: &mut Command) { | |
913 | if env::var_os("RUST_TEST_THREADS").is_none() { | |
914 | cmd.env("RUST_TEST_THREADS", self.jobs().to_string()); | |
915 | } | |
5bcae85e SL |
916 | } |
917 | ||
5bcae85e SL |
918 | /// Returns the libdir of the snapshot compiler. |
919 | fn rustc_snapshot_libdir(&self) -> PathBuf { | |
3dfed10e | 920 | self.rustc_snapshot_sysroot().join(libdir(self.config.build)) |
8faf50e0 XL |
921 | } |
922 | ||
923 | /// Returns the sysroot of the snapshot compiler. | |
924 | fn rustc_snapshot_sysroot(&self) -> &Path { | |
5e7ed085 FG |
925 | static SYSROOT_CACHE: OnceCell<PathBuf> = once_cell::sync::OnceCell::new(); |
926 | SYSROOT_CACHE.get_or_init(|| { | |
927 | let mut rustc = Command::new(&self.initial_rustc); | |
928 | rustc.args(&["--print", "sysroot"]); | |
929 | output(&mut rustc).trim().into() | |
930 | }) | |
5bcae85e SL |
931 | } |
932 | ||
933 | /// Runs a command, printing out nice contextual information if it fails. | |
934 | fn run(&self, cmd: &mut Command) { | |
dfeec247 XL |
935 | if self.config.dry_run { |
936 | return; | |
937 | } | |
ff7c6d11 | 938 | self.verbose(&format!("running: {:?}", cmd)); |
5099ac24 | 939 | run(cmd, self.is_verbose()) |
5bcae85e SL |
940 | } |
941 | ||
8bb4bdeb XL |
942 | /// Runs a command, printing out nice contextual information if it fails. |
943 | fn run_quiet(&self, cmd: &mut Command) { | |
dfeec247 XL |
944 | if self.config.dry_run { |
945 | return; | |
946 | } | |
8bb4bdeb | 947 | self.verbose(&format!("running: {:?}", cmd)); |
ff7c6d11 | 948 | run_suppressed(cmd) |
8bb4bdeb XL |
949 | } |
950 | ||
ff7c6d11 XL |
951 | /// Runs a command, printing out nice contextual information if it fails. |
952 | /// Exits if the command failed to execute at all, otherwise returns its | |
953 | /// `status.success()`. | |
954 | fn try_run(&self, cmd: &mut Command) -> bool { | |
dfeec247 XL |
955 | if self.config.dry_run { |
956 | return true; | |
957 | } | |
7cac9316 | 958 | self.verbose(&format!("running: {:?}", cmd)); |
5099ac24 | 959 | try_run(cmd, self.is_verbose()) |
7cac9316 XL |
960 | } |
961 | ||
962 | /// Runs a command, printing out nice contextual information if it fails. | |
963 | /// Exits if the command failed to execute at all, otherwise returns its | |
964 | /// `status.success()`. | |
965 | fn try_run_quiet(&self, cmd: &mut Command) -> bool { | |
dfeec247 XL |
966 | if self.config.dry_run { |
967 | return true; | |
968 | } | |
7cac9316 | 969 | self.verbose(&format!("running: {:?}", cmd)); |
ff7c6d11 | 970 | try_run_suppressed(cmd) |
7cac9316 XL |
971 | } |
972 | ||
923072b8 FG |
973 | /// Runs a command, printing out nice contextual information if it fails. |
974 | /// Returns false if do not execute at all, otherwise returns its | |
975 | /// `status.success()`. | |
976 | fn check_run(&self, cmd: &mut Command) -> bool { | |
977 | if self.config.dry_run { | |
978 | return true; | |
979 | } | |
980 | self.verbose(&format!("running: {:?}", cmd)); | |
981 | check_run(cmd, self.is_verbose()) | |
982 | } | |
983 | ||
041b39d2 XL |
984 | pub fn is_verbose(&self) -> bool { |
985 | self.verbosity > 0 | |
986 | } | |
987 | ||
5bcae85e SL |
988 | /// Prints a message if this build is configured in verbose mode. |
989 | fn verbose(&self, msg: &str) { | |
041b39d2 | 990 | if self.is_verbose() { |
5bcae85e SL |
991 | println!("{}", msg); |
992 | } | |
993 | } | |
994 | ||
532ac7d7 XL |
995 | pub fn is_verbose_than(&self, level: usize) -> bool { |
996 | self.verbosity > level | |
997 | } | |
998 | ||
999 | /// Prints a message if this build is configured in more verbose mode than `level`. | |
1000 | fn verbose_than(&self, level: usize, msg: &str) { | |
1001 | if self.is_verbose_than(level) { | |
1002 | println!("{}", msg); | |
1003 | } | |
1004 | } | |
1005 | ||
83c7162d | 1006 | fn info(&self, msg: &str) { |
dfeec247 XL |
1007 | if self.config.dry_run { |
1008 | return; | |
1009 | } | |
83c7162d XL |
1010 | println!("{}", msg); |
1011 | } | |
1012 | ||
5bcae85e SL |
1013 | /// Returns the number of parallel jobs that have been configured for this |
1014 | /// build. | |
1015 | fn jobs(&self) -> u32 { | |
04454e1e | 1016 | self.config.jobs.unwrap_or_else(|| num_cpus::get() as u32) |
5bcae85e SL |
1017 | } |
1018 | ||
ba9703b0 | 1019 | fn debuginfo_map_to(&self, which: GitRepo) -> Option<String> { |
b7449926 | 1020 | if !self.config.rust_remap_debuginfo { |
dfeec247 | 1021 | return None; |
b7449926 XL |
1022 | } |
1023 | ||
ba9703b0 | 1024 | match which { |
b7449926 | 1025 | GitRepo::Rustc => { |
1b1a35ee | 1026 | let sha = self.rust_sha().unwrap_or(&self.version); |
ba9703b0 | 1027 | Some(format!("/rustc/{}", sha)) |
b7449926 | 1028 | } |
ba9703b0 XL |
1029 | GitRepo::Llvm => Some(String::from("/rustc/llvm")), |
1030 | } | |
b7449926 XL |
1031 | } |
1032 | ||
5bcae85e | 1033 | /// Returns the path to the C compiler for the target specified. |
3dfed10e | 1034 | fn cc(&self, target: TargetSelection) -> &Path { |
abe05a73 | 1035 | self.cc[&target].path() |
5bcae85e SL |
1036 | } |
1037 | ||
1038 | /// Returns a list of flags to pass to the C compiler for the target | |
1039 | /// specified. | |
5099ac24 FG |
1040 | fn cflags(&self, target: TargetSelection, which: GitRepo, c: CLang) -> Vec<String> { |
1041 | let base = match c { | |
1042 | CLang::C => &self.cc[&target], | |
1043 | CLang::Cxx => &self.cxx[&target], | |
1044 | }; | |
1045 | ||
5bcae85e | 1046 | // Filter out -O and /O (the optimization flags) that we picked up from |
ea8adc8c | 1047 | // cc-rs because the build scripts will determine that for themselves. |
5099ac24 | 1048 | let mut base = base |
dfeec247 XL |
1049 | .args() |
1050 | .iter() | |
1051 | .map(|s| s.to_string_lossy().into_owned()) | |
1052 | .filter(|s| !s.starts_with("-O") && !s.starts_with("/O")) | |
1053 | .collect::<Vec<String>>(); | |
5bcae85e | 1054 | |
cc61c64b | 1055 | // If we're compiling on macOS then we add a few unconditional flags |
5bcae85e SL |
1056 | // indicating that we want libc++ (more filled out than libstdc++) and |
1057 | // we want to compile for 10.7. This way we can ensure that | |
a1dfa0c6 | 1058 | // LLVM/etc are all properly compiled. |
5bcae85e SL |
1059 | if target.contains("apple-darwin") { |
1060 | base.push("-stdlib=libc++".into()); | |
5bcae85e | 1061 | } |
7cac9316 XL |
1062 | |
1063 | // Work around an apparently bad MinGW / GCC optimization, | |
136023e0 | 1064 | // See: https://lists.llvm.org/pipermail/cfe-dev/2016-December/051980.html |
7cac9316 | 1065 | // See: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=78936 |
3dfed10e | 1066 | if &*target.triple == "i686-pc-windows-gnu" { |
7cac9316 XL |
1067 | base.push("-fno-omit-frame-pointer".into()); |
1068 | } | |
b7449926 | 1069 | |
ba9703b0 XL |
1070 | if let Some(map_to) = self.debuginfo_map_to(which) { |
1071 | let map = format!("{}={}", self.src.display(), map_to); | |
dfeec247 | 1072 | let cc = self.cc(target); |
b7449926 | 1073 | if cc.ends_with("clang") || cc.ends_with("gcc") { |
a1dfa0c6 | 1074 | base.push(format!("-fdebug-prefix-map={}", map)); |
b7449926 XL |
1075 | } else if cc.ends_with("clang-cl.exe") { |
1076 | base.push("-Xclang".into()); | |
a1dfa0c6 | 1077 | base.push(format!("-fdebug-prefix-map={}", map)); |
b7449926 XL |
1078 | } |
1079 | } | |
041b39d2 | 1080 | base |
5bcae85e SL |
1081 | } |
1082 | ||
1083 | /// Returns the path to the `ar` archive utility for the target specified. | |
3dfed10e | 1084 | fn ar(&self, target: TargetSelection) -> Option<&Path> { |
abe05a73 | 1085 | self.ar.get(&target).map(|p| &**p) |
5bcae85e SL |
1086 | } |
1087 | ||
b7449926 | 1088 | /// Returns the path to the `ranlib` utility for the target specified. |
3dfed10e | 1089 | fn ranlib(&self, target: TargetSelection) -> Option<&Path> { |
b7449926 XL |
1090 | self.ranlib.get(&target).map(|p| &**p) |
1091 | } | |
1092 | ||
041b39d2 | 1093 | /// Returns the path to the C++ compiler for the target specified. |
3dfed10e | 1094 | fn cxx(&self, target: TargetSelection) -> Result<&Path, String> { |
3b2f2976 | 1095 | match self.cxx.get(&target) { |
041b39d2 | 1096 | Some(p) => Ok(p.path()), |
dfeec247 XL |
1097 | None => { |
1098 | Err(format!("target `{}` is not configured as a host, only as a target", target)) | |
1099 | } | |
9e0c209e | 1100 | } |
5bcae85e SL |
1101 | } |
1102 | ||
2c00a5a8 | 1103 | /// Returns the path to the linker for the given target if it needs to be overridden. |
1b1a35ee | 1104 | fn linker(&self, target: TargetSelection) -> Option<&Path> { |
dfeec247 XL |
1105 | if let Some(linker) = self.config.target_config.get(&target).and_then(|c| c.linker.as_ref()) |
1106 | { | |
abe05a73 | 1107 | Some(linker) |
1b1a35ee XL |
1108 | } else if target.contains("vxworks") { |
1109 | // need to use CXX compiler as linker to resolve the exception functions | |
1110 | // that are only existed in CXX libraries | |
1111 | Some(self.cxx[&target].path()) | |
dfeec247 | 1112 | } else if target != self.config.build |
3dfed10e | 1113 | && util::use_host_linker(target) |
dfeec247 XL |
1114 | && !target.contains("msvc") |
1115 | { | |
abe05a73 | 1116 | Some(self.cc(target)) |
1b1a35ee | 1117 | } else if self.config.use_lld && !self.is_fuse_ld_lld(target) && self.build == target { |
74b04a01 | 1118 | Some(&self.initial_lld) |
abe05a73 XL |
1119 | } else { |
1120 | None | |
5bcae85e | 1121 | } |
85aaf69f | 1122 | } |
9e0c209e | 1123 | |
1b1a35ee XL |
1124 | // LLD is used through `-fuse-ld=lld` rather than directly. |
1125 | // Only MSVC targets use LLD directly at the moment. | |
1126 | fn is_fuse_ld_lld(&self, target: TargetSelection) -> bool { | |
1127 | self.config.use_lld && !target.contains("msvc") | |
1128 | } | |
1129 | ||
17df50a5 XL |
1130 | fn lld_flags(&self, target: TargetSelection) -> impl Iterator<Item = String> { |
1131 | let mut options = [None, None]; | |
1132 | ||
1133 | if self.config.use_lld { | |
1134 | if self.is_fuse_ld_lld(target) { | |
1135 | options[0] = Some("-Clink-arg=-fuse-ld=lld".to_string()); | |
1136 | } | |
1137 | ||
1138 | let threads = if target.contains("windows") { "/threads:1" } else { "--threads=1" }; | |
1139 | options[1] = Some(format!("-Clink-arg=-Wl,{}", threads)); | |
1140 | } | |
1141 | ||
a2a8927a | 1142 | IntoIterator::into_iter(options).flatten() |
17df50a5 XL |
1143 | } |
1144 | ||
3b2f2976 | 1145 | /// Returns if this target should statically link the C runtime, if specified |
3dfed10e | 1146 | fn crt_static(&self, target: TargetSelection) -> Option<bool> { |
3b2f2976 XL |
1147 | if target.contains("pc-windows-msvc") { |
1148 | Some(true) | |
1149 | } else { | |
dfeec247 | 1150 | self.config.target_config.get(&target).and_then(|t| t.crt_static) |
3b2f2976 XL |
1151 | } |
1152 | } | |
1153 | ||
9e0c209e | 1154 | /// Returns the "musl root" for this `target`, if defined |
3dfed10e | 1155 | fn musl_root(&self, target: TargetSelection) -> Option<&Path> { |
dfeec247 XL |
1156 | self.config |
1157 | .target_config | |
1158 | .get(&target) | |
c30ab7b3 | 1159 | .and_then(|t| t.musl_root.as_ref()) |
74b04a01 | 1160 | .or_else(|| self.config.musl_root.as_ref()) |
9e0c209e SL |
1161 | .map(|p| &**p) |
1162 | } | |
476ff2be | 1163 | |
f035d41b | 1164 | /// Returns the "musl libdir" for this `target`. |
3dfed10e | 1165 | fn musl_libdir(&self, target: TargetSelection) -> Option<PathBuf> { |
f035d41b XL |
1166 | let t = self.config.target_config.get(&target)?; |
1167 | if let libdir @ Some(_) = &t.musl_libdir { | |
1168 | return libdir.clone(); | |
1169 | } | |
1170 | self.musl_root(target).map(|root| root.join("lib")) | |
1171 | } | |
1172 | ||
532ac7d7 | 1173 | /// Returns the sysroot for the wasi target, if defined |
3dfed10e | 1174 | fn wasi_root(&self, target: TargetSelection) -> Option<&Path> { |
dfeec247 | 1175 | self.config.target_config.get(&target).and_then(|t| t.wasi_root.as_ref()).map(|p| &**p) |
532ac7d7 XL |
1176 | } |
1177 | ||
9fa01778 | 1178 | /// Returns `true` if this is a no-std `target`, if defined |
3dfed10e | 1179 | fn no_std(&self, target: TargetSelection) -> Option<bool> { |
dfeec247 | 1180 | self.config.target_config.get(&target).map(|t| t.no_std) |
83c7162d XL |
1181 | } |
1182 | ||
9fa01778 | 1183 | /// Returns `true` if the target will be tested using the `remote-test-client` |
7cac9316 | 1184 | /// and `remote-test-server` binaries. |
3dfed10e | 1185 | fn remote_tested(&self, target: TargetSelection) -> bool { |
dfeec247 XL |
1186 | self.qemu_rootfs(target).is_some() |
1187 | || target.contains("android") | |
1188 | || env::var_os("TEST_DEVICE_ADDR").is_some() | |
7cac9316 XL |
1189 | } |
1190 | ||
8bb4bdeb XL |
1191 | /// Returns the root of the "rootfs" image that this target will be using, |
1192 | /// if one was configured. | |
1193 | /// | |
1194 | /// If `Some` is returned then that means that tests for this target are | |
1195 | /// emulated with QEMU and binaries will need to be shipped to the emulator. | |
3dfed10e | 1196 | fn qemu_rootfs(&self, target: TargetSelection) -> Option<&Path> { |
dfeec247 | 1197 | self.config.target_config.get(&target).and_then(|t| t.qemu_rootfs.as_ref()).map(|p| &**p) |
8bb4bdeb XL |
1198 | } |
1199 | ||
476ff2be SL |
1200 | /// Path to the python interpreter to use |
1201 | fn python(&self) -> &Path { | |
04454e1e FG |
1202 | if self.config.build.ends_with("apple-darwin") { |
1203 | // Force /usr/bin/python3 on macOS for LLDB tests because we're loading the | |
1204 | // LLDB plugin's compiled module which only works with the system python | |
1205 | // (namely not Homebrew-installed python) | |
1206 | Path::new("/usr/bin/python3") | |
1207 | } else { | |
1208 | self.config | |
1209 | .python | |
1210 | .as_ref() | |
1211 | .expect("python is required for running LLDB or rustdoc tests") | |
1212 | } | |
476ff2be | 1213 | } |
32a655c1 | 1214 | |
ff7c6d11 XL |
1215 | /// Temporary directory that extended error information is emitted to. |
1216 | fn extended_error_dir(&self) -> PathBuf { | |
1217 | self.out.join("tmp/extended-error-metadata") | |
1218 | } | |
1219 | ||
32a655c1 SL |
1220 | /// Tests whether the `compiler` compiling for `target` should be forced to |
1221 | /// use a stage1 compiler instead. | |
1222 | /// | |
1223 | /// Currently, by default, the build system does not perform a "full | |
1224 | /// bootstrap" by default where we compile the compiler three times. | |
1225 | /// Instead, we compile the compiler two times. The final stage (stage2) | |
1226 | /// just copies the libraries from the previous stage, which is what this | |
1227 | /// method detects. | |
1228 | /// | |
1229 | /// Here we return `true` if: | |
1230 | /// | |
1231 | /// * The build isn't performing a full bootstrap | |
1232 | /// * The `compiler` is in the final stage, 2 | |
1233 | /// * We're not cross-compiling, so the artifacts are already available in | |
1234 | /// stage1 | |
1235 | /// | |
1236 | /// When all of these conditions are met the build will lift artifacts from | |
1237 | /// the previous stage forward. | |
3dfed10e | 1238 | fn force_use_stage1(&self, compiler: Compiler, target: TargetSelection) -> bool { |
dfeec247 XL |
1239 | !self.config.full_bootstrap |
1240 | && compiler.stage >= 2 | |
1241 | && (self.hosts.iter().any(|h| *h == target) || target == self.build) | |
32a655c1 | 1242 | } |
8bb4bdeb | 1243 | |
8bb4bdeb XL |
1244 | /// Given `num` in the form "a.b.c" return a "release string" which |
1245 | /// describes the release version number. | |
1246 | /// | |
1247 | /// For example on nightly this returns "a.b.c-nightly", on beta it returns | |
1248 | /// "a.b.c-beta.1" and on stable it just returns "a.b.c". | |
1249 | fn release(&self, num: &str) -> String { | |
1250 | match &self.config.channel[..] { | |
1251 | "stable" => num.to_string(), | |
dfeec247 | 1252 | "beta" => { |
94222f64 | 1253 | if self.rust_info.is_git() && !self.config.ignore_git { |
dfeec247 XL |
1254 | format!("{}-beta.{}", num, self.beta_prerelease_version()) |
1255 | } else { | |
1256 | format!("{}-beta", num) | |
1257 | } | |
1258 | } | |
8bb4bdeb XL |
1259 | "nightly" => format!("{}-nightly", num), |
1260 | _ => format!("{}-dev", num), | |
1261 | } | |
1262 | } | |
1263 | ||
ff7c6d11 XL |
1264 | fn beta_prerelease_version(&self) -> u32 { |
1265 | if let Some(s) = self.prerelease_version.get() { | |
dfeec247 | 1266 | return s; |
ff7c6d11 XL |
1267 | } |
1268 | ||
f035d41b XL |
1269 | // Figure out how many merge commits happened since we branched off master. |
1270 | // That's our beta number! | |
1271 | // (Note that we use a `..` range, not the `...` symmetric difference.) | |
064997fb FG |
1272 | let count = |
1273 | output(self.config.git().arg("rev-list").arg("--count").arg("--merges").arg(format!( | |
1274 | "refs/remotes/origin/{}..HEAD", | |
1275 | self.config.stage0_metadata.config.nightly_branch | |
1276 | ))); | |
ff7c6d11 XL |
1277 | let n = count.trim().parse().unwrap(); |
1278 | self.prerelease_version.set(Some(n)); | |
2c00a5a8 | 1279 | n |
ff7c6d11 XL |
1280 | } |
1281 | ||
8bb4bdeb XL |
1282 | /// Returns the value of `release` above for Rust itself. |
1283 | fn rust_release(&self) -> String { | |
1b1a35ee | 1284 | self.release(&self.version) |
8bb4bdeb XL |
1285 | } |
1286 | ||
1287 | /// Returns the "package version" for a component given the `num` release | |
1288 | /// number. | |
1289 | /// | |
1290 | /// The package version is typically what shows up in the names of tarballs. | |
1291 | /// For channels like beta/nightly it's just the channel name, otherwise | |
1292 | /// it's the `num` provided. | |
1293 | fn package_vers(&self, num: &str) -> String { | |
1294 | match &self.config.channel[..] { | |
1295 | "stable" => num.to_string(), | |
1296 | "beta" => "beta".to_string(), | |
1297 | "nightly" => "nightly".to_string(), | |
1298 | _ => format!("{}-dev", num), | |
1299 | } | |
1300 | } | |
1301 | ||
1302 | /// Returns the value of `package_vers` above for Rust itself. | |
1303 | fn rust_package_vers(&self) -> String { | |
1b1a35ee | 1304 | self.package_vers(&self.version) |
8faf50e0 XL |
1305 | } |
1306 | ||
3dfed10e | 1307 | fn llvm_link_tools_dynamically(&self, target: TargetSelection) -> bool { |
74b04a01 | 1308 | target.contains("linux-gnu") || target.contains("apple-darwin") |
b7449926 XL |
1309 | } |
1310 | ||
8bb4bdeb XL |
1311 | /// Returns the `version` string associated with this compiler for Rust |
1312 | /// itself. | |
1313 | /// | |
1314 | /// Note that this is a descriptive string which includes the commit date, | |
1315 | /// sha, version, etc. | |
1316 | fn rust_version(&self) -> String { | |
fc512014 XL |
1317 | let mut version = self.rust_info.version(self, &self.version); |
1318 | if let Some(ref s) = self.config.description { | |
1319 | version.push_str(" ("); | |
1320 | version.push_str(s); | |
5869c6ff | 1321 | version.push(')'); |
fc512014 XL |
1322 | } |
1323 | version | |
8bb4bdeb XL |
1324 | } |
1325 | ||
9fa01778 | 1326 | /// Returns the full commit hash. |
ea8adc8c XL |
1327 | fn rust_sha(&self) -> Option<&str> { |
1328 | self.rust_info.sha() | |
1329 | } | |
1330 | ||
cc61c64b XL |
1331 | /// Returns the `a.b.c` version that the given package is at. |
1332 | fn release_num(&self, package: &str) -> String { | |
7cac9316 | 1333 | let toml_file_name = self.src.join(&format!("src/tools/{}/Cargo.toml", package)); |
0731742a | 1334 | let toml = t!(fs::read_to_string(&toml_file_name)); |
8bb4bdeb | 1335 | for line in toml.lines() { |
f035d41b XL |
1336 | if let Some(stripped) = |
1337 | line.strip_prefix("version = \"").and_then(|s| s.strip_suffix("\"")) | |
1338 | { | |
1339 | return stripped.to_owned(); | |
8bb4bdeb XL |
1340 | } |
1341 | } | |
1342 | ||
cc61c64b | 1343 | panic!("failed to find version in {}'s Cargo.toml", package) |
8bb4bdeb XL |
1344 | } |
1345 | ||
9fa01778 | 1346 | /// Returns `true` if unstable features should be enabled for the compiler |
8bb4bdeb XL |
1347 | /// we're building. |
1348 | fn unstable_features(&self) -> bool { | |
1349 | match &self.config.channel[..] { | |
1350 | "stable" | "beta" => false, | |
1351 | "nightly" | _ => true, | |
1352 | } | |
1353 | } | |
7cac9316 | 1354 | |
f035d41b XL |
1355 | /// Returns a Vec of all the dependencies of the given root crate, |
1356 | /// including transitive dependencies and the root itself. Only includes | |
1357 | /// "local" crates (those in the local source tree, not from a registry). | |
29967ef6 | 1358 | fn in_tree_crates(&self, root: &str, target: Option<TargetSelection>) -> Vec<&Crate> { |
3b2f2976 | 1359 | let mut ret = Vec::new(); |
2c00a5a8 | 1360 | let mut list = vec![INTERNER.intern_str(root)]; |
3b2f2976 XL |
1361 | let mut visited = HashSet::new(); |
1362 | while let Some(krate) = list.pop() { | |
1363 | let krate = &self.crates[&krate]; | |
f035d41b | 1364 | ret.push(krate); |
0731742a | 1365 | for dep in &krate.deps { |
29967ef6 XL |
1366 | if !self.crates.contains_key(dep) { |
1367 | // Ignore non-workspace members. | |
1368 | continue; | |
1369 | } | |
f035d41b XL |
1370 | // Don't include optional deps if their features are not |
1371 | // enabled. Ideally this would be computed from `cargo | |
5e7ed085 FG |
1372 | // metadata --features …`, but that is somewhat slow. In |
1373 | // the future, we may want to consider just filtering all | |
1374 | // build and dev dependencies in metadata::build. | |
f035d41b | 1375 | if visited.insert(dep) |
29967ef6 XL |
1376 | && (dep != "profiler_builtins" |
1377 | || target | |
1378 | .map(|t| self.config.profiler_enabled(t)) | |
5869c6ff | 1379 | .unwrap_or_else(|| self.config.any_profiler_enabled())) |
f035d41b XL |
1380 | && (dep != "rustc_codegen_llvm" || self.config.llvm_enabled()) |
1381 | { | |
0731742a | 1382 | list.push(*dep); |
3b2f2976 XL |
1383 | } |
1384 | } | |
1385 | } | |
1386 | ret | |
1387 | } | |
83c7162d | 1388 | |
f035d41b | 1389 | fn read_stamp_file(&self, stamp: &Path) -> Vec<(PathBuf, DependencyType)> { |
83c7162d XL |
1390 | if self.config.dry_run { |
1391 | return Vec::new(); | |
1392 | } | |
1393 | ||
1394 | let mut paths = Vec::new(); | |
e74abb32 | 1395 | let contents = t!(fs::read(stamp), &stamp); |
83c7162d XL |
1396 | // This is the method we use for extracting paths from the stamp file passed to us. See |
1397 | // run_cargo for more information (in compile.rs). | |
1398 | for part in contents.split(|b| *b == 0) { | |
1399 | if part.is_empty() { | |
dfeec247 | 1400 | continue; |
83c7162d | 1401 | } |
f035d41b XL |
1402 | let dependency_type = match part[0] as char { |
1403 | 'h' => DependencyType::Host, | |
1404 | 's' => DependencyType::TargetSelfContained, | |
1405 | 't' => DependencyType::Target, | |
1406 | _ => unreachable!(), | |
1407 | }; | |
532ac7d7 | 1408 | let path = PathBuf::from(t!(str::from_utf8(&part[1..]))); |
f035d41b | 1409 | paths.push((path, dependency_type)); |
83c7162d XL |
1410 | } |
1411 | paths | |
1412 | } | |
1413 | ||
04454e1e FG |
1414 | /// Create a temporary directory in `out` and return its path. |
1415 | /// | |
1416 | /// NOTE: this temporary directory is shared between all steps; | |
1417 | /// if you need an empty directory, create a new subdirectory inside it. | |
1418 | fn tempdir(&self) -> PathBuf { | |
1419 | let tmp = self.out.join("tmp"); | |
1420 | t!(fs::create_dir_all(&tmp)); | |
1421 | tmp | |
1422 | } | |
1423 | ||
83c7162d XL |
1424 | /// Copies a file from `src` to `dst` |
1425 | pub fn copy(&self, src: &Path, dst: &Path) { | |
923072b8 FG |
1426 | self.copy_internal(src, dst, false); |
1427 | } | |
1428 | ||
1429 | fn copy_internal(&self, src: &Path, dst: &Path, dereference_symlinks: bool) { | |
dfeec247 XL |
1430 | if self.config.dry_run { |
1431 | return; | |
1432 | } | |
532ac7d7 | 1433 | self.verbose_than(1, &format!("Copy {:?} to {:?}", src, dst)); |
dfeec247 XL |
1434 | if src == dst { |
1435 | return; | |
1436 | } | |
83c7162d | 1437 | let _ = fs::remove_file(&dst); |
b7449926 | 1438 | let metadata = t!(src.symlink_metadata()); |
923072b8 | 1439 | let mut src = src.to_path_buf(); |
b7449926 | 1440 | if metadata.file_type().is_symlink() { |
923072b8 FG |
1441 | if dereference_symlinks { |
1442 | src = t!(fs::canonicalize(src)); | |
1443 | } else { | |
1444 | let link = t!(fs::read_link(src)); | |
064997fb | 1445 | t!(self.symlink_file(link, dst)); |
923072b8 FG |
1446 | return; |
1447 | } | |
1448 | } | |
1449 | if let Ok(()) = fs::hard_link(&src, dst) { | |
b7449926 XL |
1450 | // Attempt to "easy copy" by creating a hard link |
1451 | // (symlinks don't work on windows), but if that fails | |
1452 | // just fall back to a slow `copy` operation. | |
1453 | } else { | |
923072b8 | 1454 | if let Err(e) = fs::copy(&src, dst) { |
dfeec247 | 1455 | panic!("failed to copy `{}` to `{}`: {}", src.display(), dst.display(), e) |
b7449926 XL |
1456 | } |
1457 | t!(fs::set_permissions(dst, metadata.permissions())); | |
1458 | let atime = FileTime::from_last_access_time(&metadata); | |
1459 | let mtime = FileTime::from_last_modification_time(&metadata); | |
1460 | t!(filetime::set_file_times(dst, atime, mtime)); | |
83c7162d | 1461 | } |
83c7162d XL |
1462 | } |
1463 | ||
83c7162d XL |
1464 | /// Copies the `src` directory recursively to `dst`. Both are assumed to exist |
1465 | /// when this function is called. | |
1466 | pub fn cp_r(&self, src: &Path, dst: &Path) { | |
dfeec247 XL |
1467 | if self.config.dry_run { |
1468 | return; | |
1469 | } | |
dc9dc135 | 1470 | for f in self.read_dir(src) { |
83c7162d XL |
1471 | let path = f.path(); |
1472 | let name = path.file_name().unwrap(); | |
1473 | let dst = dst.join(name); | |
1474 | if t!(f.file_type()).is_dir() { | |
1475 | t!(fs::create_dir_all(&dst)); | |
1476 | self.cp_r(&path, &dst); | |
1477 | } else { | |
1478 | let _ = fs::remove_file(&dst); | |
1479 | self.copy(&path, &dst); | |
1480 | } | |
1481 | } | |
1482 | } | |
1483 | ||
1484 | /// Copies the `src` directory recursively to `dst`. Both are assumed to exist | |
1485 | /// when this function is called. Unwanted files or directories can be skipped | |
1486 | /// by returning `false` from the filter function. | |
8faf50e0 | 1487 | pub fn cp_filtered(&self, src: &Path, dst: &Path, filter: &dyn Fn(&Path) -> bool) { |
83c7162d XL |
1488 | // Immediately recurse with an empty relative path |
1489 | self.recurse_(src, dst, Path::new(""), filter) | |
1490 | } | |
1491 | ||
1492 | // Inner function does the actual work | |
8faf50e0 | 1493 | fn recurse_(&self, src: &Path, dst: &Path, relative: &Path, filter: &dyn Fn(&Path) -> bool) { |
83c7162d XL |
1494 | for f in self.read_dir(src) { |
1495 | let path = f.path(); | |
1496 | let name = path.file_name().unwrap(); | |
1497 | let dst = dst.join(name); | |
1498 | let relative = relative.join(name); | |
1499 | // Only copy file or directory if the filter function returns true | |
1500 | if filter(&relative) { | |
1501 | if t!(f.file_type()).is_dir() { | |
1502 | let _ = fs::remove_dir_all(&dst); | |
1503 | self.create_dir(&dst); | |
1504 | self.recurse_(&path, &dst, &relative, filter); | |
1505 | } else { | |
1506 | let _ = fs::remove_file(&dst); | |
1507 | self.copy(&path, &dst); | |
1508 | } | |
1509 | } | |
1510 | } | |
1511 | } | |
1512 | ||
1513 | fn copy_to_folder(&self, src: &Path, dest_folder: &Path) { | |
1514 | let file_name = src.file_name().unwrap(); | |
1515 | let dest = dest_folder.join(file_name); | |
1516 | self.copy(src, &dest); | |
1517 | } | |
1518 | ||
1519 | fn install(&self, src: &Path, dstdir: &Path, perms: u32) { | |
dfeec247 XL |
1520 | if self.config.dry_run { |
1521 | return; | |
1522 | } | |
83c7162d | 1523 | let dst = dstdir.join(src.file_name().unwrap()); |
532ac7d7 | 1524 | self.verbose_than(1, &format!("Install {:?} to {:?}", src, dst)); |
83c7162d | 1525 | t!(fs::create_dir_all(dstdir)); |
923072b8 FG |
1526 | if !src.exists() { |
1527 | panic!("Error: File \"{}\" not found!", src.display()); | |
83c7162d | 1528 | } |
923072b8 | 1529 | self.copy_internal(src, &dst, true); |
83c7162d XL |
1530 | chmod(&dst, perms); |
1531 | } | |
1532 | ||
1533 | fn create(&self, path: &Path, s: &str) { | |
dfeec247 XL |
1534 | if self.config.dry_run { |
1535 | return; | |
1536 | } | |
83c7162d XL |
1537 | t!(fs::write(path, s)); |
1538 | } | |
1539 | ||
1540 | fn read(&self, path: &Path) -> String { | |
dfeec247 XL |
1541 | if self.config.dry_run { |
1542 | return String::new(); | |
1543 | } | |
83c7162d XL |
1544 | t!(fs::read_to_string(path)) |
1545 | } | |
1546 | ||
1547 | fn create_dir(&self, dir: &Path) { | |
dfeec247 XL |
1548 | if self.config.dry_run { |
1549 | return; | |
1550 | } | |
83c7162d XL |
1551 | t!(fs::create_dir_all(dir)) |
1552 | } | |
1553 | ||
1554 | fn remove_dir(&self, dir: &Path) { | |
dfeec247 XL |
1555 | if self.config.dry_run { |
1556 | return; | |
1557 | } | |
83c7162d XL |
1558 | t!(fs::remove_dir_all(dir)) |
1559 | } | |
1560 | ||
dfeec247 | 1561 | fn read_dir(&self, dir: &Path) -> impl Iterator<Item = fs::DirEntry> { |
83c7162d XL |
1562 | let iter = match fs::read_dir(dir) { |
1563 | Ok(v) => v, | |
1564 | Err(_) if self.config.dry_run => return vec![].into_iter(), | |
1565 | Err(err) => panic!("could not read dir {:?}: {:?}", dir, err), | |
1566 | }; | |
1567 | iter.map(|e| t!(e)).collect::<Vec<_>>().into_iter() | |
1568 | } | |
1569 | ||
064997fb FG |
1570 | fn symlink_file<P: AsRef<Path>, Q: AsRef<Path>>(&self, src: P, link: Q) -> io::Result<()> { |
1571 | #[cfg(unix)] | |
1572 | use std::os::unix::fs::symlink as symlink_file; | |
1573 | #[cfg(windows)] | |
1574 | use std::os::windows::fs::symlink_file; | |
1575 | if !self.config.dry_run { symlink_file(src.as_ref(), link.as_ref()) } else { Ok(()) } | |
1576 | } | |
1577 | ||
83c7162d | 1578 | fn remove(&self, f: &Path) { |
dfeec247 XL |
1579 | if self.config.dry_run { |
1580 | return; | |
1581 | } | |
83c7162d XL |
1582 | fs::remove_file(f).unwrap_or_else(|_| panic!("failed to remove {:?}", f)); |
1583 | } | |
1b1a35ee XL |
1584 | |
1585 | /// Returns if config.ninja is enabled, and checks for ninja existence, | |
1586 | /// exiting with a nicer error message if not. | |
1587 | fn ninja(&self) -> bool { | |
1588 | let mut cmd_finder = crate::sanity::Finder::new(); | |
1589 | ||
1590 | if self.config.ninja_in_file { | |
1591 | // Some Linux distros rename `ninja` to `ninja-build`. | |
1592 | // CMake can work with either binary name. | |
1593 | if cmd_finder.maybe_have("ninja-build").is_none() | |
1594 | && cmd_finder.maybe_have("ninja").is_none() | |
1595 | { | |
1596 | eprintln!( | |
1597 | " | |
c295e0f8 XL |
1598 | Couldn't find required command: ninja (or ninja-build) |
1599 | ||
1600 | You should install ninja as described at | |
1601 | <https://github.com/ninja-build/ninja/wiki/Pre-built-Ninja-packages>, | |
1602 | or set `ninja = false` in the `[llvm]` section of `config.toml`. | |
1603 | Alternatively, set `download-ci-llvm = true` in that `[llvm]` section | |
1604 | to download LLVM rather than building it. | |
1b1a35ee XL |
1605 | " |
1606 | ); | |
064997fb | 1607 | detail_exit(1); |
1b1a35ee XL |
1608 | } |
1609 | } | |
1610 | ||
1611 | // If ninja isn't enabled but we're building for MSVC then we try | |
1612 | // doubly hard to enable it. It was realized in #43767 that the msbuild | |
1613 | // CMake generator for MSVC doesn't respect configuration options like | |
1614 | // disabling LLVM assertions, which can often be quite important! | |
1615 | // | |
1616 | // In these cases we automatically enable Ninja if we find it in the | |
1617 | // environment. | |
1618 | if !self.config.ninja_in_file && self.config.build.contains("msvc") { | |
1619 | if cmd_finder.maybe_have("ninja").is_some() { | |
1620 | return true; | |
1621 | } | |
1622 | } | |
1623 | ||
1624 | self.config.ninja_in_file | |
1625 | } | |
85aaf69f SL |
1626 | } |
1627 | ||
83c7162d XL |
1628 | #[cfg(unix)] |
1629 | fn chmod(path: &Path, perms: u32) { | |
1630 | use std::os::unix::fs::*; | |
1631 | t!(fs::set_permissions(path, fs::Permissions::from_mode(perms))); | |
1632 | } | |
1633 | #[cfg(windows)] | |
1634 | fn chmod(_path: &Path, _perms: u32) {} | |
1635 | ||
064997fb FG |
1636 | /// If code is not 0 (successful exit status), exit status is 101 (rust's default error code.) |
1637 | /// If the test is running and code is an error code, it will cause a panic. | |
1638 | fn detail_exit(code: i32) -> ! { | |
1639 | // Successful exit | |
1640 | if code == 0 { | |
1641 | std::process::exit(0); | |
1642 | } | |
1643 | if cfg!(test) { | |
1644 | panic!("status code: {}", code); | |
1645 | } else { | |
1646 | std::panic::resume_unwind(Box::new(code)); | |
1647 | } | |
1648 | } | |
1649 | ||
416331ca | 1650 | impl Compiler { |
3b2f2976 XL |
1651 | pub fn with_stage(mut self, stage: u32) -> Compiler { |
1652 | self.stage = stage; | |
1653 | self | |
5bcae85e SL |
1654 | } |
1655 | ||
9fa01778 | 1656 | /// Returns `true` if this is a snapshot compiler for `build`'s configuration |
3b2f2976 | 1657 | pub fn is_snapshot(&self, build: &Build) -> bool { |
041b39d2 | 1658 | self.stage == 0 && self.host == build.build |
5bcae85e | 1659 | } |
32a655c1 SL |
1660 | |
1661 | /// Returns if this compiler should be treated as a final stage one in the | |
1662 | /// current build session. | |
1663 | /// This takes into account whether we're performing a full bootstrap or | |
1664 | /// not; don't directly compare the stage with `2`! | |
3b2f2976 | 1665 | pub fn is_final_stage(&self, build: &Build) -> bool { |
32a655c1 SL |
1666 | let final_stage = if build.config.full_bootstrap { 2 } else { 1 }; |
1667 | self.stage >= final_stage | |
1668 | } | |
223e47cc | 1669 | } |
e1599b0c XL |
1670 | |
1671 | fn envify(s: &str) -> String { | |
1672 | s.chars() | |
1673 | .map(|c| match c { | |
1674 | '-' => '_', | |
1675 | c => c, | |
1676 | }) | |
1677 | .flat_map(|c| c.to_uppercase()) | |
1678 | .collect() | |
1679 | } |