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