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 { host_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, &host_llvm_config);
1983                let llvm_components = command(&host_llvm_config)
1984                    .arg("--components")
1985                    .run_capture_stdout(builder)
1986                    .stdout();
1987                // Remove trailing newline from llvm-config output.
1988                cmd.arg("--llvm-version")
1989                    .arg(llvm_version.trim())
1990                    .arg("--llvm-components")
1991                    .arg(llvm_components.trim());
1992                llvm_components_passed = true;
1993            }
1994            if !builder.config.is_rust_llvm(target) {
1995                cmd.arg("--system-llvm");
1996            }
1997
1998            // Tests that use compiler libraries may inherit the `-lLLVM` link
1999            // requirement, but the `-L` library path is not propagated across
2000            // separate compilations. We can add LLVM's library path to the
2001            // rustc args as a workaround.
2002            if !builder.config.dry_run() && suite.ends_with("fulldeps") {
2003                let llvm_libdir =
2004                    command(&host_llvm_config).arg("--libdir").run_capture_stdout(builder).stdout();
2005                let link_llvm = if target.is_msvc() {
2006                    format!("-Clink-arg=-LIBPATH:{llvm_libdir}")
2007                } else {
2008                    format!("-Clink-arg=-L{llvm_libdir}")
2009                };
2010                cmd.arg("--host-rustcflags").arg(link_llvm);
2011            }
2012
2013            if !builder.config.dry_run() && matches!(mode, "run-make" | "coverage-run") {
2014                // The llvm/bin directory contains many useful cross-platform
2015                // tools. Pass the path to run-make tests so they can use them.
2016                // (The coverage-run tests also need these tools to process
2017                // coverage reports.)
2018                let llvm_bin_path = host_llvm_config
2019                    .parent()
2020                    .expect("Expected llvm-config to be contained in directory");
2021                assert!(llvm_bin_path.is_dir());
2022                cmd.arg("--llvm-bin-dir").arg(llvm_bin_path);
2023            }
2024
2025            if !builder.config.dry_run() && mode == "run-make" {
2026                // If LLD is available, add it to the PATH
2027                if builder.config.lld_enabled {
2028                    let lld_install_root =
2029                        builder.ensure(llvm::Lld { target: builder.config.host_target });
2030
2031                    let lld_bin_path = lld_install_root.join("bin");
2032
2033                    let old_path = env::var_os("PATH").unwrap_or_default();
2034                    let new_path = env::join_paths(
2035                        std::iter::once(lld_bin_path).chain(env::split_paths(&old_path)),
2036                    )
2037                    .expect("Could not add LLD bin path to PATH");
2038                    cmd.env("PATH", new_path);
2039                }
2040            }
2041        }
2042
2043        // Only pass correct values for these flags for the `run-make` suite as it
2044        // requires that a C++ compiler was configured which isn't always the case.
2045        if !builder.config.dry_run() && mode == "run-make" {
2046            let mut cflags = builder.cc_handled_clags(target, CLang::C);
2047            cflags.extend(builder.cc_unhandled_cflags(target, GitRepo::Rustc, CLang::C));
2048            let mut cxxflags = builder.cc_handled_clags(target, CLang::Cxx);
2049            cxxflags.extend(builder.cc_unhandled_cflags(target, GitRepo::Rustc, CLang::Cxx));
2050            cmd.arg("--cc")
2051                .arg(builder.cc(target))
2052                .arg("--cxx")
2053                .arg(builder.cxx(target).unwrap())
2054                .arg("--cflags")
2055                .arg(cflags.join(" "))
2056                .arg("--cxxflags")
2057                .arg(cxxflags.join(" "));
2058            copts_passed = true;
2059            if let Some(ar) = builder.ar(target) {
2060                cmd.arg("--ar").arg(ar);
2061            }
2062        }
2063
2064        if !llvm_components_passed {
2065            cmd.arg("--llvm-components").arg("");
2066        }
2067        if !copts_passed {
2068            cmd.arg("--cc")
2069                .arg("")
2070                .arg("--cxx")
2071                .arg("")
2072                .arg("--cflags")
2073                .arg("")
2074                .arg("--cxxflags")
2075                .arg("");
2076        }
2077
2078        if builder.remote_tested(target) {
2079            cmd.arg("--remote-test-client").arg(builder.tool_exe(Tool::RemoteTestClient));
2080        } else if let Some(tool) = builder.runner(target) {
2081            cmd.arg("--runner").arg(tool);
2082        }
2083
2084        if suite != "mir-opt" {
2085            // Running a C compiler on MSVC requires a few env vars to be set, to be
2086            // sure to set them here.
2087            //
2088            // Note that if we encounter `PATH` we make sure to append to our own `PATH`
2089            // rather than stomp over it.
2090            if !builder.config.dry_run() && target.is_msvc() {
2091                for (k, v) in builder.cc[&target].env() {
2092                    if k != "PATH" {
2093                        cmd.env(k, v);
2094                    }
2095                }
2096            }
2097        }
2098
2099        // Special setup to enable running with sanitizers on MSVC.
2100        if !builder.config.dry_run()
2101            && target.contains("msvc")
2102            && builder.config.sanitizers_enabled(target)
2103        {
2104            // Ignore interception failures: not all dlls in the process will have been built with
2105            // address sanitizer enabled (e.g., ntdll.dll).
2106            cmd.env("ASAN_WIN_CONTINUE_ON_INTERCEPTION_FAILURE", "1");
2107            // Add the address sanitizer runtime to the PATH - it is located next to cl.exe.
2108            let asan_runtime_path = builder.cc[&target].path().parent().unwrap().to_path_buf();
2109            let old_path = cmd
2110                .get_envs()
2111                .find_map(|(k, v)| (k == "PATH").then_some(v))
2112                .flatten()
2113                .map_or_else(|| env::var_os("PATH").unwrap_or_default(), |v| v.to_owned());
2114            let new_path = env::join_paths(
2115                env::split_paths(&old_path).chain(std::iter::once(asan_runtime_path)),
2116            )
2117            .expect("Could not add ASAN runtime path to PATH");
2118            cmd.env("PATH", new_path);
2119        }
2120
2121        // Some UI tests trigger behavior in rustc where it reads $CARGO and changes behavior if it exists.
2122        // To make the tests work that rely on it not being set, make sure it is not set.
2123        cmd.env_remove("CARGO");
2124
2125        cmd.env("RUSTC_BOOTSTRAP", "1");
2126        // Override the rustc version used in symbol hashes to reduce the amount of normalization
2127        // needed when diffing test output.
2128        cmd.env("RUSTC_FORCE_RUSTC_VERSION", "compiletest");
2129        cmd.env("DOC_RUST_LANG_ORG_CHANNEL", builder.doc_rust_lang_org_channel());
2130        builder.add_rust_test_threads(&mut cmd);
2131
2132        if builder.config.sanitizers_enabled(target) {
2133            cmd.env("RUSTC_SANITIZER_SUPPORT", "1");
2134        }
2135
2136        if builder.config.profiler_enabled(target) {
2137            cmd.arg("--profiler-runtime");
2138        }
2139
2140        cmd.env("RUST_TEST_TMPDIR", builder.tempdir());
2141
2142        cmd.arg("--adb-path").arg("adb");
2143        cmd.arg("--adb-test-dir").arg(ADB_TEST_DIR);
2144        if target.contains("android") && !builder.config.dry_run() {
2145            // Assume that cc for this target comes from the android sysroot
2146            cmd.arg("--android-cross-path")
2147                .arg(builder.cc(target).parent().unwrap().parent().unwrap());
2148        } else {
2149            cmd.arg("--android-cross-path").arg("");
2150        }
2151
2152        if builder.config.cmd.rustfix_coverage() {
2153            cmd.arg("--rustfix-coverage");
2154        }
2155
2156        cmd.arg("--channel").arg(&builder.config.channel);
2157
2158        if !builder.config.omit_git_hash {
2159            cmd.arg("--git-hash");
2160        }
2161
2162        let git_config = builder.config.git_config();
2163        cmd.arg("--nightly-branch").arg(git_config.nightly_branch);
2164        cmd.arg("--git-merge-commit-email").arg(git_config.git_merge_commit_email);
2165        cmd.force_coloring_in_ci();
2166
2167        #[cfg(feature = "build-metrics")]
2168        builder.metrics.begin_test_suite(
2169            build_helper::metrics::TestSuiteMetadata::Compiletest {
2170                suite: suite.into(),
2171                mode: mode.into(),
2172                compare_mode: None,
2173                target: self.target.triple.to_string(),
2174                host: self.compiler.host.triple.to_string(),
2175                stage: self.compiler.stage,
2176            },
2177            builder,
2178        );
2179
2180        let _group = builder.msg(
2181            Kind::Test,
2182            compiler.stage,
2183            format!("compiletest suite={suite} mode={mode}"),
2184            compiler.host,
2185            target,
2186        );
2187        try_run_tests(builder, &mut cmd, false);
2188
2189        if let Some(compare_mode) = compare_mode {
2190            cmd.arg("--compare-mode").arg(compare_mode);
2191
2192            #[cfg(feature = "build-metrics")]
2193            builder.metrics.begin_test_suite(
2194                build_helper::metrics::TestSuiteMetadata::Compiletest {
2195                    suite: suite.into(),
2196                    mode: mode.into(),
2197                    compare_mode: Some(compare_mode.into()),
2198                    target: self.target.triple.to_string(),
2199                    host: self.compiler.host.triple.to_string(),
2200                    stage: self.compiler.stage,
2201                },
2202                builder,
2203            );
2204
2205            builder.info(&format!(
2206                "Check compiletest suite={} mode={} compare_mode={} ({} -> {})",
2207                suite, mode, compare_mode, &compiler.host, target
2208            ));
2209            let _time = helpers::timeit(builder);
2210            try_run_tests(builder, &mut cmd, false);
2211        }
2212    }
2213}
2214
2215#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2216struct BookTest {
2217    compiler: Compiler,
2218    path: PathBuf,
2219    name: &'static str,
2220    is_ext_doc: bool,
2221    dependencies: Vec<&'static str>,
2222}
2223
2224impl Step for BookTest {
2225    type Output = ();
2226    const ONLY_HOSTS: bool = true;
2227
2228    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
2229        run.never()
2230    }
2231
2232    /// Runs the documentation tests for a book in `src/doc`.
2233    ///
2234    /// This uses the `rustdoc` that sits next to `compiler`.
2235    fn run(self, builder: &Builder<'_>) {
2236        // External docs are different from local because:
2237        // - Some books need pre-processing by mdbook before being tested.
2238        // - They need to save their state to toolstate.
2239        // - They are only tested on the "checktools" builders.
2240        //
2241        // The local docs are tested by default, and we don't want to pay the
2242        // cost of building mdbook, so they use `rustdoc --test` directly.
2243        // Also, the unstable book is special because SUMMARY.md is generated,
2244        // so it is easier to just run `rustdoc` on its files.
2245        if self.is_ext_doc {
2246            self.run_ext_doc(builder);
2247        } else {
2248            self.run_local_doc(builder);
2249        }
2250    }
2251}
2252
2253impl BookTest {
2254    /// This runs the equivalent of `mdbook test` (via the rustbook wrapper)
2255    /// which in turn runs `rustdoc --test` on each file in the book.
2256    fn run_ext_doc(self, builder: &Builder<'_>) {
2257        let compiler = self.compiler;
2258
2259        builder.std(compiler, compiler.host);
2260
2261        // mdbook just executes a binary named "rustdoc", so we need to update
2262        // PATH so that it points to our rustdoc.
2263        let mut rustdoc_path = builder.rustdoc(compiler);
2264        rustdoc_path.pop();
2265        let old_path = env::var_os("PATH").unwrap_or_default();
2266        let new_path = env::join_paths(iter::once(rustdoc_path).chain(env::split_paths(&old_path)))
2267            .expect("could not add rustdoc to PATH");
2268
2269        let mut rustbook_cmd = builder.tool_cmd(Tool::Rustbook);
2270        let path = builder.src.join(&self.path);
2271        // Books often have feature-gated example text.
2272        rustbook_cmd.env("RUSTC_BOOTSTRAP", "1");
2273        rustbook_cmd.env("PATH", new_path).arg("test").arg(path);
2274
2275        // Books may also need to build dependencies. For example, `TheBook` has
2276        // code samples which use the `trpl` crate. For the `rustdoc` invocation
2277        // to find them them successfully, they need to be built first and their
2278        // paths used to generate the
2279        let libs = if !self.dependencies.is_empty() {
2280            let mut lib_paths = vec![];
2281            for dep in self.dependencies {
2282                let mode = Mode::ToolRustc;
2283                let target = builder.config.host_target;
2284                let cargo = tool::prepare_tool_cargo(
2285                    builder,
2286                    compiler,
2287                    mode,
2288                    target,
2289                    Kind::Build,
2290                    dep,
2291                    SourceType::Submodule,
2292                    &[],
2293                );
2294
2295                let stamp = BuildStamp::new(&builder.cargo_out(compiler, mode, target))
2296                    .with_prefix(PathBuf::from(dep).file_name().and_then(|v| v.to_str()).unwrap());
2297
2298                let output_paths = run_cargo(builder, cargo, vec![], &stamp, vec![], false, false);
2299                let directories = output_paths
2300                    .into_iter()
2301                    .filter_map(|p| p.parent().map(ToOwned::to_owned))
2302                    .fold(HashSet::new(), |mut set, dir| {
2303                        set.insert(dir);
2304                        set
2305                    });
2306
2307                lib_paths.extend(directories);
2308            }
2309            lib_paths
2310        } else {
2311            vec![]
2312        };
2313
2314        if !libs.is_empty() {
2315            let paths = libs
2316                .into_iter()
2317                .map(|path| path.into_os_string())
2318                .collect::<Vec<OsString>>()
2319                .join(OsStr::new(","));
2320            rustbook_cmd.args([OsString::from("--library-path"), paths]);
2321        }
2322
2323        builder.add_rust_test_threads(&mut rustbook_cmd);
2324        let _guard = builder.msg(
2325            Kind::Test,
2326            compiler.stage,
2327            format_args!("mdbook {}", self.path.display()),
2328            compiler.host,
2329            compiler.host,
2330        );
2331        let _time = helpers::timeit(builder);
2332        let toolstate = if rustbook_cmd.delay_failure().run(builder) {
2333            ToolState::TestPass
2334        } else {
2335            ToolState::TestFail
2336        };
2337        builder.save_toolstate(self.name, toolstate);
2338    }
2339
2340    /// This runs `rustdoc --test` on all `.md` files in the path.
2341    fn run_local_doc(self, builder: &Builder<'_>) {
2342        let compiler = self.compiler;
2343        let host = self.compiler.host;
2344
2345        builder.std(compiler, host);
2346
2347        let _guard =
2348            builder.msg(Kind::Test, compiler.stage, format!("book {}", self.name), host, host);
2349
2350        // Do a breadth-first traversal of the `src/doc` directory and just run
2351        // tests for all files that end in `*.md`
2352        let mut stack = vec![builder.src.join(self.path)];
2353        let _time = helpers::timeit(builder);
2354        let mut files = Vec::new();
2355        while let Some(p) = stack.pop() {
2356            if p.is_dir() {
2357                stack.extend(t!(p.read_dir()).map(|p| t!(p).path()));
2358                continue;
2359            }
2360
2361            if p.extension().and_then(|s| s.to_str()) != Some("md") {
2362                continue;
2363            }
2364
2365            files.push(p);
2366        }
2367
2368        files.sort();
2369
2370        for file in files {
2371            markdown_test(builder, compiler, &file);
2372        }
2373    }
2374}
2375
2376macro_rules! test_book {
2377    ($(
2378        $name:ident, $path:expr, $book_name:expr,
2379        default=$default:expr
2380        $(,submodules = $submodules:expr)?
2381        $(,dependencies=$dependencies:expr)?
2382        ;
2383    )+) => {
2384        $(
2385            #[derive(Debug, Clone, PartialEq, Eq, Hash)]
2386            pub struct $name {
2387                compiler: Compiler,
2388            }
2389
2390            impl Step for $name {
2391                type Output = ();
2392                const DEFAULT: bool = $default;
2393                const ONLY_HOSTS: bool = true;
2394
2395                fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
2396                    run.path($path)
2397                }
2398
2399                fn make_run(run: RunConfig<'_>) {
2400                    run.builder.ensure($name {
2401                        compiler: run.builder.compiler(run.builder.top_stage, run.target),
2402                    });
2403                }
2404
2405                fn run(self, builder: &Builder<'_>) {
2406                    $(
2407                        for submodule in $submodules {
2408                            builder.require_submodule(submodule, None);
2409                        }
2410                    )*
2411
2412                    let dependencies = vec![];
2413                    $(
2414                        let mut dependencies = dependencies;
2415                        for dep in $dependencies {
2416                            dependencies.push(dep);
2417                        }
2418                    )?
2419
2420                    builder.ensure(BookTest {
2421                        compiler: self.compiler,
2422                        path: PathBuf::from($path),
2423                        name: $book_name,
2424                        is_ext_doc: !$default,
2425                        dependencies,
2426                    });
2427                }
2428            }
2429        )+
2430    }
2431}
2432
2433test_book!(
2434    Nomicon, "src/doc/nomicon", "nomicon", default=false, submodules=["src/doc/nomicon"];
2435    Reference, "src/doc/reference", "reference", default=false, submodules=["src/doc/reference"];
2436    RustdocBook, "src/doc/rustdoc", "rustdoc", default=true;
2437    RustcBook, "src/doc/rustc", "rustc", default=true;
2438    RustByExample, "src/doc/rust-by-example", "rust-by-example", default=false, submodules=["src/doc/rust-by-example"];
2439    EmbeddedBook, "src/doc/embedded-book", "embedded-book", default=false, submodules=["src/doc/embedded-book"];
2440    TheBook, "src/doc/book", "book", default=false, submodules=["src/doc/book"], dependencies=["src/doc/book/packages/trpl"];
2441    UnstableBook, "src/doc/unstable-book", "unstable-book", default=true;
2442    EditionGuide, "src/doc/edition-guide", "edition-guide", default=false, submodules=["src/doc/edition-guide"];
2443);
2444
2445#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2446pub struct ErrorIndex {
2447    compiler: Compiler,
2448}
2449
2450impl Step for ErrorIndex {
2451    type Output = ();
2452    const DEFAULT: bool = true;
2453    const ONLY_HOSTS: bool = true;
2454
2455    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
2456        // Also add `error-index` here since that is what appears in the error message
2457        // when this fails.
2458        run.path("src/tools/error_index_generator").alias("error-index")
2459    }
2460
2461    fn make_run(run: RunConfig<'_>) {
2462        // error_index_generator depends on librustdoc. Use the compiler that
2463        // is normally used to build rustdoc for other tests (like compiletest
2464        // tests in tests/rustdoc) so that it shares the same artifacts.
2465        let compiler = run.builder.compiler(run.builder.top_stage, run.builder.config.host_target);
2466        run.builder.ensure(ErrorIndex { compiler });
2467    }
2468
2469    /// Runs the error index generator tool to execute the tests located in the error
2470    /// index.
2471    ///
2472    /// The `error_index_generator` tool lives in `src/tools` and is used to
2473    /// generate a markdown file from the error indexes of the code base which is
2474    /// then passed to `rustdoc --test`.
2475    fn run(self, builder: &Builder<'_>) {
2476        let compiler = self.compiler;
2477
2478        let dir = testdir(builder, compiler.host);
2479        t!(fs::create_dir_all(&dir));
2480        let output = dir.join("error-index.md");
2481
2482        let mut tool = tool::ErrorIndex::command(builder);
2483        tool.arg("markdown").arg(&output);
2484
2485        let guard =
2486            builder.msg(Kind::Test, compiler.stage, "error-index", compiler.host, compiler.host);
2487        let _time = helpers::timeit(builder);
2488        tool.run_capture(builder);
2489        drop(guard);
2490        // The tests themselves need to link to std, so make sure it is
2491        // available.
2492        builder.std(compiler, compiler.host);
2493        markdown_test(builder, compiler, &output);
2494    }
2495}
2496
2497fn markdown_test(builder: &Builder<'_>, compiler: Compiler, markdown: &Path) -> bool {
2498    if let Ok(contents) = fs::read_to_string(markdown)
2499        && !contents.contains("```")
2500    {
2501        return true;
2502    }
2503
2504    builder.verbose(|| println!("doc tests for: {}", markdown.display()));
2505    let mut cmd = builder.rustdoc_cmd(compiler);
2506    builder.add_rust_test_threads(&mut cmd);
2507    // allow for unstable options such as new editions
2508    cmd.arg("-Z");
2509    cmd.arg("unstable-options");
2510    cmd.arg("--test");
2511    cmd.arg(markdown);
2512    cmd.env("RUSTC_BOOTSTRAP", "1");
2513
2514    let test_args = builder.config.test_args().join(" ");
2515    cmd.arg("--test-args").arg(test_args);
2516
2517    cmd = cmd.delay_failure();
2518    if !builder.config.verbose_tests {
2519        cmd.run_capture(builder).is_success()
2520    } else {
2521        cmd.run(builder)
2522    }
2523}
2524
2525/// Runs `cargo test` for the compiler crates in `compiler/`.
2526///
2527/// (This step does not test `rustc_codegen_cranelift` or `rustc_codegen_gcc`,
2528/// which have their own separate test steps.)
2529#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2530pub struct CrateLibrustc {
2531    compiler: Compiler,
2532    target: TargetSelection,
2533    crates: Vec<String>,
2534}
2535
2536impl Step for CrateLibrustc {
2537    type Output = ();
2538    const DEFAULT: bool = true;
2539    const ONLY_HOSTS: bool = true;
2540
2541    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
2542        run.crate_or_deps("rustc-main").path("compiler")
2543    }
2544
2545    fn make_run(run: RunConfig<'_>) {
2546        let builder = run.builder;
2547        let host = run.build_triple();
2548        let compiler = builder.compiler_for(builder.top_stage, host, host);
2549        let crates = run.make_run_crates(Alias::Compiler);
2550
2551        builder.ensure(CrateLibrustc { compiler, target: run.target, crates });
2552    }
2553
2554    fn run(self, builder: &Builder<'_>) {
2555        builder.std(self.compiler, self.target);
2556
2557        // To actually run the tests, delegate to a copy of the `Crate` step.
2558        builder.ensure(Crate {
2559            compiler: self.compiler,
2560            target: self.target,
2561            mode: Mode::Rustc,
2562            crates: self.crates,
2563        });
2564    }
2565
2566    fn metadata(&self) -> Option<StepMetadata> {
2567        Some(StepMetadata::test("CrateLibrustc", self.target))
2568    }
2569}
2570
2571/// Given a `cargo test` subcommand, add the appropriate flags and run it.
2572///
2573/// Returns whether the test succeeded.
2574fn run_cargo_test<'a>(
2575    cargo: builder::Cargo,
2576    libtest_args: &[&str],
2577    crates: &[String],
2578    description: impl Into<Option<&'a str>>,
2579    target: TargetSelection,
2580    builder: &Builder<'_>,
2581) -> bool {
2582    let compiler = cargo.compiler();
2583    let mut cargo = prepare_cargo_test(cargo, libtest_args, crates, target, builder);
2584    let _time = helpers::timeit(builder);
2585    let _group = description.into().and_then(|what| {
2586        builder.msg_sysroot_tool(Kind::Test, compiler.stage, what, compiler.host, target)
2587    });
2588
2589    #[cfg(feature = "build-metrics")]
2590    builder.metrics.begin_test_suite(
2591        build_helper::metrics::TestSuiteMetadata::CargoPackage {
2592            crates: crates.iter().map(|c| c.to_string()).collect(),
2593            target: target.triple.to_string(),
2594            host: compiler.host.triple.to_string(),
2595            stage: compiler.stage,
2596        },
2597        builder,
2598    );
2599    add_flags_and_try_run_tests(builder, &mut cargo)
2600}
2601
2602/// Given a `cargo test` subcommand, pass it the appropriate test flags given a `builder`.
2603fn prepare_cargo_test(
2604    cargo: builder::Cargo,
2605    libtest_args: &[&str],
2606    crates: &[String],
2607    target: TargetSelection,
2608    builder: &Builder<'_>,
2609) -> BootstrapCommand {
2610    let compiler = cargo.compiler();
2611    let mut cargo: BootstrapCommand = cargo.into();
2612
2613    // Propagate `--bless` if it has not already been set/unset
2614    // Any tools that want to use this should bless if `RUSTC_BLESS` is set to
2615    // anything other than `0`.
2616    if builder.config.cmd.bless() && !cargo.get_envs().any(|v| v.0 == "RUSTC_BLESS") {
2617        cargo.env("RUSTC_BLESS", "Gesundheit");
2618    }
2619
2620    // Pass in some standard flags then iterate over the graph we've discovered
2621    // in `cargo metadata` with the maps above and figure out what `-p`
2622    // arguments need to get passed.
2623    if builder.kind == Kind::Test && !builder.fail_fast {
2624        cargo.arg("--no-fail-fast");
2625    }
2626
2627    if builder.config.json_output {
2628        cargo.arg("--message-format=json");
2629    }
2630
2631    match builder.doc_tests {
2632        DocTests::Only => {
2633            cargo.arg("--doc");
2634        }
2635        DocTests::No => {
2636            cargo.args(["--bins", "--examples", "--tests", "--benches"]);
2637        }
2638        DocTests::Yes => {}
2639    }
2640
2641    for krate in crates {
2642        cargo.arg("-p").arg(krate);
2643    }
2644
2645    cargo.arg("--").args(builder.config.test_args()).args(libtest_args);
2646    if !builder.config.verbose_tests {
2647        cargo.arg("--quiet");
2648    }
2649
2650    // The tests are going to run with the *target* libraries, so we need to
2651    // ensure that those libraries show up in the LD_LIBRARY_PATH equivalent.
2652    //
2653    // Note that to run the compiler we need to run with the *host* libraries,
2654    // but our wrapper scripts arrange for that to be the case anyway.
2655    //
2656    // We skip everything on Miri as then this overwrites the libdir set up
2657    // by `Cargo::new` and that actually makes things go wrong.
2658    if builder.kind != Kind::Miri {
2659        let mut dylib_paths = builder.rustc_lib_paths(compiler);
2660        dylib_paths.push(builder.sysroot_target_libdir(compiler, target));
2661        helpers::add_dylib_path(dylib_paths, &mut cargo);
2662    }
2663
2664    if builder.remote_tested(target) {
2665        cargo.env(
2666            format!("CARGO_TARGET_{}_RUNNER", envify(&target.triple)),
2667            format!("{} run 0", builder.tool_exe(Tool::RemoteTestClient).display()),
2668        );
2669    } else if let Some(tool) = builder.runner(target) {
2670        cargo.env(format!("CARGO_TARGET_{}_RUNNER", envify(&target.triple)), tool);
2671    }
2672
2673    cargo
2674}
2675
2676/// Runs `cargo test` for standard library crates.
2677///
2678/// (Also used internally to run `cargo test` for compiler crates.)
2679///
2680/// FIXME(Zalathar): Try to split this into two separate steps: a user-visible
2681/// step for testing standard library crates, and an internal step used for both
2682/// library crates and compiler crates.
2683#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
2684pub struct Crate {
2685    pub compiler: Compiler,
2686    pub target: TargetSelection,
2687    pub mode: Mode,
2688    pub crates: Vec<String>,
2689}
2690
2691impl Step for Crate {
2692    type Output = ();
2693    const DEFAULT: bool = true;
2694
2695    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
2696        run.crate_or_deps("sysroot").crate_or_deps("coretests").crate_or_deps("alloctests")
2697    }
2698
2699    fn make_run(run: RunConfig<'_>) {
2700        let builder = run.builder;
2701        let host = run.build_triple();
2702        let compiler = builder.compiler_for(builder.top_stage, host, host);
2703        let crates = run
2704            .paths
2705            .iter()
2706            .map(|p| builder.crate_paths[&p.assert_single_path().path].clone())
2707            .collect();
2708
2709        builder.ensure(Crate { compiler, target: run.target, mode: Mode::Std, crates });
2710    }
2711
2712    /// Runs all unit tests plus documentation tests for a given crate defined
2713    /// by a `Cargo.toml` (single manifest)
2714    ///
2715    /// This is what runs tests for crates like the standard library, compiler, etc.
2716    /// It essentially is the driver for running `cargo test`.
2717    ///
2718    /// Currently this runs all tests for a DAG by passing a bunch of `-p foo`
2719    /// arguments, and those arguments are discovered from `cargo metadata`.
2720    fn run(self, builder: &Builder<'_>) {
2721        let compiler = self.compiler;
2722        let target = self.target;
2723        let mode = self.mode;
2724
2725        // Prepare sysroot
2726        // See [field@compile::Std::force_recompile].
2727        builder.ensure(Std::new(compiler, compiler.host).force_recompile(true));
2728
2729        // If we're not doing a full bootstrap but we're testing a stage2
2730        // version of libstd, then what we're actually testing is the libstd
2731        // produced in stage1. Reflect that here by updating the compiler that
2732        // we're working with automatically.
2733        let compiler = builder.compiler_for(compiler.stage, compiler.host, target);
2734
2735        let mut cargo = if builder.kind == Kind::Miri {
2736            if builder.top_stage == 0 {
2737                eprintln!("ERROR: `x.py miri` requires stage 1 or higher");
2738                std::process::exit(1);
2739            }
2740
2741            // Build `cargo miri test` command
2742            // (Implicitly prepares target sysroot)
2743            let mut cargo = builder::Cargo::new(
2744                builder,
2745                compiler,
2746                mode,
2747                SourceType::InTree,
2748                target,
2749                Kind::MiriTest,
2750            );
2751            // This hack helps bootstrap run standard library tests in Miri. The issue is as
2752            // follows: when running `cargo miri test` on libcore, cargo builds a local copy of core
2753            // and makes it a dependency of the integration test crate. This copy duplicates all the
2754            // lang items, so the build fails. (Regular testing avoids this because the sysroot is a
2755            // literal copy of what `cargo build` produces, but since Miri builds its own sysroot
2756            // this does not work for us.) So we need to make it so that the locally built libcore
2757            // contains all the items from `core`, but does not re-define them -- we want to replace
2758            // the entire crate but a re-export of the sysroot crate. We do this by swapping out the
2759            // source file: if `MIRI_REPLACE_LIBRS_IF_NOT_TEST` is set and we are building a
2760            // `lib.rs` file, and a `lib.miri.rs` file exists in the same folder, we build that
2761            // instead. But crucially we only do that for the library, not the test builds.
2762            cargo.env("MIRI_REPLACE_LIBRS_IF_NOT_TEST", "1");
2763            // std needs to be built with `-Zforce-unstable-if-unmarked`. For some reason the builder
2764            // does not set this directly, but relies on the rustc wrapper to set it, and we are not using
2765            // the wrapper -- hence we have to set it ourselves.
2766            cargo.rustflag("-Zforce-unstable-if-unmarked");
2767            cargo
2768        } else {
2769            // Also prepare a sysroot for the target.
2770            if !builder.config.is_host_target(target) {
2771                builder.ensure(compile::Std::new(compiler, target).force_recompile(true));
2772                builder.ensure(RemoteCopyLibs { compiler, target });
2773            }
2774
2775            // Build `cargo test` command
2776            builder::Cargo::new(builder, compiler, mode, SourceType::InTree, target, builder.kind)
2777        };
2778
2779        match mode {
2780            Mode::Std => {
2781                if builder.kind == Kind::Miri {
2782                    // We can't use `std_cargo` as that uses `optimized-compiler-builtins` which
2783                    // needs host tools for the given target. This is similar to what `compile::Std`
2784                    // does when `is_for_mir_opt_tests` is true. There's probably a chance for
2785                    // de-duplication here... `std_cargo` should support a mode that avoids needing
2786                    // host tools.
2787                    cargo
2788                        .arg("--manifest-path")
2789                        .arg(builder.src.join("library/sysroot/Cargo.toml"));
2790                } else {
2791                    compile::std_cargo(builder, target, compiler.stage, &mut cargo);
2792                }
2793            }
2794            Mode::Rustc => {
2795                compile::rustc_cargo(builder, &mut cargo, target, &compiler, &self.crates);
2796            }
2797            _ => panic!("can only test libraries"),
2798        };
2799
2800        let mut crates = self.crates.clone();
2801        // The core and alloc crates can't directly be tested. We
2802        // could silently ignore them, but adding their own test
2803        // crates is less confusing for users. We still keep core and
2804        // alloc themself for doctests
2805        if crates.iter().any(|crate_| crate_ == "core") {
2806            crates.push("coretests".to_owned());
2807        }
2808        if crates.iter().any(|crate_| crate_ == "alloc") {
2809            crates.push("alloctests".to_owned());
2810        }
2811
2812        run_cargo_test(cargo, &[], &crates, &*crate_description(&self.crates), target, builder);
2813    }
2814}
2815
2816/// Rustdoc is special in various ways, which is why this step is different from `Crate`.
2817#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2818pub struct CrateRustdoc {
2819    host: TargetSelection,
2820}
2821
2822impl Step for CrateRustdoc {
2823    type Output = ();
2824    const DEFAULT: bool = true;
2825    const ONLY_HOSTS: bool = true;
2826
2827    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
2828        run.paths(&["src/librustdoc", "src/tools/rustdoc"])
2829    }
2830
2831    fn make_run(run: RunConfig<'_>) {
2832        let builder = run.builder;
2833
2834        builder.ensure(CrateRustdoc { host: run.target });
2835    }
2836
2837    fn run(self, builder: &Builder<'_>) {
2838        let target = self.host;
2839
2840        let compiler = if builder.download_rustc() {
2841            builder.compiler(builder.top_stage, target)
2842        } else {
2843            // Use the previous stage compiler to reuse the artifacts that are
2844            // created when running compiletest for tests/rustdoc. If this used
2845            // `compiler`, then it would cause rustdoc to be built *again*, which
2846            // isn't really necessary.
2847            builder.compiler_for(builder.top_stage, target, target)
2848        };
2849        // NOTE: normally `ensure(Rustc)` automatically runs `ensure(Std)` for us. However, when
2850        // using `download-rustc`, the rustc_private artifacts may be in a *different sysroot* from
2851        // the target rustdoc (`ci-rustc-sysroot` vs `stage2`). In that case, we need to ensure this
2852        // explicitly to make sure it ends up in the stage2 sysroot.
2853        builder.std(compiler, target);
2854        builder.ensure(compile::Rustc::new(compiler, target));
2855
2856        let mut cargo = tool::prepare_tool_cargo(
2857            builder,
2858            compiler,
2859            Mode::ToolRustc,
2860            target,
2861            builder.kind,
2862            "src/tools/rustdoc",
2863            SourceType::InTree,
2864            &[],
2865        );
2866        if self.host.contains("musl") {
2867            cargo.arg("'-Ctarget-feature=-crt-static'");
2868        }
2869
2870        // This is needed for running doctests on librustdoc. This is a bit of
2871        // an unfortunate interaction with how bootstrap works and how cargo
2872        // sets up the dylib path, and the fact that the doctest (in
2873        // html/markdown.rs) links to rustc-private libs. For stage1, the
2874        // compiler host dylibs (in stage1/lib) are not the same as the target
2875        // dylibs (in stage1/lib/rustlib/...). This is different from a normal
2876        // rust distribution where they are the same.
2877        //
2878        // On the cargo side, normal tests use `target_process` which handles
2879        // setting up the dylib for a *target* (stage1/lib/rustlib/... in this
2880        // case). However, for doctests it uses `rustdoc_process` which only
2881        // sets up the dylib path for the *host* (stage1/lib), which is the
2882        // wrong directory.
2883        //
2884        // Recall that we special-cased `compiler_for(top_stage)` above, so we always use stage1.
2885        //
2886        // It should be considered to just stop running doctests on
2887        // librustdoc. There is only one test, and it doesn't look too
2888        // important. There might be other ways to avoid this, but it seems
2889        // pretty convoluted.
2890        //
2891        // See also https://github.com/rust-lang/rust/issues/13983 where the
2892        // host vs target dylibs for rustdoc are consistently tricky to deal
2893        // with.
2894        //
2895        // Note that this set the host libdir for `download_rustc`, which uses a normal rust distribution.
2896        let libdir = if builder.download_rustc() {
2897            builder.rustc_libdir(compiler)
2898        } else {
2899            builder.sysroot_target_libdir(compiler, target).to_path_buf()
2900        };
2901        let mut dylib_path = dylib_path();
2902        dylib_path.insert(0, PathBuf::from(&*libdir));
2903        cargo.env(dylib_path_var(), env::join_paths(&dylib_path).unwrap());
2904
2905        run_cargo_test(cargo, &[], &["rustdoc:0.0.0".to_string()], "rustdoc", target, builder);
2906    }
2907}
2908
2909#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2910pub struct CrateRustdocJsonTypes {
2911    host: TargetSelection,
2912}
2913
2914impl Step for CrateRustdocJsonTypes {
2915    type Output = ();
2916    const DEFAULT: bool = true;
2917    const ONLY_HOSTS: bool = true;
2918
2919    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
2920        run.path("src/rustdoc-json-types")
2921    }
2922
2923    fn make_run(run: RunConfig<'_>) {
2924        let builder = run.builder;
2925
2926        builder.ensure(CrateRustdocJsonTypes { host: run.target });
2927    }
2928
2929    fn run(self, builder: &Builder<'_>) {
2930        let target = self.host;
2931
2932        // Use the previous stage compiler to reuse the artifacts that are
2933        // created when running compiletest for tests/rustdoc. If this used
2934        // `compiler`, then it would cause rustdoc to be built *again*, which
2935        // isn't really necessary.
2936        let compiler = builder.compiler_for(builder.top_stage, target, target);
2937        builder.ensure(compile::Rustc::new(compiler, target));
2938
2939        let cargo = tool::prepare_tool_cargo(
2940            builder,
2941            compiler,
2942            Mode::ToolRustc,
2943            target,
2944            builder.kind,
2945            "src/rustdoc-json-types",
2946            SourceType::InTree,
2947            &[],
2948        );
2949
2950        // FIXME: this looks very wrong, libtest doesn't accept `-C` arguments and the quotes are fishy.
2951        let libtest_args = if self.host.contains("musl") {
2952            ["'-Ctarget-feature=-crt-static'"].as_slice()
2953        } else {
2954            &[]
2955        };
2956
2957        run_cargo_test(
2958            cargo,
2959            libtest_args,
2960            &["rustdoc-json-types".to_string()],
2961            "rustdoc-json-types",
2962            target,
2963            builder,
2964        );
2965    }
2966}
2967
2968/// Some test suites are run inside emulators or on remote devices, and most
2969/// of our test binaries are linked dynamically which means we need to ship
2970/// the standard library and such to the emulator ahead of time. This step
2971/// represents this and is a dependency of all test suites.
2972///
2973/// Most of the time this is a no-op. For some steps such as shipping data to
2974/// QEMU we have to build our own tools so we've got conditional dependencies
2975/// on those programs as well. Note that the remote test client is built for
2976/// the build target (us) and the server is built for the target.
2977#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2978pub struct RemoteCopyLibs {
2979    compiler: Compiler,
2980    target: TargetSelection,
2981}
2982
2983impl Step for RemoteCopyLibs {
2984    type Output = ();
2985
2986    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
2987        run.never()
2988    }
2989
2990    fn run(self, builder: &Builder<'_>) {
2991        let compiler = self.compiler;
2992        let target = self.target;
2993        if !builder.remote_tested(target) {
2994            return;
2995        }
2996
2997        builder.std(compiler, target);
2998
2999        builder.info(&format!("REMOTE copy libs to emulator ({target})"));
3000
3001        let remote_test_server =
3002            builder.ensure(tool::RemoteTestServer { build_compiler: compiler, target });
3003
3004        // Spawn the emulator and wait for it to come online
3005        let tool = builder.tool_exe(Tool::RemoteTestClient);
3006        let mut cmd = command(&tool);
3007        cmd.arg("spawn-emulator")
3008            .arg(target.triple)
3009            .arg(&remote_test_server.tool_path)
3010            .arg(builder.tempdir());
3011        if let Some(rootfs) = builder.qemu_rootfs(target) {
3012            cmd.arg(rootfs);
3013        }
3014        cmd.run(builder);
3015
3016        // Push all our dylibs to the emulator
3017        for f in t!(builder.sysroot_target_libdir(compiler, target).read_dir()) {
3018            let f = t!(f);
3019            if helpers::is_dylib(&f.path()) {
3020                command(&tool).arg("push").arg(f.path()).run(builder);
3021            }
3022        }
3023    }
3024}
3025
3026#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3027pub struct Distcheck;
3028
3029impl Step for Distcheck {
3030    type Output = ();
3031
3032    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
3033        run.alias("distcheck")
3034    }
3035
3036    fn make_run(run: RunConfig<'_>) {
3037        run.builder.ensure(Distcheck);
3038    }
3039
3040    /// Runs `distcheck`, which is a collection of smoke tests:
3041    ///
3042    /// - Run `make check` from an unpacked dist tarball to make sure we can at the minimum run
3043    ///   check steps from those sources.
3044    /// - Check that selected dist components (`rust-src` only at the moment) at least have expected
3045    ///   directory shape and crate manifests that cargo can generate a lockfile from.
3046    ///
3047    /// FIXME(#136822): dist components are under-tested.
3048    fn run(self, builder: &Builder<'_>) {
3049        builder.info("Distcheck");
3050        let dir = builder.tempdir().join("distcheck");
3051        let _ = fs::remove_dir_all(&dir);
3052        t!(fs::create_dir_all(&dir));
3053
3054        // Guarantee that these are built before we begin running.
3055        builder.ensure(dist::PlainSourceTarball);
3056        builder.ensure(dist::Src);
3057
3058        command("tar")
3059            .arg("-xf")
3060            .arg(builder.ensure(dist::PlainSourceTarball).tarball())
3061            .arg("--strip-components=1")
3062            .current_dir(&dir)
3063            .run(builder);
3064        command("./configure")
3065            .args(&builder.config.configure_args)
3066            .arg("--enable-vendor")
3067            .current_dir(&dir)
3068            .run(builder);
3069        command(helpers::make(&builder.config.host_target.triple))
3070            .arg("check")
3071            .current_dir(&dir)
3072            .run(builder);
3073
3074        // Now make sure that rust-src has all of libstd's dependencies
3075        builder.info("Distcheck rust-src");
3076        let dir = builder.tempdir().join("distcheck-src");
3077        let _ = fs::remove_dir_all(&dir);
3078        t!(fs::create_dir_all(&dir));
3079
3080        command("tar")
3081            .arg("-xf")
3082            .arg(builder.ensure(dist::Src).tarball())
3083            .arg("--strip-components=1")
3084            .current_dir(&dir)
3085            .run(builder);
3086
3087        let toml = dir.join("rust-src/lib/rustlib/src/rust/library/std/Cargo.toml");
3088        command(&builder.initial_cargo)
3089            // Will read the libstd Cargo.toml
3090            // which uses the unstable `public-dependency` feature.
3091            .env("RUSTC_BOOTSTRAP", "1")
3092            .arg("generate-lockfile")
3093            .arg("--manifest-path")
3094            .arg(&toml)
3095            .current_dir(&dir)
3096            .run(builder);
3097    }
3098}
3099
3100#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3101pub struct Bootstrap;
3102
3103impl Step for Bootstrap {
3104    type Output = ();
3105    const DEFAULT: bool = true;
3106    const ONLY_HOSTS: bool = true;
3107
3108    /// Tests the build system itself.
3109    fn run(self, builder: &Builder<'_>) {
3110        let host = builder.config.host_target;
3111        let compiler = builder.compiler(0, host);
3112        let _guard = builder.msg(Kind::Test, 0, "bootstrap", host, host);
3113
3114        // Some tests require cargo submodule to be present.
3115        builder.build.require_submodule("src/tools/cargo", None);
3116
3117        let mut check_bootstrap = command(builder.python());
3118        check_bootstrap
3119            .args(["-m", "unittest", "bootstrap_test.py"])
3120            .env("BUILD_DIR", &builder.out)
3121            .env("BUILD_PLATFORM", builder.build.host_target.triple)
3122            .env("BOOTSTRAP_TEST_RUSTC_BIN", &builder.initial_rustc)
3123            .env("BOOTSTRAP_TEST_CARGO_BIN", &builder.initial_cargo)
3124            .current_dir(builder.src.join("src/bootstrap/"));
3125        // NOTE: we intentionally don't pass test_args here because the args for unittest and cargo test are mutually incompatible.
3126        // Use `python -m unittest` manually if you want to pass arguments.
3127        check_bootstrap.delay_failure().run(builder);
3128
3129        let mut cargo = tool::prepare_tool_cargo(
3130            builder,
3131            compiler,
3132            Mode::ToolBootstrap,
3133            host,
3134            Kind::Test,
3135            "src/bootstrap",
3136            SourceType::InTree,
3137            &[],
3138        );
3139
3140        cargo.release_build(false);
3141
3142        cargo
3143            .rustflag("-Cdebuginfo=2")
3144            .env("CARGO_TARGET_DIR", builder.out.join("bootstrap"))
3145            // Needed for insta to correctly write pending snapshots to the right directories.
3146            .env("INSTA_WORKSPACE_ROOT", &builder.src)
3147            .env("RUSTC_BOOTSTRAP", "1");
3148
3149        // bootstrap tests are racy on directory creation so just run them one at a time.
3150        // Since there's not many this shouldn't be a problem.
3151        run_cargo_test(cargo, &["--test-threads=1"], &[], None, host, builder);
3152    }
3153
3154    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
3155        // Bootstrap tests might not be perfectly self-contained and can depend on the external
3156        // environment, submodules that are checked out, etc.
3157        // Therefore we only run them by default on CI.
3158        let runs_on_ci = run.builder.config.is_running_on_ci;
3159        run.path("src/bootstrap").default_condition(runs_on_ci)
3160    }
3161
3162    fn make_run(run: RunConfig<'_>) {
3163        run.builder.ensure(Bootstrap);
3164    }
3165}
3166
3167#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3168pub struct TierCheck {
3169    pub compiler: Compiler,
3170}
3171
3172impl Step for TierCheck {
3173    type Output = ();
3174    const DEFAULT: bool = true;
3175    const ONLY_HOSTS: bool = true;
3176
3177    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
3178        run.path("src/tools/tier-check")
3179    }
3180
3181    fn make_run(run: RunConfig<'_>) {
3182        let compiler = run.builder.compiler_for(
3183            run.builder.top_stage,
3184            run.builder.build.host_target,
3185            run.target,
3186        );
3187        run.builder.ensure(TierCheck { compiler });
3188    }
3189
3190    /// Tests the Platform Support page in the rustc book.
3191    fn run(self, builder: &Builder<'_>) {
3192        builder.std(self.compiler, self.compiler.host);
3193        let mut cargo = tool::prepare_tool_cargo(
3194            builder,
3195            self.compiler,
3196            Mode::ToolStd,
3197            self.compiler.host,
3198            Kind::Run,
3199            "src/tools/tier-check",
3200            SourceType::InTree,
3201            &[],
3202        );
3203        cargo.arg(builder.src.join("src/doc/rustc/src/platform-support.md"));
3204        cargo.arg(builder.rustc(self.compiler));
3205        if builder.is_verbose() {
3206            cargo.arg("--verbose");
3207        }
3208
3209        let _guard = builder.msg(
3210            Kind::Test,
3211            self.compiler.stage,
3212            "platform support check",
3213            self.compiler.host,
3214            self.compiler.host,
3215        );
3216        BootstrapCommand::from(cargo).delay_failure().run(builder);
3217    }
3218}
3219
3220#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3221pub struct LintDocs {
3222    pub compiler: Compiler,
3223    pub target: TargetSelection,
3224}
3225
3226impl Step for LintDocs {
3227    type Output = ();
3228    const DEFAULT: bool = true;
3229    const ONLY_HOSTS: bool = true;
3230
3231    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
3232        run.path("src/tools/lint-docs")
3233    }
3234
3235    fn make_run(run: RunConfig<'_>) {
3236        run.builder.ensure(LintDocs {
3237            compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.host_target),
3238            target: run.target,
3239        });
3240    }
3241
3242    /// Tests that the lint examples in the rustc book generate the correct
3243    /// lints and have the expected format.
3244    fn run(self, builder: &Builder<'_>) {
3245        builder.ensure(crate::core::build_steps::doc::RustcBook {
3246            compiler: self.compiler,
3247            target: self.target,
3248            validate: true,
3249        });
3250    }
3251}
3252
3253#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3254pub struct RustInstaller;
3255
3256impl Step for RustInstaller {
3257    type Output = ();
3258    const ONLY_HOSTS: bool = true;
3259    const DEFAULT: bool = true;
3260
3261    /// Ensure the version placeholder replacement tool builds
3262    fn run(self, builder: &Builder<'_>) {
3263        let bootstrap_host = builder.config.host_target;
3264        let compiler = builder.compiler(0, bootstrap_host);
3265        let cargo = tool::prepare_tool_cargo(
3266            builder,
3267            compiler,
3268            Mode::ToolBootstrap,
3269            bootstrap_host,
3270            Kind::Test,
3271            "src/tools/rust-installer",
3272            SourceType::InTree,
3273            &[],
3274        );
3275
3276        let _guard = builder.msg(
3277            Kind::Test,
3278            compiler.stage,
3279            "rust-installer",
3280            bootstrap_host,
3281            bootstrap_host,
3282        );
3283        run_cargo_test(cargo, &[], &[], None, bootstrap_host, builder);
3284
3285        // We currently don't support running the test.sh script outside linux(?) environments.
3286        // Eventually this should likely migrate to #[test]s in rust-installer proper rather than a
3287        // set of scripts, which will likely allow dropping this if.
3288        if bootstrap_host != "x86_64-unknown-linux-gnu" {
3289            return;
3290        }
3291
3292        let mut cmd = command(builder.src.join("src/tools/rust-installer/test.sh"));
3293        let tmpdir = testdir(builder, compiler.host).join("rust-installer");
3294        let _ = std::fs::remove_dir_all(&tmpdir);
3295        let _ = std::fs::create_dir_all(&tmpdir);
3296        cmd.current_dir(&tmpdir);
3297        cmd.env("CARGO_TARGET_DIR", tmpdir.join("cargo-target"));
3298        cmd.env("CARGO", &builder.initial_cargo);
3299        cmd.env("RUSTC", &builder.initial_rustc);
3300        cmd.env("TMP_DIR", &tmpdir);
3301        cmd.delay_failure().run(builder);
3302    }
3303
3304    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
3305        run.path("src/tools/rust-installer")
3306    }
3307
3308    fn make_run(run: RunConfig<'_>) {
3309        run.builder.ensure(Self);
3310    }
3311}
3312
3313#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3314pub struct TestHelpers {
3315    pub target: TargetSelection,
3316}
3317
3318impl Step for TestHelpers {
3319    type Output = ();
3320
3321    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
3322        run.path("tests/auxiliary/rust_test_helpers.c")
3323    }
3324
3325    fn make_run(run: RunConfig<'_>) {
3326        run.builder.ensure(TestHelpers { target: run.target })
3327    }
3328
3329    /// Compiles the `rust_test_helpers.c` library which we used in various
3330    /// `run-pass` tests for ABI testing.
3331    fn run(self, builder: &Builder<'_>) {
3332        if builder.config.dry_run() {
3333            return;
3334        }
3335        // The x86_64-fortanix-unknown-sgx target doesn't have a working C
3336        // toolchain. However, some x86_64 ELF objects can be linked
3337        // without issues. Use this hack to compile the test helpers.
3338        let target = if self.target == "x86_64-fortanix-unknown-sgx" {
3339            TargetSelection::from_user("x86_64-unknown-linux-gnu")
3340        } else {
3341            self.target
3342        };
3343        let dst = builder.test_helpers_out(target);
3344        let src = builder.src.join("tests/auxiliary/rust_test_helpers.c");
3345        if up_to_date(&src, &dst.join("librust_test_helpers.a")) {
3346            return;
3347        }
3348
3349        let _guard = builder.msg_unstaged(Kind::Build, "test helpers", target);
3350        t!(fs::create_dir_all(&dst));
3351        let mut cfg = cc::Build::new();
3352
3353        // We may have found various cross-compilers a little differently due to our
3354        // extra configuration, so inform cc of these compilers. Note, though, that
3355        // on MSVC we still need cc's detection of env vars (ugh).
3356        if !target.is_msvc() {
3357            if let Some(ar) = builder.ar(target) {
3358                cfg.archiver(ar);
3359            }
3360            cfg.compiler(builder.cc(target));
3361        }
3362        cfg.cargo_metadata(false)
3363            .out_dir(&dst)
3364            .target(&target.triple)
3365            .host(&builder.config.host_target.triple)
3366            .opt_level(0)
3367            .warnings(false)
3368            .debug(false)
3369            .file(builder.src.join("tests/auxiliary/rust_test_helpers.c"))
3370            .compile("rust_test_helpers");
3371    }
3372}
3373
3374#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3375pub struct CodegenCranelift {
3376    compiler: Compiler,
3377    target: TargetSelection,
3378}
3379
3380impl Step for CodegenCranelift {
3381    type Output = ();
3382    const DEFAULT: bool = true;
3383    const ONLY_HOSTS: bool = true;
3384
3385    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
3386        run.paths(&["compiler/rustc_codegen_cranelift"])
3387    }
3388
3389    fn make_run(run: RunConfig<'_>) {
3390        let builder = run.builder;
3391        let host = run.build_triple();
3392        let compiler = run.builder.compiler_for(run.builder.top_stage, host, host);
3393
3394        if builder.doc_tests == DocTests::Only {
3395            return;
3396        }
3397
3398        if builder.download_rustc() {
3399            builder.info("CI rustc uses the default codegen backend. skipping");
3400            return;
3401        }
3402
3403        if !target_supports_cranelift_backend(run.target) {
3404            builder.info("target not supported by rustc_codegen_cranelift. skipping");
3405            return;
3406        }
3407
3408        if builder.remote_tested(run.target) {
3409            builder.info("remote testing is not supported by rustc_codegen_cranelift. skipping");
3410            return;
3411        }
3412
3413        if !builder.config.codegen_backends(run.target).contains(&CodegenBackendKind::Cranelift) {
3414            builder.info("cranelift not in rust.codegen-backends. skipping");
3415            return;
3416        }
3417
3418        builder.ensure(CodegenCranelift { compiler, target: run.target });
3419    }
3420
3421    fn run(self, builder: &Builder<'_>) {
3422        let compiler = self.compiler;
3423        let target = self.target;
3424
3425        builder.std(compiler, target);
3426
3427        // If we're not doing a full bootstrap but we're testing a stage2
3428        // version of libstd, then what we're actually testing is the libstd
3429        // produced in stage1. Reflect that here by updating the compiler that
3430        // we're working with automatically.
3431        let compiler = builder.compiler_for(compiler.stage, compiler.host, target);
3432
3433        let build_cargo = || {
3434            let mut cargo = builder::Cargo::new(
3435                builder,
3436                compiler,
3437                Mode::Codegen, // Must be codegen to ensure dlopen on compiled dylibs works
3438                SourceType::InTree,
3439                target,
3440                Kind::Run,
3441            );
3442
3443            cargo.current_dir(&builder.src.join("compiler/rustc_codegen_cranelift"));
3444            cargo
3445                .arg("--manifest-path")
3446                .arg(builder.src.join("compiler/rustc_codegen_cranelift/build_system/Cargo.toml"));
3447            compile::rustc_cargo_env(builder, &mut cargo, target);
3448
3449            // Avoid incremental cache issues when changing rustc
3450            cargo.env("CARGO_BUILD_INCREMENTAL", "false");
3451
3452            cargo
3453        };
3454
3455        builder.info(&format!(
3456            "{} cranelift stage{} ({} -> {})",
3457            Kind::Test.description(),
3458            compiler.stage,
3459            &compiler.host,
3460            target
3461        ));
3462        let _time = helpers::timeit(builder);
3463
3464        // FIXME handle vendoring for source tarballs before removing the --skip-test below
3465        let download_dir = builder.out.join("cg_clif_download");
3466
3467        // FIXME: Uncomment the `prepare` command below once vendoring is implemented.
3468        /*
3469        let mut prepare_cargo = build_cargo();
3470        prepare_cargo.arg("--").arg("prepare").arg("--download-dir").arg(&download_dir);
3471        #[expect(deprecated)]
3472        builder.config.try_run(&mut prepare_cargo.into()).unwrap();
3473        */
3474
3475        let mut cargo = build_cargo();
3476        cargo
3477            .arg("--")
3478            .arg("test")
3479            .arg("--download-dir")
3480            .arg(&download_dir)
3481            .arg("--out-dir")
3482            .arg(builder.stage_out(compiler, Mode::ToolRustc).join("cg_clif"))
3483            .arg("--no-unstable-features")
3484            .arg("--use-backend")
3485            .arg("cranelift")
3486            // Avoid having to vendor the standard library dependencies
3487            .arg("--sysroot")
3488            .arg("llvm")
3489            // These tests depend on crates that are not yet vendored
3490            // FIXME remove once vendoring is handled
3491            .arg("--skip-test")
3492            .arg("testsuite.extended_sysroot");
3493
3494        cargo.into_cmd().run(builder);
3495    }
3496}
3497
3498#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3499pub struct CodegenGCC {
3500    compiler: Compiler,
3501    target: TargetSelection,
3502}
3503
3504impl Step for CodegenGCC {
3505    type Output = ();
3506    const DEFAULT: bool = true;
3507    const ONLY_HOSTS: bool = true;
3508
3509    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
3510        run.paths(&["compiler/rustc_codegen_gcc"])
3511    }
3512
3513    fn make_run(run: RunConfig<'_>) {
3514        let builder = run.builder;
3515        let host = run.build_triple();
3516        let compiler = run.builder.compiler_for(run.builder.top_stage, host, host);
3517
3518        if builder.doc_tests == DocTests::Only {
3519            return;
3520        }
3521
3522        if builder.download_rustc() {
3523            builder.info("CI rustc uses the default codegen backend. skipping");
3524            return;
3525        }
3526
3527        let triple = run.target.triple;
3528        let target_supported =
3529            if triple.contains("linux") { triple.contains("x86_64") } else { false };
3530        if !target_supported {
3531            builder.info("target not supported by rustc_codegen_gcc. skipping");
3532            return;
3533        }
3534
3535        if builder.remote_tested(run.target) {
3536            builder.info("remote testing is not supported by rustc_codegen_gcc. skipping");
3537            return;
3538        }
3539
3540        if !builder.config.codegen_backends(run.target).contains(&CodegenBackendKind::Gcc) {
3541            builder.info("gcc not in rust.codegen-backends. skipping");
3542            return;
3543        }
3544
3545        builder.ensure(CodegenGCC { compiler, target: run.target });
3546    }
3547
3548    fn run(self, builder: &Builder<'_>) {
3549        let compiler = self.compiler;
3550        let target = self.target;
3551
3552        let gcc = builder.ensure(Gcc { target });
3553
3554        builder.ensure(
3555            compile::Std::new(compiler, target)
3556                .extra_rust_args(&["-Csymbol-mangling-version=v0", "-Cpanic=abort"]),
3557        );
3558
3559        // If we're not doing a full bootstrap but we're testing a stage2
3560        // version of libstd, then what we're actually testing is the libstd
3561        // produced in stage1. Reflect that here by updating the compiler that
3562        // we're working with automatically.
3563        let compiler = builder.compiler_for(compiler.stage, compiler.host, target);
3564
3565        let build_cargo = || {
3566            let mut cargo = builder::Cargo::new(
3567                builder,
3568                compiler,
3569                Mode::Codegen, // Must be codegen to ensure dlopen on compiled dylibs works
3570                SourceType::InTree,
3571                target,
3572                Kind::Run,
3573            );
3574
3575            cargo.current_dir(&builder.src.join("compiler/rustc_codegen_gcc"));
3576            cargo
3577                .arg("--manifest-path")
3578                .arg(builder.src.join("compiler/rustc_codegen_gcc/build_system/Cargo.toml"));
3579            compile::rustc_cargo_env(builder, &mut cargo, target);
3580            add_cg_gcc_cargo_flags(&mut cargo, &gcc);
3581
3582            // Avoid incremental cache issues when changing rustc
3583            cargo.env("CARGO_BUILD_INCREMENTAL", "false");
3584            cargo.rustflag("-Cpanic=abort");
3585
3586            cargo
3587        };
3588
3589        builder.info(&format!(
3590            "{} GCC stage{} ({} -> {})",
3591            Kind::Test.description(),
3592            compiler.stage,
3593            &compiler.host,
3594            target
3595        ));
3596        let _time = helpers::timeit(builder);
3597
3598        // FIXME: Uncomment the `prepare` command below once vendoring is implemented.
3599        /*
3600        let mut prepare_cargo = build_cargo();
3601        prepare_cargo.arg("--").arg("prepare");
3602        #[expect(deprecated)]
3603        builder.config.try_run(&mut prepare_cargo.into()).unwrap();
3604        */
3605
3606        let mut cargo = build_cargo();
3607
3608        cargo
3609            // cg_gcc's build system ignores RUSTFLAGS. pass some flags through CG_RUSTFLAGS instead.
3610            .env("CG_RUSTFLAGS", "-Alinker-messages")
3611            .arg("--")
3612            .arg("test")
3613            .arg("--use-backend")
3614            .arg("gcc")
3615            .arg("--gcc-path")
3616            .arg(gcc.libgccjit.parent().unwrap())
3617            .arg("--out-dir")
3618            .arg(builder.stage_out(compiler, Mode::ToolRustc).join("cg_gcc"))
3619            .arg("--release")
3620            .arg("--mini-tests")
3621            .arg("--std-tests");
3622        cargo.args(builder.config.test_args());
3623
3624        cargo.into_cmd().run(builder);
3625    }
3626}
3627
3628/// Test step that does two things:
3629/// - Runs `cargo test` for the `src/tools/test-float-parse` tool.
3630/// - Invokes the `test-float-parse` tool to test the standard library's
3631///   float parsing routines.
3632#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3633pub struct TestFloatParse {
3634    path: PathBuf,
3635    host: TargetSelection,
3636}
3637
3638impl Step for TestFloatParse {
3639    type Output = ();
3640    const ONLY_HOSTS: bool = true;
3641    const DEFAULT: bool = true;
3642
3643    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
3644        run.path("src/tools/test-float-parse")
3645    }
3646
3647    fn make_run(run: RunConfig<'_>) {
3648        for path in run.paths {
3649            let path = path.assert_single_path().path.clone();
3650            run.builder.ensure(Self { path, host: run.target });
3651        }
3652    }
3653
3654    fn run(self, builder: &Builder<'_>) {
3655        let bootstrap_host = builder.config.host_target;
3656        let compiler = builder.compiler(builder.top_stage, bootstrap_host);
3657        let path = self.path.to_str().unwrap();
3658        let crate_name = self.path.iter().next_back().unwrap().to_str().unwrap();
3659
3660        builder.ensure(tool::TestFloatParse { host: self.host });
3661
3662        // Run any unit tests in the crate
3663        let mut cargo_test = tool::prepare_tool_cargo(
3664            builder,
3665            compiler,
3666            Mode::ToolStd,
3667            bootstrap_host,
3668            Kind::Test,
3669            path,
3670            SourceType::InTree,
3671            &[],
3672        );
3673        cargo_test.allow_features(tool::TestFloatParse::ALLOW_FEATURES);
3674
3675        run_cargo_test(cargo_test, &[], &[], crate_name, bootstrap_host, builder);
3676
3677        // Run the actual parse tests.
3678        let mut cargo_run = tool::prepare_tool_cargo(
3679            builder,
3680            compiler,
3681            Mode::ToolStd,
3682            bootstrap_host,
3683            Kind::Run,
3684            path,
3685            SourceType::InTree,
3686            &[],
3687        );
3688        cargo_run.allow_features(tool::TestFloatParse::ALLOW_FEATURES);
3689
3690        if !matches!(env::var("FLOAT_PARSE_TESTS_NO_SKIP_HUGE").as_deref(), Ok("1") | Ok("true")) {
3691            cargo_run.args(["--", "--skip-huge"]);
3692        }
3693
3694        cargo_run.into_cmd().run(builder);
3695    }
3696}
3697
3698/// Runs the tool `src/tools/collect-license-metadata` in `ONLY_CHECK=1` mode,
3699/// which verifies that `license-metadata.json` is up-to-date and therefore
3700/// running the tool normally would not update anything.
3701#[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)]
3702pub struct CollectLicenseMetadata;
3703
3704impl Step for CollectLicenseMetadata {
3705    type Output = PathBuf;
3706    const ONLY_HOSTS: bool = true;
3707
3708    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
3709        run.path("src/tools/collect-license-metadata")
3710    }
3711
3712    fn make_run(run: RunConfig<'_>) {
3713        run.builder.ensure(CollectLicenseMetadata);
3714    }
3715
3716    fn run(self, builder: &Builder<'_>) -> Self::Output {
3717        let Some(reuse) = &builder.config.reuse else {
3718            panic!("REUSE is required to collect the license metadata");
3719        };
3720
3721        let dest = builder.src.join("license-metadata.json");
3722
3723        let mut cmd = builder.tool_cmd(Tool::CollectLicenseMetadata);
3724        cmd.env("REUSE_EXE", reuse);
3725        cmd.env("DEST", &dest);
3726        cmd.env("ONLY_CHECK", "1");
3727        cmd.run(builder);
3728
3729        dest
3730    }
3731}