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