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