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