]>
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 | |
3b2f2976 | 34 | //! stage0 rustc and cargo according to `src/stage0.txt`, 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; |
dfeec247 XL |
109 | use std::fs::{self, File, OpenOptions}; |
110 | use std::io::{Read, Seek, SeekFrom, Write}; | |
111 | use std::path::{Path, PathBuf}; | |
ea8adc8c | 112 | use std::process::{self, Command}; |
3b2f2976 | 113 | use std::slice; |
83c7162d | 114 | use std::str; |
5bcae85e | 115 | |
b7449926 XL |
116 | #[cfg(unix)] |
117 | use std::os::unix::fs::symlink as symlink_file; | |
118 | #[cfg(windows)] | |
119 | use std::os::windows::fs::symlink_file; | |
120 | ||
dfeec247 | 121 | use build_helper::{mtime, output, run, run_suppressed, t, try_run, try_run_suppressed}; |
83c7162d | 122 | use filetime::FileTime; |
5bcae85e | 123 | |
29967ef6 | 124 | use crate::config::{LlvmLibunwind, TargetSelection}; |
416331ca | 125 | use crate::util::{exe, libdir, CiEnv}; |
5bcae85e | 126 | |
dfeec247 XL |
127 | mod builder; |
128 | mod cache; | |
ea8adc8c | 129 | mod cc_detect; |
5bcae85e SL |
130 | mod channel; |
131 | mod check; | |
132 | mod clean; | |
133 | mod compile; | |
134 | mod config; | |
135 | mod dist; | |
136 | mod doc; | |
137 | mod flags; | |
dfeec247 | 138 | mod format; |
c30ab7b3 | 139 | mod install; |
dfeec247 | 140 | mod metadata; |
5bcae85e | 141 | mod native; |
ba9703b0 | 142 | mod run; |
5bcae85e | 143 | mod sanity; |
1b1a35ee | 144 | mod setup; |
dfeec247 | 145 | mod test; |
3b2f2976 | 146 | mod tool; |
ea8adc8c | 147 | mod toolstate; |
dfeec247 | 148 | pub mod util; |
5bcae85e SL |
149 | |
150 | #[cfg(windows)] | |
151 | mod job; | |
152 | ||
8faf50e0 | 153 | #[cfg(all(unix, not(target_os = "haiku")))] |
7cac9316 | 154 | mod job { |
0731742a | 155 | pub unsafe fn setup(build: &mut crate::Build) { |
7cac9316 XL |
156 | if build.config.low_priority { |
157 | libc::setpriority(libc::PRIO_PGRP as _, 0, 10); | |
158 | } | |
159 | } | |
160 | } | |
161 | ||
e74abb32 | 162 | #[cfg(any(target_os = "haiku", target_os = "hermit", not(any(unix, windows))))] |
5bcae85e | 163 | mod job { |
dfeec247 | 164 | pub unsafe fn setup(_build: &mut crate::Build) {} |
5bcae85e SL |
165 | } |
166 | ||
dfeec247 | 167 | use crate::cache::{Interned, INTERNER}; |
0731742a | 168 | pub use crate::config::Config; |
1b1a35ee | 169 | pub use crate::flags::Subcommand; |
5bcae85e | 170 | |
8faf50e0 | 171 | const LLVM_TOOLS: &[&str] = &[ |
29967ef6 XL |
172 | "llvm-cov", // used to generate coverage report |
173 | "llvm-nm", // used to inspect binaries; it shows symbol names, their sizes and visibility | |
174 | "llvm-objcopy", // used to transform ELFs into binary format which flashing tools consume | |
175 | "llvm-objdump", // used to disassemble programs | |
8faf50e0 | 176 | "llvm-profdata", // used to inspect and merge files generated by profiles |
29967ef6 XL |
177 | "llvm-readobj", // used to get information from ELFs/objects that the other tools don't provide |
178 | "llvm-size", // used to prints the size of the linker sections of a program | |
179 | "llvm-strip", // used to discard symbols from binary files to reduce their size | |
180 | "llvm-ar", // used for creating and modifying archive files | |
181 | "llvm-dis", // used to disassemble LLVM bitcode | |
182 | "llc", // used to compile LLVM bytecode | |
183 | "opt", // used to optimize LLVM bytecode | |
8faf50e0 XL |
184 | ]; |
185 | ||
29967ef6 XL |
186 | pub const VERSION: usize = 2; |
187 | ||
5bcae85e SL |
188 | /// A structure representing a Rust compiler. |
189 | /// | |
190 | /// Each compiler has a `stage` that it is associated with and a `host` that | |
191 | /// corresponds to the platform the compiler runs on. This structure is used as | |
192 | /// a parameter to many methods below. | |
83c7162d | 193 | #[derive(Eq, PartialOrd, Ord, PartialEq, Clone, Copy, Hash, Debug)] |
3b2f2976 | 194 | pub struct Compiler { |
5bcae85e | 195 | stage: u32, |
3dfed10e | 196 | host: TargetSelection, |
5bcae85e SL |
197 | } |
198 | ||
83c7162d XL |
199 | #[derive(PartialEq, Eq, Copy, Clone, Debug)] |
200 | pub enum DocTests { | |
dc9dc135 | 201 | /// Run normal tests and doc tests (default). |
83c7162d | 202 | Yes, |
dc9dc135 | 203 | /// Do not run any doc tests. |
83c7162d | 204 | No, |
dc9dc135 | 205 | /// Only run doc tests. |
83c7162d XL |
206 | Only, |
207 | } | |
208 | ||
b7449926 XL |
209 | pub enum GitRepo { |
210 | Rustc, | |
211 | Llvm, | |
212 | } | |
213 | ||
5bcae85e SL |
214 | /// Global configuration for the build system. |
215 | /// | |
216 | /// This structure transitively contains all configuration for the build system. | |
217 | /// All filesystem-encoded configuration is in `config`, all flags are in | |
218 | /// `flags`, and then parsed or probed information is listed in the keys below. | |
219 | /// | |
220 | /// This structure is a parameter of almost all methods in the build system, | |
221 | /// although most functions are implemented as free functions rather than | |
222 | /// methods specifically on this structure itself (to make it easier to | |
223 | /// organize). | |
224 | pub struct Build { | |
dc9dc135 | 225 | /// User-specified configuration from `config.toml`. |
5bcae85e SL |
226 | config: Config, |
227 | ||
1b1a35ee XL |
228 | // Version information |
229 | version: String, | |
230 | ||
dc9dc135 | 231 | // Properties derived from the above configuration |
5bcae85e SL |
232 | src: PathBuf, |
233 | out: PathBuf, | |
8bb4bdeb XL |
234 | rust_info: channel::GitInfo, |
235 | cargo_info: channel::GitInfo, | |
cc61c64b | 236 | rls_info: channel::GitInfo, |
f035d41b | 237 | rust_analyzer_info: channel::GitInfo, |
8faf50e0 | 238 | clippy_info: channel::GitInfo, |
0731742a | 239 | miri_info: channel::GitInfo, |
abe05a73 | 240 | rustfmt_info: channel::GitInfo, |
9fa01778 | 241 | in_tree_llvm_info: channel::GitInfo, |
5bcae85e | 242 | local_rebuild: bool, |
041b39d2 | 243 | fail_fast: bool, |
83c7162d | 244 | doc_tests: DocTests, |
041b39d2 XL |
245 | verbosity: usize, |
246 | ||
dc9dc135 | 247 | // Targets for which to build |
3dfed10e XL |
248 | build: TargetSelection, |
249 | hosts: Vec<TargetSelection>, | |
250 | targets: Vec<TargetSelection>, | |
041b39d2 | 251 | |
74b04a01 | 252 | // Stage 0 (downloaded) compiler, lld and cargo or their local rust equivalents |
041b39d2 XL |
253 | initial_rustc: PathBuf, |
254 | initial_cargo: PathBuf, | |
74b04a01 | 255 | initial_lld: PathBuf, |
f9f354fc | 256 | initial_libdir: PathBuf, |
5bcae85e | 257 | |
5bcae85e | 258 | // Runtime state filled in later on |
abe05a73 | 259 | // C/C++ compilers and archiver for all targets |
3dfed10e XL |
260 | cc: HashMap<TargetSelection, cc::Tool>, |
261 | cxx: HashMap<TargetSelection, cc::Tool>, | |
262 | ar: HashMap<TargetSelection, PathBuf>, | |
263 | ranlib: HashMap<TargetSelection, PathBuf>, | |
dc9dc135 | 264 | // Miscellaneous |
3b2f2976 | 265 | crates: HashMap<Interned<String>, Crate>, |
476ff2be | 266 | is_sudo: bool, |
7cac9316 | 267 | ci_env: CiEnv, |
ea8adc8c | 268 | delayed_failures: RefCell<Vec<String>>, |
ff7c6d11 | 269 | prerelease_version: Cell<Option<u32>>, |
dfeec247 | 270 | tool_artifacts: |
3dfed10e | 271 | RefCell<HashMap<TargetSelection, HashMap<String, (&'static str, PathBuf, Vec<String>)>>>, |
c30ab7b3 SL |
272 | } |
273 | ||
274 | #[derive(Debug)] | |
275 | struct Crate { | |
3b2f2976 | 276 | name: Interned<String>, |
94b46f34 XL |
277 | deps: HashSet<Interned<String>>, |
278 | id: String, | |
c30ab7b3 | 279 | path: PathBuf, |
5bcae85e SL |
280 | } |
281 | ||
2c00a5a8 | 282 | impl Crate { |
2c00a5a8 | 283 | fn local_path(&self, build: &Build) -> PathBuf { |
2c00a5a8 XL |
284 | self.path.strip_prefix(&build.config.src).unwrap().into() |
285 | } | |
286 | } | |
287 | ||
f035d41b XL |
288 | /// When building Rust various objects are handled differently. |
289 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] | |
290 | pub enum DependencyType { | |
291 | /// Libraries originating from proc-macros. | |
292 | Host, | |
293 | /// Typical Rust libraries. | |
294 | Target, | |
295 | /// Non Rust libraries and objects shipped to ease usage of certain targets. | |
296 | TargetSelfContained, | |
297 | } | |
298 | ||
5bcae85e SL |
299 | /// The various "modes" of invoking Cargo. |
300 | /// | |
301 | /// These entries currently correspond to the various output directories of the | |
302 | /// build system, with each mod generating output in a different directory. | |
83c7162d | 303 | #[derive(Debug, Hash, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] |
5bcae85e | 304 | pub enum Mode { |
041b39d2 | 305 | /// Build the standard library, placing output in the "stageN-std" directory. |
94b46f34 | 306 | Std, |
5bcae85e | 307 | |
94b46f34 XL |
308 | /// Build librustc, and compiler libraries, placing output in the "stageN-rustc" directory. |
309 | Rustc, | |
5bcae85e | 310 | |
29967ef6 XL |
311 | /// Build a codegen backend for rustc, placing the output in the "stageN-codegen" directory. |
312 | Codegen, | |
313 | ||
f035d41b XL |
314 | /// Build a tool, placing output in the "stage0-bootstrap-tools" |
315 | /// directory. This is for miscellaneous sets of tools that are built | |
316 | /// using the bootstrap stage0 compiler in its entirety (target libraries | |
317 | /// and all). Typically these tools compile with stable Rust. | |
8faf50e0 XL |
318 | ToolBootstrap, |
319 | ||
f035d41b XL |
320 | /// Build a tool which uses the locally built std, placing output in the |
321 | /// "stageN-tools" directory. Its usage is quite rare, mainly used by | |
322 | /// compiletest which needs libtest. | |
94b46f34 | 323 | ToolStd, |
f035d41b XL |
324 | |
325 | /// Build a tool which uses the locally built rustc and the target std, | |
326 | /// placing the output in the "stageN-tools" directory. This is used for | |
327 | /// anything that needs a fully functional rustc, such as rustdoc, clippy, | |
328 | /// cargo, rls, rustfmt, miri, etc. | |
94b46f34 XL |
329 | ToolRustc, |
330 | } | |
331 | ||
332 | impl Mode { | |
333 | pub fn is_tool(&self) -> bool { | |
3dfed10e | 334 | matches!(self, Mode::ToolBootstrap | Mode::ToolRustc | Mode::ToolStd) |
94b46f34 | 335 | } |
29967ef6 XL |
336 | |
337 | pub fn must_support_dlopen(&self) -> bool { | |
338 | matches!(self, Mode::Std | Mode::Codegen) | |
339 | } | |
5bcae85e SL |
340 | } |
341 | ||
342 | impl Build { | |
343 | /// Creates a new set of build configuration from the `flags` on the command | |
344 | /// line and the filesystem `config`. | |
345 | /// | |
346 | /// By default all build output will be placed in the current directory. | |
3b2f2976 | 347 | pub fn new(config: Config) -> Build { |
3b2f2976 | 348 | let src = config.src.clone(); |
83c7162d | 349 | let out = config.out.clone(); |
5bcae85e | 350 | |
476ff2be | 351 | let is_sudo = match env::var_os("SUDO_USER") { |
dfeec247 XL |
352 | Some(sudo_user) => match env::var_os("USER") { |
353 | Some(user) => user != sudo_user, | |
354 | None => false, | |
355 | }, | |
476ff2be SL |
356 | None => false, |
357 | }; | |
532ac7d7 XL |
358 | |
359 | let ignore_git = config.ignore_git; | |
360 | let rust_info = channel::GitInfo::new(ignore_git, &src); | |
361 | let cargo_info = channel::GitInfo::new(ignore_git, &src.join("src/tools/cargo")); | |
362 | let rls_info = channel::GitInfo::new(ignore_git, &src.join("src/tools/rls")); | |
f035d41b XL |
363 | let rust_analyzer_info = |
364 | channel::GitInfo::new(ignore_git, &src.join("src/tools/rust-analyzer")); | |
532ac7d7 XL |
365 | let clippy_info = channel::GitInfo::new(ignore_git, &src.join("src/tools/clippy")); |
366 | let miri_info = channel::GitInfo::new(ignore_git, &src.join("src/tools/miri")); | |
367 | let rustfmt_info = channel::GitInfo::new(ignore_git, &src.join("src/tools/rustfmt")); | |
368 | ||
369 | // we always try to use git for LLVM builds | |
370 | let in_tree_llvm_info = channel::GitInfo::new(false, &src.join("src/llvm-project")); | |
476ff2be | 371 | |
f9f354fc XL |
372 | let initial_target_libdir_str = if config.dry_run { |
373 | "/dummy/lib/path/to/lib/".to_string() | |
374 | } else { | |
375 | output( | |
376 | Command::new(&config.initial_rustc) | |
377 | .arg("--target") | |
3dfed10e | 378 | .arg(config.build.rustc_target_arg()) |
f9f354fc XL |
379 | .arg("--print") |
380 | .arg("target-libdir"), | |
381 | ) | |
382 | }; | |
383 | let initial_target_dir = Path::new(&initial_target_libdir_str).parent().unwrap(); | |
384 | let initial_lld = initial_target_dir.join("bin").join("rust-lld"); | |
385 | ||
386 | let initial_sysroot = if config.dry_run { | |
387 | "/dummy".to_string() | |
388 | } else { | |
389 | output(Command::new(&config.initial_rustc).arg("--print").arg("sysroot")) | |
390 | }; | |
391 | let initial_libdir = initial_target_dir | |
392 | .parent() | |
393 | .unwrap() | |
394 | .parent() | |
395 | .unwrap() | |
396 | .strip_prefix(initial_sysroot.trim()) | |
397 | .unwrap() | |
398 | .to_path_buf(); | |
74b04a01 | 399 | |
1b1a35ee XL |
400 | let version = std::fs::read_to_string(src.join("src").join("version")) |
401 | .expect("failed to read src/version"); | |
402 | let version = version.trim(); | |
403 | ||
83c7162d | 404 | let mut build = Build { |
041b39d2 XL |
405 | initial_rustc: config.initial_rustc.clone(), |
406 | initial_cargo: config.initial_cargo.clone(), | |
74b04a01 | 407 | initial_lld, |
f9f354fc | 408 | initial_libdir, |
041b39d2 | 409 | local_rebuild: config.local_rebuild, |
3b2f2976 | 410 | fail_fast: config.cmd.fail_fast(), |
0531ce1d | 411 | doc_tests: config.cmd.doc_tests(), |
3b2f2976 | 412 | verbosity: config.verbose, |
041b39d2 | 413 | |
3b2f2976 XL |
414 | build: config.build, |
415 | hosts: config.hosts.clone(), | |
416 | targets: config.targets.clone(), | |
041b39d2 | 417 | |
3b2f2976 | 418 | config, |
1b1a35ee | 419 | version: version.to_string(), |
3b2f2976 XL |
420 | src, |
421 | out, | |
5bcae85e | 422 | |
3b2f2976 XL |
423 | rust_info, |
424 | cargo_info, | |
425 | rls_info, | |
f035d41b | 426 | rust_analyzer_info, |
8faf50e0 | 427 | clippy_info, |
0731742a | 428 | miri_info, |
abe05a73 | 429 | rustfmt_info, |
9fa01778 | 430 | in_tree_llvm_info, |
5bcae85e SL |
431 | cc: HashMap::new(), |
432 | cxx: HashMap::new(), | |
abe05a73 | 433 | ar: HashMap::new(), |
b7449926 | 434 | ranlib: HashMap::new(), |
c30ab7b3 | 435 | crates: HashMap::new(), |
3b2f2976 | 436 | is_sudo, |
7cac9316 | 437 | ci_env: CiEnv::current(), |
ea8adc8c | 438 | delayed_failures: RefCell::new(Vec::new()), |
ff7c6d11 | 439 | prerelease_version: Cell::new(None), |
0531ce1d | 440 | tool_artifacts: Default::default(), |
83c7162d XL |
441 | }; |
442 | ||
443 | build.verbose("finding compilers"); | |
444 | cc_detect::find(&mut build); | |
445 | build.verbose("running sanity check"); | |
446 | sanity::check(&mut build); | |
447 | ||
448 | // If local-rust is the same major.minor as the current version, then force a | |
449 | // local-rebuild | |
dfeec247 XL |
450 | let local_version_verbose = |
451 | output(Command::new(&build.initial_rustc).arg("--version").arg("--verbose")); | |
83c7162d | 452 | let local_release = local_version_verbose |
dfeec247 | 453 | .lines() |
f035d41b | 454 | .filter_map(|x| x.strip_prefix("release:")) |
dfeec247 XL |
455 | .next() |
456 | .unwrap() | |
dfeec247 | 457 | .trim(); |
1b1a35ee | 458 | if local_release.split('.').take(2).eq(version.split('.').take(2)) { |
83c7162d XL |
459 | build.verbose(&format!("auto-detected local-rebuild {}", local_release)); |
460 | build.local_rebuild = true; | |
5bcae85e | 461 | } |
83c7162d XL |
462 | |
463 | build.verbose("learning about cargo"); | |
464 | metadata::build(&mut build); | |
465 | ||
466 | build | |
5bcae85e SL |
467 | } |
468 | ||
3b2f2976 | 469 | pub fn build_triple(&self) -> &[Interned<String>] { |
3dfed10e | 470 | slice::from_ref(&self.build.triple) |
041b39d2 XL |
471 | } |
472 | ||
5bcae85e SL |
473 | /// Executes the entire build, as configured by the flags and configuration. |
474 | pub fn build(&mut self) { | |
5bcae85e | 475 | unsafe { |
7cac9316 | 476 | job::setup(self); |
5bcae85e SL |
477 | } |
478 | ||
dfeec247 XL |
479 | if let Subcommand::Format { check } = self.config.cmd { |
480 | return format::format(self, check); | |
481 | } | |
482 | ||
ea8adc8c XL |
483 | if let Subcommand::Clean { all } = self.config.cmd { |
484 | return clean::clean(self, all); | |
5bcae85e SL |
485 | } |
486 | ||
29967ef6 XL |
487 | if let Subcommand::Setup { profile } = &self.config.cmd { |
488 | return setup::setup(&self.config.src, *profile); | |
1b1a35ee XL |
489 | } |
490 | ||
83c7162d XL |
491 | { |
492 | let builder = builder::Builder::new(&self); | |
493 | if let Some(path) = builder.paths.get(0) { | |
494 | if path == Path::new("nonexistent/path/to/trigger/cargo/metadata") { | |
495 | return; | |
496 | } | |
497 | } | |
5bcae85e | 498 | } |
5bcae85e | 499 | |
83c7162d XL |
500 | if !self.config.dry_run { |
501 | { | |
502 | self.config.dry_run = true; | |
503 | let builder = builder::Builder::new(&self); | |
504 | builder.execute_cli(); | |
505 | } | |
506 | self.config.dry_run = false; | |
507 | let builder = builder::Builder::new(&self); | |
508 | builder.execute_cli(); | |
509 | } else { | |
510 | let builder = builder::Builder::new(&self); | |
74b04a01 | 511 | builder.execute_cli(); |
83c7162d | 512 | } |
ea8adc8c XL |
513 | |
514 | // Check for postponed failures from `test --no-fail-fast`. | |
515 | let failures = self.delayed_failures.borrow(); | |
516 | if failures.len() > 0 { | |
517 | println!("\n{} command(s) did not execute successfully:\n", failures.len()); | |
518 | for failure in failures.iter() { | |
519 | println!(" - {}\n", failure); | |
520 | } | |
521 | process::exit(1); | |
522 | } | |
5bcae85e SL |
523 | } |
524 | ||
5bcae85e SL |
525 | /// Clear out `dir` if `input` is newer. |
526 | /// | |
527 | /// After this executes, it will also ensure that `dir` exists. | |
abe05a73 | 528 | fn clear_if_dirty(&self, dir: &Path, input: &Path) -> bool { |
5bcae85e | 529 | let stamp = dir.join(".stamp"); |
abe05a73 | 530 | let mut cleared = false; |
5bcae85e SL |
531 | if mtime(&stamp) < mtime(input) { |
532 | self.verbose(&format!("Dirty - {}", dir.display())); | |
533 | let _ = fs::remove_dir_all(dir); | |
abe05a73 | 534 | cleared = true; |
9e0c209e | 535 | } else if stamp.exists() { |
abe05a73 | 536 | return cleared; |
5bcae85e SL |
537 | } |
538 | t!(fs::create_dir_all(dir)); | |
539 | t!(File::create(stamp)); | |
abe05a73 | 540 | cleared |
5bcae85e SL |
541 | } |
542 | ||
9fa01778 | 543 | /// Gets the space-separated set of activated features for the standard |
5bcae85e | 544 | /// library. |
29967ef6 | 545 | fn std_features(&self, target: TargetSelection) -> String { |
476ff2be | 546 | let mut features = "panic-unwind".to_string(); |
8bb4bdeb | 547 | |
29967ef6 XL |
548 | match self.config.llvm_libunwind.unwrap_or_default() { |
549 | LlvmLibunwind::InTree => features.push_str(" llvm-libunwind"), | |
550 | LlvmLibunwind::System => features.push_str(" system-llvm-libunwind"), | |
551 | LlvmLibunwind::No => {} | |
532ac7d7 | 552 | } |
5bcae85e SL |
553 | if self.config.backtrace { |
554 | features.push_str(" backtrace"); | |
555 | } | |
29967ef6 | 556 | if self.config.profiler_enabled(target) { |
041b39d2 XL |
557 | features.push_str(" profiler"); |
558 | } | |
559 | features | |
5bcae85e SL |
560 | } |
561 | ||
9fa01778 | 562 | /// Gets the space-separated set of activated features for the compiler. |
5bcae85e SL |
563 | fn rustc_features(&self) -> String { |
564 | let mut features = String::new(); | |
a1dfa0c6 XL |
565 | if self.config.jemalloc { |
566 | features.push_str("jemalloc"); | |
5bcae85e | 567 | } |
60c5eb7d XL |
568 | if self.config.llvm_enabled() { |
569 | features.push_str(" llvm"); | |
570 | } | |
1b1a35ee XL |
571 | |
572 | // If debug logging is on, then we want the default for tracing: | |
573 | // https://github.com/tokio-rs/tracing/blob/3dd5c03d907afdf2c39444a29931833335171554/tracing/src/level_filters.rs#L26 | |
574 | // which is everything (including debug/trace/etc.) | |
575 | // if its unset, if debug_assertions is on, then debug_logging will also be on | |
576 | // as well as tracing *ignoring* this feature when debug_assertions is on | |
577 | if !self.config.rust_debug_logging { | |
578 | features.push_str(" max_level_info"); | |
579 | } | |
580 | ||
041b39d2 | 581 | features |
5bcae85e SL |
582 | } |
583 | ||
584 | /// Component directory that Cargo will produce output into (e.g. | |
585 | /// release/debug) | |
586 | fn cargo_dir(&self) -> &'static str { | |
dfeec247 | 587 | if self.config.rust_optimize { "release" } else { "debug" } |
5bcae85e SL |
588 | } |
589 | ||
abe05a73 | 590 | fn tools_dir(&self, compiler: Compiler) -> PathBuf { |
3dfed10e XL |
591 | let out = self |
592 | .out | |
593 | .join(&*compiler.host.triple) | |
594 | .join(format!("stage{}-tools-bin", compiler.stage)); | |
abe05a73 XL |
595 | t!(fs::create_dir_all(&out)); |
596 | out | |
597 | } | |
598 | ||
5bcae85e SL |
599 | /// Returns the root directory for all output generated in a particular |
600 | /// stage when running with a particular host compiler. | |
601 | /// | |
602 | /// The mode indicates what the root directory is for. | |
3b2f2976 | 603 | fn stage_out(&self, compiler: Compiler, mode: Mode) -> PathBuf { |
5bcae85e | 604 | let suffix = match mode { |
94b46f34 | 605 | Mode::Std => "-std", |
94b46f34 | 606 | Mode::Rustc => "-rustc", |
29967ef6 | 607 | Mode::Codegen => "-codegen", |
8faf50e0 | 608 | Mode::ToolBootstrap => "-bootstrap-tools", |
e1599b0c | 609 | Mode::ToolStd | Mode::ToolRustc => "-tools", |
5bcae85e | 610 | }; |
3dfed10e | 611 | self.out.join(&*compiler.host.triple).join(format!("stage{}{}", compiler.stage, suffix)) |
5bcae85e SL |
612 | } |
613 | ||
614 | /// Returns the root output directory for all Cargo output in a given stage, | |
3b2f2976 | 615 | /// running a particular compiler, whether or not we're building the |
5bcae85e | 616 | /// standard library, and targeting the specified architecture. |
3dfed10e XL |
617 | fn cargo_out(&self, compiler: Compiler, mode: Mode, target: TargetSelection) -> PathBuf { |
618 | self.stage_out(compiler, mode).join(&*target.triple).join(self.cargo_dir()) | |
5bcae85e SL |
619 | } |
620 | ||
621 | /// Root output directory for LLVM compiled for `target` | |
622 | /// | |
623 | /// Note that if LLVM is configured externally then the directory returned | |
624 | /// will likely be empty. | |
3dfed10e XL |
625 | fn llvm_out(&self, target: TargetSelection) -> PathBuf { |
626 | self.out.join(&*target.triple).join("llvm") | |
5bcae85e SL |
627 | } |
628 | ||
3dfed10e XL |
629 | fn lld_out(&self, target: TargetSelection) -> PathBuf { |
630 | self.out.join(&*target.triple).join("lld") | |
0531ce1d XL |
631 | } |
632 | ||
c30ab7b3 | 633 | /// Output directory for all documentation for a target |
3dfed10e XL |
634 | fn doc_out(&self, target: TargetSelection) -> PathBuf { |
635 | self.out.join(&*target.triple).join("doc") | |
c30ab7b3 SL |
636 | } |
637 | ||
0531ce1d | 638 | /// Output directory for all documentation for a target |
3dfed10e XL |
639 | fn compiler_doc_out(&self, target: TargetSelection) -> PathBuf { |
640 | self.out.join(&*target.triple).join("compiler-doc") | |
0531ce1d XL |
641 | } |
642 | ||
041b39d2 | 643 | /// Output directory for some generated md crate documentation for a target (temporary) |
3dfed10e XL |
644 | fn md_doc_out(&self, target: TargetSelection) -> Interned<PathBuf> { |
645 | INTERNER.intern_path(self.out.join(&*target.triple).join("md-doc")) | |
8bb4bdeb XL |
646 | } |
647 | ||
9fa01778 | 648 | /// Returns `true` if no custom `llvm-config` is set for the specified target. |
5bcae85e SL |
649 | /// |
650 | /// If no custom `llvm-config` was specified then Rust's llvm will be used. | |
3dfed10e | 651 | fn is_rust_llvm(&self, target: TargetSelection) -> bool { |
1b1a35ee XL |
652 | if self.config.llvm_from_ci && target == self.config.build { |
653 | return true; | |
654 | } | |
655 | ||
3b2f2976 | 656 | match self.config.target_config.get(&target) { |
5bcae85e | 657 | Some(ref c) => c.llvm_config.is_none(), |
dfeec247 | 658 | None => true, |
5bcae85e SL |
659 | } |
660 | } | |
661 | ||
5bcae85e | 662 | /// Returns the path to `FileCheck` binary for the specified target |
3dfed10e | 663 | fn llvm_filecheck(&self, target: TargetSelection) -> PathBuf { |
3b2f2976 | 664 | let target_config = self.config.target_config.get(&target); |
0bf4aa26 XL |
665 | if let Some(s) = target_config.and_then(|c| c.llvm_filecheck.as_ref()) { |
666 | s.to_path_buf() | |
667 | } else if let Some(s) = target_config.and_then(|c| c.llvm_config.as_ref()) { | |
32a655c1 | 668 | let llvm_bindir = output(Command::new(s).arg("--bindir")); |
3dfed10e | 669 | let filecheck = Path::new(llvm_bindir.trim()).join(exe("FileCheck", target)); |
0bf4aa26 XL |
670 | if filecheck.exists() { |
671 | filecheck | |
672 | } else { | |
673 | // On Fedora the system LLVM installs FileCheck in the | |
674 | // llvm subdirectory of the libdir. | |
675 | let llvm_libdir = output(Command::new(s).arg("--libdir")); | |
dfeec247 | 676 | let lib_filecheck = |
3dfed10e | 677 | Path::new(llvm_libdir.trim()).join("llvm").join(exe("FileCheck", target)); |
0bf4aa26 XL |
678 | if lib_filecheck.exists() { |
679 | lib_filecheck | |
680 | } else { | |
681 | // Return the most normal file name, even though | |
682 | // it doesn't exist, so that any error message | |
683 | // refers to that. | |
684 | filecheck | |
685 | } | |
686 | } | |
5bcae85e | 687 | } else { |
3b2f2976 | 688 | let base = self.llvm_out(self.config.build).join("build"); |
1b1a35ee | 689 | let base = if !self.ninja() && self.config.build.contains("msvc") { |
94b46f34 XL |
690 | if self.config.llvm_optimize { |
691 | if self.config.llvm_release_debuginfo { | |
692 | base.join("RelWithDebInfo") | |
693 | } else { | |
694 | base.join("Release") | |
695 | } | |
696 | } else { | |
697 | base.join("Debug") | |
698 | } | |
5bcae85e | 699 | } else { |
94b46f34 XL |
700 | base |
701 | }; | |
3dfed10e | 702 | base.join("bin").join(exe("FileCheck", target)) |
5bcae85e SL |
703 | } |
704 | } | |
705 | ||
8bb4bdeb | 706 | /// Directory for libraries built from C/C++ code and shared between stages. |
3dfed10e XL |
707 | fn native_dir(&self, target: TargetSelection) -> PathBuf { |
708 | self.out.join(&*target.triple).join("native") | |
8bb4bdeb XL |
709 | } |
710 | ||
5bcae85e SL |
711 | /// Root output directory for rust_test_helpers library compiled for |
712 | /// `target` | |
3dfed10e | 713 | fn test_helpers_out(&self, target: TargetSelection) -> PathBuf { |
8bb4bdeb | 714 | self.native_dir(target).join("rust-test-helpers") |
5bcae85e SL |
715 | } |
716 | ||
476ff2be SL |
717 | /// Adds the `RUST_TEST_THREADS` env var if necessary |
718 | fn add_rust_test_threads(&self, cmd: &mut Command) { | |
719 | if env::var_os("RUST_TEST_THREADS").is_none() { | |
720 | cmd.env("RUST_TEST_THREADS", self.jobs().to_string()); | |
721 | } | |
5bcae85e SL |
722 | } |
723 | ||
5bcae85e SL |
724 | /// Returns the libdir of the snapshot compiler. |
725 | fn rustc_snapshot_libdir(&self) -> PathBuf { | |
3dfed10e | 726 | self.rustc_snapshot_sysroot().join(libdir(self.config.build)) |
8faf50e0 XL |
727 | } |
728 | ||
729 | /// Returns the sysroot of the snapshot compiler. | |
730 | fn rustc_snapshot_sysroot(&self) -> &Path { | |
041b39d2 | 731 | self.initial_rustc.parent().unwrap().parent().unwrap() |
5bcae85e SL |
732 | } |
733 | ||
734 | /// Runs a command, printing out nice contextual information if it fails. | |
735 | fn run(&self, cmd: &mut Command) { | |
dfeec247 XL |
736 | if self.config.dry_run { |
737 | return; | |
738 | } | |
ff7c6d11 | 739 | self.verbose(&format!("running: {:?}", cmd)); |
416331ca | 740 | run(cmd) |
5bcae85e SL |
741 | } |
742 | ||
8bb4bdeb XL |
743 | /// Runs a command, printing out nice contextual information if it fails. |
744 | fn run_quiet(&self, cmd: &mut Command) { | |
dfeec247 XL |
745 | if self.config.dry_run { |
746 | return; | |
747 | } | |
8bb4bdeb | 748 | self.verbose(&format!("running: {:?}", cmd)); |
ff7c6d11 | 749 | run_suppressed(cmd) |
8bb4bdeb XL |
750 | } |
751 | ||
ff7c6d11 XL |
752 | /// Runs a command, printing out nice contextual information if it fails. |
753 | /// Exits if the command failed to execute at all, otherwise returns its | |
754 | /// `status.success()`. | |
755 | fn try_run(&self, cmd: &mut Command) -> bool { | |
dfeec247 XL |
756 | if self.config.dry_run { |
757 | return true; | |
758 | } | |
7cac9316 | 759 | self.verbose(&format!("running: {:?}", cmd)); |
416331ca | 760 | try_run(cmd) |
7cac9316 XL |
761 | } |
762 | ||
763 | /// Runs a command, printing out nice contextual information if it fails. | |
764 | /// Exits if the command failed to execute at all, otherwise returns its | |
765 | /// `status.success()`. | |
766 | fn try_run_quiet(&self, cmd: &mut Command) -> bool { | |
dfeec247 XL |
767 | if self.config.dry_run { |
768 | return true; | |
769 | } | |
7cac9316 | 770 | self.verbose(&format!("running: {:?}", cmd)); |
ff7c6d11 | 771 | try_run_suppressed(cmd) |
7cac9316 XL |
772 | } |
773 | ||
041b39d2 XL |
774 | pub fn is_verbose(&self) -> bool { |
775 | self.verbosity > 0 | |
776 | } | |
777 | ||
5bcae85e SL |
778 | /// Prints a message if this build is configured in verbose mode. |
779 | fn verbose(&self, msg: &str) { | |
041b39d2 | 780 | if self.is_verbose() { |
5bcae85e SL |
781 | println!("{}", msg); |
782 | } | |
783 | } | |
784 | ||
532ac7d7 XL |
785 | pub fn is_verbose_than(&self, level: usize) -> bool { |
786 | self.verbosity > level | |
787 | } | |
788 | ||
789 | /// Prints a message if this build is configured in more verbose mode than `level`. | |
790 | fn verbose_than(&self, level: usize, msg: &str) { | |
791 | if self.is_verbose_than(level) { | |
792 | println!("{}", msg); | |
793 | } | |
794 | } | |
795 | ||
83c7162d | 796 | fn info(&self, msg: &str) { |
dfeec247 XL |
797 | if self.config.dry_run { |
798 | return; | |
799 | } | |
83c7162d XL |
800 | println!("{}", msg); |
801 | } | |
802 | ||
5bcae85e SL |
803 | /// Returns the number of parallel jobs that have been configured for this |
804 | /// build. | |
805 | fn jobs(&self) -> u32 { | |
3b2f2976 | 806 | self.config.jobs.unwrap_or_else(|| num_cpus::get() as u32) |
5bcae85e SL |
807 | } |
808 | ||
ba9703b0 | 809 | fn debuginfo_map_to(&self, which: GitRepo) -> Option<String> { |
b7449926 | 810 | if !self.config.rust_remap_debuginfo { |
dfeec247 | 811 | return None; |
b7449926 XL |
812 | } |
813 | ||
ba9703b0 | 814 | match which { |
b7449926 | 815 | GitRepo::Rustc => { |
1b1a35ee | 816 | let sha = self.rust_sha().unwrap_or(&self.version); |
ba9703b0 | 817 | Some(format!("/rustc/{}", sha)) |
b7449926 | 818 | } |
ba9703b0 XL |
819 | GitRepo::Llvm => Some(String::from("/rustc/llvm")), |
820 | } | |
b7449926 XL |
821 | } |
822 | ||
5bcae85e | 823 | /// Returns the path to the C compiler for the target specified. |
3dfed10e | 824 | fn cc(&self, target: TargetSelection) -> &Path { |
abe05a73 | 825 | self.cc[&target].path() |
5bcae85e SL |
826 | } |
827 | ||
828 | /// Returns a list of flags to pass to the C compiler for the target | |
829 | /// specified. | |
3dfed10e | 830 | fn cflags(&self, target: TargetSelection, which: GitRepo) -> Vec<String> { |
5bcae85e | 831 | // Filter out -O and /O (the optimization flags) that we picked up from |
ea8adc8c | 832 | // cc-rs because the build scripts will determine that for themselves. |
dfeec247 XL |
833 | let mut base = self.cc[&target] |
834 | .args() | |
835 | .iter() | |
836 | .map(|s| s.to_string_lossy().into_owned()) | |
837 | .filter(|s| !s.starts_with("-O") && !s.starts_with("/O")) | |
838 | .collect::<Vec<String>>(); | |
5bcae85e | 839 | |
cc61c64b | 840 | // If we're compiling on macOS then we add a few unconditional flags |
5bcae85e SL |
841 | // indicating that we want libc++ (more filled out than libstdc++) and |
842 | // we want to compile for 10.7. This way we can ensure that | |
a1dfa0c6 | 843 | // LLVM/etc are all properly compiled. |
5bcae85e SL |
844 | if target.contains("apple-darwin") { |
845 | base.push("-stdlib=libc++".into()); | |
5bcae85e | 846 | } |
7cac9316 XL |
847 | |
848 | // Work around an apparently bad MinGW / GCC optimization, | |
849 | // See: http://lists.llvm.org/pipermail/cfe-dev/2016-December/051980.html | |
850 | // See: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=78936 | |
3dfed10e | 851 | if &*target.triple == "i686-pc-windows-gnu" { |
7cac9316 XL |
852 | base.push("-fno-omit-frame-pointer".into()); |
853 | } | |
b7449926 | 854 | |
ba9703b0 XL |
855 | if let Some(map_to) = self.debuginfo_map_to(which) { |
856 | let map = format!("{}={}", self.src.display(), map_to); | |
dfeec247 | 857 | let cc = self.cc(target); |
b7449926 | 858 | if cc.ends_with("clang") || cc.ends_with("gcc") { |
a1dfa0c6 | 859 | base.push(format!("-fdebug-prefix-map={}", map)); |
b7449926 XL |
860 | } else if cc.ends_with("clang-cl.exe") { |
861 | base.push("-Xclang".into()); | |
a1dfa0c6 | 862 | base.push(format!("-fdebug-prefix-map={}", map)); |
b7449926 XL |
863 | } |
864 | } | |
041b39d2 | 865 | base |
5bcae85e SL |
866 | } |
867 | ||
868 | /// Returns the path to the `ar` archive utility for the target specified. | |
3dfed10e | 869 | fn ar(&self, target: TargetSelection) -> Option<&Path> { |
abe05a73 | 870 | self.ar.get(&target).map(|p| &**p) |
5bcae85e SL |
871 | } |
872 | ||
b7449926 | 873 | /// Returns the path to the `ranlib` utility for the target specified. |
3dfed10e | 874 | fn ranlib(&self, target: TargetSelection) -> Option<&Path> { |
b7449926 XL |
875 | self.ranlib.get(&target).map(|p| &**p) |
876 | } | |
877 | ||
041b39d2 | 878 | /// Returns the path to the C++ compiler for the target specified. |
3dfed10e | 879 | fn cxx(&self, target: TargetSelection) -> Result<&Path, String> { |
3b2f2976 | 880 | match self.cxx.get(&target) { |
041b39d2 | 881 | Some(p) => Ok(p.path()), |
dfeec247 XL |
882 | None => { |
883 | Err(format!("target `{}` is not configured as a host, only as a target", target)) | |
884 | } | |
9e0c209e | 885 | } |
5bcae85e SL |
886 | } |
887 | ||
2c00a5a8 | 888 | /// Returns the path to the linker for the given target if it needs to be overridden. |
1b1a35ee | 889 | fn linker(&self, target: TargetSelection) -> Option<&Path> { |
dfeec247 XL |
890 | if let Some(linker) = self.config.target_config.get(&target).and_then(|c| c.linker.as_ref()) |
891 | { | |
abe05a73 | 892 | Some(linker) |
1b1a35ee XL |
893 | } else if target.contains("vxworks") { |
894 | // need to use CXX compiler as linker to resolve the exception functions | |
895 | // that are only existed in CXX libraries | |
896 | Some(self.cxx[&target].path()) | |
dfeec247 | 897 | } else if target != self.config.build |
3dfed10e | 898 | && util::use_host_linker(target) |
dfeec247 XL |
899 | && !target.contains("msvc") |
900 | { | |
abe05a73 | 901 | Some(self.cc(target)) |
1b1a35ee | 902 | } else if self.config.use_lld && !self.is_fuse_ld_lld(target) && self.build == target { |
74b04a01 | 903 | Some(&self.initial_lld) |
abe05a73 XL |
904 | } else { |
905 | None | |
5bcae85e | 906 | } |
85aaf69f | 907 | } |
9e0c209e | 908 | |
1b1a35ee XL |
909 | // LLD is used through `-fuse-ld=lld` rather than directly. |
910 | // Only MSVC targets use LLD directly at the moment. | |
911 | fn is_fuse_ld_lld(&self, target: TargetSelection) -> bool { | |
912 | self.config.use_lld && !target.contains("msvc") | |
913 | } | |
914 | ||
3b2f2976 | 915 | /// Returns if this target should statically link the C runtime, if specified |
3dfed10e | 916 | fn crt_static(&self, target: TargetSelection) -> Option<bool> { |
3b2f2976 XL |
917 | if target.contains("pc-windows-msvc") { |
918 | Some(true) | |
919 | } else { | |
dfeec247 | 920 | self.config.target_config.get(&target).and_then(|t| t.crt_static) |
3b2f2976 XL |
921 | } |
922 | } | |
923 | ||
9e0c209e | 924 | /// Returns the "musl root" for this `target`, if defined |
3dfed10e | 925 | fn musl_root(&self, target: TargetSelection) -> Option<&Path> { |
dfeec247 XL |
926 | self.config |
927 | .target_config | |
928 | .get(&target) | |
c30ab7b3 | 929 | .and_then(|t| t.musl_root.as_ref()) |
74b04a01 | 930 | .or_else(|| self.config.musl_root.as_ref()) |
9e0c209e SL |
931 | .map(|p| &**p) |
932 | } | |
476ff2be | 933 | |
f035d41b | 934 | /// Returns the "musl libdir" for this `target`. |
3dfed10e | 935 | fn musl_libdir(&self, target: TargetSelection) -> Option<PathBuf> { |
f035d41b XL |
936 | let t = self.config.target_config.get(&target)?; |
937 | if let libdir @ Some(_) = &t.musl_libdir { | |
938 | return libdir.clone(); | |
939 | } | |
940 | self.musl_root(target).map(|root| root.join("lib")) | |
941 | } | |
942 | ||
532ac7d7 | 943 | /// Returns the sysroot for the wasi target, if defined |
3dfed10e | 944 | fn wasi_root(&self, target: TargetSelection) -> Option<&Path> { |
dfeec247 | 945 | self.config.target_config.get(&target).and_then(|t| t.wasi_root.as_ref()).map(|p| &**p) |
532ac7d7 XL |
946 | } |
947 | ||
9fa01778 | 948 | /// Returns `true` if this is a no-std `target`, if defined |
3dfed10e | 949 | fn no_std(&self, target: TargetSelection) -> Option<bool> { |
dfeec247 | 950 | self.config.target_config.get(&target).map(|t| t.no_std) |
83c7162d XL |
951 | } |
952 | ||
9fa01778 | 953 | /// Returns `true` if the target will be tested using the `remote-test-client` |
7cac9316 | 954 | /// and `remote-test-server` binaries. |
3dfed10e | 955 | fn remote_tested(&self, target: TargetSelection) -> bool { |
dfeec247 XL |
956 | self.qemu_rootfs(target).is_some() |
957 | || target.contains("android") | |
958 | || env::var_os("TEST_DEVICE_ADDR").is_some() | |
7cac9316 XL |
959 | } |
960 | ||
8bb4bdeb XL |
961 | /// Returns the root of the "rootfs" image that this target will be using, |
962 | /// if one was configured. | |
963 | /// | |
964 | /// If `Some` is returned then that means that tests for this target are | |
965 | /// emulated with QEMU and binaries will need to be shipped to the emulator. | |
3dfed10e | 966 | fn qemu_rootfs(&self, target: TargetSelection) -> Option<&Path> { |
dfeec247 | 967 | self.config.target_config.get(&target).and_then(|t| t.qemu_rootfs.as_ref()).map(|p| &**p) |
8bb4bdeb XL |
968 | } |
969 | ||
476ff2be SL |
970 | /// Path to the python interpreter to use |
971 | fn python(&self) -> &Path { | |
972 | self.config.python.as_ref().unwrap() | |
973 | } | |
32a655c1 | 974 | |
ff7c6d11 XL |
975 | /// Temporary directory that extended error information is emitted to. |
976 | fn extended_error_dir(&self) -> PathBuf { | |
977 | self.out.join("tmp/extended-error-metadata") | |
978 | } | |
979 | ||
32a655c1 SL |
980 | /// Tests whether the `compiler` compiling for `target` should be forced to |
981 | /// use a stage1 compiler instead. | |
982 | /// | |
983 | /// Currently, by default, the build system does not perform a "full | |
984 | /// bootstrap" by default where we compile the compiler three times. | |
985 | /// Instead, we compile the compiler two times. The final stage (stage2) | |
986 | /// just copies the libraries from the previous stage, which is what this | |
987 | /// method detects. | |
988 | /// | |
989 | /// Here we return `true` if: | |
990 | /// | |
991 | /// * The build isn't performing a full bootstrap | |
992 | /// * The `compiler` is in the final stage, 2 | |
993 | /// * We're not cross-compiling, so the artifacts are already available in | |
994 | /// stage1 | |
995 | /// | |
996 | /// When all of these conditions are met the build will lift artifacts from | |
997 | /// the previous stage forward. | |
3dfed10e | 998 | fn force_use_stage1(&self, compiler: Compiler, target: TargetSelection) -> bool { |
dfeec247 XL |
999 | !self.config.full_bootstrap |
1000 | && compiler.stage >= 2 | |
1001 | && (self.hosts.iter().any(|h| *h == target) || target == self.build) | |
32a655c1 | 1002 | } |
8bb4bdeb | 1003 | |
8bb4bdeb XL |
1004 | /// Given `num` in the form "a.b.c" return a "release string" which |
1005 | /// describes the release version number. | |
1006 | /// | |
1007 | /// For example on nightly this returns "a.b.c-nightly", on beta it returns | |
1008 | /// "a.b.c-beta.1" and on stable it just returns "a.b.c". | |
1009 | fn release(&self, num: &str) -> String { | |
1010 | match &self.config.channel[..] { | |
1011 | "stable" => num.to_string(), | |
dfeec247 XL |
1012 | "beta" => { |
1013 | if self.rust_info.is_git() { | |
1014 | format!("{}-beta.{}", num, self.beta_prerelease_version()) | |
1015 | } else { | |
1016 | format!("{}-beta", num) | |
1017 | } | |
1018 | } | |
8bb4bdeb XL |
1019 | "nightly" => format!("{}-nightly", num), |
1020 | _ => format!("{}-dev", num), | |
1021 | } | |
1022 | } | |
1023 | ||
ff7c6d11 XL |
1024 | fn beta_prerelease_version(&self) -> u32 { |
1025 | if let Some(s) = self.prerelease_version.get() { | |
dfeec247 | 1026 | return s; |
ff7c6d11 XL |
1027 | } |
1028 | ||
f035d41b XL |
1029 | // Figure out how many merge commits happened since we branched off master. |
1030 | // That's our beta number! | |
1031 | // (Note that we use a `..` range, not the `...` symmetric difference.) | |
ff7c6d11 XL |
1032 | let count = output( |
1033 | Command::new("git") | |
1034 | .arg("rev-list") | |
1035 | .arg("--count") | |
1036 | .arg("--merges") | |
f035d41b | 1037 | .arg("refs/remotes/origin/master..HEAD") |
ff7c6d11 XL |
1038 | .current_dir(&self.src), |
1039 | ); | |
1040 | let n = count.trim().parse().unwrap(); | |
1041 | self.prerelease_version.set(Some(n)); | |
2c00a5a8 | 1042 | n |
ff7c6d11 XL |
1043 | } |
1044 | ||
8bb4bdeb XL |
1045 | /// Returns the value of `release` above for Rust itself. |
1046 | fn rust_release(&self) -> String { | |
1b1a35ee | 1047 | self.release(&self.version) |
8bb4bdeb XL |
1048 | } |
1049 | ||
1050 | /// Returns the "package version" for a component given the `num` release | |
1051 | /// number. | |
1052 | /// | |
1053 | /// The package version is typically what shows up in the names of tarballs. | |
1054 | /// For channels like beta/nightly it's just the channel name, otherwise | |
1055 | /// it's the `num` provided. | |
1056 | fn package_vers(&self, num: &str) -> String { | |
1057 | match &self.config.channel[..] { | |
1058 | "stable" => num.to_string(), | |
1059 | "beta" => "beta".to_string(), | |
1060 | "nightly" => "nightly".to_string(), | |
1061 | _ => format!("{}-dev", num), | |
1062 | } | |
1063 | } | |
1064 | ||
1065 | /// Returns the value of `package_vers` above for Rust itself. | |
1066 | fn rust_package_vers(&self) -> String { | |
1b1a35ee | 1067 | self.package_vers(&self.version) |
8faf50e0 XL |
1068 | } |
1069 | ||
1070 | fn llvm_tools_vers(&self) -> String { | |
1071 | self.rust_version() | |
1072 | } | |
1073 | ||
3dfed10e | 1074 | fn llvm_link_tools_dynamically(&self, target: TargetSelection) -> bool { |
74b04a01 | 1075 | target.contains("linux-gnu") || target.contains("apple-darwin") |
b7449926 XL |
1076 | } |
1077 | ||
8bb4bdeb XL |
1078 | /// Returns the `version` string associated with this compiler for Rust |
1079 | /// itself. | |
1080 | /// | |
1081 | /// Note that this is a descriptive string which includes the commit date, | |
1082 | /// sha, version, etc. | |
1083 | fn rust_version(&self) -> String { | |
1b1a35ee | 1084 | self.rust_info.version(self, &self.version) |
8bb4bdeb XL |
1085 | } |
1086 | ||
9fa01778 | 1087 | /// Returns the full commit hash. |
ea8adc8c XL |
1088 | fn rust_sha(&self) -> Option<&str> { |
1089 | self.rust_info.sha() | |
1090 | } | |
1091 | ||
cc61c64b XL |
1092 | /// Returns the `a.b.c` version that the given package is at. |
1093 | fn release_num(&self, package: &str) -> String { | |
7cac9316 | 1094 | let toml_file_name = self.src.join(&format!("src/tools/{}/Cargo.toml", package)); |
0731742a | 1095 | let toml = t!(fs::read_to_string(&toml_file_name)); |
8bb4bdeb | 1096 | for line in toml.lines() { |
f035d41b XL |
1097 | if let Some(stripped) = |
1098 | line.strip_prefix("version = \"").and_then(|s| s.strip_suffix("\"")) | |
1099 | { | |
1100 | return stripped.to_owned(); | |
8bb4bdeb XL |
1101 | } |
1102 | } | |
1103 | ||
cc61c64b | 1104 | panic!("failed to find version in {}'s Cargo.toml", package) |
8bb4bdeb XL |
1105 | } |
1106 | ||
9fa01778 | 1107 | /// Returns `true` if unstable features should be enabled for the compiler |
8bb4bdeb XL |
1108 | /// we're building. |
1109 | fn unstable_features(&self) -> bool { | |
1110 | match &self.config.channel[..] { | |
1111 | "stable" | "beta" => false, | |
1112 | "nightly" | _ => true, | |
1113 | } | |
1114 | } | |
7cac9316 | 1115 | |
f035d41b XL |
1116 | /// Returns a Vec of all the dependencies of the given root crate, |
1117 | /// including transitive dependencies and the root itself. Only includes | |
1118 | /// "local" crates (those in the local source tree, not from a registry). | |
29967ef6 | 1119 | fn in_tree_crates(&self, root: &str, target: Option<TargetSelection>) -> Vec<&Crate> { |
3b2f2976 | 1120 | let mut ret = Vec::new(); |
2c00a5a8 | 1121 | let mut list = vec![INTERNER.intern_str(root)]; |
3b2f2976 XL |
1122 | let mut visited = HashSet::new(); |
1123 | while let Some(krate) = list.pop() { | |
1124 | let krate = &self.crates[&krate]; | |
f035d41b | 1125 | ret.push(krate); |
0731742a | 1126 | for dep in &krate.deps { |
29967ef6 XL |
1127 | if !self.crates.contains_key(dep) { |
1128 | // Ignore non-workspace members. | |
1129 | continue; | |
1130 | } | |
f035d41b XL |
1131 | // Don't include optional deps if their features are not |
1132 | // enabled. Ideally this would be computed from `cargo | |
1133 | // metadata --features …`, but that is somewhat slow. Just | |
1134 | // skip `build_helper` since there aren't any operations we | |
1135 | // want to perform on it. In the future, we may want to | |
1136 | // consider just filtering all build and dev dependencies in | |
1137 | // metadata::build. | |
1138 | if visited.insert(dep) | |
1139 | && dep != "build_helper" | |
29967ef6 XL |
1140 | && (dep != "profiler_builtins" |
1141 | || target | |
1142 | .map(|t| self.config.profiler_enabled(t)) | |
1143 | .unwrap_or(self.config.any_profiler_enabled())) | |
f035d41b XL |
1144 | && (dep != "rustc_codegen_llvm" || self.config.llvm_enabled()) |
1145 | { | |
0731742a | 1146 | list.push(*dep); |
3b2f2976 XL |
1147 | } |
1148 | } | |
1149 | } | |
1150 | ret | |
1151 | } | |
83c7162d | 1152 | |
f035d41b | 1153 | fn read_stamp_file(&self, stamp: &Path) -> Vec<(PathBuf, DependencyType)> { |
83c7162d XL |
1154 | if self.config.dry_run { |
1155 | return Vec::new(); | |
1156 | } | |
1157 | ||
1158 | let mut paths = Vec::new(); | |
e74abb32 | 1159 | let contents = t!(fs::read(stamp), &stamp); |
83c7162d XL |
1160 | // This is the method we use for extracting paths from the stamp file passed to us. See |
1161 | // run_cargo for more information (in compile.rs). | |
1162 | for part in contents.split(|b| *b == 0) { | |
1163 | if part.is_empty() { | |
dfeec247 | 1164 | continue; |
83c7162d | 1165 | } |
f035d41b XL |
1166 | let dependency_type = match part[0] as char { |
1167 | 'h' => DependencyType::Host, | |
1168 | 's' => DependencyType::TargetSelfContained, | |
1169 | 't' => DependencyType::Target, | |
1170 | _ => unreachable!(), | |
1171 | }; | |
532ac7d7 | 1172 | let path = PathBuf::from(t!(str::from_utf8(&part[1..]))); |
f035d41b | 1173 | paths.push((path, dependency_type)); |
83c7162d XL |
1174 | } |
1175 | paths | |
1176 | } | |
1177 | ||
1178 | /// Copies a file from `src` to `dst` | |
1179 | pub fn copy(&self, src: &Path, dst: &Path) { | |
dfeec247 XL |
1180 | if self.config.dry_run { |
1181 | return; | |
1182 | } | |
532ac7d7 | 1183 | self.verbose_than(1, &format!("Copy {:?} to {:?}", src, dst)); |
dfeec247 XL |
1184 | if src == dst { |
1185 | return; | |
1186 | } | |
83c7162d | 1187 | let _ = fs::remove_file(&dst); |
b7449926 XL |
1188 | let metadata = t!(src.symlink_metadata()); |
1189 | if metadata.file_type().is_symlink() { | |
1190 | let link = t!(fs::read_link(src)); | |
1191 | t!(symlink_file(link, dst)); | |
1192 | } else if let Ok(()) = fs::hard_link(src, dst) { | |
1193 | // Attempt to "easy copy" by creating a hard link | |
1194 | // (symlinks don't work on windows), but if that fails | |
1195 | // just fall back to a slow `copy` operation. | |
1196 | } else { | |
1197 | if let Err(e) = fs::copy(src, dst) { | |
dfeec247 | 1198 | panic!("failed to copy `{}` to `{}`: {}", src.display(), dst.display(), e) |
b7449926 XL |
1199 | } |
1200 | t!(fs::set_permissions(dst, metadata.permissions())); | |
1201 | let atime = FileTime::from_last_access_time(&metadata); | |
1202 | let mtime = FileTime::from_last_modification_time(&metadata); | |
1203 | t!(filetime::set_file_times(dst, atime, mtime)); | |
83c7162d | 1204 | } |
83c7162d XL |
1205 | } |
1206 | ||
1207 | /// Search-and-replaces within a file. (Not maximally efficiently: allocates a | |
1208 | /// new string for each replacement.) | |
1209 | pub fn replace_in_file(&self, path: &Path, replacements: &[(&str, &str)]) { | |
dfeec247 XL |
1210 | if self.config.dry_run { |
1211 | return; | |
1212 | } | |
83c7162d XL |
1213 | let mut contents = String::new(); |
1214 | let mut file = t!(OpenOptions::new().read(true).write(true).open(path)); | |
1215 | t!(file.read_to_string(&mut contents)); | |
1216 | for &(target, replacement) in replacements { | |
1217 | contents = contents.replace(target, replacement); | |
1218 | } | |
1219 | t!(file.seek(SeekFrom::Start(0))); | |
1220 | t!(file.set_len(0)); | |
1221 | t!(file.write_all(contents.as_bytes())); | |
1222 | } | |
1223 | ||
1224 | /// Copies the `src` directory recursively to `dst`. Both are assumed to exist | |
1225 | /// when this function is called. | |
1226 | pub fn cp_r(&self, src: &Path, dst: &Path) { | |
dfeec247 XL |
1227 | if self.config.dry_run { |
1228 | return; | |
1229 | } | |
dc9dc135 | 1230 | for f in self.read_dir(src) { |
83c7162d XL |
1231 | let path = f.path(); |
1232 | let name = path.file_name().unwrap(); | |
1233 | let dst = dst.join(name); | |
1234 | if t!(f.file_type()).is_dir() { | |
1235 | t!(fs::create_dir_all(&dst)); | |
1236 | self.cp_r(&path, &dst); | |
1237 | } else { | |
1238 | let _ = fs::remove_file(&dst); | |
1239 | self.copy(&path, &dst); | |
1240 | } | |
1241 | } | |
1242 | } | |
1243 | ||
1244 | /// Copies the `src` directory recursively to `dst`. Both are assumed to exist | |
1245 | /// when this function is called. Unwanted files or directories can be skipped | |
1246 | /// by returning `false` from the filter function. | |
8faf50e0 | 1247 | pub fn cp_filtered(&self, src: &Path, dst: &Path, filter: &dyn Fn(&Path) -> bool) { |
83c7162d XL |
1248 | // Immediately recurse with an empty relative path |
1249 | self.recurse_(src, dst, Path::new(""), filter) | |
1250 | } | |
1251 | ||
1252 | // Inner function does the actual work | |
8faf50e0 | 1253 | fn recurse_(&self, src: &Path, dst: &Path, relative: &Path, filter: &dyn Fn(&Path) -> bool) { |
83c7162d XL |
1254 | for f in self.read_dir(src) { |
1255 | let path = f.path(); | |
1256 | let name = path.file_name().unwrap(); | |
1257 | let dst = dst.join(name); | |
1258 | let relative = relative.join(name); | |
1259 | // Only copy file or directory if the filter function returns true | |
1260 | if filter(&relative) { | |
1261 | if t!(f.file_type()).is_dir() { | |
1262 | let _ = fs::remove_dir_all(&dst); | |
1263 | self.create_dir(&dst); | |
1264 | self.recurse_(&path, &dst, &relative, filter); | |
1265 | } else { | |
1266 | let _ = fs::remove_file(&dst); | |
1267 | self.copy(&path, &dst); | |
1268 | } | |
1269 | } | |
1270 | } | |
1271 | } | |
1272 | ||
1273 | fn copy_to_folder(&self, src: &Path, dest_folder: &Path) { | |
1274 | let file_name = src.file_name().unwrap(); | |
1275 | let dest = dest_folder.join(file_name); | |
1276 | self.copy(src, &dest); | |
1277 | } | |
1278 | ||
1279 | fn install(&self, src: &Path, dstdir: &Path, perms: u32) { | |
dfeec247 XL |
1280 | if self.config.dry_run { |
1281 | return; | |
1282 | } | |
83c7162d | 1283 | let dst = dstdir.join(src.file_name().unwrap()); |
532ac7d7 | 1284 | self.verbose_than(1, &format!("Install {:?} to {:?}", src, dst)); |
83c7162d XL |
1285 | t!(fs::create_dir_all(dstdir)); |
1286 | drop(fs::remove_file(&dst)); | |
1287 | { | |
0bf4aa26 XL |
1288 | if !src.exists() { |
1289 | panic!("Error: File \"{}\" not found!", src.display()); | |
1290 | } | |
9fa01778 XL |
1291 | let metadata = t!(src.symlink_metadata()); |
1292 | if let Err(e) = fs::copy(&src, &dst) { | |
dfeec247 | 1293 | panic!("failed to copy `{}` to `{}`: {}", src.display(), dst.display(), e) |
9fa01778 XL |
1294 | } |
1295 | t!(fs::set_permissions(&dst, metadata.permissions())); | |
1296 | let atime = FileTime::from_last_access_time(&metadata); | |
1297 | let mtime = FileTime::from_last_modification_time(&metadata); | |
1298 | t!(filetime::set_file_times(&dst, atime, mtime)); | |
83c7162d XL |
1299 | } |
1300 | chmod(&dst, perms); | |
1301 | } | |
1302 | ||
1303 | fn create(&self, path: &Path, s: &str) { | |
dfeec247 XL |
1304 | if self.config.dry_run { |
1305 | return; | |
1306 | } | |
83c7162d XL |
1307 | t!(fs::write(path, s)); |
1308 | } | |
1309 | ||
1310 | fn read(&self, path: &Path) -> String { | |
dfeec247 XL |
1311 | if self.config.dry_run { |
1312 | return String::new(); | |
1313 | } | |
83c7162d XL |
1314 | t!(fs::read_to_string(path)) |
1315 | } | |
1316 | ||
1317 | fn create_dir(&self, dir: &Path) { | |
dfeec247 XL |
1318 | if self.config.dry_run { |
1319 | return; | |
1320 | } | |
83c7162d XL |
1321 | t!(fs::create_dir_all(dir)) |
1322 | } | |
1323 | ||
1324 | fn remove_dir(&self, dir: &Path) { | |
dfeec247 XL |
1325 | if self.config.dry_run { |
1326 | return; | |
1327 | } | |
83c7162d XL |
1328 | t!(fs::remove_dir_all(dir)) |
1329 | } | |
1330 | ||
dfeec247 | 1331 | fn read_dir(&self, dir: &Path) -> impl Iterator<Item = fs::DirEntry> { |
83c7162d XL |
1332 | let iter = match fs::read_dir(dir) { |
1333 | Ok(v) => v, | |
1334 | Err(_) if self.config.dry_run => return vec![].into_iter(), | |
1335 | Err(err) => panic!("could not read dir {:?}: {:?}", dir, err), | |
1336 | }; | |
1337 | iter.map(|e| t!(e)).collect::<Vec<_>>().into_iter() | |
1338 | } | |
1339 | ||
1340 | fn remove(&self, f: &Path) { | |
dfeec247 XL |
1341 | if self.config.dry_run { |
1342 | return; | |
1343 | } | |
83c7162d XL |
1344 | fs::remove_file(f).unwrap_or_else(|_| panic!("failed to remove {:?}", f)); |
1345 | } | |
1b1a35ee XL |
1346 | |
1347 | /// Returns if config.ninja is enabled, and checks for ninja existence, | |
1348 | /// exiting with a nicer error message if not. | |
1349 | fn ninja(&self) -> bool { | |
1350 | let mut cmd_finder = crate::sanity::Finder::new(); | |
1351 | ||
1352 | if self.config.ninja_in_file { | |
1353 | // Some Linux distros rename `ninja` to `ninja-build`. | |
1354 | // CMake can work with either binary name. | |
1355 | if cmd_finder.maybe_have("ninja-build").is_none() | |
1356 | && cmd_finder.maybe_have("ninja").is_none() | |
1357 | { | |
1358 | eprintln!( | |
1359 | " | |
1360 | Couldn't find required command: ninja | |
1361 | You should install ninja, or set ninja=false in config.toml | |
1362 | " | |
1363 | ); | |
1364 | std::process::exit(1); | |
1365 | } | |
1366 | } | |
1367 | ||
1368 | // If ninja isn't enabled but we're building for MSVC then we try | |
1369 | // doubly hard to enable it. It was realized in #43767 that the msbuild | |
1370 | // CMake generator for MSVC doesn't respect configuration options like | |
1371 | // disabling LLVM assertions, which can often be quite important! | |
1372 | // | |
1373 | // In these cases we automatically enable Ninja if we find it in the | |
1374 | // environment. | |
1375 | if !self.config.ninja_in_file && self.config.build.contains("msvc") { | |
1376 | if cmd_finder.maybe_have("ninja").is_some() { | |
1377 | return true; | |
1378 | } | |
1379 | } | |
1380 | ||
1381 | self.config.ninja_in_file | |
1382 | } | |
85aaf69f SL |
1383 | } |
1384 | ||
83c7162d XL |
1385 | #[cfg(unix)] |
1386 | fn chmod(path: &Path, perms: u32) { | |
1387 | use std::os::unix::fs::*; | |
1388 | t!(fs::set_permissions(path, fs::Permissions::from_mode(perms))); | |
1389 | } | |
1390 | #[cfg(windows)] | |
1391 | fn chmod(_path: &Path, _perms: u32) {} | |
1392 | ||
416331ca | 1393 | impl Compiler { |
3b2f2976 XL |
1394 | pub fn with_stage(mut self, stage: u32) -> Compiler { |
1395 | self.stage = stage; | |
1396 | self | |
5bcae85e SL |
1397 | } |
1398 | ||
9fa01778 | 1399 | /// Returns `true` if this is a snapshot compiler for `build`'s configuration |
3b2f2976 | 1400 | pub fn is_snapshot(&self, build: &Build) -> bool { |
041b39d2 | 1401 | self.stage == 0 && self.host == build.build |
5bcae85e | 1402 | } |
32a655c1 SL |
1403 | |
1404 | /// Returns if this compiler should be treated as a final stage one in the | |
1405 | /// current build session. | |
1406 | /// This takes into account whether we're performing a full bootstrap or | |
1407 | /// not; don't directly compare the stage with `2`! | |
3b2f2976 | 1408 | pub fn is_final_stage(&self, build: &Build) -> bool { |
32a655c1 SL |
1409 | let final_stage = if build.config.full_bootstrap { 2 } else { 1 }; |
1410 | self.stage >= final_stage | |
1411 | } | |
223e47cc | 1412 | } |
e1599b0c XL |
1413 | |
1414 | fn envify(s: &str) -> String { | |
1415 | s.chars() | |
1416 | .map(|c| match c { | |
1417 | '-' => '_', | |
1418 | c => c, | |
1419 | }) | |
1420 | .flat_map(|c| c.to_uppercase()) | |
1421 | .collect() | |
1422 | } |