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