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