compile.rs 33.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 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};
33
use {Build, Compiler, Mode};
34 35
use native;

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

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

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

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

53 54 55 56 57 58
    fn make_run(
        builder: &Builder,
        _path: Option<&Path>,
        host: Interned<String>,
        target: Interned<String>,
    ) {
59 60 61
        builder.ensure(Std {
            compiler: builder.compiler(builder.top_stage, host),
            target,
62
        });
63
    }
64 65 66 67 68 69 70 71 72 73

    /// 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;
74 75 76 77

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

        if build.force_use_stage1(compiler, target) {
78
            let from = builder.compiler(1, build.build);
79 80 81 82 83 84 85 86 87 88 89 90
            builder.ensure(Std {
                compiler: from,
                target: target,
            });
            println!("Uplifting stage1 std ({} -> {})", from.host, target);
            builder.ensure(StdLink {
                compiler: from,
                target_compiler: compiler,
                target: target,
            });
            return;
        }
91 92 93

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

        let out_dir = build.cargo_out(compiler, Mode::Libstd, target);
97
        build.clear_if_dirty(&out_dir, &builder.rustc(compiler));
98
        let mut cargo = builder.cargo(compiler, Mode::Libstd, target, "build");
99 100 101 102 103
        let mut features = build.std_features();

        if let Some(target) = env::var_os("MACOSX_STD_DEPLOYMENT_TARGET") {
            cargo.env("MACOSX_DEPLOYMENT_TARGET", target);
        }
J
Jorge Aparicio 已提交
104

105 106 107 108 109 110 111 112
        // 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");
        }
A
Alex Crichton 已提交
113

114 115 116 117 118 119 120 121 122
        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));
A
Alex Crichton 已提交
123
        }
124

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

129
        if let Some(target) = build.config.target_config.get(&target) {
130 131 132 133 134 135 136 137
            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);
            }
A
Alex Crichton 已提交
138 139
        }

140 141
        run_cargo(build,
                &mut cargo,
142
                &libstd_stamp(build, compiler, target));
143 144

        builder.ensure(StdLink {
145
            compiler: builder.compiler(compiler.stage, build.build),
146 147 148
            target_compiler: compiler,
            target: target,
        });
149
    }
150 151
}

M
Mark Simulacrum 已提交
152

153 154 155 156 157
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
struct StdLink {
    pub compiler: Compiler,
    pub target_compiler: Compiler,
    pub target: Interned<String>,
158 159
}

160
impl Step for StdLink {
161 162
    type Output = ();

163 164
    fn should_run(run: ShouldRun) -> ShouldRun {
        run.never()
165 166
    }

167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
    /// 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,
183
                &compiler.host,
184 185
                target_compiler.host,
                target);
186
        let libdir = builder.sysroot_libdir(target_compiler, target);
187 188 189 190 191 192 193 194 195 196 197 198
        add_to_sysroot(&libdir, &libstd_stamp(build, compiler, target));

        if target.contains("musl") && !target.contains("mips") {
            copy_musl_third_party_objects(build, target, &libdir);
        }

        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);
        }
199
    }
200 201 202 203 204
}

/// Copies the crt(1,i,n).o startup objects
///
/// Only required for musl targets that statically link to libc
205
fn copy_musl_third_party_objects(build: &Build, target: Interned<String>, into: &Path) {
206
    for &obj in &["crt1.o", "crti.o", "crtn.o"] {
207
        copy(&build.musl_root(target).unwrap().join("lib").join(obj), &into.join(obj));
208
    }
A
Alex Crichton 已提交
209 210
}

211 212 213 214 215 216 217 218 219 220 221 222
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));
    }
}

223 224 225 226
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct StartupObjects {
    pub compiler: Compiler,
    pub target: Interned<String>,
227
}
228

229
impl Step for StartupObjects {
230 231
    type Output = ();

232 233
    fn should_run(run: ShouldRun) -> ShouldRun {
        run.path("src/rtstartup")
234 235
    }

236 237 238 239 240 241
    fn make_run(
        builder: &Builder,
        _path: Option<&Path>,
        host: Interned<String>,
        target: Interned<String>,
    ) {
242 243 244
        builder.ensure(StartupObjects {
            compiler: builder.compiler(builder.top_stage, host),
            target,
245
        });
246 247
    }

248 249 250 251 252 253 254 255
    /// 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;
256
        let for_compiler = self.compiler;
257 258 259
        let target = self.target;
        if !target.contains("pc-windows-gnu") {
            return
260 261
        }

262 263
        let src_dir = &build.src.join("src/rtstartup");
        let dst_dir = &build.native_dir(target).join("rtstartup");
264
        let sysroot_dir = &builder.sysroot_libdir(for_compiler, target);
265 266 267 268 269 270
        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 已提交
271
                let mut cmd = Command::new(&build.initial_rustc);
272
                build.run(cmd.env("RUSTC_BOOTSTRAP", "1")
M
Mark Simulacrum 已提交
273
                            .arg("--cfg").arg("stage0")
274 275
                            .arg("--target").arg(target)
                            .arg("--emit=obj")
M
Mark Simulacrum 已提交
276
                            .arg("--o").arg(dst_file)
277 278
                            .arg(src_file));
            }
A
Alex Crichton 已提交
279

280 281 282 283 284 285
            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 已提交
286 287 288
    }
}

289 290 291 292
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct Test {
    pub compiler: Compiler,
    pub target: Interned<String>,
293 294
}

295
impl Step for Test {
296
    type Output = ();
297 298
    const DEFAULT: bool = true;

299 300
    fn should_run(run: ShouldRun) -> ShouldRun {
        run.path("src/libtest").krate("test")
301 302
    }

303 304 305 306 307 308
    fn make_run(
        builder: &Builder,
        _path: Option<&Path>,
        host: Interned<String>,
        target: Interned<String>,
    ) {
309 310 311
        builder.ensure(Test {
            compiler: builder.compiler(builder.top_stage, host),
            target,
312
        });
313
    }
314 315 316 317 318 319 320 321 322 323

    /// 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;
324 325 326 327 328

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

        if build.force_use_stage1(compiler, target) {
            builder.ensure(Test {
329
                compiler: builder.compiler(1, build.build),
330 331 332 333
                target: target,
            });
            println!("Uplifting stage1 test ({} -> {})", &build.build, target);
            builder.ensure(TestLink {
334
                compiler: builder.compiler(1, build.build),
335 336 337 338 339 340
                target_compiler: compiler,
                target: target,
            });
            return;
        }

341 342
        let _folder = build.fold_output(|| format!("stage{}-test", compiler.stage));
        println!("Building stage{} test artifacts ({} -> {})", compiler.stage,
343
                &compiler.host, target);
344 345
        let out_dir = build.cargo_out(compiler, Mode::Libtest, target);
        build.clear_if_dirty(&out_dir, &libstd_stamp(build, compiler, target));
346
        let mut cargo = builder.cargo(compiler, Mode::Libtest, target, "build");
347 348 349 350 351 352 353 354
        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"));
        run_cargo(build,
                &mut cargo,
                &libtest_stamp(build, compiler, target));
355 356

        builder.ensure(TestLink {
357
            compiler: builder.compiler(compiler.stage, build.build),
358 359 360
            target_compiler: compiler,
            target: target,
        });
P
Peter Wagenet 已提交
361
    }
362 363
}

364 365 366 367 368
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct TestLink {
    pub compiler: Compiler,
    pub target_compiler: Compiler,
    pub target: Interned<String>,
369 370
}

371
impl Step for TestLink {
372 373
    type Output = ();

374 375
    fn should_run(run: ShouldRun) -> ShouldRun {
        run.never()
376 377
    }

378 379 380 381 382 383 384 385 386
    /// 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,
387
                &compiler.host,
388 389
                target_compiler.host,
                target);
390
        add_to_sysroot(&builder.sysroot_libdir(target_compiler, target),
391
                    &libtest_stamp(build, compiler, target));
392
    }
393
}
A
Alex Crichton 已提交
394

395 396 397 398
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct Rustc {
    pub compiler: Compiler,
    pub target: Interned<String>,
399 400
}

401
impl Step for Rustc {
402
    type Output = ();
403 404 405
    const ONLY_HOSTS: bool = true;
    const DEFAULT: bool = true;

406 407
    fn should_run(run: ShouldRun) -> ShouldRun {
        run.path("src/librustc").krate("rustc-main")
408 409
    }

410 411 412 413 414 415
    fn make_run(
        builder: &Builder,
        _path: Option<&Path>,
        host: Interned<String>,
        target: Interned<String>,
    ) {
416 417 418
        builder.ensure(Rustc {
            compiler: builder.compiler(builder.top_stage, host),
            target,
419
        });
420
    }
421 422 423 424 425 426 427 428 429 430

    /// 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;
431 432 433 434 435 436 437 438 439

        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 {
440
                compiler: builder.compiler(1, build.build),
441 442 443 444
                target: target,
            });
            println!("Uplifting stage1 rustc ({} -> {})", &build.build, target);
            builder.ensure(RustcLink {
445
                compiler: builder.compiler(1, build.build),
446 447 448 449 450 451 452 453
                target_compiler: compiler,
                target,
            });
            return;
        }

        // Ensure that build scripts have a std to link against.
        builder.ensure(Std {
454 455
            compiler: builder.compiler(self.compiler.stage, build.build),
            target: build.build,
456 457
        });

458 459
        let _folder = build.fold_output(|| format!("stage{}-rustc", compiler.stage));
        println!("Building stage{} compiler artifacts ({} -> {})",
460
                 compiler.stage, &compiler.host, target);
461 462 463 464

        let out_dir = build.cargo_out(compiler, Mode::Librustc, target);
        build.clear_if_dirty(&out_dir, &libtest_stamp(build, compiler, target));

465
        let mut cargo = builder.cargo(compiler, Mode::Librustc, target, "build");
466 467 468 469 470 471 472 473 474 475 476 477 478 479
        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 {
M
Mark Simulacrum 已提交
480 481
            let libdir_relative =
                build.config.libdir_relative.clone().unwrap_or(PathBuf::from("lib"));
482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505
            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));
506
        let target_config = build.config.target_config.get(&target);
507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529
        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);
        }
        if let Some(ref s) = build.config.rustc_default_ar {
            cargo.env("CFG_DEFAULT_AR", s);
        }
        run_cargo(build,
                  &mut cargo,
                  &librustc_stamp(build, compiler, target));
530 531

        builder.ensure(RustcLink {
532
            compiler: builder.compiler(compiler.stage, build.build),
533 534 535
            target_compiler: compiler,
            target,
        });
A
Alex Crichton 已提交
536
    }
537 538
}

539 540 541 542 543
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
struct RustcLink {
    pub compiler: Compiler,
    pub target_compiler: Compiler,
    pub target: Interned<String>,
544 545
}

546
impl Step for RustcLink {
547 548
    type Output = ();

549 550
    fn should_run(run: ShouldRun) -> ShouldRun {
        run.never()
551 552
    }

553 554 555 556 557 558 559 560 561
    /// 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,
562
                 &compiler.host,
563 564
                 target_compiler.host,
                 target);
565
        add_to_sysroot(&builder.sysroot_libdir(target_compiler, target),
566 567
                       &librustc_stamp(build, compiler, target));
    }
A
Alex Crichton 已提交
568 569 570 571
}

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

576 577
/// Cargo's output path for libtest in a given stage, compiled by a particular
/// compiler for the specified target.
578
pub fn libtest_stamp(build: &Build, compiler: Compiler, target: Interned<String>) -> PathBuf {
579
    build.cargo_out(compiler, Mode::Libtest, target).join(".libtest.stamp")
580 581
}

582 583
/// Cargo's output path for librustc in a given stage, compiled by a particular
/// compiler for the specified target.
584
pub fn librustc_stamp(build: &Build, compiler: Compiler, target: Interned<String>) -> PathBuf {
585 586 587
    build.cargo_out(compiler, Mode::Librustc, target).join(".librustc.stamp")
}

588 589 590 591
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 已提交
592 593
}

594 595 596
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct Sysroot {
    pub compiler: Compiler,
597 598
}

599 600
impl Step for Sysroot {
    type Output = Interned<PathBuf>;
601

602 603
    fn should_run(run: ShouldRun) -> ShouldRun {
        run.never()
604 605
    }

606 607 608 609 610 611
    /// 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.
612
    fn run(self, builder: &Builder) -> Interned<PathBuf> {
613 614
        let build = builder.build;
        let compiler = self.compiler;
615
        let sysroot = if compiler.stage == 0 {
616
            build.out.join(&compiler.host).join("stage0-sysroot")
617
        } else {
618
            build.out.join(&compiler.host).join(format!("stage{}", compiler.stage))
619
        };
620 621
        let _ = fs::remove_dir_all(&sysroot);
        t!(fs::create_dir_all(&sysroot));
622
        INTERNER.intern_path(sysroot)
623
    }
624 625
}

626 627
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct Assemble {
628 629 630 631
    /// 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.
632
    pub target_compiler: Compiler,
633
}
634

635 636
impl Step for Assemble {
    type Output = Compiler;
637

638 639
    fn should_run(run: ShouldRun) -> ShouldRun {
        run.path("src/rustc")
640 641
    }

642 643 644 645 646
    /// 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.
647
    fn run(self, builder: &Builder) -> Compiler {
648
        let build = builder.build;
649
        let target_compiler = self.target_compiler;
650

651 652 653 654 655 656
        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;
        }
657

658 659 660 661 662 663
        // 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...
664 665 666 667
            // 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)
668 669 670
        } else {
            // Build the compiler we'll use to build the stage requested. This
            // may build more than one compiler (going down to stage 0).
671
            builder.compiler(target_compiler.stage - 1, target_compiler.host)
672 673 674 675 676 677 678
        };

        // 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 已提交
679 680 681 682 683
        if builder.build.flags.keep_stage.map_or(false, |s| target_compiler.stage <= s) {
            builder.verbose("skipping compilation of compiler due to --keep-stage");
            let compiler = build_compiler;
            for stage in 0..min(target_compiler.stage, builder.flags.keep_stage.unwrap()) {
                let target_compiler = builder.compiler(stage, target_compiler.host);
M
Mark Simulacrum 已提交
684 685 686 687
                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 已提交
688 689 690 691
            }
        } else {
            builder.ensure(Rustc { compiler: build_compiler, target: target_compiler.host });
        }
692 693 694 695

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

        // Link in all dylibs to the libdir
698
        let sysroot = builder.sysroot(target_compiler);
699
        let sysroot_libdir = sysroot.join(libdir(&*host));
700
        t!(fs::create_dir_all(&sysroot_libdir));
701
        let src_libdir = builder.sysroot_libdir(build_compiler, host);
702 703 704 705 706
        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 已提交
707 708
        }

709
        let out_dir = build.cargo_out(build_compiler, Mode::Librustc, host);
710 711

        // Link the compiler binary itself into place
712
        let rustc = out_dir.join(exe("rustc", &*host));
713 714
        let bindir = sysroot.join("bin");
        t!(fs::create_dir_all(&bindir));
715
        let compiler = builder.rustc(target_compiler);
716 717 718 719
        let _ = fs::remove_file(&compiler);
        copy(&rustc, &compiler);

        // See if rustdoc exists to link it into place
720
        let rustdoc = exe("rustdoc", &*host);
721 722 723 724 725 726
        let rustdoc_src = out_dir.join(&rustdoc);
        let rustdoc_dst = bindir.join(&rustdoc);
        if fs::metadata(&rustdoc_src).is_ok() {
            let _ = fs::remove_file(&rustdoc_dst);
            copy(&rustdoc_src, &rustdoc_dst);
        }
727 728

        target_compiler
A
Alex Crichton 已提交
729 730 731 732 733
    }
}

/// Link some files into a rustc sysroot.
///
734 735 736 737 738 739
/// 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));
    let mut contents = Vec::new();
    t!(t!(File::open(stamp)).read_to_end(&mut contents));
M
Mark Simulacrum 已提交
740 741
    // This is the method we use for extracting paths from the stamp file passed to us. See
    // run_cargo for more information (in this file).
742 743
    for part in contents.split(|b| *b == 0) {
        if part.is_empty() {
A
Alex Crichton 已提交
744 745
            continue
        }
746
        let path = Path::new(t!(str::from_utf8(part)));
747
        copy(&path, &sysroot_dst.join(path.file_name().unwrap()));
A
Alex Crichton 已提交
748 749
    }
}
750

M
Mark Simulacrum 已提交
751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773
// 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
    }
}

774 775 776 777 778
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 已提交
779 780 781 782 783 784 785 786

    if stderr_isatty() {
        // 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");
    }

787 788 789 790
    build.verbose(&format!("running: {:?}", cargo));
    let mut child = match cargo.spawn() {
        Ok(child) => child,
        Err(e) => panic!("failed to execute command: {:?}\nerror: {}", cargo, e),
791 792
    };

793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809
    // `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 已提交
810 811
        let json: serde_json::Value = if line.starts_with("{") {
            t!(serde_json::from_str(&line))
812 813 814 815 816
        } else {
            // If this was informational, just print it out and continue
            println!("{}", line);
            continue
        };
M
Mark Simulacrum 已提交
817
        if json["reason"].as_str() != Some("compiler-artifact") {
818 819 820
            continue
        }
        for filename in json["filenames"].as_array().unwrap() {
M
Mark Simulacrum 已提交
821
            let filename = filename.as_str().unwrap();
822 823 824 825 826 827 828 829 830 831 832 833
            // 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 已提交
834 835
                continue;
            }
836 837 838

            // 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 已提交
839
            if filename.starts_with(&target_deps_dir) {
840
                deps.push(filename.to_path_buf());
M
Mark Simulacrum 已提交
841 842
                continue;
            }
843 844 845 846 847

            // 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 已提交
848 849 850 851
            toplevel.push((filename.file_stem().unwrap()
                                    .to_str().unwrap().to_string(),
                            filename.extension().unwrap().to_owned()
                                    .to_str().unwrap().to_string()));
852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 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
        }
    }

    // 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 {
        return
    }
    if max > stamp_mtime {
        build.verbose(&format!("updating {:?} as {:?} changed", stamp, max_path));
    } else {
        build.verbose(&format!("updating {:?} as deps changed", stamp));
925
    }
926
    t!(t!(File::create(stamp)).write_all(&new_contents));
927
}