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