Skip to main content

bootstrap/core/build_steps/
test.rs

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