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