lib.rs 47.5 KB
Newer Older
1
//! Implementation of rustbuild, the Rust build system.
2
//!
3 4 5
//! This module, and its descendants, are the implementation of the Rust build
//! system. Most of this build system is backed by Cargo but the outer layer
//! here serves as the ability to orchestrate calling Cargo, sequencing Cargo
6
//! builds, building artifacts like LLVM, etc. The goals of rustbuild are:
7
//!
8 9 10 11 12 13 14 15
//! * To be an easily understandable, easily extensible, and maintainable build
//!   system.
//! * Leverage standard tools in the Rust ecosystem to build the compiler, aka
//!   crates.io and Cargo.
//! * A standard interface to build across all platforms, including MSVC
//!
//! ## Architecture
//!
16 17 18 19 20 21
//! The build system defers most of the complicated logic managing invocations
//! of rustc and rustdoc to Cargo itself. However, moving through various stages
//! and copying artifacts is still necessary for it to do. Each time rustbuild
//! is invoked, it will iterate through the list of predefined steps and execute
//! each serially in turn if it matches the paths passed or is a default rule.
//! For each step rustbuild relies on the step internally being incremental and
22 23 24 25 26 27
//! parallel. Note, though, that the `-j` parameter to rustbuild gets forwarded
//! to appropriate test harnesses and such.
//!
//! Most of the "meaty" steps that matter are backed by Cargo, which does indeed
//! have its own parallelism and incremental management. Later steps, like
//! tests, aren't incremental and simply run the entire suite currently.
28 29
//! However, compiletest itself tries to avoid running tests when the artifacts
//! that are involved (mainly the compiler) haven't changed.
30
//!
31
//! When you execute `x.py build`, the steps executed are:
32 33
//!
//! * First, the python script is run. This will automatically download the
34
//!   stage0 rustc and cargo according to `src/stage0.txt`, or use the cached
35 36 37 38
//!   versions if they're available. These are then used to compile rustbuild
//!   itself (using Cargo). Finally, control is then transferred to rustbuild.
//!
//! * Rustbuild takes over, performs sanity checks, probes the environment,
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
//!   reads configuration, and starts executing steps as it reads the command
//!   line arguments (paths) or going through the default rules.
//!
//!   The build output will be something like the following:
//!
//!   Building stage0 std artifacts
//!   Copying stage0 std
//!   Building stage0 test artifacts
//!   Copying stage0 test
//!   Building stage0 compiler artifacts
//!   Copying stage0 rustc
//!   Assembling stage1 compiler
//!   Building stage1 std artifacts
//!   Copying stage1 std
//!   Building stage1 test artifacts
//!   Copying stage1 test
//!   Building stage1 compiler artifacts
//!   Copying stage1 rustc
//!   Assembling stage2 compiler
//!   Uplifting stage1 std
//!   Uplifting stage1 test
//!   Uplifting stage1 rustc
//!
//! Let's disect that a little:
//!
//! ## Building stage0 {std,test,compiler} artifacts
//!
//! These steps use the provided (downloaded, usually) compiler to compile the
//! local Rust source into libraries we can use.
//!
//! ## Copying stage0 {std,test,rustc}
//!
//! This copies the build output from Cargo into
A
Alexander Regueiro 已提交
72
//! `build/$HOST/stage0-sysroot/lib/rustlib/$ARCH/lib`. FIXME: this step's
73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88
//! documentation should be expanded -- the information already here may be
//! incorrect.
//!
//! ## Assembling stage1 compiler
//!
//! This copies the libraries we built in "building stage0 ... artifacts" into
//! the stage1 compiler's lib directory. These are the host libraries that the
//! compiler itself uses to run. These aren't actually used by artifacts the new
//! compiler generates. This step also copies the rustc and rustdoc binaries we
//! generated into build/$HOST/stage/bin.
//!
//! The stage1/bin/rustc is a fully functional compiler, but it doesn't yet have
//! any libraries to link built binaries or libraries to. The next 3 steps will
//! provide those libraries for it; they are mostly equivalent to constructing
//! the stage1/bin compiler so we don't go through them individually.
//!
89
//! ## Uplifting stage1 {std,test,rustc}
90 91 92 93 94 95 96
//!
//! This step copies the libraries from the stage1 compiler sysroot into the
//! stage2 compiler. This is done to avoid rebuilding the compiler; libraries
//! we'd build in this step should be identical (in function, if not necessarily
//! identical on disk) so there's no need to recompile the compiler again. Note
//! that if you want to, you can enable the full-bootstrap option to change this
//! behavior.
97 98 99 100 101 102 103 104
//!
//! Each step is driven by a separate Cargo project and rustbuild orchestrates
//! copying files between steps and otherwise preparing for Cargo to run.
//!
//! ## Further information
//!
//! More documentation can be found in each respective module below, and you can
//! also check out the `src/bootstrap/README.md` file for more information.
105

106
// NO-RUSTC-WRAPPER
107
#![deny(warnings, rust_2018_idioms, unused_lifetimes)]
108

109
#![feature(core_intrinsics)]
O
Oliver Schneider 已提交
110
#![feature(drain_filter)]
A
Alex Crichton 已提交
111

112
use std::cell::{RefCell, Cell};
113
use std::collections::{HashSet, HashMap};
A
Alex Crichton 已提交
114
use std::env;
115
use std::fs::{self, OpenOptions, File};
116
use std::io::{Seek, SeekFrom, Write, Read};
117
use std::path::{PathBuf, Path};
118
use std::process::{self, Command};
M
Mark Simulacrum 已提交
119
use std::slice;
120
use std::str;
121

T
Tom Tromey 已提交
122 123 124 125 126
#[cfg(unix)]
use std::os::unix::fs::symlink as symlink_file;
#[cfg(windows)]
use std::os::windows::fs::symlink_file;

127 128 129
use build_helper::{
    mtime, output, run_silent, run_suppressed, t, try_run_silent, try_run_suppressed,
};
130
use filetime::FileTime;
131

132
use crate::util::{exe, libdir, CiEnv};
133

A
Alex Crichton 已提交
134
mod cc_detect;
135 136
mod channel;
mod check;
137
mod test;
138 139
mod clean;
mod compile;
140
mod metadata;
141 142 143 144
mod config;
mod dist;
mod doc;
mod flags;
145
mod install;
146 147 148
mod native;
mod sanity;
pub mod util;
149
mod builder;
150 151
mod cache;
mod tool;
152
mod toolstate;
153 154 155 156

#[cfg(windows)]
mod job;

157
#[cfg(all(unix, not(target_os = "haiku")))]
158
mod job {
L
ljedrz 已提交
159
    pub unsafe fn setup(build: &mut crate::Build) {
160
        if build.config.low_priority {
Q
QuietMisdreavus 已提交
161
            libc::setpriority(libc::PRIO_PGRP as _, 0, 10);
162 163 164 165
        }
    }
}

166
#[cfg(any(target_os = "haiku", not(any(unix, windows))))]
167
mod job {
L
ljedrz 已提交
168
    pub unsafe fn setup(_build: &mut crate::Build) {
169
    }
170 171
}

L
ljedrz 已提交
172 173 174 175
pub use crate::config::Config;
use crate::flags::Subcommand;
use crate::cache::{Interned, INTERNER};
use crate::toolstate::ToolState;
176

J
Jorge Aparicio 已提交
177
const LLVM_TOOLS: &[&str] = &[
J
Jorge Aparicio 已提交
178 179 180 181
    "llvm-nm", // used to inspect binaries; it shows symbol names, their sizes and visibility
    "llvm-objcopy", // used to transform ELFs into binary format which flashing tools consume
    "llvm-objdump", // used to disassemble programs
    "llvm-profdata", // used to inspect and merge files generated by profiles
J
sort  
Jorge Aparicio 已提交
182
    "llvm-readobj", // used to get information from ELFs/objects that the other tools don't provide
183 184
    "llvm-size", // used to prints the size of the linker sections of a program
    "llvm-strip", // used to discard symbols from binary files to reduce their size
185
    "llvm-ar" // used for creating and modifying archive files
J
Jorge Aparicio 已提交
186
];
187

188 189 190 191 192
/// A structure representing a Rust compiler.
///
/// Each compiler has a `stage` that it is associated with and a `host` that
/// corresponds to the platform the compiler runs on. This structure is used as
/// a parameter to many methods below.
M
Mark Simulacrum 已提交
193
#[derive(Eq, PartialOrd, Ord, PartialEq, Clone, Copy, Hash, Debug)]
194
pub struct Compiler {
195
    stage: u32,
196
    host: Interned<String>,
197 198
}

K
kennytm 已提交
199
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
K
kennytm 已提交
200
pub enum DocTests {
A
Alexander Regueiro 已提交
201
    /// Run normal tests and doc tests (default).
K
kennytm 已提交
202
    Yes,
A
Alexander Regueiro 已提交
203
    /// Do not run any doc tests.
K
kennytm 已提交
204
    No,
A
Alexander Regueiro 已提交
205
    /// Only run doc tests.
K
kennytm 已提交
206 207 208
    Only,
}

209 210 211 212 213
pub enum GitRepo {
    Rustc,
    Llvm,
}

214 215 216 217 218 219 220 221 222 223 224
/// Global configuration for the build system.
///
/// This structure transitively contains all configuration for the build system.
/// All filesystem-encoded configuration is in `config`, all flags are in
/// `flags`, and then parsed or probed information is listed in the keys below.
///
/// This structure is a parameter of almost all methods in the build system,
/// although most functions are implemented as free functions rather than
/// methods specifically on this structure itself (to make it easier to
/// organize).
pub struct Build {
A
Alexander Regueiro 已提交
225
    /// User-specified configuration from `config.toml`.
226 227
    config: Config,

A
Alexander Regueiro 已提交
228
    // Properties derived from the above configuration
229 230
    src: PathBuf,
    out: PathBuf,
231 232
    rust_info: channel::GitInfo,
    cargo_info: channel::GitInfo,
A
Alex Crichton 已提交
233
    rls_info: channel::GitInfo,
234
    clippy_info: channel::GitInfo,
235
    miri_info: channel::GitInfo,
N
Nick Cameron 已提交
236
    rustfmt_info: channel::GitInfo,
237 238
    in_tree_llvm_info: channel::GitInfo,
    emscripten_llvm_info: channel::GitInfo,
239
    local_rebuild: bool,
240
    fail_fast: bool,
K
kennytm 已提交
241
    doc_tests: DocTests,
M
Mark Simulacrum 已提交
242
    verbosity: usize,
243

A
Alexander Regueiro 已提交
244
    // Targets for which to build
245 246 247
    build: Interned<String>,
    hosts: Vec<Interned<String>>,
    targets: Vec<Interned<String>>,
248

A
Alexander Regueiro 已提交
249
    // Stage 0 (downloaded) compiler and cargo or their local rust equivalents
250 251 252
    initial_rustc: PathBuf,
    initial_cargo: PathBuf,

253
    // Runtime state filled in later on
254 255
    // C/C++ compilers and archiver for all targets
    cc: HashMap<Interned<String>, cc::Tool>,
A
Alex Crichton 已提交
256
    cxx: HashMap<Interned<String>, cc::Tool>,
257
    ar: HashMap<Interned<String>, PathBuf>,
258
    ranlib: HashMap<Interned<String>, PathBuf>,
A
Alexander Regueiro 已提交
259
    // Miscellaneous
260
    crates: HashMap<Interned<String>, Crate>,
261
    is_sudo: bool,
262
    ci_env: CiEnv,
263
    delayed_failures: RefCell<Vec<String>>,
264
    prerelease_version: Cell<Option<u32>>,
265 266 267 268
    tool_artifacts: RefCell<HashMap<
        Interned<String>,
        HashMap<String, (&'static str, PathBuf, Vec<String>)>
    >>,
269 270 271 272
}

#[derive(Debug)]
struct Crate {
273
    name: Interned<String>,
274 275
    deps: HashSet<Interned<String>>,
    id: String,
276
    path: PathBuf,
277 278
}

279 280 281 282 283 284 285 286 287 288 289 290
impl Crate {
    fn is_local(&self, build: &Build) -> bool {
        self.path.starts_with(&build.config.src) &&
        !self.path.to_string_lossy().ends_with("_shim")
    }

    fn local_path(&self, build: &Build) -> PathBuf {
        assert!(self.is_local(build));
        self.path.strip_prefix(&build.config.src).unwrap().into()
    }
}

291 292 293 294
/// The various "modes" of invoking Cargo.
///
/// These entries currently correspond to the various output directories of the
/// build system, with each mod generating output in a different directory.
K
kennytm 已提交
295
#[derive(Debug, Hash, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
296
pub enum Mode {
M
Mark Simulacrum 已提交
297
    /// Build the standard library, placing output in the "stageN-std" directory.
C
Collins Abitekaniza 已提交
298
    Std,
299

M
Mark Simulacrum 已提交
300
    /// Build libtest, placing output in the "stageN-test" directory.
C
Collins Abitekaniza 已提交
301
    Test,
302

303
    /// Build librustc, and compiler libraries, placing output in the "stageN-rustc" directory.
C
Collins Abitekaniza 已提交
304
    Rustc,
305 306

    /// Build codegen libraries, placing output in the "stageN-codegen" directory
C
Collins Abitekaniza 已提交
307
    Codegen,
308

309 310 311 312 313 314 315 316 317
    /// Build some tools, placing output in the "stageN-tools" directory. The
    /// "other" here is for miscellaneous sets of tools that are built using the
    /// bootstrap compiler in its entirety (target libraries and all).
    /// Typically these tools compile with stable Rust.
    ToolBootstrap,

    /// Compile a tool which uses all libraries we compile (up to rustc).
    /// Doesn't use the stage0 compiler libraries like "other", and includes
    /// tools like rustdoc, cargo, rls, etc.
C
Collins Abitekaniza 已提交
318
    ToolTest,
A
Alex Crichton 已提交
319
    ToolStd,
C
Collins Abitekaniza 已提交
320
    ToolRustc,
321 322
}

C
Collins Abitekaniza 已提交
323 324 325
impl Mode {
    pub fn is_tool(&self) -> bool {
        match self {
A
Alex Crichton 已提交
326
            Mode::ToolBootstrap | Mode::ToolRustc | Mode::ToolStd => true,
C
Collins Abitekaniza 已提交
327 328 329 330 331
            _ => false
        }
    }
}

332 333 334 335 336
impl Build {
    /// Creates a new set of build configuration from the `flags` on the command
    /// line and the filesystem `config`.
    ///
    /// By default all build output will be placed in the current directory.
M
Mark Simulacrum 已提交
337 338
    pub fn new(config: Config) -> Build {
        let src = config.src.clone();
339
        let out = config.out.clone();
340

341 342 343 344 345 346 347 348 349
        let is_sudo = match env::var_os("SUDO_USER") {
            Some(sudo_user) => {
                match env::var_os("USER") {
                    Some(user) => user != sudo_user,
                    None => false,
                }
            }
            None => false,
        };
J
Josh Stone 已提交
350 351 352 353 354 355 356 357 358 359 360 361

        let ignore_git = config.ignore_git;
        let rust_info = channel::GitInfo::new(ignore_git, &src);
        let cargo_info = channel::GitInfo::new(ignore_git, &src.join("src/tools/cargo"));
        let rls_info = channel::GitInfo::new(ignore_git, &src.join("src/tools/rls"));
        let clippy_info = channel::GitInfo::new(ignore_git, &src.join("src/tools/clippy"));
        let miri_info = channel::GitInfo::new(ignore_git, &src.join("src/tools/miri"));
        let rustfmt_info = channel::GitInfo::new(ignore_git, &src.join("src/tools/rustfmt"));

        // we always try to use git for LLVM builds
        let in_tree_llvm_info = channel::GitInfo::new(false, &src.join("src/llvm-project"));
        let emscripten_llvm_info = channel::GitInfo::new(false, &src.join("src/llvm-emscripten"));
362

363
        let mut build = Build {
364 365 366
            initial_rustc: config.initial_rustc.clone(),
            initial_cargo: config.initial_cargo.clone(),
            local_rebuild: config.local_rebuild,
M
Mark Simulacrum 已提交
367
            fail_fast: config.cmd.fail_fast(),
368
            doc_tests: config.cmd.doc_tests(),
M
Mark Simulacrum 已提交
369
            verbosity: config.verbose,
370

M
Mark Simulacrum 已提交
371 372 373
            build: config.build,
            hosts: config.hosts.clone(),
            targets: config.targets.clone(),
374

375 376 377
            config,
            src,
            out,
378

379 380 381
            rust_info,
            cargo_info,
            rls_info,
382
            clippy_info,
383
            miri_info,
N
Nick Cameron 已提交
384
            rustfmt_info,
385 386
            in_tree_llvm_info,
            emscripten_llvm_info,
387 388
            cc: HashMap::new(),
            cxx: HashMap::new(),
389
            ar: HashMap::new(),
390
            ranlib: HashMap::new(),
391
            crates: HashMap::new(),
392
            is_sudo,
393
            ci_env: CiEnv::current(),
394
            delayed_failures: RefCell::new(Vec::new()),
395
            prerelease_version: Cell::new(None),
396
            tool_artifacts: Default::default(),
397 398 399 400 401 402
        };

        build.verbose("finding compilers");
        cc_detect::find(&mut build);
        build.verbose("running sanity check");
        sanity::check(&mut build);
M
Mark Simulacrum 已提交
403 404 405 406 407 408 409

        // If local-rust is the same major.minor as the current version, then force a
        // local-rebuild
        let local_version_verbose = output(
            Command::new(&build.initial_rustc).arg("--version").arg("--verbose"));
        let local_release = local_version_verbose
            .lines().filter(|x| x.starts_with("release:"))
410
            .next().unwrap().trim_start_matches("release:").trim();
M
Mark Simulacrum 已提交
411 412 413 414
        let my_version = channel::CFG_RELEASE_NUM;
        if local_release.split('.').take(2).eq(my_version.split('.').take(2)) {
            build.verbose(&format!("auto-detected local-rebuild {}", local_release));
            build.local_rebuild = true;
415
        }
M
Mark Simulacrum 已提交
416

417 418 419 420
        build.verbose("learning about cargo");
        metadata::build(&mut build);

        build
421 422
    }

M
Mark Simulacrum 已提交
423 424 425 426 427 428
    pub fn build_triple(&self) -> &[Interned<String>] {
        unsafe {
            slice::from_raw_parts(&self.build, 1)
        }
    }

429 430 431
    /// Executes the entire build, as configured by the flags and configuration.
    pub fn build(&mut self) {
        unsafe {
432
            job::setup(self);
433 434
        }

T
Tommy Ip 已提交
435 436
        if let Subcommand::Clean { all } = self.config.cmd {
            return clean::clean(self, all);
437 438
        }

439 440 441 442 443 444
        {
            let builder = builder::Builder::new(&self);
            if let Some(path) = builder.paths.get(0) {
                if path == Path::new("nonexistent/path/to/trigger/cargo/metadata") {
                    return;
                }
445 446
            }
        }
447 448

        if !self.config.dry_run {
449
            {
450 451
                self.config.dry_run = true;
                let builder = builder::Builder::new(&self);
452 453
                builder.execute_cli();
            }
454 455
            self.config.dry_run = false;
            let builder = builder::Builder::new(&self);
456
            builder.execute_cli();
457 458 459 460
        } else {
            let builder = builder::Builder::new(&self);
            let _ = builder.execute_cli();
        }
461 462 463 464 465 466 467 468 469 470

        // Check for postponed failures from `test --no-fail-fast`.
        let failures = self.delayed_failures.borrow();
        if failures.len() > 0 {
            println!("\n{} command(s) did not execute successfully:\n", failures.len());
            for failure in failures.iter() {
                println!("  - {}\n", failure);
            }
            process::exit(1);
        }
471 472 473 474 475
    }

    /// Clear out `dir` if `input` is newer.
    ///
    /// After this executes, it will also ensure that `dir` exists.
476
    fn clear_if_dirty(&self, dir: &Path, input: &Path) -> bool {
477
        let stamp = dir.join(".stamp");
478
        let mut cleared = false;
479 480 481
        if mtime(&stamp) < mtime(input) {
            self.verbose(&format!("Dirty - {}", dir.display()));
            let _ = fs::remove_dir_all(dir);
482
            cleared = true;
483
        } else if stamp.exists() {
484
            return cleared;
485 486 487
        }
        t!(fs::create_dir_all(dir));
        t!(File::create(stamp));
488
        cleared
489 490
    }

A
Alexander Regueiro 已提交
491
    /// Gets the space-separated set of activated features for the standard
492 493
    /// library.
    fn std_features(&self) -> String {
494
        let mut features = "panic-unwind".to_string();
J
Jorge Aparicio 已提交
495

496 497 498
        if self.config.llvm_libunwind {
            features.push_str(" llvm-libunwind");
        }
499 500 501
        if self.config.backtrace {
            features.push_str(" backtrace");
        }
502 503 504
        if self.config.profiler {
            features.push_str(" profiler");
        }
505 506 507
        if self.config.wasm_syscall {
            features.push_str(" wasm_syscall");
        }
M
Mark Simulacrum 已提交
508
        features
509 510
    }

A
Alexander Regueiro 已提交
511
    /// Gets the space-separated set of activated features for the compiler.
512
    fn rustc_features(&self) -> String {
513 514 515 516 517
        let mut features = String::new();
        if self.config.jemalloc {
            features.push_str("jemalloc");
        }
        features
518 519 520 521 522 523 524 525
    }

    /// Component directory that Cargo will produce output into (e.g.
    /// release/debug)
    fn cargo_dir(&self) -> &'static str {
        if self.config.rust_optimize {"release"} else {"debug"}
    }

526 527 528 529 530 531
    fn tools_dir(&self, compiler: Compiler) -> PathBuf {
        let out = self.out.join(&*compiler.host).join(format!("stage{}-tools-bin", compiler.stage));
        t!(fs::create_dir_all(&out));
        out
    }

532 533 534 535
    /// Returns the root directory for all output generated in a particular
    /// stage when running with a particular host compiler.
    ///
    /// The mode indicates what the root directory is for.
536
    fn stage_out(&self, compiler: Compiler, mode: Mode) -> PathBuf {
537
        let suffix = match mode {
C
Collins Abitekaniza 已提交
538 539 540
            Mode::Std => "-std",
            Mode::Test => "-test",
            Mode::Rustc => "-rustc",
541
            Mode::Codegen => "-codegen",
542
            Mode::ToolBootstrap => "-bootstrap-tools",
A
Alex Crichton 已提交
543
            Mode::ToolStd => "-tools",
C
Collins Abitekaniza 已提交
544
            Mode::ToolTest => "-tools",
545
            Mode::ToolRustc => "-tools",
546
        };
547
        self.out.join(&*compiler.host)
548 549 550 551
                .join(format!("stage{}{}", compiler.stage, suffix))
    }

    /// Returns the root output directory for all Cargo output in a given stage,
F
Fourchaux 已提交
552
    /// running a particular compiler, whether or not we're building the
553 554
    /// standard library, and targeting the specified architecture.
    fn cargo_out(&self,
555
                 compiler: Compiler,
556
                 mode: Mode,
557 558
                 target: Interned<String>) -> PathBuf {
        self.stage_out(compiler, mode).join(&*target).join(self.cargo_dir())
559 560 561 562 563 564
    }

    /// Root output directory for LLVM compiled for `target`
    ///
    /// Note that if LLVM is configured externally then the directory returned
    /// will likely be empty.
565 566
    fn llvm_out(&self, target: Interned<String>) -> PathBuf {
        self.out.join(&*target).join("llvm")
567 568
    }

569 570 571 572
    fn emscripten_llvm_out(&self, target: Interned<String>) -> PathBuf {
        self.out.join(&*target).join("llvm-emscripten")
    }

573 574 575 576
    fn lld_out(&self, target: Interned<String>) -> PathBuf {
        self.out.join(&*target).join("lld")
    }

577
    /// Output directory for all documentation for a target
578 579
    fn doc_out(&self, target: Interned<String>) -> PathBuf {
        self.out.join(&*target).join("doc")
580 581
    }

582 583 584 585 586
    /// Output directory for all documentation for a target
    fn compiler_doc_out(&self, target: Interned<String>) -> PathBuf {
        self.out.join(&*target).join("compiler-doc")
    }

587
    /// Output directory for some generated md crate documentation for a target (temporary)
588 589
    fn md_doc_out(&self, target: Interned<String>) -> Interned<PathBuf> {
        INTERNER.intern_path(self.out.join(&*target).join("md-doc"))
590 591
    }

592 593 594
    /// Output directory for all crate documentation for a target (temporary)
    ///
    /// The artifacts here are then copied into `doc_out` above.
595 596
    fn crate_doc_out(&self, target: Interned<String>) -> PathBuf {
        self.out.join(&*target).join("crate-docs")
597 598
    }

A
Alexander Regueiro 已提交
599
    /// Returns `true` if no custom `llvm-config` is set for the specified target.
600 601
    ///
    /// If no custom `llvm-config` was specified then Rust's llvm will be used.
602 603
    fn is_rust_llvm(&self, target: Interned<String>) -> bool {
        match self.config.target_config.get(&target) {
604 605 606 607 608
            Some(ref c) => c.llvm_config.is_none(),
            None => true
        }
    }

609
    /// Returns the path to `FileCheck` binary for the specified target
610 611
    fn llvm_filecheck(&self, target: Interned<String>) -> PathBuf {
        let target_config = self.config.target_config.get(&target);
612 613 614
        if let Some(s) = target_config.and_then(|c| c.llvm_filecheck.as_ref()) {
            s.to_path_buf()
        } else if let Some(s) = target_config.and_then(|c| c.llvm_config.as_ref()) {
S
Seo Sanghyeon 已提交
615
            let llvm_bindir = output(Command::new(s).arg("--bindir"));
616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633
            let filecheck = Path::new(llvm_bindir.trim()).join(exe("FileCheck", &*target));
            if filecheck.exists() {
                filecheck
            } else {
                // On Fedora the system LLVM installs FileCheck in the
                // llvm subdirectory of the libdir.
                let llvm_libdir = output(Command::new(s).arg("--libdir"));
                let lib_filecheck = Path::new(llvm_libdir.trim())
                    .join("llvm").join(exe("FileCheck", &*target));
                if lib_filecheck.exists() {
                    lib_filecheck
                } else {
                    // Return the most normal file name, even though
                    // it doesn't exist, so that any error message
                    // refers to that.
                    filecheck
                }
            }
634
        } else {
635
            let base = self.llvm_out(self.config.build).join("build");
J
John Kåre Alsaker 已提交
636 637 638 639 640 641 642 643 644 645
            let base = if !self.config.ninja && self.config.build.contains("msvc") {
                if self.config.llvm_optimize {
                    if self.config.llvm_release_debuginfo {
                        base.join("RelWithDebInfo")
                    } else {
                        base.join("Release")
                    }
                } else {
                    base.join("Debug")
                }
646
            } else {
J
John Kåre Alsaker 已提交
647 648 649
                base
            };
            base.join("bin").join(exe("FileCheck", &*target))
650 651 652
        }
    }

653
    /// Directory for libraries built from C/C++ code and shared between stages.
654 655
    fn native_dir(&self, target: Interned<String>) -> PathBuf {
        self.out.join(&*target).join("native")
656 657
    }

658 659
    /// Root output directory for rust_test_helpers library compiled for
    /// `target`
660
    fn test_helpers_out(&self, target: Interned<String>) -> PathBuf {
661
        self.native_dir(target).join("rust-test-helpers")
662 663
    }

664 665 666 667 668 669 670
    /// Adds the `RUST_TEST_THREADS` env var if necessary
    fn add_rust_test_threads(&self, cmd: &mut Command) {
        if env::var_os("RUST_TEST_THREADS").is_none() {
            cmd.env("RUST_TEST_THREADS", self.jobs().to_string());
        }
    }

671 672
    /// Returns the libdir of the snapshot compiler.
    fn rustc_snapshot_libdir(&self) -> PathBuf {
673 674 675 676 677
        self.rustc_snapshot_sysroot().join(libdir(&self.config.build))
    }

    /// Returns the sysroot of the snapshot compiler.
    fn rustc_snapshot_sysroot(&self) -> &Path {
678
        self.initial_rustc.parent().unwrap().parent().unwrap()
679 680 681 682
    }

    /// Runs a command, printing out nice contextual information if it fails.
    fn run(&self, cmd: &mut Command) {
683
        if self.config.dry_run { return; }
684 685
        self.verbose(&format!("running: {:?}", cmd));
        run_silent(cmd)
686 687
    }

688 689
    /// Runs a command, printing out nice contextual information if it fails.
    fn run_quiet(&self, cmd: &mut Command) {
690
        if self.config.dry_run { return; }
691
        self.verbose(&format!("running: {:?}", cmd));
692
        run_suppressed(cmd)
693 694
    }

695 696 697 698
    /// Runs a command, printing out nice contextual information if it fails.
    /// Exits if the command failed to execute at all, otherwise returns its
    /// `status.success()`.
    fn try_run(&self, cmd: &mut Command) -> bool {
699
        if self.config.dry_run { return true; }
700
        self.verbose(&format!("running: {:?}", cmd));
701
        try_run_silent(cmd)
702 703 704 705 706 707
    }

    /// Runs a command, printing out nice contextual information if it fails.
    /// Exits if the command failed to execute at all, otherwise returns its
    /// `status.success()`.
    fn try_run_quiet(&self, cmd: &mut Command) -> bool {
708
        if self.config.dry_run { return true; }
709
        self.verbose(&format!("running: {:?}", cmd));
710
        try_run_suppressed(cmd)
711 712
    }

M
Mark Simulacrum 已提交
713 714 715 716
    pub fn is_verbose(&self) -> bool {
        self.verbosity > 0
    }

717 718
    /// Prints a message if this build is configured in verbose mode.
    fn verbose(&self, msg: &str) {
M
Mark Simulacrum 已提交
719
        if self.is_verbose() {
720 721 722 723
            println!("{}", msg);
        }
    }

724 725 726 727 728 729 730 731 732 733 734
    pub fn is_verbose_than(&self, level: usize) -> bool {
        self.verbosity > level
    }

    /// Prints a message if this build is configured in more verbose mode than `level`.
    fn verbose_than(&self, level: usize, msg: &str) {
        if self.is_verbose_than(level) {
            println!("{}", msg);
        }
    }

735 736 737 738 739
    fn info(&self, msg: &str) {
        if self.config.dry_run { return; }
        println!("{}", msg);
    }

740 741 742
    /// Returns the number of parallel jobs that have been configured for this
    /// build.
    fn jobs(&self) -> u32 {
M
Mark Simulacrum 已提交
743
        self.config.jobs.unwrap_or_else(|| num_cpus::get() as u32)
744 745
    }

746 747 748 749 750 751 752
    fn debuginfo_map(&self, which: GitRepo) -> Option<String> {
        if !self.config.rust_remap_debuginfo {
            return None
        }

        let path = match which {
            GitRepo::Rustc => {
753
                let sha = self.rust_sha().unwrap_or(channel::CFG_RELEASE_NUM);
754 755
                format!("/rustc/{}", sha)
            }
756
            GitRepo::Llvm => String::from("/rustc/llvm"),
757 758 759 760
        };
        Some(format!("{}={}", self.src.display(), path))
    }

761
    /// Returns the path to the C compiler for the target specified.
762
    fn cc(&self, target: Interned<String>) -> &Path {
763
        self.cc[&target].path()
764 765 766 767
    }

    /// Returns a list of flags to pass to the C compiler for the target
    /// specified.
768
    fn cflags(&self, target: Interned<String>, which: GitRepo) -> Vec<String> {
769
        // Filter out -O and /O (the optimization flags) that we picked up from
A
Alex Crichton 已提交
770
        // cc-rs because the build scripts will determine that for themselves.
771
        let mut base = self.cc[&target].args().iter()
772 773
                           .map(|s| s.to_string_lossy().into_owned())
                           .filter(|s| !s.starts_with("-O") && !s.starts_with("/O"))
774
                           .collect::<Vec<String>>();
775

776
        // If we're compiling on macOS then we add a few unconditional flags
777 778
        // indicating that we want libc++ (more filled out than libstdc++) and
        // we want to compile for 10.7. This way we can ensure that
779
        // LLVM/etc are all properly compiled.
780 781 782
        if target.contains("apple-darwin") {
            base.push("-stdlib=libc++".into());
        }
783 784 785 786

        // Work around an apparently bad MinGW / GCC optimization,
        // See: http://lists.llvm.org/pipermail/cfe-dev/2016-December/051980.html
        // See: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=78936
787
        if &*target == "i686-pc-windows-gnu" {
788 789
            base.push("-fno-omit-frame-pointer".into());
        }
790 791 792 793

        if let Some(map) = self.debuginfo_map(which) {
        let cc = self.cc(target);
            if cc.ends_with("clang") || cc.ends_with("gcc") {
794
                base.push(format!("-fdebug-prefix-map={}", map));
795 796
            } else if cc.ends_with("clang-cl.exe") {
                base.push("-Xclang".into());
797
                base.push(format!("-fdebug-prefix-map={}", map));
798 799
            }
        }
M
Mark Simulacrum 已提交
800
        base
801 802 803
    }

    /// Returns the path to the `ar` archive utility for the target specified.
804
    fn ar(&self, target: Interned<String>) -> Option<&Path> {
805 806 807
        self.ar.get(&target).map(|p| &**p)
    }

808 809 810 811 812
    /// Returns the path to the `ranlib` utility for the target specified.
    fn ranlib(&self, target: Interned<String>) -> Option<&Path> {
        self.ranlib.get(&target).map(|p| &**p)
    }

813
    /// Returns the path to the C++ compiler for the target specified.
814 815
    fn cxx(&self, target: Interned<String>) -> Result<&Path, String> {
        match self.cxx.get(&target) {
816 817 818 819
            Some(p) => Ok(p.path()),
            None => Err(format!(
                    "target `{}` is not configured as a host, only as a target",
                    target))
820
        }
821 822
    }

823
    /// Returns the path to the linker for the given target if it needs to be overridden.
824
    fn linker(&self, target: Interned<String>) -> Option<&Path> {
825 826 827
        if let Some(linker) = self.config.target_config.get(&target)
                                                       .and_then(|c| c.linker.as_ref()) {
            Some(linker)
828
        } else if target != self.config.build &&
829 830
                  !target.contains("msvc") &&
                  !target.contains("emscripten") &&
P
Petr Hosek 已提交
831
                  !target.contains("wasm32") &&
832
                  !target.contains("nvptx") &&
P
Petr Hosek 已提交
833
                  !target.contains("fuchsia") {
834 835 836
            Some(self.cc(target))
        } else {
            None
837
        }
A
Alex Crichton 已提交
838
    }
839

840 841
    /// Returns if this target should statically link the C runtime, if specified
    fn crt_static(&self, target: Interned<String>) -> Option<bool> {
842 843 844 845 846 847
        if target.contains("pc-windows-msvc") {
            Some(true)
        } else {
            self.config.target_config.get(&target)
                .and_then(|t| t.crt_static)
        }
848 849
    }

850
    /// Returns the "musl root" for this `target`, if defined
851 852
    fn musl_root(&self, target: Interned<String>) -> Option<&Path> {
        self.config.target_config.get(&target)
853
            .and_then(|t| t.musl_root.as_ref())
854 855 856
            .or(self.config.musl_root.as_ref())
            .map(|p| &**p)
    }
857

858 859 860 861 862 863 864
    /// Returns the sysroot for the wasi target, if defined
    fn wasi_root(&self, target: Interned<String>) -> Option<&Path> {
        self.config.target_config.get(&target)
            .and_then(|t| t.wasi_root.as_ref())
            .map(|p| &**p)
    }

A
Alexander Regueiro 已提交
865
    /// Returns `true` if this is a no-std `target`, if defined
866 867 868 869 870
    fn no_std(&self, target: Interned<String>) -> Option<bool> {
        self.config.target_config.get(&target)
            .map(|t| t.no_std)
    }

A
Alexander Regueiro 已提交
871
    /// Returns `true` if the target will be tested using the `remote-test-client`
872
    /// and `remote-test-server` binaries.
873
    fn remote_tested(&self, target: Interned<String>) -> bool {
874 875
        self.qemu_rootfs(target).is_some() || target.contains("android") ||
        env::var_os("TEST_DEVICE_ADDR").is_some()
876 877
    }

878 879 880 881 882
    /// Returns the root of the "rootfs" image that this target will be using,
    /// if one was configured.
    ///
    /// If `Some` is returned then that means that tests for this target are
    /// emulated with QEMU and binaries will need to be shipped to the emulator.
883 884
    fn qemu_rootfs(&self, target: Interned<String>) -> Option<&Path> {
        self.config.target_config.get(&target)
885 886 887 888
            .and_then(|t| t.qemu_rootfs.as_ref())
            .map(|p| &**p)
    }

889 890 891 892
    /// Path to the python interpreter to use
    fn python(&self) -> &Path {
        self.config.python.as_ref().unwrap()
    }
893

894 895 896 897 898
    /// Temporary directory that extended error information is emitted to.
    fn extended_error_dir(&self) -> PathBuf {
        self.out.join("tmp/extended-error-metadata")
    }

899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916
    /// Tests whether the `compiler` compiling for `target` should be forced to
    /// use a stage1 compiler instead.
    ///
    /// Currently, by default, the build system does not perform a "full
    /// bootstrap" by default where we compile the compiler three times.
    /// Instead, we compile the compiler two times. The final stage (stage2)
    /// just copies the libraries from the previous stage, which is what this
    /// method detects.
    ///
    /// Here we return `true` if:
    ///
    /// * The build isn't performing a full bootstrap
    /// * The `compiler` is in the final stage, 2
    /// * We're not cross-compiling, so the artifacts are already available in
    ///   stage1
    ///
    /// When all of these conditions are met the build will lift artifacts from
    /// the previous stage forward.
917
    fn force_use_stage1(&self, compiler: Compiler, target: Interned<String>) -> bool {
918 919
        !self.config.full_bootstrap &&
            compiler.stage >= 2 &&
920
            (self.hosts.iter().any(|h| *h == target) || target == self.build)
921
    }
922 923 924 925 926 927 928 929 930

    /// Given `num` in the form "a.b.c" return a "release string" which
    /// describes the release version number.
    ///
    /// For example on nightly this returns "a.b.c-nightly", on beta it returns
    /// "a.b.c-beta.1" and on stable it just returns "a.b.c".
    fn release(&self, num: &str) -> String {
        match &self.config.channel[..] {
            "stable" => num.to_string(),
931 932 933 934 935
            "beta" => if self.rust_info.is_git() {
                format!("{}-beta.{}", num, self.beta_prerelease_version())
            } else {
                format!("{}-beta", num)
            },
936 937 938 939 940
            "nightly" => format!("{}-nightly", num),
            _ => format!("{}-dev", num),
        }
    }

941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987
    fn beta_prerelease_version(&self) -> u32 {
        if let Some(s) = self.prerelease_version.get() {
            return s
        }

        let beta = output(
            Command::new("git")
                .arg("ls-remote")
                .arg("origin")
                .arg("beta")
                .current_dir(&self.src)
        );
        let beta = beta.trim().split_whitespace().next().unwrap();
        let master = output(
            Command::new("git")
                .arg("ls-remote")
                .arg("origin")
                .arg("master")
                .current_dir(&self.src)
        );
        let master = master.trim().split_whitespace().next().unwrap();

        // Figure out where the current beta branch started.
        let base = output(
            Command::new("git")
                .arg("merge-base")
                .arg(beta)
                .arg(master)
                .current_dir(&self.src),
        );
        let base = base.trim();

        // Next figure out how many merge commits happened since we branched off
        // beta. That's our beta number!
        let count = output(
            Command::new("git")
                .arg("rev-list")
                .arg("--count")
                .arg("--merges")
                .arg(format!("{}...HEAD", base))
                .current_dir(&self.src),
        );
        let n = count.trim().parse().unwrap();
        self.prerelease_version.set(Some(n));
        n
    }

988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012
    /// Returns the value of `release` above for Rust itself.
    fn rust_release(&self) -> String {
        self.release(channel::CFG_RELEASE_NUM)
    }

    /// Returns the "package version" for a component given the `num` release
    /// number.
    ///
    /// The package version is typically what shows up in the names of tarballs.
    /// For channels like beta/nightly it's just the channel name, otherwise
    /// it's the `num` provided.
    fn package_vers(&self, num: &str) -> String {
        match &self.config.channel[..] {
            "stable" => num.to_string(),
            "beta" => "beta".to_string(),
            "nightly" => "nightly".to_string(),
            _ => format!("{}-dev", num),
        }
    }

    /// Returns the value of `package_vers` above for Rust itself.
    fn rust_package_vers(&self) -> String {
        self.package_vers(channel::CFG_RELEASE_NUM)
    }

1013 1014
    /// Returns the value of `package_vers` above for Cargo
    fn cargo_package_vers(&self) -> String {
N
Nick Cameron 已提交
1015
        self.package_vers(&self.release_num("cargo"))
1016 1017
    }

1018 1019 1020 1021 1022
    /// Returns the value of `package_vers` above for rls
    fn rls_package_vers(&self) -> String {
        self.package_vers(&self.release_num("rls"))
    }

1023 1024 1025 1026 1027
    /// Returns the value of `package_vers` above for clippy
    fn clippy_package_vers(&self) -> String {
        self.package_vers(&self.release_num("clippy"))
    }

O
fixup  
Oliver Scherer 已提交
1028 1029 1030 1031 1032
    /// Returns the value of `package_vers` above for miri
    fn miri_package_vers(&self) -> String {
        self.package_vers(&self.release_num("miri"))
    }

N
Nick Cameron 已提交
1033 1034 1035 1036 1037
    /// Returns the value of `package_vers` above for rustfmt
    fn rustfmt_package_vers(&self) -> String {
        self.package_vers(&self.release_num("rustfmt"))
    }

1038
    fn llvm_tools_package_vers(&self) -> String {
1039
        self.package_vers(channel::CFG_RELEASE_NUM)
1040
    }
1041

1042
    fn llvm_tools_vers(&self) -> String {
1043
        self.rust_version()
1044 1045
    }

T
Tom Tromey 已提交
1046
    fn lldb_package_vers(&self) -> String {
1047
        self.package_vers(channel::CFG_RELEASE_NUM)
T
Tom Tromey 已提交
1048 1049 1050 1051 1052 1053
    }

    fn lldb_vers(&self) -> String {
        self.rust_version()
    }

1054 1055 1056 1057
    fn llvm_link_tools_dynamically(&self, target: Interned<String>) -> bool {
        (target.contains("linux-gnu") || target.contains("apple-darwin"))
    }

1058 1059 1060 1061 1062 1063 1064 1065 1066
    /// Returns the `version` string associated with this compiler for Rust
    /// itself.
    ///
    /// Note that this is a descriptive string which includes the commit date,
    /// sha, version, etc.
    fn rust_version(&self) -> String {
        self.rust_info.version(self, channel::CFG_RELEASE_NUM)
    }

A
Alexander Regueiro 已提交
1067
    /// Returns the full commit hash.
1068 1069 1070 1071
    fn rust_sha(&self) -> Option<&str> {
        self.rust_info.sha()
    }

N
Nick Cameron 已提交
1072 1073
    /// Returns the `a.b.c` version that the given package is at.
    fn release_num(&self, package: &str) -> String {
A
Alex Crichton 已提交
1074
        let toml_file_name = self.src.join(&format!("src/tools/{}/Cargo.toml", package));
1075
        let toml = t!(fs::read_to_string(&toml_file_name));
1076 1077 1078 1079 1080 1081 1082 1083
        for line in toml.lines() {
            let prefix = "version = \"";
            let suffix = "\"";
            if line.starts_with(prefix) && line.ends_with(suffix) {
                return line[prefix.len()..line.len() - suffix.len()].to_string()
            }
        }

N
Nick Cameron 已提交
1084
        panic!("failed to find version in {}'s Cargo.toml", package)
1085 1086
    }

A
Alexander Regueiro 已提交
1087
    /// Returns `true` if unstable features should be enabled for the compiler
1088 1089 1090 1091 1092 1093 1094
    /// we're building.
    fn unstable_features(&self) -> bool {
        match &self.config.channel[..] {
            "stable" | "beta" => false,
            "nightly" | _ => true,
        }
    }
1095

1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117
    /// Updates the actual toolstate of a tool.
    ///
    /// The toolstates are saved to the file specified by the key
    /// `rust.save-toolstates` in `config.toml`. If unspecified, nothing will be
    /// done. The file is updated immediately after this function completes.
    pub fn save_toolstate(&self, tool: &str, state: ToolState) {
        if let Some(ref path) = self.config.save_toolstates {
            let mut file = t!(fs::OpenOptions::new()
                .create(true)
                .read(true)
                .write(true)
                .open(path));

            let mut current_toolstates: HashMap<Box<str>, ToolState> =
                serde_json::from_reader(&mut file).unwrap_or_default();
            current_toolstates.insert(tool.into(), state);
            t!(file.seek(SeekFrom::Start(0)));
            t!(file.set_len(0));
            t!(serde_json::to_writer(file, &current_toolstates));
        }
    }

1118
    fn in_tree_crates(&self, root: &str) -> Vec<&Crate> {
1119
        let mut ret = Vec::new();
1120
        let mut list = vec![INTERNER.intern_str(root)];
1121 1122
        let mut visited = HashSet::new();
        while let Some(krate) = list.pop() {
1123
            let krate = &self.crates[&krate];
1124 1125
            if krate.is_local(self) {
                ret.push(krate);
1126 1127 1128 1129
            }
            for dep in &krate.deps {
                if visited.insert(dep) && dep != "build_helper" {
                    list.push(*dep);
1130 1131 1132 1133 1134
                }
            }
        }
        ret
    }
1135

J
John Kåre Alsaker 已提交
1136
    fn read_stamp_file(&self, stamp: &Path) -> Vec<(PathBuf, bool)> {
1137 1138 1139 1140 1141
        if self.config.dry_run {
            return Vec::new();
        }

        let mut paths = Vec::new();
1142
        let contents = t!(fs::read(stamp));
1143 1144 1145 1146 1147 1148
        // This is the method we use for extracting paths from the stamp file passed to us. See
        // run_cargo for more information (in compile.rs).
        for part in contents.split(|b| *b == 0) {
            if part.is_empty() {
                continue
            }
J
John Kåre Alsaker 已提交
1149 1150 1151
            let host = part[0] as char == 'h';
            let path = PathBuf::from(t!(str::from_utf8(&part[1..])));
            paths.push((path, host));
1152 1153 1154 1155 1156 1157 1158
        }
        paths
    }

    /// Copies a file from `src` to `dst`
    pub fn copy(&self, src: &Path, dst: &Path) {
        if self.config.dry_run { return; }
1159
        self.verbose_than(1, &format!("Copy {:?} to {:?}", src, dst));
1160
        let _ = fs::remove_file(&dst);
T
Tom Tromey 已提交
1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177
        let metadata = t!(src.symlink_metadata());
        if metadata.file_type().is_symlink() {
            let link = t!(fs::read_link(src));
            t!(symlink_file(link, dst));
        } else if let Ok(()) = fs::hard_link(src, dst) {
            // Attempt to "easy copy" by creating a hard link
            // (symlinks don't work on windows), but if that fails
            // just fall back to a slow `copy` operation.
        } else {
            if let Err(e) = fs::copy(src, dst) {
                panic!("failed to copy `{}` to `{}`: {}", src.display(),
                       dst.display(), e)
            }
            t!(fs::set_permissions(dst, metadata.permissions()));
            let atime = FileTime::from_last_access_time(&metadata);
            let mtime = FileTime::from_last_modification_time(&metadata);
            t!(filetime::set_file_times(dst, atime, mtime));
1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199
        }
    }

    /// Search-and-replaces within a file. (Not maximally efficiently: allocates a
    /// new string for each replacement.)
    pub fn replace_in_file(&self, path: &Path, replacements: &[(&str, &str)]) {
        if self.config.dry_run { return; }
        let mut contents = String::new();
        let mut file = t!(OpenOptions::new().read(true).write(true).open(path));
        t!(file.read_to_string(&mut contents));
        for &(target, replacement) in replacements {
            contents = contents.replace(target, replacement);
        }
        t!(file.seek(SeekFrom::Start(0)));
        t!(file.set_len(0));
        t!(file.write_all(contents.as_bytes()));
    }

    /// Copies the `src` directory recursively to `dst`. Both are assumed to exist
    /// when this function is called.
    pub fn cp_r(&self, src: &Path, dst: &Path) {
        if self.config.dry_run { return; }
1200
        for f in self.read_dir(src) {
1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216
            let path = f.path();
            let name = path.file_name().unwrap();
            let dst = dst.join(name);
            if t!(f.file_type()).is_dir() {
                t!(fs::create_dir_all(&dst));
                self.cp_r(&path, &dst);
            } else {
                let _ = fs::remove_file(&dst);
                self.copy(&path, &dst);
            }
        }
    }

    /// Copies the `src` directory recursively to `dst`. Both are assumed to exist
    /// when this function is called. Unwanted files or directories can be skipped
    /// by returning `false` from the filter function.
1217
    pub fn cp_filtered(&self, src: &Path, dst: &Path, filter: &dyn Fn(&Path) -> bool) {
1218 1219 1220 1221 1222
        // Immediately recurse with an empty relative path
        self.recurse_(src, dst, Path::new(""), filter)
    }

    // Inner function does the actual work
1223
    fn recurse_(&self, src: &Path, dst: &Path, relative: &Path, filter: &dyn Fn(&Path) -> bool) {
1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251
        for f in self.read_dir(src) {
            let path = f.path();
            let name = path.file_name().unwrap();
            let dst = dst.join(name);
            let relative = relative.join(name);
            // Only copy file or directory if the filter function returns true
            if filter(&relative) {
                if t!(f.file_type()).is_dir() {
                    let _ = fs::remove_dir_all(&dst);
                    self.create_dir(&dst);
                    self.recurse_(&path, &dst, &relative, filter);
                } else {
                    let _ = fs::remove_file(&dst);
                    self.copy(&path, &dst);
                }
            }
        }
    }

    fn copy_to_folder(&self, src: &Path, dest_folder: &Path) {
        let file_name = src.file_name().unwrap();
        let dest = dest_folder.join(file_name);
        self.copy(src, &dest);
    }

    fn install(&self, src: &Path, dstdir: &Path, perms: u32) {
        if self.config.dry_run { return; }
        let dst = dstdir.join(src.file_name().unwrap());
O
O01eg 已提交
1252
        self.verbose_than(1, &format!("Install {:?} to {:?}", src, dst));
1253 1254 1255
        t!(fs::create_dir_all(dstdir));
        drop(fs::remove_file(&dst));
        {
1256 1257 1258
            if !src.exists() {
                panic!("Error: File \"{}\" not found!", src.display());
            }
1259 1260 1261 1262 1263 1264 1265 1266 1267
            let metadata = t!(src.symlink_metadata());
            if let Err(e) = fs::copy(&src, &dst) {
                panic!("failed to copy `{}` to `{}`: {}", src.display(),
                       dst.display(), e)
            }
            t!(fs::set_permissions(&dst, metadata.permissions()));
            let atime = FileTime::from_last_access_time(&metadata);
            let mtime = FileTime::from_last_modification_time(&metadata);
            t!(filetime::set_file_times(&dst, atime, mtime));
1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278
        }
        chmod(&dst, perms);
    }

    fn create(&self, path: &Path, s: &str) {
        if self.config.dry_run { return; }
        t!(fs::write(path, s));
    }

    fn read(&self, path: &Path) -> String {
        if self.config.dry_run { return String::new(); }
1279
        t!(fs::read_to_string(path))
1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299
    }

    fn create_dir(&self, dir: &Path) {
        if self.config.dry_run { return; }
        t!(fs::create_dir_all(dir))
    }

    fn remove_dir(&self, dir: &Path) {
        if self.config.dry_run { return; }
        t!(fs::remove_dir_all(dir))
    }

    fn read_dir(&self, dir: &Path) -> impl Iterator<Item=fs::DirEntry> {
        let iter = match fs::read_dir(dir) {
            Ok(v) => v,
            Err(_) if self.config.dry_run => return vec![].into_iter(),
            Err(err) => panic!("could not read dir {:?}: {:?}", dir, err),
        };
        iter.map(|e| t!(e)).collect::<Vec<_>>().into_iter()
    }
1300 1301 1302 1303 1304

    fn remove(&self, f: &Path) {
        if self.config.dry_run { return; }
        fs::remove_file(f).unwrap_or_else(|_| panic!("failed to remove {:?}", f));
    }
1305 1306 1307 1308 1309 1310
}

#[cfg(unix)]
fn chmod(path: &Path, perms: u32) {
    use std::os::unix::fs::*;
    t!(fs::set_permissions(path, fs::Permissions::from_mode(perms)));
A
Alex Crichton 已提交
1311
}
1312 1313 1314
#[cfg(windows)]
fn chmod(_path: &Path, _perms: u32) {}

A
Alex Crichton 已提交
1315

1316
impl Compiler {
1317
    pub fn with_stage(mut self, stage: u32) -> Compiler {
1318 1319
        self.stage = stage;
        self
1320 1321
    }

A
Alexander Regueiro 已提交
1322
    /// Returns `true` if this is a snapshot compiler for `build`'s configuration
1323
    pub fn is_snapshot(&self, build: &Build) -> bool {
1324
        self.stage == 0 && self.host == build.build
1325
    }
1326

1327 1328 1329 1330
    /// Returns if this compiler should be treated as a final stage one in the
    /// current build session.
    /// This takes into account whether we're performing a full bootstrap or
    /// not; don't directly compare the stage with `2`!
1331
    pub fn is_final_stage(&self, build: &Build) -> bool {
1332 1333 1334
        let final_stage = if build.config.full_bootstrap { 2 } else { 1 };
        self.stage >= final_stage
    }
A
Alex Crichton 已提交
1335
}