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