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