bootstrap/core/build_steps/
test.rs

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