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