lib.rs 31.7 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
extern crate serde_json;
127 128 129 130 131 132
extern crate cmake;
extern crate filetime;
extern crate gcc;
extern crate getopts;
extern crate num_cpus;
extern crate toml;
133

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

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

146 147
use build_helper::{run_silent, run_suppressed, try_run_silent, try_run_suppressed, output, mtime,
                   BuildExpectation};
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
mod toolstate;
169 170 171 172

#[cfg(windows)]
mod job;

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

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

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

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

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

/// 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,
222 223
    rust_info: channel::GitInfo,
    cargo_info: channel::GitInfo,
A
Alex Crichton 已提交
224
    rls_info: channel::GitInfo,
225
    local_rebuild: bool,
226
    fail_fast: bool,
M
Mark Simulacrum 已提交
227
    verbosity: usize,
228

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

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

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

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

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

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

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

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

M
Mark Simulacrum 已提交
280
    /// Build some tool, placing output in the "stageN-tools" directory.
281 282 283 284 285 286 287 288
    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 已提交
289
    pub fn new(config: Config) -> Build {
290
        let cwd = t!(env::current_dir());
M
Mark Simulacrum 已提交
291
        let src = config.src.clone();
292 293
        let out = cwd.join("build");

294 295 296 297 298 299 300 301 302
        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,
        };
303 304 305
        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"));
306

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

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

318 319 320
            config,
            src,
            out,
321

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

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

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

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

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

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

    /// 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);
381 382
        } else if stamp.exists() {
            return
383 384 385 386 387 388 389 390
        }
        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 {
391
        let mut features = "panic-unwind".to_string();
J
Jorge Aparicio 已提交
392

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

    /// 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 已提交
414 415 416
        if self.config.llvm_enabled {
            features.push_str(" llvm");
        }
M
Mark Simulacrum 已提交
417
        features
418 419 420 421 422 423 424 425
    }

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

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

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

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

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

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

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

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

482 483 484
    /// 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.
485 486
    fn is_rust_llvm(&self, target: Interned<String>) -> bool {
        match self.config.target_config.get(&target) {
487 488 489 490 491
            Some(ref c) => c.llvm_config.is_none(),
            None => true
        }
    }

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

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

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

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

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

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

547 548 549 550 551 552 553
    /// Runs a command, printing out nice contextual information if its build
    /// status is not the expected one
    fn run_expecting(&self, cmd: &mut Command, expect: BuildExpectation) {
        self.verbose(&format!("running: {:?}", cmd));
        run_silent(cmd, expect)
    }

554 555
    /// Runs a command, printing out nice contextual information if it fails.
    fn run(&self, cmd: &mut Command) {
556
        self.run_expecting(cmd, BuildExpectation::None)
557 558
    }

559 560 561
    /// Runs a command, printing out nice contextual information if it fails.
    fn run_quiet(&self, cmd: &mut Command) {
        self.verbose(&format!("running: {:?}", cmd));
562
        run_suppressed(cmd, BuildExpectation::None)
563 564
    }

565 566 567 568 569
    /// Runs a command, printing out nice contextual information if its build
    /// status is not the expected one.
    /// Exits if the command failed to execute at all, otherwise returns whether
    /// the expectation was met
    fn try_run(&self, cmd: &mut Command, expect: BuildExpectation) -> bool {
570
        self.verbose(&format!("running: {:?}", cmd));
571
        try_run_silent(cmd, expect)
572 573 574 575 576 577 578
    }

    /// 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));
579
        try_run_suppressed(cmd, BuildExpectation::None)
580 581
    }

M
Mark Simulacrum 已提交
582 583 584 585 586 587 588 589
    pub fn is_verbose(&self) -> bool {
        self.verbosity > 0
    }

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

590 591
    /// Prints a message if this build is configured in verbose mode.
    fn verbose(&self, msg: &str) {
M
Mark Simulacrum 已提交
592
        if self.is_verbose() {
593 594 595 596 597 598 599
            println!("{}", msg);
        }
    }

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

    /// Returns the path to the C compiler for the target specified.
604 605
    fn cc(&self, target: Interned<String>) -> &Path {
        self.cc[&target].0.path()
606 607 608 609
    }

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

618
        // If we're compiling on macOS then we add a few unconditional flags
619 620 621 622 623 624
        // 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());
        }
625 626 627 628

        // 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
629
        if &*target == "i686-pc-windows-gnu" {
630 631
            base.push("-fno-omit-frame-pointer".into());
        }
M
Mark Simulacrum 已提交
632
        base
633 634 635
    }

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

640
    /// Returns the path to the C++ compiler for the target specified.
641 642
    fn cxx(&self, target: Interned<String>) -> Result<&Path, String> {
        match self.cxx.get(&target) {
643 644 645 646
            Some(p) => Ok(p.path()),
            None => Err(format!(
                    "target `{}` is not configured as a host, only as a target",
                    target))
647
        }
648 649 650
    }

    /// Returns flags to pass to the compiler to generate code for `target`.
651
    fn rustc_flags(&self, target: Interned<String>) -> Vec<String> {
652 653 654 655 656 657 658 659
        // 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 已提交
660 661
        if target != self.config.build && !target.contains("msvc") &&
            !target.contains("emscripten") {
662 663
            base.push(format!("-Clinker={}", self.cc(target).display()));
        }
M
Mark Simulacrum 已提交
664
        base
A
Alex Crichton 已提交
665
    }
666

667 668
    /// Returns if this target should statically link the C runtime, if specified
    fn crt_static(&self, target: Interned<String>) -> Option<bool> {
669 670 671 672 673 674
        if target.contains("pc-windows-msvc") {
            Some(true)
        } else {
            self.config.target_config.get(&target)
                .and_then(|t| t.crt_static)
        }
675 676
    }

677
    /// Returns the "musl root" for this `target`, if defined
678 679
    fn musl_root(&self, target: Interned<String>) -> Option<&Path> {
        self.config.target_config.get(&target)
680
            .and_then(|t| t.musl_root.as_ref())
681 682 683
            .or(self.config.musl_root.as_ref())
            .map(|p| &**p)
    }
684

685 686
    /// Returns whether the target will be tested using the `remote-test-client`
    /// and `remote-test-server` binaries.
687
    fn remote_tested(&self, target: Interned<String>) -> bool {
688 689
        self.qemu_rootfs(target).is_some() || target.contains("android") ||
        env::var_os("TEST_DEVICE_ADDR").is_some()
690 691
    }

692 693 694 695 696
    /// 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.
697 698
    fn qemu_rootfs(&self, target: Interned<String>) -> Option<&Path> {
        self.config.target_config.get(&target)
699 700 701 702
            .and_then(|t| t.qemu_rootfs.as_ref())
            .map(|p| &**p)
    }

703 704 705 706
    /// Path to the python interpreter to use
    fn python(&self) -> &Path {
        self.config.python.as_ref().unwrap()
    }
707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725

    /// 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.
726
    fn force_use_stage1(&self, compiler: Compiler, target: Interned<String>) -> bool {
727 728
        !self.config.full_bootstrap &&
            compiler.stage >= 2 &&
729
            (self.hosts.iter().any(|h| *h == target) || target == self.build)
730
    }
731 732 733

    /// Returns the directory that OpenSSL artifacts are compiled into if
    /// configured to do so.
734
    fn openssl_dir(&self, target: Interned<String>) -> Option<PathBuf> {
735 736 737 738
        // OpenSSL not used on Windows
        if target.contains("windows") {
            None
        } else if self.config.openssl_static {
739
            Some(self.out.join(&*target).join("openssl"))
740 741 742 743 744 745 746
        } else {
            None
        }
    }

    /// Returns the directory that OpenSSL artifacts are installed into if
    /// configured as such.
747
    fn openssl_install_dir(&self, target: Interned<String>) -> Option<PathBuf> {
748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789
        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)
    }

790 791
    /// Returns the value of `package_vers` above for Cargo
    fn cargo_package_vers(&self) -> String {
N
Nick Cameron 已提交
792
        self.package_vers(&self.release_num("cargo"))
793 794
    }

795 796 797 798 799
    /// Returns the value of `package_vers` above for rls
    fn rls_package_vers(&self) -> String {
        self.package_vers(&self.release_num("rls"))
    }

800 801 802 803 804 805 806 807 808
    /// 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)
    }

809 810 811 812 813
    /// Return the full commit hash
    fn rust_sha(&self) -> Option<&str> {
        self.rust_info.sha()
    }

N
Nick Cameron 已提交
814 815
    /// Returns the `a.b.c` version that the given package is at.
    fn release_num(&self, package: &str) -> String {
816
        let mut toml = String::new();
A
Alex Crichton 已提交
817
        let toml_file_name = self.src.join(&format!("src/tools/{}/Cargo.toml", package));
N
Nick Cameron 已提交
818
        t!(t!(File::open(toml_file_name)).read_to_string(&mut toml));
819 820 821 822 823 824 825 826
        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 已提交
827
        panic!("failed to find version in {}'s Cargo.toml", package)
828 829
    }

830 831 832 833 834 835 836 837
    /// 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,
        }
    }
838 839 840 841 842 843 844 845 846 847 848 849 850

    /// 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
        }
    }
851 852 853 854

    /// Get a list of crates from a root crate.
    ///
    /// Returns Vec<(crate, path to crate, is_root_crate)>
855 856
    fn crates(&self, root: &str) -> Vec<(Interned<String>, &Path)> {
        let interned = INTERNER.intern_string(root.to_owned());
857
        let mut ret = Vec::new();
858
        let mut list = vec![interned];
859 860
        let mut visited = HashSet::new();
        while let Some(krate) = list.pop() {
861
            let krate = &self.crates[&krate];
862 863
            // If we can't strip prefix, then out-of-tree path
            let path = krate.path.strip_prefix(&self.src).unwrap_or(&krate.path);
864
            ret.push((krate.name, path));
865 866
            for dep in &krate.deps {
                if visited.insert(dep) && dep != "build_helper" {
867
                    list.push(*dep);
868 869 870 871 872
                }
            }
        }
        ret
    }
A
Alex Crichton 已提交
873 874
}

875 876
impl<'a> Compiler {
    pub fn with_stage(mut self, stage: u32) -> Compiler {
877 878
        self.stage = stage;
        self
879 880 881
    }

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

886 887 888 889
    /// 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`!
890
    pub fn is_final_stage(&self, build: &Build) -> bool {
891 892 893
        let final_stage = if build.config.full_bootstrap { 2 } else { 1 };
        self.stage >= final_stage
    }
A
Alex Crichton 已提交
894
}