bootstrap/core/build_steps/
test.rs

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