Skip to main content

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