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