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