]>
Commit | Line | Data |
---|---|---|
1a4d82fc | 1 | // Copyright 2015 The Rust Project Developers. See the COPYRIGHT |
223e47cc LB |
2 | // file at the top-level directory of this distribution and at |
3 | // http://rust-lang.org/COPYRIGHT. | |
4 | // | |
5 | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | |
6 | // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | |
7 | // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | |
8 | // option. This file may not be copied, modified, or distributed | |
9 | // except according to those terms. | |
10 | ||
5bcae85e | 11 | //! Implementation of rustbuild, the Rust build system. |
a7813a04 | 12 | //! |
5bcae85e SL |
13 | //! This module, and its descendants, are the implementation of the Rust build |
14 | //! system. Most of this build system is backed by Cargo but the outer layer | |
15 | //! here serves as the ability to orchestrate calling Cargo, sequencing Cargo | |
476ff2be | 16 | //! builds, building artifacts like LLVM, etc. The goals of rustbuild are: |
5bcae85e | 17 | //! |
476ff2be SL |
18 | //! * To be an easily understandable, easily extensible, and maintainable build |
19 | //! system. | |
20 | //! * Leverage standard tools in the Rust ecosystem to build the compiler, aka | |
21 | //! crates.io and Cargo. | |
22 | //! * A standard interface to build across all platforms, including MSVC | |
23 | //! | |
24 | //! ## Architecture | |
25 | //! | |
3b2f2976 XL |
26 | //! The build system defers most of the complicated logic managing invocations |
27 | //! of rustc and rustdoc to Cargo itself. However, moving through various stages | |
28 | //! and copying artifacts is still necessary for it to do. Each time rustbuild | |
29 | //! is invoked, it will iterate through the list of predefined steps and execute | |
30 | //! each serially in turn if it matches the paths passed or is a default rule. | |
31 | //! For each step rustbuild relies on the step internally being incremental and | |
476ff2be SL |
32 | //! parallel. Note, though, that the `-j` parameter to rustbuild gets forwarded |
33 | //! to appropriate test harnesses and such. | |
34 | //! | |
35 | //! Most of the "meaty" steps that matter are backed by Cargo, which does indeed | |
36 | //! have its own parallelism and incremental management. Later steps, like | |
37 | //! tests, aren't incremental and simply run the entire suite currently. | |
3b2f2976 XL |
38 | //! However, compiletest itself tries to avoid running tests when the artifacts |
39 | //! that are involved (mainly the compiler) haven't changed. | |
476ff2be SL |
40 | //! |
41 | //! When you execute `x.py build`, the steps which are executed are: | |
42 | //! | |
43 | //! * First, the python script is run. This will automatically download the | |
3b2f2976 | 44 | //! stage0 rustc and cargo according to `src/stage0.txt`, or use the cached |
476ff2be SL |
45 | //! versions if they're available. These are then used to compile rustbuild |
46 | //! itself (using Cargo). Finally, control is then transferred to rustbuild. | |
47 | //! | |
48 | //! * Rustbuild takes over, performs sanity checks, probes the environment, | |
3b2f2976 XL |
49 | //! reads configuration, and starts executing steps as it reads the command |
50 | //! line arguments (paths) or going through the default rules. | |
476ff2be | 51 | //! |
3b2f2976 XL |
52 | //! The build output will be something like the following: |
53 | //! | |
54 | //! Building stage0 std artifacts | |
55 | //! Copying stage0 std | |
56 | //! Building stage0 test artifacts | |
57 | //! Copying stage0 test | |
58 | //! Building stage0 compiler artifacts | |
59 | //! Copying stage0 rustc | |
60 | //! Assembling stage1 compiler | |
61 | //! Building stage1 std artifacts | |
62 | //! Copying stage1 std | |
63 | //! Building stage1 test artifacts | |
64 | //! Copying stage1 test | |
65 | //! Building stage1 compiler artifacts | |
66 | //! Copying stage1 rustc | |
67 | //! Assembling stage2 compiler | |
68 | //! Uplifting stage1 std | |
69 | //! Uplifting stage1 test | |
70 | //! Uplifting stage1 rustc | |
71 | //! | |
72 | //! Let's disect that a little: | |
73 | //! | |
74 | //! ## Building stage0 {std,test,compiler} artifacts | |
75 | //! | |
76 | //! These steps use the provided (downloaded, usually) compiler to compile the | |
77 | //! local Rust source into libraries we can use. | |
78 | //! | |
79 | //! ## Copying stage0 {std,test,rustc} | |
80 | //! | |
81 | //! This copies the build output from Cargo into | |
82 | //! `build/$HOST/stage0-sysroot/lib/rustlib/$ARCH/lib`. FIXME: This step's | |
83 | //! documentation should be expanded -- the information already here may be | |
84 | //! incorrect. | |
85 | //! | |
86 | //! ## Assembling stage1 compiler | |
87 | //! | |
88 | //! This copies the libraries we built in "building stage0 ... artifacts" into | |
89 | //! the stage1 compiler's lib directory. These are the host libraries that the | |
90 | //! compiler itself uses to run. These aren't actually used by artifacts the new | |
91 | //! compiler generates. This step also copies the rustc and rustdoc binaries we | |
92 | //! generated into build/$HOST/stage/bin. | |
93 | //! | |
94 | //! The stage1/bin/rustc is a fully functional compiler, but it doesn't yet have | |
95 | //! any libraries to link built binaries or libraries to. The next 3 steps will | |
96 | //! provide those libraries for it; they are mostly equivalent to constructing | |
97 | //! the stage1/bin compiler so we don't go through them individually. | |
98 | //! | |
99 | //! ## Uplifting stage1 {std,test,rustc} | |
100 | //! | |
101 | //! This step copies the libraries from the stage1 compiler sysroot into the | |
102 | //! stage2 compiler. This is done to avoid rebuilding the compiler; libraries | |
103 | //! we'd build in this step should be identical (in function, if not necessarily | |
104 | //! identical on disk) so there's no need to recompile the compiler again. Note | |
105 | //! that if you want to, you can enable the full-bootstrap option to change this | |
106 | //! behavior. | |
476ff2be SL |
107 | //! |
108 | //! Each step is driven by a separate Cargo project and rustbuild orchestrates | |
109 | //! copying files between steps and otherwise preparing for Cargo to run. | |
110 | //! | |
111 | //! ## Further information | |
112 | //! | |
113 | //! More documentation can be found in each respective module below, and you can | |
114 | //! also check out the `src/bootstrap/README.md` file for more information. | |
5bcae85e | 115 | |
0531ce1d | 116 | #![deny(warnings)] |
2c00a5a8 | 117 | #![feature(core_intrinsics)] |
32a655c1 | 118 | |
8bb4bdeb | 119 | #[macro_use] |
5bcae85e | 120 | extern crate build_helper; |
3b2f2976 XL |
121 | #[macro_use] |
122 | extern crate serde_derive; | |
123 | #[macro_use] | |
124 | extern crate lazy_static; | |
125 | extern crate serde_json; | |
5bcae85e SL |
126 | extern crate cmake; |
127 | extern crate filetime; | |
ea8adc8c | 128 | extern crate cc; |
5bcae85e | 129 | extern crate getopts; |
5bcae85e | 130 | extern crate num_cpus; |
5bcae85e | 131 | extern crate toml; |
ff7c6d11 | 132 | extern crate time; |
83c7162d XL |
133 | extern crate petgraph; |
134 | ||
135 | #[cfg(test)] | |
136 | #[macro_use] | |
137 | extern crate pretty_assertions; | |
a7813a04 | 138 | |
7cac9316 XL |
139 | #[cfg(unix)] |
140 | extern crate libc; | |
141 | ||
ff7c6d11 | 142 | use std::cell::{RefCell, Cell}; |
3b2f2976 | 143 | use std::collections::{HashSet, HashMap}; |
7453a54e | 144 | use std::env; |
83c7162d XL |
145 | use std::fs::{self, OpenOptions, File}; |
146 | use std::io::{self, Seek, SeekFrom, Write, Read}; | |
7cac9316 | 147 | use std::path::{PathBuf, Path}; |
ea8adc8c | 148 | use std::process::{self, Command}; |
3b2f2976 | 149 | use std::slice; |
83c7162d | 150 | use std::str; |
5bcae85e | 151 | |
ff7c6d11 | 152 | use build_helper::{run_silent, run_suppressed, try_run_silent, try_run_suppressed, output, mtime}; |
83c7162d | 153 | use filetime::FileTime; |
5bcae85e | 154 | |
3b2f2976 | 155 | use util::{exe, libdir, OutputFolder, CiEnv}; |
5bcae85e | 156 | |
ea8adc8c | 157 | mod cc_detect; |
5bcae85e SL |
158 | mod channel; |
159 | mod check; | |
2c00a5a8 | 160 | mod test; |
5bcae85e SL |
161 | mod clean; |
162 | mod compile; | |
c30ab7b3 | 163 | mod metadata; |
5bcae85e SL |
164 | mod config; |
165 | mod dist; | |
166 | mod doc; | |
167 | mod flags; | |
c30ab7b3 | 168 | mod install; |
5bcae85e SL |
169 | mod native; |
170 | mod sanity; | |
5bcae85e | 171 | pub mod util; |
3b2f2976 XL |
172 | mod builder; |
173 | mod cache; | |
174 | mod tool; | |
ea8adc8c | 175 | mod toolstate; |
5bcae85e SL |
176 | |
177 | #[cfg(windows)] | |
178 | mod job; | |
179 | ||
7cac9316 XL |
180 | #[cfg(unix)] |
181 | mod job { | |
182 | use libc; | |
183 | ||
184 | pub unsafe fn setup(build: &mut ::Build) { | |
185 | if build.config.low_priority { | |
186 | libc::setpriority(libc::PRIO_PGRP as _, 0, 10); | |
187 | } | |
188 | } | |
189 | } | |
190 | ||
191 | #[cfg(not(any(unix, windows)))] | |
5bcae85e | 192 | mod job { |
7cac9316 XL |
193 | pub unsafe fn setup(_build: &mut ::Build) { |
194 | } | |
5bcae85e SL |
195 | } |
196 | ||
197 | pub use config::Config; | |
3b2f2976 XL |
198 | use flags::Subcommand; |
199 | use cache::{Interned, INTERNER}; | |
ff7c6d11 | 200 | use toolstate::ToolState; |
5bcae85e SL |
201 | |
202 | /// A structure representing a Rust compiler. | |
203 | /// | |
204 | /// Each compiler has a `stage` that it is associated with and a `host` that | |
205 | /// corresponds to the platform the compiler runs on. This structure is used as | |
206 | /// a parameter to many methods below. | |
83c7162d | 207 | #[derive(Eq, PartialOrd, Ord, PartialEq, Clone, Copy, Hash, Debug)] |
3b2f2976 | 208 | pub struct Compiler { |
5bcae85e | 209 | stage: u32, |
3b2f2976 | 210 | host: Interned<String>, |
5bcae85e SL |
211 | } |
212 | ||
83c7162d XL |
213 | #[derive(PartialEq, Eq, Copy, Clone, Debug)] |
214 | pub enum DocTests { | |
215 | // Default, run normal tests and doc tests. | |
216 | Yes, | |
217 | // Do not run any doc tests. | |
218 | No, | |
219 | // Only run doc tests. | |
220 | Only, | |
221 | } | |
222 | ||
5bcae85e SL |
223 | /// Global configuration for the build system. |
224 | /// | |
225 | /// This structure transitively contains all configuration for the build system. | |
226 | /// All filesystem-encoded configuration is in `config`, all flags are in | |
227 | /// `flags`, and then parsed or probed information is listed in the keys below. | |
228 | /// | |
229 | /// This structure is a parameter of almost all methods in the build system, | |
230 | /// although most functions are implemented as free functions rather than | |
231 | /// methods specifically on this structure itself (to make it easier to | |
232 | /// organize). | |
233 | pub struct Build { | |
234 | // User-specified configuration via config.toml | |
235 | config: Config, | |
236 | ||
5bcae85e | 237 | // Derived properties from the above two configurations |
5bcae85e SL |
238 | src: PathBuf, |
239 | out: PathBuf, | |
8bb4bdeb XL |
240 | rust_info: channel::GitInfo, |
241 | cargo_info: channel::GitInfo, | |
cc61c64b | 242 | rls_info: channel::GitInfo, |
abe05a73 | 243 | rustfmt_info: channel::GitInfo, |
5bcae85e | 244 | local_rebuild: bool, |
041b39d2 | 245 | fail_fast: bool, |
83c7162d | 246 | doc_tests: DocTests, |
041b39d2 XL |
247 | verbosity: usize, |
248 | ||
249 | // Targets for which to build. | |
3b2f2976 XL |
250 | build: Interned<String>, |
251 | hosts: Vec<Interned<String>>, | |
252 | targets: Vec<Interned<String>>, | |
041b39d2 XL |
253 | |
254 | // Stage 0 (downloaded) compiler and cargo or their local rust equivalents. | |
255 | initial_rustc: PathBuf, | |
256 | initial_cargo: PathBuf, | |
5bcae85e SL |
257 | |
258 | // Probed tools at runtime | |
5bcae85e SL |
259 | lldb_version: Option<String>, |
260 | lldb_python_dir: Option<String>, | |
261 | ||
262 | // Runtime state filled in later on | |
abe05a73 XL |
263 | // C/C++ compilers and archiver for all targets |
264 | cc: HashMap<Interned<String>, cc::Tool>, | |
ea8adc8c | 265 | cxx: HashMap<Interned<String>, cc::Tool>, |
abe05a73 XL |
266 | ar: HashMap<Interned<String>, PathBuf>, |
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 | ||
322 | /// Build some tools, placing output in the "stageN-tools" directory. | |
323 | ToolStd, | |
324 | ToolTest, | |
325 | ToolRustc, | |
326 | } | |
327 | ||
328 | impl Mode { | |
329 | pub fn is_tool(&self) -> bool { | |
330 | match self { | |
331 | Mode::ToolStd | Mode::ToolTest | Mode::ToolRustc => true, | |
332 | _ => false | |
333 | } | |
334 | } | |
5bcae85e SL |
335 | } |
336 | ||
337 | impl Build { | |
338 | /// Creates a new set of build configuration from the `flags` on the command | |
339 | /// line and the filesystem `config`. | |
340 | /// | |
341 | /// By default all build output will be placed in the current directory. | |
3b2f2976 | 342 | pub fn new(config: Config) -> Build { |
3b2f2976 | 343 | let src = config.src.clone(); |
83c7162d | 344 | let out = config.out.clone(); |
5bcae85e | 345 | |
476ff2be SL |
346 | let is_sudo = match env::var_os("SUDO_USER") { |
347 | Some(sudo_user) => { | |
348 | match env::var_os("USER") { | |
349 | Some(user) => user != sudo_user, | |
350 | None => false, | |
351 | } | |
352 | } | |
353 | None => false, | |
354 | }; | |
3b2f2976 XL |
355 | let rust_info = channel::GitInfo::new(&config, &src); |
356 | let cargo_info = channel::GitInfo::new(&config, &src.join("src/tools/cargo")); | |
357 | let rls_info = channel::GitInfo::new(&config, &src.join("src/tools/rls")); | |
abe05a73 | 358 | let rustfmt_info = channel::GitInfo::new(&config, &src.join("src/tools/rustfmt")); |
476ff2be | 359 | |
83c7162d | 360 | let mut build = Build { |
041b39d2 XL |
361 | initial_rustc: config.initial_rustc.clone(), |
362 | initial_cargo: config.initial_cargo.clone(), | |
363 | local_rebuild: config.local_rebuild, | |
3b2f2976 | 364 | fail_fast: config.cmd.fail_fast(), |
0531ce1d | 365 | doc_tests: config.cmd.doc_tests(), |
3b2f2976 | 366 | verbosity: config.verbose, |
041b39d2 | 367 | |
3b2f2976 XL |
368 | build: config.build, |
369 | hosts: config.hosts.clone(), | |
370 | targets: config.targets.clone(), | |
041b39d2 | 371 | |
3b2f2976 XL |
372 | config, |
373 | src, | |
374 | out, | |
5bcae85e | 375 | |
3b2f2976 XL |
376 | rust_info, |
377 | cargo_info, | |
378 | rls_info, | |
abe05a73 | 379 | rustfmt_info, |
5bcae85e SL |
380 | cc: HashMap::new(), |
381 | cxx: HashMap::new(), | |
abe05a73 | 382 | ar: HashMap::new(), |
c30ab7b3 | 383 | crates: HashMap::new(), |
5bcae85e SL |
384 | lldb_version: None, |
385 | lldb_python_dir: None, | |
3b2f2976 | 386 | is_sudo, |
7cac9316 | 387 | ci_env: CiEnv::current(), |
ea8adc8c | 388 | delayed_failures: RefCell::new(Vec::new()), |
ff7c6d11 | 389 | prerelease_version: Cell::new(None), |
0531ce1d | 390 | tool_artifacts: Default::default(), |
83c7162d XL |
391 | }; |
392 | ||
393 | build.verbose("finding compilers"); | |
394 | cc_detect::find(&mut build); | |
395 | build.verbose("running sanity check"); | |
396 | sanity::check(&mut build); | |
397 | ||
398 | // If local-rust is the same major.minor as the current version, then force a | |
399 | // local-rebuild | |
400 | let local_version_verbose = output( | |
401 | Command::new(&build.initial_rustc).arg("--version").arg("--verbose")); | |
402 | let local_release = local_version_verbose | |
403 | .lines().filter(|x| x.starts_with("release:")) | |
404 | .next().unwrap().trim_left_matches("release:").trim(); | |
405 | let my_version = channel::CFG_RELEASE_NUM; | |
406 | if local_release.split('.').take(2).eq(my_version.split('.').take(2)) { | |
407 | build.verbose(&format!("auto-detected local-rebuild {}", local_release)); | |
408 | build.local_rebuild = true; | |
5bcae85e | 409 | } |
83c7162d XL |
410 | |
411 | build.verbose("learning about cargo"); | |
412 | metadata::build(&mut build); | |
413 | ||
414 | build | |
5bcae85e SL |
415 | } |
416 | ||
3b2f2976 | 417 | pub fn build_triple(&self) -> &[Interned<String>] { |
041b39d2 | 418 | unsafe { |
3b2f2976 | 419 | slice::from_raw_parts(&self.build, 1) |
041b39d2 XL |
420 | } |
421 | } | |
422 | ||
5bcae85e SL |
423 | /// Executes the entire build, as configured by the flags and configuration. |
424 | pub fn build(&mut self) { | |
5bcae85e | 425 | unsafe { |
7cac9316 | 426 | job::setup(self); |
5bcae85e SL |
427 | } |
428 | ||
ea8adc8c XL |
429 | if let Subcommand::Clean { all } = self.config.cmd { |
430 | return clean::clean(self, all); | |
5bcae85e SL |
431 | } |
432 | ||
83c7162d XL |
433 | { |
434 | let builder = builder::Builder::new(&self); | |
435 | if let Some(path) = builder.paths.get(0) { | |
436 | if path == Path::new("nonexistent/path/to/trigger/cargo/metadata") { | |
437 | return; | |
438 | } | |
439 | } | |
5bcae85e | 440 | } |
5bcae85e | 441 | |
83c7162d XL |
442 | if !self.config.dry_run { |
443 | { | |
444 | self.config.dry_run = true; | |
445 | let builder = builder::Builder::new(&self); | |
446 | builder.execute_cli(); | |
447 | } | |
448 | self.config.dry_run = false; | |
449 | let builder = builder::Builder::new(&self); | |
450 | builder.execute_cli(); | |
451 | } else { | |
452 | let builder = builder::Builder::new(&self); | |
453 | let _ = builder.execute_cli(); | |
454 | } | |
ea8adc8c XL |
455 | |
456 | // Check for postponed failures from `test --no-fail-fast`. | |
457 | let failures = self.delayed_failures.borrow(); | |
458 | if failures.len() > 0 { | |
459 | println!("\n{} command(s) did not execute successfully:\n", failures.len()); | |
460 | for failure in failures.iter() { | |
461 | println!(" - {}\n", failure); | |
462 | } | |
463 | process::exit(1); | |
464 | } | |
5bcae85e SL |
465 | } |
466 | ||
5bcae85e SL |
467 | /// Clear out `dir` if `input` is newer. |
468 | /// | |
469 | /// After this executes, it will also ensure that `dir` exists. | |
abe05a73 | 470 | fn clear_if_dirty(&self, dir: &Path, input: &Path) -> bool { |
5bcae85e | 471 | let stamp = dir.join(".stamp"); |
abe05a73 | 472 | let mut cleared = false; |
5bcae85e SL |
473 | if mtime(&stamp) < mtime(input) { |
474 | self.verbose(&format!("Dirty - {}", dir.display())); | |
475 | let _ = fs::remove_dir_all(dir); | |
abe05a73 | 476 | cleared = true; |
9e0c209e | 477 | } else if stamp.exists() { |
abe05a73 | 478 | return cleared; |
5bcae85e SL |
479 | } |
480 | t!(fs::create_dir_all(dir)); | |
481 | t!(File::create(stamp)); | |
abe05a73 | 482 | cleared |
5bcae85e SL |
483 | } |
484 | ||
5bcae85e SL |
485 | /// Get the space-separated set of activated features for the standard |
486 | /// library. | |
487 | fn std_features(&self) -> String { | |
476ff2be | 488 | let mut features = "panic-unwind".to_string(); |
8bb4bdeb | 489 | |
5bcae85e SL |
490 | if self.config.debug_jemalloc { |
491 | features.push_str(" debug-jemalloc"); | |
492 | } | |
493 | if self.config.use_jemalloc { | |
494 | features.push_str(" jemalloc"); | |
495 | } | |
496 | if self.config.backtrace { | |
497 | features.push_str(" backtrace"); | |
498 | } | |
041b39d2 XL |
499 | if self.config.profiler { |
500 | features.push_str(" profiler"); | |
501 | } | |
2c00a5a8 XL |
502 | if self.config.wasm_syscall { |
503 | features.push_str(" wasm_syscall"); | |
504 | } | |
041b39d2 | 505 | features |
5bcae85e SL |
506 | } |
507 | ||
508 | /// Get the space-separated set of activated features for the compiler. | |
509 | fn rustc_features(&self) -> String { | |
510 | let mut features = String::new(); | |
511 | if self.config.use_jemalloc { | |
512 | features.push_str(" jemalloc"); | |
513 | } | |
041b39d2 | 514 | features |
5bcae85e SL |
515 | } |
516 | ||
517 | /// Component directory that Cargo will produce output into (e.g. | |
518 | /// release/debug) | |
519 | fn cargo_dir(&self) -> &'static str { | |
520 | if self.config.rust_optimize {"release"} else {"debug"} | |
521 | } | |
522 | ||
abe05a73 XL |
523 | fn tools_dir(&self, compiler: Compiler) -> PathBuf { |
524 | let out = self.out.join(&*compiler.host).join(format!("stage{}-tools-bin", compiler.stage)); | |
525 | t!(fs::create_dir_all(&out)); | |
526 | out | |
527 | } | |
528 | ||
5bcae85e SL |
529 | /// Returns the root directory for all output generated in a particular |
530 | /// stage when running with a particular host compiler. | |
531 | /// | |
532 | /// The mode indicates what the root directory is for. | |
3b2f2976 | 533 | fn stage_out(&self, compiler: Compiler, mode: Mode) -> PathBuf { |
5bcae85e | 534 | let suffix = match mode { |
94b46f34 XL |
535 | Mode::Std => "-std", |
536 | Mode::Test => "-test", | |
537 | Mode::Codegen => "-rustc", | |
538 | Mode::Rustc => "-rustc", | |
539 | Mode::ToolStd | Mode::ToolTest | Mode::ToolRustc => "-tools", | |
5bcae85e | 540 | }; |
3b2f2976 | 541 | self.out.join(&*compiler.host) |
5bcae85e SL |
542 | .join(format!("stage{}{}", compiler.stage, suffix)) |
543 | } | |
544 | ||
545 | /// Returns the root output directory for all Cargo output in a given stage, | |
3b2f2976 | 546 | /// running a particular compiler, whether or not we're building the |
5bcae85e SL |
547 | /// standard library, and targeting the specified architecture. |
548 | fn cargo_out(&self, | |
3b2f2976 | 549 | compiler: Compiler, |
5bcae85e | 550 | mode: Mode, |
3b2f2976 XL |
551 | target: Interned<String>) -> PathBuf { |
552 | self.stage_out(compiler, mode).join(&*target).join(self.cargo_dir()) | |
5bcae85e SL |
553 | } |
554 | ||
555 | /// Root output directory for LLVM compiled for `target` | |
556 | /// | |
557 | /// Note that if LLVM is configured externally then the directory returned | |
558 | /// will likely be empty. | |
3b2f2976 XL |
559 | fn llvm_out(&self, target: Interned<String>) -> PathBuf { |
560 | self.out.join(&*target).join("llvm") | |
5bcae85e SL |
561 | } |
562 | ||
2c00a5a8 XL |
563 | fn emscripten_llvm_out(&self, target: Interned<String>) -> PathBuf { |
564 | self.out.join(&*target).join("llvm-emscripten") | |
565 | } | |
566 | ||
0531ce1d XL |
567 | fn lld_out(&self, target: Interned<String>) -> PathBuf { |
568 | self.out.join(&*target).join("lld") | |
569 | } | |
570 | ||
c30ab7b3 | 571 | /// Output directory for all documentation for a target |
3b2f2976 XL |
572 | fn doc_out(&self, target: Interned<String>) -> PathBuf { |
573 | self.out.join(&*target).join("doc") | |
c30ab7b3 SL |
574 | } |
575 | ||
0531ce1d XL |
576 | /// Output directory for all documentation for a target |
577 | fn compiler_doc_out(&self, target: Interned<String>) -> PathBuf { | |
578 | self.out.join(&*target).join("compiler-doc") | |
579 | } | |
580 | ||
041b39d2 | 581 | /// Output directory for some generated md crate documentation for a target (temporary) |
3b2f2976 XL |
582 | fn md_doc_out(&self, target: Interned<String>) -> Interned<PathBuf> { |
583 | INTERNER.intern_path(self.out.join(&*target).join("md-doc")) | |
041b39d2 XL |
584 | } |
585 | ||
8bb4bdeb XL |
586 | /// Output directory for all crate documentation for a target (temporary) |
587 | /// | |
588 | /// The artifacts here are then copied into `doc_out` above. | |
3b2f2976 XL |
589 | fn crate_doc_out(&self, target: Interned<String>) -> PathBuf { |
590 | self.out.join(&*target).join("crate-docs") | |
8bb4bdeb XL |
591 | } |
592 | ||
5bcae85e SL |
593 | /// Returns true if no custom `llvm-config` is set for the specified target. |
594 | /// | |
595 | /// If no custom `llvm-config` was specified then Rust's llvm will be used. | |
3b2f2976 XL |
596 | fn is_rust_llvm(&self, target: Interned<String>) -> bool { |
597 | match self.config.target_config.get(&target) { | |
5bcae85e SL |
598 | Some(ref c) => c.llvm_config.is_none(), |
599 | None => true | |
600 | } | |
601 | } | |
602 | ||
5bcae85e | 603 | /// Returns the path to `FileCheck` binary for the specified target |
3b2f2976 XL |
604 | fn llvm_filecheck(&self, target: Interned<String>) -> PathBuf { |
605 | let target_config = self.config.target_config.get(&target); | |
5bcae85e | 606 | if let Some(s) = target_config.and_then(|c| c.llvm_config.as_ref()) { |
32a655c1 | 607 | let llvm_bindir = output(Command::new(s).arg("--bindir")); |
3b2f2976 | 608 | Path::new(llvm_bindir.trim()).join(exe("FileCheck", &*target)) |
5bcae85e | 609 | } else { |
3b2f2976 | 610 | let base = self.llvm_out(self.config.build).join("build"); |
94b46f34 XL |
611 | let base = if !self.config.ninja && self.config.build.contains("msvc") { |
612 | if self.config.llvm_optimize { | |
613 | if self.config.llvm_release_debuginfo { | |
614 | base.join("RelWithDebInfo") | |
615 | } else { | |
616 | base.join("Release") | |
617 | } | |
618 | } else { | |
619 | base.join("Debug") | |
620 | } | |
5bcae85e | 621 | } else { |
94b46f34 XL |
622 | base |
623 | }; | |
624 | base.join("bin").join(exe("FileCheck", &*target)) | |
5bcae85e SL |
625 | } |
626 | } | |
627 | ||
8bb4bdeb | 628 | /// Directory for libraries built from C/C++ code and shared between stages. |
3b2f2976 XL |
629 | fn native_dir(&self, target: Interned<String>) -> PathBuf { |
630 | self.out.join(&*target).join("native") | |
8bb4bdeb XL |
631 | } |
632 | ||
5bcae85e SL |
633 | /// Root output directory for rust_test_helpers library compiled for |
634 | /// `target` | |
3b2f2976 | 635 | fn test_helpers_out(&self, target: Interned<String>) -> PathBuf { |
8bb4bdeb | 636 | self.native_dir(target).join("rust-test-helpers") |
5bcae85e SL |
637 | } |
638 | ||
476ff2be SL |
639 | /// Adds the `RUST_TEST_THREADS` env var if necessary |
640 | fn add_rust_test_threads(&self, cmd: &mut Command) { | |
641 | if env::var_os("RUST_TEST_THREADS").is_none() { | |
642 | cmd.env("RUST_TEST_THREADS", self.jobs().to_string()); | |
643 | } | |
5bcae85e SL |
644 | } |
645 | ||
5bcae85e SL |
646 | /// Returns the libdir of the snapshot compiler. |
647 | fn rustc_snapshot_libdir(&self) -> PathBuf { | |
041b39d2 | 648 | self.initial_rustc.parent().unwrap().parent().unwrap() |
5bcae85e SL |
649 | .join(libdir(&self.config.build)) |
650 | } | |
651 | ||
652 | /// Runs a command, printing out nice contextual information if it fails. | |
653 | fn run(&self, cmd: &mut Command) { | |
83c7162d | 654 | if self.config.dry_run { return; } |
ff7c6d11 XL |
655 | self.verbose(&format!("running: {:?}", cmd)); |
656 | run_silent(cmd) | |
5bcae85e SL |
657 | } |
658 | ||
8bb4bdeb XL |
659 | /// Runs a command, printing out nice contextual information if it fails. |
660 | fn run_quiet(&self, cmd: &mut Command) { | |
83c7162d | 661 | if self.config.dry_run { return; } |
8bb4bdeb | 662 | self.verbose(&format!("running: {:?}", cmd)); |
ff7c6d11 | 663 | run_suppressed(cmd) |
8bb4bdeb XL |
664 | } |
665 | ||
ff7c6d11 XL |
666 | /// Runs a command, printing out nice contextual information if it fails. |
667 | /// Exits if the command failed to execute at all, otherwise returns its | |
668 | /// `status.success()`. | |
669 | fn try_run(&self, cmd: &mut Command) -> bool { | |
83c7162d | 670 | if self.config.dry_run { return true; } |
7cac9316 | 671 | self.verbose(&format!("running: {:?}", cmd)); |
ff7c6d11 | 672 | try_run_silent(cmd) |
7cac9316 XL |
673 | } |
674 | ||
675 | /// Runs a command, printing out nice contextual information if it fails. | |
676 | /// Exits if the command failed to execute at all, otherwise returns its | |
677 | /// `status.success()`. | |
678 | fn try_run_quiet(&self, cmd: &mut Command) -> bool { | |
83c7162d | 679 | if self.config.dry_run { return true; } |
7cac9316 | 680 | self.verbose(&format!("running: {:?}", cmd)); |
ff7c6d11 | 681 | try_run_suppressed(cmd) |
7cac9316 XL |
682 | } |
683 | ||
041b39d2 XL |
684 | pub fn is_verbose(&self) -> bool { |
685 | self.verbosity > 0 | |
686 | } | |
687 | ||
5bcae85e SL |
688 | /// Prints a message if this build is configured in verbose mode. |
689 | fn verbose(&self, msg: &str) { | |
041b39d2 | 690 | if self.is_verbose() { |
5bcae85e SL |
691 | println!("{}", msg); |
692 | } | |
693 | } | |
694 | ||
83c7162d XL |
695 | fn info(&self, msg: &str) { |
696 | if self.config.dry_run { return; } | |
697 | println!("{}", msg); | |
698 | } | |
699 | ||
5bcae85e SL |
700 | /// Returns the number of parallel jobs that have been configured for this |
701 | /// build. | |
702 | fn jobs(&self) -> u32 { | |
3b2f2976 | 703 | self.config.jobs.unwrap_or_else(|| num_cpus::get() as u32) |
5bcae85e SL |
704 | } |
705 | ||
706 | /// Returns the path to the C compiler for the target specified. | |
3b2f2976 | 707 | fn cc(&self, target: Interned<String>) -> &Path { |
abe05a73 | 708 | self.cc[&target].path() |
5bcae85e SL |
709 | } |
710 | ||
711 | /// Returns a list of flags to pass to the C compiler for the target | |
712 | /// specified. | |
3b2f2976 | 713 | fn cflags(&self, target: Interned<String>) -> Vec<String> { |
5bcae85e | 714 | // Filter out -O and /O (the optimization flags) that we picked up from |
ea8adc8c | 715 | // cc-rs because the build scripts will determine that for themselves. |
abe05a73 | 716 | let mut base = self.cc[&target].args().iter() |
5bcae85e SL |
717 | .map(|s| s.to_string_lossy().into_owned()) |
718 | .filter(|s| !s.starts_with("-O") && !s.starts_with("/O")) | |
719 | .collect::<Vec<_>>(); | |
720 | ||
cc61c64b | 721 | // If we're compiling on macOS then we add a few unconditional flags |
5bcae85e SL |
722 | // indicating that we want libc++ (more filled out than libstdc++) and |
723 | // we want to compile for 10.7. This way we can ensure that | |
724 | // LLVM/jemalloc/etc are all properly compiled. | |
725 | if target.contains("apple-darwin") { | |
726 | base.push("-stdlib=libc++".into()); | |
5bcae85e | 727 | } |
7cac9316 XL |
728 | |
729 | // Work around an apparently bad MinGW / GCC optimization, | |
730 | // See: http://lists.llvm.org/pipermail/cfe-dev/2016-December/051980.html | |
731 | // See: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=78936 | |
3b2f2976 | 732 | if &*target == "i686-pc-windows-gnu" { |
7cac9316 XL |
733 | base.push("-fno-omit-frame-pointer".into()); |
734 | } | |
041b39d2 | 735 | base |
5bcae85e SL |
736 | } |
737 | ||
738 | /// Returns the path to the `ar` archive utility for the target specified. | |
3b2f2976 | 739 | fn ar(&self, target: Interned<String>) -> Option<&Path> { |
abe05a73 | 740 | self.ar.get(&target).map(|p| &**p) |
5bcae85e SL |
741 | } |
742 | ||
041b39d2 | 743 | /// Returns the path to the C++ compiler for the target specified. |
3b2f2976 XL |
744 | fn cxx(&self, target: Interned<String>) -> Result<&Path, String> { |
745 | match self.cxx.get(&target) { | |
041b39d2 XL |
746 | Some(p) => Ok(p.path()), |
747 | None => Err(format!( | |
748 | "target `{}` is not configured as a host, only as a target", | |
749 | target)) | |
9e0c209e | 750 | } |
5bcae85e SL |
751 | } |
752 | ||
2c00a5a8 | 753 | /// Returns the path to the linker for the given target if it needs to be overridden. |
abe05a73 XL |
754 | fn linker(&self, target: Interned<String>) -> Option<&Path> { |
755 | if let Some(linker) = self.config.target_config.get(&target) | |
756 | .and_then(|c| c.linker.as_ref()) { | |
757 | Some(linker) | |
758 | } else if target != self.config.build && | |
0531ce1d XL |
759 | !target.contains("msvc") && |
760 | !target.contains("emscripten") && | |
761 | !target.contains("wasm32") { | |
abe05a73 XL |
762 | Some(self.cc(target)) |
763 | } else { | |
764 | None | |
5bcae85e | 765 | } |
85aaf69f | 766 | } |
9e0c209e | 767 | |
3b2f2976 XL |
768 | /// Returns if this target should statically link the C runtime, if specified |
769 | fn crt_static(&self, target: Interned<String>) -> Option<bool> { | |
770 | if target.contains("pc-windows-msvc") { | |
771 | Some(true) | |
772 | } else { | |
773 | self.config.target_config.get(&target) | |
774 | .and_then(|t| t.crt_static) | |
775 | } | |
776 | } | |
777 | ||
9e0c209e | 778 | /// Returns the "musl root" for this `target`, if defined |
3b2f2976 XL |
779 | fn musl_root(&self, target: Interned<String>) -> Option<&Path> { |
780 | self.config.target_config.get(&target) | |
c30ab7b3 | 781 | .and_then(|t| t.musl_root.as_ref()) |
9e0c209e SL |
782 | .or(self.config.musl_root.as_ref()) |
783 | .map(|p| &**p) | |
784 | } | |
476ff2be | 785 | |
83c7162d XL |
786 | /// Returns true if this is a no-std `target`, if defined |
787 | fn no_std(&self, target: Interned<String>) -> Option<bool> { | |
788 | self.config.target_config.get(&target) | |
789 | .map(|t| t.no_std) | |
790 | } | |
791 | ||
7cac9316 XL |
792 | /// Returns whether the target will be tested using the `remote-test-client` |
793 | /// and `remote-test-server` binaries. | |
3b2f2976 XL |
794 | fn remote_tested(&self, target: Interned<String>) -> bool { |
795 | self.qemu_rootfs(target).is_some() || target.contains("android") || | |
796 | env::var_os("TEST_DEVICE_ADDR").is_some() | |
7cac9316 XL |
797 | } |
798 | ||
8bb4bdeb XL |
799 | /// Returns the root of the "rootfs" image that this target will be using, |
800 | /// if one was configured. | |
801 | /// | |
802 | /// If `Some` is returned then that means that tests for this target are | |
803 | /// emulated with QEMU and binaries will need to be shipped to the emulator. | |
3b2f2976 XL |
804 | fn qemu_rootfs(&self, target: Interned<String>) -> Option<&Path> { |
805 | self.config.target_config.get(&target) | |
8bb4bdeb XL |
806 | .and_then(|t| t.qemu_rootfs.as_ref()) |
807 | .map(|p| &**p) | |
808 | } | |
809 | ||
476ff2be SL |
810 | /// Path to the python interpreter to use |
811 | fn python(&self) -> &Path { | |
812 | self.config.python.as_ref().unwrap() | |
813 | } | |
32a655c1 | 814 | |
ff7c6d11 XL |
815 | /// Temporary directory that extended error information is emitted to. |
816 | fn extended_error_dir(&self) -> PathBuf { | |
817 | self.out.join("tmp/extended-error-metadata") | |
818 | } | |
819 | ||
32a655c1 SL |
820 | /// Tests whether the `compiler` compiling for `target` should be forced to |
821 | /// use a stage1 compiler instead. | |
822 | /// | |
823 | /// Currently, by default, the build system does not perform a "full | |
824 | /// bootstrap" by default where we compile the compiler three times. | |
825 | /// Instead, we compile the compiler two times. The final stage (stage2) | |
826 | /// just copies the libraries from the previous stage, which is what this | |
827 | /// method detects. | |
828 | /// | |
829 | /// Here we return `true` if: | |
830 | /// | |
831 | /// * The build isn't performing a full bootstrap | |
832 | /// * The `compiler` is in the final stage, 2 | |
833 | /// * We're not cross-compiling, so the artifacts are already available in | |
834 | /// stage1 | |
835 | /// | |
836 | /// When all of these conditions are met the build will lift artifacts from | |
837 | /// the previous stage forward. | |
3b2f2976 | 838 | fn force_use_stage1(&self, compiler: Compiler, target: Interned<String>) -> bool { |
32a655c1 SL |
839 | !self.config.full_bootstrap && |
840 | compiler.stage >= 2 && | |
ea8adc8c | 841 | (self.hosts.iter().any(|h| *h == target) || target == self.build) |
32a655c1 | 842 | } |
8bb4bdeb XL |
843 | |
844 | /// Returns the directory that OpenSSL artifacts are compiled into if | |
845 | /// configured to do so. | |
3b2f2976 | 846 | fn openssl_dir(&self, target: Interned<String>) -> Option<PathBuf> { |
8bb4bdeb XL |
847 | // OpenSSL not used on Windows |
848 | if target.contains("windows") { | |
849 | None | |
850 | } else if self.config.openssl_static { | |
3b2f2976 | 851 | Some(self.out.join(&*target).join("openssl")) |
8bb4bdeb XL |
852 | } else { |
853 | None | |
854 | } | |
855 | } | |
856 | ||
857 | /// Returns the directory that OpenSSL artifacts are installed into if | |
858 | /// configured as such. | |
3b2f2976 | 859 | fn openssl_install_dir(&self, target: Interned<String>) -> Option<PathBuf> { |
8bb4bdeb XL |
860 | self.openssl_dir(target).map(|p| p.join("install")) |
861 | } | |
862 | ||
863 | /// Given `num` in the form "a.b.c" return a "release string" which | |
864 | /// describes the release version number. | |
865 | /// | |
866 | /// For example on nightly this returns "a.b.c-nightly", on beta it returns | |
867 | /// "a.b.c-beta.1" and on stable it just returns "a.b.c". | |
868 | fn release(&self, num: &str) -> String { | |
869 | match &self.config.channel[..] { | |
870 | "stable" => num.to_string(), | |
ff7c6d11 XL |
871 | "beta" => if self.rust_info.is_git() { |
872 | format!("{}-beta.{}", num, self.beta_prerelease_version()) | |
873 | } else { | |
874 | format!("{}-beta", num) | |
875 | }, | |
8bb4bdeb XL |
876 | "nightly" => format!("{}-nightly", num), |
877 | _ => format!("{}-dev", num), | |
878 | } | |
879 | } | |
880 | ||
ff7c6d11 XL |
881 | fn beta_prerelease_version(&self) -> u32 { |
882 | if let Some(s) = self.prerelease_version.get() { | |
883 | return s | |
884 | } | |
885 | ||
886 | let beta = output( | |
887 | Command::new("git") | |
888 | .arg("ls-remote") | |
889 | .arg("origin") | |
890 | .arg("beta") | |
891 | .current_dir(&self.src) | |
892 | ); | |
893 | let beta = beta.trim().split_whitespace().next().unwrap(); | |
894 | let master = output( | |
895 | Command::new("git") | |
896 | .arg("ls-remote") | |
897 | .arg("origin") | |
898 | .arg("master") | |
899 | .current_dir(&self.src) | |
900 | ); | |
901 | let master = master.trim().split_whitespace().next().unwrap(); | |
902 | ||
903 | // Figure out where the current beta branch started. | |
904 | let base = output( | |
905 | Command::new("git") | |
906 | .arg("merge-base") | |
907 | .arg(beta) | |
908 | .arg(master) | |
909 | .current_dir(&self.src), | |
910 | ); | |
911 | let base = base.trim(); | |
912 | ||
913 | // Next figure out how many merge commits happened since we branched off | |
914 | // beta. That's our beta number! | |
915 | let count = output( | |
916 | Command::new("git") | |
917 | .arg("rev-list") | |
918 | .arg("--count") | |
919 | .arg("--merges") | |
920 | .arg(format!("{}...HEAD", base)) | |
921 | .current_dir(&self.src), | |
922 | ); | |
923 | let n = count.trim().parse().unwrap(); | |
924 | self.prerelease_version.set(Some(n)); | |
2c00a5a8 | 925 | n |
ff7c6d11 XL |
926 | } |
927 | ||
8bb4bdeb XL |
928 | /// Returns the value of `release` above for Rust itself. |
929 | fn rust_release(&self) -> String { | |
930 | self.release(channel::CFG_RELEASE_NUM) | |
931 | } | |
932 | ||
933 | /// Returns the "package version" for a component given the `num` release | |
934 | /// number. | |
935 | /// | |
936 | /// The package version is typically what shows up in the names of tarballs. | |
937 | /// For channels like beta/nightly it's just the channel name, otherwise | |
938 | /// it's the `num` provided. | |
939 | fn package_vers(&self, num: &str) -> String { | |
940 | match &self.config.channel[..] { | |
941 | "stable" => num.to_string(), | |
942 | "beta" => "beta".to_string(), | |
943 | "nightly" => "nightly".to_string(), | |
944 | _ => format!("{}-dev", num), | |
945 | } | |
946 | } | |
947 | ||
948 | /// Returns the value of `package_vers` above for Rust itself. | |
949 | fn rust_package_vers(&self) -> String { | |
950 | self.package_vers(channel::CFG_RELEASE_NUM) | |
951 | } | |
952 | ||
953 | /// Returns the value of `package_vers` above for Cargo | |
954 | fn cargo_package_vers(&self) -> String { | |
cc61c64b | 955 | self.package_vers(&self.release_num("cargo")) |
8bb4bdeb XL |
956 | } |
957 | ||
7cac9316 XL |
958 | /// Returns the value of `package_vers` above for rls |
959 | fn rls_package_vers(&self) -> String { | |
960 | self.package_vers(&self.release_num("rls")) | |
961 | } | |
962 | ||
abe05a73 XL |
963 | /// Returns the value of `package_vers` above for rustfmt |
964 | fn rustfmt_package_vers(&self) -> String { | |
965 | self.package_vers(&self.release_num("rustfmt")) | |
966 | } | |
967 | ||
8bb4bdeb XL |
968 | /// Returns the `version` string associated with this compiler for Rust |
969 | /// itself. | |
970 | /// | |
971 | /// Note that this is a descriptive string which includes the commit date, | |
972 | /// sha, version, etc. | |
973 | fn rust_version(&self) -> String { | |
974 | self.rust_info.version(self, channel::CFG_RELEASE_NUM) | |
975 | } | |
976 | ||
ea8adc8c XL |
977 | /// Return the full commit hash |
978 | fn rust_sha(&self) -> Option<&str> { | |
979 | self.rust_info.sha() | |
980 | } | |
981 | ||
cc61c64b XL |
982 | /// Returns the `a.b.c` version that the given package is at. |
983 | fn release_num(&self, package: &str) -> String { | |
8bb4bdeb | 984 | let mut toml = String::new(); |
7cac9316 | 985 | let toml_file_name = self.src.join(&format!("src/tools/{}/Cargo.toml", package)); |
cc61c64b | 986 | t!(t!(File::open(toml_file_name)).read_to_string(&mut toml)); |
8bb4bdeb XL |
987 | for line in toml.lines() { |
988 | let prefix = "version = \""; | |
989 | let suffix = "\""; | |
990 | if line.starts_with(prefix) && line.ends_with(suffix) { | |
991 | return line[prefix.len()..line.len() - suffix.len()].to_string() | |
992 | } | |
993 | } | |
994 | ||
cc61c64b | 995 | panic!("failed to find version in {}'s Cargo.toml", package) |
8bb4bdeb XL |
996 | } |
997 | ||
998 | /// Returns whether unstable features should be enabled for the compiler | |
999 | /// we're building. | |
1000 | fn unstable_features(&self) -> bool { | |
1001 | match &self.config.channel[..] { | |
1002 | "stable" | "beta" => false, | |
1003 | "nightly" | _ => true, | |
1004 | } | |
1005 | } | |
7cac9316 XL |
1006 | |
1007 | /// Fold the output of the commands after this method into a group. The fold | |
1008 | /// ends when the returned object is dropped. Folding can only be used in | |
1009 | /// the Travis CI environment. | |
1010 | pub fn fold_output<D, F>(&self, name: F) -> Option<OutputFolder> | |
1011 | where D: Into<String>, F: FnOnce() -> D | |
1012 | { | |
83c7162d | 1013 | if !self.config.dry_run && self.ci_env == CiEnv::Travis { |
7cac9316 XL |
1014 | Some(OutputFolder::new(name().into())) |
1015 | } else { | |
1016 | None | |
1017 | } | |
1018 | } | |
3b2f2976 | 1019 | |
ff7c6d11 XL |
1020 | /// Updates the actual toolstate of a tool. |
1021 | /// | |
1022 | /// The toolstates are saved to the file specified by the key | |
1023 | /// `rust.save-toolstates` in `config.toml`. If unspecified, nothing will be | |
1024 | /// done. The file is updated immediately after this function completes. | |
1025 | pub fn save_toolstate(&self, tool: &str, state: ToolState) { | |
1026 | use std::io::{Seek, SeekFrom}; | |
1027 | ||
1028 | if let Some(ref path) = self.config.save_toolstates { | |
1029 | let mut file = t!(fs::OpenOptions::new() | |
1030 | .create(true) | |
1031 | .read(true) | |
1032 | .write(true) | |
1033 | .open(path)); | |
1034 | ||
1035 | let mut current_toolstates: HashMap<Box<str>, ToolState> = | |
1036 | serde_json::from_reader(&mut file).unwrap_or_default(); | |
1037 | current_toolstates.insert(tool.into(), state); | |
1038 | t!(file.seek(SeekFrom::Start(0))); | |
1039 | t!(file.set_len(0)); | |
1040 | t!(serde_json::to_writer(file, ¤t_toolstates)); | |
1041 | } | |
1042 | } | |
1043 | ||
2c00a5a8 | 1044 | fn in_tree_crates(&self, root: &str) -> Vec<&Crate> { |
3b2f2976 | 1045 | let mut ret = Vec::new(); |
2c00a5a8 | 1046 | let mut list = vec![INTERNER.intern_str(root)]; |
3b2f2976 XL |
1047 | let mut visited = HashSet::new(); |
1048 | while let Some(krate) = list.pop() { | |
1049 | let krate = &self.crates[&krate]; | |
2c00a5a8 XL |
1050 | if krate.is_local(self) { |
1051 | ret.push(krate); | |
1052 | for dep in &krate.deps { | |
1053 | if visited.insert(dep) && dep != "build_helper" { | |
1054 | list.push(*dep); | |
1055 | } | |
3b2f2976 XL |
1056 | } |
1057 | } | |
1058 | } | |
1059 | ret | |
1060 | } | |
83c7162d XL |
1061 | |
1062 | fn read_stamp_file(&self, stamp: &Path) -> Vec<PathBuf> { | |
1063 | if self.config.dry_run { | |
1064 | return Vec::new(); | |
1065 | } | |
1066 | ||
1067 | let mut paths = Vec::new(); | |
1068 | let mut contents = Vec::new(); | |
1069 | t!(t!(File::open(stamp)).read_to_end(&mut contents)); | |
1070 | // This is the method we use for extracting paths from the stamp file passed to us. See | |
1071 | // run_cargo for more information (in compile.rs). | |
1072 | for part in contents.split(|b| *b == 0) { | |
1073 | if part.is_empty() { | |
1074 | continue | |
1075 | } | |
1076 | let path = PathBuf::from(t!(str::from_utf8(part))); | |
1077 | paths.push(path); | |
1078 | } | |
1079 | paths | |
1080 | } | |
1081 | ||
1082 | /// Copies a file from `src` to `dst` | |
1083 | pub fn copy(&self, src: &Path, dst: &Path) { | |
1084 | if self.config.dry_run { return; } | |
1085 | let _ = fs::remove_file(&dst); | |
1086 | // Attempt to "easy copy" by creating a hard link (symlinks don't work on | |
1087 | // windows), but if that fails just fall back to a slow `copy` operation. | |
1088 | if let Ok(()) = fs::hard_link(src, dst) { | |
1089 | return | |
1090 | } | |
1091 | if let Err(e) = fs::copy(src, dst) { | |
1092 | panic!("failed to copy `{}` to `{}`: {}", src.display(), | |
1093 | dst.display(), e) | |
1094 | } | |
1095 | let metadata = t!(src.metadata()); | |
1096 | t!(fs::set_permissions(dst, metadata.permissions())); | |
1097 | let atime = FileTime::from_last_access_time(&metadata); | |
1098 | let mtime = FileTime::from_last_modification_time(&metadata); | |
1099 | t!(filetime::set_file_times(dst, atime, mtime)); | |
1100 | } | |
1101 | ||
1102 | /// Search-and-replaces within a file. (Not maximally efficiently: allocates a | |
1103 | /// new string for each replacement.) | |
1104 | pub fn replace_in_file(&self, path: &Path, replacements: &[(&str, &str)]) { | |
1105 | if self.config.dry_run { return; } | |
1106 | let mut contents = String::new(); | |
1107 | let mut file = t!(OpenOptions::new().read(true).write(true).open(path)); | |
1108 | t!(file.read_to_string(&mut contents)); | |
1109 | for &(target, replacement) in replacements { | |
1110 | contents = contents.replace(target, replacement); | |
1111 | } | |
1112 | t!(file.seek(SeekFrom::Start(0))); | |
1113 | t!(file.set_len(0)); | |
1114 | t!(file.write_all(contents.as_bytes())); | |
1115 | } | |
1116 | ||
1117 | /// Copies the `src` directory recursively to `dst`. Both are assumed to exist | |
1118 | /// when this function is called. | |
1119 | pub fn cp_r(&self, src: &Path, dst: &Path) { | |
1120 | if self.config.dry_run { return; } | |
1121 | for f in t!(fs::read_dir(src)) { | |
1122 | let f = t!(f); | |
1123 | let path = f.path(); | |
1124 | let name = path.file_name().unwrap(); | |
1125 | let dst = dst.join(name); | |
1126 | if t!(f.file_type()).is_dir() { | |
1127 | t!(fs::create_dir_all(&dst)); | |
1128 | self.cp_r(&path, &dst); | |
1129 | } else { | |
1130 | let _ = fs::remove_file(&dst); | |
1131 | self.copy(&path, &dst); | |
1132 | } | |
1133 | } | |
1134 | } | |
1135 | ||
1136 | /// Copies the `src` directory recursively to `dst`. Both are assumed to exist | |
1137 | /// when this function is called. Unwanted files or directories can be skipped | |
1138 | /// by returning `false` from the filter function. | |
1139 | pub fn cp_filtered(&self, src: &Path, dst: &Path, filter: &Fn(&Path) -> bool) { | |
1140 | // Immediately recurse with an empty relative path | |
1141 | self.recurse_(src, dst, Path::new(""), filter) | |
1142 | } | |
1143 | ||
1144 | // Inner function does the actual work | |
1145 | fn recurse_(&self, src: &Path, dst: &Path, relative: &Path, filter: &Fn(&Path) -> bool) { | |
1146 | for f in self.read_dir(src) { | |
1147 | let path = f.path(); | |
1148 | let name = path.file_name().unwrap(); | |
1149 | let dst = dst.join(name); | |
1150 | let relative = relative.join(name); | |
1151 | // Only copy file or directory if the filter function returns true | |
1152 | if filter(&relative) { | |
1153 | if t!(f.file_type()).is_dir() { | |
1154 | let _ = fs::remove_dir_all(&dst); | |
1155 | self.create_dir(&dst); | |
1156 | self.recurse_(&path, &dst, &relative, filter); | |
1157 | } else { | |
1158 | let _ = fs::remove_file(&dst); | |
1159 | self.copy(&path, &dst); | |
1160 | } | |
1161 | } | |
1162 | } | |
1163 | } | |
1164 | ||
1165 | fn copy_to_folder(&self, src: &Path, dest_folder: &Path) { | |
1166 | let file_name = src.file_name().unwrap(); | |
1167 | let dest = dest_folder.join(file_name); | |
1168 | self.copy(src, &dest); | |
1169 | } | |
1170 | ||
1171 | fn install(&self, src: &Path, dstdir: &Path, perms: u32) { | |
1172 | if self.config.dry_run { return; } | |
1173 | let dst = dstdir.join(src.file_name().unwrap()); | |
1174 | t!(fs::create_dir_all(dstdir)); | |
1175 | drop(fs::remove_file(&dst)); | |
1176 | { | |
1177 | let mut s = t!(fs::File::open(&src)); | |
1178 | let mut d = t!(fs::File::create(&dst)); | |
1179 | io::copy(&mut s, &mut d).expect("failed to copy"); | |
1180 | } | |
1181 | chmod(&dst, perms); | |
1182 | } | |
1183 | ||
1184 | fn create(&self, path: &Path, s: &str) { | |
1185 | if self.config.dry_run { return; } | |
1186 | t!(fs::write(path, s)); | |
1187 | } | |
1188 | ||
1189 | fn read(&self, path: &Path) -> String { | |
1190 | if self.config.dry_run { return String::new(); } | |
1191 | t!(fs::read_to_string(path)) | |
1192 | } | |
1193 | ||
1194 | fn create_dir(&self, dir: &Path) { | |
1195 | if self.config.dry_run { return; } | |
1196 | t!(fs::create_dir_all(dir)) | |
1197 | } | |
1198 | ||
1199 | fn remove_dir(&self, dir: &Path) { | |
1200 | if self.config.dry_run { return; } | |
1201 | t!(fs::remove_dir_all(dir)) | |
1202 | } | |
1203 | ||
1204 | fn read_dir(&self, dir: &Path) -> impl Iterator<Item=fs::DirEntry> { | |
1205 | let iter = match fs::read_dir(dir) { | |
1206 | Ok(v) => v, | |
1207 | Err(_) if self.config.dry_run => return vec![].into_iter(), | |
1208 | Err(err) => panic!("could not read dir {:?}: {:?}", dir, err), | |
1209 | }; | |
1210 | iter.map(|e| t!(e)).collect::<Vec<_>>().into_iter() | |
1211 | } | |
1212 | ||
1213 | fn remove(&self, f: &Path) { | |
1214 | if self.config.dry_run { return; } | |
1215 | fs::remove_file(f).unwrap_or_else(|_| panic!("failed to remove {:?}", f)); | |
1216 | } | |
85aaf69f SL |
1217 | } |
1218 | ||
83c7162d XL |
1219 | #[cfg(unix)] |
1220 | fn chmod(path: &Path, perms: u32) { | |
1221 | use std::os::unix::fs::*; | |
1222 | t!(fs::set_permissions(path, fs::Permissions::from_mode(perms))); | |
1223 | } | |
1224 | #[cfg(windows)] | |
1225 | fn chmod(_path: &Path, _perms: u32) {} | |
1226 | ||
1227 | ||
3b2f2976 XL |
1228 | impl<'a> Compiler { |
1229 | pub fn with_stage(mut self, stage: u32) -> Compiler { | |
1230 | self.stage = stage; | |
1231 | self | |
5bcae85e SL |
1232 | } |
1233 | ||
1234 | /// Returns whether this is a snapshot compiler for `build`'s configuration | |
3b2f2976 | 1235 | pub fn is_snapshot(&self, build: &Build) -> bool { |
041b39d2 | 1236 | self.stage == 0 && self.host == build.build |
5bcae85e | 1237 | } |
32a655c1 SL |
1238 | |
1239 | /// Returns if this compiler should be treated as a final stage one in the | |
1240 | /// current build session. | |
1241 | /// This takes into account whether we're performing a full bootstrap or | |
1242 | /// not; don't directly compare the stage with `2`! | |
3b2f2976 | 1243 | pub fn is_final_stage(&self, build: &Build) -> bool { |
32a655c1 SL |
1244 | let final_stage = if build.config.full_bootstrap { 2 } else { 1 }; |
1245 | self.stage >= final_stage | |
1246 | } | |
223e47cc | 1247 | } |