about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2020-07-28 13:56:32 +0000
committerbors <bors@rust-lang.org>2020-07-28 13:56:32 +0000
commit7b3a7819371cef92a187e9bac8f7810ccde15216 (patch)
treea67620ed2276657f29e99763d4dd4d94dc14be27
parent2c28244cf0fc9868f55070e55b8f332d196eaf3f (diff)
parentda40cf81e63d04dced9d943743bb2a58801916e7 (diff)
downloadrust-7b3a7819371cef92a187e9bac8f7810ccde15216.tar.gz
rust-7b3a7819371cef92a187e9bac8f7810ccde15216.zip
Auto merge of #73964 - jyn514:sane-defaults, r=Mark-Simulacrum
Improve defaults in x.py

- Make the default stage dependent on the subcommand
- Don't build stage1 rustc artifacts with x.py build --stage 1. If this is what you want, use x.py build --stage 2 instead, which gives you a working libstd.
- Change default debuginfo when debug = true from 2 to 1

I tried to fix CI to use `--stage 2` everywhere it currently has no stage, but I might have missed a spot.
This does not update much of the documentation - most of it is in https://github.com/rust-lang/rustc-dev-guide/ or https://github.com/rust-lang/rust-forge and will need a separate PR.

See individual commits for a detailed rationale of each change.
See also the MCP: https://github.com/rust-lang/compiler-team/issues/326

r? @Mark-Simulacrum , but anyone is free to give an opinion.
-rw-r--r--.github/workflows/ci.yml4
-rw-r--r--config.toml.example5
-rw-r--r--src/bootstrap/builder.rs60
-rw-r--r--src/bootstrap/builder/tests.rs1017
-rw-r--r--src/bootstrap/compile.rs4
-rw-r--r--src/bootstrap/config.rs2
-rw-r--r--src/bootstrap/flags.rs2
-rw-r--r--src/bootstrap/mk/Makefile.in34
-rw-r--r--src/ci/azure-pipelines/auto.yml2
-rw-r--r--src/ci/docker/host-aarch64/aarch64-gnu/Dockerfile2
-rw-r--r--src/ci/docker/host-x86_64/arm-android/Dockerfile2
-rw-r--r--src/ci/docker/host-x86_64/armhf-gnu/Dockerfile2
-rw-r--r--src/ci/docker/host-x86_64/disabled/asmjs/Dockerfile2
-rw-r--r--src/ci/docker/host-x86_64/disabled/dist-armv7-android/Dockerfile2
-rw-r--r--src/ci/docker/host-x86_64/disabled/dist-i686-android/Dockerfile2
-rw-r--r--src/ci/docker/host-x86_64/disabled/riscv64gc-linux/Dockerfile2
-rw-r--r--src/ci/docker/host-x86_64/dist-i586-gnu-i586-i686-musl/Dockerfile2
-rw-r--r--src/ci/docker/host-x86_64/dist-various-1/Dockerfile2
-rw-r--r--src/ci/docker/host-x86_64/i686-gnu-nopt/Dockerfile2
-rw-r--r--src/ci/docker/host-x86_64/i686-gnu/Dockerfile2
-rw-r--r--src/ci/docker/host-x86_64/mingw-check/Dockerfile4
-rw-r--r--src/ci/docker/host-x86_64/test-various/Dockerfile6
-rw-r--r--src/ci/docker/host-x86_64/wasm32/Dockerfile2
-rw-r--r--src/ci/docker/host-x86_64/x86_64-gnu-debug/Dockerfile4
-rw-r--r--src/ci/docker/host-x86_64/x86_64-gnu-distcheck/Dockerfile4
-rw-r--r--src/ci/docker/host-x86_64/x86_64-gnu-full-bootstrap/Dockerfile2
-rw-r--r--src/ci/docker/host-x86_64/x86_64-gnu-llvm-8/Dockerfile8
-rw-r--r--src/ci/docker/host-x86_64/x86_64-gnu-nopt/Dockerfile2
-rwxr-xr-xsrc/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh6
-rw-r--r--src/ci/docker/host-x86_64/x86_64-gnu/Dockerfile2
-rw-r--r--src/ci/github-actions/ci.yml4
31 files changed, 660 insertions, 536 deletions
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index d83971b70be..565c916db5b 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -406,7 +406,7 @@ jobs:
             os: windows-latest-xl
           - name: x86_64-msvc-cargo
             env:
-              SCRIPT: python x.py test src/tools/cargotest src/tools/cargo
+              SCRIPT: python x.py --stage 2 test src/tools/cargotest src/tools/cargo
               RUST_CONFIGURE_ARGS: "--build=x86_64-pc-windows-msvc --enable-lld"
               VCVARS_BAT: vcvars64.bat
               NO_DEBUG_ASSERTIONS: 1
@@ -598,7 +598,7 @@ jobs:
             os: macos-latest
           - name: x86_64-apple
             env:
-              SCRIPT: "./x.py test"
+              SCRIPT: "./x.py --stage 2 test"
               RUST_CONFIGURE_ARGS: "--build=x86_64-apple-darwin --enable-sanitizers --enable-profiler --set rust.jemalloc"
               RUSTC_RETRY_LINKER_ON_SEGFAULT: 1
               MACOSX_DEPLOYMENT_TARGET: 10.8
diff --git a/config.toml.example b/config.toml.example
index 7b1fee7b24e..8ecd8d88d48 100644
--- a/config.toml.example
+++ b/config.toml.example
@@ -341,7 +341,10 @@
 # Debuginfo for tests run with compiletest is not controlled by this option
 # and needs to be enabled separately with `debuginfo-level-tests`.
 #
-# Defaults to 2 if debug is true
+# Note that debuginfo-level = 2 generates several gigabytes of debuginfo
+# and will slow down the linking process significantly.
+#
+# Defaults to 1 if debug is true
 #debuginfo-level = 0
 
 # Debuginfo level for the compiler.
diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs
index c1e56347ab1..144e146685f 100644
--- a/src/bootstrap/builder.rs
+++ b/src/bootstrap/builder.rs
@@ -232,7 +232,7 @@ impl StepDescription {
                 }
 
                 if !attempted_run {
-                    panic!("Error: no rules matched {}.", path.display());
+                    panic!("error: no rules matched {}", path.display());
                 }
             }
         }
@@ -501,16 +501,7 @@ impl<'a> Builder<'a> {
             _ => return None,
         };
 
-        let builder = Builder {
-            build,
-            top_stage: build.config.stage.unwrap_or(2),
-            kind,
-            cache: Cache::new(),
-            stack: RefCell::new(Vec::new()),
-            time_spent_on_dependencies: Cell::new(Duration::new(0, 0)),
-            paths: vec![],
-        };
-
+        let builder = Self::new_internal(build, kind, vec![]);
         let builder = &builder;
         let mut should_run = ShouldRun::new(builder);
         for desc in Builder::get_step_descriptions(builder.kind) {
@@ -535,6 +526,32 @@ impl<'a> Builder<'a> {
         Some(help)
     }
 
+    fn new_internal(build: &Build, kind: Kind, paths: Vec<PathBuf>) -> Builder<'_> {
+        let top_stage = if let Some(explicit_stage) = build.config.stage {
+            explicit_stage
+        } else {
+            // See https://github.com/rust-lang/compiler-team/issues/326
+            match kind {
+                Kind::Doc => 0,
+                Kind::Build | Kind::Test => 1,
+                Kind::Bench | Kind::Dist | Kind::Install => 2,
+                // These are all bootstrap tools, which don't depend on the compiler.
+                // The stage we pass shouldn't matter, but use 0 just in case.
+                Kind::Check | Kind::Clippy | Kind::Fix | Kind::Run | Kind::Format => 0,
+            }
+        };
+
+        Builder {
+            build,
+            top_stage,
+            kind,
+            cache: Cache::new(),
+            stack: RefCell::new(Vec::new()),
+            time_spent_on_dependencies: Cell::new(Duration::new(0, 0)),
+            paths,
+        }
+    }
+
     pub fn new(build: &Build) -> Builder<'_> {
         let (kind, paths) = match build.config.cmd {
             Subcommand::Build { ref paths } => (Kind::Build, &paths[..]),
@@ -550,15 +567,20 @@ impl<'a> Builder<'a> {
             Subcommand::Format { .. } | Subcommand::Clean { .. } => panic!(),
         };
 
-        Builder {
-            build,
-            top_stage: build.config.stage.unwrap_or(2),
-            kind,
-            cache: Cache::new(),
-            stack: RefCell::new(Vec::new()),
-            time_spent_on_dependencies: Cell::new(Duration::new(0, 0)),
-            paths: paths.to_owned(),
+        let this = Self::new_internal(build, kind, paths.to_owned());
+
+        // CI should always run stage 2 builds, unless it specifically states otherwise
+        #[cfg(not(test))]
+        if build.config.stage.is_none() && build.ci_env != crate::CiEnv::None {
+            match kind {
+                Kind::Test | Kind::Doc | Kind::Build | Kind::Bench | Kind::Dist | Kind::Install => {
+                    assert_eq!(this.top_stage, 2)
+                }
+                Kind::Check | Kind::Clippy | Kind::Fix | Kind::Run | Kind::Format => {}
+            }
         }
+
+        this
     }
 
     pub fn execute_cli(&self) {
diff --git a/src/bootstrap/builder/tests.rs b/src/bootstrap/builder/tests.rs
index 6684ca82c27..4293844fcbf 100644
--- a/src/bootstrap/builder/tests.rs
+++ b/src/bootstrap/builder/tests.rs
@@ -2,8 +2,6 @@ use super::*;
 use crate::config::{Config, TargetSelection};
 use std::thread;
 
-use pretty_assertions::assert_eq;
-
 fn configure(host: &[&str], target: &[&str]) -> Config {
     let mut config = Config::default_opts();
     // don't save toolstates
@@ -35,463 +33,564 @@ fn first<A, B>(v: Vec<(A, B)>) -> Vec<A> {
     v.into_iter().map(|(a, _)| a).collect::<Vec<_>>()
 }
 
-#[test]
-fn dist_baseline() {
-    let build = Build::new(configure(&[], &[]));
-    let mut builder = Builder::new(&build);
-    builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Dist), &[]);
-
-    let a = TargetSelection::from_user("A");
-
-    assert_eq!(first(builder.cache.all::<dist::Docs>()), &[dist::Docs { host: a },]);
-    assert_eq!(first(builder.cache.all::<dist::Mingw>()), &[dist::Mingw { host: a },]);
-    assert_eq!(
-        first(builder.cache.all::<dist::Rustc>()),
-        &[dist::Rustc { compiler: Compiler { host: a, stage: 2 } },]
-    );
-    assert_eq!(
-        first(builder.cache.all::<dist::Std>()),
-        &[dist::Std { compiler: Compiler { host: a, stage: 1 }, target: a },]
-    );
-    assert_eq!(first(builder.cache.all::<dist::Src>()), &[dist::Src]);
-    // Make sure rustdoc is only built once.
-    assert_eq!(
-        first(builder.cache.all::<tool::Rustdoc>()),
-        &[tool::Rustdoc { compiler: Compiler { host: a, stage: 2 } },]
-    );
-}
-
-#[test]
-fn dist_with_targets() {
-    let build = Build::new(configure(&[], &["B"]));
-    let mut builder = Builder::new(&build);
-    builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Dist), &[]);
-
-    let a = TargetSelection::from_user("A");
-    let b = TargetSelection::from_user("B");
-
-    assert_eq!(
-        first(builder.cache.all::<dist::Docs>()),
-        &[dist::Docs { host: a }, dist::Docs { host: b },]
-    );
-    assert_eq!(
-        first(builder.cache.all::<dist::Mingw>()),
-        &[dist::Mingw { host: a }, dist::Mingw { host: b },]
-    );
-    assert_eq!(
-        first(builder.cache.all::<dist::Rustc>()),
-        &[dist::Rustc { compiler: Compiler { host: a, stage: 2 } },]
-    );
-    assert_eq!(
-        first(builder.cache.all::<dist::Std>()),
-        &[
-            dist::Std { compiler: Compiler { host: a, stage: 1 }, target: a },
-            dist::Std { compiler: Compiler { host: a, stage: 2 }, target: b },
-        ]
-    );
-    assert_eq!(first(builder.cache.all::<dist::Src>()), &[dist::Src]);
-}
-
-#[test]
-fn dist_with_hosts() {
-    let build = Build::new(configure(&["B"], &[]));
-    let mut builder = Builder::new(&build);
-    builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Dist), &[]);
-
-    let a = TargetSelection::from_user("A");
-    let b = TargetSelection::from_user("B");
-
-    assert_eq!(
-        first(builder.cache.all::<dist::Docs>()),
-        &[dist::Docs { host: a }, dist::Docs { host: b },]
-    );
-    assert_eq!(
-        first(builder.cache.all::<dist::Mingw>()),
-        &[dist::Mingw { host: a }, dist::Mingw { host: b },]
-    );
-    assert_eq!(
-        first(builder.cache.all::<dist::Rustc>()),
-        &[
-            dist::Rustc { compiler: Compiler { host: a, stage: 2 } },
-            dist::Rustc { compiler: Compiler { host: b, stage: 2 } },
-        ]
-    );
-    assert_eq!(
-        first(builder.cache.all::<dist::Std>()),
-        &[
-            dist::Std { compiler: Compiler { host: a, stage: 1 }, target: a },
-            dist::Std { compiler: Compiler { host: a, stage: 1 }, target: b },
-        ]
-    );
-    assert_eq!(first(builder.cache.all::<dist::Src>()), &[dist::Src]);
-}
-
-#[test]
-fn dist_only_cross_host() {
-    let a = TargetSelection::from_user("A");
-    let b = TargetSelection::from_user("B");
-    let mut build = Build::new(configure(&["B"], &[]));
-    build.config.docs = false;
-    build.config.extended = true;
-    build.hosts = vec![b];
-    let mut builder = Builder::new(&build);
-    builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Dist), &[]);
-
-    assert_eq!(
-        first(builder.cache.all::<dist::Rustc>()),
-        &[dist::Rustc { compiler: Compiler { host: b, stage: 2 } },]
-    );
-    assert_eq!(
-        first(builder.cache.all::<compile::Rustc>()),
-        &[
-            compile::Rustc { compiler: Compiler { host: a, stage: 0 }, target: a },
-            compile::Rustc { compiler: Compiler { host: a, stage: 1 }, target: b },
-        ]
-    );
-}
-
-#[test]
-fn dist_with_targets_and_hosts() {
-    let build = Build::new(configure(&["B"], &["C"]));
-    let mut builder = Builder::new(&build);
-    builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Dist), &[]);
-
-    let a = TargetSelection::from_user("A");
-    let b = TargetSelection::from_user("B");
-    let c = TargetSelection::from_user("C");
-
-    assert_eq!(
-        first(builder.cache.all::<dist::Docs>()),
-        &[dist::Docs { host: a }, dist::Docs { host: b }, dist::Docs { host: c },]
-    );
-    assert_eq!(
-        first(builder.cache.all::<dist::Mingw>()),
-        &[dist::Mingw { host: a }, dist::Mingw { host: b }, dist::Mingw { host: c },]
-    );
-    assert_eq!(
-        first(builder.cache.all::<dist::Rustc>()),
-        &[
-            dist::Rustc { compiler: Compiler { host: a, stage: 2 } },
-            dist::Rustc { compiler: Compiler { host: b, stage: 2 } },
-        ]
-    );
-    assert_eq!(
-        first(builder.cache.all::<dist::Std>()),
-        &[
-            dist::Std { compiler: Compiler { host: a, stage: 1 }, target: a },
-            dist::Std { compiler: Compiler { host: a, stage: 1 }, target: b },
-            dist::Std { compiler: Compiler { host: a, stage: 2 }, target: c },
-        ]
-    );
-    assert_eq!(first(builder.cache.all::<dist::Src>()), &[dist::Src]);
-}
-
-#[test]
-fn dist_with_target_flag() {
-    let mut config = configure(&["B"], &["C"]);
-    config.skip_only_host_steps = true; // as-if --target=C was passed
-    let build = Build::new(config);
-    let mut builder = Builder::new(&build);
-    builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Dist), &[]);
-
-    let a = TargetSelection::from_user("A");
-    let b = TargetSelection::from_user("B");
-    let c = TargetSelection::from_user("C");
-
-    assert_eq!(
-        first(builder.cache.all::<dist::Docs>()),
-        &[dist::Docs { host: a }, dist::Docs { host: b }, dist::Docs { host: c },]
-    );
-    assert_eq!(
-        first(builder.cache.all::<dist::Mingw>()),
-        &[dist::Mingw { host: a }, dist::Mingw { host: b }, dist::Mingw { host: c },]
-    );
-    assert_eq!(first(builder.cache.all::<dist::Rustc>()), &[]);
-    assert_eq!(
-        first(builder.cache.all::<dist::Std>()),
-        &[
-            dist::Std { compiler: Compiler { host: a, stage: 1 }, target: a },
-            dist::Std { compiler: Compiler { host: a, stage: 1 }, target: b },
-            dist::Std { compiler: Compiler { host: a, stage: 2 }, target: c },
-        ]
-    );
-    assert_eq!(first(builder.cache.all::<dist::Src>()), &[]);
-}
-
-#[test]
-fn dist_with_same_targets_and_hosts() {
-    let build = Build::new(configure(&["B"], &["B"]));
-    let mut builder = Builder::new(&build);
-    builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Dist), &[]);
-
-    let a = TargetSelection::from_user("A");
-    let b = TargetSelection::from_user("B");
-
-    assert_eq!(
-        first(builder.cache.all::<dist::Docs>()),
-        &[dist::Docs { host: a }, dist::Docs { host: b },]
-    );
-    assert_eq!(
-        first(builder.cache.all::<dist::Mingw>()),
-        &[dist::Mingw { host: a }, dist::Mingw { host: b },]
-    );
-    assert_eq!(
-        first(builder.cache.all::<dist::Rustc>()),
-        &[
-            dist::Rustc { compiler: Compiler { host: a, stage: 2 } },
-            dist::Rustc { compiler: Compiler { host: b, stage: 2 } },
-        ]
-    );
-    assert_eq!(
-        first(builder.cache.all::<dist::Std>()),
-        &[
-            dist::Std { compiler: Compiler { host: a, stage: 1 }, target: a },
-            dist::Std { compiler: Compiler { host: a, stage: 1 }, target: b },
-        ]
-    );
-    assert_eq!(first(builder.cache.all::<dist::Src>()), &[dist::Src]);
-    assert_eq!(
-        first(builder.cache.all::<compile::Std>()),
-        &[
-            compile::Std { compiler: Compiler { host: a, stage: 0 }, target: a },
-            compile::Std { compiler: Compiler { host: a, stage: 1 }, target: a },
-            compile::Std { compiler: Compiler { host: a, stage: 2 }, target: a },
-            compile::Std { compiler: Compiler { host: a, stage: 1 }, target: b },
-            compile::Std { compiler: Compiler { host: a, stage: 2 }, target: b },
-        ]
-    );
-    assert_eq!(
-        first(builder.cache.all::<compile::Assemble>()),
-        &[
-            compile::Assemble { target_compiler: Compiler { host: a, stage: 0 } },
-            compile::Assemble { target_compiler: Compiler { host: a, stage: 1 } },
-            compile::Assemble { target_compiler: Compiler { host: a, stage: 2 } },
-            compile::Assemble { target_compiler: Compiler { host: b, stage: 2 } },
-        ]
-    );
-}
-
-#[test]
-fn build_default() {
-    let build = Build::new(configure(&["B"], &["C"]));
-    let mut builder = Builder::new(&build);
-    builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Build), &[]);
-
-    let a = TargetSelection::from_user("A");
-    let b = TargetSelection::from_user("B");
-    let c = TargetSelection::from_user("C");
-
-    assert_eq!(
-        first(builder.cache.all::<compile::Std>()),
-        &[
-            compile::Std { compiler: Compiler { host: a, stage: 0 }, target: a },
-            compile::Std { compiler: Compiler { host: a, stage: 1 }, target: a },
-            compile::Std { compiler: Compiler { host: a, stage: 2 }, target: a },
-            compile::Std { compiler: Compiler { host: b, stage: 2 }, target: a },
-            compile::Std { compiler: Compiler { host: a, stage: 1 }, target: b },
-            compile::Std { compiler: Compiler { host: a, stage: 2 }, target: b },
-            compile::Std { compiler: Compiler { host: b, stage: 2 }, target: b },
-            compile::Std { compiler: Compiler { host: a, stage: 2 }, target: c },
-            compile::Std { compiler: Compiler { host: b, stage: 2 }, target: c },
-        ]
-    );
-    assert!(!builder.cache.all::<compile::Assemble>().is_empty());
-    assert_eq!(
-        first(builder.cache.all::<compile::Rustc>()),
-        &[
-            compile::Rustc { compiler: Compiler { host: a, stage: 0 }, target: a },
-            compile::Rustc { compiler: Compiler { host: a, stage: 1 }, target: a },
-            compile::Rustc { compiler: Compiler { host: a, stage: 2 }, target: a },
-            compile::Rustc { compiler: Compiler { host: b, stage: 2 }, target: a },
-            compile::Rustc { compiler: Compiler { host: a, stage: 1 }, target: b },
-            compile::Rustc { compiler: Compiler { host: a, stage: 2 }, target: b },
-            compile::Rustc { compiler: Compiler { host: b, stage: 2 }, target: b },
-        ]
-    );
-}
-
-#[test]
-fn build_with_target_flag() {
-    let mut config = configure(&["B"], &["C"]);
-    config.skip_only_host_steps = true;
-    let build = Build::new(config);
-    let mut builder = Builder::new(&build);
-    builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Build), &[]);
-
-    let a = TargetSelection::from_user("A");
-    let b = TargetSelection::from_user("B");
-    let c = TargetSelection::from_user("C");
-
-    assert_eq!(
-        first(builder.cache.all::<compile::Std>()),
-        &[
-            compile::Std { compiler: Compiler { host: a, stage: 0 }, target: a },
-            compile::Std { compiler: Compiler { host: a, stage: 1 }, target: a },
-            compile::Std { compiler: Compiler { host: a, stage: 2 }, target: a },
-            compile::Std { compiler: Compiler { host: b, stage: 2 }, target: a },
-            compile::Std { compiler: Compiler { host: a, stage: 1 }, target: b },
-            compile::Std { compiler: Compiler { host: a, stage: 2 }, target: b },
-            compile::Std { compiler: Compiler { host: b, stage: 2 }, target: b },
-            compile::Std { compiler: Compiler { host: a, stage: 2 }, target: c },
-            compile::Std { compiler: Compiler { host: b, stage: 2 }, target: c },
-        ]
-    );
-    assert_eq!(
-        first(builder.cache.all::<compile::Assemble>()),
-        &[
-            compile::Assemble { target_compiler: Compiler { host: a, stage: 0 } },
-            compile::Assemble { target_compiler: Compiler { host: a, stage: 1 } },
-            compile::Assemble { target_compiler: Compiler { host: a, stage: 2 } },
-            compile::Assemble { target_compiler: Compiler { host: b, stage: 2 } },
-        ]
-    );
-    assert_eq!(
-        first(builder.cache.all::<compile::Rustc>()),
-        &[
-            compile::Rustc { compiler: Compiler { host: a, stage: 0 }, target: a },
-            compile::Rustc { compiler: Compiler { host: a, stage: 1 }, target: a },
-            compile::Rustc { compiler: Compiler { host: a, stage: 1 }, target: b },
-        ]
-    );
-}
-
-#[test]
-fn test_with_no_doc_stage0() {
-    let mut config = configure(&[], &[]);
-    config.stage = Some(0);
-    config.cmd = Subcommand::Test {
-        paths: vec!["library/std".into()],
-        test_args: vec![],
-        rustc_args: vec![],
-        fail_fast: true,
-        doc_tests: DocTests::No,
-        bless: false,
-        compare_mode: None,
-        rustfix_coverage: false,
-        pass: None,
-    };
-
-    let build = Build::new(config);
-    let mut builder = Builder::new(&build);
-
-    let host = TargetSelection::from_user("A");
-
-    builder
-        .run_step_descriptions(&[StepDescription::from::<test::Crate>()], &["library/std".into()]);
-
-    // Ensure we don't build any compiler artifacts.
-    assert!(!builder.cache.contains::<compile::Rustc>());
-    assert_eq!(
-        first(builder.cache.all::<test::Crate>()),
-        &[test::Crate {
-            compiler: Compiler { host, stage: 0 },
-            target: host,
-            mode: Mode::Std,
-            test_kind: test::TestKind::Test,
-            krate: INTERNER.intern_str("std"),
-        },]
-    );
-}
-
-#[test]
-fn test_exclude() {
-    let mut config = configure(&[], &[]);
-    config.exclude = vec!["src/tools/tidy".into()];
-    config.cmd = Subcommand::Test {
-        paths: Vec::new(),
-        test_args: Vec::new(),
-        rustc_args: Vec::new(),
-        fail_fast: true,
-        doc_tests: DocTests::No,
-        bless: false,
-        compare_mode: None,
-        rustfix_coverage: false,
-        pass: None,
-    };
-
-    let build = Build::new(config);
-    let builder = Builder::new(&build);
-    builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Test), &[]);
-
-    // Ensure we have really excluded tidy
-    assert!(!builder.cache.contains::<test::Tidy>());
-
-    // Ensure other tests are not affected.
-    assert!(builder.cache.contains::<test::RustdocUi>());
-}
-
-#[test]
-fn doc_default() {
-    let mut config = configure(&[], &[]);
-    config.compiler_docs = true;
-    config.cmd = Subcommand::Doc { paths: Vec::new(), open: false };
-    let build = Build::new(config);
-    let mut builder = Builder::new(&build);
-    builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Doc), &[]);
-    let a = TargetSelection::from_user("A");
-
-    // error_index_generator uses stage 1 to share rustdoc artifacts with the
-    // rustdoc tool.
-    assert_eq!(
-        first(builder.cache.all::<doc::ErrorIndex>()),
-        &[doc::ErrorIndex { compiler: Compiler { host: a, stage: 1 }, target: a },]
-    );
-    assert_eq!(
-        first(builder.cache.all::<tool::ErrorIndex>()),
-        &[tool::ErrorIndex { compiler: Compiler { host: a, stage: 1 } }]
-    );
-    // This is actually stage 1, but Rustdoc::run swaps out the compiler with
-    // stage minus 1 if --stage is not 0. Very confusing!
-    assert_eq!(
-        first(builder.cache.all::<tool::Rustdoc>()),
-        &[tool::Rustdoc { compiler: Compiler { host: a, stage: 2 } },]
-    );
+mod defaults {
+    use super::{configure, first};
+    use crate::builder::*;
+    use crate::Config;
+    use pretty_assertions::assert_eq;
+
+    #[test]
+    fn build_default() {
+        let build = Build::new(configure(&[], &[]));
+        let mut builder = Builder::new(&build);
+        builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Build), &[]);
+
+        let a = TargetSelection::from_user("A");
+        assert_eq!(
+            first(builder.cache.all::<compile::Std>()),
+            &[
+                compile::Std { compiler: Compiler { host: a, stage: 0 }, target: a },
+                compile::Std { compiler: Compiler { host: a, stage: 1 }, target: a },
+            ]
+        );
+        assert!(!builder.cache.all::<compile::Assemble>().is_empty());
+        // Make sure rustdoc is only built once.
+        assert_eq!(
+            first(builder.cache.all::<tool::Rustdoc>()),
+            // Recall that rustdoc stages are off-by-one
+            // - this is the compiler it's _linked_ to, not built with.
+            &[tool::Rustdoc { compiler: Compiler { host: a, stage: 1 } }],
+        );
+        assert_eq!(
+            first(builder.cache.all::<compile::Rustc>()),
+            &[compile::Rustc { compiler: Compiler { host: a, stage: 0 }, target: a },]
+        );
+    }
+
+    #[test]
+    fn build_stage_0() {
+        let config = Config { stage: Some(0), ..configure(&[], &[]) };
+        let build = Build::new(config);
+        let mut builder = Builder::new(&build);
+        builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Build), &[]);
+
+        let a = TargetSelection::from_user("A");
+        assert_eq!(
+            first(builder.cache.all::<compile::Std>()),
+            &[compile::Std { compiler: Compiler { host: a, stage: 0 }, target: a },]
+        );
+        assert!(!builder.cache.all::<compile::Assemble>().is_empty());
+        assert_eq!(
+            first(builder.cache.all::<tool::Rustdoc>()),
+            // This is the beta rustdoc.
+            // Add an assert here to make sure this is the only rustdoc built.
+            &[tool::Rustdoc { compiler: Compiler { host: a, stage: 0 } }],
+        );
+        assert!(builder.cache.all::<compile::Rustc>().is_empty());
+    }
+
+    #[test]
+    fn doc_default() {
+        let mut config = configure(&[], &[]);
+        config.compiler_docs = true;
+        config.cmd = Subcommand::Doc { paths: Vec::new(), open: false };
+        let build = Build::new(config);
+        let mut builder = Builder::new(&build);
+        builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Doc), &[]);
+        let a = TargetSelection::from_user("A");
+
+        // error_index_generator uses stage 0 to share rustdoc artifacts with the
+        // rustdoc tool.
+        assert_eq!(
+            first(builder.cache.all::<doc::ErrorIndex>()),
+            &[doc::ErrorIndex { compiler: Compiler { host: a, stage: 0 }, target: a },]
+        );
+        assert_eq!(
+            first(builder.cache.all::<tool::ErrorIndex>()),
+            &[tool::ErrorIndex { compiler: Compiler { host: a, stage: 0 } }]
+        );
+        // docs should be built with the beta compiler, not with the stage0 artifacts.
+        // recall that rustdoc is off-by-one: `stage` is the compiler rustdoc is _linked_ to,
+        // not the one it was built by.
+        assert_eq!(
+            first(builder.cache.all::<tool::Rustdoc>()),
+            &[tool::Rustdoc { compiler: Compiler { host: a, stage: 0 } },]
+        );
+    }
 }
 
-//FIXME(mark-i-m): reinstate this test when things are fixed...
-//#[test]
-#[allow(dead_code)]
-fn test_docs() {
-    // Behavior of `x.py test` doing various documentation tests.
-    let mut config = configure(&[], &[]);
-    config.cmd = Subcommand::Test {
-        paths: vec![],
-        test_args: vec![],
-        rustc_args: vec![],
-        fail_fast: true,
-        doc_tests: DocTests::Yes,
-        bless: false,
-        compare_mode: None,
-        rustfix_coverage: false,
-        pass: None,
-    };
-    let build = Build::new(config);
-    let mut builder = Builder::new(&build);
-    builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Test), &[]);
-    let a = TargetSelection::from_user("A");
-
-    // error_index_generator uses stage 1 to share rustdoc artifacts with the
-    // rustdoc tool.
-    assert_eq!(
-        first(builder.cache.all::<doc::ErrorIndex>()),
-        &[doc::ErrorIndex { compiler: Compiler { host: a, stage: 1 }, target: a },]
-    );
-    assert_eq!(
-        first(builder.cache.all::<tool::ErrorIndex>()),
-        &[tool::ErrorIndex { compiler: Compiler { host: a, stage: 1 } }]
-    );
-    // Unfortunately rustdoc is built twice. Once from stage1 for compiletest
-    // (and other things), and once from stage0 for std crates. Ideally it
-    // would only be built once. If someone wants to fix this, it might be
-    // worth investigating if it would be possible to test std from stage1.
-    // Note that the stages here are +1 than what they actually are because
-    // Rustdoc::run swaps out the compiler with stage minus 1 if --stage is
-    // not 0.
-    assert_eq!(
-        first(builder.cache.all::<tool::Rustdoc>()),
-        &[
-            tool::Rustdoc { compiler: Compiler { host: a, stage: 1 } },
-            tool::Rustdoc { compiler: Compiler { host: a, stage: 2 } },
-        ]
-    );
+mod dist {
+    use super::{first, Config};
+    use crate::builder::*;
+    use pretty_assertions::assert_eq;
+
+    fn configure(host: &[&str], target: &[&str]) -> Config {
+        Config { stage: Some(2), ..super::configure(host, target) }
+    }
+
+    #[test]
+    fn dist_baseline() {
+        let build = Build::new(configure(&[], &[]));
+        let mut builder = Builder::new(&build);
+        builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Dist), &[]);
+
+        let a = TargetSelection::from_user("A");
+
+        assert_eq!(first(builder.cache.all::<dist::Docs>()), &[dist::Docs { host: a },]);
+        assert_eq!(first(builder.cache.all::<dist::Mingw>()), &[dist::Mingw { host: a },]);
+        assert_eq!(
+            first(builder.cache.all::<dist::Rustc>()),
+            &[dist::Rustc { compiler: Compiler { host: a, stage: 2 } },]
+        );
+        assert_eq!(
+            first(builder.cache.all::<dist::Std>()),
+            &[dist::Std { compiler: Compiler { host: a, stage: 1 }, target: a },]
+        );
+        assert_eq!(first(builder.cache.all::<dist::Src>()), &[dist::Src]);
+        // Make sure rustdoc is only built once.
+        assert_eq!(
+            first(builder.cache.all::<tool::Rustdoc>()),
+            &[tool::Rustdoc { compiler: Compiler { host: a, stage: 2 } },]
+        );
+    }
+
+    #[test]
+    fn dist_with_targets() {
+        let build = Build::new(configure(&[], &["B"]));
+        let mut builder = Builder::new(&build);
+        builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Dist), &[]);
+
+        let a = TargetSelection::from_user("A");
+        let b = TargetSelection::from_user("B");
+
+        assert_eq!(
+            first(builder.cache.all::<dist::Docs>()),
+            &[dist::Docs { host: a }, dist::Docs { host: b },]
+        );
+        assert_eq!(
+            first(builder.cache.all::<dist::Mingw>()),
+            &[dist::Mingw { host: a }, dist::Mingw { host: b },]
+        );
+        assert_eq!(
+            first(builder.cache.all::<dist::Rustc>()),
+            &[dist::Rustc { compiler: Compiler { host: a, stage: 2 } },]
+        );
+        assert_eq!(
+            first(builder.cache.all::<dist::Std>()),
+            &[
+                dist::Std { compiler: Compiler { host: a, stage: 1 }, target: a },
+                dist::Std { compiler: Compiler { host: a, stage: 2 }, target: b },
+            ]
+        );
+        assert_eq!(first(builder.cache.all::<dist::Src>()), &[dist::Src]);
+    }
+
+    #[test]
+    fn dist_with_hosts() {
+        let build = Build::new(configure(&["B"], &[]));
+        let mut builder = Builder::new(&build);
+        builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Dist), &[]);
+
+        let a = TargetSelection::from_user("A");
+        let b = TargetSelection::from_user("B");
+
+        assert_eq!(
+            first(builder.cache.all::<dist::Docs>()),
+            &[dist::Docs { host: a }, dist::Docs { host: b },]
+        );
+        assert_eq!(
+            first(builder.cache.all::<dist::Mingw>()),
+            &[dist::Mingw { host: a }, dist::Mingw { host: b },]
+        );
+        assert_eq!(
+            first(builder.cache.all::<dist::Rustc>()),
+            &[
+                dist::Rustc { compiler: Compiler { host: a, stage: 2 } },
+                dist::Rustc { compiler: Compiler { host: b, stage: 2 } },
+            ]
+        );
+        assert_eq!(
+            first(builder.cache.all::<dist::Std>()),
+            &[
+                dist::Std { compiler: Compiler { host: a, stage: 1 }, target: a },
+                dist::Std { compiler: Compiler { host: a, stage: 1 }, target: b },
+            ]
+        );
+        assert_eq!(first(builder.cache.all::<dist::Src>()), &[dist::Src]);
+    }
+
+    #[test]
+    fn dist_only_cross_host() {
+        let a = TargetSelection::from_user("A");
+        let b = TargetSelection::from_user("B");
+        let mut build = Build::new(configure(&["B"], &[]));
+        build.config.docs = false;
+        build.config.extended = true;
+        build.hosts = vec![b];
+        let mut builder = Builder::new(&build);
+        builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Dist), &[]);
+
+        assert_eq!(
+            first(builder.cache.all::<dist::Rustc>()),
+            &[dist::Rustc { compiler: Compiler { host: b, stage: 2 } },]
+        );
+        assert_eq!(
+            first(builder.cache.all::<compile::Rustc>()),
+            &[
+                compile::Rustc { compiler: Compiler { host: a, stage: 0 }, target: a },
+                compile::Rustc { compiler: Compiler { host: a, stage: 1 }, target: b },
+            ]
+        );
+    }
+
+    #[test]
+    fn dist_with_targets_and_hosts() {
+        let build = Build::new(configure(&["B"], &["C"]));
+        let mut builder = Builder::new(&build);
+        builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Dist), &[]);
+
+        let a = TargetSelection::from_user("A");
+        let b = TargetSelection::from_user("B");
+        let c = TargetSelection::from_user("C");
+
+        assert_eq!(
+            first(builder.cache.all::<dist::Docs>()),
+            &[dist::Docs { host: a }, dist::Docs { host: b }, dist::Docs { host: c },]
+        );
+        assert_eq!(
+            first(builder.cache.all::<dist::Mingw>()),
+            &[dist::Mingw { host: a }, dist::Mingw { host: b }, dist::Mingw { host: c },]
+        );
+        assert_eq!(
+            first(builder.cache.all::<dist::Rustc>()),
+            &[
+                dist::Rustc { compiler: Compiler { host: a, stage: 2 } },
+                dist::Rustc { compiler: Compiler { host: b, stage: 2 } },
+            ]
+        );
+        assert_eq!(
+            first(builder.cache.all::<dist::Std>()),
+            &[
+                dist::Std { compiler: Compiler { host: a, stage: 1 }, target: a },
+                dist::Std { compiler: Compiler { host: a, stage: 1 }, target: b },
+                dist::Std { compiler: Compiler { host: a, stage: 2 }, target: c },
+            ]
+        );
+        assert_eq!(first(builder.cache.all::<dist::Src>()), &[dist::Src]);
+    }
+
+    #[test]
+    fn dist_with_target_flag() {
+        let mut config = configure(&["B"], &["C"]);
+        config.skip_only_host_steps = true; // as-if --target=C was passed
+        let build = Build::new(config);
+        let mut builder = Builder::new(&build);
+        builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Dist), &[]);
+
+        let a = TargetSelection::from_user("A");
+        let b = TargetSelection::from_user("B");
+        let c = TargetSelection::from_user("C");
+
+        assert_eq!(
+            first(builder.cache.all::<dist::Docs>()),
+            &[dist::Docs { host: a }, dist::Docs { host: b }, dist::Docs { host: c },]
+        );
+        assert_eq!(
+            first(builder.cache.all::<dist::Mingw>()),
+            &[dist::Mingw { host: a }, dist::Mingw { host: b }, dist::Mingw { host: c },]
+        );
+        assert_eq!(first(builder.cache.all::<dist::Rustc>()), &[]);
+        assert_eq!(
+            first(builder.cache.all::<dist::Std>()),
+            &[
+                dist::Std { compiler: Compiler { host: a, stage: 1 }, target: a },
+                dist::Std { compiler: Compiler { host: a, stage: 1 }, target: b },
+                dist::Std { compiler: Compiler { host: a, stage: 2 }, target: c },
+            ]
+        );
+        assert_eq!(first(builder.cache.all::<dist::Src>()), &[]);
+    }
+
+    #[test]
+    fn dist_with_same_targets_and_hosts() {
+        let build = Build::new(configure(&["B"], &["B"]));
+        let mut builder = Builder::new(&build);
+        builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Dist), &[]);
+
+        let a = TargetSelection::from_user("A");
+        let b = TargetSelection::from_user("B");
+
+        assert_eq!(
+            first(builder.cache.all::<dist::Docs>()),
+            &[dist::Docs { host: a }, dist::Docs { host: b },]
+        );
+        assert_eq!(
+            first(builder.cache.all::<dist::Mingw>()),
+            &[dist::Mingw { host: a }, dist::Mingw { host: b },]
+        );
+        assert_eq!(
+            first(builder.cache.all::<dist::Rustc>()),
+            &[
+                dist::Rustc { compiler: Compiler { host: a, stage: 2 } },
+                dist::Rustc { compiler: Compiler { host: b, stage: 2 } },
+            ]
+        );
+        assert_eq!(
+            first(builder.cache.all::<dist::Std>()),
+            &[
+                dist::Std { compiler: Compiler { host: a, stage: 1 }, target: a },
+                dist::Std { compiler: Compiler { host: a, stage: 1 }, target: b },
+            ]
+        );
+        assert_eq!(first(builder.cache.all::<dist::Src>()), &[dist::Src]);
+        assert_eq!(
+            first(builder.cache.all::<compile::Std>()),
+            &[
+                compile::Std { compiler: Compiler { host: a, stage: 0 }, target: a },
+                compile::Std { compiler: Compiler { host: a, stage: 1 }, target: a },
+                compile::Std { compiler: Compiler { host: a, stage: 2 }, target: a },
+                compile::Std { compiler: Compiler { host: a, stage: 1 }, target: b },
+                compile::Std { compiler: Compiler { host: a, stage: 2 }, target: b },
+            ]
+        );
+        assert_eq!(
+            first(builder.cache.all::<compile::Assemble>()),
+            &[
+                compile::Assemble { target_compiler: Compiler { host: a, stage: 0 } },
+                compile::Assemble { target_compiler: Compiler { host: a, stage: 1 } },
+                compile::Assemble { target_compiler: Compiler { host: a, stage: 2 } },
+                compile::Assemble { target_compiler: Compiler { host: b, stage: 2 } },
+            ]
+        );
+    }
+
+    #[test]
+    fn build_all() {
+        let build = Build::new(configure(&["B"], &["C"]));
+        let mut builder = Builder::new(&build);
+        builder.run_step_descriptions(
+            &Builder::get_step_descriptions(Kind::Build),
+            &["src/rustc".into(), "library/std".into()],
+        );
+
+        let a = TargetSelection::from_user("A");
+        let b = TargetSelection::from_user("B");
+        let c = TargetSelection::from_user("C");
+
+        assert_eq!(
+            first(builder.cache.all::<compile::Std>()),
+            &[
+                compile::Std { compiler: Compiler { host: a, stage: 0 }, target: a },
+                compile::Std { compiler: Compiler { host: a, stage: 1 }, target: a },
+                compile::Std { compiler: Compiler { host: a, stage: 2 }, target: a },
+                compile::Std { compiler: Compiler { host: b, stage: 2 }, target: a },
+                compile::Std { compiler: Compiler { host: a, stage: 1 }, target: b },
+                compile::Std { compiler: Compiler { host: a, stage: 2 }, target: b },
+                compile::Std { compiler: Compiler { host: b, stage: 2 }, target: b },
+                compile::Std { compiler: Compiler { host: a, stage: 2 }, target: c },
+                compile::Std { compiler: Compiler { host: b, stage: 2 }, target: c },
+            ]
+        );
+        assert!(!builder.cache.all::<compile::Assemble>().is_empty());
+        assert_eq!(
+            first(builder.cache.all::<compile::Rustc>()),
+            &[
+                compile::Rustc { compiler: Compiler { host: a, stage: 0 }, target: a },
+                compile::Rustc { compiler: Compiler { host: a, stage: 1 }, target: a },
+                compile::Rustc { compiler: Compiler { host: a, stage: 2 }, target: a },
+                compile::Rustc { compiler: Compiler { host: b, stage: 2 }, target: a },
+                compile::Rustc { compiler: Compiler { host: a, stage: 1 }, target: b },
+                compile::Rustc { compiler: Compiler { host: a, stage: 2 }, target: b },
+                compile::Rustc { compiler: Compiler { host: b, stage: 2 }, target: b },
+            ]
+        );
+    }
+
+    #[test]
+    fn build_with_target_flag() {
+        let mut config = configure(&["B"], &["C"]);
+        config.skip_only_host_steps = true;
+        let build = Build::new(config);
+        let mut builder = Builder::new(&build);
+        builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Build), &[]);
+
+        let a = TargetSelection::from_user("A");
+        let b = TargetSelection::from_user("B");
+        let c = TargetSelection::from_user("C");
+
+        assert_eq!(
+            first(builder.cache.all::<compile::Std>()),
+            &[
+                compile::Std { compiler: Compiler { host: a, stage: 0 }, target: a },
+                compile::Std { compiler: Compiler { host: a, stage: 1 }, target: a },
+                compile::Std { compiler: Compiler { host: a, stage: 2 }, target: a },
+                compile::Std { compiler: Compiler { host: b, stage: 2 }, target: a },
+                compile::Std { compiler: Compiler { host: a, stage: 1 }, target: b },
+                compile::Std { compiler: Compiler { host: a, stage: 2 }, target: b },
+                compile::Std { compiler: Compiler { host: b, stage: 2 }, target: b },
+                compile::Std { compiler: Compiler { host: a, stage: 2 }, target: c },
+                compile::Std { compiler: Compiler { host: b, stage: 2 }, target: c },
+            ]
+        );
+        assert_eq!(
+            first(builder.cache.all::<compile::Assemble>()),
+            &[
+                compile::Assemble { target_compiler: Compiler { host: a, stage: 0 } },
+                compile::Assemble { target_compiler: Compiler { host: a, stage: 1 } },
+                compile::Assemble { target_compiler: Compiler { host: a, stage: 2 } },
+                compile::Assemble { target_compiler: Compiler { host: b, stage: 2 } },
+            ]
+        );
+        assert_eq!(
+            first(builder.cache.all::<compile::Rustc>()),
+            &[
+                compile::Rustc { compiler: Compiler { host: a, stage: 0 }, target: a },
+                compile::Rustc { compiler: Compiler { host: a, stage: 1 }, target: a },
+                compile::Rustc { compiler: Compiler { host: a, stage: 1 }, target: b },
+            ]
+        );
+    }
+
+    #[test]
+    fn test_with_no_doc_stage0() {
+        let mut config = configure(&[], &[]);
+        config.stage = Some(0);
+        config.cmd = Subcommand::Test {
+            paths: vec!["library/std".into()],
+            test_args: vec![],
+            rustc_args: vec![],
+            fail_fast: true,
+            doc_tests: DocTests::No,
+            bless: false,
+            compare_mode: None,
+            rustfix_coverage: false,
+            pass: None,
+        };
+
+        let build = Build::new(config);
+        let mut builder = Builder::new(&build);
+
+        let host = TargetSelection::from_user("A");
+
+        builder.run_step_descriptions(
+            &[StepDescription::from::<test::Crate>()],
+            &["library/std".into()],
+        );
+
+        // Ensure we don't build any compiler artifacts.
+        assert!(!builder.cache.contains::<compile::Rustc>());
+        assert_eq!(
+            first(builder.cache.all::<test::Crate>()),
+            &[test::Crate {
+                compiler: Compiler { host, stage: 0 },
+                target: host,
+                mode: Mode::Std,
+                test_kind: test::TestKind::Test,
+                krate: INTERNER.intern_str("std"),
+            },]
+        );
+    }
+
+    #[test]
+    fn test_exclude() {
+        let mut config = configure(&[], &[]);
+        config.exclude = vec!["src/tools/tidy".into()];
+        config.cmd = Subcommand::Test {
+            paths: Vec::new(),
+            test_args: Vec::new(),
+            rustc_args: Vec::new(),
+            fail_fast: true,
+            doc_tests: DocTests::No,
+            bless: false,
+            compare_mode: None,
+            rustfix_coverage: false,
+            pass: None,
+        };
+
+        let build = Build::new(config);
+        let builder = Builder::new(&build);
+        builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Test), &[]);
+
+        // Ensure we have really excluded tidy
+        assert!(!builder.cache.contains::<test::Tidy>());
+
+        // Ensure other tests are not affected.
+        assert!(builder.cache.contains::<test::RustdocUi>());
+    }
+
+    #[test]
+    fn doc_ci() {
+        let mut config = configure(&[], &[]);
+        config.compiler_docs = true;
+        config.cmd = Subcommand::Doc { paths: Vec::new(), open: false };
+        let build = Build::new(config);
+        let mut builder = Builder::new(&build);
+        builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Doc), &[]);
+        let a = TargetSelection::from_user("A");
+
+        // error_index_generator uses stage 1 to share rustdoc artifacts with the
+        // rustdoc tool.
+        assert_eq!(
+            first(builder.cache.all::<doc::ErrorIndex>()),
+            &[doc::ErrorIndex { compiler: Compiler { host: a, stage: 1 }, target: a },]
+        );
+        assert_eq!(
+            first(builder.cache.all::<tool::ErrorIndex>()),
+            &[tool::ErrorIndex { compiler: Compiler { host: a, stage: 1 } }]
+        );
+        // This is actually stage 1, but Rustdoc::run swaps out the compiler with
+        // stage minus 1 if --stage is not 0. Very confusing!
+        assert_eq!(
+            first(builder.cache.all::<tool::Rustdoc>()),
+            &[tool::Rustdoc { compiler: Compiler { host: a, stage: 2 } },]
+        );
+    }
+
+    //FIXME(mark-i-m): reinstate this test when things are fixed...
+    //#[test]
+    #[allow(dead_code)]
+    fn test_docs() {
+        // Behavior of `x.py test` doing various documentation tests.
+        let mut config = configure(&[], &[]);
+        config.cmd = Subcommand::Test {
+            paths: vec![],
+            test_args: vec![],
+            rustc_args: vec![],
+            fail_fast: true,
+            doc_tests: DocTests::Yes,
+            bless: false,
+            compare_mode: None,
+            rustfix_coverage: false,
+            pass: None,
+        };
+        let build = Build::new(config);
+        let mut builder = Builder::new(&build);
+        builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Test), &[]);
+        let a = TargetSelection::from_user("A");
+
+        // error_index_generator uses stage 1 to share rustdoc artifacts with the
+        // rustdoc tool.
+        assert_eq!(
+            first(builder.cache.all::<doc::ErrorIndex>()),
+            &[doc::ErrorIndex { compiler: Compiler { host: a, stage: 1 }, target: a },]
+        );
+        assert_eq!(
+            first(builder.cache.all::<tool::ErrorIndex>()),
+            &[tool::ErrorIndex { compiler: Compiler { host: a, stage: 1 } }]
+        );
+        // Unfortunately rustdoc is built twice. Once from stage1 for compiletest
+        // (and other things), and once from stage0 for std crates. Ideally it
+        // would only be built once. If someone wants to fix this, it might be
+        // worth investigating if it would be possible to test std from stage1.
+        // Note that the stages here are +1 than what they actually are because
+        // Rustdoc::run swaps out the compiler with stage minus 1 if --stage is
+        // not 0.
+        assert_eq!(
+            first(builder.cache.all::<tool::Rustdoc>()),
+            &[
+                tool::Rustdoc { compiler: Compiler { host: a, stage: 1 } },
+                tool::Rustdoc { compiler: Compiler { host: a, stage: 2 } },
+            ]
+        );
+    }
 }
diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs
index e3d1e005373..373e240cb8e 100644
--- a/src/bootstrap/compile.rs
+++ b/src/bootstrap/compile.rs
@@ -446,10 +446,10 @@ pub struct Rustc {
 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")
+        run.path("src/rustc")
     }
 
     fn make_run(run: RunConfig<'_>) {
diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs
index d71f3170420..d64ca95d243 100644
--- a/src/bootstrap/config.rs
+++ b/src/bootstrap/config.rs
@@ -734,7 +734,7 @@ impl Config {
 
         let with_defaults = |debuginfo_level_specific: Option<u32>| {
             debuginfo_level_specific.or(debuginfo_level).unwrap_or(if debug == Some(true) {
-                2
+                1
             } else {
                 0
             })
diff --git a/src/bootstrap/flags.rs b/src/bootstrap/flags.rs
index a298b299667..56e4f0467cc 100644
--- a/src/bootstrap/flags.rs
+++ b/src/bootstrap/flags.rs
@@ -425,7 +425,7 @@ Arguments:
     This subcommand accepts a number of paths to tools to build and run. For
     example:
 
-        ./x.py run src/tool/expand-yaml-anchors
+        ./x.py run src/tools/expand-yaml-anchors
 
     At least a tool needs to be called.",
                 );
diff --git a/src/bootstrap/mk/Makefile.in b/src/bootstrap/mk/Makefile.in
index e5b9f27c258..1564cfb0619 100644
--- a/src/bootstrap/mk/Makefile.in
+++ b/src/bootstrap/mk/Makefile.in
@@ -9,8 +9,8 @@ endif
 BOOTSTRAP := $(CFG_PYTHON) $(CFG_SRC_DIR)src/bootstrap/bootstrap.py
 
 all:
-	$(Q)$(BOOTSTRAP) build $(BOOTSTRAP_ARGS)
-	$(Q)$(BOOTSTRAP) doc $(BOOTSTRAP_ARGS)
+	$(Q)$(BOOTSTRAP) build --stage 2 $(BOOTSTRAP_ARGS)
+	$(Q)$(BOOTSTRAP) doc --stage 2 $(BOOTSTRAP_ARGS)
 
 help:
 	$(Q)echo 'Welcome to the rustbuild build system!'
@@ -31,17 +31,17 @@ rustc-stage2:
 
 docs: doc
 doc:
-	$(Q)$(BOOTSTRAP) doc $(BOOTSTRAP_ARGS)
+	$(Q)$(BOOTSTRAP) doc --stage 2 $(BOOTSTRAP_ARGS)
 nomicon:
-	$(Q)$(BOOTSTRAP) doc src/doc/nomicon $(BOOTSTRAP_ARGS)
+	$(Q)$(BOOTSTRAP) doc --stage 2 src/doc/nomicon $(BOOTSTRAP_ARGS)
 book:
-	$(Q)$(BOOTSTRAP) doc src/doc/book $(BOOTSTRAP_ARGS)
+	$(Q)$(BOOTSTRAP) doc --stage 2 src/doc/book $(BOOTSTRAP_ARGS)
 standalone-docs:
-	$(Q)$(BOOTSTRAP) doc src/doc $(BOOTSTRAP_ARGS)
+	$(Q)$(BOOTSTRAP) doc --stage 2 src/doc $(BOOTSTRAP_ARGS)
 check:
-	$(Q)$(BOOTSTRAP) test $(BOOTSTRAP_ARGS)
+	$(Q)$(BOOTSTRAP) test --stage 2 $(BOOTSTRAP_ARGS)
 check-aux:
-	$(Q)$(BOOTSTRAP) test \
+	$(Q)$(BOOTSTRAP) test --stage 2 \
 		src/tools/cargo \
 		src/tools/cargotest \
 		$(BOOTSTRAP_ARGS)
@@ -51,18 +51,18 @@ dist:
 	$(Q)$(BOOTSTRAP) dist $(BOOTSTRAP_ARGS)
 distcheck:
 	$(Q)$(BOOTSTRAP) dist $(BOOTSTRAP_ARGS)
-	$(Q)$(BOOTSTRAP) test distcheck $(BOOTSTRAP_ARGS)
+	$(Q)$(BOOTSTRAP) test --stage 2 distcheck $(BOOTSTRAP_ARGS)
 install:
 	$(Q)$(BOOTSTRAP) install $(BOOTSTRAP_ARGS)
 tidy:
-	$(Q)$(BOOTSTRAP) test src/tools/tidy $(BOOTSTRAP_ARGS)
+	$(Q)$(BOOTSTRAP) test --stage 2 src/tools/tidy $(BOOTSTRAP_ARGS)
 prepare:
-	$(Q)$(BOOTSTRAP) build nonexistent/path/to/trigger/cargo/metadata
+	$(Q)$(BOOTSTRAP) build --stage 2 nonexistent/path/to/trigger/cargo/metadata
 
 check-stage2-T-arm-linux-androideabi-H-x86_64-unknown-linux-gnu:
-	$(Q)$(BOOTSTRAP) test --target arm-linux-androideabi
+	$(Q)$(BOOTSTRAP) test --stage 2 --target arm-linux-androideabi
 check-stage2-T-x86_64-unknown-linux-musl-H-x86_64-unknown-linux-gnu:
-	$(Q)$(BOOTSTRAP) test --target x86_64-unknown-linux-musl
+	$(Q)$(BOOTSTRAP) test --stage 2 --target x86_64-unknown-linux-musl
 
 TESTS_IN_2 := \
 	src/test/ui \
@@ -70,18 +70,18 @@ TESTS_IN_2 := \
 	src/tools/linkchecker
 
 ci-subset-1:
-	$(Q)$(BOOTSTRAP) test $(TESTS_IN_2:%=--exclude %)
+	$(Q)$(BOOTSTRAP) test --stage 2 $(TESTS_IN_2:%=--exclude %)
 ci-subset-2:
-	$(Q)$(BOOTSTRAP) test $(TESTS_IN_2)
+	$(Q)$(BOOTSTRAP) test --stage 2 $(TESTS_IN_2)
 
 TESTS_IN_MINGW_2 := \
 	src/test/ui \
 	src/test/compile-fail
 
 ci-mingw-subset-1:
-	$(Q)$(BOOTSTRAP) test $(TESTS_IN_MINGW_2:%=--exclude %)
+	$(Q)$(BOOTSTRAP) test --stage 2 $(TESTS_IN_MINGW_2:%=--exclude %)
 ci-mingw-subset-2:
-	$(Q)$(BOOTSTRAP) test $(TESTS_IN_MINGW_2)
+	$(Q)$(BOOTSTRAP) test --stage 2 $(TESTS_IN_MINGW_2)
 
 
 .PHONY: dist
diff --git a/src/ci/azure-pipelines/auto.yml b/src/ci/azure-pipelines/auto.yml
index 06e284c763c..2185b0d30db 100644
--- a/src/ci/azure-pipelines/auto.yml
+++ b/src/ci/azure-pipelines/auto.yml
@@ -36,7 +36,7 @@ jobs:
       # Note that the compiler is compiled to target 10.8 here because the Xcode
       # version that we're using, 8.2, cannot compile LLVM for OSX 10.7.
       x86_64-apple:
-        SCRIPT: ./x.py test
+        SCRIPT: ./x.py --stage 2 test
         INITIAL_RUST_CONFIGURE_ARGS: --build=x86_64-apple-darwin --enable-sanitizers --enable-profiler --set rust.jemalloc
         RUSTC_RETRY_LINKER_ON_SEGFAULT: 1
         MACOSX_DEPLOYMENT_TARGET: 10.8
diff --git a/src/ci/docker/host-aarch64/aarch64-gnu/Dockerfile b/src/ci/docker/host-aarch64/aarch64-gnu/Dockerfile
index 114ac832cf5..b6cf60a5e15 100644
--- a/src/ci/docker/host-aarch64/aarch64-gnu/Dockerfile
+++ b/src/ci/docker/host-aarch64/aarch64-gnu/Dockerfile
@@ -23,4 +23,4 @@ ENV RUST_CONFIGURE_ARGS \
  --enable-sanitizers \
  --enable-profiler \
  --enable-compiler-docs
-ENV SCRIPT python3 ../x.py test
+ENV SCRIPT python3 ../x.py --stage 2 test
diff --git a/src/ci/docker/host-x86_64/arm-android/Dockerfile b/src/ci/docker/host-x86_64/arm-android/Dockerfile
index aa9335c473b..add2647fa1e 100644
--- a/src/ci/docker/host-x86_64/arm-android/Dockerfile
+++ b/src/ci/docker/host-x86_64/arm-android/Dockerfile
@@ -31,7 +31,7 @@ ENV TARGETS=arm-linux-androideabi
 
 ENV RUST_CONFIGURE_ARGS --arm-linux-androideabi-ndk=/android/ndk/arm-14
 
-ENV SCRIPT python3 ../x.py test --target $TARGETS
+ENV SCRIPT python3 ../x.py --stage 2 test --target $TARGETS
 
 COPY scripts/sccache.sh /scripts/
 RUN sh /scripts/sccache.sh
diff --git a/src/ci/docker/host-x86_64/armhf-gnu/Dockerfile b/src/ci/docker/host-x86_64/armhf-gnu/Dockerfile
index 71071761f05..1f3092c5513 100644
--- a/src/ci/docker/host-x86_64/armhf-gnu/Dockerfile
+++ b/src/ci/docker/host-x86_64/armhf-gnu/Dockerfile
@@ -78,6 +78,6 @@ COPY scripts/sccache.sh /scripts/
 RUN sh /scripts/sccache.sh
 
 ENV RUST_CONFIGURE_ARGS --qemu-armhf-rootfs=/tmp/rootfs
-ENV SCRIPT python3 ../x.py test --target arm-unknown-linux-gnueabihf
+ENV SCRIPT python3 ../x.py --stage 2 test --target arm-unknown-linux-gnueabihf
 
 ENV NO_CHANGE_USER=1
diff --git a/src/ci/docker/host-x86_64/disabled/asmjs/Dockerfile b/src/ci/docker/host-x86_64/disabled/asmjs/Dockerfile
index 3fa65511e94..a4d9f53ebab 100644
--- a/src/ci/docker/host-x86_64/disabled/asmjs/Dockerfile
+++ b/src/ci/docker/host-x86_64/disabled/asmjs/Dockerfile
@@ -33,7 +33,7 @@ ENV EMCC_CFLAGS=-O1
 # Emscripten installation is user-specific
 ENV NO_CHANGE_USER=1
 
-ENV SCRIPT python3 ../x.py test --target $TARGETS
+ENV SCRIPT python3 ../x.py --stage 2 test --target $TARGETS
 
 # This is almost identical to the wasm32-unknown-emscripten target, so
 # running with assertions again is not useful
diff --git a/src/ci/docker/host-x86_64/disabled/dist-armv7-android/Dockerfile b/src/ci/docker/host-x86_64/disabled/dist-armv7-android/Dockerfile
index 7227c41ccca..f986c38ea02 100644
--- a/src/ci/docker/host-x86_64/disabled/dist-armv7-android/Dockerfile
+++ b/src/ci/docker/host-x86_64/disabled/dist-armv7-android/Dockerfile
@@ -33,7 +33,7 @@ ENV RUST_CONFIGURE_ARGS \
 # build to finish we use --warn-unresolved-symbols. Note that the missing
 # symbols does not affect std, only the compiler (llvm) and cargo (openssl).
 ENV SCRIPT \
-  python3 ../x.py build src/llvm --host $HOSTS --target $HOSTS && \
+  python3 ../x.py --stage 2 build src/llvm --host $HOSTS --target $HOSTS && \
   (export RUSTFLAGS="\"-C link-arg=-Wl,--warn-unresolved-symbols\""; \
     rm /android/ndk/arm && \
     ln -s /android/ndk/arm-14 /android/ndk/arm && \
diff --git a/src/ci/docker/host-x86_64/disabled/dist-i686-android/Dockerfile b/src/ci/docker/host-x86_64/disabled/dist-i686-android/Dockerfile
index b74dcefa351..4dfbc725607 100644
--- a/src/ci/docker/host-x86_64/disabled/dist-i686-android/Dockerfile
+++ b/src/ci/docker/host-x86_64/disabled/dist-i686-android/Dockerfile
@@ -33,7 +33,7 @@ ENV RUST_CONFIGURE_ARGS \
 # build to finish we use --warn-unresolved-symbols. Note that the missing
 # symbols does not affect std, only the compiler (llvm) and cargo (openssl).
 ENV SCRIPT \
-  python3 ../x.py build src/llvm --host $HOSTS --target $HOSTS && \
+  python3 ../x.py --stage 2 build src/llvm --host $HOSTS --target $HOSTS && \
   (export RUSTFLAGS="\"-C link-arg=-Wl,--warn-unresolved-symbols\""; \
     rm /android/ndk/x86 && \
     ln -s /android/ndk/x86-14 /android/ndk/x86 && \
diff --git a/src/ci/docker/host-x86_64/disabled/riscv64gc-linux/Dockerfile b/src/ci/docker/host-x86_64/disabled/riscv64gc-linux/Dockerfile
index a938899636a..e3c35000eb8 100644
--- a/src/ci/docker/host-x86_64/disabled/riscv64gc-linux/Dockerfile
+++ b/src/ci/docker/host-x86_64/disabled/riscv64gc-linux/Dockerfile
@@ -97,6 +97,6 @@ COPY scripts/sccache.sh /scripts/
 RUN sh /scripts/sccache.sh
 
 ENV RUST_CONFIGURE_ARGS --qemu-riscv64-rootfs=/tmp/rootfs
-ENV SCRIPT python3 ../x.py test --target riscv64gc-unknown-linux-gnu
+ENV SCRIPT python3 ../x.py --stage 2 test --target riscv64gc-unknown-linux-gnu
 
 ENV NO_CHANGE_USER=1
diff --git a/src/ci/docker/host-x86_64/dist-i586-gnu-i586-i686-musl/Dockerfile b/src/ci/docker/host-x86_64/dist-i586-gnu-i586-i686-musl/Dockerfile
index 996fffeb871..162d7a1345c 100644
--- a/src/ci/docker/host-x86_64/dist-i586-gnu-i586-i686-musl/Dockerfile
+++ b/src/ci/docker/host-x86_64/dist-i586-gnu-i586-i686-musl/Dockerfile
@@ -46,5 +46,5 @@ ENV CFLAGS_i586_unknown_linux_musl=-Wa,-mrelax-relocations=no
 ENV TARGETS=i586-unknown-linux-gnu,i686-unknown-linux-musl
 
 ENV SCRIPT \
-      python3 ../x.py test --target $TARGETS && \
+      python3 ../x.py --stage 2 test --target $TARGETS && \
       python3 ../x.py dist --target $TARGETS,i586-unknown-linux-musl
diff --git a/src/ci/docker/host-x86_64/dist-various-1/Dockerfile b/src/ci/docker/host-x86_64/dist-various-1/Dockerfile
index ac228cfe01d..fdd777e824b 100644
--- a/src/ci/docker/host-x86_64/dist-various-1/Dockerfile
+++ b/src/ci/docker/host-x86_64/dist-various-1/Dockerfile
@@ -192,7 +192,7 @@ ENV RUST_CONFIGURE_ARGS \
       --disable-docs
 
 ENV SCRIPT \
-      python3 ../x.py test --target $RUN_MAKE_TARGETS src/test/run-make && \
+      python3 ../x.py --stage 2 test --target $RUN_MAKE_TARGETS src/test/run-make && \
       python3 ../x.py dist --target $TARGETS
 
 # sccache
diff --git a/src/ci/docker/host-x86_64/i686-gnu-nopt/Dockerfile b/src/ci/docker/host-x86_64/i686-gnu-nopt/Dockerfile
index 436215839f7..6a596b3465f 100644
--- a/src/ci/docker/host-x86_64/i686-gnu-nopt/Dockerfile
+++ b/src/ci/docker/host-x86_64/i686-gnu-nopt/Dockerfile
@@ -20,7 +20,7 @@ COPY scripts/sccache.sh /scripts/
 RUN sh /scripts/sccache.sh
 
 ENV RUST_CONFIGURE_ARGS --build=i686-unknown-linux-gnu --disable-optimize-tests
-ENV SCRIPT python3 ../x.py test
+ENV SCRIPT python3 ../x.py --stage 2 test
 
 # FIXME(#59637) takes too long on CI right now
 ENV NO_LLVM_ASSERTIONS=1 NO_DEBUG_ASSERTIONS=1
diff --git a/src/ci/docker/host-x86_64/i686-gnu/Dockerfile b/src/ci/docker/host-x86_64/i686-gnu/Dockerfile
index 34a76f39668..9d319017d79 100644
--- a/src/ci/docker/host-x86_64/i686-gnu/Dockerfile
+++ b/src/ci/docker/host-x86_64/i686-gnu/Dockerfile
@@ -22,7 +22,7 @@ RUN sh /scripts/sccache.sh
 ENV RUST_CONFIGURE_ARGS --build=i686-unknown-linux-gnu
 # Exclude some tests that are unlikely to be platform specific, to speed up
 # this slow job.
-ENV SCRIPT python3 ../x.py test \
+ENV SCRIPT python3 ../x.py --stage 2 test \
   --exclude src/bootstrap \
   --exclude src/test/rustdoc-js \
   --exclude src/tools/error_index_generator \
diff --git a/src/ci/docker/host-x86_64/mingw-check/Dockerfile b/src/ci/docker/host-x86_64/mingw-check/Dockerfile
index 0c59b95ea21..b902eda87bc 100644
--- a/src/ci/docker/host-x86_64/mingw-check/Dockerfile
+++ b/src/ci/docker/host-x86_64/mingw-check/Dockerfile
@@ -22,10 +22,10 @@ RUN sh /scripts/sccache.sh
 COPY host-x86_64/mingw-check/validate-toolstate.sh /scripts/
 
 ENV RUN_CHECK_WITH_PARALLEL_QUERIES 1
-ENV SCRIPT python3 ../x.py test src/tools/expand-yaml-anchors && \
+ENV SCRIPT python3 ../x.py --stage 2 test src/tools/expand-yaml-anchors && \
            python3 ../x.py check --target=i686-pc-windows-gnu --host=i686-pc-windows-gnu && \
            python3 ../x.py build --stage 0 src/tools/build-manifest && \
            python3 ../x.py test --stage 0 src/tools/compiletest && \
-           python3 ../x.py test src/tools/tidy && \
+           python3 ../x.py test --stage 2 src/tools/tidy && \
            python3 ../x.py doc --stage 0 library/std && \
            /scripts/validate-toolstate.sh
diff --git a/src/ci/docker/host-x86_64/test-various/Dockerfile b/src/ci/docker/host-x86_64/test-various/Dockerfile
index b7276f60867..c55a284e137 100644
--- a/src/ci/docker/host-x86_64/test-various/Dockerfile
+++ b/src/ci/docker/host-x86_64/test-various/Dockerfile
@@ -40,7 +40,7 @@ ENV RUST_CONFIGURE_ARGS \
 ENV NO_DEBUG_ASSERTIONS=1
 
 ENV WASM_TARGETS=wasm32-unknown-unknown
-ENV WASM_SCRIPT python3 /checkout/x.py test --target $WASM_TARGETS \
+ENV WASM_SCRIPT python3 /checkout/x.py --stage 2 test --target $WASM_TARGETS \
   src/test/run-make \
   src/test/ui \
   src/test/compile-fail \
@@ -49,13 +49,13 @@ ENV WASM_SCRIPT python3 /checkout/x.py test --target $WASM_TARGETS \
   library/core
 
 ENV NVPTX_TARGETS=nvptx64-nvidia-cuda
-ENV NVPTX_SCRIPT python3 /checkout/x.py test --target $NVPTX_TARGETS \
+ENV NVPTX_SCRIPT python3 /checkout/x.py --stage 2 test --target $NVPTX_TARGETS \
   src/test/run-make \
   src/test/assembly
 
 ENV MUSL_TARGETS=x86_64-unknown-linux-musl \
     CC_x86_64_unknown_linux_musl=x86_64-linux-musl-gcc \
     CXX_x86_64_unknown_linux_musl=x86_64-linux-musl-g++
-ENV MUSL_SCRIPT python3 /checkout/x.py test --target $MUSL_TARGETS
+ENV MUSL_SCRIPT python3 /checkout/x.py --stage 2 test --target $MUSL_TARGETS
 
 ENV SCRIPT $WASM_SCRIPT && $NVPTX_SCRIPT && $MUSL_SCRIPT
diff --git a/src/ci/docker/host-x86_64/wasm32/Dockerfile b/src/ci/docker/host-x86_64/wasm32/Dockerfile
index a40ccb6bfd5..e00177b4a67 100644
--- a/src/ci/docker/host-x86_64/wasm32/Dockerfile
+++ b/src/ci/docker/host-x86_64/wasm32/Dockerfile
@@ -52,7 +52,7 @@ ENV NO_CHANGE_USER=1
 
 # FIXME: Re-enable these tests once https://github.com/rust-lang/cargo/pull/7476
 # is picked up by CI
-ENV SCRIPT python3 ../x.py test --target $TARGETS \
+ENV SCRIPT python3 ../x.py test --stage 2 --target $TARGETS \
     --exclude library/core \
     --exclude library/alloc \
     --exclude library/proc_macro \
diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-debug/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-debug/Dockerfile
index c5e41b8a75a..c1cb20b631d 100644
--- a/src/ci/docker/host-x86_64/x86_64-gnu-debug/Dockerfile
+++ b/src/ci/docker/host-x86_64/x86_64-gnu-debug/Dockerfile
@@ -40,5 +40,5 @@ ENV RUST_CONFIGURE_ARGS \
       --set target.x86_64-unknown-linux-gnu.cxx=clang++
 
 ENV SCRIPT \
-  python3 ../x.py build && \
-  python3 ../x.py test src/test/run-make-fulldeps --test-args clang
+  python3 ../x.py --stage 2 build && \
+  python3 ../x.py --stage 2 test src/test/run-make-fulldeps --test-args clang
diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-distcheck/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-distcheck/Dockerfile
index cc07a591cc1..68e89a7bade 100644
--- a/src/ci/docker/host-x86_64/x86_64-gnu-distcheck/Dockerfile
+++ b/src/ci/docker/host-x86_64/x86_64-gnu-distcheck/Dockerfile
@@ -19,10 +19,10 @@ COPY scripts/sccache.sh /scripts/
 RUN sh /scripts/sccache.sh
 
 ENV RUST_CONFIGURE_ARGS --build=x86_64-unknown-linux-gnu --set rust.ignore-git=false
-ENV SCRIPT python3 ../x.py test distcheck
+ENV SCRIPT python3 ../x.py --stage 2 test distcheck
 ENV DIST_SRC 1
 
-# The purpose of this builder is to test that we can `./x.py test` successfully
+# The purpose of this builder is to test that we can `./x.py --stage 2 test` successfully
 # from a tarball, not to test LLVM/rustc's own set of assertions. These cause a
 # significant hit to CI compile time (over a half hour as observed in #61185),
 # so disable assertions for this builder.
diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-full-bootstrap/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-full-bootstrap/Dockerfile
index de7ee6950b5..8648e5ed7a4 100644
--- a/src/ci/docker/host-x86_64/x86_64-gnu-full-bootstrap/Dockerfile
+++ b/src/ci/docker/host-x86_64/x86_64-gnu-full-bootstrap/Dockerfile
@@ -21,7 +21,7 @@ RUN sh /scripts/sccache.sh
 ENV RUST_CONFIGURE_ARGS \
       --build=x86_64-unknown-linux-gnu \
       --enable-full-bootstrap
-ENV SCRIPT python3 ../x.py build
+ENV SCRIPT python3 ../x.py --stage 2 build
 
 # In general this just slows down the build and we're just a smoke test that
 # a full bootstrap works in general, so there's not much need to take this
diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-8/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-8/Dockerfile
index 1d9cad149d9..5c971c73c97 100644
--- a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-8/Dockerfile
+++ b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-8/Dockerfile
@@ -30,7 +30,7 @@ ENV RUST_CONFIGURE_ARGS \
       --enable-llvm-link-shared \
       --set rust.thin-lto-import-instr-limit=10
 
-ENV SCRIPT python2.7 ../x.py test --exclude src/tools/tidy && \
+ENV SCRIPT python2.7 ../x.py --stage 2 test --exclude src/tools/tidy && \
            # Run the `mir-opt` tests again but this time for a 32-bit target.
            # This enforces that tests using `// EMIT_MIR_FOR_EACH_BIT_WIDTH` have
            # both 32-bit and 64-bit outputs updated by the PR author, before
@@ -43,7 +43,7 @@ ENV SCRIPT python2.7 ../x.py test --exclude src/tools/tidy && \
            # This also requires `--pass=build` because we can't execute the tests
            # on the `x86_64` host when they're built as `armv5te` binaries.
            # (we're only interested in the MIR output, so this doesn't matter)
-           python2.7 ../x.py test src/test/mir-opt --pass=build \
+           python2.7 ../x.py --stage 2 test src/test/mir-opt --pass=build \
                                   --target=armv5te-unknown-linux-gnueabi && \
            # Run the UI test suite again, but in `--pass=check` mode
            #
@@ -53,9 +53,9 @@ ENV SCRIPT python2.7 ../x.py test --exclude src/tools/tidy && \
            # FIXME: We ideally want to test this in 32-bit mode, but currently
            # (due to the LLVM problems mentioned above) that isn't readily
            # possible.
-           python2.7 ../x.py test src/test/ui --pass=check && \
+           python2.7 ../x.py --stage 2 test src/test/ui --pass=check && \
            # Run tidy at the very end, after all the other tests.
-           python2.7 ../x.py test src/tools/tidy
+           python2.7 ../x.py --stage 2 test src/tools/tidy
 
 # The purpose of this container isn't to test with debug assertions and
 # this is run on all PRs, so let's get speedier builds by disabling these extra
diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-nopt/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-nopt/Dockerfile
index 096f67e13d1..fa769cac9c1 100644
--- a/src/ci/docker/host-x86_64/x86_64-gnu-nopt/Dockerfile
+++ b/src/ci/docker/host-x86_64/x86_64-gnu-nopt/Dockerfile
@@ -21,4 +21,4 @@ RUN sh /scripts/sccache.sh
 ENV RUST_CONFIGURE_ARGS --build=x86_64-unknown-linux-gnu \
   --disable-optimize-tests \
   --set rust.test-compare-mode
-ENV SCRIPT python3 ../x.py test
+ENV SCRIPT python3 ../x.py --stage 2 test
diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh b/src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh
index b4b23a245e0..49a8e5e88a0 100755
--- a/src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh
+++ b/src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh
@@ -7,7 +7,7 @@ X_PY="$1"
 # Try to test all the tools and store the build/test success in the TOOLSTATE_FILE
 
 set +e
-python3 "$X_PY" test --no-fail-fast \
+python3 "$X_PY" test --stage 2 --no-fail-fast \
     src/doc/book \
     src/doc/nomicon \
     src/doc/reference \
@@ -22,5 +22,5 @@ set -e
 
 # debugging: print out the saved toolstates
 cat /tmp/toolstate/toolstates.json
-python3 "$X_PY" test check-tools
-python3 "$X_PY" test src/tools/clippy
+python3 "$X_PY" test --stage 2 check-tools
+python3 "$X_PY" test --stage 2 src/tools/clippy
diff --git a/src/ci/docker/host-x86_64/x86_64-gnu/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu/Dockerfile
index af6e1318062..f8bacf79ac0 100644
--- a/src/ci/docker/host-x86_64/x86_64-gnu/Dockerfile
+++ b/src/ci/docker/host-x86_64/x86_64-gnu/Dockerfile
@@ -23,4 +23,4 @@ ENV RUST_CONFIGURE_ARGS \
  --enable-sanitizers \
  --enable-profiler \
  --enable-compiler-docs
-ENV SCRIPT python3 ../x.py test
+ENV SCRIPT python3 ../x.py --stage 2 test
diff --git a/src/ci/github-actions/ci.yml b/src/ci/github-actions/ci.yml
index 0aeb6a04e5f..a7c1987e8b1 100644
--- a/src/ci/github-actions/ci.yml
+++ b/src/ci/github-actions/ci.yml
@@ -471,7 +471,7 @@ jobs:
 
           - name: x86_64-msvc-cargo
             env:
-              SCRIPT: python x.py test src/tools/cargotest src/tools/cargo
+              SCRIPT: python x.py --stage 2 test src/tools/cargotest src/tools/cargo
               RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-msvc --enable-lld
               VCVARS_BAT: vcvars64.bat
               # FIXME(#59637)
@@ -613,7 +613,7 @@ jobs:
 
           - name: x86_64-apple
             env:
-              SCRIPT: ./x.py test
+              SCRIPT: ./x.py --stage 2 test
               RUST_CONFIGURE_ARGS: --build=x86_64-apple-darwin --enable-sanitizers --enable-profiler --set rust.jemalloc
               RUSTC_RETRY_LINKER_ON_SEGFAULT: 1
               MACOSX_DEPLOYMENT_TARGET: 10.8