]> git.proxmox.com Git - rustc.git/blobdiff - src/bootstrap/compile.rs
New upstream version 1.47.0~beta.2+dfsg1
[rustc.git] / src / bootstrap / compile.rs
index e6aa78fba52ffd21209eef963e3cde30fdad6a72..373e240cb8e3e456018e8eec30688443d2425d40 100644 (file)
@@ -1,13 +1,3 @@
-// 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.
-
 //! Implementation of compiling various phases of the compiler and standard
 //! library.
 //!
 
 use std::borrow::Cow;
 use std::env;
-use std::fs::{self, File};
-use std::io::BufReader;
+use std::fs;
 use std::io::prelude::*;
+use std::io::BufReader;
 use std::path::{Path, PathBuf};
-use std::process::{Command, Stdio};
+use std::process::{exit, Command, Stdio};
 use std::str;
-use std::cmp::min;
 
-use build_helper::{output, mtime, up_to_date};
+use build_helper::{output, t, up_to_date};
 use filetime::FileTime;
-use serde_json;
-
-use util::{exe, libdir, is_dylib, copy, read_stamp_file, CiEnv};
-use {Build, Compiler, Mode};
-use native;
-use tool;
-
-use cache::{INTERNER, Interned};
-use builder::{Step, RunConfig, ShouldRun, Builder};
-
-#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
+use serde::Deserialize;
+
+use crate::builder::Cargo;
+use crate::builder::{Builder, Kind, RunConfig, ShouldRun, Step};
+use crate::cache::{Interned, INTERNER};
+use crate::config::TargetSelection;
+use crate::dist;
+use crate::native;
+use crate::tool::SourceType;
+use crate::util::{exe, is_dylib, symlink_dir};
+use crate::{Compiler, DependencyType, GitRepo, Mode};
+
+#[derive(Debug, PartialOrd, Ord, Copy, Clone, PartialEq, Eq, Hash)]
 pub struct Std {
-    pub target: Interned<String>,
+    pub target: TargetSelection,
     pub compiler: Compiler,
 }
 
@@ -48,243 +39,374 @@ impl Step for Std {
     type Output = ();
     const DEFAULT: bool = true;
 
-    fn should_run(run: ShouldRun) -> ShouldRun {
-        run.all_krates("std")
+    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
+        run.all_krates("test")
     }
 
-    fn make_run(run: RunConfig) {
+    fn make_run(run: RunConfig<'_>) {
         run.builder.ensure(Std {
             compiler: run.builder.compiler(run.builder.top_stage, run.host),
             target: run.target,
         });
     }
 
-    /// Build the standard library.
+    /// Builds 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;
+    fn run(self, builder: &Builder<'_>) {
         let target = self.target;
         let compiler = self.compiler;
 
-        builder.ensure(StartupObjects { compiler, target });
+        if builder.config.keep_stage.contains(&compiler.stage) {
+            builder.info("Warning: Using a potentially old libstd. This may not behave well.");
+            builder.ensure(StdLink { compiler, target_compiler: compiler, target });
+            return;
+        }
 
-        if build.force_use_stage1(compiler, target) {
-            let from = builder.compiler(1, build.build);
-            builder.ensure(Std {
-                compiler: from,
-                target,
-            });
-            println!("Uplifting stage1 std ({} -> {})", from.host, target);
+        let mut target_deps = builder.ensure(StartupObjects { compiler, target });
+
+        let compiler_to_use = builder.compiler_for(compiler.stage, compiler.host, target);
+        if compiler_to_use != compiler {
+            builder.ensure(Std { compiler: compiler_to_use, target });
+            builder.info(&format!("Uplifting stage1 std ({} -> {})", compiler_to_use.host, target));
 
             // Even if we're not building std this stage, the new sysroot must
-            // still contain the musl startup objects.
-            if target.contains("musl") {
-                let libdir = builder.sysroot_libdir(compiler, target);
-                copy_musl_third_party_objects(build, target, &libdir);
-            }
+            // still contain the third party objects needed by various targets.
+            copy_third_party_objects(builder, &compiler, target);
+            copy_self_contained_objects(builder, &compiler, target);
 
             builder.ensure(StdLink {
-                compiler: from,
+                compiler: compiler_to_use,
                 target_compiler: compiler,
                 target,
             });
             return;
         }
 
-        if target.contains("musl") {
-            let libdir = builder.sysroot_libdir(compiler, target);
-            copy_musl_third_party_objects(build, target, &libdir);
-        }
-
-        let out_dir = build.cargo_out(compiler, Mode::Libstd, target);
-        build.clear_if_dirty(&out_dir, &builder.rustc(compiler));
-        let mut cargo = builder.cargo(compiler, Mode::Libstd, target, "build");
-        std_cargo(builder, &compiler, target, &mut cargo);
-
-        let _folder = build.fold_output(|| format!("stage{}-std", compiler.stage));
-        println!("Building stage{} std artifacts ({} -> {})", compiler.stage,
-                &compiler.host, target);
-        run_cargo(build,
-                  &mut cargo,
-                  &libstd_stamp(build, compiler, target),
-                  false);
+        target_deps.extend(copy_third_party_objects(builder, &compiler, target));
+        target_deps.extend(copy_self_contained_objects(builder, &compiler, target));
+
+        let mut cargo = builder.cargo(compiler, Mode::Std, SourceType::InTree, target, "build");
+        std_cargo(builder, target, compiler.stage, &mut cargo);
+
+        builder.info(&format!(
+            "Building stage{} std artifacts ({} -> {})",
+            compiler.stage, &compiler.host, target
+        ));
+        run_cargo(
+            builder,
+            cargo,
+            vec![],
+            &libstd_stamp(builder, compiler, target),
+            target_deps,
+            false,
+        );
 
         builder.ensure(StdLink {
-            compiler: builder.compiler(compiler.stage, build.build),
+            compiler: builder.compiler(compiler.stage, builder.config.build),
             target_compiler: compiler,
             target,
         });
     }
 }
 
-/// 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));
+fn copy_and_stamp(
+    builder: &Builder<'_>,
+    libdir: &Path,
+    sourcedir: &Path,
+    name: &str,
+    target_deps: &mut Vec<(PathBuf, DependencyType)>,
+    dependency_type: DependencyType,
+) {
+    let target = libdir.join(name);
+    builder.copy(&sourcedir.join(name), &target);
+
+    target_deps.push((target, dependency_type));
+}
+
+/// Copies third party objects needed by various targets.
+fn copy_third_party_objects(
+    builder: &Builder<'_>,
+    compiler: &Compiler,
+    target: TargetSelection,
+) -> Vec<(PathBuf, DependencyType)> {
+    let mut target_deps = vec![];
+
+    // FIXME: remove this in 2021
+    if target == "x86_64-fortanix-unknown-sgx" {
+        if env::var_os("X86_FORTANIX_SGX_LIBS").is_some() {
+            builder.info("Warning: X86_FORTANIX_SGX_LIBS environment variable is ignored, libunwind is now compiled as part of rustbuild");
+        }
+    }
+
+    if builder.config.sanitizers && compiler.stage != 0 {
+        // The sanitizers are only copied in stage1 or above,
+        // to avoid creating dependency on LLVM.
+        target_deps.extend(
+            copy_sanitizers(builder, &compiler, target)
+                .into_iter()
+                .map(|d| (d, DependencyType::Target)),
+        );
+    }
+
+    target_deps
+}
+
+/// Copies third party objects needed by various targets for self-contained linkage.
+fn copy_self_contained_objects(
+    builder: &Builder<'_>,
+    compiler: &Compiler,
+    target: TargetSelection,
+) -> Vec<(PathBuf, DependencyType)> {
+    let libdir_self_contained = builder.sysroot_libdir(*compiler, target).join("self-contained");
+    t!(fs::create_dir_all(&libdir_self_contained));
+    let mut target_deps = vec![];
+
+    // Copies the CRT objects.
+    //
+    // rustc historically provides a more self-contained installation for musl targets
+    // not requiring the presence of a native musl toolchain. For example, it can fall back
+    // to using gcc from a glibc-targeting toolchain for linking.
+    // To do that we have to distribute musl startup objects as a part of Rust toolchain
+    // and link with them manually in the self-contained mode.
+    if target.contains("musl") {
+        let srcdir = builder.musl_libdir(target).unwrap();
+        for &obj in &["crt1.o", "Scrt1.o", "rcrt1.o", "crti.o", "crtn.o"] {
+            copy_and_stamp(
+                builder,
+                &libdir_self_contained,
+                &srcdir,
+                obj,
+                &mut target_deps,
+                DependencyType::TargetSelfContained,
+            );
+        }
+    } else if target.ends_with("-wasi") {
+        let srcdir = builder.wasi_root(target).unwrap().join("lib/wasm32-wasi");
+        copy_and_stamp(
+            builder,
+            &libdir_self_contained,
+            &srcdir,
+            "crt1.o",
+            &mut target_deps,
+            DependencyType::TargetSelfContained,
+        );
+    } else if target.contains("windows-gnu") {
+        for obj in ["crt2.o", "dllcrt2.o"].iter() {
+            let src = compiler_file(builder, builder.cc(target), target, obj);
+            let target = libdir_self_contained.join(obj);
+            builder.copy(&src, &target);
+            target_deps.push((target, DependencyType::TargetSelfContained));
+        }
     }
+
+    target_deps
 }
 
 /// Configure cargo to compile the standard library, adding appropriate env vars
 /// and such.
-pub fn std_cargo(build: &Builder,
-                 compiler: &Compiler,
-                 target: Interned<String>,
-                 cargo: &mut Command) {
-    let mut features = build.std_features();
-
+pub fn std_cargo(builder: &Builder<'_>, target: TargetSelection, stage: u32, cargo: &mut Cargo) {
     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");
-    }
+    // Determine if we're going to compile in optimized C intrinsics to
+    // the `compiler-builtins` crate. These intrinsics live in LLVM's
+    // `compiler-rt` repository, but our `src/llvm-project` submodule isn't
+    // always checked out, so we need to conditionally look for this. (e.g. if
+    // an external LLVM is used we skip the LLVM submodule checkout).
+    //
+    // Note that this shouldn't affect the correctness of `compiler-builtins`,
+    // but only its speed. Some intrinsics in C haven't been translated to Rust
+    // yet but that's pretty rare. Other intrinsics have optimized
+    // implementations in C which have only had slower versions ported to Rust,
+    // so we favor the C version where we can, but it's not critical.
+    //
+    // If `compiler-rt` is available ensure that the `c` feature of the
+    // `compiler-builtins` crate is enabled and it's configured to learn where
+    // `compiler-rt` is located.
+    let compiler_builtins_root = builder.src.join("src/llvm-project/compiler-rt");
+    let compiler_builtins_c_feature = if compiler_builtins_root.exists() {
+        // Note that `libprofiler_builtins/build.rs` also computes this so if
+        // you're changing something here please also change that.
+        cargo.env("RUST_COMPILER_RT_ROOT", &compiler_builtins_root);
+        " compiler-builtins-c".to_string()
+    } else {
+        String::new()
+    };
 
-    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
-        let llvm_config = build.ensure(native::Llvm {
-            target: build.config.build,
-            emscripten: false,
-        });
-        cargo.env("LLVM_CONFIG", llvm_config);
-    }
+    if builder.no_std(target) == Some(true) {
+        let mut features = "compiler-builtins-mem".to_string();
+        features.push_str(&compiler_builtins_c_feature);
+
+        // for no-std targets we only compile a few no_std crates
+        cargo
+            .args(&["-p", "alloc"])
+            .arg("--manifest-path")
+            .arg(builder.src.join("library/alloc/Cargo.toml"))
+            .arg("--features")
+            .arg("compiler-builtins-mem compiler-builtins-c");
+    } else {
+        let mut features = builder.std_features();
+        features.push_str(&compiler_builtins_c_feature);
 
-    cargo.arg("--features").arg(features)
-        .arg("--manifest-path")
-        .arg(build.src.join("src/libstd/Cargo.toml"));
+        cargo
+            .arg("--features")
+            .arg(features)
+            .arg("--manifest-path")
+            .arg(builder.src.join("library/test/Cargo.toml"));
 
-    if let Some(target) = build.config.target_config.get(&target) {
-        if let Some(ref jemalloc) = target.jemalloc {
-            cargo.env("JEMALLOC_OVERRIDE", jemalloc);
+        // Help the libc crate compile by assisting it in finding various
+        // sysroot native libraries.
+        if target.contains("musl") {
+            if let Some(p) = builder.musl_libdir(target) {
+                let root = format!("native={}", p.to_str().unwrap());
+                cargo.rustflag("-L").rustflag(&root);
+            }
         }
-    }
-    if target.contains("musl") {
-        if let Some(p) = build.musl_root(target) {
-            cargo.env("MUSL_ROOT", p);
+
+        if target.ends_with("-wasi") {
+            if let Some(p) = builder.wasi_root(target) {
+                let root = format!("native={}/lib/wasm32-wasi", p.to_str().unwrap());
+                cargo.rustflag("-L").rustflag(&root);
+            }
         }
     }
+
+    // By default, rustc uses `-Cembed-bitcode=yes`, and Cargo overrides that
+    // with `-Cembed-bitcode=no` for non-LTO builds. However, libstd must be
+    // built with bitcode so that the produced rlibs can be used for both LTO
+    // builds (which use bitcode) and non-LTO builds (which use object code).
+    // So we override the override here!
+    //
+    // But we don't bother for the stage 0 compiler because it's never used
+    // with LTO.
+    if stage >= 1 {
+        cargo.rustflag("-Cembed-bitcode=yes");
+    }
+
+    // By default, rustc does not include unwind tables unless they are required
+    // for a particular target. They are not required by RISC-V targets, but
+    // compiling the standard library with them means that users can get
+    // backtraces without having to recompile the standard library themselves.
+    //
+    // This choice was discussed in https://github.com/rust-lang/rust/pull/69890
+    if target.contains("riscv") {
+        cargo.rustflag("-Cforce-unwind-tables=yes");
+    }
 }
 
 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
 struct StdLink {
     pub compiler: Compiler,
     pub target_compiler: Compiler,
-    pub target: Interned<String>,
+    pub target: TargetSelection,
 }
 
 impl Step for StdLink {
     type Output = ();
 
-    fn should_run(run: ShouldRun) -> ShouldRun {
+    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
         run.never()
     }
 
     /// Link all libstd rlibs/dylibs into the sysroot location.
     ///
-    /// Links those artifacts generated by `compiler` to the `stage` compiler's
+    /// Links those artifacts generated by `compiler` to 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;
+    fn run(self, builder: &Builder<'_>) {
         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,
-                &compiler.host,
-                target_compiler.host,
-                target);
+        builder.info(&format!(
+            "Copying stage{} std from stage{} ({} -> {} / {})",
+            target_compiler.stage, compiler.stage, &compiler.host, target_compiler.host, target
+        ));
         let libdir = builder.sysroot_libdir(target_compiler, target);
-        add_to_sysroot(&libdir, &libstd_stamp(build, compiler, target));
+        let hostdir = builder.sysroot_libdir(target_compiler, compiler.host);
+        add_to_sysroot(builder, &libdir, &hostdir, &libstd_stamp(builder, 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);
-        }
+/// Copies sanitizer runtime libraries into target libdir.
+fn copy_sanitizers(
+    builder: &Builder<'_>,
+    compiler: &Compiler,
+    target: TargetSelection,
+) -> Vec<PathBuf> {
+    let runtimes: Vec<native::SanitizerRuntime> = builder.ensure(native::Sanitizers { target });
 
-        builder.ensure(tool::CleanTools {
-            compiler: target_compiler,
-            target,
-            mode: Mode::Libstd,
-        });
+    if builder.config.dry_run {
+        return Vec::new();
     }
-}
 
-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));
+    let mut target_deps = Vec::new();
+    let libdir = builder.sysroot_libdir(*compiler, target);
+
+    for runtime in &runtimes {
+        let dst = libdir.join(&runtime.name);
+        builder.copy(&runtime.path, &dst);
+
+        if target == "x86_64-apple-darwin" {
+            // Update the library install name reflect the fact it has been renamed.
+            let status = Command::new("install_name_tool")
+                .arg("-id")
+                .arg(format!("@rpath/{}", runtime.name))
+                .arg(&dst)
+                .status()
+                .expect("failed to execute `install_name_tool`");
+            assert!(status.success());
+        }
+
+        target_deps.push(dst);
     }
+
+    target_deps
 }
 
 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
 pub struct StartupObjects {
     pub compiler: Compiler,
-    pub target: Interned<String>,
+    pub target: TargetSelection,
 }
 
 impl Step for StartupObjects {
-    type Output = ();
+    type Output = Vec<(PathBuf, DependencyType)>;
 
-    fn should_run(run: ShouldRun) -> ShouldRun {
-        run.path("src/rtstartup")
+    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
+        run.path("library/rtstartup")
     }
 
-    fn make_run(run: RunConfig) {
+    fn make_run(run: RunConfig<'_>) {
         run.builder.ensure(StartupObjects {
             compiler: run.builder.compiler(run.builder.top_stage, run.host),
             target: run.target,
         });
     }
 
-    /// Build and prepare startup objects like rsbegin.o and rsend.o
+    /// Builds 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;
+    fn run(self, builder: &Builder<'_>) -> Vec<(PathBuf, DependencyType)> {
         let for_compiler = self.compiler;
         let target = self.target;
-        if !target.contains("pc-windows-gnu") {
-            return
+        if !target.contains("windows-gnu") {
+            return vec![];
         }
 
-        let src_dir = &build.src.join("src/rtstartup");
-        let dst_dir = &build.native_dir(target).join("rtstartup");
+        let mut target_deps = vec![];
+
+        let src_dir = &builder.src.join("library").join("rtstartup");
+        let dst_dir = &builder.native_dir(target).join("rtstartup");
         let sysroot_dir = &builder.sysroot_libdir(for_compiler, target);
         t!(fs::create_dir_all(dst_dir));
 
@@ -292,260 +414,194 @@ impl Step for StartupObjects {
             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) {
-                let mut cmd = Command::new(&build.initial_rustc);
-                build.run(cmd.env("RUSTC_BOOTSTRAP", "1")
-                            .arg("--cfg").arg("stage0")
-                            .arg("--target").arg(target)
-                            .arg("--emit=obj")
-                            .arg("-o").arg(dst_file)
-                            .arg(src_file));
+                let mut cmd = Command::new(&builder.initial_rustc);
+                builder.run(
+                    cmd.env("RUSTC_BOOTSTRAP", "1")
+                        .arg("--cfg")
+                        .arg("bootstrap")
+                        .arg("--target")
+                        .arg(target.rustc_target_arg())
+                        .arg("--emit=obj")
+                        .arg("-o")
+                        .arg(dst_file)
+                        .arg(src_file),
+                );
             }
 
-            copy(dst_file, &sysroot_dir.join(file.to_string() + ".o"));
-        }
-
-        for obj in ["crt2.o", "dllcrt2.o"].iter() {
-            let src = compiler_file(build,
-                                    build.cc(target),
-                                    target,
-                                    obj);
-            copy(&src, &sysroot_dir.join(obj));
-        }
-    }
-}
-
-#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
-pub struct Test {
-    pub compiler: Compiler,
-    pub target: Interned<String>,
-}
-
-impl Step for Test {
-    type Output = ();
-    const DEFAULT: bool = true;
-
-    fn should_run(run: ShouldRun) -> ShouldRun {
-        run.all_krates("test")
-    }
-
-    fn make_run(run: RunConfig) {
-        run.builder.ensure(Test {
-            compiler: run.builder.compiler(run.builder.top_stage, run.host),
-            target: run.target,
-        });
-    }
-
-    /// 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;
-
-        builder.ensure(Std { compiler, target });
-
-        if build.force_use_stage1(compiler, target) {
-            builder.ensure(Test {
-                compiler: builder.compiler(1, build.build),
-                target,
-            });
-            println!("Uplifting stage1 test ({} -> {})", &build.build, target);
-            builder.ensure(TestLink {
-                compiler: builder.compiler(1, build.build),
-                target_compiler: compiler,
-                target,
-            });
-            return;
+            let target = sysroot_dir.join((*file).to_string() + ".o");
+            builder.copy(dst_file, &target);
+            target_deps.push((target, DependencyType::Target));
         }
 
-        let out_dir = build.cargo_out(compiler, Mode::Libtest, target);
-        build.clear_if_dirty(&out_dir, &libstd_stamp(build, compiler, target));
-        let mut cargo = builder.cargo(compiler, Mode::Libtest, target, "build");
-        test_cargo(build, &compiler, target, &mut cargo);
-
-        let _folder = build.fold_output(|| format!("stage{}-test", compiler.stage));
-        println!("Building stage{} test artifacts ({} -> {})", compiler.stage,
-                &compiler.host, target);
-        run_cargo(build,
-                  &mut cargo,
-                  &libtest_stamp(build, compiler, target),
-                  false);
-
-        builder.ensure(TestLink {
-            compiler: builder.compiler(compiler.stage, build.build),
-            target_compiler: compiler,
-            target,
-        });
-    }
-}
-
-/// 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"));
-}
-
-#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
-pub struct TestLink {
-    pub compiler: Compiler,
-    pub target_compiler: Compiler,
-    pub target: Interned<String>,
-}
-
-impl Step for TestLink {
-    type Output = ();
-
-    fn should_run(run: ShouldRun) -> ShouldRun {
-        run.never()
-    }
-
-    /// 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,
-                &compiler.host,
-                target_compiler.host,
-                target);
-        add_to_sysroot(&builder.sysroot_libdir(target_compiler, target),
-                    &libtest_stamp(build, compiler, target));
-        builder.ensure(tool::CleanTools {
-            compiler: target_compiler,
-            target,
-            mode: Mode::Libtest,
-        });
+        target_deps
     }
 }
 
-#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
+#[derive(Debug, PartialOrd, Ord, Copy, Clone, PartialEq, Eq, Hash)]
 pub struct Rustc {
+    pub target: TargetSelection,
     pub compiler: Compiler,
-    pub target: Interned<String>,
 }
 
 impl Step for Rustc {
     type Output = ();
     const ONLY_HOSTS: bool = true;
-    const DEFAULT: bool = true;
+    const DEFAULT: bool = false;
 
-    fn should_run(run: ShouldRun) -> ShouldRun {
-        run.all_krates("rustc-main")
+    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
+        run.path("src/rustc")
     }
 
-    fn make_run(run: RunConfig) {
+    fn make_run(run: RunConfig<'_>) {
         run.builder.ensure(Rustc {
             compiler: run.builder.compiler(run.builder.top_stage, run.host),
             target: run.target,
         });
     }
 
-    /// Build the compiler.
+    /// Builds 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;
+    fn run(self, builder: &Builder<'_>) {
         let compiler = self.compiler;
         let target = self.target;
 
-        builder.ensure(Test { compiler, target });
+        builder.ensure(Std { compiler, target });
 
-        if build.force_use_stage1(compiler, target) {
-            builder.ensure(Rustc {
-                compiler: builder.compiler(1, build.build),
-                target,
-            });
-            println!("Uplifting stage1 rustc ({} -> {})", &build.build, target);
+        if builder.config.keep_stage.contains(&compiler.stage) {
+            builder.info("Warning: Using a potentially old librustc. This may not behave well.");
+            builder.ensure(RustcLink { compiler, target_compiler: compiler, target });
+            return;
+        }
+
+        let compiler_to_use = builder.compiler_for(compiler.stage, compiler.host, target);
+        if compiler_to_use != compiler {
+            builder.ensure(Rustc { compiler: compiler_to_use, target });
+            builder
+                .info(&format!("Uplifting stage1 rustc ({} -> {})", builder.config.build, target));
             builder.ensure(RustcLink {
-                compiler: builder.compiler(1, build.build),
+                compiler: compiler_to_use,
                 target_compiler: compiler,
                 target,
             });
             return;
         }
 
-        // Ensure that build scripts have a std to link against.
+        // Ensure that build scripts and proc macros have a std / libproc_macro to link against.
         builder.ensure(Std {
-            compiler: builder.compiler(self.compiler.stage, build.build),
-            target: build.build,
+            compiler: builder.compiler(self.compiler.stage, builder.config.build),
+            target: builder.config.build,
         });
-        let cargo_out = builder.cargo_out(compiler, Mode::Librustc, target);
-        build.clear_if_dirty(&cargo_out, &libstd_stamp(build, compiler, target));
-        build.clear_if_dirty(&cargo_out, &libtest_stamp(build, compiler, target));
-
-        let mut cargo = builder.cargo(compiler, Mode::Librustc, target, "build");
-        rustc_cargo(build, &mut cargo);
 
-        let _folder = build.fold_output(|| format!("stage{}-rustc", compiler.stage));
-        println!("Building stage{} compiler artifacts ({} -> {})",
-                 compiler.stage, &compiler.host, target);
-        run_cargo(build,
-                  &mut cargo,
-                  &librustc_stamp(build, compiler, target),
-                  false);
+        let mut cargo = builder.cargo(compiler, Mode::Rustc, SourceType::InTree, target, "build");
+        rustc_cargo(builder, &mut cargo, target);
+
+        builder.info(&format!(
+            "Building stage{} compiler artifacts ({} -> {})",
+            compiler.stage, &compiler.host, target
+        ));
+        run_cargo(
+            builder,
+            cargo,
+            vec![],
+            &librustc_stamp(builder, compiler, target),
+            vec![],
+            false,
+        );
 
         builder.ensure(RustcLink {
-            compiler: builder.compiler(compiler.stage, build.build),
+            compiler: builder.compiler(compiler.stage, builder.config.build),
             target_compiler: compiler,
             target,
         });
     }
 }
 
-pub fn rustc_cargo(build: &Build, cargo: &mut Command) {
-    cargo.arg("--features").arg(build.rustc_features())
-         .arg("--manifest-path")
-         .arg(build.src.join("src/rustc/Cargo.toml"));
-    rustc_cargo_env(build, cargo);
+pub fn rustc_cargo(builder: &Builder<'_>, cargo: &mut Cargo, target: TargetSelection) {
+    cargo
+        .arg("--features")
+        .arg(builder.rustc_features())
+        .arg("--manifest-path")
+        .arg(builder.src.join("src/rustc/Cargo.toml"));
+    rustc_cargo_env(builder, cargo, target);
 }
 
-fn rustc_cargo_env(build: &Build, cargo: &mut Command) {
+pub fn rustc_cargo_env(builder: &Builder<'_>, cargo: &mut Cargo, target: TargetSelection) {
     // 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())
-         .env("CFG_CODEGEN_BACKENDS_DIR", &build.config.rust_codegen_backends_dir);
+    cargo
+        .env("CFG_RELEASE", builder.rust_release())
+        .env("CFG_RELEASE_CHANNEL", &builder.config.channel)
+        .env("CFG_VERSION", builder.rust_version())
+        .env("CFG_PREFIX", builder.config.prefix.clone().unwrap_or_default());
 
-    let libdir_relative = build.config.libdir_relative().unwrap_or(Path::new("lib"));
+    let libdir_relative = builder.config.libdir_relative().unwrap_or_else(|| Path::new("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() {
+    if let Some(ref ver_date) = builder.rust_info.commit_date() {
         cargo.env("CFG_VER_DATE", ver_date);
     }
-    if let Some(ref ver_hash) = build.rust_info.sha() {
+    if let Some(ref ver_hash) = builder.rust_info.sha() {
         cargo.env("CFG_VER_HASH", ver_hash);
     }
-    if !build.unstable_features() {
+    if !builder.unstable_features() {
         cargo.env("CFG_DISABLE_UNSTABLE_FEATURES", "1");
     }
-    if let Some(ref s) = build.config.rustc_default_linker {
+    if let Some(ref s) = builder.config.rustc_default_linker {
         cargo.env("CFG_DEFAULT_LINKER", s);
     }
-    if build.config.rustc_parallel_queries {
-        cargo.env("RUSTC_PARALLEL_QUERIES", "1");
+    if builder.config.rustc_parallel {
+        cargo.rustflag("--cfg=parallel_compiler");
+    }
+    if builder.config.rust_verify_llvm_ir {
+        cargo.env("RUSTC_VERIFY_LLVM_IR", "1");
+    }
+
+    // Pass down configuration from the LLVM build into the build of
+    // librustc_llvm and librustc_codegen_llvm.
+    //
+    // Note that this is disabled if LLVM itself is disabled or we're in a check
+    // build. If we are in a check build we still go ahead here presuming we've
+    // detected that LLVM is alreay built and good to go which helps prevent
+    // busting caches (e.g. like #71152).
+    if builder.config.llvm_enabled()
+        && (builder.kind != Kind::Check
+            || crate::native::prebuilt_llvm_config(builder, target).is_ok())
+    {
+        if builder.is_rust_llvm(target) {
+            cargo.env("LLVM_RUSTLLVM", "1");
+        }
+        let llvm_config = builder.ensure(native::Llvm { target });
+        cargo.env("LLVM_CONFIG", &llvm_config);
+        let target_config = builder.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);
+        }
+        // Some LLVM linker flags (-L and -l) may be needed to link librustc_llvm.
+        if let Some(ref s) = builder.config.llvm_ldflags {
+            cargo.env("LLVM_LINKER_FLAGS", s);
+        }
+        // Building with a static libstdc++ is only supported on linux right now,
+        // not for MSVC or macOS
+        if builder.config.llvm_static_stdcpp
+            && !target.contains("freebsd")
+            && !target.contains("msvc")
+            && !target.contains("apple")
+        {
+            let file = compiler_file(builder, builder.cxx(target).unwrap(), target, "libstdc++.a");
+            cargo.env("LLVM_STATIC_STDCPP", file);
+        }
+        if builder.config.llvm_link_shared || builder.config.llvm_thin_lto {
+            cargo.env("LLVM_LINK_SHARED", "1");
+        }
+        if builder.config.llvm_use_libcxx {
+            cargo.env("LLVM_USE_LIBCXX", "1");
+        }
+        if builder.config.llvm_optimize && !builder.config.llvm_release_debuginfo {
+            cargo.env("LLVM_NDEBUG", "1");
+        }
     }
 }
 
@@ -553,252 +609,58 @@ fn rustc_cargo_env(build: &Build, cargo: &mut Command) {
 struct RustcLink {
     pub compiler: Compiler,
     pub target_compiler: Compiler,
-    pub target: Interned<String>,
+    pub target: TargetSelection,
 }
 
 impl Step for RustcLink {
     type Output = ();
 
-    fn should_run(run: ShouldRun) -> ShouldRun {
+    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
         run.never()
     }
 
     /// Same as `std_link`, only for librustc
-    fn run(self, builder: &Builder) {
-        let build = builder.build;
+    fn run(self, builder: &Builder<'_>) {
         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,
-                 &compiler.host,
-                 target_compiler.host,
-                 target);
-        add_to_sysroot(&builder.sysroot_libdir(target_compiler, target),
-                       &librustc_stamp(build, compiler, target));
-        builder.ensure(tool::CleanTools {
-            compiler: target_compiler,
-            target,
-            mode: Mode::Librustc,
-        });
-    }
-}
-
-#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
-pub struct CodegenBackend {
-    pub compiler: Compiler,
-    pub target: Interned<String>,
-    pub backend: Interned<String>,
-}
-
-impl Step for CodegenBackend {
-    type Output = ();
-    const ONLY_HOSTS: bool = true;
-    const DEFAULT: bool = true;
-
-    fn should_run(run: ShouldRun) -> ShouldRun {
-        run.all_krates("rustc_trans")
-    }
-
-    fn make_run(run: RunConfig) {
-        let backend = run.builder.config.rust_codegen_backends.get(0);
-        let backend = backend.cloned().unwrap_or_else(|| {
-            INTERNER.intern_str("llvm")
-        });
-        run.builder.ensure(CodegenBackend {
-            compiler: run.builder.compiler(run.builder.top_stage, run.host),
-            target: run.target,
-            backend
-        });
-    }
-
-    fn run(self, builder: &Builder) {
-        let build = builder.build;
-        let compiler = self.compiler;
-        let target = self.target;
-
-        builder.ensure(Rustc { compiler, target });
-
-        if build.force_use_stage1(compiler, target) {
-            builder.ensure(CodegenBackend {
-                compiler: builder.compiler(1, build.build),
-                target,
-                backend: self.backend,
-            });
-            return;
-        }
-
-        let mut cargo = builder.cargo(compiler, Mode::Librustc, target, "build");
-        let mut features = build.rustc_features().to_string();
-        cargo.arg("--manifest-path")
-            .arg(build.src.join("src/librustc_trans/Cargo.toml"));
-        rustc_cargo_env(build, &mut cargo);
-
-        match &*self.backend {
-            "llvm" | "emscripten" => {
-                // Build LLVM for our target. This will implicitly build the
-                // host LLVM if necessary.
-                let llvm_config = builder.ensure(native::Llvm {
-                    target,
-                    emscripten: self.backend == "emscripten",
-                });
-
-                if self.backend == "emscripten" {
-                    features.push_str(" emscripten");
-                }
-
-                println!("Building stage{} codegen artifacts ({} -> {}, {})",
-                         compiler.stage, &compiler.host, target, self.backend);
-
-                // Pass down configuration from the LLVM build into the build of
-                // librustc_llvm and librustc_trans.
-                if build.is_rust_llvm(target) {
-                    cargo.env("LLVM_RUSTLLVM", "1");
-                }
-                cargo.env("LLVM_CONFIG", &llvm_config);
-                if self.backend != "emscripten" {
-                    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("freebsd") &&
-                   !target.contains("windows") &&
-                   !target.contains("apple") {
-                    let file = compiler_file(build,
-                                             build.cxx(target).unwrap(),
-                                             target,
-                                             "libstdc++.a");
-                    cargo.env("LLVM_STATIC_STDCPP", file);
-                }
-                if build.config.llvm_link_shared {
-                    cargo.env("LLVM_LINK_SHARED", "1");
-                }
-            }
-            _ => panic!("unknown backend: {}", self.backend),
-        }
-
-        let tmp_stamp = build.cargo_out(compiler, Mode::Librustc, target)
-            .join(".tmp.stamp");
-
-        let _folder = build.fold_output(|| format!("stage{}-rustc_trans", compiler.stage));
-        let files = run_cargo(build,
-                              cargo.arg("--features").arg(features),
-                              &tmp_stamp,
-                              false);
-        let mut files = files.into_iter()
-            .filter(|f| {
-                let filename = f.file_name().unwrap().to_str().unwrap();
-                is_dylib(filename) && filename.contains("rustc_trans-")
-            });
-        let codegen_backend = match files.next() {
-            Some(f) => f,
-            None => panic!("no dylibs built for codegen backend?"),
-        };
-        if let Some(f) = files.next() {
-            panic!("codegen backend built two dylibs:\n{}\n{}",
-                   codegen_backend.display(),
-                   f.display());
-        }
-        let stamp = codegen_backend_stamp(build, compiler, target, self.backend);
-        let codegen_backend = codegen_backend.to_str().unwrap();
-        t!(t!(File::create(&stamp)).write_all(codegen_backend.as_bytes()));
-    }
-}
-
-/// Creates the `codegen-backends` folder for a compiler that's about to be
-/// assembled as a complete compiler.
-///
-/// This will take the codegen artifacts produced by `compiler` and link them
-/// into an appropriate location for `target_compiler` to be a functional
-/// compiler.
-fn copy_codegen_backends_to_sysroot(builder: &Builder,
-                                    compiler: Compiler,
-                                    target_compiler: Compiler) {
-    let build = builder.build;
-    let target = target_compiler.host;
-
-    // Note that this step is different than all the other `*Link` steps in
-    // that it's not assembling a bunch of libraries but rather is primarily
-    // moving the codegen backend into place. The codegen backend of rustc is
-    // not linked into the main compiler by default but is rather dynamically
-    // selected at runtime for inclusion.
-    //
-    // Here we're looking for the output dylib of the `CodegenBackend` step and
-    // we're copying that into the `codegen-backends` folder.
-    let dst = builder.sysroot_codegen_backends(target_compiler);
-    t!(fs::create_dir_all(&dst));
-
-    for backend in builder.config.rust_codegen_backends.iter() {
-        let stamp = codegen_backend_stamp(build, compiler, target, *backend);
-        let mut dylib = String::new();
-        t!(t!(File::open(&stamp)).read_to_string(&mut dylib));
-        let file = Path::new(&dylib);
-        let filename = file.file_name().unwrap().to_str().unwrap();
-        // change `librustc_trans-xxxxxx.so` to `librustc_trans-llvm.so`
-        let target_filename = {
-            let dash = filename.find("-").unwrap();
-            let dot = filename.find(".").unwrap();
-            format!("{}-{}{}",
-                    &filename[..dash],
-                    backend,
-                    &filename[dot..])
-        };
-        copy(&file, &dst.join(target_filename));
+        builder.info(&format!(
+            "Copying stage{} rustc from stage{} ({} -> {} / {})",
+            target_compiler.stage, compiler.stage, &compiler.host, target_compiler.host, target
+        ));
+        add_to_sysroot(
+            builder,
+            &builder.sysroot_libdir(target_compiler, target),
+            &builder.sysroot_libdir(target_compiler, compiler.host),
+            &librustc_stamp(builder, compiler, target),
+        );
     }
 }
 
-fn copy_lld_to_sysroot(builder: &Builder,
-                       target_compiler: Compiler,
-                       lld_install_root: &Path) {
-    let target = target_compiler.host;
-
-    let dst = builder.sysroot_libdir(target_compiler, target)
-        .parent()
-        .unwrap()
-        .join("bin");
-    t!(fs::create_dir_all(&dst));
-
-    let exe = exe("lld", &target);
-    copy(&lld_install_root.join("bin").join(&exe), &dst.join(&exe));
-}
-
 /// Cargo's output path for the standard library in a given stage, compiled
 /// by a particular compiler for the specified target.
-pub fn libstd_stamp(build: &Build, compiler: Compiler, target: Interned<String>) -> PathBuf {
-    build.cargo_out(compiler, Mode::Libstd, target).join(".libstd.stamp")
-}
-
-/// Cargo's output path for libtest in a given stage, compiled by a particular
-/// compiler for the specified target.
-pub fn libtest_stamp(build: &Build, compiler: Compiler, target: Interned<String>) -> PathBuf {
-    build.cargo_out(compiler, Mode::Libtest, target).join(".libtest.stamp")
+pub fn libstd_stamp(builder: &Builder<'_>, compiler: Compiler, target: TargetSelection) -> PathBuf {
+    builder.cargo_out(compiler, Mode::Std, target).join(".libstd.stamp")
 }
 
 /// Cargo's output path for librustc in a given stage, compiled by a particular
 /// compiler for the specified target.
-pub fn librustc_stamp(build: &Build, compiler: Compiler, target: Interned<String>) -> PathBuf {
-    build.cargo_out(compiler, Mode::Librustc, target).join(".librustc.stamp")
-}
-
-fn codegen_backend_stamp(build: &Build,
-                         compiler: Compiler,
-                         target: Interned<String>,
-                         backend: Interned<String>) -> PathBuf {
-    build.cargo_out(compiler, Mode::Librustc, target)
-        .join(format!(".librustc_trans-{}.stamp", backend))
+pub fn librustc_stamp(
+    builder: &Builder<'_>,
+    compiler: Compiler,
+    target: TargetSelection,
+) -> PathBuf {
+    builder.cargo_out(compiler, Mode::Rustc, target).join(".librustc.stamp")
 }
 
-fn compiler_file(build: &Build,
-                 compiler: &Path,
-                 target: Interned<String>,
-                 file: &str) -> PathBuf {
+pub fn compiler_file(
+    builder: &Builder<'_>,
+    compiler: &Path,
+    target: TargetSelection,
+    file: &str,
+) -> PathBuf {
     let mut cmd = Command::new(compiler);
-    cmd.args(build.cflags(target));
+    cmd.args(builder.cflags(target, GitRepo::Rustc));
     cmd.arg(format!("-print-file-name={}", file));
     let out = output(&mut cmd);
     PathBuf::from(out.trim())
@@ -812,7 +674,7 @@ pub struct Sysroot {
 impl Step for Sysroot {
     type Output = Interned<PathBuf>;
 
-    fn should_run(run: ShouldRun) -> ShouldRun {
+    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
         run.never()
     }
 
@@ -822,21 +684,44 @@ impl Step for Sysroot {
     /// 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.
-    fn run(self, builder: &Builder) -> Interned<PathBuf> {
-        let build = builder.build;
+    fn run(self, builder: &Builder<'_>) -> Interned<PathBuf> {
         let compiler = self.compiler;
         let sysroot = if compiler.stage == 0 {
-            build.out.join(&compiler.host).join("stage0-sysroot")
+            builder.out.join(&compiler.host.triple).join("stage0-sysroot")
         } else {
-            build.out.join(&compiler.host).join(format!("stage{}", compiler.stage))
+            builder.out.join(&compiler.host.triple).join(format!("stage{}", compiler.stage))
         };
         let _ = fs::remove_dir_all(&sysroot);
         t!(fs::create_dir_all(&sysroot));
+
+        // Symlink the source root into the same location inside the sysroot,
+        // where `rust-src` component would go (`$sysroot/lib/rustlib/src/rust`),
+        // so that any tools relying on `rust-src` also work for local builds,
+        // and also for translating the virtual `/rustc/$hash` back to the real
+        // directory (for running tests with `rust.remap-debuginfo = true`).
+        let sysroot_lib_rustlib_src = sysroot.join("lib/rustlib/src");
+        t!(fs::create_dir_all(&sysroot_lib_rustlib_src));
+        let sysroot_lib_rustlib_src_rust = sysroot_lib_rustlib_src.join("rust");
+        if let Err(e) = symlink_dir(&builder.config, &builder.src, &sysroot_lib_rustlib_src_rust) {
+            eprintln!(
+                "warning: creating symbolic link `{}` to `{}` failed with {}",
+                sysroot_lib_rustlib_src_rust.display(),
+                builder.src.display(),
+                e,
+            );
+            if builder.config.rust_remap_debuginfo {
+                eprintln!(
+                    "warning: some `src/test/ui` tests will fail when lacking `{}`",
+                    sysroot_lib_rustlib_src_rust.display(),
+                );
+            }
+        }
+
         INTERNER.intern_path(sysroot)
     }
 }
 
-#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
+#[derive(Debug, Copy, PartialOrd, Ord, Clone, PartialEq, Eq, Hash)]
 pub struct Assemble {
     /// The compiler which we will produce in this step. Assemble itself will
     /// take care of ensuring that the necessary prerequisites to do so exist,
@@ -848,22 +733,23 @@ pub struct Assemble {
 impl Step for Assemble {
     type Output = Compiler;
 
-    fn should_run(run: ShouldRun) -> ShouldRun {
-        run.all_krates("rustc-main")
+    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
+        run.never()
     }
 
     /// 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
+    /// must have been previously produced by the `stage - 1` builder.build
     /// compiler.
-    fn run(self, builder: &Builder) -> Compiler {
-        let build = builder.build;
+    fn run(self, builder: &Builder<'_>) -> Compiler {
         let target_compiler = self.target_compiler;
 
         if target_compiler.stage == 0 {
-            assert_eq!(build.build, target_compiler.host,
-                "Cannot obtain compiler for non-native build triple at stage 0");
+            assert_eq!(
+                builder.config.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;
         }
@@ -884,77 +770,60 @@ impl Step for Assemble {
         //
         // FIXME: It may be faster if we build just a stage 1 compiler and then
         //        use that to bootstrap this compiler forward.
-        let build_compiler =
-            builder.compiler(target_compiler.stage - 1, build.build);
+        let build_compiler = builder.compiler(target_compiler.stage - 1, builder.config.build);
 
         // 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).
-        if builder.build.config.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.config.keep_stage.unwrap()) {
-                let target_compiler = builder.compiler(stage, target_compiler.host);
-                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 });
-            }
-        } else {
-            builder.ensure(Rustc {
-                compiler: build_compiler,
-                target: target_compiler.host,
-            });
-            for &backend in build.config.rust_codegen_backends.iter() {
-                builder.ensure(CodegenBackend {
-                    compiler: build_compiler,
-                    target: target_compiler.host,
-                    backend,
-                });
-            }
-        }
+        builder.ensure(Rustc { compiler: build_compiler, target: target_compiler.host });
 
-        let lld_install = if build.config.lld_enabled {
-            Some(builder.ensure(native::Lld {
-                target: target_compiler.host,
-            }))
+        let lld_install = if builder.config.lld_enabled {
+            Some(builder.ensure(native::Lld { target: target_compiler.host }))
         } else {
             None
         };
 
         let stage = target_compiler.stage;
         let host = target_compiler.host;
-        println!("Assembling stage{} compiler ({})", stage, host);
+        builder.info(&format!("Assembling stage{} compiler ({})", stage, host));
 
         // Link in all dylibs to the libdir
         let sysroot = builder.sysroot(target_compiler);
-        let sysroot_libdir = sysroot.join(libdir(&*host));
-        t!(fs::create_dir_all(&sysroot_libdir));
+        let rustc_libdir = builder.rustc_libdir(target_compiler);
+        t!(fs::create_dir_all(&rustc_libdir));
         let src_libdir = builder.sysroot_libdir(build_compiler, host);
-        for f in t!(fs::read_dir(&src_libdir)).map(|f| t!(f)) {
+        for f in builder.read_dir(&src_libdir) {
             let filename = f.file_name().into_string().unwrap();
             if is_dylib(&filename) {
-                copy(&f.path(), &sysroot_libdir.join(&filename));
+                builder.copy(&f.path(), &rustc_libdir.join(&filename));
             }
         }
 
-        copy_codegen_backends_to_sysroot(builder,
-                                         build_compiler,
-                                         target_compiler);
+        let libdir = builder.sysroot_libdir(target_compiler, target_compiler.host);
         if let Some(lld_install) = lld_install {
-            copy_lld_to_sysroot(builder, target_compiler, &lld_install);
+            let src_exe = exe("lld", target_compiler.host);
+            let dst_exe = exe("rust-lld", target_compiler.host);
+            // we prepend this bin directory to the user PATH when linking Rust binaries. To
+            // avoid shadowing the system LLD we rename the LLD we provide to `rust-lld`.
+            let dst = libdir.parent().unwrap().join("bin");
+            t!(fs::create_dir_all(&dst));
+            builder.copy(&lld_install.join("bin").join(&src_exe), &dst.join(&dst_exe));
         }
 
+        // Ensure that `libLLVM.so` ends up in the newly build compiler directory,
+        // so that it can be found when the newly built `rustc` is run.
+        dist::maybe_install_llvm_runtime(builder, target_compiler.host, &sysroot);
+        dist::maybe_install_llvm_target(builder, target_compiler.host, &sysroot);
+
         // Link the compiler binary itself into place
-        let out_dir = build.cargo_out(build_compiler, Mode::Librustc, host);
-        let rustc = out_dir.join(exe("rustc", &*host));
+        let out_dir = builder.cargo_out(build_compiler, Mode::Rustc, host);
+        let rustc = out_dir.join(exe("rustc_binary", host));
         let bindir = sysroot.join("bin");
         t!(fs::create_dir_all(&bindir));
         let compiler = builder.rustc(target_compiler);
-        let _ = fs::remove_file(&compiler);
-        copy(&rustc, &compiler);
+        builder.copy(&rustc, &compiler);
 
         target_compiler
     }
@@ -964,80 +833,92 @@ impl Step for Assemble {
 ///
 /// For a particular stage this will link the file listed in `stamp` into the
 /// `sysroot_dst` provided.
-pub fn add_to_sysroot(sysroot_dst: &Path, stamp: &Path) {
+pub fn add_to_sysroot(
+    builder: &Builder<'_>,
+    sysroot_dst: &Path,
+    sysroot_host_dst: &Path,
+    stamp: &Path,
+) {
+    let self_contained_dst = &sysroot_dst.join("self-contained");
     t!(fs::create_dir_all(&sysroot_dst));
-    for path in read_stamp_file(stamp) {
-        copy(&path, &sysroot_dst.join(path.file_name().unwrap()));
+    t!(fs::create_dir_all(&sysroot_host_dst));
+    t!(fs::create_dir_all(&self_contained_dst));
+    for (path, dependency_type) in builder.read_stamp_file(stamp) {
+        let dst = match dependency_type {
+            DependencyType::Host => sysroot_host_dst,
+            DependencyType::Target => sysroot_dst,
+            DependencyType::TargetSelfContained => self_contained_dst,
+        };
+        builder.copy(&path, &dst.join(path.file_name().unwrap()));
     }
 }
 
-// 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;
+pub fn run_cargo(
+    builder: &Builder<'_>,
+    cargo: Cargo,
+    tail_args: Vec<String>,
+    stamp: &Path,
+    additional_target_deps: Vec<(PathBuf, DependencyType)>,
+    is_check: bool,
+) -> Vec<PathBuf> {
+    if builder.config.dry_run {
+        return Vec::new();
     }
-    unsafe {
-        let handle = GetStdHandle(STD_ERROR_HANDLE);
-        let mut out = 0;
-        GetConsoleMode(handle, &mut out) != 0
-    }
-}
 
-pub fn run_cargo(build: &Build, cargo: &mut Command, stamp: &Path, is_check: bool)
-    -> Vec<PathBuf>
-{
     // `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());
+    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 ok = stream_cargo(build, cargo, &mut |msg| {
-        let filenames = match msg {
-            CargoMessage::CompilerArtifact { filenames, .. } => filenames,
+    let ok = stream_cargo(builder, cargo, tail_args, &mut |msg| {
+        let (filenames, crate_types) = match msg {
+            CargoMessage::CompilerArtifact {
+                filenames,
+                target: CargoTarget { crate_types },
+                ..
+            } => (filenames, crate_types),
             _ => return,
         };
         for filename in filenames {
             // Skip files like executables
-            if !filename.ends_with(".rlib") &&
-               !filename.ends_with(".lib") &&
-               !is_dylib(&filename) &&
-               !(is_check && filename.ends_with(".rmeta")) {
-                return;
+            if !(filename.ends_with(".rlib")
+                || filename.ends_with(".lib")
+                || filename.ends_with(".a")
+                || is_dylib(&filename)
+                || (is_check && filename.ends_with(".rmeta")))
+            {
+                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.
+            // worry about it, it's not relevant for us
             if filename.starts_with(&host_root_dir) {
-                return;
+                // Unless it's a proc macro used in the compiler
+                if crate_types.iter().any(|t| t == "proc-macro") {
+                    deps.push((filename.to_path_buf(), DependencyType::Host));
+                }
+                continue;
             }
 
             // If this was output in the `deps` dir then this is a precise file
             // name (hash included) so we start tracking it.
             if filename.starts_with(&target_deps_dir) {
-                deps.push(filename.to_path_buf());
-                return;
+                deps.push((filename.to_path_buf(), DependencyType::Target));
+                continue;
             }
 
             // Otherwise this was a "top level artifact" which right now doesn't
@@ -1061,7 +942,7 @@ pub fn run_cargo(build: &Build, cargo: &mut Command, stamp: &Path, is_check: boo
     });
 
     if !ok {
-        panic!("cargo must succeed");
+        exit(1);
     }
 
     // Ok now we need to actually find all the files listed in `toplevel`. We've
@@ -1073,14 +954,14 @@ pub fn run_cargo(build: &Build, cargo: &mut Command, stamp: &Path, is_check: boo
         .collect::<Vec<_>>();
     for (prefix, extension, expected_len) in toplevel {
         let candidates = contents.iter().filter(|&&(_, ref filename, ref meta)| {
-            filename.starts_with(&prefix[..]) &&
-                filename[prefix.len()..].starts_with("-") &&
-                filename.ends_with(&extension[..]) &&
-                meta.len() == expected_len
-        });
-        let max = candidates.max_by_key(|&&(_, _, ref metadata)| {
-            FileTime::from_last_modification_time(metadata)
+            meta.len() == expected_len
+                && filename
+                    .strip_prefix(&prefix[..])
+                    .map(|s| s.starts_with('-') && s.ends_with(&extension[..]))
+                    .unwrap_or(false)
         });
+        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),
@@ -1089,68 +970,56 @@ pub fn run_cargo(build: &Build, cargo: &mut Command, stamp: &Path, is_check: boo
             let candidate = format!("{}.lib", path_to_add);
             let candidate = PathBuf::from(candidate);
             if candidate.exists() {
-                deps.push(candidate);
+                deps.push((candidate, DependencyType::Target));
             }
         }
-        deps.push(path_to_add.into());
+        deps.push((path_to_add.into(), DependencyType::Target));
     }
 
-    // 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.extend(additional_target_deps);
     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.iter() {
-        let mtime = mtime(dep);
-        if Some(mtime) > max {
-            max = Some(mtime);
-            max_path = Some(dep.clone());
-        }
+    for (dep, dependency_type) in deps.iter() {
+        new_contents.extend(match *dependency_type {
+            DependencyType::Host => b"h",
+            DependencyType::Target => b"t",
+            DependencyType::TargetSelfContained => b"s",
+        });
         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 {
-        build.verbose(&format!("not updating {:?}; contents equal and {:?} <= {:?}",
-                stamp, max, stamp_mtime));
-        return deps
-    }
-    if max > stamp_mtime {
-        build.verbose(&format!("updating {:?} as {:?} changed", stamp, max_path));
-    } else {
-        build.verbose(&format!("updating {:?} as deps changed", stamp));
-    }
-    t!(t!(File::create(stamp)).write_all(&new_contents));
-    deps
+    t!(fs::write(&stamp, &new_contents));
+    deps.into_iter().map(|(d, _)| d).collect()
 }
 
 pub fn stream_cargo(
-    build: &Build,
-    cargo: &mut Command,
-    cb: &mut FnMut(CargoMessage),
+    builder: &Builder<'_>,
+    cargo: Cargo,
+    tail_args: Vec<String>,
+    cb: &mut dyn FnMut(CargoMessage<'_>),
 ) -> bool {
+    let mut cargo = Command::from(cargo);
+    if builder.config.dry_run {
+        return true;
+    }
     // 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());
-
-    if stderr_isatty() && build.ci_env == CiEnv::None {
-        // 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");
+    let mut message_format = if builder.config.json_output {
+        String::from("json")
+    } else {
+        String::from("json-render-diagnostics")
+    };
+    if let Some(s) = &builder.config.rustc_error_format {
+        message_format.push_str(",json-diagnostic-");
+        message_format.push_str(s);
+    }
+    cargo.arg("--message-format").arg(message_format).stdout(Stdio::piped());
+
+    for arg in tail_args {
+        cargo.arg(arg);
     }
 
-    build.verbose(&format!("running: {:?}", cargo));
+    builder.verbose(&format!("running: {:?}", cargo));
     let mut child = match cargo.spawn() {
         Ok(child) => child,
         Err(e) => panic!("failed to execute command: {:?}\nerror: {}", cargo, e),
@@ -1162,24 +1031,36 @@ pub fn stream_cargo(
     let stdout = BufReader::new(child.stdout.take().unwrap());
     for line in stdout.lines() {
         let line = t!(line);
-        match serde_json::from_str::<CargoMessage>(&line) {
-            Ok(msg) => cb(msg),
+        match serde_json::from_str::<CargoMessage<'_>>(&line) {
+            Ok(msg) => {
+                if builder.config.json_output {
+                    // Forward JSON to stdout.
+                    println!("{}", line);
+                }
+                cb(msg)
+            }
             // If this was informational, just print it out and continue
-            Err(_) => println!("{}", line)
+            Err(_) => println!("{}", line),
         }
     }
 
     // Make sure Cargo actually succeeded after we read all of its stdout.
     let status = t!(child.wait());
     if !status.success() {
-        println!("command did not execute successfully: {:?}\n\
+        eprintln!(
+            "command did not execute successfully: {:?}\n\
                   expected success, got: {}",
-                 cargo,
-                 status);
+            cargo, status
+        );
     }
     status.success()
 }
 
+#[derive(Deserialize)]
+pub struct CargoTarget<'a> {
+    crate_types: Vec<Cow<'a, str>>,
+}
+
 #[derive(Deserialize)]
 #[serde(tag = "reason", rename_all = "kebab-case")]
 pub enum CargoMessage<'a> {
@@ -1187,8 +1068,12 @@ pub enum CargoMessage<'a> {
         package_id: Cow<'a, str>,
         features: Vec<Cow<'a, str>>,
         filenames: Vec<Cow<'a, str>>,
+        target: CargoTarget<'a>,
     },
     BuildScriptExecuted {
         package_id: Cow<'a, str>,
-    }
+    },
+    BuildFinished {
+        success: bool,
+    },
 }