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