Skip to main content

bootstrap/core/build_steps/
test.rs

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