Skip to main content

bootstrap/core/build_steps/
check.rs

1//! Implementation of compiling the compiler and standard library, in "check"-based modes.
2
3use std::fs;
4use std::path::{Path, PathBuf};
5
6use crate::core::build_steps::compile::{
7    ArtifactKeepMode, add_to_sysroot, run_cargo, rustc_cargo, rustc_cargo_env, std_cargo,
8    std_crates_for_make_run,
9};
10use crate::core::build_steps::tool;
11use crate::core::build_steps::tool::{
12    SourceType, TEST_FLOAT_PARSE_ALLOW_FEATURES, ToolTargetBuildMode, get_tool_target_compiler,
13    prepare_tool_cargo,
14};
15use crate::core::builder::{
16    self, Alias, Builder, Cargo, CommandLineStep, Kind, RunConfig, ShouldRun, Step, StepMetadata,
17    crate_description,
18};
19use crate::core::config::{Subcommand, TargetSelection};
20use crate::utils::build_stamp::{self, BuildStamp};
21use crate::utils::helpers::t;
22use crate::{CodegenBackendKind, Compiler, Mode};
23
24/// Allows individual check-step instances to keep track of whether they
25/// represent `cargo check` or `cargo fix`, independently of [`Builder::kind`].
26#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
27enum CheckKind {
28    Check,
29    Fix,
30}
31
32impl CheckKind {
33    fn to_kind(self) -> Kind {
34        match self {
35            CheckKind::Check => Kind::Check,
36            CheckKind::Fix => Kind::Fix,
37        }
38    }
39}
40
41#[derive(Debug, Clone, PartialEq, Eq, Hash)]
42pub struct Std {
43    /// Compiler that will check this std.
44    pub build_compiler: Compiler,
45    pub target: TargetSelection,
46    /// Whether to build only a subset of crates.
47    ///
48    /// This shouldn't be used from other steps; see the comment on [`compile::Rustc`].
49    ///
50    /// [`compile::Rustc`]: crate::core::build_steps::compile::Rustc
51    crates: Vec<String>,
52}
53
54impl Std {
55    const CRATE_OR_DEPS: &[&str] = &["sysroot", "coretests", "alloctests"];
56}
57
58impl CommandLineStep for Std {
59    type Output = BuildStamp;
60
61    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
62        let mut run = run;
63        for c in Std::CRATE_OR_DEPS {
64            run = run.crate_or_deps(c);
65        }
66
67        run.path("library")
68    }
69
70    fn is_default_step(_builder: &Builder<'_>) -> bool {
71        true
72    }
73
74    fn make_run(run: RunConfig<'_>) {
75        if !run.builder.download_rustc() && run.builder.config.skip_std_check_if_no_download_rustc {
76            eprintln!(
77                "WARNING: `--skip-std-check-if-no-download-rustc` flag was passed and `rust.download-rustc` is not available. Skipping."
78            );
79            return;
80        }
81
82        if run.builder.config.compile_time_deps {
83            // libstd doesn't have any important build scripts and can't have any proc macros
84            return;
85        }
86
87        // Explicitly pass -p for all dependencies crates -- this will force cargo
88        // to also check the tests/benches/examples for these crates, rather
89        // than just the leaf crate.
90        let crates = std_crates_for_make_run(&run);
91        run.builder.ensure(Std {
92            build_compiler: prepare_compiler_for_check(run.builder, run.target, Mode::Std)
93                .build_compiler(),
94            target: run.target,
95            crates,
96        });
97    }
98
99    fn run(self, builder: &Builder<'_>) -> Self::Output {
100        let build_compiler = self.build_compiler;
101        let target = self.target;
102
103        let mut cargo = builder::Cargo::new(
104            builder,
105            build_compiler,
106            Mode::Std,
107            SourceType::InTree,
108            target,
109            builder.kind,
110        );
111
112        std_cargo(builder, target, &mut cargo, &self.crates);
113        if matches!(builder.config.cmd, Subcommand::Fix) {
114            // By default, cargo tries to fix all targets. Tell it not to fix tests until we've added `test` to the sysroot.
115            cargo.arg("--lib");
116        }
117
118        let _guard = builder.msg(
119            builder.kind,
120            format_args!("library artifacts{}", crate_description(&self.crates)),
121            Mode::Std,
122            build_compiler,
123            target,
124        );
125
126        let check_stamp =
127            build_stamp::libstd_stamp(builder, build_compiler, target).with_prefix("check");
128        run_cargo(
129            builder,
130            cargo,
131            builder.config.free_args.clone(),
132            &check_stamp,
133            vec![],
134            ArtifactKeepMode::OnlyRmeta,
135        );
136
137        drop(_guard);
138
139        // don't check test dependencies if we haven't built libtest
140        if !self.crates.iter().any(|krate| krate == "test") {
141            return check_stamp;
142        }
143
144        // Then run cargo again, once we've put the rmeta files for the library
145        // crates into the sysroot. This is needed because e.g., core's tests
146        // depend on `libtest` -- Cargo presumes it will exist, but it doesn't
147        // since we initialize with an empty sysroot.
148        //
149        // Currently only the "libtest" tree of crates does this.
150        let mut cargo = builder::Cargo::new(
151            builder,
152            build_compiler,
153            Mode::Std,
154            SourceType::InTree,
155            target,
156            Kind::Check,
157        );
158
159        std_cargo(builder, target, &mut cargo, &self.crates);
160
161        let stamp =
162            build_stamp::libstd_stamp(builder, build_compiler, target).with_prefix("check-test");
163        let _guard = builder.msg(
164            Kind::Check,
165            "library test/bench/example targets",
166            Mode::Std,
167            build_compiler,
168            target,
169        );
170        run_cargo(
171            builder,
172            cargo,
173            builder.config.free_args.clone(),
174            &stamp,
175            vec![],
176            ArtifactKeepMode::OnlyRmeta,
177        );
178        check_stamp
179    }
180
181    fn metadata(&self) -> Option<StepMetadata> {
182        Some(StepMetadata::check("std", self.target).built_by(self.build_compiler))
183    }
184}
185
186/// Represents a proof that rustc was **checked**.
187/// Contains directories with .rmeta files generated by checking rustc for a specific
188/// target.
189#[derive(Debug, Clone, PartialEq, Eq, Hash)]
190struct RmetaSysroot {
191    host_dir: PathBuf,
192    target_dir: PathBuf,
193}
194
195impl RmetaSysroot {
196    /// Copy rmeta artifacts from the given `stamp` into a sysroot located at `directory`.
197    fn from_stamp(
198        builder: &Builder<'_>,
199        stamp: BuildStamp,
200        target: TargetSelection,
201        directory: &Path,
202    ) -> Self {
203        let host_dir = directory.join("host");
204        let target_dir = directory.join(target);
205        let _ = fs::remove_dir_all(directory);
206        t!(fs::create_dir_all(directory));
207        add_to_sysroot(builder, &target_dir, &host_dir, &stamp);
208
209        Self { host_dir, target_dir }
210    }
211
212    /// Configure the given cargo invocation so that the compiled crate will be able to use
213    /// rustc .rmeta artifacts that were previously generated.
214    fn configure_cargo(&self, cargo: &mut Cargo) {
215        cargo.append_to_env(
216            "RUSTC_ADDITIONAL_SYSROOT_PATHS",
217            format!("{},{}", self.host_dir.to_str().unwrap(), self.target_dir.to_str().unwrap()),
218            ",",
219        );
220    }
221}
222
223/// Checks rustc using the given `build_compiler` for the given `target`, and produces
224/// a sysroot in the build directory that stores the generated .rmeta files.
225///
226/// This step exists so that we can store the generated .rmeta artifacts into a separate
227/// directory, instead of copying them into the sysroot of `build_compiler`, which would
228/// "pollute" it (that is especially problematic for the external stage0 rustc).
229#[derive(Debug, Clone, PartialEq, Eq, Hash)]
230struct PrepareRustcRmetaSysroot {
231    build_compiler: CompilerForCheck,
232    target: TargetSelection,
233}
234
235impl PrepareRustcRmetaSysroot {
236    fn new(build_compiler: CompilerForCheck, target: TargetSelection) -> Self {
237        Self { build_compiler, target }
238    }
239}
240
241impl Step for PrepareRustcRmetaSysroot {
242    type Output = RmetaSysroot;
243
244    fn run(self, builder: &Builder<'_>) -> Self::Output {
245        // Check rustc
246        let stamp = Rustc::check_rustc_for_preparing_sysroot(builder, &self);
247
248        let build_compiler = self.build_compiler.build_compiler();
249
250        // Copy the generated rmeta artifacts to a separate directory
251        let dir = builder
252            .out
253            .join(build_compiler.host)
254            .join(format!("stage{}-rustc-rmeta-artifacts", build_compiler.stage + 1));
255        RmetaSysroot::from_stamp(builder, stamp, self.target, &dir)
256    }
257}
258
259/// Checks std using the given `build_compiler` for the given `target`, and produces
260/// a sysroot in the build directory that stores the generated .rmeta files.
261///
262/// This step exists so that we can store the generated .rmeta artifacts into a separate
263/// directory, instead of copying them into the sysroot of `build_compiler`, which would
264/// "pollute" it (that is especially problematic for the external stage0 rustc).
265#[derive(Debug, Clone, PartialEq, Eq, Hash)]
266struct PrepareStdRmetaSysroot {
267    build_compiler: Compiler,
268    target: TargetSelection,
269}
270
271impl PrepareStdRmetaSysroot {
272    fn new(build_compiler: Compiler, target: TargetSelection) -> Self {
273        Self { build_compiler, target }
274    }
275}
276
277impl Step for PrepareStdRmetaSysroot {
278    type Output = RmetaSysroot;
279
280    fn run(self, builder: &Builder<'_>) -> Self::Output {
281        // Check std
282        let stamp = builder.ensure(Std {
283            build_compiler: self.build_compiler,
284            target: self.target,
285            crates: vec![],
286        });
287
288        // Copy the generated rmeta artifacts to a separate directory
289        let dir = builder
290            .out
291            .join(self.build_compiler.host)
292            .join(format!("stage{}-std-rmeta-artifacts", self.build_compiler.stage));
293
294        RmetaSysroot::from_stamp(builder, stamp, self.target, &dir)
295    }
296}
297
298/// Checks rustc using `build_compiler`.
299#[derive(Debug, Clone, PartialEq, Eq, Hash)]
300pub struct Rustc {
301    check_kind: CheckKind,
302
303    /// Compiler that will check this rustc.
304    build_compiler: CompilerForCheck,
305    target: TargetSelection,
306
307    /// Whether to build only a subset of crates.
308    ///
309    /// This shouldn't be used from other steps; see the comment on [`compile::Rustc`].
310    ///
311    /// [`compile::Rustc`]: crate::core::build_steps::compile::Rustc
312    crates: Vec<String>,
313}
314
315impl Rustc {
316    fn check_rustc_for_preparing_sysroot(
317        builder: &Builder<'_>,
318        prepare: &PrepareRustcRmetaSysroot,
319    ) -> BuildStamp {
320        builder.ensure(Rustc {
321            // We specifically want `cargo check`, not the current bootstrap subcommand.
322            check_kind: CheckKind::Check,
323            build_compiler: prepare.build_compiler.clone(),
324            target: prepare.target,
325            crates: vec![],
326        })
327    }
328}
329
330impl CommandLineStep for Rustc {
331    type Output = BuildStamp;
332    const IS_HOST: bool = true;
333
334    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
335        run.crate_or_deps("rustc-main").path("compiler")
336    }
337
338    fn is_default_step(_builder: &Builder<'_>) -> bool {
339        true
340    }
341
342    fn make_run(run: RunConfig<'_>) {
343        let check_kind = match run.builder.kind {
344            Kind::Check => CheckKind::Check,
345            Kind::Fix => CheckKind::Fix,
346            kind => panic!("unexpected kind for `check::Rustc`: {kind:?}"),
347        };
348
349        let target = run.target;
350        let build_compiler = prepare_compiler_for_check(run.builder, target, Mode::Rustc);
351        let crates = run.make_run_crates(Alias::Compiler);
352
353        run.builder.ensure(Rustc { check_kind, build_compiler, target, crates });
354    }
355
356    /// Check the compiler.
357    ///
358    /// This will check the compiler for a particular stage of the build using
359    /// the `compiler` targeting the `target` architecture. The artifacts
360    /// created will also be linked into the sysroot directory.
361    ///
362    /// If we check a stage 2 compiler, we will have to first build a stage 1 compiler to check it.
363    fn run(self, builder: &Builder<'_>) -> Self::Output {
364        let build_compiler = self.build_compiler.build_compiler;
365        let target = self.target;
366
367        let mut cargo = builder::Cargo::new(
368            builder,
369            build_compiler,
370            Mode::Rustc,
371            SourceType::InTree,
372            target,
373            self.check_kind.to_kind(),
374        );
375
376        rustc_cargo(builder, &mut cargo, target, &build_compiler, &self.crates);
377        self.build_compiler.configure_cargo(&mut cargo);
378
379        // Explicitly pass -p for all compiler crates -- this will force cargo
380        // to also check the tests/benches/examples for these crates, rather
381        // than just the leaf crate.
382        for krate in &*self.crates {
383            cargo.arg("-p").arg(krate);
384        }
385
386        let _guard = builder.msg(
387            self.check_kind.to_kind(),
388            format_args!("compiler artifacts{}", crate_description(&self.crates)),
389            Mode::Rustc,
390            self.build_compiler.build_compiler(),
391            target,
392        );
393
394        let stamp =
395            build_stamp::librustc_stamp(builder, build_compiler, target).with_prefix("check");
396
397        run_cargo(
398            builder,
399            cargo,
400            builder.config.free_args.clone(),
401            &stamp,
402            vec![],
403            ArtifactKeepMode::OnlyRmeta,
404        );
405
406        stamp
407    }
408
409    fn metadata(&self) -> Option<StepMetadata> {
410        let mut metadata = StepMetadata::new("rustc", self.target, self.check_kind.to_kind())
411            .built_by(self.build_compiler.build_compiler());
412        if !self.crates.is_empty() {
413            metadata = metadata.with_metadata(format!("({} crates)", self.crates.len()));
414        }
415        Some(metadata)
416    }
417}
418
419/// Represents a compiler that can check something.
420///
421/// If the compiler was created for `Mode::ToolRustcPrivate` or `Mode::Codegen`, it will also contain
422/// .rmeta artifacts from rustc that was already checked using `build_compiler`.
423///
424/// All steps that use this struct in a "general way" (i.e. they don't know exactly what kind of
425/// thing is being built) should call `configure_cargo` to ensure that the rmeta artifacts are
426/// properly linked, if present.
427#[derive(Debug, Clone, PartialEq, Eq, Hash)]
428pub struct CompilerForCheck {
429    build_compiler: Compiler,
430    rustc_rmeta_sysroot: Option<RmetaSysroot>,
431    std_rmeta_sysroot: Option<RmetaSysroot>,
432}
433
434impl CompilerForCheck {
435    pub fn build_compiler(&self) -> Compiler {
436        self.build_compiler
437    }
438
439    /// If there are any rustc rmeta artifacts available, configure the Cargo invocation
440    /// so that the artifact being built can find them.
441    pub fn configure_cargo(&self, cargo: &mut Cargo) {
442        if let Some(sysroot) = &self.rustc_rmeta_sysroot {
443            sysroot.configure_cargo(cargo);
444        }
445        if let Some(sysroot) = &self.std_rmeta_sysroot {
446            sysroot.configure_cargo(cargo);
447        }
448    }
449}
450
451/// Prepare the standard library for checking something (that requires stdlib) using
452/// `build_compiler`.
453fn prepare_std(
454    builder: &Builder<'_>,
455    build_compiler: Compiler,
456    target: TargetSelection,
457) -> Option<RmetaSysroot> {
458    // We need to build the host stdlib even if we only check, to compile build scripts and proc
459    // macros
460    builder.std(build_compiler, builder.host_target);
461
462    // If we're cross-compiling, we generate the rmeta files for the given target
463    // This check has to be here, because if we generate both .so and .rmeta files, rustc will fail,
464    // as it will have multiple candidates for linking.
465    if builder.host_target != target {
466        Some(builder.ensure(PrepareStdRmetaSysroot::new(build_compiler, target)))
467    } else {
468        None
469    }
470}
471
472/// Prepares a compiler that will check something with the given `mode`.
473pub fn prepare_compiler_for_check(
474    builder: &Builder<'_>,
475    target: TargetSelection,
476    mode: Mode,
477) -> CompilerForCheck {
478    let host = builder.host_target;
479
480    let mut rustc_rmeta_sysroot = None;
481    let mut std_rmeta_sysroot = None;
482    let build_compiler = match mode {
483        Mode::ToolBootstrap => builder.compiler(0, host),
484        // We could also only check std here and use `prepare_std`, but `ToolTarget` is currently
485        // only used for running in-tree Clippy on bootstrap tools, so it does not seem worth it to
486        // optimize it. Therefore, here we build std for the target, instead of just checking it.
487        Mode::ToolTarget => get_tool_target_compiler(builder, ToolTargetBuildMode::Build(target)),
488        Mode::ToolStd => {
489            if builder.config.compile_time_deps {
490                // When --compile-time-deps is passed, we can't use any rustc
491                // other than the bootstrap compiler. Luckily build scripts and
492                // proc macros for tools are unlikely to need nightly.
493                builder.compiler(0, host)
494            } else {
495                // These tools require the local standard library to be checked
496                let build_compiler = builder.compiler(builder.top_stage, host);
497                std_rmeta_sysroot = prepare_std(builder, build_compiler, target);
498                build_compiler
499            }
500        }
501        Mode::ToolRustcPrivate | Mode::Codegen => {
502            // Check Rustc to produce the required rmeta artifacts for rustc_private, and then
503            // return the build compiler that was used to check rustc.
504            // We do not need to check examples/tests/etc. of Rustc for rustc_private, so we pass
505            // an empty set of crates, which will avoid using `cargo -p`.
506            let compiler_for_rustc = prepare_compiler_for_check(builder, target, Mode::Rustc);
507            rustc_rmeta_sysroot = Some(
508                builder.ensure(PrepareRustcRmetaSysroot::new(compiler_for_rustc.clone(), target)),
509            );
510            let build_compiler = compiler_for_rustc.build_compiler();
511
512            // To check a rustc_private tool, we also need to check std that it will link to
513            std_rmeta_sysroot = prepare_std(builder, build_compiler, target);
514            build_compiler
515        }
516        Mode::Rustc => {
517            // This is a horrible hack, because we actually change the compiler stage numbering
518            // here. If you do `x check --stage 1 --host FOO`, we build stage 1 host rustc,
519            // and use that to check stage 1 FOO rustc (which actually makes that stage 2 FOO
520            // rustc).
521            //
522            // FIXME: remove this and either fix cross-compilation check on stage 2 (which has a
523            // myriad of other problems) or disable cross-checking on stage 1.
524            let stage = if host == target { builder.top_stage - 1 } else { builder.top_stage };
525            let build_compiler = builder.compiler(stage, host);
526
527            // To check rustc, we need to check std that it will link to
528            std_rmeta_sysroot = prepare_std(builder, build_compiler, target);
529            build_compiler
530        }
531        Mode::Std => {
532            // When checking std stage N, we want to do it with the stage N compiler
533            // Note: we don't need to build the host stdlib here, because when compiling std, the
534            // stage 0 stdlib is used to compile build scripts and proc macros.
535            builder.compiler(builder.top_stage, host)
536        }
537    };
538    CompilerForCheck { build_compiler, rustc_rmeta_sysroot, std_rmeta_sysroot }
539}
540
541/// Check the Cranelift codegen backend.
542#[derive(Debug, Clone, PartialEq, Eq, Hash)]
543pub struct CraneliftCodegenBackend {
544    build_compiler: CompilerForCheck,
545    target: TargetSelection,
546}
547
548impl CommandLineStep for CraneliftCodegenBackend {
549    type Output = ();
550    const IS_HOST: bool = true;
551
552    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
553        run.alias("rustc_codegen_cranelift").alias("cg_clif")
554    }
555
556    fn is_default_step(_builder: &Builder<'_>) -> bool {
557        true
558    }
559
560    fn make_run(run: RunConfig<'_>) {
561        run.builder.ensure(CraneliftCodegenBackend {
562            build_compiler: prepare_compiler_for_check(run.builder, run.target, Mode::Codegen),
563            target: run.target,
564        });
565    }
566
567    fn run(self, builder: &Builder<'_>) {
568        let build_compiler = self.build_compiler.build_compiler();
569        let target = self.target;
570
571        let mut cargo = builder::Cargo::new(
572            builder,
573            build_compiler,
574            Mode::Codegen,
575            SourceType::InTree,
576            target,
577            builder.kind,
578        );
579
580        cargo
581            .arg("--manifest-path")
582            .arg(builder.src.join("compiler/rustc_codegen_cranelift/Cargo.toml"));
583        rustc_cargo_env(builder, &mut cargo, target);
584        self.build_compiler.configure_cargo(&mut cargo);
585
586        let _guard = builder.msg(
587            Kind::Check,
588            "rustc_codegen_cranelift",
589            Mode::Codegen,
590            build_compiler,
591            target,
592        );
593
594        let stamp = build_stamp::codegen_backend_stamp(
595            builder,
596            build_compiler,
597            target,
598            &CodegenBackendKind::Cranelift,
599        )
600        .with_prefix("check");
601
602        run_cargo(
603            builder,
604            cargo,
605            builder.config.free_args.clone(),
606            &stamp,
607            vec![],
608            ArtifactKeepMode::OnlyRmeta,
609        );
610    }
611
612    fn metadata(&self) -> Option<StepMetadata> {
613        Some(
614            StepMetadata::check("rustc_codegen_cranelift", self.target)
615                .built_by(self.build_compiler.build_compiler()),
616        )
617    }
618}
619
620/// Check the GCC codegen backend.
621#[derive(Debug, Clone, PartialEq, Eq, Hash)]
622pub struct GccCodegenBackend {
623    build_compiler: CompilerForCheck,
624    target: TargetSelection,
625}
626
627impl CommandLineStep for GccCodegenBackend {
628    type Output = ();
629    const IS_HOST: bool = true;
630
631    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
632        run.alias("rustc_codegen_gcc").alias("cg_gcc")
633    }
634
635    fn is_default_step(_builder: &Builder<'_>) -> bool {
636        true
637    }
638
639    fn make_run(run: RunConfig<'_>) {
640        run.builder.ensure(GccCodegenBackend {
641            build_compiler: prepare_compiler_for_check(run.builder, run.target, Mode::Codegen),
642            target: run.target,
643        });
644    }
645
646    fn run(self, builder: &Builder<'_>) {
647        // FIXME: remove once https://github.com/rust-lang/rust/issues/112393 is resolved
648        if builder.build.config.vendor {
649            println!("Skipping checking of `rustc_codegen_gcc` with vendoring enabled.");
650            return;
651        }
652
653        let build_compiler = self.build_compiler.build_compiler();
654        let target = self.target;
655
656        let mut cargo = builder::Cargo::new(
657            builder,
658            build_compiler,
659            Mode::Codegen,
660            SourceType::InTree,
661            target,
662            builder.kind,
663        );
664
665        cargo.arg("--manifest-path").arg(builder.src.join("compiler/rustc_codegen_gcc/Cargo.toml"));
666        rustc_cargo_env(builder, &mut cargo, target);
667        self.build_compiler.configure_cargo(&mut cargo);
668
669        let _guard =
670            builder.msg(Kind::Check, "rustc_codegen_gcc", Mode::Codegen, build_compiler, target);
671
672        let stamp = build_stamp::codegen_backend_stamp(
673            builder,
674            build_compiler,
675            target,
676            &CodegenBackendKind::Gcc,
677        )
678        .with_prefix("check");
679
680        run_cargo(
681            builder,
682            cargo,
683            builder.config.free_args.clone(),
684            &stamp,
685            vec![],
686            ArtifactKeepMode::OnlyRmeta,
687        );
688    }
689
690    fn metadata(&self) -> Option<StepMetadata> {
691        Some(
692            StepMetadata::check("rustc_codegen_gcc", self.target)
693                .built_by(self.build_compiler.build_compiler()),
694        )
695    }
696}
697
698macro_rules! tool_check_step {
699    (
700        $name:ident {
701            // The part of this path after the final '/' is also used as a display name.
702            path: $path:literal
703            $(, alt_path: $alt_path:literal )*
704            // `Mode` to use when checking this tool
705            , mode: $mode:expr
706            // Subset of nightly features that are allowed to be used when checking
707            $(, allow_features: $allow_features:expr )?
708            // Features that should be enabled when checking
709            $(, enable_features: [$($enable_features:expr),*] )?
710            $(, default_features: $default_features:expr )?
711            $(, default: $default:literal )?
712            $( , )?
713        }
714    ) => {
715        #[derive(Debug, Clone, PartialEq, Eq, Hash)]
716        pub struct $name {
717            compiler: CompilerForCheck,
718            target: TargetSelection,
719        }
720
721        impl CommandLineStep for $name {
722            type Output = ();
723            const IS_HOST: bool = true;
724
725            fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
726                run.multi_path(&[$path $(, $alt_path )*])
727            }
728
729            fn is_default_step(_builder: &Builder<'_>) -> bool {
730                // Most of the tool-checks using this macro are run by default.
731                true $( && const { $default } )?
732            }
733
734            fn make_run(run: RunConfig<'_>) {
735                let target = run.target;
736                let mode: Mode = $mode;
737
738                let compiler = prepare_compiler_for_check(run.builder, target, mode);
739
740                // It doesn't make sense to cross-check bootstrap tools
741                if mode == Mode::ToolBootstrap && target != run.builder.host_target {
742                    println!("WARNING: not checking bootstrap tool {} for target {target} as it is a bootstrap (host-only) tool", stringify!($path));
743                    return;
744                };
745
746                run.builder.ensure($name { target, compiler });
747            }
748
749            fn run(self, builder: &Builder<'_>) {
750                let Self { target, compiler } = self;
751                let allow_features = {
752                    let mut _value = "";
753                    $( _value = $allow_features; )?
754                    _value
755                };
756                let extra_features: &[&str] = &[$($($enable_features),*)?];
757                let default_features = {
758                    let mut _value = true;
759                    $( _value = $default_features; )?
760                    _value
761                };
762                let mode: Mode = $mode;
763                run_tool_check_step(builder, compiler, target, $path, mode, allow_features, extra_features, default_features);
764            }
765
766            fn metadata(&self) -> Option<StepMetadata> {
767                Some(StepMetadata::check(stringify!($name), self.target).built_by(self.compiler.build_compiler))
768            }
769        }
770    }
771}
772
773/// Used by the implementation of `Step::run` in `tool_check_step!`.
774#[allow(clippy::too_many_arguments)]
775fn run_tool_check_step(
776    builder: &Builder<'_>,
777    compiler: CompilerForCheck,
778    target: TargetSelection,
779    path: &str,
780    mode: Mode,
781    allow_features: &str,
782    extra_features: &[&str],
783    default_features: bool,
784) {
785    let display_name = path.rsplit('/').next().unwrap();
786
787    let build_compiler = compiler.build_compiler();
788
789    let extra_features = extra_features.iter().map(|f| f.to_string()).collect::<Vec<String>>();
790    let mut cargo = prepare_tool_cargo(
791        builder,
792        build_compiler,
793        mode,
794        target,
795        builder.kind,
796        path,
797        // Currently, all of the tools that use this macro/function are in-tree.
798        // If support for out-of-tree tools is re-added in the future, those
799        // steps should probably be marked non-default so that the default
800        // checks aren't affected by toolstate being broken.
801        SourceType::InTree,
802        &extra_features,
803    );
804    cargo.allow_features(allow_features);
805    compiler.configure_cargo(&mut cargo);
806
807    // FIXME: check bootstrap doesn't currently work when multiple targets are checked
808    // FIXME: rust-analyzer does not work with --all-targets
809    if display_name == "rust-analyzer" {
810        cargo.arg("--bins");
811        cargo.arg("--tests");
812        cargo.arg("--benches");
813    } else {
814        cargo.arg("--all-targets");
815    }
816
817    if !default_features {
818        cargo.arg("--no-default-features");
819    }
820
821    let stamp = BuildStamp::new(&builder.cargo_out(build_compiler, mode, target))
822        .with_prefix(&format!("{display_name}-check"));
823
824    let _guard = builder.msg(builder.kind, display_name, mode, build_compiler, target);
825    run_cargo(
826        builder,
827        cargo,
828        builder.config.free_args.clone(),
829        &stamp,
830        vec![],
831        ArtifactKeepMode::OnlyRmeta,
832    );
833}
834
835tool_check_step!(Rustdoc {
836    path: "src/tools/rustdoc",
837    alt_path: "src/librustdoc",
838    mode: Mode::ToolRustcPrivate
839});
840// Clippy, miri and Rustfmt are hybrids. They are external tools, but use a git subtree instead
841// of a submodule. Since the SourceType only drives the deny-warnings
842// behavior, treat it as in-tree so that any new warnings in clippy will be
843// rejected.
844tool_check_step!(Clippy { path: "src/tools/clippy", mode: Mode::ToolRustcPrivate });
845tool_check_step!(Miri {
846    path: "src/tools/miri",
847    mode: Mode::ToolRustcPrivate,
848    enable_features: ["check_only"],
849});
850tool_check_step!(CargoMiri { path: "src/tools/miri/cargo-miri", mode: Mode::ToolRustcPrivate });
851tool_check_step!(Priroda { path: "src/tools/miri/priroda", mode: Mode::ToolRustcPrivate });
852tool_check_step!(Rustfmt { path: "src/tools/rustfmt", mode: Mode::ToolRustcPrivate });
853tool_check_step!(RustAnalyzer {
854    path: "src/tools/rust-analyzer",
855    mode: Mode::ToolRustcPrivate,
856    allow_features: tool::RustAnalyzer::ALLOW_FEATURES,
857    enable_features: ["in-rust-tree"],
858});
859tool_check_step!(MiroptTestTools {
860    path: "src/tools/miropt-test-tools",
861    mode: Mode::ToolBootstrap
862});
863// We want to test the local std
864tool_check_step!(TestFloatParse {
865    path: "src/tools/test-float-parse",
866    mode: Mode::ToolStd,
867    allow_features: TEST_FLOAT_PARSE_ALLOW_FEATURES
868});
869tool_check_step!(FeaturesStatusDump {
870    path: "src/tools/features-status-dump",
871    mode: Mode::ToolBootstrap
872});
873
874tool_check_step!(Bootstrap { path: "src/bootstrap", mode: Mode::ToolBootstrap, default: false });
875
876// `run-make-support` will be built as part of suitable run-make compiletest test steps, but support
877// check to make it easier to work on.
878tool_check_step!(RunMakeSupport {
879    path: "src/tools/run-make-support",
880    mode: Mode::ToolBootstrap,
881    default: false
882});
883
884tool_check_step!(CoverageDump {
885    path: "src/tools/coverage-dump",
886    mode: Mode::ToolBootstrap,
887    default: false
888});
889
890// Compiletest is implicitly "checked" when it gets built in order to run tests,
891// so this is mainly for people working on compiletest to run locally.
892tool_check_step!(Compiletest {
893    path: "src/tools/compiletest",
894    mode: Mode::ToolBootstrap,
895    default: false,
896});
897
898// As with compiletest, rustdoc-gui-test is automatically built when running
899// relevant tests. So being able to check it is mainly useful for people
900// working on on rustdoc-gui-test itself, or on its compiletest dependency.
901tool_check_step!(RustdocGuiTest {
902    path: "src/tools/rustdoc-gui-test",
903    mode: Mode::ToolBootstrap,
904    default: false,
905});
906
907tool_check_step!(Linkchecker {
908    path: "src/tools/linkchecker",
909    mode: Mode::ToolBootstrap,
910    default: false
911});
912
913tool_check_step!(BumpStage0 {
914    path: "src/tools/bump-stage0",
915    mode: Mode::ToolBootstrap,
916    default: false
917});
918
919// Tidy is implicitly checked when `./x test tidy` is executed
920// (if you set a pre-push hook, the command is called).
921// So this is mainly for people working on tidy.
922tool_check_step!(Tidy { path: "src/tools/tidy", mode: Mode::ToolBootstrap, default: false });