Skip to main content

bootstrap/core/build_steps/
test.rs

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