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