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