bootstrap/core/build_steps/
check.rs

1//! Implementation of compiling the compiler and standard library, in "check"-based modes.
2
3use crate::core::build_steps::compile::{
4    add_to_sysroot, run_cargo, rustc_cargo, rustc_cargo_env, std_cargo, std_crates_for_run_make,
5};
6use crate::core::build_steps::tool;
7use crate::core::build_steps::tool::{COMPILETEST_ALLOW_FEATURES, SourceType, prepare_tool_cargo};
8use crate::core::builder::{
9    self, Alias, Builder, Kind, RunConfig, ShouldRun, Step, StepMetadata, crate_description,
10};
11use crate::core::config::TargetSelection;
12use crate::utils::build_stamp::{self, BuildStamp};
13use crate::{Compiler, Mode, Subcommand};
14
15#[derive(Debug, Clone, PartialEq, Eq, Hash)]
16pub struct Std {
17    /// Compiler that will check this std.
18    pub build_compiler: Compiler,
19    pub target: TargetSelection,
20    /// Whether to build only a subset of crates.
21    ///
22    /// This shouldn't be used from other steps; see the comment on [`compile::Rustc`].
23    ///
24    /// [`compile::Rustc`]: crate::core::build_steps::compile::Rustc
25    crates: Vec<String>,
26}
27
28impl Std {
29    const CRATE_OR_DEPS: &[&str] = &["sysroot", "coretests", "alloctests"];
30
31    pub fn new(build_compiler: Compiler, target: TargetSelection) -> Self {
32        Self { build_compiler, target, crates: vec![] }
33    }
34}
35
36impl Step for Std {
37    type Output = ();
38    const DEFAULT: bool = true;
39
40    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
41        let mut run = run;
42        for c in Std::CRATE_OR_DEPS {
43            run = run.crate_or_deps(c);
44        }
45
46        run.path("library")
47    }
48
49    fn make_run(run: RunConfig<'_>) {
50        let crates = std_crates_for_run_make(&run);
51        run.builder.ensure(Std {
52            build_compiler: prepare_compiler_for_check(run.builder, run.target, Mode::Std),
53            target: run.target,
54            crates,
55        });
56    }
57
58    fn run(self, builder: &Builder<'_>) {
59        if !builder.download_rustc() && builder.config.skip_std_check_if_no_download_rustc {
60            eprintln!(
61                "WARNING: `--skip-std-check-if-no-download-rustc` flag was passed and `rust.download-rustc` is not available. Skipping."
62            );
63            return;
64        }
65
66        let build_compiler = self.build_compiler;
67        let stage = build_compiler.stage;
68        let target = self.target;
69
70        let mut cargo = builder::Cargo::new(
71            builder,
72            build_compiler,
73            Mode::Std,
74            SourceType::InTree,
75            target,
76            Kind::Check,
77        );
78
79        std_cargo(builder, target, stage, &mut cargo);
80        if matches!(builder.config.cmd, Subcommand::Fix) {
81            // By default, cargo tries to fix all targets. Tell it not to fix tests until we've added `test` to the sysroot.
82            cargo.arg("--lib");
83        }
84
85        for krate in &*self.crates {
86            cargo.arg("-p").arg(krate);
87        }
88
89        let _guard = builder.msg_check(
90            format_args!("library artifacts{}", crate_description(&self.crates)),
91            target,
92            Some(stage),
93        );
94
95        let stamp = build_stamp::libstd_stamp(builder, build_compiler, target).with_prefix("check");
96        run_cargo(builder, cargo, builder.config.free_args.clone(), &stamp, vec![], true, false);
97
98        drop(_guard);
99
100        // don't check test dependencies if we haven't built libtest
101        if !self.crates.iter().any(|krate| krate == "test") {
102            return;
103        }
104
105        // Then run cargo again, once we've put the rmeta files for the library
106        // crates into the sysroot. This is needed because e.g., core's tests
107        // depend on `libtest` -- Cargo presumes it will exist, but it doesn't
108        // since we initialize with an empty sysroot.
109        //
110        // Currently only the "libtest" tree of crates does this.
111        let mut cargo = builder::Cargo::new(
112            builder,
113            build_compiler,
114            Mode::Std,
115            SourceType::InTree,
116            target,
117            Kind::Check,
118        );
119
120        std_cargo(builder, target, build_compiler.stage, &mut cargo);
121
122        // Explicitly pass -p for all dependencies krates -- this will force cargo
123        // to also check the tests/benches/examples for these crates, rather
124        // than just the leaf crate.
125        for krate in &*self.crates {
126            cargo.arg("-p").arg(krate);
127        }
128
129        let stamp =
130            build_stamp::libstd_stamp(builder, build_compiler, target).with_prefix("check-test");
131        let _guard = builder.msg_check("library test/bench/example targets", target, Some(stage));
132        run_cargo(builder, cargo, builder.config.free_args.clone(), &stamp, vec![], true, false);
133    }
134
135    fn metadata(&self) -> Option<StepMetadata> {
136        Some(StepMetadata::check("std", self.target).built_by(self.build_compiler))
137    }
138}
139
140/// Checks rustc using `build_compiler` and copies the built
141/// .rmeta files into the sysroot of `build_compiler`.
142#[derive(Debug, Clone, PartialEq, Eq, Hash)]
143pub struct Rustc {
144    /// Compiler that will check this rustc.
145    pub build_compiler: Compiler,
146    pub target: TargetSelection,
147    /// Whether to build only a subset of crates.
148    ///
149    /// This shouldn't be used from other steps; see the comment on [`compile::Rustc`].
150    ///
151    /// [`compile::Rustc`]: crate::core::build_steps::compile::Rustc
152    crates: Vec<String>,
153}
154
155impl Rustc {
156    pub fn new(builder: &Builder<'_>, build_compiler: Compiler, target: TargetSelection) -> Self {
157        let crates = builder
158            .in_tree_crates("rustc-main", Some(target))
159            .into_iter()
160            .map(|krate| krate.name.to_string())
161            .collect();
162        Self { build_compiler, target, crates }
163    }
164}
165
166impl Step for Rustc {
167    type Output = ();
168    const ONLY_HOSTS: bool = true;
169    const DEFAULT: bool = true;
170
171    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
172        run.crate_or_deps("rustc-main").path("compiler")
173    }
174
175    fn make_run(run: RunConfig<'_>) {
176        let crates = run.make_run_crates(Alias::Compiler);
177        run.builder.ensure(Rustc {
178            target: run.target,
179            build_compiler: prepare_compiler_for_check(run.builder, run.target, Mode::Rustc),
180            crates,
181        });
182    }
183
184    /// Check the compiler.
185    ///
186    /// This will check the compiler for a particular stage of the build using
187    /// the `compiler` targeting the `target` architecture. The artifacts
188    /// created will also be linked into the sysroot directory.
189    ///
190    /// If we check a stage 2 compiler, we will have to first build a stage 1 compiler to check it.
191    fn run(self, builder: &Builder<'_>) {
192        let build_compiler = self.build_compiler;
193        let target = self.target;
194
195        // Build host std for compiling build scripts
196        builder.std(build_compiler, build_compiler.host);
197
198        // Build target std so that the checked rustc can link to it during the check
199        // FIXME: maybe we can a way to only do a check of std here?
200        // But for that we would have to copy the stdlib rmetas to the sysroot of the build
201        // compiler, which conflicts with std rlibs, if we also build std.
202        builder.std(build_compiler, target);
203
204        let mut cargo = builder::Cargo::new(
205            builder,
206            build_compiler,
207            Mode::Rustc,
208            SourceType::InTree,
209            target,
210            Kind::Check,
211        );
212
213        rustc_cargo(builder, &mut cargo, target, &build_compiler, &self.crates);
214
215        // Explicitly pass -p for all compiler crates -- this will force cargo
216        // to also check the tests/benches/examples for these crates, rather
217        // than just the leaf crate.
218        for krate in &*self.crates {
219            cargo.arg("-p").arg(krate);
220        }
221
222        let _guard = builder.msg_check(
223            format_args!("compiler artifacts{}", crate_description(&self.crates)),
224            target,
225            None,
226        );
227
228        let stamp =
229            build_stamp::librustc_stamp(builder, build_compiler, target).with_prefix("check");
230
231        run_cargo(builder, cargo, builder.config.free_args.clone(), &stamp, vec![], true, false);
232
233        let libdir = builder.sysroot_target_libdir(build_compiler, target);
234        let hostdir = builder.sysroot_target_libdir(build_compiler, build_compiler.host);
235        add_to_sysroot(builder, &libdir, &hostdir, &stamp);
236    }
237
238    fn metadata(&self) -> Option<StepMetadata> {
239        Some(StepMetadata::check("rustc", self.target).built_by(self.build_compiler))
240    }
241}
242
243/// Prepares a compiler that will check something with the given `mode`.
244fn prepare_compiler_for_check(
245    builder: &Builder<'_>,
246    target: TargetSelection,
247    mode: Mode,
248) -> Compiler {
249    let host = builder.host_target;
250    match mode {
251        Mode::ToolBootstrap => builder.compiler(0, host),
252        Mode::ToolStd => {
253            // These tools require the local standard library to be checked
254            let build_compiler = builder.compiler(builder.top_stage, host);
255
256            // We need to build the host stdlib to check the tool itself.
257            // We need to build the target stdlib so that the tool can link to it.
258            builder.std(build_compiler, host);
259            // We could only check this library in theory, but `check::Std` doesn't copy rmetas
260            // into `build_compiler`'s sysroot to avoid clashes with `.rlibs`, so we build it
261            // instead.
262            builder.std(build_compiler, target);
263            build_compiler
264        }
265        Mode::ToolRustc | Mode::Codegen => {
266            // FIXME: this is a hack, see description of Mode::Rustc below
267            let stage = if host == target { builder.top_stage - 1 } else { builder.top_stage };
268            // When checking tool stage N, we check it with compiler stage N-1
269            let build_compiler = builder.compiler(stage, host);
270            builder.ensure(Rustc::new(builder, build_compiler, target));
271            build_compiler
272        }
273        Mode::Rustc => {
274            // This is a horrible hack, because we actually change the compiler stage numbering
275            // here. If you do `x check --stage 1 --host FOO`, we build stage 1 host rustc,
276            // and use that to check stage 1 FOO rustc (which actually makes that stage 2 FOO
277            // rustc).
278            //
279            // FIXME: remove this and either fix cross-compilation check on stage 2 (which has a
280            // myriad of other problems) or disable cross-checking on stage 1.
281            let stage = if host == target { builder.top_stage - 1 } else { builder.top_stage };
282            builder.compiler(stage, host)
283        }
284        Mode::Std => {
285            // When checking std stage N, we want to do it with the stage N compiler
286            // Note: we don't need to build the host stdlib here, because when compiling std, the
287            // stage 0 stdlib is used to compile build scripts and proc macros.
288            builder.compiler(builder.top_stage, host)
289        }
290    }
291}
292
293/// Checks a single codegen backend.
294#[derive(Debug, Clone, PartialEq, Eq, Hash)]
295pub struct CodegenBackend {
296    pub build_compiler: Compiler,
297    pub target: TargetSelection,
298    pub backend: &'static str,
299}
300
301impl Step for CodegenBackend {
302    type Output = ();
303    const ONLY_HOSTS: bool = true;
304    const DEFAULT: bool = true;
305
306    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
307        run.paths(&["compiler/rustc_codegen_cranelift", "compiler/rustc_codegen_gcc"])
308    }
309
310    fn make_run(run: RunConfig<'_>) {
311        // FIXME: only check the backend(s) that were actually selected in run.paths
312        let build_compiler = prepare_compiler_for_check(run.builder, run.target, Mode::Codegen);
313        for &backend in &["cranelift", "gcc"] {
314            run.builder.ensure(CodegenBackend { build_compiler, target: run.target, backend });
315        }
316    }
317
318    fn run(self, builder: &Builder<'_>) {
319        // FIXME: remove once https://github.com/rust-lang/rust/issues/112393 is resolved
320        if builder.build.config.vendor && self.backend == "gcc" {
321            println!("Skipping checking of `rustc_codegen_gcc` with vendoring enabled.");
322            return;
323        }
324
325        let build_compiler = self.build_compiler;
326        let target = self.target;
327        let backend = self.backend;
328
329        let mut cargo = builder::Cargo::new(
330            builder,
331            build_compiler,
332            Mode::Codegen,
333            SourceType::InTree,
334            target,
335            builder.kind,
336        );
337
338        cargo
339            .arg("--manifest-path")
340            .arg(builder.src.join(format!("compiler/rustc_codegen_{backend}/Cargo.toml")));
341        rustc_cargo_env(builder, &mut cargo, target, build_compiler.stage);
342
343        let _guard = builder.msg_check(format!("rustc_codegen_{backend}"), target, None);
344
345        let stamp = build_stamp::codegen_backend_stamp(builder, build_compiler, target, backend)
346            .with_prefix("check");
347
348        run_cargo(builder, cargo, builder.config.free_args.clone(), &stamp, vec![], true, false);
349    }
350
351    fn metadata(&self) -> Option<StepMetadata> {
352        Some(StepMetadata::check(self.backend, self.target).built_by(self.build_compiler))
353    }
354}
355
356/// Checks Rust analyzer that links to .rmetas from a checked rustc.
357#[derive(Debug, Clone, PartialEq, Eq, Hash)]
358pub struct RustAnalyzer {
359    pub build_compiler: Compiler,
360    pub target: TargetSelection,
361}
362
363impl Step for RustAnalyzer {
364    type Output = ();
365    const ONLY_HOSTS: bool = true;
366    const DEFAULT: bool = true;
367
368    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
369        let builder = run.builder;
370        run.path("src/tools/rust-analyzer").default_condition(
371            builder
372                .config
373                .tools
374                .as_ref()
375                .is_none_or(|tools| tools.iter().any(|tool| tool == "rust-analyzer")),
376        )
377    }
378
379    fn make_run(run: RunConfig<'_>) {
380        let build_compiler = prepare_compiler_for_check(run.builder, run.target, Mode::ToolRustc);
381        run.builder.ensure(RustAnalyzer { build_compiler, target: run.target });
382    }
383
384    fn run(self, builder: &Builder<'_>) {
385        let build_compiler = self.build_compiler;
386        let target = self.target;
387
388        let mut cargo = prepare_tool_cargo(
389            builder,
390            build_compiler,
391            Mode::ToolRustc,
392            target,
393            builder.kind,
394            "src/tools/rust-analyzer",
395            SourceType::InTree,
396            &["in-rust-tree".to_owned()],
397        );
398
399        cargo.allow_features(crate::core::build_steps::tool::RustAnalyzer::ALLOW_FEATURES);
400
401        cargo.arg("--bins");
402        cargo.arg("--tests");
403        cargo.arg("--benches");
404
405        // Cargo's output path in a given stage, compiled by a particular
406        // compiler for the specified target.
407        let stamp = BuildStamp::new(&builder.cargo_out(build_compiler, Mode::ToolRustc, target))
408            .with_prefix("rust-analyzer-check");
409
410        let _guard = builder.msg_check("rust-analyzer artifacts", target, None);
411        run_cargo(builder, cargo, builder.config.free_args.clone(), &stamp, vec![], true, false);
412    }
413
414    fn metadata(&self) -> Option<StepMetadata> {
415        Some(StepMetadata::check("rust-analyzer", self.target).built_by(self.build_compiler))
416    }
417}
418
419/// Compiletest is implicitly "checked" when it gets built in order to run tests,
420/// so this is mainly for people working on compiletest to run locally.
421#[derive(Debug, Clone, PartialEq, Eq, Hash)]
422pub struct Compiletest {
423    pub target: TargetSelection,
424}
425
426impl Step for Compiletest {
427    type Output = ();
428    const ONLY_HOSTS: bool = true;
429    const DEFAULT: bool = false;
430
431    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
432        run.path("src/tools/compiletest")
433    }
434
435    fn make_run(run: RunConfig<'_>) {
436        run.builder.ensure(Compiletest { target: run.target });
437    }
438
439    fn run(self, builder: &Builder<'_>) {
440        let mode = if builder.config.compiletest_use_stage0_libtest {
441            Mode::ToolBootstrap
442        } else {
443            Mode::ToolStd
444        };
445        let build_compiler = prepare_compiler_for_check(builder, self.target, mode);
446
447        let mut cargo = prepare_tool_cargo(
448            builder,
449            build_compiler,
450            mode,
451            self.target,
452            builder.kind,
453            "src/tools/compiletest",
454            SourceType::InTree,
455            &[],
456        );
457
458        cargo.allow_features(COMPILETEST_ALLOW_FEATURES);
459
460        cargo.arg("--all-targets");
461
462        let stamp = BuildStamp::new(&builder.cargo_out(build_compiler, mode, self.target))
463            .with_prefix("compiletest-check");
464
465        let _guard = builder.msg_check("compiletest artifacts", self.target, None);
466        run_cargo(builder, cargo, builder.config.free_args.clone(), &stamp, vec![], true, false);
467    }
468
469    fn metadata(&self) -> Option<StepMetadata> {
470        Some(StepMetadata::check("compiletest", self.target))
471    }
472}
473
474macro_rules! tool_check_step {
475    (
476        $name:ident {
477            // The part of this path after the final '/' is also used as a display name.
478            path: $path:literal
479            $(, alt_path: $alt_path:literal )*
480            , mode: $mode:path
481            $(, allow_features: $allow_features:expr )?
482            $(, default: $default:literal )?
483            $( , )?
484        }
485    ) => {
486        #[derive(Debug, Clone, PartialEq, Eq, Hash)]
487        pub struct $name {
488            pub build_compiler: Compiler,
489            pub target: TargetSelection,
490        }
491
492        impl Step for $name {
493            type Output = ();
494            const ONLY_HOSTS: bool = true;
495            /// Most of the tool-checks using this macro are run by default.
496            const DEFAULT: bool = true $( && $default )?;
497
498            fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
499                run.paths(&[ $path, $( $alt_path ),* ])
500            }
501
502            fn make_run(run: RunConfig<'_>) {
503                let target = run.target;
504                let build_compiler = prepare_compiler_for_check(run.builder, target, $mode);
505
506                // It doesn't make sense to cross-check bootstrap tools
507                if $mode == Mode::ToolBootstrap && target != run.builder.host_target {
508                    println!("WARNING: not checking bootstrap tool {} for target {target} as it is a bootstrap (host-only) tool", stringify!($path));
509                    return;
510                };
511
512                run.builder.ensure($name { target, build_compiler });
513            }
514
515            fn run(self, builder: &Builder<'_>) {
516                let Self { target, build_compiler } = self;
517                let allow_features = {
518                    let mut _value = "";
519                    $( _value = $allow_features; )?
520                    _value
521                };
522                run_tool_check_step(builder, build_compiler, target, $path, $mode, allow_features);
523            }
524
525            fn metadata(&self) -> Option<StepMetadata> {
526                Some(StepMetadata::check(stringify!($name), self.target).built_by(self.build_compiler))
527            }
528        }
529    }
530}
531
532/// Used by the implementation of `Step::run` in `tool_check_step!`.
533fn run_tool_check_step(
534    builder: &Builder<'_>,
535    build_compiler: Compiler,
536    target: TargetSelection,
537    path: &str,
538    mode: Mode,
539    allow_features: &str,
540) {
541    let display_name = path.rsplit('/').next().unwrap();
542
543    let mut cargo = prepare_tool_cargo(
544        builder,
545        build_compiler,
546        mode,
547        target,
548        builder.kind,
549        path,
550        // Currently, all of the tools that use this macro/function are in-tree.
551        // If support for out-of-tree tools is re-added in the future, those
552        // steps should probably be marked non-default so that the default
553        // checks aren't affected by toolstate being broken.
554        SourceType::InTree,
555        &[],
556    );
557    cargo.allow_features(allow_features);
558
559    // FIXME: check bootstrap doesn't currently work with --all-targets
560    cargo.arg("--all-targets");
561
562    let stamp = BuildStamp::new(&builder.cargo_out(build_compiler, mode, target))
563        .with_prefix(&format!("{display_name}-check"));
564
565    let stage = match mode {
566        // Mode::ToolRustc is included here because of how msg_sysroot_tool prints stages
567        Mode::Std | Mode::ToolRustc => build_compiler.stage,
568        _ => build_compiler.stage + 1,
569    };
570
571    let _guard =
572        builder.msg_tool(builder.kind, mode, display_name, stage, &build_compiler.host, &target);
573    run_cargo(builder, cargo, builder.config.free_args.clone(), &stamp, vec![], true, false);
574}
575
576tool_check_step!(Rustdoc {
577    path: "src/tools/rustdoc",
578    alt_path: "src/librustdoc",
579    mode: Mode::ToolRustc
580});
581// Clippy, miri and Rustfmt are hybrids. They are external tools, but use a git subtree instead
582// of a submodule. Since the SourceType only drives the deny-warnings
583// behavior, treat it as in-tree so that any new warnings in clippy will be
584// rejected.
585tool_check_step!(Clippy { path: "src/tools/clippy", mode: Mode::ToolRustc });
586tool_check_step!(Miri { path: "src/tools/miri", mode: Mode::ToolRustc });
587tool_check_step!(CargoMiri { path: "src/tools/miri/cargo-miri", mode: Mode::ToolRustc });
588tool_check_step!(Rustfmt { path: "src/tools/rustfmt", mode: Mode::ToolRustc });
589tool_check_step!(MiroptTestTools {
590    path: "src/tools/miropt-test-tools",
591    mode: Mode::ToolBootstrap
592});
593// We want to test the local std
594tool_check_step!(TestFloatParse {
595    path: "src/tools/test-float-parse",
596    mode: Mode::ToolStd,
597    allow_features: tool::TestFloatParse::ALLOW_FEATURES
598});
599tool_check_step!(FeaturesStatusDump {
600    path: "src/tools/features-status-dump",
601    mode: Mode::ToolBootstrap
602});
603
604tool_check_step!(Bootstrap { path: "src/bootstrap", mode: Mode::ToolBootstrap, default: false });
605
606// `run-make-support` will be built as part of suitable run-make compiletest test steps, but support
607// check to make it easier to work on.
608tool_check_step!(RunMakeSupport {
609    path: "src/tools/run-make-support",
610    mode: Mode::ToolBootstrap,
611    default: false
612});
613
614tool_check_step!(CoverageDump {
615    path: "src/tools/coverage-dump",
616    mode: Mode::ToolBootstrap,
617    default: false
618});