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