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