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