Skip to main content

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