compile.rs 34.5 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 12 13 14 15 16 17 18
//! Implementation of compiling various phases of the compiler and standard
//! library.
//!
//! This module contains some of the real meat in the rustbuild build system
//! which is where Cargo is used to compiler the standard library, libtest, and
//! compiler. This module is also responsible for assembling the sysroot as it
//! goes along from the output of the previous stage.

19
use std::env;
20
use std::fs::{self, File};
21 22
use std::io::BufReader;
use std::io::prelude::*;
A
Alex Crichton 已提交
23
use std::path::{Path, PathBuf};
24 25
use std::process::{Command, Stdio};
use std::str;
M
Mark Simulacrum 已提交
26
use std::cmp::min;
A
Alex Crichton 已提交
27

28
use build_helper::{output, mtime, up_to_date};
29
use filetime::FileTime;
M
Mark Simulacrum 已提交
30
use serde_json;
A
Alex Crichton 已提交
31

32
use util::{exe, libdir, is_dylib, copy, read_stamp_file, CiEnv};
33
use {Build, Compiler, Mode};
34
use native;
35
use tool;
36

37
use cache::{INTERNER, Interned};
38
use builder::{Step, RunConfig, ShouldRun, Builder};
A
Alex Crichton 已提交
39

40 41 42 43
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct Std {
    pub target: Interned<String>,
    pub compiler: Compiler,
44
}
P
Peter Wagenet 已提交
45

46
impl Step for Std {
47
    type Output = ();
48 49
    const DEFAULT: bool = true;

50 51
    fn should_run(run: ShouldRun) -> ShouldRun {
        run.path("src/libstd").krate("std")
52 53
    }

54 55 56 57
    fn make_run(run: RunConfig) {
        run.builder.ensure(Std {
            compiler: run.builder.compiler(run.builder.top_stage, run.host),
            target: run.target,
58
        });
59
    }
60 61 62 63 64 65 66 67 68 69

    /// Build the standard library.
    ///
    /// This will build the standard library for a particular stage of the build
    /// using the `compiler` targeting the `target` architecture. The artifacts
    /// created will also be linked into the sysroot directory.
    fn run(self, builder: &Builder) {
        let build = builder.build;
        let target = self.target;
        let compiler = self.compiler;
70 71 72 73

        builder.ensure(StartupObjects { compiler, target });

        if build.force_use_stage1(compiler, target) {
74
            let from = builder.compiler(1, build.build);
75 76
            builder.ensure(Std {
                compiler: from,
77
                target,
78 79
            });
            println!("Uplifting stage1 std ({} -> {})", from.host, target);
80 81 82 83 84 85 86 87

            // Even if we're not building std this stage, the new sysroot must
            // still contain the musl startup objects.
            if target.contains("musl") && !target.contains("mips") {
                let libdir = builder.sysroot_libdir(compiler, target);
                copy_musl_third_party_objects(build, target, &libdir);
            }

88 89 90
            builder.ensure(StdLink {
                compiler: from,
                target_compiler: compiler,
91
                target,
92 93 94
            });
            return;
        }
95 96 97

        let _folder = build.fold_output(|| format!("stage{}-std", compiler.stage));
        println!("Building stage{} std artifacts ({} -> {})", compiler.stage,
98
                &compiler.host, target);
99

100 101 102 103 104
        if target.contains("musl") && !target.contains("mips") {
            let libdir = builder.sysroot_libdir(compiler, target);
            copy_musl_third_party_objects(build, target, &libdir);
        }

105
        let out_dir = build.stage_out(compiler, Mode::Libstd);
106
        build.clear_if_dirty(&out_dir, &builder.rustc(compiler));
107
        let mut cargo = builder.cargo(compiler, Mode::Libstd, target, "build");
A
Alex Crichton 已提交
108
        std_cargo(build, &compiler, target, &mut cargo);
109 110
        run_cargo(build,
                &mut cargo,
111
                &libstd_stamp(build, compiler, target));
112 113

        builder.ensure(StdLink {
114
            compiler: builder.compiler(compiler.stage, build.build),
115
            target_compiler: compiler,
116
            target,
117
        });
118
    }
119 120
}

121 122 123 124 125 126 127 128 129 130 131 132 133 134
/// Copies the crt(1,i,n).o startup objects
///
/// Since musl supports fully static linking, we can cross link for it even
/// with a glibc-targeting toolchain, given we have the appropriate startup
/// files. As those shipped with glibc won't work, copy the ones provided by
/// musl so we have them on linux-gnu hosts.
fn copy_musl_third_party_objects(build: &Build,
                                 target: Interned<String>,
                                 into: &Path) {
    for &obj in &["crt1.o", "crti.o", "crtn.o"] {
        copy(&build.musl_root(target).unwrap().join("lib").join(obj), &into.join(obj));
    }
}

A
Alex Crichton 已提交
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181
/// Configure cargo to compile the standard library, adding appropriate env vars
/// and such.
pub fn std_cargo(build: &Build,
                 compiler: &Compiler,
                 target: Interned<String>,
                 cargo: &mut Command) {
    let mut features = build.std_features();

    if let Some(target) = env::var_os("MACOSX_STD_DEPLOYMENT_TARGET") {
        cargo.env("MACOSX_DEPLOYMENT_TARGET", target);
    }

    // When doing a local rebuild we tell cargo that we're stage1 rather than
    // stage0. This works fine if the local rust and being-built rust have the
    // same view of what the default allocator is, but fails otherwise. Since
    // we don't have a way to express an allocator preference yet, work
    // around the issue in the case of a local rebuild with jemalloc disabled.
    if compiler.stage == 0 && build.local_rebuild && !build.config.use_jemalloc {
        features.push_str(" force_alloc_system");
    }

    if compiler.stage != 0 && build.config.sanitizers {
        // This variable is used by the sanitizer runtime crates, e.g.
        // rustc_lsan, to build the sanitizer runtime from C code
        // When this variable is missing, those crates won't compile the C code,
        // so we don't set this variable during stage0 where llvm-config is
        // missing
        // We also only build the runtimes when --enable-sanitizers (or its
        // config.toml equivalent) is used
        cargo.env("LLVM_CONFIG", build.llvm_config(target));
    }

    cargo.arg("--features").arg(features)
        .arg("--manifest-path")
        .arg(build.src.join("src/libstd/Cargo.toml"));

    if let Some(target) = build.config.target_config.get(&target) {
        if let Some(ref jemalloc) = target.jemalloc {
            cargo.env("JEMALLOC_OVERRIDE", jemalloc);
        }
    }
    if target.contains("musl") {
        if let Some(p) = build.musl_root(target) {
            cargo.env("MUSL_ROOT", p);
        }
    }
}
M
Mark Simulacrum 已提交
182

183 184 185 186 187
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
struct StdLink {
    pub compiler: Compiler,
    pub target_compiler: Compiler,
    pub target: Interned<String>,
188 189
}

190
impl Step for StdLink {
191 192
    type Output = ();

193 194
    fn should_run(run: ShouldRun) -> ShouldRun {
        run.never()
195 196
    }

197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212
    /// Link all libstd rlibs/dylibs into the sysroot location.
    ///
    /// Links those artifacts generated by `compiler` to a the `stage` compiler's
    /// sysroot for the specified `host` and `target`.
    ///
    /// Note that this assumes that `compiler` has already generated the libstd
    /// libraries for `target`, and this method will find them in the relevant
    /// output directory.
    fn run(self, builder: &Builder) {
        let build = builder.build;
        let compiler = self.compiler;
        let target_compiler = self.target_compiler;
        let target = self.target;
        println!("Copying stage{} std from stage{} ({} -> {} / {})",
                target_compiler.stage,
                compiler.stage,
213
                &compiler.host,
214 215
                target_compiler.host,
                target);
216
        let libdir = builder.sysroot_libdir(target_compiler, target);
217 218 219 220 221 222 223 224
        add_to_sysroot(&libdir, &libstd_stamp(build, compiler, target));

        if build.config.sanitizers && compiler.stage != 0 && target == "x86_64-apple-darwin" {
            // The sanitizers are only built in stage1 or above, so the dylibs will
            // be missing in stage0 and causes panic. See the `std()` function above
            // for reason why the sanitizers are not built in stage0.
            copy_apple_sanitizer_dylibs(&build.native_dir(target), "osx", &libdir);
        }
225 226 227

        builder.ensure(tool::CleanTools {
            compiler: target_compiler,
228
            target,
229 230
            mode: Mode::Libstd,
        });
231
    }
232 233
}

234 235 236 237 238 239 240 241 242 243 244 245
fn copy_apple_sanitizer_dylibs(native_dir: &Path, platform: &str, into: &Path) {
    for &sanitizer in &["asan", "tsan"] {
        let filename = format!("libclang_rt.{}_{}_dynamic.dylib", sanitizer, platform);
        let mut src_path = native_dir.join(sanitizer);
        src_path.push("build");
        src_path.push("lib");
        src_path.push("darwin");
        src_path.push(&filename);
        copy(&src_path, &into.join(filename));
    }
}

246 247 248 249
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct StartupObjects {
    pub compiler: Compiler,
    pub target: Interned<String>,
250
}
251

252
impl Step for StartupObjects {
253 254
    type Output = ();

255 256
    fn should_run(run: ShouldRun) -> ShouldRun {
        run.path("src/rtstartup")
257 258
    }

259 260 261 262
    fn make_run(run: RunConfig) {
        run.builder.ensure(StartupObjects {
            compiler: run.builder.compiler(run.builder.top_stage, run.host),
            target: run.target,
263
        });
264 265
    }

266 267 268 269 270 271 272 273
    /// Build and prepare startup objects like rsbegin.o and rsend.o
    ///
    /// These are primarily used on Windows right now for linking executables/dlls.
    /// They don't require any library support as they're just plain old object
    /// files, so we just use the nightly snapshot compiler to always build them (as
    /// no other compilers are guaranteed to be available).
    fn run(self, builder: &Builder) {
        let build = builder.build;
274
        let for_compiler = self.compiler;
275 276 277
        let target = self.target;
        if !target.contains("pc-windows-gnu") {
            return
278 279
        }

280 281
        let src_dir = &build.src.join("src/rtstartup");
        let dst_dir = &build.native_dir(target).join("rtstartup");
282
        let sysroot_dir = &builder.sysroot_libdir(for_compiler, target);
283 284 285 286 287 288
        t!(fs::create_dir_all(dst_dir));

        for file in &["rsbegin", "rsend"] {
            let src_file = &src_dir.join(file.to_string() + ".rs");
            let dst_file = &dst_dir.join(file.to_string() + ".o");
            if !up_to_date(src_file, dst_file) {
M
Mark Simulacrum 已提交
289
                let mut cmd = Command::new(&build.initial_rustc);
290
                build.run(cmd.env("RUSTC_BOOTSTRAP", "1")
M
Mark Simulacrum 已提交
291
                            .arg("--cfg").arg("stage0")
292 293
                            .arg("--target").arg(target)
                            .arg("--emit=obj")
M
Mark Simulacrum 已提交
294
                            .arg("-o").arg(dst_file)
295 296
                            .arg(src_file));
            }
A
Alex Crichton 已提交
297

298 299 300 301 302 303
            copy(dst_file, &sysroot_dir.join(file.to_string() + ".o"));
        }

        for obj in ["crt2.o", "dllcrt2.o"].iter() {
            copy(&compiler_file(build.cc(target), obj), &sysroot_dir.join(obj));
        }
A
Alex Crichton 已提交
304 305 306
    }
}

307 308 309 310
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct Test {
    pub compiler: Compiler,
    pub target: Interned<String>,
311 312
}

313
impl Step for Test {
314
    type Output = ();
315 316
    const DEFAULT: bool = true;

317 318
    fn should_run(run: ShouldRun) -> ShouldRun {
        run.path("src/libtest").krate("test")
319 320
    }

321 322 323 324
    fn make_run(run: RunConfig) {
        run.builder.ensure(Test {
            compiler: run.builder.compiler(run.builder.top_stage, run.host),
            target: run.target,
325
        });
326
    }
327 328 329 330 331 332 333 334 335 336

    /// Build libtest.
    ///
    /// This will build libtest and supporting libraries for a particular stage of
    /// the build using the `compiler` targeting the `target` architecture. The
    /// artifacts created will also be linked into the sysroot directory.
    fn run(self, builder: &Builder) {
        let build = builder.build;
        let target = self.target;
        let compiler = self.compiler;
337 338 339 340 341

        builder.ensure(Std { compiler, target });

        if build.force_use_stage1(compiler, target) {
            builder.ensure(Test {
342
                compiler: builder.compiler(1, build.build),
343
                target,
344 345 346
            });
            println!("Uplifting stage1 test ({} -> {})", &build.build, target);
            builder.ensure(TestLink {
347
                compiler: builder.compiler(1, build.build),
348
                target_compiler: compiler,
349
                target,
350 351 352 353
            });
            return;
        }

354 355
        let _folder = build.fold_output(|| format!("stage{}-test", compiler.stage));
        println!("Building stage{} test artifacts ({} -> {})", compiler.stage,
356
                &compiler.host, target);
357
        let out_dir = build.stage_out(compiler, Mode::Libtest);
358
        build.clear_if_dirty(&out_dir, &libstd_stamp(build, compiler, target));
359
        let mut cargo = builder.cargo(compiler, Mode::Libtest, target, "build");
A
Alex Crichton 已提交
360
        test_cargo(build, &compiler, target, &mut cargo);
361 362 363
        run_cargo(build,
                &mut cargo,
                &libtest_stamp(build, compiler, target));
364 365

        builder.ensure(TestLink {
366
            compiler: builder.compiler(compiler.stage, build.build),
367
            target_compiler: compiler,
368
            target,
369
        });
P
Peter Wagenet 已提交
370
    }
371 372
}

A
Alex Crichton 已提交
373 374 375 376 377 378 379 380 381 382 383 384
/// Same as `std_cargo`, but for libtest
pub fn test_cargo(build: &Build,
                  _compiler: &Compiler,
                  _target: Interned<String>,
                  cargo: &mut Command) {
    if let Some(target) = env::var_os("MACOSX_STD_DEPLOYMENT_TARGET") {
        cargo.env("MACOSX_DEPLOYMENT_TARGET", target);
    }
    cargo.arg("--manifest-path")
        .arg(build.src.join("src/libtest/Cargo.toml"));
}

385 386 387 388 389
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct TestLink {
    pub compiler: Compiler,
    pub target_compiler: Compiler,
    pub target: Interned<String>,
390 391
}

392
impl Step for TestLink {
393 394
    type Output = ();

395 396
    fn should_run(run: ShouldRun) -> ShouldRun {
        run.never()
397 398
    }

399 400 401 402 403 404 405 406 407
    /// Same as `std_link`, only for libtest
    fn run(self, builder: &Builder) {
        let build = builder.build;
        let compiler = self.compiler;
        let target_compiler = self.target_compiler;
        let target = self.target;
        println!("Copying stage{} test from stage{} ({} -> {} / {})",
                target_compiler.stage,
                compiler.stage,
408
                &compiler.host,
409 410
                target_compiler.host,
                target);
411
        add_to_sysroot(&builder.sysroot_libdir(target_compiler, target),
412
                    &libtest_stamp(build, compiler, target));
413 414
        builder.ensure(tool::CleanTools {
            compiler: target_compiler,
415
            target,
416 417
            mode: Mode::Libtest,
        });
418
    }
419
}
A
Alex Crichton 已提交
420

421 422 423 424
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct Rustc {
    pub compiler: Compiler,
    pub target: Interned<String>,
425 426
}

427
impl Step for Rustc {
428
    type Output = ();
429 430 431
    const ONLY_HOSTS: bool = true;
    const DEFAULT: bool = true;

432 433
    fn should_run(run: ShouldRun) -> ShouldRun {
        run.path("src/librustc").krate("rustc-main")
434 435
    }

436 437 438 439
    fn make_run(run: RunConfig) {
        run.builder.ensure(Rustc {
            compiler: run.builder.compiler(run.builder.top_stage, run.host),
            target: run.target,
440
        });
441
    }
442 443 444 445 446 447 448 449 450 451

    /// Build the compiler.
    ///
    /// This will build the compiler for a particular stage of the build using
    /// the `compiler` targeting the `target` architecture. The artifacts
    /// created will also be linked into the sysroot directory.
    fn run(self, builder: &Builder) {
        let build = builder.build;
        let compiler = self.compiler;
        let target = self.target;
452 453 454 455 456 457 458 459 460

        builder.ensure(Test { compiler, target });

        // Build LLVM for our target. This will implicitly build the host LLVM
        // if necessary.
        builder.ensure(native::Llvm { target });

        if build.force_use_stage1(compiler, target) {
            builder.ensure(Rustc {
461
                compiler: builder.compiler(1, build.build),
462
                target,
463 464 465
            });
            println!("Uplifting stage1 rustc ({} -> {})", &build.build, target);
            builder.ensure(RustcLink {
466
                compiler: builder.compiler(1, build.build),
467 468 469 470 471 472 473 474
                target_compiler: compiler,
                target,
            });
            return;
        }

        // Ensure that build scripts have a std to link against.
        builder.ensure(Std {
475 476
            compiler: builder.compiler(self.compiler.stage, build.build),
            target: build.build,
477 478
        });

479 480
        let _folder = build.fold_output(|| format!("stage{}-rustc", compiler.stage));
        println!("Building stage{} compiler artifacts ({} -> {})",
481
                 compiler.stage, &compiler.host, target);
482

483 484 485
        let stage_out = builder.stage_out(compiler, Mode::Librustc);
        build.clear_if_dirty(&stage_out, &libstd_stamp(build, compiler, target));
        build.clear_if_dirty(&stage_out, &libtest_stamp(build, compiler, target));
486

487
        let mut cargo = builder.cargo(compiler, Mode::Librustc, target, "build");
A
Alex Crichton 已提交
488
        rustc_cargo(build, &compiler, target, &mut cargo);
489 490 491
        run_cargo(build,
                  &mut cargo,
                  &librustc_stamp(build, compiler, target));
492 493

        builder.ensure(RustcLink {
494
            compiler: builder.compiler(compiler.stage, build.build),
495 496 497
            target_compiler: compiler,
            target,
        });
A
Alex Crichton 已提交
498
    }
499 500
}

A
Alex Crichton 已提交
501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565
/// Same as `std_cargo`, but for libtest
pub fn rustc_cargo(build: &Build,
                   compiler: &Compiler,
                   target: Interned<String>,
                   cargo: &mut Command) {
    cargo.arg("--features").arg(build.rustc_features())
         .arg("--manifest-path")
         .arg(build.src.join("src/rustc/Cargo.toml"));

    // Set some configuration variables picked up by build scripts and
    // the compiler alike
    cargo.env("CFG_RELEASE", build.rust_release())
         .env("CFG_RELEASE_CHANNEL", &build.config.channel)
         .env("CFG_VERSION", build.rust_version())
         .env("CFG_PREFIX", build.config.prefix.clone().unwrap_or_default());

    if compiler.stage == 0 {
        cargo.env("CFG_LIBDIR_RELATIVE", "lib");
    } else {
        let libdir_relative =
            build.config.libdir_relative.clone().unwrap_or(PathBuf::from("lib"));
        cargo.env("CFG_LIBDIR_RELATIVE", libdir_relative);
    }

    // If we're not building a compiler with debugging information then remove
    // these two env vars which would be set otherwise.
    if build.config.rust_debuginfo_only_std {
        cargo.env_remove("RUSTC_DEBUGINFO");
        cargo.env_remove("RUSTC_DEBUGINFO_LINES");
    }

    if let Some(ref ver_date) = build.rust_info.commit_date() {
        cargo.env("CFG_VER_DATE", ver_date);
    }
    if let Some(ref ver_hash) = build.rust_info.sha() {
        cargo.env("CFG_VER_HASH", ver_hash);
    }
    if !build.unstable_features() {
        cargo.env("CFG_DISABLE_UNSTABLE_FEATURES", "1");
    }
    // Flag that rust llvm is in use
    if build.is_rust_llvm(target) {
        cargo.env("LLVM_RUSTLLVM", "1");
    }
    cargo.env("LLVM_CONFIG", build.llvm_config(target));
    let target_config = build.config.target_config.get(&target);
    if let Some(s) = target_config.and_then(|c| c.llvm_config.as_ref()) {
        cargo.env("CFG_LLVM_ROOT", s);
    }
    // Building with a static libstdc++ is only supported on linux right now,
    // not for MSVC or macOS
    if build.config.llvm_static_stdcpp &&
       !target.contains("windows") &&
       !target.contains("apple") {
        cargo.env("LLVM_STATIC_STDCPP",
                  compiler_file(build.cxx(target).unwrap(), "libstdc++.a"));
    }
    if build.config.llvm_link_shared {
        cargo.env("LLVM_LINK_SHARED", "1");
    }
    if let Some(ref s) = build.config.rustc_default_linker {
        cargo.env("CFG_DEFAULT_LINKER", s);
    }
}

566 567 568 569 570
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
struct RustcLink {
    pub compiler: Compiler,
    pub target_compiler: Compiler,
    pub target: Interned<String>,
571 572
}

573
impl Step for RustcLink {
574 575
    type Output = ();

576 577
    fn should_run(run: ShouldRun) -> ShouldRun {
        run.never()
578 579
    }

580 581 582 583 584 585 586 587 588
    /// Same as `std_link`, only for librustc
    fn run(self, builder: &Builder) {
        let build = builder.build;
        let compiler = self.compiler;
        let target_compiler = self.target_compiler;
        let target = self.target;
        println!("Copying stage{} rustc from stage{} ({} -> {} / {})",
                 target_compiler.stage,
                 compiler.stage,
589
                 &compiler.host,
590 591
                 target_compiler.host,
                 target);
592
        add_to_sysroot(&builder.sysroot_libdir(target_compiler, target),
593
                       &librustc_stamp(build, compiler, target));
594 595
        builder.ensure(tool::CleanTools {
            compiler: target_compiler,
596
            target,
597 598
            mode: Mode::Librustc,
        });
599
    }
A
Alex Crichton 已提交
600 601 602 603
}

/// Cargo's output path for the standard library in a given stage, compiled
/// by a particular compiler for the specified target.
604
pub fn libstd_stamp(build: &Build, compiler: Compiler, target: Interned<String>) -> PathBuf {
605
    build.cargo_out(compiler, Mode::Libstd, target).join(".libstd.stamp")
A
Alex Crichton 已提交
606 607
}

608 609
/// Cargo's output path for libtest in a given stage, compiled by a particular
/// compiler for the specified target.
610
pub fn libtest_stamp(build: &Build, compiler: Compiler, target: Interned<String>) -> PathBuf {
611
    build.cargo_out(compiler, Mode::Libtest, target).join(".libtest.stamp")
612 613
}

614 615
/// Cargo's output path for librustc in a given stage, compiled by a particular
/// compiler for the specified target.
616
pub fn librustc_stamp(build: &Build, compiler: Compiler, target: Interned<String>) -> PathBuf {
617 618 619
    build.cargo_out(compiler, Mode::Librustc, target).join(".librustc.stamp")
}

620 621 622 623
fn compiler_file(compiler: &Path, file: &str) -> PathBuf {
    let out = output(Command::new(compiler)
                            .arg(format!("-print-file-name={}", file)));
    PathBuf::from(out.trim())
A
Alex Crichton 已提交
624 625
}

626 627 628
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct Sysroot {
    pub compiler: Compiler,
629 630
}

631 632
impl Step for Sysroot {
    type Output = Interned<PathBuf>;
633

634 635
    fn should_run(run: ShouldRun) -> ShouldRun {
        run.never()
636 637
    }

638 639 640 641 642 643
    /// Returns the sysroot for the `compiler` specified that *this build system
    /// generates*.
    ///
    /// That is, the sysroot for the stage0 compiler is not what the compiler
    /// thinks it is by default, but it's the same as the default for stages
    /// 1-3.
644
    fn run(self, builder: &Builder) -> Interned<PathBuf> {
645 646
        let build = builder.build;
        let compiler = self.compiler;
647
        let sysroot = if compiler.stage == 0 {
648
            build.out.join(&compiler.host).join("stage0-sysroot")
649
        } else {
650
            build.out.join(&compiler.host).join(format!("stage{}", compiler.stage))
651
        };
652 653
        let _ = fs::remove_dir_all(&sysroot);
        t!(fs::create_dir_all(&sysroot));
654
        INTERNER.intern_path(sysroot)
655
    }
656 657
}

658 659
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct Assemble {
660 661 662 663
    /// The compiler which we will produce in this step. Assemble itself will
    /// take care of ensuring that the necessary prerequisites to do so exist,
    /// that is, this target can be a stage2 compiler and Assemble will build
    /// previous stages for you.
664
    pub target_compiler: Compiler,
665
}
666

667 668
impl Step for Assemble {
    type Output = Compiler;
669

670 671
    fn should_run(run: ShouldRun) -> ShouldRun {
        run.path("src/rustc")
672 673
    }

674 675 676 677 678
    /// Prepare a new compiler from the artifacts in `stage`
    ///
    /// This will assemble a compiler in `build/$host/stage$stage`. The compiler
    /// must have been previously produced by the `stage - 1` build.build
    /// compiler.
679
    fn run(self, builder: &Builder) -> Compiler {
680
        let build = builder.build;
681
        let target_compiler = self.target_compiler;
682

683 684 685 686 687 688
        if target_compiler.stage == 0 {
            assert_eq!(build.build, target_compiler.host,
                "Cannot obtain compiler for non-native build triple at stage 0");
            // The stage 0 compiler for the build triple is always pre-built.
            return target_compiler;
        }
689

690 691 692 693 694 695
        // Get the compiler that we'll use to bootstrap ourselves.
        let build_compiler = if target_compiler.host != build.build {
            // Build a compiler for the host platform. We cannot use the stage0
            // compiler for the host platform for this because it doesn't have
            // the libraries we need.  FIXME: Perhaps we should download those
            // libraries? It would make builds faster...
696 697 698 699
            // FIXME: It may be faster if we build just a stage 1
            // compiler and then use that to bootstrap this compiler
            // forward.
            builder.compiler(target_compiler.stage - 1, build.build)
700 701 702
        } else {
            // Build the compiler we'll use to build the stage requested. This
            // may build more than one compiler (going down to stage 0).
703
            builder.compiler(target_compiler.stage - 1, target_compiler.host)
704 705 706 707 708 709 710
        };

        // Build the libraries for this compiler to link to (i.e., the libraries
        // it uses at runtime). NOTE: Crates the target compiler compiles don't
        // link to these. (FIXME: Is that correct? It seems to be correct most
        // of the time but I think we do link to these for stage2/bin compilers
        // when not performing a full bootstrap).
M
Mark Simulacrum 已提交
711
        if builder.build.config.keep_stage.map_or(false, |s| target_compiler.stage <= s) {
M
Mark Simulacrum 已提交
712 713
            builder.verbose("skipping compilation of compiler due to --keep-stage");
            let compiler = build_compiler;
M
Mark Simulacrum 已提交
714
            for stage in 0..min(target_compiler.stage, builder.config.keep_stage.unwrap()) {
M
Mark Simulacrum 已提交
715
                let target_compiler = builder.compiler(stage, target_compiler.host);
M
Mark Simulacrum 已提交
716 717 718 719
                let target = target_compiler.host;
                builder.ensure(StdLink { compiler, target_compiler, target });
                builder.ensure(TestLink { compiler, target_compiler, target });
                builder.ensure(RustcLink { compiler, target_compiler, target });
M
Mark Simulacrum 已提交
720 721 722 723
            }
        } else {
            builder.ensure(Rustc { compiler: build_compiler, target: target_compiler.host });
        }
724 725 726 727

        let stage = target_compiler.stage;
        let host = target_compiler.host;
        println!("Assembling stage{} compiler ({})", stage, host);
728 729

        // Link in all dylibs to the libdir
730
        let sysroot = builder.sysroot(target_compiler);
731
        let sysroot_libdir = sysroot.join(libdir(&*host));
732
        t!(fs::create_dir_all(&sysroot_libdir));
733
        let src_libdir = builder.sysroot_libdir(build_compiler, host);
734 735 736 737 738
        for f in t!(fs::read_dir(&src_libdir)).map(|f| t!(f)) {
            let filename = f.file_name().into_string().unwrap();
            if is_dylib(&filename) {
                copy(&f.path(), &sysroot_libdir.join(&filename));
            }
A
Alex Crichton 已提交
739 740
        }

741
        let out_dir = build.cargo_out(build_compiler, Mode::Librustc, host);
742 743

        // Link the compiler binary itself into place
744
        let rustc = out_dir.join(exe("rustc", &*host));
745 746
        let bindir = sysroot.join("bin");
        t!(fs::create_dir_all(&bindir));
747
        let compiler = builder.rustc(target_compiler);
748 749 750
        let _ = fs::remove_file(&compiler);
        copy(&rustc, &compiler);

751
        target_compiler
A
Alex Crichton 已提交
752 753 754 755 756
    }
}

/// Link some files into a rustc sysroot.
///
757 758 759 760
/// For a particular stage this will link the file listed in `stamp` into the
/// `sysroot_dst` provided.
fn add_to_sysroot(sysroot_dst: &Path, stamp: &Path) {
    t!(fs::create_dir_all(&sysroot_dst));
761
    for path in read_stamp_file(stamp) {
762
        copy(&path, &sysroot_dst.join(path.file_name().unwrap()));
A
Alex Crichton 已提交
763 764
    }
}
765

M
Mark Simulacrum 已提交
766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788
// Avoiding a dependency on winapi to keep compile times down
#[cfg(unix)]
fn stderr_isatty() -> bool {
    use libc;
    unsafe { libc::isatty(libc::STDERR_FILENO) != 0 }
}
#[cfg(windows)]
fn stderr_isatty() -> bool {
    type DWORD = u32;
    type BOOL = i32;
    type HANDLE = *mut u8;
    const STD_ERROR_HANDLE: DWORD = -12i32 as DWORD;
    extern "system" {
        fn GetStdHandle(which: DWORD) -> HANDLE;
        fn GetConsoleMode(hConsoleHandle: HANDLE, lpMode: *mut DWORD) -> BOOL;
    }
    unsafe {
        let handle = GetStdHandle(STD_ERROR_HANDLE);
        let mut out = 0;
        GetConsoleMode(handle, &mut out) != 0
    }
}

789 790 791 792 793
fn run_cargo(build: &Build, cargo: &mut Command, stamp: &Path) {
    // Instruct Cargo to give us json messages on stdout, critically leaving
    // stderr as piped so we can get those pretty colors.
    cargo.arg("--message-format").arg("json")
         .stdout(Stdio::piped());
M
Mark Simulacrum 已提交
794

795
    if stderr_isatty() && build.ci_env == CiEnv::None {
M
Mark Simulacrum 已提交
796 797 798 799 800 801
        // since we pass message-format=json to cargo, we need to tell the rustc
        // wrapper to give us colored output if necessary. This is because we
        // only want Cargo's JSON output, not rustcs.
        cargo.env("RUSTC_COLOR", "1");
    }

802 803 804 805
    build.verbose(&format!("running: {:?}", cargo));
    let mut child = match cargo.spawn() {
        Ok(child) => child,
        Err(e) => panic!("failed to execute command: {:?}\nerror: {}", cargo, e),
806 807
    };

808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824
    // `target_root_dir` looks like $dir/$target/release
    let target_root_dir = stamp.parent().unwrap();
    // `target_deps_dir` looks like $dir/$target/release/deps
    let target_deps_dir = target_root_dir.join("deps");
    // `host_root_dir` looks like $dir/release
    let host_root_dir = target_root_dir.parent().unwrap() // chop off `release`
                                       .parent().unwrap() // chop off `$target`
                                       .join(target_root_dir.file_name().unwrap());

    // Spawn Cargo slurping up its JSON output. We'll start building up the
    // `deps` array of all files it generated along with a `toplevel` array of
    // files we need to probe for later.
    let mut deps = Vec::new();
    let mut toplevel = Vec::new();
    let stdout = BufReader::new(child.stdout.take().unwrap());
    for line in stdout.lines() {
        let line = t!(line);
M
Mark Simulacrum 已提交
825 826
        let json: serde_json::Value = if line.starts_with("{") {
            t!(serde_json::from_str(&line))
827 828 829 830 831
        } else {
            // If this was informational, just print it out and continue
            println!("{}", line);
            continue
        };
M
Mark Simulacrum 已提交
832
        if json["reason"].as_str() != Some("compiler-artifact") {
833 834 835
            continue
        }
        for filename in json["filenames"].as_array().unwrap() {
M
Mark Simulacrum 已提交
836
            let filename = filename.as_str().unwrap();
837 838 839 840 841 842 843 844 845 846 847 848
            // Skip files like executables
            if !filename.ends_with(".rlib") &&
               !filename.ends_with(".lib") &&
               !is_dylib(&filename) {
                continue
            }

            let filename = Path::new(filename);

            // If this was an output file in the "host dir" we don't actually
            // worry about it, it's not relevant for us.
            if filename.starts_with(&host_root_dir) {
M
Mark Simulacrum 已提交
849 850
                continue;
            }
851 852 853

            // If this was output in the `deps` dir then this is a precise file
            // name (hash included) so we start tracking it.
M
Mark Simulacrum 已提交
854
            if filename.starts_with(&target_deps_dir) {
855
                deps.push(filename.to_path_buf());
M
Mark Simulacrum 已提交
856 857
                continue;
            }
858 859 860 861 862

            // Otherwise this was a "top level artifact" which right now doesn't
            // have a hash in the name, but there's a version of this file in
            // the `deps` folder which *does* have a hash in the name. That's
            // the one we'll want to we'll probe for it later.
M
Mark Simulacrum 已提交
863 864 865 866
            toplevel.push((filename.file_stem().unwrap()
                                    .to_str().unwrap().to_string(),
                            filename.extension().unwrap().to_owned()
                                    .to_str().unwrap().to_string()));
867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933
        }
    }

    // Make sure Cargo actually succeeded after we read all of its stdout.
    let status = t!(child.wait());
    if !status.success() {
        panic!("command did not execute successfully: {:?}\n\
                expected success, got: {}",
               cargo,
               status);
    }

    // Ok now we need to actually find all the files listed in `toplevel`. We've
    // got a list of prefix/extensions and we basically just need to find the
    // most recent file in the `deps` folder corresponding to each one.
    let contents = t!(target_deps_dir.read_dir())
        .map(|e| t!(e))
        .map(|e| (e.path(), e.file_name().into_string().unwrap(), t!(e.metadata())))
        .collect::<Vec<_>>();
    for (prefix, extension) in toplevel {
        let candidates = contents.iter().filter(|&&(_, ref filename, _)| {
            filename.starts_with(&prefix[..]) &&
                filename[prefix.len()..].starts_with("-") &&
                filename.ends_with(&extension[..])
        });
        let max = candidates.max_by_key(|&&(_, _, ref metadata)| {
            FileTime::from_last_modification_time(metadata)
        });
        let path_to_add = match max {
            Some(triple) => triple.0.to_str().unwrap(),
            None => panic!("no output generated for {:?} {:?}", prefix, extension),
        };
        if is_dylib(path_to_add) {
            let candidate = format!("{}.lib", path_to_add);
            let candidate = PathBuf::from(candidate);
            if candidate.exists() {
                deps.push(candidate);
            }
        }
        deps.push(path_to_add.into());
    }

    // Now we want to update the contents of the stamp file, if necessary. First
    // we read off the previous contents along with its mtime. If our new
    // contents (the list of files to copy) is different or if any dep's mtime
    // is newer then we rewrite the stamp file.
    deps.sort();
    let mut stamp_contents = Vec::new();
    if let Ok(mut f) = File::open(stamp) {
        t!(f.read_to_end(&mut stamp_contents));
    }
    let stamp_mtime = mtime(&stamp);
    let mut new_contents = Vec::new();
    let mut max = None;
    let mut max_path = None;
    for dep in deps {
        let mtime = mtime(&dep);
        if Some(mtime) > max {
            max = Some(mtime);
            max_path = Some(dep.clone());
        }
        new_contents.extend(dep.to_str().unwrap().as_bytes());
        new_contents.extend(b"\0");
    }
    let max = max.unwrap();
    let max_path = max_path.unwrap();
    if stamp_contents == new_contents && max <= stamp_mtime {
934 935
        build.verbose(&format!("not updating {:?}; contents equal and {} <= {}",
                stamp, max, stamp_mtime));
936 937 938 939 940 941
        return
    }
    if max > stamp_mtime {
        build.verbose(&format!("updating {:?} as {:?} changed", stamp, max_path));
    } else {
        build.verbose(&format!("updating {:?} as deps changed", stamp));
942
    }
943
    t!(t!(File::create(stamp)).write_all(&new_contents));
944
}