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