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