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