lib.rs 30.9 KB
Newer Older
A
Alex Crichton 已提交
1 2 3 4 5 6 7 8 9 10
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

11
//! Implementation of rustbuild, the Rust build system.
12
//!
13 14 15
//! 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
16
//! builds, building artifacts like LLVM, etc. The goals of rustbuild are:
17
//!
18 19 20 21 22 23 24 25
//! * 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
//!
26 27 28 29 30 31
//! 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
32 33 34 35 36 37
//! 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.
38 39
//! However, compiletest itself tries to avoid running tests when the artifacts
//! that are involved (mainly the compiler) haven't changed.
40 41 42 43
//!
//! When you execute `x.py build`, the steps which are executed are:
//!
//! * First, the python script is run. This will automatically download the
44
//!   stage0 rustc and cargo according to `src/stage0.txt`, or use the cached
45 46 47 48
//!   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,
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
//!   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
//! `build/$HOST/stage0-sysroot/lib/rustlib/$ARCH/lib`. FIXME: This step's
//! 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.
//!
99
//! ## Uplifting stage1 {std,test,rustc}
100 101 102 103 104 105 106
//!
//! 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.
107 108 109 110 111 112 113 114
//!
//! 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.
115

A
Alex Crichton 已提交
116
#![deny(warnings)]
117
#![allow(stable_features)]
118
#![feature(associated_consts)]
A
Alex Crichton 已提交
119

120
#[macro_use]
121
extern crate build_helper;
122 123
#[macro_use]
extern crate serde_derive;
124 125
#[macro_use]
extern crate lazy_static;
126 127
extern crate serde;
extern crate serde_json;
128 129 130 131 132 133
extern crate cmake;
extern crate filetime;
extern crate gcc;
extern crate getopts;
extern crate num_cpus;
extern crate toml;
134

135 136 137
#[cfg(unix)]
extern crate libc;

138
use std::cell::Cell;
139
use std::collections::{HashSet, HashMap};
A
Alex Crichton 已提交
140
use std::env;
141
use std::fs::{self, File};
142
use std::io::Read;
143
use std::path::{PathBuf, Path};
144
use std::process::Command;
M
Mark Simulacrum 已提交
145
use std::slice;
146

147
use build_helper::{run_silent, run_suppressed, try_run_silent, try_run_suppressed, output, mtime};
148

149
use util::{exe, libdir, OutputFolder, CiEnv};
150 151 152 153 154 155

mod cc;
mod channel;
mod check;
mod clean;
mod compile;
156
mod metadata;
157 158 159 160
mod config;
mod dist;
mod doc;
mod flags;
161
mod install;
162 163 164
mod native;
mod sanity;
pub mod util;
165
mod builder;
166 167
mod cache;
mod tool;
168 169 170 171

#[cfg(windows)]
mod job;

172
#[cfg(unix)]
173
mod job {
174 175 176 177
    use libc;

    pub unsafe fn setup(build: &mut ::Build) {
        if build.config.low_priority {
Q
QuietMisdreavus 已提交
178
            libc::setpriority(libc::PRIO_PGRP as _, 0, 10);
179 180 181 182 183
        }
    }
}

#[cfg(not(any(unix, windows)))]
184
mod job {
185 186
    pub unsafe fn setup(_build: &mut ::Build) {
    }
187 188 189
}

pub use config::Config;
M
Mark Simulacrum 已提交
190
use flags::Subcommand;
191
use cache::{Interned, INTERNER};
192 193 194 195 196 197

/// 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.
198 199
#[derive(Eq, PartialEq, Clone, Copy, Hash, Debug)]
pub struct Compiler {
200
    stage: u32,
201
    host: Interned<String>,
202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220
}

/// 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 {
    // User-specified configuration via config.toml
    config: Config,

    // Derived properties from the above two configurations
    src: PathBuf,
    out: PathBuf,
221 222
    rust_info: channel::GitInfo,
    cargo_info: channel::GitInfo,
A
Alex Crichton 已提交
223
    rls_info: channel::GitInfo,
224
    local_rebuild: bool,
225
    fail_fast: bool,
M
Mark Simulacrum 已提交
226
    verbosity: usize,
227

228
    // Targets for which to build.
229 230 231
    build: Interned<String>,
    hosts: Vec<Interned<String>>,
    targets: Vec<Interned<String>>,
232

233 234 235 236
    // Stage 0 (downloaded) compiler and cargo or their local rust equivalents.
    initial_rustc: PathBuf,
    initial_cargo: PathBuf,

237 238 239 240 241
    // Probed tools at runtime
    lldb_version: Option<String>,
    lldb_python_dir: Option<String>,

    // Runtime state filled in later on
M
Mark Simulacrum 已提交
242
    // target -> (cc, ar)
243
    cc: HashMap<Interned<String>, (gcc::Tool, Option<PathBuf>)>,
M
Mark Simulacrum 已提交
244
    // host -> (cc, ar)
245 246
    cxx: HashMap<Interned<String>, gcc::Tool>,
    crates: HashMap<Interned<String>, Crate>,
247
    is_sudo: bool,
248
    ci_env: CiEnv,
249
    delayed_failures: Cell<usize>,
250 251 252 253
}

#[derive(Debug)]
struct Crate {
254
    name: Interned<String>,
255
    version: String,
256
    deps: Vec<Interned<String>>,
257 258 259 260
    path: PathBuf,
    doc_step: String,
    build_step: String,
    test_step: String,
U
Ulrik Sverdrup 已提交
261
    bench_step: String,
262 263 264 265 266 267
}

/// 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.
268
#[derive(Debug, Hash, Clone, Copy, PartialEq, Eq)]
269
pub enum Mode {
M
Mark Simulacrum 已提交
270
    /// Build the standard library, placing output in the "stageN-std" directory.
271 272
    Libstd,

M
Mark Simulacrum 已提交
273
    /// Build libtest, placing output in the "stageN-test" directory.
274 275
    Libtest,

M
Mark Simulacrum 已提交
276
    /// Build librustc and compiler libraries, placing output in the "stageN-rustc" directory.
277 278
    Librustc,

M
Mark Simulacrum 已提交
279
    /// Build some tool, placing output in the "stageN-tools" directory.
280 281 282 283 284 285 286 287
    Tool,
}

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 已提交
288
    pub fn new(config: Config) -> Build {
289
        let cwd = t!(env::current_dir());
M
Mark Simulacrum 已提交
290
        let src = config.src.clone();
291 292
        let out = cwd.join("build");

293 294 295 296 297 298 299 300 301
        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,
        };
302 303 304
        let rust_info = channel::GitInfo::new(&config, &src);
        let cargo_info = channel::GitInfo::new(&config, &src.join("src/tools/cargo"));
        let rls_info = channel::GitInfo::new(&config, &src.join("src/tools/rls"));
305

306
        Build {
307 308 309
            initial_rustc: config.initial_rustc.clone(),
            initial_cargo: config.initial_cargo.clone(),
            local_rebuild: config.local_rebuild,
M
Mark Simulacrum 已提交
310 311
            fail_fast: config.cmd.fail_fast(),
            verbosity: config.verbose,
312

M
Mark Simulacrum 已提交
313 314 315
            build: config.build,
            hosts: config.hosts.clone(),
            targets: config.targets.clone(),
316

317 318 319 320
            config: config,
            src: src,
            out: out,

321 322
            rust_info: rust_info,
            cargo_info: cargo_info,
A
Alex Crichton 已提交
323
            rls_info: rls_info,
324 325
            cc: HashMap::new(),
            cxx: HashMap::new(),
326
            crates: HashMap::new(),
327 328
            lldb_version: None,
            lldb_python_dir: None,
329
            is_sudo: is_sudo,
330
            ci_env: CiEnv::current(),
331
            delayed_failures: Cell::new(0),
332 333 334
        }
    }

M
Mark Simulacrum 已提交
335 336 337 338 339 340
    pub fn build_triple(&self) -> &[Interned<String>] {
        unsafe {
            slice::from_raw_parts(&self.build, 1)
        }
    }

341 342 343
    /// Executes the entire build, as configured by the flags and configuration.
    pub fn build(&mut self) {
        unsafe {
344
            job::setup(self);
345 346
        }

M
Mark Simulacrum 已提交
347
        if let Subcommand::Clean = self.config.cmd {
348 349 350 351 352 353 354
            return clean::clean(self);
        }

        self.verbose("finding compilers");
        cc::find(self);
        self.verbose("running sanity check");
        sanity::check(self);
355
        // If local-rust is the same major.minor as the current version, then force a local-rebuild
356
        let local_version_verbose = output(
357
            Command::new(&self.initial_rustc).arg("--version").arg("--verbose"));
358 359 360
        let local_release = local_version_verbose
            .lines().filter(|x| x.starts_with("release:"))
            .next().unwrap().trim_left_matches("release:").trim();
361 362
        let my_version = channel::CFG_RELEASE_NUM;
        if local_release.split('.').take(2).eq(my_version.split('.').take(2)) {
363
            self.verbose(&format!("auto-detected local-rebuild {}", local_release));
364 365
            self.local_rebuild = true;
        }
366 367
        self.verbose("learning about cargo");
        metadata::build(self);
368

369
        builder::Builder::run(&self);
370 371 372 373 374 375 376 377 378 379
    }

    /// Clear out `dir` if `input` is newer.
    ///
    /// After this executes, it will also ensure that `dir` exists.
    fn clear_if_dirty(&self, dir: &Path, input: &Path) {
        let stamp = dir.join(".stamp");
        if mtime(&stamp) < mtime(input) {
            self.verbose(&format!("Dirty - {}", dir.display()));
            let _ = fs::remove_dir_all(dir);
380 381
        } else if stamp.exists() {
            return
382 383 384 385 386 387 388 389
        }
        t!(fs::create_dir_all(dir));
        t!(File::create(stamp));
    }

    /// Get the space-separated set of activated features for the standard
    /// library.
    fn std_features(&self) -> String {
390
        let mut features = "panic-unwind".to_string();
J
Jorge Aparicio 已提交
391

392 393 394 395 396 397
        if self.config.debug_jemalloc {
            features.push_str(" debug-jemalloc");
        }
        if self.config.use_jemalloc {
            features.push_str(" jemalloc");
        }
398 399 400
        if self.config.backtrace {
            features.push_str(" backtrace");
        }
401 402 403
        if self.config.profiler {
            features.push_str(" profiler");
        }
M
Mark Simulacrum 已提交
404
        features
405 406 407 408 409 410 411 412
    }

    /// Get the space-separated set of activated features for the compiler.
    fn rustc_features(&self) -> String {
        let mut features = String::new();
        if self.config.use_jemalloc {
            features.push_str(" jemalloc");
        }
B
bjorn3 已提交
413 414 415
        if self.config.llvm_enabled {
            features.push_str(" llvm");
        }
M
Mark Simulacrum 已提交
416
        features
417 418 419 420 421 422 423 424
    }

    /// 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"}
    }

425 426
    /// Get the directory for incremental by-products when using the
    /// given compiler.
427
    fn incremental_dir(&self, compiler: Compiler) -> PathBuf {
428
        self.out.join(&*compiler.host).join(format!("stage{}-incremental", compiler.stage))
429 430
    }

431 432 433 434
    /// 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.
435
    fn stage_out(&self, compiler: Compiler, mode: Mode) -> PathBuf {
436 437 438 439 440 441
        let suffix = match mode {
            Mode::Libstd => "-std",
            Mode::Libtest => "-test",
            Mode::Tool => "-tools",
            Mode::Librustc => "-rustc",
        };
442
        self.out.join(&*compiler.host)
443 444 445 446
                .join(format!("stage{}{}", compiler.stage, suffix))
    }

    /// Returns the root output directory for all Cargo output in a given stage,
447
    /// running a particular compiler, wehther or not we're building the
448 449
    /// standard library, and targeting the specified architecture.
    fn cargo_out(&self,
450
                 compiler: Compiler,
451
                 mode: Mode,
452 453
                 target: Interned<String>) -> PathBuf {
        self.stage_out(compiler, mode).join(&*target).join(self.cargo_dir())
454 455 456 457 458 459
    }

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

464
    /// Output directory for all documentation for a target
465 466
    fn doc_out(&self, target: Interned<String>) -> PathBuf {
        self.out.join(&*target).join("doc")
467 468
    }

469
    /// Output directory for some generated md crate documentation for a target (temporary)
470 471
    fn md_doc_out(&self, target: Interned<String>) -> Interned<PathBuf> {
        INTERNER.intern_path(self.out.join(&*target).join("md-doc"))
472 473
    }

474 475 476
    /// Output directory for all crate documentation for a target (temporary)
    ///
    /// The artifacts here are then copied into `doc_out` above.
477 478
    fn crate_doc_out(&self, target: Interned<String>) -> PathBuf {
        self.out.join(&*target).join("crate-docs")
479 480
    }

481 482 483
    /// Returns true if no custom `llvm-config` is set for the specified target.
    ///
    /// If no custom `llvm-config` was specified then Rust's llvm will be used.
484 485
    fn is_rust_llvm(&self, target: Interned<String>) -> bool {
        match self.config.target_config.get(&target) {
486 487 488 489 490
            Some(ref c) => c.llvm_config.is_none(),
            None => true
        }
    }

491 492 493 494
    /// Returns the path to `llvm-config` for the specified target.
    ///
    /// If a custom `llvm-config` was specified for target then that's returned
    /// instead.
495 496
    fn llvm_config(&self, target: Interned<String>) -> PathBuf {
        let target_config = self.config.target_config.get(&target);
497 498 499
        if let Some(s) = target_config.and_then(|c| c.llvm_config.as_ref()) {
            s.clone()
        } else {
500 501
            self.llvm_out(self.config.build).join("bin")
                .join(exe("llvm-config", &*target))
502 503 504 505
        }
    }

    /// Returns the path to `FileCheck` binary for the specified target
506 507
    fn llvm_filecheck(&self, target: Interned<String>) -> PathBuf {
        let target_config = self.config.target_config.get(&target);
508
        if let Some(s) = target_config.and_then(|c| c.llvm_config.as_ref()) {
S
Seo Sanghyeon 已提交
509
            let llvm_bindir = output(Command::new(s).arg("--bindir"));
510
            Path::new(llvm_bindir.trim()).join(exe("FileCheck", &*target))
511
        } else {
512 513
            let base = self.llvm_out(self.config.build).join("build");
            let exe = exe("FileCheck", &*target);
514
            if !self.config.ninja && self.config.build.contains("msvc") {
515 516 517 518 519 520 521
                base.join("Release/bin").join(exe)
            } else {
                base.join("bin").join(exe)
            }
        }
    }

522
    /// Directory for libraries built from C/C++ code and shared between stages.
523 524
    fn native_dir(&self, target: Interned<String>) -> PathBuf {
        self.out.join(&*target).join("native")
525 526
    }

527 528
    /// Root output directory for rust_test_helpers library compiled for
    /// `target`
529
    fn test_helpers_out(&self, target: Interned<String>) -> PathBuf {
530
        self.native_dir(target).join("rust-test-helpers")
531 532
    }

533 534 535 536 537 538 539
    /// 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());
        }
    }

540 541
    /// Returns the libdir of the snapshot compiler.
    fn rustc_snapshot_libdir(&self) -> PathBuf {
542
        self.initial_rustc.parent().unwrap().parent().unwrap()
543 544 545 546 547 548 549 550 551
            .join(libdir(&self.config.build))
    }

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

552 553 554 555 556 557
    /// Runs a command, printing out nice contextual information if it fails.
    fn run_quiet(&self, cmd: &mut Command) {
        self.verbose(&format!("running: {:?}", cmd));
        run_suppressed(cmd)
    }

558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573
    /// 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 {
        self.verbose(&format!("running: {:?}", cmd));
        try_run_silent(cmd)
    }

    /// 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 {
        self.verbose(&format!("running: {:?}", cmd));
        try_run_suppressed(cmd)
    }

M
Mark Simulacrum 已提交
574 575 576 577 578 579 580 581
    pub fn is_verbose(&self) -> bool {
        self.verbosity > 0
    }

    pub fn is_very_verbose(&self) -> bool {
        self.verbosity > 1
    }

582 583
    /// Prints a message if this build is configured in verbose mode.
    fn verbose(&self, msg: &str) {
M
Mark Simulacrum 已提交
584
        if self.is_verbose() {
585 586 587 588 589 590 591
            println!("{}", msg);
        }
    }

    /// Returns the number of parallel jobs that have been configured for this
    /// build.
    fn jobs(&self) -> u32 {
M
Mark Simulacrum 已提交
592
        self.config.jobs.unwrap_or_else(|| num_cpus::get() as u32)
593 594 595
    }

    /// Returns the path to the C compiler for the target specified.
596 597
    fn cc(&self, target: Interned<String>) -> &Path {
        self.cc[&target].0.path()
598 599 600 601
    }

    /// Returns a list of flags to pass to the C compiler for the target
    /// specified.
602
    fn cflags(&self, target: Interned<String>) -> Vec<String> {
603 604
        // Filter out -O and /O (the optimization flags) that we picked up from
        // gcc-rs because the build scripts will determine that for themselves.
605
        let mut base = self.cc[&target].0.args().iter()
606 607 608 609
                           .map(|s| s.to_string_lossy().into_owned())
                           .filter(|s| !s.starts_with("-O") && !s.starts_with("/O"))
                           .collect::<Vec<_>>();

610
        // If we're compiling on macOS then we add a few unconditional flags
611 612 613 614 615 616
        // indicating that we want libc++ (more filled out than libstdc++) and
        // we want to compile for 10.7. This way we can ensure that
        // LLVM/jemalloc/etc are all properly compiled.
        if target.contains("apple-darwin") {
            base.push("-stdlib=libc++".into());
        }
617 618 619 620

        // 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
621
        if &*target == "i686-pc-windows-gnu" {
622 623
            base.push("-fno-omit-frame-pointer".into());
        }
M
Mark Simulacrum 已提交
624
        base
625 626 627
    }

    /// Returns the path to the `ar` archive utility for the target specified.
628 629
    fn ar(&self, target: Interned<String>) -> Option<&Path> {
        self.cc[&target].1.as_ref().map(|p| &**p)
630 631
    }

632
    /// Returns the path to the C++ compiler for the target specified.
633 634
    fn cxx(&self, target: Interned<String>) -> Result<&Path, String> {
        match self.cxx.get(&target) {
635 636 637 638
            Some(p) => Ok(p.path()),
            None => Err(format!(
                    "target `{}` is not configured as a host, only as a target",
                    target))
639
        }
640 641 642
    }

    /// Returns flags to pass to the compiler to generate code for `target`.
643
    fn rustc_flags(&self, target: Interned<String>) -> Vec<String> {
644 645 646 647 648 649 650 651
        // New flags should be added here with great caution!
        //
        // It's quite unfortunate to **require** flags to generate code for a
        // target, so it should only be passed here if absolutely necessary!
        // Most default configuration should be done through target specs rather
        // than an entry here.

        let mut base = Vec::new();
B
Tidy  
Brian Anderson 已提交
652 653
        if target != self.config.build && !target.contains("msvc") &&
            !target.contains("emscripten") {
654 655
            base.push(format!("-Clinker={}", self.cc(target).display()));
        }
M
Mark Simulacrum 已提交
656
        base
A
Alex Crichton 已提交
657
    }
658 659

    /// Returns the "musl root" for this `target`, if defined
660 661
    fn musl_root(&self, target: Interned<String>) -> Option<&Path> {
        self.config.target_config.get(&target)
662
            .and_then(|t| t.musl_root.as_ref())
663 664 665
            .or(self.config.musl_root.as_ref())
            .map(|p| &**p)
    }
666

667 668
    /// Returns whether the target will be tested using the `remote-test-client`
    /// and `remote-test-server` binaries.
669
    fn remote_tested(&self, target: Interned<String>) -> bool {
670 671
        self.qemu_rootfs(target).is_some() || target.contains("android") ||
        env::var_os("TEST_DEVICE_ADDR").is_some()
672 673
    }

674 675 676 677 678
    /// 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.
679 680
    fn qemu_rootfs(&self, target: Interned<String>) -> Option<&Path> {
        self.config.target_config.get(&target)
681 682 683 684
            .and_then(|t| t.qemu_rootfs.as_ref())
            .map(|p| &**p)
    }

685 686 687 688
    /// Path to the python interpreter to use
    fn python(&self) -> &Path {
        self.config.python.as_ref().unwrap()
    }
689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707

    /// 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.
708
    fn force_use_stage1(&self, compiler: Compiler, target: Interned<String>) -> bool {
709 710
        !self.config.full_bootstrap &&
            compiler.stage >= 2 &&
M
Mark Simulacrum 已提交
711
            self.hosts.iter().any(|h| *h == target)
712
    }
713 714 715

    /// Returns the directory that OpenSSL artifacts are compiled into if
    /// configured to do so.
716
    fn openssl_dir(&self, target: Interned<String>) -> Option<PathBuf> {
717 718 719 720
        // OpenSSL not used on Windows
        if target.contains("windows") {
            None
        } else if self.config.openssl_static {
721
            Some(self.out.join(&*target).join("openssl"))
722 723 724 725 726 727 728
        } else {
            None
        }
    }

    /// Returns the directory that OpenSSL artifacts are installed into if
    /// configured as such.
729
    fn openssl_install_dir(&self, target: Interned<String>) -> Option<PathBuf> {
730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771
        self.openssl_dir(target).map(|p| p.join("install"))
    }

    /// 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(),
            "beta" => format!("{}-beta{}", num, channel::CFG_PRERELEASE_VERSION),
            "nightly" => format!("{}-nightly", num),
            _ => format!("{}-dev", num),
        }
    }

    /// 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)
    }

772 773
    /// Returns the value of `package_vers` above for Cargo
    fn cargo_package_vers(&self) -> String {
N
Nick Cameron 已提交
774
        self.package_vers(&self.release_num("cargo"))
775 776
    }

777 778 779 780 781
    /// Returns the value of `package_vers` above for rls
    fn rls_package_vers(&self) -> String {
        self.package_vers(&self.release_num("rls"))
    }

782 783 784 785 786 787 788 789 790
    /// 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)
    }

N
Nick Cameron 已提交
791 792
    /// Returns the `a.b.c` version that the given package is at.
    fn release_num(&self, package: &str) -> String {
793
        let mut toml = String::new();
A
Alex Crichton 已提交
794
        let toml_file_name = self.src.join(&format!("src/tools/{}/Cargo.toml", package));
N
Nick Cameron 已提交
795
        t!(t!(File::open(toml_file_name)).read_to_string(&mut toml));
796 797 798 799 800 801 802 803
        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 已提交
804
        panic!("failed to find version in {}'s Cargo.toml", package)
805 806
    }

807 808 809 810 811 812 813 814
    /// Returns whether unstable features should be enabled for the compiler
    /// we're building.
    fn unstable_features(&self) -> bool {
        match &self.config.channel[..] {
            "stable" | "beta" => false,
            "nightly" | _ => true,
        }
    }
815 816 817 818 819 820 821 822 823 824 825 826 827

    /// Fold the output of the commands after this method into a group. The fold
    /// ends when the returned object is dropped. Folding can only be used in
    /// the Travis CI environment.
    pub fn fold_output<D, F>(&self, name: F) -> Option<OutputFolder>
        where D: Into<String>, F: FnOnce() -> D
    {
        if self.ci_env == CiEnv::Travis {
            Some(OutputFolder::new(name().into()))
        } else {
            None
        }
    }
828 829 830 831

    /// Get a list of crates from a root crate.
    ///
    /// Returns Vec<(crate, path to crate, is_root_crate)>
832 833
    fn crates(&self, root: &str) -> Vec<(Interned<String>, &Path)> {
        let interned = INTERNER.intern_string(root.to_owned());
834
        let mut ret = Vec::new();
835
        let mut list = vec![interned];
836 837
        let mut visited = HashSet::new();
        while let Some(krate) = list.pop() {
838
            let krate = &self.crates[&krate];
839 840
            // If we can't strip prefix, then out-of-tree path
            let path = krate.path.strip_prefix(&self.src).unwrap_or(&krate.path);
841
            ret.push((krate.name, path));
842 843
            for dep in &krate.deps {
                if visited.insert(dep) && dep != "build_helper" {
844
                    list.push(*dep);
845 846 847 848 849
                }
            }
        }
        ret
    }
A
Alex Crichton 已提交
850 851
}

852 853
impl<'a> Compiler {
    pub fn with_stage(mut self, stage: u32) -> Compiler {
854 855
        self.stage = stage;
        self
856 857 858
    }

    /// Returns whether this is a snapshot compiler for `build`'s configuration
859
    pub fn is_snapshot(&self, build: &Build) -> bool {
860
        self.stage == 0 && self.host == build.build
861
    }
862

863 864 865 866
    /// 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`!
867
    pub fn is_final_stage(&self, build: &Build) -> bool {
868 869 870
        let final_stage = if build.config.full_bootstrap { 2 } else { 1 };
        self.stage >= final_stage
    }
A
Alex Crichton 已提交
871
}