about summary refs log tree commit diff
path: root/src/bootstrap
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2016-12-30 07:34:19 +0000
committerbors <bors@rust-lang.org>2016-12-30 07:34:19 +0000
commit7f2d2afa9196ba6314a29e58d5324dbd9923c75e (patch)
tree483907141a5dcd2cfc7e4ea91f7234d77bddf83b /src/bootstrap
parent75f5981d4500e52de767d61ec50a34b97be2a301 (diff)
parente484197482698ee0bb83a66987a5f64c46ae306b (diff)
Auto merge of #38697 - alexcrichton:rollup, r=alexcrichton
Rollup of 25 pull requests

- Successful merges: #37149, #38491, #38517, #38559, #38587, #38609, #38611, #38622, #38628, #38630, #38631, #38632, #38635, #38647, #38649, #38655, #38659, #38660, #38662, #38665, #38671, #38674, #38676, #38693, #38695
- Failed merges: #38657, #38680
Diffstat (limited to 'src/bootstrap')
-rw-r--r--src/bootstrap/check.rs18
-rw-r--r--src/bootstrap/compile.rs114
-rw-r--r--src/bootstrap/config.rs7
-rw-r--r--src/bootstrap/config.toml.example18
-rw-r--r--src/bootstrap/doc.rs15
-rw-r--r--src/bootstrap/lib.rs28
-rw-r--r--src/bootstrap/mk/Makefile.in12
-rw-r--r--src/bootstrap/native.rs9
-rw-r--r--src/bootstrap/step.rs218
9 files changed, 301 insertions, 138 deletions
diff --git a/src/bootstrap/check.rs b/src/bootstrap/check.rs
index e7b0afeb8ce..ec0243908ed 100644
--- a/src/bootstrap/check.rs
+++ b/src/bootstrap/check.rs
@@ -341,12 +341,22 @@ pub fn krate(build: &Build,
     println!("{} {} stage{} ({} -> {})", test_kind, name, compiler.stage,
              compiler.host, target);
 
+    // If we're not doing a full bootstrap but we're testing a stage2 version of
+    // libstd, then what we're actually testing is the libstd produced in
+    // stage1. Reflect that here by updating the compiler that we're working
+    // with automatically.
+    let compiler = if build.force_use_stage1(compiler, target) {
+        Compiler::new(1, compiler.host)
+    } else {
+        compiler.clone()
+    };
+
     // Build up the base `cargo test` command.
     //
     // Pass in some standard flags then iterate over the graph we've discovered
     // in `cargo metadata` with the maps above and figure out what `-p`
     // arguments need to get passed.
-    let mut cargo = build.cargo(compiler, mode, target, test_kind.subcommand());
+    let mut cargo = build.cargo(&compiler, mode, target, test_kind.subcommand());
     cargo.arg("--manifest-path")
          .arg(build.src.join(path).join("Cargo.toml"))
          .arg("--features").arg(features);
@@ -380,7 +390,7 @@ pub fn krate(build: &Build,
     // Note that to run the compiler we need to run with the *host* libraries,
     // but our wrapper scripts arrange for that to be the case anyway.
     let mut dylib_path = dylib_path();
-    dylib_path.insert(0, build.sysroot_libdir(compiler, target));
+    dylib_path.insert(0, build.sysroot_libdir(&compiler, target));
     cargo.env(dylib_path_var(), env::join_paths(&dylib_path).unwrap());
 
     if target.contains("android") {
@@ -399,10 +409,10 @@ pub fn krate(build: &Build,
 
     if target.contains("android") {
         build.run(&mut cargo);
-        krate_android(build, compiler, target, mode);
+        krate_android(build, &compiler, target, mode);
     } else if target.contains("emscripten") {
         build.run(&mut cargo);
-        krate_emscripten(build, compiler, target, mode);
+        krate_emscripten(build, &compiler, target, mode);
     } else {
         cargo.args(&build.flags.cmd.test_args());
         build.run(&mut cargo);
diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs
index b268686ca6c..dcccf788935 100644
--- a/src/bootstrap/compile.rs
+++ b/src/bootstrap/compile.rs
@@ -33,17 +33,12 @@ use {Build, Compiler, Mode};
 /// 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.
-pub fn std<'a>(build: &'a Build, target: &str, compiler: &Compiler<'a>) {
-    println!("Building stage{} std artifacts ({} -> {})", compiler.stage,
-             compiler.host, target);
-
+pub fn std(build: &Build, target: &str, compiler: &Compiler) {
     let libdir = build.sysroot_libdir(compiler, target);
-    let _ = fs::remove_dir_all(&libdir);
     t!(fs::create_dir_all(&libdir));
 
-    // Some platforms have startup objects that may be required to produce the
-    // libstd dynamic library, for example.
-    build_startup_objects(build, target, &libdir);
+    println!("Building stage{} std artifacts ({} -> {})", compiler.stage,
+             compiler.host, target);
 
     let out_dir = build.cargo_out(compiler, Mode::Libstd, target);
     build.clear_if_dirty(&out_dir, &build.compiler_path(compiler));
@@ -65,29 +60,30 @@ pub fn std<'a>(build: &'a Build, target: &str, compiler: &Compiler<'a>) {
 
     build.run(&mut cargo);
     update_mtime(&libstd_stamp(build, &compiler, target));
-    std_link(build, target, compiler.stage, compiler.host);
 }
 
 /// Link all libstd rlibs/dylibs into the sysroot location.
 ///
-/// Links those artifacts generated in the given `stage` for `target` produced
-/// by `compiler` into `host`'s sysroot.
+/// 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.
 pub fn std_link(build: &Build,
-                target: &str,
-                stage: u32,
-                host: &str) {
-    let compiler = Compiler::new(stage, &build.config.build);
-    let target_compiler = Compiler::new(compiler.stage, host);
+                compiler: &Compiler,
+                target_compiler: &Compiler,
+                target: &str) {
+    println!("Copying stage{} std from stage{} ({} -> {} / {})",
+             target_compiler.stage,
+             compiler.stage,
+             compiler.host,
+             target_compiler.host,
+             target);
     let libdir = build.sysroot_libdir(&target_compiler, target);
     let out_dir = build.cargo_out(&compiler, Mode::Libstd, target);
 
-    // If we're linking one compiler host's output into another, then we weren't
-    // called from the `std` method above. In that case we clean out what's
-    // already there.
-    if host != compiler.host {
-        let _ = fs::remove_dir_all(&libdir);
-        t!(fs::create_dir_all(&libdir));
-    }
+    t!(fs::create_dir_all(&libdir));
     add_to_sysroot(&out_dir, &libdir);
 
     if target.contains("musl") && !target.contains("mips") {
@@ -110,12 +106,15 @@ fn copy_musl_third_party_objects(build: &Build, target: &str, into: &Path) {
 /// 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 build_startup_objects(build: &Build, target: &str, into: &Path) {
+pub fn build_startup_objects(build: &Build, for_compiler: &Compiler, target: &str) {
     if !target.contains("pc-windows-gnu") {
         return
     }
+
     let compiler = Compiler::new(0, &build.config.build);
     let compiler_path = build.compiler_path(&compiler);
+    let into = build.sysroot_libdir(for_compiler, target);
+    t!(fs::create_dir_all(&into));
 
     for file in t!(fs::read_dir(build.src.join("src/rtstartup"))) {
         let file = t!(file);
@@ -123,7 +122,7 @@ fn build_startup_objects(build: &Build, target: &str, into: &Path) {
         build.run(cmd.env("RUSTC_BOOTSTRAP", "1")
                      .arg("--target").arg(target)
                      .arg("--emit=obj")
-                     .arg("--out-dir").arg(into)
+                     .arg("--out-dir").arg(&into)
                      .arg(file.path()));
     }
 
@@ -137,7 +136,7 @@ fn build_startup_objects(build: &Build, target: &str, into: &Path) {
 /// 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.
-pub fn test<'a>(build: &'a Build, target: &str, compiler: &Compiler<'a>) {
+pub fn test(build: &Build, target: &str, compiler: &Compiler) {
     println!("Building stage{} test artifacts ({} -> {})", compiler.stage,
              compiler.host, target);
     let out_dir = build.cargo_out(compiler, Mode::Libtest, target);
@@ -147,19 +146,19 @@ pub fn test<'a>(build: &'a Build, target: &str, compiler: &Compiler<'a>) {
          .arg(build.src.join("src/rustc/test_shim/Cargo.toml"));
     build.run(&mut cargo);
     update_mtime(&libtest_stamp(build, compiler, target));
-    test_link(build, target, compiler.stage, compiler.host);
 }
 
-/// Link all libtest rlibs/dylibs into the sysroot location.
-///
-/// Links those artifacts generated in the given `stage` for `target` produced
-/// by `compiler` into `host`'s sysroot.
+/// Same as `std_link`, only for libtest
 pub fn test_link(build: &Build,
-                 target: &str,
-                 stage: u32,
-                 host: &str) {
-    let compiler = Compiler::new(stage, &build.config.build);
-    let target_compiler = Compiler::new(compiler.stage, host);
+                 compiler: &Compiler,
+                 target_compiler: &Compiler,
+                 target: &str) {
+    println!("Copying stage{} test from stage{} ({} -> {} / {})",
+             target_compiler.stage,
+             compiler.stage,
+             compiler.host,
+             target_compiler.host,
+             target);
     let libdir = build.sysroot_libdir(&target_compiler, target);
     let out_dir = build.cargo_out(&compiler, Mode::Libtest, target);
     add_to_sysroot(&out_dir, &libdir);
@@ -170,7 +169,7 @@ pub fn test_link(build: &Build,
 /// 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.
-pub fn rustc<'a>(build: &'a Build, target: &str, compiler: &Compiler<'a>) {
+pub fn rustc(build: &Build, target: &str, compiler: &Compiler) {
     println!("Building stage{} compiler artifacts ({} -> {})",
              compiler.stage, compiler.host, target);
 
@@ -222,20 +221,19 @@ pub fn rustc<'a>(build: &'a Build, target: &str, compiler: &Compiler<'a>) {
         cargo.env("CFG_DEFAULT_AR", s);
     }
     build.run(&mut cargo);
-
-    rustc_link(build, target, compiler.stage, compiler.host);
 }
 
-/// Link all librustc rlibs/dylibs into the sysroot location.
-///
-/// Links those artifacts generated in the given `stage` for `target` produced
-/// by `compiler` into `host`'s sysroot.
+/// Same as `std_link`, only for librustc
 pub fn rustc_link(build: &Build,
-                  target: &str,
-                  stage: u32,
-                  host: &str) {
-    let compiler = Compiler::new(stage, &build.config.build);
-    let target_compiler = Compiler::new(compiler.stage, host);
+                  compiler: &Compiler,
+                  target_compiler: &Compiler,
+                  target: &str) {
+    println!("Copying stage{} rustc from stage{} ({} -> {} / {})",
+             target_compiler.stage,
+             compiler.stage,
+             compiler.host,
+             target_compiler.host,
+             target);
     let libdir = build.sysroot_libdir(&target_compiler, target);
     let out_dir = build.cargo_out(&compiler, Mode::Librustc, target);
     add_to_sysroot(&out_dir, &libdir);
@@ -259,6 +257,17 @@ fn compiler_file(compiler: &Path, file: &str) -> PathBuf {
     PathBuf::from(out.trim())
 }
 
+pub fn create_sysroot(build: &Build, compiler: &Compiler) {
+    // nothing to do in stage0
+    if compiler.stage == 0 {
+        return
+    }
+
+    let sysroot = build.sysroot(compiler);
+    let _ = fs::remove_dir_all(&sysroot);
+    t!(fs::create_dir_all(&sysroot));
+}
+
 /// Prepare a new compiler from the artifacts in `stage`
 ///
 /// This will assemble a compiler in `build/$host/stage$stage`. The compiler
@@ -269,18 +278,17 @@ pub fn assemble_rustc(build: &Build, stage: u32, host: &str) {
     if stage == 0 {
         return
     }
+
+    println!("Copying stage{} compiler ({})", stage, host);
+
     // The compiler that we're assembling
     let target_compiler = Compiler::new(stage, host);
 
     // The compiler that compiled the compiler we're assembling
     let build_compiler = Compiler::new(stage - 1, &build.config.build);
 
-    // Clear out old files
-    let sysroot = build.sysroot(&target_compiler);
-    let _ = fs::remove_dir_all(&sysroot);
-    t!(fs::create_dir_all(&sysroot));
-
     // Link in all dylibs to the libdir
+    let sysroot = build.sysroot(&target_compiler);
     let sysroot_libdir = sysroot.join(libdir(host));
     t!(fs::create_dir_all(&sysroot_libdir));
     let src_libdir = build.sysroot_libdir(&build_compiler, host);
diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs
index 6b86e537b7d..9767afd73ca 100644
--- a/src/bootstrap/config.rs
+++ b/src/bootstrap/config.rs
@@ -46,6 +46,7 @@ pub struct Config {
     pub docs: bool,
     pub vendor: bool,
     pub target_config: HashMap<String, Target>,
+    pub full_bootstrap: bool,
 
     // llvm codegen options
     pub llvm_assertions: bool,
@@ -54,6 +55,7 @@ pub struct Config {
     pub llvm_version_check: bool,
     pub llvm_static_stdcpp: bool,
     pub llvm_link_shared: bool,
+    pub llvm_targets: Option<String>,
 
     // rust codegen options
     pub rust_optimize: bool,
@@ -134,6 +136,7 @@ struct Build {
     vendor: Option<bool>,
     nodejs: Option<String>,
     python: Option<String>,
+    full_bootstrap: Option<bool>,
 }
 
 /// TOML representation of various global install decisions.
@@ -152,6 +155,7 @@ struct Llvm {
     release_debuginfo: Option<bool>,
     version_check: Option<bool>,
     static_libstdcpp: Option<bool>,
+    targets: Option<String>,
 }
 
 #[derive(RustcDecodable)]
@@ -264,6 +268,7 @@ impl Config {
         set(&mut config.docs, build.docs);
         set(&mut config.submodules, build.submodules);
         set(&mut config.vendor, build.vendor);
+        set(&mut config.full_bootstrap, build.full_bootstrap);
 
         if let Some(ref install) = toml.install {
             config.prefix = install.prefix.clone();
@@ -285,6 +290,7 @@ impl Config {
             set(&mut config.llvm_release_debuginfo, llvm.release_debuginfo);
             set(&mut config.llvm_version_check, llvm.version_check);
             set(&mut config.llvm_static_stdcpp, llvm.static_libstdcpp);
+            config.llvm_targets = llvm.targets.clone();
         }
 
         if let Some(ref rust) = toml.rust {
@@ -393,6 +399,7 @@ impl Config {
                 ("NINJA", self.ninja),
                 ("CODEGEN_TESTS", self.codegen_tests),
                 ("VENDOR", self.vendor),
+                ("FULL_BOOTSTRAP", self.full_bootstrap),
             }
 
             match key {
diff --git a/src/bootstrap/config.toml.example b/src/bootstrap/config.toml.example
index 5fc095137c7..69210c4959b 100644
--- a/src/bootstrap/config.toml.example
+++ b/src/bootstrap/config.toml.example
@@ -42,6 +42,17 @@
 # example.
 #ninja = false
 
+# LLVM targets to build support for.
+# Note: this is NOT related to Rust compilation targets. However, as Rust is
+# dependent on LLVM for code generation, turning targets off here WILL lead to
+# the resulting rustc being unable to compile for the disabled architectures.
+# Also worth pointing out is that, in case support for new targets are added to
+# LLVM, enabling them here doesn't mean Rust is automatically gaining said
+# support. You'll need to write a target specification at least, and most
+# likely, teach rustc about the C ABI of the target. Get in touch with the
+# Rust team and file an issue if you need assistance in porting!
+#targets = "X86;ARM;AArch64;Mips;PowerPC;SystemZ;JSBackend;MSP430;Sparc;NVPTX"
+
 # =============================================================================
 # General build configuration options
 # =============================================================================
@@ -100,6 +111,13 @@
 # Indicate whether the vendored sources are used for Rust dependencies or not
 #vendor = false
 
+# Typically the build system will build the rust compiler twice. The second
+# compiler, however, will simply use its own libraries to link against. If you
+# would rather to perform a full bootstrap, compiling the compiler three times,
+# then you can set this option to true. You shouldn't ever need to set this
+# option to true.
+#full-bootstrap = false
+
 # =============================================================================
 # General install configuration options
 # =============================================================================
diff --git a/src/bootstrap/doc.rs b/src/bootstrap/doc.rs
index 30c7fefad87..4c4462bf122 100644
--- a/src/bootstrap/doc.rs
+++ b/src/bootstrap/doc.rs
@@ -137,6 +137,11 @@ pub fn std(build: &Build, stage: u32, target: &str) {
     let out = build.doc_out(target);
     t!(fs::create_dir_all(&out));
     let compiler = Compiler::new(stage, &build.config.build);
+    let compiler = if build.force_use_stage1(&compiler, target) {
+        Compiler::new(1, compiler.host)
+    } else {
+        compiler
+    };
     let out_dir = build.stage_out(&compiler, Mode::Libstd)
                        .join(target).join("doc");
     let rustdoc = build.rustdoc(&compiler);
@@ -160,6 +165,11 @@ pub fn test(build: &Build, stage: u32, target: &str) {
     let out = build.doc_out(target);
     t!(fs::create_dir_all(&out));
     let compiler = Compiler::new(stage, &build.config.build);
+    let compiler = if build.force_use_stage1(&compiler, target) {
+        Compiler::new(1, compiler.host)
+    } else {
+        compiler
+    };
     let out_dir = build.stage_out(&compiler, Mode::Libtest)
                        .join(target).join("doc");
     let rustdoc = build.rustdoc(&compiler);
@@ -182,6 +192,11 @@ pub fn rustc(build: &Build, stage: u32, target: &str) {
     let out = build.doc_out(target);
     t!(fs::create_dir_all(&out));
     let compiler = Compiler::new(stage, &build.config.build);
+    let compiler = if build.force_use_stage1(&compiler, target) {
+        Compiler::new(1, compiler.host)
+    } else {
+        compiler
+    };
     let out_dir = build.stage_out(&compiler, Mode::Librustc)
                        .join(target).join("doc");
     let rustdoc = build.rustdoc(&compiler);
diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs
index 665e0c67b7f..49eaed4c67a 100644
--- a/src/bootstrap/lib.rs
+++ b/src/bootstrap/lib.rs
@@ -572,9 +572,7 @@ impl Build {
         let mut cmd = Command::new(self.tool(&compiler, tool));
         let host = compiler.host;
         let mut paths = vec![
-            self.cargo_out(compiler, Mode::Libstd, host).join("deps"),
-            self.cargo_out(compiler, Mode::Libtest, host).join("deps"),
-            self.cargo_out(compiler, Mode::Librustc, host).join("deps"),
+            self.sysroot_libdir(compiler, compiler.host),
             self.cargo_out(compiler, Mode::Tool, host).join("deps"),
         ];
 
@@ -880,6 +878,30 @@ impl Build {
     fn python(&self) -> &Path {
         self.config.python.as_ref().unwrap()
     }
+
+    /// Tests whether the `compiler` compiling for `target` should be forced to
+    /// use a stage1 compiler instead.
+    ///
+    /// Currently, by default, the build system does not perform a "full
+    /// bootstrap" by default where we compile the compiler three times.
+    /// Instead, we compile the compiler two times. The final stage (stage2)
+    /// just copies the libraries from the previous stage, which is what this
+    /// method detects.
+    ///
+    /// Here we return `true` if:
+    ///
+    /// * The build isn't performing a full bootstrap
+    /// * The `compiler` is in the final stage, 2
+    /// * We're not cross-compiling, so the artifacts are already available in
+    ///   stage1
+    ///
+    /// When all of these conditions are met the build will lift artifacts from
+    /// the previous stage forward.
+    fn force_use_stage1(&self, compiler: &Compiler, target: &str) -> bool {
+        !self.config.full_bootstrap &&
+            compiler.stage >= 2 &&
+            self.config.host.iter().any(|h| h == target)
+    }
 }
 
 impl<'a> Compiler<'a> {
diff --git a/src/bootstrap/mk/Makefile.in b/src/bootstrap/mk/Makefile.in
index 0d83a79cf32..a3333369930 100644
--- a/src/bootstrap/mk/Makefile.in
+++ b/src/bootstrap/mk/Makefile.in
@@ -51,8 +51,16 @@ standalone-docs:
 	$(Q)$(BOOTSTRAP) doc src/doc $(BOOTSTRAP_ARGS)
 check:
 	$(Q)$(BOOTSTRAP) test $(BOOTSTRAP_ARGS)
-check-cargotest:
-	$(Q)$(BOOTSTRAP) test src/tools/cargotest $(BOOTSTRAP_ARGS)
+check-aux:
+	$(Q)$(BOOTSTRAP) test \
+		src/tools/cargotest \
+		src/test/pretty \
+		src/test/run-pass/pretty \
+		src/test/run-fail/pretty \
+		src/test/run-pass-valgrind/pretty \
+		src/test/run-pass-fulldeps/pretty \
+		src/test/run-fail-fulldeps/pretty \
+		$(BOOTSTRAP_ARGS)
 dist:
 	$(Q)$(BOOTSTRAP) dist $(BOOTSTRAP_ARGS)
 distcheck:
diff --git a/src/bootstrap/native.rs b/src/bootstrap/native.rs
index 09dbd9f8220..4b6fef8edc1 100644
--- a/src/bootstrap/native.rs
+++ b/src/bootstrap/native.rs
@@ -75,13 +75,18 @@ pub fn llvm(build: &Build, target: &str) {
         (true, true) => "RelWithDebInfo",
     };
 
+    // NOTE: remember to also update `config.toml.example` when changing the defaults!
+    let llvm_targets = match build.config.llvm_targets {
+        Some(ref s) => s,
+        None => "X86;ARM;AArch64;Mips;PowerPC;SystemZ;JSBackend;MSP430;Sparc;NVPTX",
+    };
+
     cfg.target(target)
        .host(&build.config.build)
        .out_dir(&dst)
        .profile(profile)
        .define("LLVM_ENABLE_ASSERTIONS", assertions)
-       .define("LLVM_TARGETS_TO_BUILD",
-               "X86;ARM;AArch64;Mips;PowerPC;SystemZ;JSBackend;MSP430;Sparc")
+       .define("LLVM_TARGETS_TO_BUILD", llvm_targets)
        .define("LLVM_INCLUDE_EXAMPLES", "OFF")
        .define("LLVM_INCLUDE_TESTS", "OFF")
        .define("LLVM_INCLUDE_DOCS", "OFF")
diff --git a/src/bootstrap/step.rs b/src/bootstrap/step.rs
index 52caa3f0958..7bcfb06f210 100644
--- a/src/bootstrap/step.rs
+++ b/src/bootstrap/step.rs
@@ -44,7 +44,7 @@ pub fn run(build: &Build) {
     rules.run(&steps);
 }
 
-pub fn build_rules(build: &Build) -> Rules {
+pub fn build_rules<'a>(build: &'a Build) -> Rules {
     let mut rules = Rules::new(build);
 
     // This is the first rule that we're going to define for rustbuild, which is
@@ -117,6 +117,7 @@ pub fn build_rules(build: &Build) -> Rules {
 
     // the compiler with no target libraries ready to go
     rules.build("rustc", "src/rustc")
+         .dep(|s| s.name("create-sysroot").target(s.host))
          .dep(move |s| {
              if s.stage == 0 {
                  Step::noop()
@@ -151,77 +152,145 @@ pub fn build_rules(build: &Build) -> Rules {
     // Crate compilations
     //
     // Tools used during the build system but not shipped
+    rules.build("create-sysroot", "path/to/nowhere")
+         .run(move |s| compile::create_sysroot(build, &s.compiler()));
+
+    // These rules are "pseudo rules" that don't actually do any work
+    // themselves, but represent a complete sysroot with the relevant compiler
+    // linked into place.
+    //
+    // That is, depending on "libstd" means that when the rule is completed then
+    // the `stage` sysroot for the compiler `host` will be available with a
+    // standard library built for `target` linked in place. Not all rules need
+    // the compiler itself to be available, just the standard library, so
+    // there's a distinction between the two.
     rules.build("libstd", "src/libstd")
-         .dep(|s| s.name("build-crate-std_shim"));
+         .dep(|s| s.name("rustc").target(s.host))
+         .dep(|s| s.name("libstd-link"));
     rules.build("libtest", "src/libtest")
-         .dep(|s| s.name("build-crate-test_shim"));
+         .dep(|s| s.name("libstd"))
+         .dep(|s| s.name("libtest-link"))
+         .default(true);
     rules.build("librustc", "src/librustc")
-         .dep(|s| s.name("build-crate-rustc-main"));
+         .dep(|s| s.name("libtest"))
+         .dep(|s| s.name("librustc-link"))
+         .host(true)
+         .default(true);
+
+    // Helper method to define the rules to link a crate into its place in the
+    // sysroot.
+    //
+    // The logic here is a little subtle as there's a few cases to consider.
+    // Not all combinations of (stage, host, target) actually require something
+    // to be compiled, but rather libraries could get propagated from a
+    // different location. For example:
+    //
+    // * Any crate with a `host` that's not the build triple will not actually
+    //   compile something. A different `host` means that the build triple will
+    //   actually compile the libraries, and then we'll copy them over from the
+    //   build triple to the `host` directory.
+    //
+    // * Some crates aren't even compiled by the build triple, but may be copied
+    //   from previous stages. For example if we're not doing a full bootstrap
+    //   then we may just depend on the stage1 versions of libraries to be
+    //   available to get linked forward.
+    //
+    // * Finally, there are some cases, however, which do indeed comiple crates
+    //   and link them into place afterwards.
+    //
+    // The rule definition below mirrors these three cases. The `dep` method
+    // calculates the correct dependency which either comes from stage1, a
+    // different compiler, or from actually building the crate itself (the `dep`
+    // rule). The `run` rule then mirrors these three cases and links the cases
+    // forward into the compiler sysroot specified from the correct location.
+    fn crate_rule<'a, 'b>(build: &'a Build,
+                          rules: &'b mut Rules<'a>,
+                          krate: &'a str,
+                          dep: &'a str,
+                          link: fn(&Build, &Compiler, &Compiler, &str))
+                          -> RuleBuilder<'a, 'b> {
+        let mut rule = rules.build(&krate, "path/to/nowhere");
+        rule.dep(move |s| {
+                if build.force_use_stage1(&s.compiler(), s.target) {
+                    s.host(&build.config.build).stage(1)
+                } else if s.host == build.config.build {
+                    s.name(dep)
+                } else {
+                    s.host(&build.config.build)
+                }
+            })
+            .run(move |s| {
+                if build.force_use_stage1(&s.compiler(), s.target) {
+                    link(build,
+                         &s.stage(1).host(&build.config.build).compiler(),
+                         &s.compiler(),
+                         s.target)
+                } else if s.host == build.config.build {
+                    link(build, &s.compiler(), &s.compiler(), s.target)
+                } else {
+                    link(build,
+                         &s.host(&build.config.build).compiler(),
+                         &s.compiler(),
+                         s.target)
+                }
+            });
+            return rule
+    }
+
+    // Similar to the `libstd`, `libtest`, and `librustc` rules above, except
+    // these rules only represent the libraries being available in the sysroot,
+    // not the compiler itself. This is done as not all rules need a compiler in
+    // the sysroot, but may just need the libraries.
+    //
+    // All of these rules use the helper definition above.
+    crate_rule(build,
+               &mut rules,
+               "libstd-link",
+               "build-crate-std_shim",
+               compile::std_link)
+        .dep(|s| s.name("startup-objects"))
+        .dep(|s| s.name("create-sysroot").target(s.host));
+    crate_rule(build,
+               &mut rules,
+               "libtest-link",
+               "build-crate-test_shim",
+               compile::test_link)
+        .dep(|s| s.name("libstd-link"));
+    crate_rule(build,
+               &mut rules,
+               "librustc-link",
+               "build-crate-rustc-main",
+               compile::rustc_link)
+        .dep(|s| s.name("libtest-link"));
+
     for (krate, path, _default) in krates("std_shim") {
         rules.build(&krate.build_step, path)
+             .dep(|s| s.name("startup-objects"))
              .dep(move |s| s.name("rustc").host(&build.config.build).target(s.host))
-             .dep(move |s| {
-                 if s.host == build.config.build {
-                     Step::noop()
-                 } else {
-                    s.host(&build.config.build)
-                 }
-             })
-             .run(move |s| {
-                 if s.host == build.config.build {
-                    compile::std(build, s.target, &s.compiler())
-                 } else {
-                    compile::std_link(build, s.target, s.stage, s.host)
-                 }
-             });
+             .run(move |s| compile::std(build, s.target, &s.compiler()));
     }
-    for (krate, path, default) in krates("test_shim") {
+    for (krate, path, _default) in krates("test_shim") {
         rules.build(&krate.build_step, path)
-             .dep(|s| s.name("libstd"))
-             .dep(move |s| {
-                 if s.host == build.config.build {
-                    Step::noop()
-                 } else {
-                    s.host(&build.config.build)
-                 }
-             })
-             .default(default)
-             .run(move |s| {
-                 if s.host == build.config.build {
-                    compile::test(build, s.target, &s.compiler())
-                 } else {
-                    compile::test_link(build, s.target, s.stage, s.host)
-                 }
-             });
+             .dep(|s| s.name("libstd-link"))
+             .run(move |s| compile::test(build, s.target, &s.compiler()));
     }
-    for (krate, path, default) in krates("rustc-main") {
+    for (krate, path, _default) in krates("rustc-main") {
         rules.build(&krate.build_step, path)
-             .dep(|s| s.name("libtest"))
+             .dep(|s| s.name("libtest-link"))
              .dep(move |s| s.name("llvm").host(&build.config.build).stage(0))
-             .dep(move |s| {
-                 if s.host == build.config.build {
-                    Step::noop()
-                 } else {
-                    s.host(&build.config.build)
-                 }
-             })
-             .host(true)
-             .default(default)
-             .run(move |s| {
-                 if s.host == build.config.build {
-                    compile::rustc(build, s.target, &s.compiler())
-                 } else {
-                    compile::rustc_link(build, s.target, s.stage, s.host)
-                 }
-             });
+             .run(move |s| compile::rustc(build, s.target, &s.compiler()));
     }
 
+    rules.build("startup-objects", "src/rtstartup")
+         .dep(|s| s.name("create-sysroot").target(s.host))
+         .run(move |s| compile::build_startup_objects(build, &s.compiler(), s.target));
+
     // ========================================================================
     // Test targets
     //
     // Various unit tests and tests suites we can run
     {
-        let mut suite = |name, path, dir, mode| {
+        let mut suite = |name, path, mode, dir| {
             rules.test(name, path)
                  .dep(|s| s.name("libtest"))
                  .dep(|s| s.name("tool-compiletest").target(s.host))
@@ -233,9 +302,9 @@ pub fn build_rules(build: &Build) -> Rules {
                          Step::noop()
                      }
                  })
-                 .default(true)
+                 .default(mode != "pretty") // pretty tests don't run everywhere
                  .run(move |s| {
-                     check::compiletest(build, &s.compiler(), s.target, dir, mode)
+                     check::compiletest(build, &s.compiler(), s.target, mode, dir)
                  });
         };
 
@@ -254,13 +323,6 @@ pub fn build_rules(build: &Build) -> Rules {
         suite("check-incremental", "src/test/incremental", "incremental",
               "incremental");
         suite("check-ui", "src/test/ui", "ui", "ui");
-        suite("check-pretty", "src/test/pretty", "pretty", "pretty");
-        suite("check-pretty-rpass", "src/test/run-pass/pretty", "pretty",
-              "run-pass");
-        suite("check-pretty-rfail", "src/test/run-pass/pretty", "pretty",
-              "run-fail");
-        suite("check-pretty-valgrind", "src/test/run-pass-valgrind", "pretty",
-              "run-pass-valgrind");
     }
 
     if build.config.build.contains("msvc") {
@@ -290,14 +352,15 @@ pub fn build_rules(build: &Build) -> Rules {
                                          s.target));
 
     {
-        let mut suite = |name, path, dir, mode| {
+        let mut suite = |name, path, mode, dir| {
             rules.test(name, path)
                  .dep(|s| s.name("librustc"))
+                 .dep(|s| s.name("test-helpers"))
                  .dep(|s| s.name("tool-compiletest").target(s.host))
-                 .default(true)
+                 .default(mode != "pretty")
                  .host(true)
                  .run(move |s| {
-                     check::compiletest(build, &s.compiler(), s.target, dir, mode)
+                     check::compiletest(build, &s.compiler(), s.target, mode, dir)
                  });
         };
 
@@ -307,9 +370,16 @@ pub fn build_rules(build: &Build) -> Rules {
               "compile-fail", "compile-fail-fulldeps");
         suite("check-rmake", "src/test/run-make", "run-make", "run-make");
         suite("check-rustdoc", "src/test/rustdoc", "rustdoc", "rustdoc");
-        suite("check-pretty-rpass-full", "src/test/run-pass-fulldeps",
+        suite("check-pretty", "src/test/pretty", "pretty", "pretty");
+        suite("check-pretty-rpass", "src/test/run-pass/pretty", "pretty",
+              "run-pass");
+        suite("check-pretty-rfail", "src/test/run-fail/pretty", "pretty",
+              "run-fail");
+        suite("check-pretty-valgrind", "src/test/run-pass-valgrind/pretty", "pretty",
+              "run-pass-valgrind");
+        suite("check-pretty-rpass-full", "src/test/run-pass-fulldeps/pretty",
               "pretty", "run-pass-fulldeps");
-        suite("check-pretty-rfail-full", "src/test/run-fail-fulldeps",
+        suite("check-pretty-rfail-full", "src/test/run-fail-fulldeps/pretty",
               "pretty", "run-fail-fulldeps");
     }
 
@@ -444,25 +514,25 @@ pub fn build_rules(build: &Build) -> Rules {
          .run(move |s| doc::standalone(build, s.stage, s.target));
     rules.doc("doc-error-index", "src/tools/error_index_generator")
          .dep(move |s| s.name("tool-error-index").target(&build.config.build))
-         .dep(move |s| s.name("librustc"))
+         .dep(move |s| s.name("librustc-link"))
          .default(build.config.docs)
          .host(true)
          .run(move |s| doc::error_index(build, s.stage, s.target));
     for (krate, path, default) in krates("std_shim") {
         rules.doc(&krate.doc_step, path)
-             .dep(|s| s.name("libstd"))
+             .dep(|s| s.name("libstd-link"))
              .default(default && build.config.docs)
              .run(move |s| doc::std(build, s.stage, s.target));
     }
     for (krate, path, default) in krates("test_shim") {
         rules.doc(&krate.doc_step, path)
-             .dep(|s| s.name("libtest"))
+             .dep(|s| s.name("libtest-link"))
              .default(default && build.config.compiler_docs)
              .run(move |s| doc::test(build, s.stage, s.target));
     }
     for (krate, path, default) in krates("rustc-main") {
         rules.doc(&krate.doc_step, path)
-             .dep(|s| s.name("librustc"))
+             .dep(|s| s.name("librustc-link"))
              .host(true)
              .default(default && build.config.compiler_docs)
              .run(move |s| doc::rustc(build, s.stage, s.target));
@@ -481,9 +551,9 @@ pub fn build_rules(build: &Build) -> Rules {
              // for the `rust-std` package, so if this is a host target we
              // depend on librustc and otherwise we just depend on libtest.
              if build.config.host.iter().any(|t| t == s.target) {
-                 s.name("librustc")
+                 s.name("librustc-link")
              } else {
-                 s.name("libtest")
+                 s.name("libtest-link")
              }
          })
          .default(true)