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