bootstrap/core/build_steps/
test.rs

1//! Build-and-run steps for `./x.py test` test fixtures
2//!
3//! `./x.py test` (aka [`Kind::Test`]) is currently allowed to reach build steps in other modules.
4//! However, this contains ~all test parts we expect people to be able to build and run locally.
5
6// (This file should be split up, but having tidy block all changes is not helpful.)
7// ignore-tidy-filelength
8
9use std::collections::HashSet;
10use std::env::split_paths;
11use std::ffi::{OsStr, OsString};
12use std::path::{Path, PathBuf};
13use std::{env, fs, iter};
14
15use build_helper::exit;
16
17use crate::core::build_steps::compile::{ArtifactKeepMode, Std, run_cargo};
18use crate::core::build_steps::doc::{DocumentationFormat, prepare_doc_compiler};
19use crate::core::build_steps::gcc::{Gcc, GccTargetPair, add_cg_gcc_cargo_flags};
20use crate::core::build_steps::llvm::get_llvm_version;
21use crate::core::build_steps::run::{get_completion_paths, get_help_path};
22use crate::core::build_steps::synthetic_targets::MirOptPanicAbortSyntheticTarget;
23use crate::core::build_steps::test::compiletest::CompiletestMode;
24use crate::core::build_steps::tool::{
25    self, RustcPrivateCompilers, SourceType, TEST_FLOAT_PARSE_ALLOW_FEATURES, Tool,
26    ToolTargetBuildMode, get_tool_target_compiler,
27};
28use crate::core::build_steps::toolstate::ToolState;
29use crate::core::build_steps::{compile, dist, llvm};
30use crate::core::builder::{
31    self, Alias, Builder, Compiler, Kind, RunConfig, ShouldRun, Step, StepMetadata,
32    crate_description,
33};
34use crate::core::config::TargetSelection;
35use crate::core::config::flags::{Subcommand, get_completion, top_level_help};
36use crate::core::{android, debuggers};
37use crate::utils::build_stamp::{self, BuildStamp};
38use crate::utils::exec::{BootstrapCommand, command};
39use crate::utils::helpers::{
40    self, LldThreads, add_dylib_path, add_rustdoc_cargo_linker_args, dylib_path, dylib_path_var,
41    linker_args, linker_flags, t, target_supports_cranelift_backend, up_to_date,
42};
43use crate::utils::render_tests::{add_flags_and_try_run_tests, try_run_tests};
44use crate::{CLang, CodegenBackendKind, DocTests, GitRepo, Mode, PathSet, envify};
45
46mod compiletest;
47
48/// Runs `cargo test` on various internal tools used by bootstrap.
49#[derive(Debug, Clone, PartialEq, Eq, Hash)]
50pub struct CrateBootstrap {
51    path: PathBuf,
52    host: TargetSelection,
53}
54
55impl Step for CrateBootstrap {
56    type Output = ();
57    const IS_HOST: bool = true;
58
59    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
60        // This step is responsible for several different tool paths.
61        //
62        // By default, it will test all of them, but requesting specific tools on the command-line
63        // (e.g. `./x test src/tools/coverage-dump`) will test only the specified tools.
64        run.path("src/tools/jsondoclint")
65            .path("src/tools/replace-version-placeholder")
66            .path("src/tools/coverage-dump")
67            // We want `./x test tidy` to _run_ the tidy tool, not its tests.
68            // So we need a separate alias to test the tidy tool itself.
69            .alias("tidyselftest")
70    }
71
72    fn is_default_step(_builder: &Builder<'_>) -> bool {
73        true
74    }
75
76    fn make_run(run: RunConfig<'_>) {
77        // Create and ensure a separate instance of this step for each path
78        // that was selected on the command-line (or selected by default).
79        for path in run.paths {
80            let path = path.assert_single_path().path.clone();
81            run.builder.ensure(CrateBootstrap { host: run.target, path });
82        }
83    }
84
85    fn run(self, builder: &Builder<'_>) {
86        let bootstrap_host = builder.config.host_target;
87        let compiler = builder.compiler(0, bootstrap_host);
88        let mut path = self.path.to_str().unwrap();
89
90        // Map alias `tidyselftest` back to the actual crate path of tidy.
91        if path == "tidyselftest" {
92            path = "src/tools/tidy";
93        }
94
95        let cargo = tool::prepare_tool_cargo(
96            builder,
97            compiler,
98            Mode::ToolBootstrap,
99            bootstrap_host,
100            Kind::Test,
101            path,
102            SourceType::InTree,
103            &[],
104        );
105
106        let crate_name = path.rsplit_once('/').unwrap().1;
107        run_cargo_test(cargo, &[], &[], crate_name, bootstrap_host, builder);
108    }
109
110    fn metadata(&self) -> Option<StepMetadata> {
111        Some(
112            StepMetadata::test("crate-bootstrap", self.host)
113                .with_metadata(self.path.as_path().to_string_lossy().to_string()),
114        )
115    }
116}
117
118#[derive(Debug, Clone, PartialEq, Eq, Hash)]
119pub struct Linkcheck {
120    host: TargetSelection,
121}
122
123impl Step for Linkcheck {
124    type Output = ();
125    const IS_HOST: bool = true;
126
127    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
128        run.path("src/tools/linkchecker")
129    }
130
131    fn is_default_step(builder: &Builder<'_>) -> bool {
132        builder.config.docs
133    }
134
135    fn make_run(run: RunConfig<'_>) {
136        run.builder.ensure(Linkcheck { host: run.target });
137    }
138
139    /// Runs the `linkchecker` tool as compiled in `stage` by the `host` compiler.
140    ///
141    /// This tool in `src/tools` will verify the validity of all our links in the
142    /// documentation to ensure we don't have a bunch of dead ones.
143    fn run(self, builder: &Builder<'_>) {
144        let host = self.host;
145        let hosts = &builder.hosts;
146        let targets = &builder.targets;
147
148        // if we have different hosts and targets, some things may be built for
149        // the host (e.g. rustc) and others for the target (e.g. std). The
150        // documentation built for each will contain broken links to
151        // docs built for the other platform (e.g. rustc linking to cargo)
152        if (hosts != targets) && !hosts.is_empty() && !targets.is_empty() {
153            panic!(
154                "Linkcheck currently does not support builds with different hosts and targets.
155You can skip linkcheck with --skip src/tools/linkchecker"
156            );
157        }
158
159        builder.info(&format!("Linkcheck ({host})"));
160
161        // Test the linkchecker itself.
162        let bootstrap_host = builder.config.host_target;
163        let compiler = builder.compiler(0, bootstrap_host);
164
165        let cargo = tool::prepare_tool_cargo(
166            builder,
167            compiler,
168            Mode::ToolBootstrap,
169            bootstrap_host,
170            Kind::Test,
171            "src/tools/linkchecker",
172            SourceType::InTree,
173            &[],
174        );
175        run_cargo_test(cargo, &[], &[], "linkchecker self tests", bootstrap_host, builder);
176
177        if builder.doc_tests == DocTests::No {
178            return;
179        }
180
181        // Build all the default documentation.
182        builder.run_default_doc_steps();
183
184        // Build the linkchecker before calling `msg`, since GHA doesn't support nested groups.
185        let linkchecker = builder.tool_cmd(Tool::Linkchecker);
186
187        // Run the linkchecker.
188        let _guard = builder.msg_test("Linkcheck", bootstrap_host, 1);
189        let _time = helpers::timeit(builder);
190        linkchecker.delay_failure().arg(builder.out.join(host).join("doc")).run(builder);
191    }
192
193    fn metadata(&self) -> Option<StepMetadata> {
194        Some(StepMetadata::test("link-check", self.host))
195    }
196}
197
198fn check_if_tidy_is_installed(builder: &Builder<'_>) -> bool {
199    command("tidy")
200        .allow_failure()
201        .arg("--version")
202        // Cache the output to avoid running this command more than once (per builder).
203        .cached()
204        .run_capture_stdout(builder)
205        .is_success()
206}
207
208#[derive(Debug, Clone, PartialEq, Eq, Hash)]
209pub struct HtmlCheck {
210    target: TargetSelection,
211}
212
213impl Step for HtmlCheck {
214    type Output = ();
215    const IS_HOST: bool = true;
216
217    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
218        run.path("src/tools/html-checker")
219    }
220
221    fn is_default_step(builder: &Builder<'_>) -> bool {
222        check_if_tidy_is_installed(builder)
223    }
224
225    fn make_run(run: RunConfig<'_>) {
226        run.builder.ensure(HtmlCheck { target: run.target });
227    }
228
229    fn run(self, builder: &Builder<'_>) {
230        if !check_if_tidy_is_installed(builder) {
231            eprintln!("not running HTML-check tool because `tidy` is missing");
232            eprintln!(
233                "You need the HTML tidy tool https://www.html-tidy.org/, this tool is *not* part of the rust project and needs to be installed separately, for example via your package manager."
234            );
235            panic!("Cannot run html-check tests");
236        }
237        // Ensure that a few different kinds of documentation are available.
238        builder.run_default_doc_steps();
239        builder.ensure(crate::core::build_steps::doc::Rustc::for_stage(
240            builder,
241            builder.top_stage,
242            self.target,
243        ));
244
245        builder
246            .tool_cmd(Tool::HtmlChecker)
247            .delay_failure()
248            .arg(builder.doc_out(self.target))
249            .run(builder);
250    }
251
252    fn metadata(&self) -> Option<StepMetadata> {
253        Some(StepMetadata::test("html-check", self.target))
254    }
255}
256
257/// Builds cargo and then runs the `src/tools/cargotest` tool, which checks out
258/// some representative crate repositories and runs `cargo test` on them, in
259/// order to test cargo.
260#[derive(Debug, Clone, PartialEq, Eq, Hash)]
261pub struct Cargotest {
262    build_compiler: Compiler,
263    host: TargetSelection,
264}
265
266impl Step for Cargotest {
267    type Output = ();
268    const IS_HOST: bool = true;
269
270    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
271        run.path("src/tools/cargotest")
272    }
273
274    fn make_run(run: RunConfig<'_>) {
275        if run.builder.top_stage == 0 {
276            eprintln!(
277                "ERROR: running cargotest with stage 0 is currently unsupported. Use at least stage 1."
278            );
279            exit!(1);
280        }
281        // We want to build cargo stage N (where N == top_stage), and rustc stage N,
282        // and test both of these together.
283        // So we need to get a build compiler stage N-1 to build the stage N components.
284        run.builder.ensure(Cargotest {
285            build_compiler: run.builder.compiler(run.builder.top_stage - 1, run.target),
286            host: run.target,
287        });
288    }
289
290    /// Runs the `cargotest` tool as compiled in `stage` by the `host` compiler.
291    ///
292    /// This tool in `src/tools` will check out a few Rust projects and run `cargo
293    /// test` to ensure that we don't regress the test suites there.
294    fn run(self, builder: &Builder<'_>) {
295        // cargotest's staging has several pieces:
296        // consider ./x test cargotest --stage=2.
297        //
298        // The test goal is to exercise a (stage 2 cargo, stage 2 rustc) pair through a stage 2
299        // cargotest tool.
300        // To produce the stage 2 cargo and cargotest, we need to do so with the stage 1 rustc and std.
301        // Importantly, the stage 2 rustc being tested (`tested_compiler`) via stage 2 cargotest is
302        // the rustc built by an earlier stage 1 rustc (the build_compiler). These are two different
303        // compilers!
304        let cargo =
305            builder.ensure(tool::Cargo::from_build_compiler(self.build_compiler, self.host));
306        let tested_compiler = builder.compiler(self.build_compiler.stage + 1, self.host);
307        builder.std(tested_compiler, self.host);
308
309        // Note that this is a short, cryptic, and not scoped directory name. This
310        // is currently to minimize the length of path on Windows where we otherwise
311        // quickly run into path name limit constraints.
312        let out_dir = builder.out.join("ct");
313        t!(fs::create_dir_all(&out_dir));
314
315        let _time = helpers::timeit(builder);
316        let mut cmd = builder.tool_cmd(Tool::CargoTest);
317        cmd.arg(&cargo.tool_path)
318            .arg(&out_dir)
319            .args(builder.config.test_args())
320            .env("RUSTC", builder.rustc(tested_compiler))
321            .env("RUSTDOC", builder.rustdoc_for_compiler(tested_compiler));
322        add_rustdoc_cargo_linker_args(&mut cmd, builder, tested_compiler.host, LldThreads::No);
323        cmd.delay_failure().run(builder);
324    }
325
326    fn metadata(&self) -> Option<StepMetadata> {
327        Some(StepMetadata::test("cargotest", self.host).stage(self.build_compiler.stage + 1))
328    }
329}
330
331/// Runs `cargo test` for cargo itself.
332/// We label these tests as "cargo self-tests".
333#[derive(Debug, Clone, PartialEq, Eq, Hash)]
334pub struct Cargo {
335    build_compiler: Compiler,
336    host: TargetSelection,
337}
338
339impl Cargo {
340    const CRATE_PATH: &str = "src/tools/cargo";
341}
342
343impl Step for Cargo {
344    type Output = ();
345    const IS_HOST: bool = true;
346
347    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
348        run.path(Self::CRATE_PATH)
349    }
350
351    fn make_run(run: RunConfig<'_>) {
352        run.builder.ensure(Cargo {
353            build_compiler: get_tool_target_compiler(
354                run.builder,
355                ToolTargetBuildMode::Build(run.target),
356            ),
357            host: run.target,
358        });
359    }
360
361    /// Runs `cargo test` for `cargo` packaged with Rust.
362    fn run(self, builder: &Builder<'_>) {
363        // When we do a "stage 1 cargo self-test", it means that we test the stage 1 rustc
364        // using stage 1 cargo. So we actually build cargo using the stage 0 compiler, and then
365        // run its tests against the stage 1 compiler (called `tested_compiler` below).
366        builder.ensure(tool::Cargo::from_build_compiler(self.build_compiler, self.host));
367
368        let tested_compiler = builder.compiler(self.build_compiler.stage + 1, self.host);
369        builder.std(tested_compiler, self.host);
370        // We also need to build rustdoc for cargo tests
371        // It will be located in the bindir of `tested_compiler`, so we don't need to explicitly
372        // pass its path to Cargo.
373        builder.rustdoc_for_compiler(tested_compiler);
374
375        let cargo = tool::prepare_tool_cargo(
376            builder,
377            self.build_compiler,
378            Mode::ToolTarget,
379            self.host,
380            Kind::Test,
381            Self::CRATE_PATH,
382            SourceType::Submodule,
383            &[],
384        );
385
386        // NOTE: can't use `run_cargo_test` because we need to overwrite `PATH`
387        let mut cargo = prepare_cargo_test(cargo, &[], &[], self.host, builder);
388
389        // Don't run cross-compile tests, we may not have cross-compiled libstd libs
390        // available.
391        cargo.env("CFG_DISABLE_CROSS_TESTS", "1");
392        // Forcibly disable tests using nightly features since any changes to
393        // those features won't be able to land.
394        cargo.env("CARGO_TEST_DISABLE_NIGHTLY", "1");
395
396        // Configure PATH to find the right rustc. NB. we have to use PATH
397        // and not RUSTC because the Cargo test suite has tests that will
398        // fail if rustc is not spelled `rustc`.
399        cargo.env("PATH", bin_path_for_cargo(builder, tested_compiler));
400
401        // The `cargo` command configured above has dylib dir path set to the `build_compiler`'s
402        // libdir. That causes issues in cargo test, because the programs that cargo compiles are
403        // incorrectly picking that libdir, even though they should be picking the
404        // `tested_compiler`'s libdir. We thus have to override the precedence here.
405        let mut existing_dylib_paths = cargo
406            .get_envs()
407            .find(|(k, _)| *k == OsStr::new(dylib_path_var()))
408            .and_then(|(_, v)| v)
409            .map(|value| split_paths(value).collect::<Vec<PathBuf>>())
410            .unwrap_or_default();
411        existing_dylib_paths.insert(0, builder.rustc_libdir(tested_compiler));
412        add_dylib_path(existing_dylib_paths, &mut cargo);
413
414        // Cargo's test suite uses `CARGO_RUSTC_CURRENT_DIR` to determine the path that `file!` is
415        // relative to. Cargo no longer sets this env var, so we have to do that. This has to be the
416        // same value as `-Zroot-dir`.
417        cargo.env("CARGO_RUSTC_CURRENT_DIR", builder.src.display().to_string());
418
419        #[cfg(feature = "build-metrics")]
420        builder.metrics.begin_test_suite(
421            build_helper::metrics::TestSuiteMetadata::CargoPackage {
422                crates: vec!["cargo".into()],
423                target: self.host.triple.to_string(),
424                host: self.host.triple.to_string(),
425                stage: self.build_compiler.stage + 1,
426            },
427            builder,
428        );
429
430        let _time = helpers::timeit(builder);
431        add_flags_and_try_run_tests(builder, &mut cargo);
432    }
433
434    fn metadata(&self) -> Option<StepMetadata> {
435        Some(StepMetadata::test("cargo", self.host).built_by(self.build_compiler))
436    }
437}
438
439#[derive(Debug, Clone, PartialEq, Eq, Hash)]
440pub struct RustAnalyzer {
441    compilers: RustcPrivateCompilers,
442}
443
444impl Step for RustAnalyzer {
445    type Output = ();
446    const IS_HOST: bool = true;
447
448    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
449        run.path("src/tools/rust-analyzer")
450    }
451
452    fn is_default_step(_builder: &Builder<'_>) -> bool {
453        true
454    }
455
456    fn make_run(run: RunConfig<'_>) {
457        run.builder.ensure(Self {
458            compilers: RustcPrivateCompilers::new(
459                run.builder,
460                run.builder.top_stage,
461                run.builder.host_target,
462            ),
463        });
464    }
465
466    /// Runs `cargo test` for rust-analyzer
467    fn run(self, builder: &Builder<'_>) {
468        let build_compiler = self.compilers.build_compiler();
469        let target = self.compilers.target();
470
471        // NOTE: rust-analyzer repo currently (as of 2025-12-11) does not run tests against 32-bit
472        // targets, so we also don't run them in rust-lang/rust CI (because that will just mean that
473        // subtree syncs will keep getting 32-bit-specific failures that are not observed in
474        // rust-analyzer repo CI).
475        //
476        // Some 32-bit specific failures include e.g. target pointer width specific hashes.
477
478        // FIXME: eventually, we should probably reduce the amount of target tuple substring
479        // matching in bootstrap.
480        if target.starts_with("i686") {
481            return;
482        }
483
484        let mut cargo = tool::prepare_tool_cargo(
485            builder,
486            build_compiler,
487            Mode::ToolRustcPrivate,
488            target,
489            Kind::Test,
490            "src/tools/rust-analyzer",
491            SourceType::InTree,
492            &["in-rust-tree".to_owned()],
493        );
494        cargo.allow_features(tool::RustAnalyzer::ALLOW_FEATURES);
495
496        // N.B. it turns out _setting_ `CARGO_WORKSPACE_DIR` actually somehow breaks `expect-test`,
497        // even though previously we actually needed to set that hack to allow `expect-test` to
498        // correctly discover the r-a workspace instead of the outer r-l/r workspace.
499
500        // FIXME: RA's test suite tries to write to the source directory, that can't work in Rust CI
501        // without properly wiring up the writable test dir.
502        cargo.env("SKIP_SLOW_TESTS", "1");
503
504        // NOTE: we need to skip `src/tools/rust-analyzer/xtask` as they seem to exercise rustup /
505        // stable rustfmt.
506        //
507        // NOTE: you can only skip a specific workspace package via `--exclude=...` if you *also*
508        // specify `--workspace`.
509        cargo.arg("--workspace");
510        cargo.arg("--exclude=xtask");
511
512        let mut skip_tests = vec![];
513
514        // NOTE: the following test skips is a bit cheeky in that it assumes there are no
515        // identically named tests across different r-a packages, where we want to run the
516        // identically named test in one package but not another. If we want to support that use
517        // case, we'd have to run the r-a tests in two batches (with one excluding the package that
518        // we *don't* want to run the test for, and the other batch including).
519
520        // Across all platforms.
521        skip_tests.extend_from_slice(&[
522            // FIXME: this test wants to find a `rustc`. We need to provide it with a path to staged
523            // in-tree `rustc`, but setting `RUSTC` env var requires some reworking of bootstrap.
524            "tests::smoke_test_real_sysroot_cargo",
525            // NOTE: part of `smol-str` test suite; this tries to access a stable rustfmt from the
526            // environment, which is not something we want to do.
527            "check_code_formatting",
528        ]);
529
530        let skip_tests = skip_tests.iter().map(|name| format!("--skip={name}")).collect::<Vec<_>>();
531        let skip_tests = skip_tests.iter().map(|s| s.as_str()).collect::<Vec<_>>();
532
533        cargo.add_rustc_lib_path(builder);
534        run_cargo_test(cargo, skip_tests.as_slice(), &[], "rust-analyzer", target, builder);
535    }
536
537    fn metadata(&self) -> Option<StepMetadata> {
538        Some(
539            StepMetadata::test("rust-analyzer", self.compilers.target())
540                .built_by(self.compilers.build_compiler()),
541        )
542    }
543}
544
545/// Runs `cargo test` for rustfmt.
546#[derive(Debug, Clone, PartialEq, Eq, Hash)]
547pub struct Rustfmt {
548    compilers: RustcPrivateCompilers,
549}
550
551impl Step for Rustfmt {
552    type Output = ();
553    const IS_HOST: bool = true;
554
555    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
556        run.path("src/tools/rustfmt")
557    }
558
559    fn make_run(run: RunConfig<'_>) {
560        run.builder.ensure(Rustfmt {
561            compilers: RustcPrivateCompilers::new(
562                run.builder,
563                run.builder.top_stage,
564                run.builder.host_target,
565            ),
566        });
567    }
568
569    /// Runs `cargo test` for rustfmt.
570    fn run(self, builder: &Builder<'_>) {
571        let build_compiler = self.compilers.build_compiler();
572        let target = self.compilers.target();
573
574        let mut cargo = tool::prepare_tool_cargo(
575            builder,
576            build_compiler,
577            Mode::ToolRustcPrivate,
578            target,
579            Kind::Test,
580            "src/tools/rustfmt",
581            SourceType::InTree,
582            &[],
583        );
584
585        let dir = testdir(builder, target);
586        t!(fs::create_dir_all(&dir));
587        cargo.env("RUSTFMT_TEST_DIR", dir);
588
589        cargo.add_rustc_lib_path(builder);
590
591        run_cargo_test(cargo, &[], &[], "rustfmt", target, builder);
592    }
593
594    fn metadata(&self) -> Option<StepMetadata> {
595        Some(
596            StepMetadata::test("rustfmt", self.compilers.target())
597                .built_by(self.compilers.build_compiler()),
598        )
599    }
600}
601
602#[derive(Debug, Clone, PartialEq, Eq, Hash)]
603pub struct Miri {
604    target: TargetSelection,
605}
606
607impl Miri {
608    /// Run `cargo miri setup` for the given target, return where the Miri sysroot was put.
609    pub fn build_miri_sysroot(
610        builder: &Builder<'_>,
611        compiler: Compiler,
612        target: TargetSelection,
613    ) -> PathBuf {
614        let miri_sysroot = builder.out.join(compiler.host).join("miri-sysroot");
615        let mut cargo = builder::Cargo::new(
616            builder,
617            compiler,
618            Mode::Std,
619            SourceType::Submodule,
620            target,
621            Kind::MiriSetup,
622        );
623
624        // Tell `cargo miri setup` where to find the sources.
625        cargo.env("MIRI_LIB_SRC", builder.src.join("library"));
626        // Tell it where to put the sysroot.
627        cargo.env("MIRI_SYSROOT", &miri_sysroot);
628
629        let mut cargo = BootstrapCommand::from(cargo);
630        let _guard =
631            builder.msg(Kind::Build, "miri sysroot", Mode::ToolRustcPrivate, compiler, target);
632        cargo.run(builder);
633
634        // # Determine where Miri put its sysroot.
635        // To this end, we run `cargo miri setup --print-sysroot` and capture the output.
636        // (We do this separately from the above so that when the setup actually
637        // happens we get some output.)
638        // We re-use the `cargo` from above.
639        cargo.arg("--print-sysroot");
640
641        builder.do_if_verbose(|| println!("running: {cargo:?}"));
642        let stdout = cargo.run_capture_stdout(builder).stdout();
643        // Output is "<sysroot>\n".
644        let sysroot = stdout.trim_end();
645        builder.do_if_verbose(|| println!("`cargo miri setup --print-sysroot` said: {sysroot:?}"));
646        PathBuf::from(sysroot)
647    }
648}
649
650impl Step for Miri {
651    type Output = ();
652
653    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
654        run.path("src/tools/miri")
655    }
656
657    fn make_run(run: RunConfig<'_>) {
658        run.builder.ensure(Miri { target: run.target });
659    }
660
661    /// Runs `cargo test` for miri.
662    fn run(self, builder: &Builder<'_>) {
663        let host = builder.build.host_target;
664        let target = self.target;
665        let stage = builder.top_stage;
666        if stage == 0 {
667            eprintln!("miri cannot be tested at stage 0");
668            std::process::exit(1);
669        }
670
671        // This compiler runs on the host, we'll just use it for the target.
672        let compilers = RustcPrivateCompilers::new(builder, stage, host);
673
674        // Build our tools.
675        let miri = builder.ensure(tool::Miri::from_compilers(compilers));
676        // the ui tests also assume cargo-miri has been built
677        builder.ensure(tool::CargoMiri::from_compilers(compilers));
678
679        let target_compiler = compilers.target_compiler();
680
681        // We also need sysroots, for Miri and for the host (the latter for build scripts).
682        // This is for the tests so everything is done with the target compiler.
683        let miri_sysroot = Miri::build_miri_sysroot(builder, target_compiler, target);
684        builder.std(target_compiler, host);
685        let host_sysroot = builder.sysroot(target_compiler);
686
687        // Miri has its own "target dir" for ui test dependencies. Make sure it gets cleared when
688        // the sysroot gets rebuilt, to avoid "found possibly newer version of crate `std`" errors.
689        if !builder.config.dry_run() {
690            // This has to match `CARGO_TARGET_TMPDIR` in Miri's `ui.rs`.
691            // This means we need `host` here as that's the target `ui.rs` is built for.
692            let ui_test_dep_dir = builder
693                .stage_out(miri.build_compiler, Mode::ToolStd)
694                .join(host)
695                .join("tmp")
696                .join("miri_ui");
697            // The mtime of `miri_sysroot` changes when the sysroot gets rebuilt (also see
698            // <https://github.com/RalfJung/rustc-build-sysroot/commit/10ebcf60b80fe2c3dc765af0ff19fdc0da4b7466>).
699            // We can hence use that directly as a signal to clear the ui test dir.
700            build_stamp::clear_if_dirty(builder, &ui_test_dep_dir, &miri_sysroot);
701        }
702
703        // Run `cargo test`.
704        // This is with the Miri crate, so it uses the host compiler.
705        let mut cargo = tool::prepare_tool_cargo(
706            builder,
707            miri.build_compiler,
708            Mode::ToolRustcPrivate,
709            host,
710            Kind::Test,
711            "src/tools/miri",
712            SourceType::InTree,
713            &[],
714        );
715
716        cargo.add_rustc_lib_path(builder);
717
718        // We can NOT use `run_cargo_test` since Miri's integration tests do not use the usual test
719        // harness and therefore do not understand the flags added by `add_flags_and_try_run_test`.
720        let mut cargo = prepare_cargo_test(cargo, &[], &[], host, builder);
721
722        // miri tests need to know about the stage sysroot
723        cargo.env("MIRI_SYSROOT", &miri_sysroot);
724        cargo.env("MIRI_HOST_SYSROOT", &host_sysroot);
725
726        // Set the target.
727        cargo.env("MIRI_TEST_TARGET", target.rustc_target_arg());
728
729        {
730            let _guard = builder.msg_test("miri", target, target_compiler.stage);
731            let _time = helpers::timeit(builder);
732            cargo.run(builder);
733        }
734
735        // Run it again for mir-opt-level 4 to catch some miscompilations.
736        if builder.config.test_args().is_empty() {
737            cargo.env("MIRIFLAGS", "-O -Zmir-opt-level=4 -Cdebug-assertions=yes");
738            // Optimizations can change backtraces
739            cargo.env("MIRI_SKIP_UI_CHECKS", "1");
740            // `MIRI_SKIP_UI_CHECKS` and `RUSTC_BLESS` are incompatible
741            cargo.env_remove("RUSTC_BLESS");
742            // Optimizations can change error locations and remove UB so don't run `fail` tests.
743            cargo.args(["tests/pass", "tests/panic"]);
744
745            {
746                let _guard =
747                    builder.msg_test("miri (mir-opt-level 4)", target, target_compiler.stage);
748                let _time = helpers::timeit(builder);
749                cargo.run(builder);
750            }
751        }
752    }
753}
754
755/// Runs `cargo miri test` to demonstrate that `src/tools/miri/cargo-miri`
756/// works and that libtest works under miri.
757#[derive(Debug, Clone, PartialEq, Eq, Hash)]
758pub struct CargoMiri {
759    target: TargetSelection,
760}
761
762impl Step for CargoMiri {
763    type Output = ();
764
765    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
766        run.path("src/tools/miri/cargo-miri")
767    }
768
769    fn make_run(run: RunConfig<'_>) {
770        run.builder.ensure(CargoMiri { target: run.target });
771    }
772
773    /// Tests `cargo miri test`.
774    fn run(self, builder: &Builder<'_>) {
775        let host = builder.build.host_target;
776        let target = self.target;
777        let stage = builder.top_stage;
778        if stage == 0 {
779            eprintln!("cargo-miri cannot be tested at stage 0");
780            std::process::exit(1);
781        }
782
783        // This compiler runs on the host, we'll just use it for the target.
784        let build_compiler = builder.compiler(stage, host);
785
786        // Run `cargo miri test`.
787        // This is just a smoke test (Miri's own CI invokes this in a bunch of different ways and ensures
788        // that we get the desired output), but that is sufficient to make sure that the libtest harness
789        // itself executes properly under Miri, and that all the logic in `cargo-miri` does not explode.
790        let mut cargo = tool::prepare_tool_cargo(
791            builder,
792            build_compiler,
793            Mode::ToolStd, // it's unclear what to use here, we're not building anything just doing a smoke test!
794            target,
795            Kind::MiriTest,
796            "src/tools/miri/test-cargo-miri",
797            SourceType::Submodule,
798            &[],
799        );
800
801        // We're not using `prepare_cargo_test` so we have to do this ourselves.
802        // (We're not using that as the test-cargo-miri crate is not known to bootstrap.)
803        match builder.doc_tests {
804            DocTests::Yes => {}
805            DocTests::No => {
806                cargo.args(["--lib", "--bins", "--examples", "--tests", "--benches"]);
807            }
808            DocTests::Only => {
809                cargo.arg("--doc");
810            }
811        }
812        cargo.arg("--").args(builder.config.test_args());
813
814        // Finally, run everything.
815        let mut cargo = BootstrapCommand::from(cargo);
816        {
817            let _guard = builder.msg_test("cargo-miri", target, stage);
818            let _time = helpers::timeit(builder);
819            cargo.run(builder);
820        }
821    }
822}
823
824#[derive(Debug, Clone, PartialEq, Eq, Hash)]
825pub struct CompiletestTest {
826    host: TargetSelection,
827}
828
829impl Step for CompiletestTest {
830    type Output = ();
831
832    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
833        run.path("src/tools/compiletest")
834    }
835
836    fn make_run(run: RunConfig<'_>) {
837        run.builder.ensure(CompiletestTest { host: run.target });
838    }
839
840    /// Runs `cargo test` for compiletest.
841    fn run(self, builder: &Builder<'_>) {
842        let host = self.host;
843
844        // Now that compiletest uses only stable Rust, building it always uses
845        // the stage 0 compiler. However, some of its unit tests need to be able
846        // to query information from an in-tree compiler, so we treat `--stage`
847        // as selecting the stage of that secondary compiler.
848
849        if builder.top_stage == 0 && !builder.config.compiletest_allow_stage0 {
850            eprintln!("\
851ERROR: `--stage 0` causes compiletest to query information from the stage0 (precompiled) compiler, instead of the in-tree compiler, which can cause some tests to fail inappropriately
852NOTE: if you're sure you want to do this, please open an issue as to why. In the meantime, you can override this with `--set build.compiletest-allow-stage0=true`."
853            );
854            crate::exit!(1);
855        }
856
857        let bootstrap_compiler = builder.compiler(0, host);
858        let staged_compiler = builder.compiler(builder.top_stage, host);
859
860        let mut cargo = tool::prepare_tool_cargo(
861            builder,
862            bootstrap_compiler,
863            Mode::ToolBootstrap,
864            host,
865            Kind::Test,
866            "src/tools/compiletest",
867            SourceType::InTree,
868            &[],
869        );
870
871        // Used for `compiletest` self-tests to have the path to the *staged* compiler. Getting this
872        // right is important, as `compiletest` is intended to only support one target spec JSON
873        // format, namely that of the staged compiler.
874        cargo.env("TEST_RUSTC", builder.rustc(staged_compiler));
875
876        run_cargo_test(cargo, &[], &[], "compiletest self test", host, builder);
877    }
878}
879
880#[derive(Debug, Clone, PartialEq, Eq, Hash)]
881pub struct Clippy {
882    compilers: RustcPrivateCompilers,
883}
884
885impl Step for Clippy {
886    type Output = ();
887    const IS_HOST: bool = true;
888
889    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
890        run.suite_path("src/tools/clippy/tests").path("src/tools/clippy")
891    }
892
893    fn is_default_step(_builder: &Builder<'_>) -> bool {
894        false
895    }
896
897    fn make_run(run: RunConfig<'_>) {
898        run.builder.ensure(Clippy {
899            compilers: RustcPrivateCompilers::new(
900                run.builder,
901                run.builder.top_stage,
902                run.builder.host_target,
903            ),
904        });
905    }
906
907    /// Runs `cargo test` for clippy.
908    fn run(self, builder: &Builder<'_>) {
909        let target = self.compilers.target();
910
911        // We need to carefully distinguish the compiler that builds clippy, and the compiler
912        // that is linked into the clippy being tested. `target_compiler` is the latter,
913        // and it must also be used by clippy's test runner to build tests and their dependencies.
914        let target_compiler = self.compilers.target_compiler();
915        let build_compiler = self.compilers.build_compiler();
916
917        let mut cargo = tool::prepare_tool_cargo(
918            builder,
919            build_compiler,
920            Mode::ToolRustcPrivate,
921            target,
922            Kind::Test,
923            "src/tools/clippy",
924            SourceType::InTree,
925            &[],
926        );
927
928        cargo.env("RUSTC_TEST_SUITE", builder.rustc(build_compiler));
929        cargo.env("RUSTC_LIB_PATH", builder.rustc_libdir(build_compiler));
930        let host_libs =
931            builder.stage_out(build_compiler, Mode::ToolRustcPrivate).join(builder.cargo_dir());
932        cargo.env("HOST_LIBS", host_libs);
933
934        // Build the standard library that the tests can use.
935        builder.std(target_compiler, target);
936        cargo.env("TEST_SYSROOT", builder.sysroot(target_compiler));
937        cargo.env("TEST_RUSTC", builder.rustc(target_compiler));
938        cargo.env("TEST_RUSTC_LIB", builder.rustc_libdir(target_compiler));
939
940        // Collect paths of tests to run
941        'partially_test: {
942            let paths = &builder.config.paths[..];
943            let mut test_names = Vec::new();
944            for path in paths {
945                if let Some(path) =
946                    helpers::is_valid_test_suite_arg(path, "src/tools/clippy/tests", builder)
947                {
948                    test_names.push(path);
949                } else if path.ends_with("src/tools/clippy") {
950                    // When src/tools/clippy is called directly, all tests should be run.
951                    break 'partially_test;
952                }
953            }
954            cargo.env("TESTNAME", test_names.join(","));
955        }
956
957        cargo.add_rustc_lib_path(builder);
958        let cargo = prepare_cargo_test(cargo, &[], &[], target, builder);
959
960        let _guard = builder.msg_test("clippy", target, target_compiler.stage);
961
962        // Clippy reports errors if it blessed the outputs
963        if cargo.allow_failure().run(builder) {
964            // The tests succeeded; nothing to do.
965            return;
966        }
967
968        if !builder.config.cmd.bless() {
969            crate::exit!(1);
970        }
971    }
972
973    fn metadata(&self) -> Option<StepMetadata> {
974        Some(
975            StepMetadata::test("clippy", self.compilers.target())
976                .built_by(self.compilers.build_compiler()),
977        )
978    }
979}
980
981fn bin_path_for_cargo(builder: &Builder<'_>, compiler: Compiler) -> OsString {
982    let path = builder.sysroot(compiler).join("bin");
983    let old_path = env::var_os("PATH").unwrap_or_default();
984    env::join_paths(iter::once(path).chain(env::split_paths(&old_path))).expect("")
985}
986
987/// Run the rustdoc-themes tool to test a given compiler.
988#[derive(Debug, Clone, Hash, PartialEq, Eq)]
989pub struct RustdocTheme {
990    /// The compiler (more accurately, its rustdoc) that we test.
991    test_compiler: Compiler,
992}
993
994impl Step for RustdocTheme {
995    type Output = ();
996    const IS_HOST: bool = true;
997
998    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
999        run.path("src/tools/rustdoc-themes")
1000    }
1001
1002    fn is_default_step(_builder: &Builder<'_>) -> bool {
1003        true
1004    }
1005
1006    fn make_run(run: RunConfig<'_>) {
1007        let test_compiler = run.builder.compiler(run.builder.top_stage, run.target);
1008
1009        run.builder.ensure(RustdocTheme { test_compiler });
1010    }
1011
1012    fn run(self, builder: &Builder<'_>) {
1013        let rustdoc = builder.bootstrap_out.join("rustdoc");
1014        let mut cmd = builder.tool_cmd(Tool::RustdocTheme);
1015        cmd.arg(rustdoc.to_str().unwrap())
1016            .arg(builder.src.join("src/librustdoc/html/static/css/rustdoc.css").to_str().unwrap())
1017            .env("RUSTC_STAGE", self.test_compiler.stage.to_string())
1018            .env("RUSTC_SYSROOT", builder.sysroot(self.test_compiler))
1019            .env(
1020                "RUSTDOC_LIBDIR",
1021                builder.sysroot_target_libdir(self.test_compiler, self.test_compiler.host),
1022            )
1023            .env("CFG_RELEASE_CHANNEL", &builder.config.channel)
1024            .env("RUSTDOC_REAL", builder.rustdoc_for_compiler(self.test_compiler))
1025            .env("RUSTC_BOOTSTRAP", "1");
1026        cmd.args(linker_args(builder, self.test_compiler.host, LldThreads::No));
1027
1028        cmd.delay_failure().run(builder);
1029    }
1030
1031    fn metadata(&self) -> Option<StepMetadata> {
1032        Some(
1033            StepMetadata::test("rustdoc-theme", self.test_compiler.host)
1034                .stage(self.test_compiler.stage),
1035        )
1036    }
1037}
1038
1039/// Test rustdoc JS for the standard library.
1040#[derive(Debug, Clone, Hash, PartialEq, Eq)]
1041pub struct RustdocJSStd {
1042    /// Compiler that will build the standary library.
1043    build_compiler: Compiler,
1044    target: TargetSelection,
1045}
1046
1047impl Step for RustdocJSStd {
1048    type Output = ();
1049    const IS_HOST: bool = true;
1050
1051    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1052        run.suite_path("tests/rustdoc-js-std")
1053    }
1054
1055    fn is_default_step(builder: &Builder<'_>) -> bool {
1056        builder.config.nodejs.is_some()
1057    }
1058
1059    fn make_run(run: RunConfig<'_>) {
1060        run.builder.ensure(RustdocJSStd {
1061            build_compiler: run.builder.compiler(run.builder.top_stage, run.builder.host_target),
1062            target: run.target,
1063        });
1064    }
1065
1066    fn run(self, builder: &Builder<'_>) {
1067        let nodejs =
1068            builder.config.nodejs.as_ref().expect("need nodejs to run rustdoc-js-std tests");
1069        let mut command = command(nodejs);
1070        command
1071            .arg(builder.src.join("src/tools/rustdoc-js/tester.js"))
1072            .arg("--crate-name")
1073            .arg("std")
1074            .arg("--resource-suffix")
1075            .arg(&builder.version)
1076            .arg("--doc-folder")
1077            .arg(builder.doc_out(self.target))
1078            .arg("--test-folder")
1079            .arg(builder.src.join("tests/rustdoc-js-std"));
1080        for path in &builder.paths {
1081            if let Some(p) = helpers::is_valid_test_suite_arg(path, "tests/rustdoc-js-std", builder)
1082            {
1083                if !p.ends_with(".js") {
1084                    eprintln!("A non-js file was given: `{}`", path.display());
1085                    panic!("Cannot run rustdoc-js-std tests");
1086                }
1087                command.arg("--test-file").arg(path);
1088            }
1089        }
1090        builder.ensure(crate::core::build_steps::doc::Std::from_build_compiler(
1091            self.build_compiler,
1092            self.target,
1093            DocumentationFormat::Html,
1094        ));
1095        let _guard = builder.msg_test("rustdoc-js-std", self.target, self.build_compiler.stage);
1096        command.run(builder);
1097    }
1098
1099    fn metadata(&self) -> Option<StepMetadata> {
1100        Some(StepMetadata::test("rustdoc-js-std", self.target).stage(self.build_compiler.stage))
1101    }
1102}
1103
1104#[derive(Debug, Clone, Hash, PartialEq, Eq)]
1105pub struct RustdocJSNotStd {
1106    pub target: TargetSelection,
1107    pub compiler: Compiler,
1108}
1109
1110impl Step for RustdocJSNotStd {
1111    type Output = ();
1112    const IS_HOST: bool = true;
1113
1114    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1115        run.suite_path("tests/rustdoc-js")
1116    }
1117
1118    fn is_default_step(builder: &Builder<'_>) -> bool {
1119        builder.config.nodejs.is_some()
1120    }
1121
1122    fn make_run(run: RunConfig<'_>) {
1123        let compiler = run.builder.compiler(run.builder.top_stage, run.build_triple());
1124        run.builder.ensure(RustdocJSNotStd { target: run.target, compiler });
1125    }
1126
1127    fn run(self, builder: &Builder<'_>) {
1128        builder.ensure(Compiletest {
1129            test_compiler: self.compiler,
1130            target: self.target,
1131            mode: CompiletestMode::RustdocJs,
1132            suite: "rustdoc-js",
1133            path: "tests/rustdoc-js",
1134            compare_mode: None,
1135        });
1136    }
1137}
1138
1139fn get_browser_ui_test_version_inner(
1140    builder: &Builder<'_>,
1141    yarn: &Path,
1142    global: bool,
1143) -> Option<String> {
1144    let mut command = command(yarn);
1145    command
1146        .arg("--cwd")
1147        .arg(&builder.build.out)
1148        .arg("list")
1149        .arg("--parseable")
1150        .arg("--long")
1151        .arg("--depth=0");
1152    if global {
1153        command.arg("--global");
1154    }
1155    // Cache the command output so that `test::RustdocGUI` only performs these
1156    // command-line probes once.
1157    let lines = command.allow_failure().cached().run_capture(builder).stdout();
1158    lines
1159        .lines()
1160        .find_map(|l| l.split(':').nth(1)?.strip_prefix("browser-ui-test@"))
1161        .map(|v| v.to_owned())
1162}
1163
1164fn get_browser_ui_test_version(builder: &Builder<'_>) -> Option<String> {
1165    let yarn = builder.config.yarn.as_deref()?;
1166    get_browser_ui_test_version_inner(builder, yarn, false)
1167        .or_else(|| get_browser_ui_test_version_inner(builder, yarn, true))
1168}
1169
1170/// Run GUI tests on a given rustdoc.
1171#[derive(Debug, Clone, Hash, PartialEq, Eq)]
1172pub struct RustdocGUI {
1173    /// The compiler whose rustdoc we are testing.
1174    test_compiler: Compiler,
1175    target: TargetSelection,
1176}
1177
1178impl Step for RustdocGUI {
1179    type Output = ();
1180    const IS_HOST: bool = true;
1181
1182    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1183        run.suite_path("tests/rustdoc-gui")
1184    }
1185
1186    fn is_default_step(builder: &Builder<'_>) -> bool {
1187        builder.config.nodejs.is_some()
1188            && builder.doc_tests != DocTests::Only
1189            && get_browser_ui_test_version(builder).is_some()
1190    }
1191
1192    fn make_run(run: RunConfig<'_>) {
1193        let test_compiler = run.builder.compiler(run.builder.top_stage, run.build_triple());
1194        run.builder.ensure(RustdocGUI { test_compiler, target: run.target });
1195    }
1196
1197    fn run(self, builder: &Builder<'_>) {
1198        builder.std(self.test_compiler, self.target);
1199
1200        let mut cmd = builder.tool_cmd(Tool::RustdocGUITest);
1201
1202        let out_dir = builder.test_out(self.target).join("rustdoc-gui");
1203        build_stamp::clear_if_dirty(
1204            builder,
1205            &out_dir,
1206            &builder.rustdoc_for_compiler(self.test_compiler),
1207        );
1208
1209        if let Some(src) = builder.config.src.to_str() {
1210            cmd.arg("--rust-src").arg(src);
1211        }
1212
1213        if let Some(out_dir) = out_dir.to_str() {
1214            cmd.arg("--out-dir").arg(out_dir);
1215        }
1216
1217        if let Some(initial_cargo) = builder.config.initial_cargo.to_str() {
1218            cmd.arg("--initial-cargo").arg(initial_cargo);
1219        }
1220
1221        cmd.arg("--jobs").arg(builder.jobs().to_string());
1222
1223        cmd.env("RUSTDOC", builder.rustdoc_for_compiler(self.test_compiler))
1224            .env("RUSTC", builder.rustc(self.test_compiler));
1225
1226        add_rustdoc_cargo_linker_args(&mut cmd, builder, self.test_compiler.host, LldThreads::No);
1227
1228        for path in &builder.paths {
1229            if let Some(p) = helpers::is_valid_test_suite_arg(path, "tests/rustdoc-gui", builder) {
1230                if !p.ends_with(".goml") {
1231                    eprintln!("A non-goml file was given: `{}`", path.display());
1232                    panic!("Cannot run rustdoc-gui tests");
1233                }
1234                if let Some(name) = path.file_name().and_then(|f| f.to_str()) {
1235                    cmd.arg("--goml-file").arg(name);
1236                }
1237            }
1238        }
1239
1240        for test_arg in builder.config.test_args() {
1241            cmd.arg("--test-arg").arg(test_arg);
1242        }
1243
1244        if let Some(ref nodejs) = builder.config.nodejs {
1245            cmd.arg("--nodejs").arg(nodejs);
1246        }
1247
1248        if let Some(ref yarn) = builder.config.yarn {
1249            cmd.arg("--yarn").arg(yarn);
1250        }
1251
1252        let _time = helpers::timeit(builder);
1253        let _guard = builder.msg_test("rustdoc-gui", self.target, self.test_compiler.stage);
1254        try_run_tests(builder, &mut cmd, true);
1255    }
1256
1257    fn metadata(&self) -> Option<StepMetadata> {
1258        Some(StepMetadata::test("rustdoc-gui", self.target).stage(self.test_compiler.stage))
1259    }
1260}
1261
1262/// Runs `src/tools/tidy` and `cargo fmt --check` to detect various style
1263/// problems in the repository.
1264///
1265/// (To run the tidy tool's internal tests, use the alias "tidyselftest" instead.)
1266#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1267pub struct Tidy;
1268
1269impl Step for Tidy {
1270    type Output = ();
1271    const IS_HOST: bool = true;
1272
1273    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1274        run.path("src/tools/tidy")
1275    }
1276
1277    fn is_default_step(builder: &Builder<'_>) -> bool {
1278        builder.doc_tests != DocTests::Only
1279    }
1280
1281    fn make_run(run: RunConfig<'_>) {
1282        run.builder.ensure(Tidy);
1283    }
1284
1285    /// Runs the `tidy` tool.
1286    ///
1287    /// This tool in `src/tools` checks up on various bits and pieces of style and
1288    /// otherwise just implements a few lint-like checks that are specific to the
1289    /// compiler itself.
1290    ///
1291    /// Once tidy passes, this step also runs `fmt --check` if tests are being run
1292    /// for the `dev` or `nightly` channels.
1293    fn run(self, builder: &Builder<'_>) {
1294        let mut cmd = builder.tool_cmd(Tool::Tidy);
1295        cmd.arg(&builder.src);
1296        cmd.arg(&builder.initial_cargo);
1297        cmd.arg(&builder.out);
1298        // Tidy is heavily IO constrained. Still respect `-j`, but use a higher limit if `jobs` hasn't been configured.
1299        let jobs = builder.config.jobs.unwrap_or_else(|| {
1300            8 * std::thread::available_parallelism().map_or(1, std::num::NonZeroUsize::get) as u32
1301        });
1302        cmd.arg(jobs.to_string());
1303        // pass the path to the yarn command used for installing js deps.
1304        if let Some(yarn) = &builder.config.yarn {
1305            cmd.arg(yarn);
1306        } else {
1307            cmd.arg("yarn");
1308        }
1309        if builder.is_verbose() {
1310            cmd.arg("--verbose");
1311        }
1312        if builder.config.cmd.bless() {
1313            cmd.arg("--bless");
1314        }
1315        if let Some(s) =
1316            builder.config.cmd.extra_checks().or(builder.config.tidy_extra_checks.as_deref())
1317        {
1318            cmd.arg(format!("--extra-checks={s}"));
1319        }
1320        let mut args = std::env::args_os();
1321        if args.any(|arg| arg == OsStr::new("--")) {
1322            cmd.arg("--");
1323            cmd.args(args);
1324        }
1325
1326        if builder.config.channel == "dev" || builder.config.channel == "nightly" {
1327            if !builder.config.json_output {
1328                builder.info("fmt check");
1329                if builder.config.initial_rustfmt.is_none() {
1330                    let inferred_rustfmt_dir = builder.initial_sysroot.join("bin");
1331                    eprintln!(
1332                        "\
1333ERROR: no `rustfmt` binary found in {PATH}
1334INFO: `rust.channel` is currently set to \"{CHAN}\"
1335HELP: if you are testing a beta branch, set `rust.channel` to \"beta\" in the `bootstrap.toml` file
1336HELP: to skip test's attempt to check tidiness, pass `--skip src/tools/tidy` to `x.py test`",
1337                        PATH = inferred_rustfmt_dir.display(),
1338                        CHAN = builder.config.channel,
1339                    );
1340                    crate::exit!(1);
1341                }
1342                let all = false;
1343                crate::core::build_steps::format::format(
1344                    builder,
1345                    !builder.config.cmd.bless(),
1346                    all,
1347                    &[],
1348                );
1349            } else {
1350                eprintln!(
1351                    "WARNING: `--json-output` is not supported on rustfmt, formatting will be skipped"
1352                );
1353            }
1354        }
1355
1356        builder.info("tidy check");
1357        cmd.delay_failure().run(builder);
1358
1359        builder.info("x.py completions check");
1360        let completion_paths = get_completion_paths(builder);
1361        if builder.config.cmd.bless() {
1362            builder.ensure(crate::core::build_steps::run::GenerateCompletions);
1363        } else if completion_paths
1364            .into_iter()
1365            .any(|(shell, path)| get_completion(shell, &path).is_some())
1366        {
1367            eprintln!(
1368                "x.py completions were changed; run `x.py run generate-completions` to update them"
1369            );
1370            crate::exit!(1);
1371        }
1372
1373        builder.info("x.py help check");
1374        if builder.config.cmd.bless() {
1375            builder.ensure(crate::core::build_steps::run::GenerateHelp);
1376        } else {
1377            let help_path = get_help_path(builder);
1378            let cur_help = std::fs::read_to_string(&help_path).unwrap_or_else(|err| {
1379                eprintln!("couldn't read {}: {}", help_path.display(), err);
1380                crate::exit!(1);
1381            });
1382            let new_help = top_level_help();
1383
1384            if new_help != cur_help {
1385                eprintln!("x.py help was changed; run `x.py run generate-help` to update it");
1386                crate::exit!(1);
1387            }
1388        }
1389    }
1390
1391    fn metadata(&self) -> Option<StepMetadata> {
1392        Some(StepMetadata::test("tidy", TargetSelection::default()))
1393    }
1394}
1395
1396/// Runs `cargo test` on the `src/tools/run-make-support` crate.
1397/// That crate is used by run-make tests.
1398#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1399pub struct CrateRunMakeSupport {
1400    host: TargetSelection,
1401}
1402
1403impl Step for CrateRunMakeSupport {
1404    type Output = ();
1405    const IS_HOST: bool = true;
1406
1407    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1408        run.path("src/tools/run-make-support")
1409    }
1410
1411    fn make_run(run: RunConfig<'_>) {
1412        run.builder.ensure(CrateRunMakeSupport { host: run.target });
1413    }
1414
1415    /// Runs `cargo test` for run-make-support.
1416    fn run(self, builder: &Builder<'_>) {
1417        let host = self.host;
1418        let compiler = builder.compiler(0, host);
1419
1420        let mut cargo = tool::prepare_tool_cargo(
1421            builder,
1422            compiler,
1423            Mode::ToolBootstrap,
1424            host,
1425            Kind::Test,
1426            "src/tools/run-make-support",
1427            SourceType::InTree,
1428            &[],
1429        );
1430        cargo.allow_features("test");
1431        run_cargo_test(cargo, &[], &[], "run-make-support self test", host, builder);
1432    }
1433}
1434
1435#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1436pub struct CrateBuildHelper {
1437    host: TargetSelection,
1438}
1439
1440impl Step for CrateBuildHelper {
1441    type Output = ();
1442    const IS_HOST: bool = true;
1443
1444    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1445        run.path("src/build_helper")
1446    }
1447
1448    fn make_run(run: RunConfig<'_>) {
1449        run.builder.ensure(CrateBuildHelper { host: run.target });
1450    }
1451
1452    /// Runs `cargo test` for build_helper.
1453    fn run(self, builder: &Builder<'_>) {
1454        let host = self.host;
1455        let compiler = builder.compiler(0, host);
1456
1457        let mut cargo = tool::prepare_tool_cargo(
1458            builder,
1459            compiler,
1460            Mode::ToolBootstrap,
1461            host,
1462            Kind::Test,
1463            "src/build_helper",
1464            SourceType::InTree,
1465            &[],
1466        );
1467        cargo.allow_features("test");
1468        run_cargo_test(cargo, &[], &[], "build_helper self test", host, builder);
1469    }
1470}
1471
1472fn testdir(builder: &Builder<'_>, host: TargetSelection) -> PathBuf {
1473    builder.out.join(host).join("test")
1474}
1475
1476/// Declares a test step that invokes compiletest on a particular test suite.
1477macro_rules! test {
1478    (
1479        $( #[$attr:meta] )* // allow docstrings and attributes
1480        $name:ident {
1481            path: $path:expr,
1482            mode: $mode:expr,
1483            suite: $suite:expr,
1484            default: $default:expr
1485            $( , IS_HOST: $IS_HOST:expr )? // default: false
1486            $( , compare_mode: $compare_mode:expr )? // default: None
1487            $( , )? // optional trailing comma
1488        }
1489    ) => {
1490        $( #[$attr] )*
1491        #[derive(Debug, Clone, PartialEq, Eq, Hash)]
1492        pub struct $name {
1493            test_compiler: Compiler,
1494            target: TargetSelection,
1495        }
1496
1497        impl Step for $name {
1498            type Output = ();
1499            const IS_HOST: bool = (const {
1500                #[allow(unused_assignments, unused_mut)]
1501                let mut value = false;
1502                $( value = $IS_HOST; )?
1503                value
1504            });
1505
1506            fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1507                run.suite_path($path)
1508            }
1509
1510            fn is_default_step(_builder: &Builder<'_>) -> bool {
1511                const { $default }
1512            }
1513
1514            fn make_run(run: RunConfig<'_>) {
1515                let test_compiler = run.builder.compiler(run.builder.top_stage, run.build_triple());
1516
1517                run.builder.ensure($name { test_compiler, target: run.target });
1518            }
1519
1520            fn run(self, builder: &Builder<'_>) {
1521                builder.ensure(Compiletest {
1522                    test_compiler: self.test_compiler,
1523                    target: self.target,
1524                    mode: const { $mode },
1525                    suite: $suite,
1526                    path: $path,
1527                    compare_mode: (const {
1528                        #[allow(unused_assignments, unused_mut)]
1529                        let mut value = None;
1530                        $( value = $compare_mode; )?
1531                        value
1532                    }),
1533                })
1534            }
1535        }
1536    };
1537}
1538
1539test!(Ui { path: "tests/ui", mode: CompiletestMode::Ui, suite: "ui", default: true });
1540
1541test!(Crashes {
1542    path: "tests/crashes",
1543    mode: CompiletestMode::Crashes,
1544    suite: "crashes",
1545    default: true,
1546});
1547
1548test!(CodegenLlvm {
1549    path: "tests/codegen-llvm",
1550    mode: CompiletestMode::Codegen,
1551    suite: "codegen-llvm",
1552    default: true
1553});
1554
1555test!(CodegenUnits {
1556    path: "tests/codegen-units",
1557    mode: CompiletestMode::CodegenUnits,
1558    suite: "codegen-units",
1559    default: true,
1560});
1561
1562test!(Incremental {
1563    path: "tests/incremental",
1564    mode: CompiletestMode::Incremental,
1565    suite: "incremental",
1566    default: true,
1567});
1568
1569test!(Debuginfo {
1570    path: "tests/debuginfo",
1571    mode: CompiletestMode::Debuginfo,
1572    suite: "debuginfo",
1573    default: true,
1574    compare_mode: Some("split-dwarf"),
1575});
1576
1577test!(UiFullDeps {
1578    path: "tests/ui-fulldeps",
1579    mode: CompiletestMode::Ui,
1580    suite: "ui-fulldeps",
1581    default: true,
1582    IS_HOST: true,
1583});
1584
1585test!(RustdocHtml {
1586    path: "tests/rustdoc-html",
1587    mode: CompiletestMode::RustdocHtml,
1588    suite: "rustdoc-html",
1589    default: true,
1590    IS_HOST: true,
1591});
1592test!(RustdocUi {
1593    path: "tests/rustdoc-ui",
1594    mode: CompiletestMode::Ui,
1595    suite: "rustdoc-ui",
1596    default: true,
1597    IS_HOST: true,
1598});
1599
1600test!(RustdocJson {
1601    path: "tests/rustdoc-json",
1602    mode: CompiletestMode::RustdocJson,
1603    suite: "rustdoc-json",
1604    default: true,
1605    IS_HOST: true,
1606});
1607
1608test!(Pretty {
1609    path: "tests/pretty",
1610    mode: CompiletestMode::Pretty,
1611    suite: "pretty",
1612    default: true,
1613    IS_HOST: true,
1614});
1615
1616test!(RunMake {
1617    path: "tests/run-make",
1618    mode: CompiletestMode::RunMake,
1619    suite: "run-make",
1620    default: true,
1621});
1622test!(RunMakeCargo {
1623    path: "tests/run-make-cargo",
1624    mode: CompiletestMode::RunMake,
1625    suite: "run-make-cargo",
1626    default: true
1627});
1628
1629test!(AssemblyLlvm {
1630    path: "tests/assembly-llvm",
1631    mode: CompiletestMode::Assembly,
1632    suite: "assembly-llvm",
1633    default: true
1634});
1635
1636/// Runs the coverage test suite at `tests/coverage` in some or all of the
1637/// coverage test modes.
1638#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1639pub struct Coverage {
1640    pub compiler: Compiler,
1641    pub target: TargetSelection,
1642    pub(crate) mode: CompiletestMode,
1643}
1644
1645impl Coverage {
1646    const PATH: &'static str = "tests/coverage";
1647    const SUITE: &'static str = "coverage";
1648    const ALL_MODES: &[CompiletestMode] =
1649        &[CompiletestMode::CoverageMap, CompiletestMode::CoverageRun];
1650}
1651
1652impl Step for Coverage {
1653    type Output = ();
1654    /// Compiletest will automatically skip the "coverage-run" tests if necessary.
1655    const IS_HOST: bool = false;
1656
1657    fn should_run(mut run: ShouldRun<'_>) -> ShouldRun<'_> {
1658        // Support various invocation styles, including:
1659        // - `./x test coverage`
1660        // - `./x test tests/coverage/trivial.rs`
1661        // - `./x test coverage-map`
1662        // - `./x test coverage-run -- tests/coverage/trivial.rs`
1663        run = run.suite_path(Self::PATH);
1664        for mode in Self::ALL_MODES {
1665            run = run.alias(mode.as_str());
1666        }
1667        run
1668    }
1669
1670    fn is_default_step(_builder: &Builder<'_>) -> bool {
1671        true
1672    }
1673
1674    fn make_run(run: RunConfig<'_>) {
1675        let compiler = run.builder.compiler(run.builder.top_stage, run.build_triple());
1676        let target = run.target;
1677
1678        // List of (coverage) test modes that the coverage test suite will be
1679        // run in. It's OK for this to contain duplicates, because the call to
1680        // `Builder::ensure` below will take care of deduplication.
1681        let mut modes = vec![];
1682
1683        // From the pathsets that were selected on the command-line (or by default),
1684        // determine which modes to run in.
1685        for path in &run.paths {
1686            match path {
1687                PathSet::Set(_) => {
1688                    for &mode in Self::ALL_MODES {
1689                        if path.assert_single_path().path == Path::new(mode.as_str()) {
1690                            modes.push(mode);
1691                            break;
1692                        }
1693                    }
1694                }
1695                PathSet::Suite(_) => {
1696                    modes.extend_from_slice(Self::ALL_MODES);
1697                    break;
1698                }
1699            }
1700        }
1701
1702        // Skip any modes that were explicitly skipped/excluded on the command-line.
1703        // FIXME(Zalathar): Integrate this into central skip handling somehow?
1704        modes.retain(|mode| {
1705            !run.builder.config.skip.iter().any(|skip| skip == Path::new(mode.as_str()))
1706        });
1707
1708        // FIXME(Zalathar): Make these commands skip all coverage tests, as expected:
1709        // - `./x test --skip=tests`
1710        // - `./x test --skip=tests/coverage`
1711        // - `./x test --skip=coverage`
1712        // Skip handling currently doesn't have a way to know that skipping the coverage
1713        // suite should also skip the `coverage-map` and `coverage-run` aliases.
1714
1715        for mode in modes {
1716            run.builder.ensure(Coverage { compiler, target, mode });
1717        }
1718    }
1719
1720    fn run(self, builder: &Builder<'_>) {
1721        let Self { compiler, target, mode } = self;
1722        // Like other compiletest suite test steps, delegate to an internal
1723        // compiletest task to actually run the tests.
1724        builder.ensure(Compiletest {
1725            test_compiler: compiler,
1726            target,
1727            mode,
1728            suite: Self::SUITE,
1729            path: Self::PATH,
1730            compare_mode: None,
1731        });
1732    }
1733}
1734
1735test!(CoverageRunRustdoc {
1736    path: "tests/coverage-run-rustdoc",
1737    mode: CompiletestMode::CoverageRun,
1738    suite: "coverage-run-rustdoc",
1739    default: true,
1740    IS_HOST: true,
1741});
1742
1743// For the mir-opt suite we do not use macros, as we need custom behavior when blessing.
1744#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1745pub struct MirOpt {
1746    pub compiler: Compiler,
1747    pub target: TargetSelection,
1748}
1749
1750impl Step for MirOpt {
1751    type Output = ();
1752
1753    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1754        run.suite_path("tests/mir-opt")
1755    }
1756
1757    fn is_default_step(_builder: &Builder<'_>) -> bool {
1758        true
1759    }
1760
1761    fn make_run(run: RunConfig<'_>) {
1762        let compiler = run.builder.compiler(run.builder.top_stage, run.build_triple());
1763        run.builder.ensure(MirOpt { compiler, target: run.target });
1764    }
1765
1766    fn run(self, builder: &Builder<'_>) {
1767        let run = |target| {
1768            builder.ensure(Compiletest {
1769                test_compiler: self.compiler,
1770                target,
1771                mode: CompiletestMode::MirOpt,
1772                suite: "mir-opt",
1773                path: "tests/mir-opt",
1774                compare_mode: None,
1775            })
1776        };
1777
1778        run(self.target);
1779
1780        // Run more targets with `--bless`. But we always run the host target first, since some
1781        // tests use very specific `only` clauses that are not covered by the target set below.
1782        if builder.config.cmd.bless() {
1783            // All that we really need to do is cover all combinations of 32/64-bit and unwind/abort,
1784            // but while we're at it we might as well flex our cross-compilation support. This
1785            // selection covers all our tier 1 operating systems and architectures using only tier
1786            // 1 targets.
1787
1788            for target in ["aarch64-unknown-linux-gnu", "i686-pc-windows-msvc"] {
1789                run(TargetSelection::from_user(target));
1790            }
1791
1792            for target in ["x86_64-apple-darwin", "i686-unknown-linux-musl"] {
1793                let target = TargetSelection::from_user(target);
1794                let panic_abort_target = builder.ensure(MirOptPanicAbortSyntheticTarget {
1795                    compiler: self.compiler,
1796                    base: target,
1797                });
1798                run(panic_abort_target);
1799            }
1800        }
1801    }
1802}
1803
1804/// Executes the `compiletest` tool to run a suite of tests.
1805///
1806/// Compiles all tests with `test_compiler` for `target` with the specified
1807/// compiletest `mode` and `suite` arguments. For example `mode` can be
1808/// "mir-opt" and `suite` can be something like "debuginfo".
1809#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1810struct Compiletest {
1811    /// The compiler that we're testing.
1812    test_compiler: Compiler,
1813    target: TargetSelection,
1814    mode: CompiletestMode,
1815    suite: &'static str,
1816    path: &'static str,
1817    compare_mode: Option<&'static str>,
1818}
1819
1820impl Step for Compiletest {
1821    type Output = ();
1822
1823    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1824        run.never()
1825    }
1826
1827    fn run(self, builder: &Builder<'_>) {
1828        if builder.doc_tests == DocTests::Only {
1829            return;
1830        }
1831
1832        if builder.top_stage == 0 && !builder.config.compiletest_allow_stage0 {
1833            eprintln!("\
1834ERROR: `--stage 0` runs compiletest on the stage0 (precompiled) compiler, not your local changes, and will almost always cause tests to fail
1835HELP: to test the compiler or standard library, omit the stage or explicitly use `--stage 1` instead
1836NOTE: if you're sure you want to do this, please open an issue as to why. In the meantime, you can override this with `--set build.compiletest-allow-stage0=true`."
1837            );
1838            crate::exit!(1);
1839        }
1840
1841        let mut test_compiler = self.test_compiler;
1842        let target = self.target;
1843        let mode = self.mode;
1844        let suite = self.suite;
1845
1846        // Path for test suite
1847        let suite_path = self.path;
1848
1849        // Skip codegen tests if they aren't enabled in configuration.
1850        if !builder.config.codegen_tests && mode == CompiletestMode::Codegen {
1851            return;
1852        }
1853
1854        // Support stage 1 ui-fulldeps. This is somewhat complicated: ui-fulldeps tests for the most
1855        // part test the *API* of the compiler, not how it compiles a given file. As a result, we
1856        // can run them against the stage 1 sources as long as we build them with the stage 0
1857        // bootstrap compiler.
1858        // NOTE: Only stage 1 is special cased because we need the rustc_private artifacts to match the
1859        // running compiler in stage 2 when plugins run.
1860        let query_compiler;
1861        let (stage, stage_id) = if suite == "ui-fulldeps" && test_compiler.stage == 1 {
1862            // Even when using the stage 0 compiler, we also need to provide the stage 1 compiler
1863            // so that compiletest can query it for target information.
1864            query_compiler = Some(test_compiler);
1865            // At stage 0 (stage - 1) we are using the stage0 compiler. Using `self.target` can lead
1866            // finding an incorrect compiler path on cross-targets, as the stage 0 is always equal to
1867            // `build.build` in the configuration.
1868            let build = builder.build.host_target;
1869            test_compiler = builder.compiler(test_compiler.stage - 1, build);
1870            let test_stage = test_compiler.stage + 1;
1871            (test_stage, format!("stage{test_stage}-{build}"))
1872        } else {
1873            query_compiler = None;
1874            let stage = test_compiler.stage;
1875            (stage, format!("stage{stage}-{target}"))
1876        };
1877
1878        if suite.ends_with("fulldeps") {
1879            builder.ensure(compile::Rustc::new(test_compiler, target));
1880        }
1881
1882        if suite == "debuginfo" {
1883            builder.ensure(dist::DebuggerScripts {
1884                sysroot: builder.sysroot(test_compiler).to_path_buf(),
1885                target,
1886            });
1887        }
1888        if mode == CompiletestMode::RunMake {
1889            builder.tool_exe(Tool::RunMakeSupport);
1890        }
1891
1892        // ensure that `libproc_macro` is available on the host.
1893        if suite == "mir-opt" {
1894            builder.ensure(
1895                compile::Std::new(test_compiler, test_compiler.host).is_for_mir_opt_tests(true),
1896            );
1897        } else {
1898            builder.std(test_compiler, test_compiler.host);
1899        }
1900
1901        let mut cmd = builder.tool_cmd(Tool::Compiletest);
1902
1903        if suite == "mir-opt" {
1904            builder.ensure(compile::Std::new(test_compiler, target).is_for_mir_opt_tests(true));
1905        } else {
1906            builder.std(test_compiler, target);
1907        }
1908
1909        builder.ensure(RemoteCopyLibs { build_compiler: test_compiler, target });
1910
1911        // compiletest currently has... a lot of arguments, so let's just pass all
1912        // of them!
1913
1914        cmd.arg("--stage").arg(stage.to_string());
1915        cmd.arg("--stage-id").arg(stage_id);
1916
1917        cmd.arg("--compile-lib-path").arg(builder.rustc_libdir(test_compiler));
1918        cmd.arg("--run-lib-path").arg(builder.sysroot_target_libdir(test_compiler, target));
1919        cmd.arg("--rustc-path").arg(builder.rustc(test_compiler));
1920        if let Some(query_compiler) = query_compiler {
1921            cmd.arg("--query-rustc-path").arg(builder.rustc(query_compiler));
1922        }
1923
1924        // Minicore auxiliary lib for `no_core` tests that need `core` stubs in cross-compilation
1925        // scenarios.
1926        cmd.arg("--minicore-path")
1927            .arg(builder.src.join("tests").join("auxiliary").join("minicore.rs"));
1928
1929        let is_rustdoc = suite == "rustdoc-ui" || suite == "rustdoc-js";
1930
1931        // There are (potentially) 2 `cargo`s to consider:
1932        //
1933        // - A "bootstrap" cargo, which is the same cargo used to build bootstrap itself, and is
1934        //   used to build the `run-make` test recipes and the `run-make-support` test library. All
1935        //   of these may not use unstable rustc/cargo features.
1936        // - An in-tree cargo, which should be considered as under test. The `run-make-cargo` test
1937        //   suite is intended to support the use case of testing the "toolchain" (that is, at the
1938        //   minimum the interaction between in-tree cargo + rustc) together.
1939        //
1940        // For build time and iteration purposes, we partition `run-make` tests which needs an
1941        // in-tree cargo (a smaller subset) versus `run-make` tests that do not into two test
1942        // suites, `run-make` and `run-make-cargo`. That way, contributors who do not need to run
1943        // the `run-make` tests that need in-tree cargo do not need to spend time building in-tree
1944        // cargo.
1945        if mode == CompiletestMode::RunMake {
1946            // We need to pass the compiler that was used to compile run-make-support,
1947            // because we have to use the same compiler to compile rmake.rs recipes.
1948            let stage0_rustc_path = builder.compiler(0, test_compiler.host);
1949            cmd.arg("--stage0-rustc-path").arg(builder.rustc(stage0_rustc_path));
1950
1951            if suite == "run-make-cargo" {
1952                let cargo_path = if test_compiler.stage == 0 {
1953                    // If we're using `--stage 0`, we should provide the bootstrap cargo.
1954                    builder.initial_cargo.clone()
1955                } else {
1956                    builder
1957                        .ensure(tool::Cargo::from_build_compiler(
1958                            builder.compiler(test_compiler.stage - 1, test_compiler.host),
1959                            test_compiler.host,
1960                        ))
1961                        .tool_path
1962                };
1963
1964                cmd.arg("--cargo-path").arg(cargo_path);
1965            }
1966        }
1967
1968        // Avoid depending on rustdoc when we don't need it.
1969        if matches!(
1970            mode,
1971            CompiletestMode::RunMake
1972                | CompiletestMode::RustdocHtml
1973                | CompiletestMode::RustdocJs
1974                | CompiletestMode::RustdocJson
1975        ) || matches!(suite, "rustdoc-ui" | "coverage-run-rustdoc")
1976        {
1977            cmd.arg("--rustdoc-path").arg(builder.rustdoc_for_compiler(test_compiler));
1978        }
1979
1980        if mode == CompiletestMode::RustdocJson {
1981            // Use the stage0 compiler for jsondocck
1982            let json_compiler = builder.compiler(0, builder.host_target);
1983            cmd.arg("--jsondocck-path")
1984                .arg(builder.ensure(tool::JsonDocCk { compiler: json_compiler, target }).tool_path);
1985            cmd.arg("--jsondoclint-path").arg(
1986                builder.ensure(tool::JsonDocLint { compiler: json_compiler, target }).tool_path,
1987            );
1988        }
1989
1990        if matches!(mode, CompiletestMode::CoverageMap | CompiletestMode::CoverageRun) {
1991            let coverage_dump = builder.tool_exe(Tool::CoverageDump);
1992            cmd.arg("--coverage-dump-path").arg(coverage_dump);
1993        }
1994
1995        cmd.arg("--src-root").arg(&builder.src);
1996        cmd.arg("--src-test-suite-root").arg(builder.src.join("tests").join(suite));
1997
1998        // N.B. it's important to distinguish between the *root* build directory, the *host* build
1999        // directory immediately under the root build directory, and the test-suite-specific build
2000        // directory.
2001        cmd.arg("--build-root").arg(&builder.out);
2002        cmd.arg("--build-test-suite-root").arg(testdir(builder, test_compiler.host).join(suite));
2003
2004        // When top stage is 0, that means that we're testing an externally provided compiler.
2005        // In that case we need to use its specific sysroot for tests to pass.
2006        // Note: DO NOT check if test_compiler.stage is 0, because the test compiler can be stage 0
2007        // even if the top stage is 1 (when we run the ui-fulldeps suite).
2008        let sysroot = if builder.top_stage == 0 {
2009            builder.initial_sysroot.clone()
2010        } else {
2011            builder.sysroot(test_compiler)
2012        };
2013
2014        cmd.arg("--sysroot-base").arg(sysroot);
2015
2016        cmd.arg("--suite").arg(suite);
2017        cmd.arg("--mode").arg(mode.as_str());
2018        cmd.arg("--target").arg(target.rustc_target_arg());
2019        cmd.arg("--host").arg(&*test_compiler.host.triple);
2020        cmd.arg("--llvm-filecheck").arg(builder.llvm_filecheck(builder.config.host_target));
2021
2022        if let Some(codegen_backend) = builder.config.cmd.test_codegen_backend() {
2023            if !builder
2024                .config
2025                .enabled_codegen_backends(test_compiler.host)
2026                .contains(codegen_backend)
2027            {
2028                eprintln!(
2029                    "\
2030ERROR: No configured backend named `{name}`
2031HELP: You can add it into `bootstrap.toml` in `rust.codegen-backends = [{name:?}]`",
2032                    name = codegen_backend.name(),
2033                );
2034                crate::exit!(1);
2035            }
2036
2037            if let CodegenBackendKind::Gcc = codegen_backend
2038                && builder.config.rustc_debug_assertions
2039            {
2040                eprintln!(
2041                    r#"WARNING: Running tests with the GCC codegen backend while rustc debug assertions are enabled. This might lead to test failures.
2042Please disable assertions with `rust.debug-assertions = false`.
2043        "#
2044                );
2045            }
2046
2047            // Tells compiletest that we want to use this codegen in particular and to override
2048            // the default one.
2049            cmd.arg("--override-codegen-backend").arg(codegen_backend.name());
2050            // Tells compiletest which codegen backend to use.
2051            // It is used to e.g. ignore tests that don't support that codegen backend.
2052            cmd.arg("--default-codegen-backend").arg(codegen_backend.name());
2053        } else {
2054            // Tells compiletest which codegen backend to use.
2055            // It is used to e.g. ignore tests that don't support that codegen backend.
2056            cmd.arg("--default-codegen-backend")
2057                .arg(builder.config.default_codegen_backend(test_compiler.host).name());
2058        }
2059        if builder.config.cmd.bypass_ignore_backends() {
2060            cmd.arg("--bypass-ignore-backends");
2061        }
2062
2063        if builder.build.config.llvm_enzyme {
2064            cmd.arg("--has-enzyme");
2065        }
2066
2067        if builder.build.config.llvm_offload {
2068            cmd.arg("--has-offload");
2069        }
2070
2071        if builder.config.cmd.bless() {
2072            cmd.arg("--bless");
2073        }
2074
2075        if builder.config.cmd.force_rerun() {
2076            cmd.arg("--force-rerun");
2077        }
2078
2079        if builder.config.cmd.no_capture() {
2080            cmd.arg("--no-capture");
2081        }
2082
2083        let compare_mode =
2084            builder.config.cmd.compare_mode().or_else(|| {
2085                if builder.config.test_compare_mode { self.compare_mode } else { None }
2086            });
2087
2088        if let Some(ref pass) = builder.config.cmd.pass() {
2089            cmd.arg("--pass");
2090            cmd.arg(pass);
2091        }
2092
2093        if let Some(ref run) = builder.config.cmd.run() {
2094            cmd.arg("--run");
2095            cmd.arg(run);
2096        }
2097
2098        if let Some(ref nodejs) = builder.config.nodejs {
2099            cmd.arg("--nodejs").arg(nodejs);
2100        } else if mode == CompiletestMode::RustdocJs {
2101            panic!("need nodejs to run rustdoc-js suite");
2102        }
2103        if builder.config.rust_optimize_tests {
2104            cmd.arg("--optimize-tests");
2105        }
2106        if builder.config.rust_randomize_layout {
2107            cmd.arg("--rust-randomized-layout");
2108        }
2109        if builder.config.cmd.only_modified() {
2110            cmd.arg("--only-modified");
2111        }
2112        if let Some(compiletest_diff_tool) = &builder.config.compiletest_diff_tool {
2113            cmd.arg("--compiletest-diff-tool").arg(compiletest_diff_tool);
2114        }
2115
2116        let mut flags = if is_rustdoc { Vec::new() } else { vec!["-Crpath".to_string()] };
2117        flags.push(format!(
2118            "-Cdebuginfo={}",
2119            if mode == CompiletestMode::Codegen {
2120                // codegen tests typically check LLVM IR and are sensitive to additional debuginfo.
2121                // So do not apply `rust.debuginfo-level-tests` for codegen tests.
2122                if builder.config.rust_debuginfo_level_tests
2123                    != crate::core::config::DebuginfoLevel::None
2124                {
2125                    println!(
2126                        "NOTE: ignoring `rust.debuginfo-level-tests={}` for codegen tests",
2127                        builder.config.rust_debuginfo_level_tests
2128                    );
2129                }
2130                crate::core::config::DebuginfoLevel::None
2131            } else {
2132                builder.config.rust_debuginfo_level_tests
2133            }
2134        ));
2135        flags.extend(builder.config.cmd.compiletest_rustc_args().iter().map(|s| s.to_string()));
2136
2137        if suite != "mir-opt" {
2138            if let Some(linker) = builder.linker(target) {
2139                cmd.arg("--target-linker").arg(linker);
2140            }
2141            if let Some(linker) = builder.linker(test_compiler.host) {
2142                cmd.arg("--host-linker").arg(linker);
2143            }
2144        }
2145
2146        // FIXME(136096): on macOS, we get linker warnings about duplicate `-lm` flags.
2147        if suite == "ui-fulldeps" && target.ends_with("darwin") {
2148            flags.push("-Alinker_messages".into());
2149        }
2150
2151        let mut hostflags = flags.clone();
2152        hostflags.extend(linker_flags(builder, test_compiler.host, LldThreads::No));
2153
2154        let mut targetflags = flags;
2155
2156        // Provide `rust_test_helpers` for both host and target.
2157        if suite == "ui" || suite == "incremental" {
2158            builder.ensure(TestHelpers { target: test_compiler.host });
2159            builder.ensure(TestHelpers { target });
2160            hostflags.push(format!(
2161                "-Lnative={}",
2162                builder.test_helpers_out(test_compiler.host).display()
2163            ));
2164            targetflags.push(format!("-Lnative={}", builder.test_helpers_out(target).display()));
2165        }
2166
2167        for flag in hostflags {
2168            cmd.arg("--host-rustcflags").arg(flag);
2169        }
2170        for flag in targetflags {
2171            cmd.arg("--target-rustcflags").arg(flag);
2172        }
2173
2174        cmd.arg("--python").arg(
2175            builder.config.python.as_ref().expect("python is required for running rustdoc tests"),
2176        );
2177
2178        // Discover and set some flags related to running tests on Android targets.
2179        let android = android::discover_android(builder, target);
2180        if let Some(android::Android { adb_path, adb_test_dir, android_cross_path }) = &android {
2181            cmd.arg("--adb-path").arg(adb_path);
2182            cmd.arg("--adb-test-dir").arg(adb_test_dir);
2183            cmd.arg("--android-cross-path").arg(android_cross_path);
2184        }
2185
2186        if mode == CompiletestMode::Debuginfo {
2187            if let Some(debuggers::Cdb { cdb }) = debuggers::discover_cdb(target) {
2188                cmd.arg("--cdb").arg(cdb);
2189            }
2190
2191            if let Some(debuggers::Gdb { gdb }) = debuggers::discover_gdb(builder, android.as_ref())
2192            {
2193                cmd.arg("--gdb").arg(gdb.as_ref());
2194            }
2195
2196            if let Some(debuggers::Lldb { lldb_exe, lldb_version }) =
2197                debuggers::discover_lldb(builder)
2198            {
2199                cmd.arg("--lldb").arg(lldb_exe);
2200                cmd.arg("--lldb-version").arg(lldb_version);
2201            }
2202        }
2203
2204        if helpers::forcing_clang_based_tests() {
2205            let clang_exe = builder.llvm_out(target).join("bin").join("clang");
2206            cmd.arg("--run-clang-based-tests-with").arg(clang_exe);
2207        }
2208
2209        for exclude in &builder.config.skip {
2210            cmd.arg("--skip");
2211            cmd.arg(exclude);
2212        }
2213
2214        // Get paths from cmd args
2215        let mut paths = match &builder.config.cmd {
2216            Subcommand::Test { .. } => &builder.config.paths[..],
2217            _ => &[],
2218        };
2219
2220        // in rustdoc-js mode, allow filters to be rs files or js files.
2221        // use a late-initialized Vec to avoid cloning for other modes.
2222        let mut paths_v;
2223        if mode == CompiletestMode::RustdocJs {
2224            paths_v = paths.to_vec();
2225            for p in &mut paths_v {
2226                if let Some(ext) = p.extension()
2227                    && ext == "js"
2228                {
2229                    p.set_extension("rs");
2230                }
2231            }
2232            paths = &paths_v;
2233        }
2234        // Get test-args by striping suite path
2235        let mut test_args: Vec<&str> = paths
2236            .iter()
2237            .filter_map(|p| helpers::is_valid_test_suite_arg(p, suite_path, builder))
2238            .collect();
2239
2240        test_args.append(&mut builder.config.test_args());
2241
2242        // On Windows, replace forward slashes in test-args by backslashes
2243        // so the correct filters are passed to libtest
2244        if cfg!(windows) {
2245            let test_args_win: Vec<String> =
2246                test_args.iter().map(|s| s.replace('/', "\\")).collect();
2247            cmd.args(&test_args_win);
2248        } else {
2249            cmd.args(&test_args);
2250        }
2251
2252        if builder.is_verbose() {
2253            cmd.arg("--verbose");
2254        }
2255
2256        if builder.config.rustc_debug_assertions {
2257            cmd.arg("--with-rustc-debug-assertions");
2258        }
2259
2260        if builder.config.std_debug_assertions {
2261            cmd.arg("--with-std-debug-assertions");
2262        }
2263
2264        if builder.config.rust_remap_debuginfo {
2265            cmd.arg("--with-std-remap-debuginfo");
2266        }
2267
2268        cmd.arg("--jobs").arg(builder.jobs().to_string());
2269
2270        let mut llvm_components_passed = false;
2271        let mut copts_passed = false;
2272        if builder.config.llvm_enabled(test_compiler.host) {
2273            let llvm::LlvmResult { host_llvm_config, .. } =
2274                builder.ensure(llvm::Llvm { target: builder.config.host_target });
2275            if !builder.config.dry_run() {
2276                let llvm_version = get_llvm_version(builder, &host_llvm_config);
2277                let llvm_components = command(&host_llvm_config)
2278                    .cached()
2279                    .arg("--components")
2280                    .run_capture_stdout(builder)
2281                    .stdout();
2282                // Remove trailing newline from llvm-config output.
2283                cmd.arg("--llvm-version")
2284                    .arg(llvm_version.trim())
2285                    .arg("--llvm-components")
2286                    .arg(llvm_components.trim());
2287                llvm_components_passed = true;
2288            }
2289            if !builder.config.is_rust_llvm(target) {
2290                cmd.arg("--system-llvm");
2291            }
2292
2293            // Tests that use compiler libraries may inherit the `-lLLVM` link
2294            // requirement, but the `-L` library path is not propagated across
2295            // separate compilations. We can add LLVM's library path to the
2296            // rustc args as a workaround.
2297            if !builder.config.dry_run() && suite.ends_with("fulldeps") {
2298                let llvm_libdir = command(&host_llvm_config)
2299                    .cached()
2300                    .arg("--libdir")
2301                    .run_capture_stdout(builder)
2302                    .stdout();
2303                let link_llvm = if target.is_msvc() {
2304                    format!("-Clink-arg=-LIBPATH:{llvm_libdir}")
2305                } else {
2306                    format!("-Clink-arg=-L{llvm_libdir}")
2307                };
2308                cmd.arg("--host-rustcflags").arg(link_llvm);
2309            }
2310
2311            if !builder.config.dry_run()
2312                && matches!(mode, CompiletestMode::RunMake | CompiletestMode::CoverageRun)
2313            {
2314                // The llvm/bin directory contains many useful cross-platform
2315                // tools. Pass the path to run-make tests so they can use them.
2316                // (The coverage-run tests also need these tools to process
2317                // coverage reports.)
2318                let llvm_bin_path = host_llvm_config
2319                    .parent()
2320                    .expect("Expected llvm-config to be contained in directory");
2321                assert!(llvm_bin_path.is_dir());
2322                cmd.arg("--llvm-bin-dir").arg(llvm_bin_path);
2323            }
2324
2325            if !builder.config.dry_run() && mode == CompiletestMode::RunMake {
2326                // If LLD is available, add it to the PATH
2327                if builder.config.lld_enabled {
2328                    let lld_install_root =
2329                        builder.ensure(llvm::Lld { target: builder.config.host_target });
2330
2331                    let lld_bin_path = lld_install_root.join("bin");
2332
2333                    let old_path = env::var_os("PATH").unwrap_or_default();
2334                    let new_path = env::join_paths(
2335                        std::iter::once(lld_bin_path).chain(env::split_paths(&old_path)),
2336                    )
2337                    .expect("Could not add LLD bin path to PATH");
2338                    cmd.env("PATH", new_path);
2339                }
2340            }
2341        }
2342
2343        // Only pass correct values for these flags for the `run-make` suite as it
2344        // requires that a C++ compiler was configured which isn't always the case.
2345        if !builder.config.dry_run() && mode == CompiletestMode::RunMake {
2346            let mut cflags = builder.cc_handled_clags(target, CLang::C);
2347            cflags.extend(builder.cc_unhandled_cflags(target, GitRepo::Rustc, CLang::C));
2348            let mut cxxflags = builder.cc_handled_clags(target, CLang::Cxx);
2349            cxxflags.extend(builder.cc_unhandled_cflags(target, GitRepo::Rustc, CLang::Cxx));
2350            cmd.arg("--cc")
2351                .arg(builder.cc(target))
2352                .arg("--cxx")
2353                .arg(builder.cxx(target).unwrap())
2354                .arg("--cflags")
2355                .arg(cflags.join(" "))
2356                .arg("--cxxflags")
2357                .arg(cxxflags.join(" "));
2358            copts_passed = true;
2359            if let Some(ar) = builder.ar(target) {
2360                cmd.arg("--ar").arg(ar);
2361            }
2362        }
2363
2364        if !llvm_components_passed {
2365            cmd.arg("--llvm-components").arg("");
2366        }
2367        if !copts_passed {
2368            cmd.arg("--cc")
2369                .arg("")
2370                .arg("--cxx")
2371                .arg("")
2372                .arg("--cflags")
2373                .arg("")
2374                .arg("--cxxflags")
2375                .arg("");
2376        }
2377
2378        if builder.remote_tested(target) {
2379            cmd.arg("--remote-test-client").arg(builder.tool_exe(Tool::RemoteTestClient));
2380        } else if let Some(tool) = builder.runner(target) {
2381            cmd.arg("--runner").arg(tool);
2382        }
2383
2384        if suite != "mir-opt" {
2385            // Running a C compiler on MSVC requires a few env vars to be set, to be
2386            // sure to set them here.
2387            //
2388            // Note that if we encounter `PATH` we make sure to append to our own `PATH`
2389            // rather than stomp over it.
2390            if !builder.config.dry_run() && target.is_msvc() {
2391                for (k, v) in builder.cc[&target].env() {
2392                    if k != "PATH" {
2393                        cmd.env(k, v);
2394                    }
2395                }
2396            }
2397        }
2398
2399        // Special setup to enable running with sanitizers on MSVC.
2400        if !builder.config.dry_run()
2401            && target.contains("msvc")
2402            && builder.config.sanitizers_enabled(target)
2403        {
2404            // Ignore interception failures: not all dlls in the process will have been built with
2405            // address sanitizer enabled (e.g., ntdll.dll).
2406            cmd.env("ASAN_WIN_CONTINUE_ON_INTERCEPTION_FAILURE", "1");
2407            // Add the address sanitizer runtime to the PATH - it is located next to cl.exe.
2408            let asan_runtime_path = builder.cc[&target].path().parent().unwrap().to_path_buf();
2409            let old_path = cmd
2410                .get_envs()
2411                .find_map(|(k, v)| (k == "PATH").then_some(v))
2412                .flatten()
2413                .map_or_else(|| env::var_os("PATH").unwrap_or_default(), |v| v.to_owned());
2414            let new_path = env::join_paths(
2415                env::split_paths(&old_path).chain(std::iter::once(asan_runtime_path)),
2416            )
2417            .expect("Could not add ASAN runtime path to PATH");
2418            cmd.env("PATH", new_path);
2419        }
2420
2421        // Some UI tests trigger behavior in rustc where it reads $CARGO and changes behavior if it exists.
2422        // To make the tests work that rely on it not being set, make sure it is not set.
2423        cmd.env_remove("CARGO");
2424
2425        cmd.env("RUSTC_BOOTSTRAP", "1");
2426        // Override the rustc version used in symbol hashes to reduce the amount of normalization
2427        // needed when diffing test output.
2428        cmd.env("RUSTC_FORCE_RUSTC_VERSION", "compiletest");
2429        cmd.env("DOC_RUST_LANG_ORG_CHANNEL", builder.doc_rust_lang_org_channel());
2430        builder.add_rust_test_threads(&mut cmd);
2431
2432        if builder.config.sanitizers_enabled(target) {
2433            cmd.env("RUSTC_SANITIZER_SUPPORT", "1");
2434        }
2435
2436        if builder.config.profiler_enabled(target) {
2437            cmd.arg("--profiler-runtime");
2438        }
2439
2440        cmd.env("RUST_TEST_TMPDIR", builder.tempdir());
2441
2442        if builder.config.cmd.rustfix_coverage() {
2443            cmd.arg("--rustfix-coverage");
2444        }
2445
2446        cmd.arg("--channel").arg(&builder.config.channel);
2447
2448        if !builder.config.omit_git_hash {
2449            cmd.arg("--git-hash");
2450        }
2451
2452        let git_config = builder.config.git_config();
2453        cmd.arg("--nightly-branch").arg(git_config.nightly_branch);
2454        cmd.arg("--git-merge-commit-email").arg(git_config.git_merge_commit_email);
2455
2456        #[cfg(feature = "build-metrics")]
2457        builder.metrics.begin_test_suite(
2458            build_helper::metrics::TestSuiteMetadata::Compiletest {
2459                suite: suite.into(),
2460                mode: mode.to_string(),
2461                compare_mode: None,
2462                target: self.target.triple.to_string(),
2463                host: self.test_compiler.host.triple.to_string(),
2464                stage: self.test_compiler.stage,
2465            },
2466            builder,
2467        );
2468
2469        let _group = builder.msg_test(
2470            format!("with compiletest suite={suite} mode={mode}"),
2471            target,
2472            test_compiler.stage,
2473        );
2474        try_run_tests(builder, &mut cmd, false);
2475
2476        if let Some(compare_mode) = compare_mode {
2477            cmd.arg("--compare-mode").arg(compare_mode);
2478
2479            #[cfg(feature = "build-metrics")]
2480            builder.metrics.begin_test_suite(
2481                build_helper::metrics::TestSuiteMetadata::Compiletest {
2482                    suite: suite.into(),
2483                    mode: mode.to_string(),
2484                    compare_mode: Some(compare_mode.into()),
2485                    target: self.target.triple.to_string(),
2486                    host: self.test_compiler.host.triple.to_string(),
2487                    stage: self.test_compiler.stage,
2488                },
2489                builder,
2490            );
2491
2492            builder.info(&format!(
2493                "Check compiletest suite={} mode={} compare_mode={} ({} -> {})",
2494                suite, mode, compare_mode, &test_compiler.host, target
2495            ));
2496            let _time = helpers::timeit(builder);
2497            try_run_tests(builder, &mut cmd, false);
2498        }
2499    }
2500
2501    fn metadata(&self) -> Option<StepMetadata> {
2502        Some(
2503            StepMetadata::test(&format!("compiletest-{}", self.suite), self.target)
2504                .stage(self.test_compiler.stage),
2505        )
2506    }
2507}
2508
2509/// Runs the documentation tests for a book in `src/doc` using the `rustdoc` of `test_compiler`.
2510#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2511struct BookTest {
2512    test_compiler: Compiler,
2513    path: PathBuf,
2514    name: &'static str,
2515    is_ext_doc: bool,
2516    dependencies: Vec<&'static str>,
2517}
2518
2519impl Step for BookTest {
2520    type Output = ();
2521    const IS_HOST: bool = true;
2522
2523    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
2524        run.never()
2525    }
2526
2527    fn run(self, builder: &Builder<'_>) {
2528        // External docs are different from local because:
2529        // - Some books need pre-processing by mdbook before being tested.
2530        // - They need to save their state to toolstate.
2531        // - They are only tested on the "checktools" builders.
2532        //
2533        // The local docs are tested by default, and we don't want to pay the
2534        // cost of building mdbook, so they use `rustdoc --test` directly.
2535        // Also, the unstable book is special because SUMMARY.md is generated,
2536        // so it is easier to just run `rustdoc` on its files.
2537        if self.is_ext_doc {
2538            self.run_ext_doc(builder);
2539        } else {
2540            self.run_local_doc(builder);
2541        }
2542    }
2543}
2544
2545impl BookTest {
2546    /// This runs the equivalent of `mdbook test` (via the rustbook wrapper)
2547    /// which in turn runs `rustdoc --test` on each file in the book.
2548    fn run_ext_doc(self, builder: &Builder<'_>) {
2549        let test_compiler = self.test_compiler;
2550
2551        builder.std(test_compiler, test_compiler.host);
2552
2553        // mdbook just executes a binary named "rustdoc", so we need to update
2554        // PATH so that it points to our rustdoc.
2555        let mut rustdoc_path = builder.rustdoc_for_compiler(test_compiler);
2556        rustdoc_path.pop();
2557        let old_path = env::var_os("PATH").unwrap_or_default();
2558        let new_path = env::join_paths(iter::once(rustdoc_path).chain(env::split_paths(&old_path)))
2559            .expect("could not add rustdoc to PATH");
2560
2561        let mut rustbook_cmd = builder.tool_cmd(Tool::Rustbook);
2562        let path = builder.src.join(&self.path);
2563        // Books often have feature-gated example text.
2564        rustbook_cmd.env("RUSTC_BOOTSTRAP", "1");
2565        rustbook_cmd.env("PATH", new_path).arg("test").arg(path);
2566
2567        // Books may also need to build dependencies. For example, `TheBook` has
2568        // code samples which use the `trpl` crate. For the `rustdoc` invocation
2569        // to find them them successfully, they need to be built first and their
2570        // paths used to generate the
2571        let libs = if !self.dependencies.is_empty() {
2572            let mut lib_paths = vec![];
2573            for dep in self.dependencies {
2574                let mode = Mode::ToolRustcPrivate;
2575                let target = builder.config.host_target;
2576                let cargo = tool::prepare_tool_cargo(
2577                    builder,
2578                    test_compiler,
2579                    mode,
2580                    target,
2581                    Kind::Build,
2582                    dep,
2583                    SourceType::Submodule,
2584                    &[],
2585                );
2586
2587                let stamp = BuildStamp::new(&builder.cargo_out(test_compiler, mode, target))
2588                    .with_prefix(PathBuf::from(dep).file_name().and_then(|v| v.to_str()).unwrap());
2589
2590                let output_paths =
2591                    run_cargo(builder, cargo, vec![], &stamp, vec![], ArtifactKeepMode::OnlyRlib);
2592                let directories = output_paths
2593                    .into_iter()
2594                    .filter_map(|p| p.parent().map(ToOwned::to_owned))
2595                    .fold(HashSet::new(), |mut set, dir| {
2596                        set.insert(dir);
2597                        set
2598                    });
2599
2600                lib_paths.extend(directories);
2601            }
2602            lib_paths
2603        } else {
2604            vec![]
2605        };
2606
2607        if !libs.is_empty() {
2608            let paths = libs
2609                .into_iter()
2610                .map(|path| path.into_os_string())
2611                .collect::<Vec<OsString>>()
2612                .join(OsStr::new(","));
2613            rustbook_cmd.args([OsString::from("--library-path"), paths]);
2614        }
2615
2616        builder.add_rust_test_threads(&mut rustbook_cmd);
2617        let _guard = builder.msg_test(
2618            format_args!("mdbook {}", self.path.display()),
2619            test_compiler.host,
2620            test_compiler.stage,
2621        );
2622        let _time = helpers::timeit(builder);
2623        let toolstate = if rustbook_cmd.delay_failure().run(builder) {
2624            ToolState::TestPass
2625        } else {
2626            ToolState::TestFail
2627        };
2628        builder.save_toolstate(self.name, toolstate);
2629    }
2630
2631    /// This runs `rustdoc --test` on all `.md` files in the path.
2632    fn run_local_doc(self, builder: &Builder<'_>) {
2633        let test_compiler = self.test_compiler;
2634        let host = self.test_compiler.host;
2635
2636        builder.std(test_compiler, host);
2637
2638        let _guard = builder.msg_test(
2639            format!("book {}", self.name),
2640            test_compiler.host,
2641            test_compiler.stage,
2642        );
2643
2644        // Do a breadth-first traversal of the `src/doc` directory and just run
2645        // tests for all files that end in `*.md`
2646        let mut stack = vec![builder.src.join(self.path)];
2647        let _time = helpers::timeit(builder);
2648        let mut files = Vec::new();
2649        while let Some(p) = stack.pop() {
2650            if p.is_dir() {
2651                stack.extend(t!(p.read_dir()).map(|p| t!(p).path()));
2652                continue;
2653            }
2654
2655            if p.extension().and_then(|s| s.to_str()) != Some("md") {
2656                continue;
2657            }
2658
2659            files.push(p);
2660        }
2661
2662        files.sort();
2663
2664        for file in files {
2665            markdown_test(builder, test_compiler, &file);
2666        }
2667    }
2668}
2669
2670macro_rules! test_book {
2671    ($(
2672        $name:ident, $path:expr, $book_name:expr,
2673        default=$default:expr
2674        $(,submodules = $submodules:expr)?
2675        $(,dependencies=$dependencies:expr)?
2676        ;
2677    )+) => {
2678        $(
2679            #[derive(Debug, Clone, PartialEq, Eq, Hash)]
2680            pub struct $name {
2681                test_compiler: Compiler,
2682            }
2683
2684            impl Step for $name {
2685                type Output = ();
2686                const IS_HOST: bool = true;
2687
2688                fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
2689                    run.path($path)
2690                }
2691
2692                fn is_default_step(_builder: &Builder<'_>) -> bool {
2693                    const { $default }
2694                }
2695
2696                fn make_run(run: RunConfig<'_>) {
2697                    run.builder.ensure($name {
2698                        test_compiler: run.builder.compiler(run.builder.top_stage, run.target),
2699                    });
2700                }
2701
2702                fn run(self, builder: &Builder<'_>) {
2703                    $(
2704                        for submodule in $submodules {
2705                            builder.require_submodule(submodule, None);
2706                        }
2707                    )*
2708
2709                    let dependencies = vec![];
2710                    $(
2711                        let mut dependencies = dependencies;
2712                        for dep in $dependencies {
2713                            dependencies.push(dep);
2714                        }
2715                    )?
2716
2717                    builder.ensure(BookTest {
2718                        test_compiler: self.test_compiler,
2719                        path: PathBuf::from($path),
2720                        name: $book_name,
2721                        is_ext_doc: !$default,
2722                        dependencies,
2723                    });
2724                }
2725            }
2726        )+
2727    }
2728}
2729
2730test_book!(
2731    Nomicon, "src/doc/nomicon", "nomicon", default=false, submodules=["src/doc/nomicon"];
2732    Reference, "src/doc/reference", "reference", default=false, submodules=["src/doc/reference"];
2733    RustdocBook, "src/doc/rustdoc", "rustdoc", default=true;
2734    RustcBook, "src/doc/rustc", "rustc", default=true;
2735    RustByExample, "src/doc/rust-by-example", "rust-by-example", default=false, submodules=["src/doc/rust-by-example"];
2736    EmbeddedBook, "src/doc/embedded-book", "embedded-book", default=false, submodules=["src/doc/embedded-book"];
2737    TheBook, "src/doc/book", "book", default=false, submodules=["src/doc/book"], dependencies=["src/doc/book/packages/trpl"];
2738    UnstableBook, "src/doc/unstable-book", "unstable-book", default=true;
2739    EditionGuide, "src/doc/edition-guide", "edition-guide", default=false, submodules=["src/doc/edition-guide"];
2740);
2741
2742#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2743pub struct ErrorIndex {
2744    compilers: RustcPrivateCompilers,
2745}
2746
2747impl Step for ErrorIndex {
2748    type Output = ();
2749    const IS_HOST: bool = true;
2750
2751    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
2752        // Also add `error-index` here since that is what appears in the error message
2753        // when this fails.
2754        run.path("src/tools/error_index_generator").alias("error-index")
2755    }
2756
2757    fn is_default_step(_builder: &Builder<'_>) -> bool {
2758        true
2759    }
2760
2761    fn make_run(run: RunConfig<'_>) {
2762        // error_index_generator depends on librustdoc. Use the compiler that
2763        // is normally used to build rustdoc for other tests (like compiletest
2764        // tests in tests/rustdoc-html) so that it shares the same artifacts.
2765        let compilers = RustcPrivateCompilers::new(
2766            run.builder,
2767            run.builder.top_stage,
2768            run.builder.config.host_target,
2769        );
2770        run.builder.ensure(ErrorIndex { compilers });
2771    }
2772
2773    /// Runs the error index generator tool to execute the tests located in the error
2774    /// index.
2775    ///
2776    /// The `error_index_generator` tool lives in `src/tools` and is used to
2777    /// generate a markdown file from the error indexes of the code base which is
2778    /// then passed to `rustdoc --test`.
2779    fn run(self, builder: &Builder<'_>) {
2780        // The compiler that we are testing
2781        let target_compiler = self.compilers.target_compiler();
2782
2783        let dir = testdir(builder, target_compiler.host);
2784        t!(fs::create_dir_all(&dir));
2785        let output = dir.join("error-index.md");
2786
2787        let mut tool = tool::ErrorIndex::command(builder, self.compilers);
2788        tool.arg("markdown").arg(&output);
2789
2790        let guard = builder.msg_test("error-index", target_compiler.host, target_compiler.stage);
2791        let _time = helpers::timeit(builder);
2792        tool.run_capture(builder);
2793        drop(guard);
2794        // The tests themselves need to link to std, so make sure it is
2795        // available.
2796        builder.std(target_compiler, target_compiler.host);
2797        markdown_test(builder, target_compiler, &output);
2798    }
2799}
2800
2801fn markdown_test(builder: &Builder<'_>, compiler: Compiler, markdown: &Path) -> bool {
2802    if let Ok(contents) = fs::read_to_string(markdown)
2803        && !contents.contains("```")
2804    {
2805        return true;
2806    }
2807
2808    builder.do_if_verbose(|| println!("doc tests for: {}", markdown.display()));
2809    let mut cmd = builder.rustdoc_cmd(compiler);
2810    builder.add_rust_test_threads(&mut cmd);
2811    // allow for unstable options such as new editions
2812    cmd.arg("-Z");
2813    cmd.arg("unstable-options");
2814    cmd.arg("--test");
2815    cmd.arg(markdown);
2816    cmd.env("RUSTC_BOOTSTRAP", "1");
2817
2818    let test_args = builder.config.test_args().join(" ");
2819    cmd.arg("--test-args").arg(test_args);
2820
2821    cmd = cmd.delay_failure();
2822    if !builder.config.verbose_tests {
2823        cmd.run_capture(builder).is_success()
2824    } else {
2825        cmd.run(builder)
2826    }
2827}
2828
2829/// Runs `cargo test` for the compiler crates in `compiler/`.
2830///
2831/// (This step does not test `rustc_codegen_cranelift` or `rustc_codegen_gcc`,
2832/// which have their own separate test steps.)
2833#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2834pub struct CrateLibrustc {
2835    /// The compiler that will run unit tests and doctests on the in-tree rustc source.
2836    build_compiler: Compiler,
2837    target: TargetSelection,
2838    crates: Vec<String>,
2839}
2840
2841impl Step for CrateLibrustc {
2842    type Output = ();
2843    const IS_HOST: bool = true;
2844
2845    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
2846        run.crate_or_deps("rustc-main").path("compiler")
2847    }
2848
2849    fn is_default_step(_builder: &Builder<'_>) -> bool {
2850        true
2851    }
2852
2853    fn make_run(run: RunConfig<'_>) {
2854        let builder = run.builder;
2855        let host = run.build_triple();
2856        let build_compiler = builder.compiler(builder.top_stage - 1, host);
2857        let crates = run.make_run_crates(Alias::Compiler);
2858
2859        builder.ensure(CrateLibrustc { build_compiler, target: run.target, crates });
2860    }
2861
2862    fn run(self, builder: &Builder<'_>) {
2863        builder.std(self.build_compiler, self.target);
2864
2865        // To actually run the tests, delegate to a copy of the `Crate` step.
2866        builder.ensure(Crate {
2867            build_compiler: self.build_compiler,
2868            target: self.target,
2869            mode: Mode::Rustc,
2870            crates: self.crates,
2871        });
2872    }
2873
2874    fn metadata(&self) -> Option<StepMetadata> {
2875        Some(StepMetadata::test("CrateLibrustc", self.target).built_by(self.build_compiler))
2876    }
2877}
2878
2879/// Given a `cargo test` subcommand, add the appropriate flags and run it.
2880///
2881/// Returns whether the test succeeded.
2882fn run_cargo_test<'a>(
2883    cargo: builder::Cargo,
2884    libtest_args: &[&str],
2885    crates: &[String],
2886    description: impl Into<Option<&'a str>>,
2887    target: TargetSelection,
2888    builder: &Builder<'_>,
2889) -> bool {
2890    let compiler = cargo.compiler();
2891    let stage = match cargo.mode() {
2892        Mode::Std => compiler.stage,
2893        _ => compiler.stage + 1,
2894    };
2895
2896    let mut cargo = prepare_cargo_test(cargo, libtest_args, crates, target, builder);
2897    let _time = helpers::timeit(builder);
2898
2899    let _group = description.into().and_then(|what| builder.msg_test(what, target, stage));
2900
2901    #[cfg(feature = "build-metrics")]
2902    builder.metrics.begin_test_suite(
2903        build_helper::metrics::TestSuiteMetadata::CargoPackage {
2904            crates: crates.iter().map(|c| c.to_string()).collect(),
2905            target: target.triple.to_string(),
2906            host: compiler.host.triple.to_string(),
2907            stage: compiler.stage,
2908        },
2909        builder,
2910    );
2911    add_flags_and_try_run_tests(builder, &mut cargo)
2912}
2913
2914/// Given a `cargo test` subcommand, pass it the appropriate test flags given a `builder`.
2915fn prepare_cargo_test(
2916    cargo: builder::Cargo,
2917    libtest_args: &[&str],
2918    crates: &[String],
2919    target: TargetSelection,
2920    builder: &Builder<'_>,
2921) -> BootstrapCommand {
2922    let compiler = cargo.compiler();
2923    let mut cargo: BootstrapCommand = cargo.into();
2924
2925    // Propagate `--bless` if it has not already been set/unset
2926    // Any tools that want to use this should bless if `RUSTC_BLESS` is set to
2927    // anything other than `0`.
2928    if builder.config.cmd.bless() && !cargo.get_envs().any(|v| v.0 == "RUSTC_BLESS") {
2929        cargo.env("RUSTC_BLESS", "Gesundheit");
2930    }
2931
2932    // Pass in some standard flags then iterate over the graph we've discovered
2933    // in `cargo metadata` with the maps above and figure out what `-p`
2934    // arguments need to get passed.
2935    if builder.kind == Kind::Test && !builder.fail_fast {
2936        cargo.arg("--no-fail-fast");
2937    }
2938
2939    if builder.config.json_output {
2940        cargo.arg("--message-format=json");
2941    }
2942
2943    match builder.doc_tests {
2944        DocTests::Only => {
2945            cargo.arg("--doc");
2946        }
2947        DocTests::No => {
2948            cargo.args(["--bins", "--examples", "--tests", "--benches"]);
2949        }
2950        DocTests::Yes => {}
2951    }
2952
2953    for krate in crates {
2954        cargo.arg("-p").arg(krate);
2955    }
2956
2957    cargo.arg("--").args(builder.config.test_args()).args(libtest_args);
2958    if !builder.config.verbose_tests {
2959        cargo.arg("--quiet");
2960    }
2961
2962    // The tests are going to run with the *target* libraries, so we need to
2963    // ensure that those libraries show up in the LD_LIBRARY_PATH equivalent.
2964    //
2965    // Note that to run the compiler we need to run with the *host* libraries,
2966    // but our wrapper scripts arrange for that to be the case anyway.
2967    //
2968    // We skip everything on Miri as then this overwrites the libdir set up
2969    // by `Cargo::new` and that actually makes things go wrong.
2970    if builder.kind != Kind::Miri {
2971        let mut dylib_paths = builder.rustc_lib_paths(compiler);
2972        dylib_paths.push(builder.sysroot_target_libdir(compiler, target));
2973        helpers::add_dylib_path(dylib_paths, &mut cargo);
2974    }
2975
2976    if builder.remote_tested(target) {
2977        cargo.env(
2978            format!("CARGO_TARGET_{}_RUNNER", envify(&target.triple)),
2979            format!("{} run 0", builder.tool_exe(Tool::RemoteTestClient).display()),
2980        );
2981    } else if let Some(tool) = builder.runner(target) {
2982        cargo.env(format!("CARGO_TARGET_{}_RUNNER", envify(&target.triple)), tool);
2983    }
2984
2985    cargo
2986}
2987
2988/// Runs `cargo test` for standard library crates.
2989///
2990/// (Also used internally to run `cargo test` for compiler crates.)
2991///
2992/// FIXME(Zalathar): Try to split this into two separate steps: a user-visible
2993/// step for testing standard library crates, and an internal step used for both
2994/// library crates and compiler crates.
2995#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2996pub struct Crate {
2997    /// The compiler that will *build* libstd or rustc in test mode.
2998    build_compiler: Compiler,
2999    target: TargetSelection,
3000    mode: Mode,
3001    crates: Vec<String>,
3002}
3003
3004impl Step for Crate {
3005    type Output = ();
3006
3007    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
3008        run.crate_or_deps("sysroot").crate_or_deps("coretests").crate_or_deps("alloctests")
3009    }
3010
3011    fn is_default_step(_builder: &Builder<'_>) -> bool {
3012        true
3013    }
3014
3015    fn make_run(run: RunConfig<'_>) {
3016        let builder = run.builder;
3017        let host = run.build_triple();
3018        let build_compiler = builder.compiler(builder.top_stage, host);
3019        let crates = run
3020            .paths
3021            .iter()
3022            .map(|p| builder.crate_paths[&p.assert_single_path().path].clone())
3023            .collect();
3024
3025        builder.ensure(Crate { build_compiler, target: run.target, mode: Mode::Std, crates });
3026    }
3027
3028    /// Runs all unit tests plus documentation tests for a given crate defined
3029    /// by a `Cargo.toml` (single manifest)
3030    ///
3031    /// This is what runs tests for crates like the standard library, compiler, etc.
3032    /// It essentially is the driver for running `cargo test`.
3033    ///
3034    /// Currently this runs all tests for a DAG by passing a bunch of `-p foo`
3035    /// arguments, and those arguments are discovered from `cargo metadata`.
3036    fn run(self, builder: &Builder<'_>) {
3037        let build_compiler = self.build_compiler;
3038        let target = self.target;
3039        let mode = self.mode;
3040
3041        // Prepare sysroot
3042        // See [field@compile::Std::force_recompile].
3043        builder.ensure(Std::new(build_compiler, build_compiler.host).force_recompile(true));
3044
3045        let mut cargo = if builder.kind == Kind::Miri {
3046            if builder.top_stage == 0 {
3047                eprintln!("ERROR: `x.py miri` requires stage 1 or higher");
3048                std::process::exit(1);
3049            }
3050
3051            // Build `cargo miri test` command
3052            // (Implicitly prepares target sysroot)
3053            let mut cargo = builder::Cargo::new(
3054                builder,
3055                build_compiler,
3056                mode,
3057                SourceType::InTree,
3058                target,
3059                Kind::MiriTest,
3060            );
3061            // This hack helps bootstrap run standard library tests in Miri. The issue is as
3062            // follows: when running `cargo miri test` on libcore, cargo builds a local copy of core
3063            // and makes it a dependency of the integration test crate. This copy duplicates all the
3064            // lang items, so the build fails. (Regular testing avoids this because the sysroot is a
3065            // literal copy of what `cargo build` produces, but since Miri builds its own sysroot
3066            // this does not work for us.) So we need to make it so that the locally built libcore
3067            // contains all the items from `core`, but does not re-define them -- we want to replace
3068            // the entire crate but a re-export of the sysroot crate. We do this by swapping out the
3069            // source file: if `MIRI_REPLACE_LIBRS_IF_NOT_TEST` is set and we are building a
3070            // `lib.rs` file, and a `lib.miri.rs` file exists in the same folder, we build that
3071            // instead. But crucially we only do that for the library, not the test builds.
3072            cargo.env("MIRI_REPLACE_LIBRS_IF_NOT_TEST", "1");
3073            // std needs to be built with `-Zforce-unstable-if-unmarked`. For some reason the builder
3074            // does not set this directly, but relies on the rustc wrapper to set it, and we are not using
3075            // the wrapper -- hence we have to set it ourselves.
3076            cargo.rustflag("-Zforce-unstable-if-unmarked");
3077            cargo
3078        } else {
3079            // Also prepare a sysroot for the target.
3080            if !builder.config.is_host_target(target) {
3081                builder.ensure(compile::Std::new(build_compiler, target).force_recompile(true));
3082                builder.ensure(RemoteCopyLibs { build_compiler, target });
3083            }
3084
3085            // Build `cargo test` command
3086            builder::Cargo::new(
3087                builder,
3088                build_compiler,
3089                mode,
3090                SourceType::InTree,
3091                target,
3092                builder.kind,
3093            )
3094        };
3095
3096        match mode {
3097            Mode::Std => {
3098                if builder.kind == Kind::Miri {
3099                    // We can't use `std_cargo` as that uses `optimized-compiler-builtins` which
3100                    // needs host tools for the given target. This is similar to what `compile::Std`
3101                    // does when `is_for_mir_opt_tests` is true. There's probably a chance for
3102                    // de-duplication here... `std_cargo` should support a mode that avoids needing
3103                    // host tools.
3104                    cargo
3105                        .arg("--manifest-path")
3106                        .arg(builder.src.join("library/sysroot/Cargo.toml"));
3107                } else {
3108                    compile::std_cargo(builder, target, &mut cargo, &[]);
3109                }
3110            }
3111            Mode::Rustc => {
3112                compile::rustc_cargo(builder, &mut cargo, target, &build_compiler, &self.crates);
3113            }
3114            _ => panic!("can only test libraries"),
3115        };
3116
3117        let mut crates = self.crates.clone();
3118        // The core and alloc crates can't directly be tested. We
3119        // could silently ignore them, but adding their own test
3120        // crates is less confusing for users. We still keep core and
3121        // alloc themself for doctests
3122        if crates.iter().any(|crate_| crate_ == "core") {
3123            crates.push("coretests".to_owned());
3124        }
3125        if crates.iter().any(|crate_| crate_ == "alloc") {
3126            crates.push("alloctests".to_owned());
3127        }
3128
3129        run_cargo_test(cargo, &[], &crates, &*crate_description(&self.crates), target, builder);
3130    }
3131}
3132
3133/// Run cargo tests for the rustdoc crate.
3134/// Rustdoc is special in various ways, which is why this step is different from `Crate`.
3135#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3136pub struct CrateRustdoc {
3137    host: TargetSelection,
3138}
3139
3140impl Step for CrateRustdoc {
3141    type Output = ();
3142    const IS_HOST: bool = true;
3143
3144    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
3145        run.paths(&["src/librustdoc", "src/tools/rustdoc"])
3146    }
3147
3148    fn is_default_step(_builder: &Builder<'_>) -> bool {
3149        true
3150    }
3151
3152    fn make_run(run: RunConfig<'_>) {
3153        let builder = run.builder;
3154
3155        builder.ensure(CrateRustdoc { host: run.target });
3156    }
3157
3158    fn run(self, builder: &Builder<'_>) {
3159        let target = self.host;
3160
3161        let compiler = if builder.download_rustc() {
3162            builder.compiler(builder.top_stage, target)
3163        } else {
3164            // Use the previous stage compiler to reuse the artifacts that are
3165            // created when running compiletest for tests/rustdoc-html. If this used
3166            // `compiler`, then it would cause rustdoc to be built *again*, which
3167            // isn't really necessary.
3168            builder.compiler_for(builder.top_stage, target, target)
3169        };
3170        // NOTE: normally `ensure(Rustc)` automatically runs `ensure(Std)` for us. However, when
3171        // using `download-rustc`, the rustc_private artifacts may be in a *different sysroot* from
3172        // the target rustdoc (`ci-rustc-sysroot` vs `stage2`). In that case, we need to ensure this
3173        // explicitly to make sure it ends up in the stage2 sysroot.
3174        builder.std(compiler, target);
3175        builder.ensure(compile::Rustc::new(compiler, target));
3176
3177        let mut cargo = tool::prepare_tool_cargo(
3178            builder,
3179            compiler,
3180            Mode::ToolRustcPrivate,
3181            target,
3182            builder.kind,
3183            "src/tools/rustdoc",
3184            SourceType::InTree,
3185            &[],
3186        );
3187        if self.host.contains("musl") {
3188            cargo.arg("'-Ctarget-feature=-crt-static'");
3189        }
3190
3191        // This is needed for running doctests on librustdoc. This is a bit of
3192        // an unfortunate interaction with how bootstrap works and how cargo
3193        // sets up the dylib path, and the fact that the doctest (in
3194        // html/markdown.rs) links to rustc-private libs. For stage1, the
3195        // compiler host dylibs (in stage1/lib) are not the same as the target
3196        // dylibs (in stage1/lib/rustlib/...). This is different from a normal
3197        // rust distribution where they are the same.
3198        //
3199        // On the cargo side, normal tests use `target_process` which handles
3200        // setting up the dylib for a *target* (stage1/lib/rustlib/... in this
3201        // case). However, for doctests it uses `rustdoc_process` which only
3202        // sets up the dylib path for the *host* (stage1/lib), which is the
3203        // wrong directory.
3204        //
3205        // Recall that we special-cased `compiler_for(top_stage)` above, so we always use stage1.
3206        //
3207        // It should be considered to just stop running doctests on
3208        // librustdoc. There is only one test, and it doesn't look too
3209        // important. There might be other ways to avoid this, but it seems
3210        // pretty convoluted.
3211        //
3212        // See also https://github.com/rust-lang/rust/issues/13983 where the
3213        // host vs target dylibs for rustdoc are consistently tricky to deal
3214        // with.
3215        //
3216        // Note that this set the host libdir for `download_rustc`, which uses a normal rust distribution.
3217        let libdir = if builder.download_rustc() {
3218            builder.rustc_libdir(compiler)
3219        } else {
3220            builder.sysroot_target_libdir(compiler, target).to_path_buf()
3221        };
3222        let mut dylib_path = dylib_path();
3223        dylib_path.insert(0, PathBuf::from(&*libdir));
3224        cargo.env(dylib_path_var(), env::join_paths(&dylib_path).unwrap());
3225
3226        run_cargo_test(cargo, &[], &["rustdoc:0.0.0".to_string()], "rustdoc", target, builder);
3227    }
3228}
3229
3230#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3231pub struct CrateRustdocJsonTypes {
3232    build_compiler: Compiler,
3233    target: TargetSelection,
3234}
3235
3236impl Step for CrateRustdocJsonTypes {
3237    type Output = ();
3238    const IS_HOST: bool = true;
3239
3240    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
3241        run.path("src/rustdoc-json-types")
3242    }
3243
3244    fn is_default_step(_builder: &Builder<'_>) -> bool {
3245        true
3246    }
3247
3248    fn make_run(run: RunConfig<'_>) {
3249        let builder = run.builder;
3250
3251        builder.ensure(CrateRustdocJsonTypes {
3252            build_compiler: get_tool_target_compiler(
3253                builder,
3254                ToolTargetBuildMode::Build(run.target),
3255            ),
3256            target: run.target,
3257        });
3258    }
3259
3260    fn run(self, builder: &Builder<'_>) {
3261        let target = self.target;
3262
3263        let cargo = tool::prepare_tool_cargo(
3264            builder,
3265            self.build_compiler,
3266            Mode::ToolTarget,
3267            target,
3268            builder.kind,
3269            "src/rustdoc-json-types",
3270            SourceType::InTree,
3271            &[],
3272        );
3273
3274        // FIXME: this looks very wrong, libtest doesn't accept `-C` arguments and the quotes are fishy.
3275        let libtest_args = if target.contains("musl") {
3276            ["'-Ctarget-feature=-crt-static'"].as_slice()
3277        } else {
3278            &[]
3279        };
3280
3281        run_cargo_test(
3282            cargo,
3283            libtest_args,
3284            &["rustdoc-json-types".to_string()],
3285            "rustdoc-json-types",
3286            target,
3287            builder,
3288        );
3289    }
3290}
3291
3292/// Some test suites are run inside emulators or on remote devices, and most
3293/// of our test binaries are linked dynamically which means we need to ship
3294/// the standard library and such to the emulator ahead of time. This step
3295/// represents this and is a dependency of all test suites.
3296///
3297/// Most of the time this is a no-op. For some steps such as shipping data to
3298/// QEMU we have to build our own tools so we've got conditional dependencies
3299/// on those programs as well. Note that the remote test client is built for
3300/// the build target (us) and the server is built for the target.
3301#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3302pub struct RemoteCopyLibs {
3303    build_compiler: Compiler,
3304    target: TargetSelection,
3305}
3306
3307impl Step for RemoteCopyLibs {
3308    type Output = ();
3309
3310    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
3311        run.never()
3312    }
3313
3314    fn run(self, builder: &Builder<'_>) {
3315        let build_compiler = self.build_compiler;
3316        let target = self.target;
3317        if !builder.remote_tested(target) {
3318            return;
3319        }
3320
3321        builder.std(build_compiler, target);
3322
3323        builder.info(&format!("REMOTE copy libs to emulator ({target})"));
3324
3325        let remote_test_server = builder.ensure(tool::RemoteTestServer { build_compiler, target });
3326
3327        // Spawn the emulator and wait for it to come online
3328        let tool = builder.tool_exe(Tool::RemoteTestClient);
3329        let mut cmd = command(&tool);
3330        cmd.arg("spawn-emulator")
3331            .arg(target.triple)
3332            .arg(&remote_test_server.tool_path)
3333            .arg(builder.tempdir());
3334        if let Some(rootfs) = builder.qemu_rootfs(target) {
3335            cmd.arg(rootfs);
3336        }
3337        cmd.run(builder);
3338
3339        // Push all our dylibs to the emulator
3340        for f in t!(builder.sysroot_target_libdir(build_compiler, target).read_dir()) {
3341            let f = t!(f);
3342            if helpers::is_dylib(&f.path()) {
3343                command(&tool).arg("push").arg(f.path()).run(builder);
3344            }
3345        }
3346    }
3347}
3348
3349#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3350pub struct Distcheck;
3351
3352impl Step for Distcheck {
3353    type Output = ();
3354
3355    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
3356        run.alias("distcheck")
3357    }
3358
3359    fn make_run(run: RunConfig<'_>) {
3360        run.builder.ensure(Distcheck);
3361    }
3362
3363    /// Runs `distcheck`, which is a collection of smoke tests:
3364    ///
3365    /// - Run `make check` from an unpacked dist tarball to make sure we can at the minimum run
3366    ///   check steps from those sources.
3367    /// - Check that selected dist components (`rust-src` only at the moment) at least have expected
3368    ///   directory shape and crate manifests that cargo can generate a lockfile from.
3369    /// - Check that we can run `cargo metadata` on the workspace in the `rustc-dev` component
3370    ///
3371    /// FIXME(#136822): dist components are under-tested.
3372    fn run(self, builder: &Builder<'_>) {
3373        // Use a temporary directory completely outside the current checkout, to avoid reusing any
3374        // local source code, built artifacts or configuration by accident
3375        let root_dir = std::env::temp_dir().join("distcheck");
3376
3377        distcheck_plain_source_tarball(builder, &root_dir.join("distcheck-rustc-src"));
3378        distcheck_rust_src(builder, &root_dir.join("distcheck-rust-src"));
3379        distcheck_rustc_dev(builder, &root_dir.join("distcheck-rustc-dev"));
3380    }
3381}
3382
3383/// Check that we can build some basic things from the plain source tarball
3384fn distcheck_plain_source_tarball(builder: &Builder<'_>, plain_src_dir: &Path) {
3385    builder.info("Distcheck plain source tarball");
3386    let plain_src_tarball = builder.ensure(dist::PlainSourceTarball);
3387    builder.clear_dir(plain_src_dir);
3388
3389    let configure_args: Vec<String> = std::env::var("DISTCHECK_CONFIGURE_ARGS")
3390        .map(|args| args.split(" ").map(|s| s.to_string()).collect::<Vec<String>>())
3391        .unwrap_or_default();
3392
3393    command("tar")
3394        .arg("-xf")
3395        .arg(plain_src_tarball.tarball())
3396        .arg("--strip-components=1")
3397        .current_dir(plain_src_dir)
3398        .run(builder);
3399    command("./configure")
3400        .arg("--set")
3401        .arg("rust.omit-git-hash=false")
3402        .args(&configure_args)
3403        .arg("--enable-vendor")
3404        .current_dir(plain_src_dir)
3405        .run(builder);
3406    command(helpers::make(&builder.config.host_target.triple))
3407        .arg("check")
3408        // Do not run the build as if we were in CI, otherwise git would be assumed to be
3409        // present, but we build from a tarball here
3410        .env("GITHUB_ACTIONS", "0")
3411        .current_dir(plain_src_dir)
3412        .run(builder);
3413    // Mitigate pressure on small-capacity disks.
3414    builder.remove_dir(plain_src_dir);
3415}
3416
3417/// Check that rust-src has all of libstd's dependencies
3418fn distcheck_rust_src(builder: &Builder<'_>, src_dir: &Path) {
3419    builder.info("Distcheck rust-src");
3420    let src_tarball = builder.ensure(dist::Src);
3421    builder.clear_dir(src_dir);
3422
3423    command("tar")
3424        .arg("-xf")
3425        .arg(src_tarball.tarball())
3426        .arg("--strip-components=1")
3427        .current_dir(src_dir)
3428        .run(builder);
3429
3430    let toml = src_dir.join("rust-src/lib/rustlib/src/rust/library/std/Cargo.toml");
3431    command(&builder.initial_cargo)
3432        // Will read the libstd Cargo.toml
3433        // which uses the unstable `public-dependency` feature.
3434        .env("RUSTC_BOOTSTRAP", "1")
3435        .arg("generate-lockfile")
3436        .arg("--manifest-path")
3437        .arg(&toml)
3438        .current_dir(src_dir)
3439        .run(builder);
3440    // Mitigate pressure on small-capacity disks.
3441    builder.remove_dir(src_dir);
3442}
3443
3444/// Check that rustc-dev's compiler crate source code can be loaded with `cargo metadata`
3445fn distcheck_rustc_dev(builder: &Builder<'_>, dir: &Path) {
3446    builder.info("Distcheck rustc-dev");
3447    let tarball = builder.ensure(dist::RustcDev::new(builder, builder.host_target)).unwrap();
3448    builder.clear_dir(dir);
3449
3450    command("tar")
3451        .arg("-xf")
3452        .arg(tarball.tarball())
3453        .arg("--strip-components=1")
3454        .current_dir(dir)
3455        .run(builder);
3456
3457    command(&builder.initial_cargo)
3458        .arg("metadata")
3459        .arg("--manifest-path")
3460        .arg("rustc-dev/lib/rustlib/rustc-src/rust/compiler/rustc/Cargo.toml")
3461        .env("RUSTC_BOOTSTRAP", "1")
3462        // We might not have a globally available `rustc` binary on CI
3463        .env("RUSTC", &builder.initial_rustc)
3464        .current_dir(dir)
3465        .run(builder);
3466    // Mitigate pressure on small-capacity disks.
3467    builder.remove_dir(dir);
3468}
3469
3470/// Runs unit tests in `bootstrap_test.py`, which test the Python parts of bootstrap.
3471#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3472pub(crate) struct BootstrapPy;
3473
3474impl Step for BootstrapPy {
3475    type Output = ();
3476    const IS_HOST: bool = true;
3477
3478    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
3479        run.alias("bootstrap-py")
3480    }
3481
3482    fn is_default_step(builder: &Builder<'_>) -> bool {
3483        // Bootstrap tests might not be perfectly self-contained and can depend
3484        // on the environment, so only run them by default in CI, not locally.
3485        // See `test::Bootstrap::should_run`.
3486        builder.config.is_running_on_ci
3487    }
3488
3489    fn make_run(run: RunConfig<'_>) {
3490        run.builder.ensure(BootstrapPy)
3491    }
3492
3493    fn run(self, builder: &Builder<'_>) -> Self::Output {
3494        let mut check_bootstrap = command(
3495            builder.config.python.as_ref().expect("python is required for running bootstrap tests"),
3496        );
3497        check_bootstrap
3498            .args(["-m", "unittest", "bootstrap_test.py"])
3499            // Forward command-line args after `--` to unittest, for filtering etc.
3500            .args(builder.config.test_args())
3501            .env("BUILD_DIR", &builder.out)
3502            .env("BUILD_PLATFORM", builder.build.host_target.triple)
3503            .env("BOOTSTRAP_TEST_RUSTC_BIN", &builder.initial_rustc)
3504            .env("BOOTSTRAP_TEST_CARGO_BIN", &builder.initial_cargo)
3505            .current_dir(builder.src.join("src/bootstrap/"));
3506        check_bootstrap.delay_failure().run(builder);
3507    }
3508}
3509
3510#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3511pub struct Bootstrap;
3512
3513impl Step for Bootstrap {
3514    type Output = ();
3515    const IS_HOST: bool = true;
3516
3517    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
3518        run.path("src/bootstrap")
3519    }
3520
3521    fn is_default_step(builder: &Builder<'_>) -> bool {
3522        // Bootstrap tests might not be perfectly self-contained and can depend on the external
3523        // environment, submodules that are checked out, etc.
3524        // Therefore we only run them by default on CI.
3525        builder.config.is_running_on_ci
3526    }
3527
3528    /// Tests the build system itself.
3529    fn run(self, builder: &Builder<'_>) {
3530        let host = builder.config.host_target;
3531        let build_compiler = builder.compiler(0, host);
3532
3533        // Some tests require cargo submodule to be present.
3534        builder.build.require_submodule("src/tools/cargo", None);
3535
3536        let mut cargo = tool::prepare_tool_cargo(
3537            builder,
3538            build_compiler,
3539            Mode::ToolBootstrap,
3540            host,
3541            Kind::Test,
3542            "src/bootstrap",
3543            SourceType::InTree,
3544            &[],
3545        );
3546
3547        cargo.release_build(false);
3548
3549        cargo
3550            .rustflag("-Cdebuginfo=2")
3551            .env("CARGO_TARGET_DIR", builder.out.join("bootstrap"))
3552            // Needed for insta to correctly write pending snapshots to the right directories.
3553            .env("INSTA_WORKSPACE_ROOT", &builder.src)
3554            .env("RUSTC_BOOTSTRAP", "1");
3555
3556        if builder.config.cmd.bless() {
3557            // Tell `insta` to automatically bless any failing `.snap` files.
3558            // Unlike compiletest blessing, the tests might still report failure.
3559            // Does not bless inline snapshots.
3560            cargo.env("INSTA_UPDATE", "always");
3561        }
3562
3563        run_cargo_test(cargo, &[], &[], None, host, builder);
3564    }
3565
3566    fn make_run(run: RunConfig<'_>) {
3567        run.builder.ensure(Bootstrap);
3568    }
3569}
3570
3571fn get_compiler_to_test(builder: &Builder<'_>, target: TargetSelection) -> Compiler {
3572    builder.compiler(builder.top_stage, target)
3573}
3574
3575/// Tests the Platform Support page in the rustc book.
3576/// `test_compiler` is used to query the actual targets that are checked.
3577#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3578pub struct TierCheck {
3579    test_compiler: Compiler,
3580}
3581
3582impl Step for TierCheck {
3583    type Output = ();
3584    const IS_HOST: bool = true;
3585
3586    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
3587        run.path("src/tools/tier-check")
3588    }
3589
3590    fn is_default_step(_builder: &Builder<'_>) -> bool {
3591        true
3592    }
3593
3594    fn make_run(run: RunConfig<'_>) {
3595        run.builder
3596            .ensure(TierCheck { test_compiler: get_compiler_to_test(run.builder, run.target) });
3597    }
3598
3599    fn run(self, builder: &Builder<'_>) {
3600        let tool_build_compiler = builder.compiler(0, builder.host_target);
3601
3602        let mut cargo = tool::prepare_tool_cargo(
3603            builder,
3604            tool_build_compiler,
3605            Mode::ToolBootstrap,
3606            tool_build_compiler.host,
3607            Kind::Run,
3608            "src/tools/tier-check",
3609            SourceType::InTree,
3610            &[],
3611        );
3612        cargo.arg(builder.src.join("src/doc/rustc/src/platform-support.md"));
3613        cargo.arg(builder.rustc(self.test_compiler));
3614
3615        let _guard = builder.msg_test(
3616            "platform support check",
3617            self.test_compiler.host,
3618            self.test_compiler.stage,
3619        );
3620        BootstrapCommand::from(cargo).delay_failure().run(builder);
3621    }
3622
3623    fn metadata(&self) -> Option<StepMetadata> {
3624        Some(StepMetadata::test("tier-check", self.test_compiler.host))
3625    }
3626}
3627
3628#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3629pub struct LintDocs {
3630    build_compiler: Compiler,
3631    target: TargetSelection,
3632}
3633
3634impl Step for LintDocs {
3635    type Output = ();
3636    const IS_HOST: bool = true;
3637
3638    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
3639        run.path("src/tools/lint-docs")
3640    }
3641
3642    fn is_default_step(builder: &Builder<'_>) -> bool {
3643        // Lint docs tests might not work with stage 1, so do not run this test by default in
3644        // `x test` below stage 2.
3645        builder.top_stage >= 2
3646    }
3647
3648    fn make_run(run: RunConfig<'_>) {
3649        if run.builder.top_stage < 2 {
3650            eprintln!("WARNING: lint-docs tests might not work below stage 2");
3651        }
3652
3653        run.builder.ensure(LintDocs {
3654            build_compiler: prepare_doc_compiler(
3655                run.builder,
3656                run.builder.config.host_target,
3657                run.builder.top_stage,
3658            ),
3659            target: run.target,
3660        });
3661    }
3662
3663    /// Tests that the lint examples in the rustc book generate the correct
3664    /// lints and have the expected format.
3665    fn run(self, builder: &Builder<'_>) {
3666        builder.ensure(crate::core::build_steps::doc::RustcBook::validate(
3667            self.build_compiler,
3668            self.target,
3669        ));
3670    }
3671
3672    fn metadata(&self) -> Option<StepMetadata> {
3673        Some(StepMetadata::test("lint-docs", self.target).built_by(self.build_compiler))
3674    }
3675}
3676
3677#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3678pub struct RustInstaller;
3679
3680impl Step for RustInstaller {
3681    type Output = ();
3682    const IS_HOST: bool = true;
3683
3684    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
3685        run.path("src/tools/rust-installer")
3686    }
3687
3688    fn is_default_step(_builder: &Builder<'_>) -> bool {
3689        true
3690    }
3691
3692    fn make_run(run: RunConfig<'_>) {
3693        run.builder.ensure(Self);
3694    }
3695
3696    /// Ensure the version placeholder replacement tool builds
3697    fn run(self, builder: &Builder<'_>) {
3698        let bootstrap_host = builder.config.host_target;
3699        let build_compiler = builder.compiler(0, bootstrap_host);
3700        let cargo = tool::prepare_tool_cargo(
3701            builder,
3702            build_compiler,
3703            Mode::ToolBootstrap,
3704            bootstrap_host,
3705            Kind::Test,
3706            "src/tools/rust-installer",
3707            SourceType::InTree,
3708            &[],
3709        );
3710
3711        let _guard = builder.msg_test("rust-installer", bootstrap_host, 1);
3712        run_cargo_test(cargo, &[], &[], None, bootstrap_host, builder);
3713
3714        // We currently don't support running the test.sh script outside linux(?) environments.
3715        // Eventually this should likely migrate to #[test]s in rust-installer proper rather than a
3716        // set of scripts, which will likely allow dropping this if.
3717        if bootstrap_host != "x86_64-unknown-linux-gnu" {
3718            return;
3719        }
3720
3721        let mut cmd = command(builder.src.join("src/tools/rust-installer/test.sh"));
3722        let tmpdir = testdir(builder, build_compiler.host).join("rust-installer");
3723        let _ = std::fs::remove_dir_all(&tmpdir);
3724        let _ = std::fs::create_dir_all(&tmpdir);
3725        cmd.current_dir(&tmpdir);
3726        cmd.env("CARGO_TARGET_DIR", tmpdir.join("cargo-target"));
3727        cmd.env("CARGO", &builder.initial_cargo);
3728        cmd.env("RUSTC", &builder.initial_rustc);
3729        cmd.env("TMP_DIR", &tmpdir);
3730        cmd.delay_failure().run(builder);
3731    }
3732}
3733
3734#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3735pub struct TestHelpers {
3736    pub target: TargetSelection,
3737}
3738
3739impl Step for TestHelpers {
3740    type Output = ();
3741
3742    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
3743        run.path("tests/auxiliary/rust_test_helpers.c")
3744    }
3745
3746    fn make_run(run: RunConfig<'_>) {
3747        run.builder.ensure(TestHelpers { target: run.target })
3748    }
3749
3750    /// Compiles the `rust_test_helpers.c` library which we used in various
3751    /// `run-pass` tests for ABI testing.
3752    fn run(self, builder: &Builder<'_>) {
3753        if builder.config.dry_run() {
3754            return;
3755        }
3756        // The x86_64-fortanix-unknown-sgx target doesn't have a working C
3757        // toolchain. However, some x86_64 ELF objects can be linked
3758        // without issues. Use this hack to compile the test helpers.
3759        let target = if self.target == "x86_64-fortanix-unknown-sgx" {
3760            TargetSelection::from_user("x86_64-unknown-linux-gnu")
3761        } else {
3762            self.target
3763        };
3764        let dst = builder.test_helpers_out(target);
3765        let src = builder.src.join("tests/auxiliary/rust_test_helpers.c");
3766        if up_to_date(&src, &dst.join("librust_test_helpers.a")) {
3767            return;
3768        }
3769
3770        let _guard = builder.msg_unstaged(Kind::Build, "test helpers", target);
3771        t!(fs::create_dir_all(&dst));
3772        let mut cfg = cc::Build::new();
3773
3774        // We may have found various cross-compilers a little differently due to our
3775        // extra configuration, so inform cc of these compilers. Note, though, that
3776        // on MSVC we still need cc's detection of env vars (ugh).
3777        if !target.is_msvc() {
3778            if let Some(ar) = builder.ar(target) {
3779                cfg.archiver(ar);
3780            }
3781            cfg.compiler(builder.cc(target));
3782        }
3783        cfg.cargo_metadata(false)
3784            .out_dir(&dst)
3785            .target(&target.triple)
3786            .host(&builder.config.host_target.triple)
3787            .opt_level(0)
3788            .warnings(false)
3789            .debug(false)
3790            .file(builder.src.join("tests/auxiliary/rust_test_helpers.c"))
3791            .compile("rust_test_helpers");
3792    }
3793}
3794
3795#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3796pub struct CodegenCranelift {
3797    compilers: RustcPrivateCompilers,
3798    target: TargetSelection,
3799}
3800
3801impl Step for CodegenCranelift {
3802    type Output = ();
3803    const IS_HOST: bool = true;
3804
3805    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
3806        run.paths(&["compiler/rustc_codegen_cranelift"])
3807    }
3808
3809    fn is_default_step(_builder: &Builder<'_>) -> bool {
3810        true
3811    }
3812
3813    fn make_run(run: RunConfig<'_>) {
3814        let builder = run.builder;
3815        let host = run.build_triple();
3816        let compilers = RustcPrivateCompilers::new(run.builder, run.builder.top_stage, host);
3817
3818        if builder.doc_tests == DocTests::Only {
3819            return;
3820        }
3821
3822        if builder.download_rustc() {
3823            builder.info("CI rustc uses the default codegen backend. skipping");
3824            return;
3825        }
3826
3827        if !target_supports_cranelift_backend(run.target) {
3828            builder.info("target not supported by rustc_codegen_cranelift. skipping");
3829            return;
3830        }
3831
3832        if builder.remote_tested(run.target) {
3833            builder.info("remote testing is not supported by rustc_codegen_cranelift. skipping");
3834            return;
3835        }
3836
3837        if !builder
3838            .config
3839            .enabled_codegen_backends(run.target)
3840            .contains(&CodegenBackendKind::Cranelift)
3841        {
3842            builder.info("cranelift not in rust.codegen-backends. skipping");
3843            return;
3844        }
3845
3846        builder.ensure(CodegenCranelift { compilers, target: run.target });
3847    }
3848
3849    fn run(self, builder: &Builder<'_>) {
3850        let compilers = self.compilers;
3851        let build_compiler = compilers.build_compiler();
3852
3853        // We need to run the cranelift tests with the compiler against cranelift links to, not with
3854        // the build compiler.
3855        let target_compiler = compilers.target_compiler();
3856        let target = self.target;
3857
3858        builder.std(target_compiler, target);
3859
3860        let mut cargo = builder::Cargo::new(
3861            builder,
3862            target_compiler,
3863            Mode::Codegen, // Must be codegen to ensure dlopen on compiled dylibs works
3864            SourceType::InTree,
3865            target,
3866            Kind::Run,
3867        );
3868
3869        cargo.current_dir(&builder.src.join("compiler/rustc_codegen_cranelift"));
3870        cargo
3871            .arg("--manifest-path")
3872            .arg(builder.src.join("compiler/rustc_codegen_cranelift/build_system/Cargo.toml"));
3873        compile::rustc_cargo_env(builder, &mut cargo, target);
3874
3875        // Avoid incremental cache issues when changing rustc
3876        cargo.env("CARGO_BUILD_INCREMENTAL", "false");
3877
3878        let _guard = builder.msg_test(
3879            "rustc_codegen_cranelift",
3880            target_compiler.host,
3881            target_compiler.stage,
3882        );
3883
3884        // FIXME handle vendoring for source tarballs before removing the --skip-test below
3885        let download_dir = builder.out.join("cg_clif_download");
3886
3887        cargo
3888            .arg("--")
3889            .arg("test")
3890            .arg("--download-dir")
3891            .arg(&download_dir)
3892            .arg("--out-dir")
3893            .arg(builder.stage_out(build_compiler, Mode::Codegen).join("cg_clif"))
3894            .arg("--no-unstable-features")
3895            .arg("--use-backend")
3896            .arg("cranelift")
3897            // Avoid having to vendor the standard library dependencies
3898            .arg("--sysroot")
3899            .arg("llvm")
3900            // These tests depend on crates that are not yet vendored
3901            // FIXME remove once vendoring is handled
3902            .arg("--skip-test")
3903            .arg("testsuite.extended_sysroot");
3904
3905        cargo.into_cmd().run(builder);
3906    }
3907
3908    fn metadata(&self) -> Option<StepMetadata> {
3909        Some(
3910            StepMetadata::test("rustc_codegen_cranelift", self.target)
3911                .built_by(self.compilers.build_compiler()),
3912        )
3913    }
3914}
3915
3916#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3917pub struct CodegenGCC {
3918    compilers: RustcPrivateCompilers,
3919    target: TargetSelection,
3920}
3921
3922impl Step for CodegenGCC {
3923    type Output = ();
3924    const IS_HOST: bool = true;
3925
3926    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
3927        run.paths(&["compiler/rustc_codegen_gcc"])
3928    }
3929
3930    fn is_default_step(_builder: &Builder<'_>) -> bool {
3931        true
3932    }
3933
3934    fn make_run(run: RunConfig<'_>) {
3935        let builder = run.builder;
3936        let host = run.build_triple();
3937        let compilers = RustcPrivateCompilers::new(run.builder, run.builder.top_stage, host);
3938
3939        if builder.doc_tests == DocTests::Only {
3940            return;
3941        }
3942
3943        if builder.download_rustc() {
3944            builder.info("CI rustc uses the default codegen backend. skipping");
3945            return;
3946        }
3947
3948        let triple = run.target.triple;
3949        let target_supported =
3950            if triple.contains("linux") { triple.contains("x86_64") } else { false };
3951        if !target_supported {
3952            builder.info("target not supported by rustc_codegen_gcc. skipping");
3953            return;
3954        }
3955
3956        if builder.remote_tested(run.target) {
3957            builder.info("remote testing is not supported by rustc_codegen_gcc. skipping");
3958            return;
3959        }
3960
3961        if !builder.config.enabled_codegen_backends(run.target).contains(&CodegenBackendKind::Gcc) {
3962            builder.info("gcc not in rust.codegen-backends. skipping");
3963            return;
3964        }
3965
3966        builder.ensure(CodegenGCC { compilers, target: run.target });
3967    }
3968
3969    fn run(self, builder: &Builder<'_>) {
3970        let compilers = self.compilers;
3971        let target = self.target;
3972
3973        let gcc = builder.ensure(Gcc { target_pair: GccTargetPair::for_native_build(target) });
3974
3975        builder.ensure(
3976            compile::Std::new(compilers.build_compiler(), target)
3977                .extra_rust_args(&["-Csymbol-mangling-version=v0", "-Cpanic=abort"]),
3978        );
3979
3980        let _guard = builder.msg_test(
3981            "rustc_codegen_gcc",
3982            compilers.target(),
3983            compilers.target_compiler().stage,
3984        );
3985
3986        let mut cargo = builder::Cargo::new(
3987            builder,
3988            compilers.build_compiler(),
3989            Mode::Codegen, // Must be codegen to ensure dlopen on compiled dylibs works
3990            SourceType::InTree,
3991            target,
3992            Kind::Run,
3993        );
3994
3995        cargo.current_dir(&builder.src.join("compiler/rustc_codegen_gcc"));
3996        cargo
3997            .arg("--manifest-path")
3998            .arg(builder.src.join("compiler/rustc_codegen_gcc/build_system/Cargo.toml"));
3999        compile::rustc_cargo_env(builder, &mut cargo, target);
4000        add_cg_gcc_cargo_flags(&mut cargo, &gcc);
4001
4002        // Avoid incremental cache issues when changing rustc
4003        cargo.env("CARGO_BUILD_INCREMENTAL", "false");
4004        cargo.rustflag("-Cpanic=abort");
4005
4006        cargo
4007            // cg_gcc's build system ignores RUSTFLAGS. pass some flags through CG_RUSTFLAGS instead.
4008            .env("CG_RUSTFLAGS", "-Alinker-messages")
4009            .arg("--")
4010            .arg("test")
4011            .arg("--use-backend")
4012            .arg("gcc")
4013            .arg("--gcc-path")
4014            .arg(gcc.libgccjit().parent().unwrap())
4015            .arg("--out-dir")
4016            .arg(builder.stage_out(compilers.build_compiler(), Mode::Codegen).join("cg_gcc"))
4017            .arg("--release")
4018            .arg("--mini-tests")
4019            .arg("--std-tests");
4020
4021        cargo.args(builder.config.test_args());
4022
4023        cargo.into_cmd().run(builder);
4024    }
4025
4026    fn metadata(&self) -> Option<StepMetadata> {
4027        Some(
4028            StepMetadata::test("rustc_codegen_gcc", self.target)
4029                .built_by(self.compilers.build_compiler()),
4030        )
4031    }
4032}
4033
4034/// Test step that does two things:
4035/// - Runs `cargo test` for the `src/tools/test-float-parse` tool.
4036/// - Invokes the `test-float-parse` tool to test the standard library's
4037///   float parsing routines.
4038#[derive(Debug, Clone, PartialEq, Eq, Hash)]
4039pub struct TestFloatParse {
4040    /// The build compiler which will build and run unit tests of `test-float-parse`, and which will
4041    /// build the `test-float-parse` tool itself.
4042    ///
4043    /// Note that the staging is a bit funny here, because this step essentially tests std, but it
4044    /// also needs to build the tool. So if we test stage1 std, we build:
4045    /// 1) stage1 rustc
4046    /// 2) Use that to build stage1 libstd
4047    /// 3) Use that to build and run *stage2* test-float-parse
4048    build_compiler: Compiler,
4049    /// Target for which we build std and test that std.
4050    target: TargetSelection,
4051}
4052
4053impl Step for TestFloatParse {
4054    type Output = ();
4055    const IS_HOST: bool = true;
4056
4057    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
4058        run.path("src/tools/test-float-parse")
4059    }
4060
4061    fn is_default_step(_builder: &Builder<'_>) -> bool {
4062        true
4063    }
4064
4065    fn make_run(run: RunConfig<'_>) {
4066        run.builder.ensure(Self {
4067            build_compiler: get_compiler_to_test(run.builder, run.target),
4068            target: run.target,
4069        });
4070    }
4071
4072    fn run(self, builder: &Builder<'_>) {
4073        let build_compiler = self.build_compiler;
4074        let target = self.target;
4075
4076        // Build the standard library that will be tested, and a stdlib for host code
4077        builder.std(build_compiler, target);
4078        builder.std(build_compiler, builder.host_target);
4079
4080        // Run any unit tests in the crate
4081        let mut cargo_test = tool::prepare_tool_cargo(
4082            builder,
4083            build_compiler,
4084            Mode::ToolStd,
4085            target,
4086            Kind::Test,
4087            "src/tools/test-float-parse",
4088            SourceType::InTree,
4089            &[],
4090        );
4091        cargo_test.allow_features(TEST_FLOAT_PARSE_ALLOW_FEATURES);
4092
4093        run_cargo_test(cargo_test, &[], &[], "test-float-parse", target, builder);
4094
4095        // Run the actual parse tests.
4096        let mut cargo_run = tool::prepare_tool_cargo(
4097            builder,
4098            build_compiler,
4099            Mode::ToolStd,
4100            target,
4101            Kind::Run,
4102            "src/tools/test-float-parse",
4103            SourceType::InTree,
4104            &[],
4105        );
4106        cargo_run.allow_features(TEST_FLOAT_PARSE_ALLOW_FEATURES);
4107
4108        if !matches!(env::var("FLOAT_PARSE_TESTS_NO_SKIP_HUGE").as_deref(), Ok("1") | Ok("true")) {
4109            cargo_run.args(["--", "--skip-huge"]);
4110        }
4111
4112        cargo_run.into_cmd().run(builder);
4113    }
4114}
4115
4116/// Runs the tool `src/tools/collect-license-metadata` in `ONLY_CHECK=1` mode,
4117/// which verifies that `license-metadata.json` is up-to-date and therefore
4118/// running the tool normally would not update anything.
4119#[derive(Debug, Clone, Hash, PartialEq, Eq)]
4120pub struct CollectLicenseMetadata;
4121
4122impl Step for CollectLicenseMetadata {
4123    type Output = PathBuf;
4124    const IS_HOST: bool = true;
4125
4126    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
4127        run.path("src/tools/collect-license-metadata")
4128    }
4129
4130    fn make_run(run: RunConfig<'_>) {
4131        run.builder.ensure(CollectLicenseMetadata);
4132    }
4133
4134    fn run(self, builder: &Builder<'_>) -> Self::Output {
4135        let Some(reuse) = &builder.config.reuse else {
4136            panic!("REUSE is required to collect the license metadata");
4137        };
4138
4139        let dest = builder.src.join("license-metadata.json");
4140
4141        let mut cmd = builder.tool_cmd(Tool::CollectLicenseMetadata);
4142        cmd.env("REUSE_EXE", reuse);
4143        cmd.env("DEST", &dest);
4144        cmd.env("ONLY_CHECK", "1");
4145        cmd.run(builder);
4146
4147        dest
4148    }
4149}
4150
4151#[derive(Debug, Clone, PartialEq, Eq, Hash)]
4152pub struct RemoteTestClientTests {
4153    host: TargetSelection,
4154}
4155
4156impl Step for RemoteTestClientTests {
4157    type Output = ();
4158    const IS_HOST: bool = true;
4159
4160    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
4161        run.path("src/tools/remote-test-client")
4162    }
4163
4164    fn is_default_step(_builder: &Builder<'_>) -> bool {
4165        true
4166    }
4167
4168    fn make_run(run: RunConfig<'_>) {
4169        run.builder.ensure(Self { host: run.target });
4170    }
4171
4172    fn run(self, builder: &Builder<'_>) {
4173        let bootstrap_host = builder.config.host_target;
4174        let compiler = builder.compiler(0, bootstrap_host);
4175
4176        let cargo = tool::prepare_tool_cargo(
4177            builder,
4178            compiler,
4179            Mode::ToolBootstrap,
4180            bootstrap_host,
4181            Kind::Test,
4182            "src/tools/remote-test-client",
4183            SourceType::InTree,
4184            &[],
4185        );
4186
4187        run_cargo_test(cargo, &[], &[], "remote-test-client", bootstrap_host, builder);
4188    }
4189}