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 ONLY_HOSTS: 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/// Checks a single codegen backend.
318#[derive(Debug, Clone, PartialEq, Eq, Hash)]
319pub struct CodegenBackend {
320    pub build_compiler: Compiler,
321    pub target: TargetSelection,
322    pub backend: CodegenBackendKind,
323}
324
325impl Step for CodegenBackend {
326    type Output = ();
327    const ONLY_HOSTS: bool = true;
328    const DEFAULT: bool = true;
329
330    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
331        run.paths(&["compiler/rustc_codegen_cranelift", "compiler/rustc_codegen_gcc"])
332    }
333
334    fn make_run(run: RunConfig<'_>) {
335        // FIXME: only check the backend(s) that were actually selected in run.paths
336        let build_compiler = prepare_compiler_for_check(run.builder, run.target, Mode::Codegen);
337        for backend in [CodegenBackendKind::Cranelift, CodegenBackendKind::Gcc] {
338            run.builder.ensure(CodegenBackend { build_compiler, target: run.target, backend });
339        }
340    }
341
342    fn run(self, builder: &Builder<'_>) {
343        // FIXME: remove once https://github.com/rust-lang/rust/issues/112393 is resolved
344        if builder.build.config.vendor && self.backend.is_gcc() {
345            println!("Skipping checking of `rustc_codegen_gcc` with vendoring enabled.");
346            return;
347        }
348
349        let build_compiler = self.build_compiler;
350        let target = self.target;
351        let backend = self.backend;
352
353        let mut cargo = builder::Cargo::new(
354            builder,
355            build_compiler,
356            Mode::Codegen,
357            SourceType::InTree,
358            target,
359            builder.kind,
360        );
361
362        cargo
363            .arg("--manifest-path")
364            .arg(builder.src.join(format!("compiler/{}/Cargo.toml", backend.crate_name())));
365        rustc_cargo_env(builder, &mut cargo, target);
366
367        let _guard = builder.msg(
368            Kind::Check,
369            backend.crate_name(),
370            Mode::Codegen,
371            self.build_compiler,
372            target,
373        );
374
375        let stamp = build_stamp::codegen_backend_stamp(builder, build_compiler, target, &backend)
376            .with_prefix("check");
377
378        run_cargo(builder, cargo, builder.config.free_args.clone(), &stamp, vec![], true, false);
379    }
380
381    fn metadata(&self) -> Option<StepMetadata> {
382        Some(
383            StepMetadata::check(&self.backend.crate_name(), self.target)
384                .built_by(self.build_compiler),
385        )
386    }
387}
388
389macro_rules! tool_check_step {
390    (
391        $name:ident {
392            // The part of this path after the final '/' is also used as a display name.
393            path: $path:literal
394            $(, alt_path: $alt_path:literal )*
395            // Closure that returns `Mode` based on the passed `&Builder<'_>`
396            , mode: $mode:expr
397            // Subset of nightly features that are allowed to be used when checking
398            $(, allow_features: $allow_features:expr )?
399            // Features that should be enabled when checking
400            $(, enable_features: [$($enable_features:expr),*] )?
401            $(, default: $default:literal )?
402            $( , )?
403        }
404    ) => {
405        #[derive(Debug, Clone, PartialEq, Eq, Hash)]
406        pub struct $name {
407            pub build_compiler: Compiler,
408            pub target: TargetSelection,
409        }
410
411        impl Step for $name {
412            type Output = ();
413            const ONLY_HOSTS: bool = true;
414            /// Most of the tool-checks using this macro are run by default.
415            const DEFAULT: bool = true $( && $default )?;
416
417            fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
418                run.paths(&[ $path, $( $alt_path ),* ])
419            }
420
421            fn make_run(run: RunConfig<'_>) {
422                let target = run.target;
423                let builder = run.builder;
424                let mode = $mode(builder);
425
426                let build_compiler = prepare_compiler_for_check(run.builder, target, mode);
427
428                // It doesn't make sense to cross-check bootstrap tools
429                if mode == Mode::ToolBootstrap && target != run.builder.host_target {
430                    println!("WARNING: not checking bootstrap tool {} for target {target} as it is a bootstrap (host-only) tool", stringify!($path));
431                    return;
432                };
433
434                run.builder.ensure($name { target, build_compiler });
435            }
436
437            fn run(self, builder: &Builder<'_>) {
438                let Self { target, build_compiler } = self;
439                let allow_features = {
440                    let mut _value = "";
441                    $( _value = $allow_features; )?
442                    _value
443                };
444                let extra_features: &[&str] = &[$($($enable_features),*)?];
445                let mode = $mode(builder);
446                run_tool_check_step(builder, build_compiler, target, $path, mode, allow_features, extra_features);
447            }
448
449            fn metadata(&self) -> Option<StepMetadata> {
450                Some(StepMetadata::check(stringify!($name), self.target).built_by(self.build_compiler))
451            }
452        }
453    }
454}
455
456/// Used by the implementation of `Step::run` in `tool_check_step!`.
457fn run_tool_check_step(
458    builder: &Builder<'_>,
459    build_compiler: Compiler,
460    target: TargetSelection,
461    path: &str,
462    mode: Mode,
463    allow_features: &str,
464    extra_features: &[&str],
465) {
466    let display_name = path.rsplit('/').next().unwrap();
467
468    let extra_features = extra_features.iter().map(|f| f.to_string()).collect::<Vec<String>>();
469    let mut cargo = prepare_tool_cargo(
470        builder,
471        build_compiler,
472        mode,
473        target,
474        builder.kind,
475        path,
476        // Currently, all of the tools that use this macro/function are in-tree.
477        // If support for out-of-tree tools is re-added in the future, those
478        // steps should probably be marked non-default so that the default
479        // checks aren't affected by toolstate being broken.
480        SourceType::InTree,
481        &extra_features,
482    );
483    cargo.allow_features(allow_features);
484
485    // FIXME: check bootstrap doesn't currently work when multiple targets are checked
486    // FIXME: rust-analyzer does not work with --all-targets
487    if display_name == "rust-analyzer" {
488        cargo.arg("--bins");
489        cargo.arg("--tests");
490        cargo.arg("--benches");
491    } else {
492        cargo.arg("--all-targets");
493    }
494
495    let stamp = BuildStamp::new(&builder.cargo_out(build_compiler, mode, target))
496        .with_prefix(&format!("{display_name}-check"));
497
498    let _guard = builder.msg(builder.kind, display_name, mode, build_compiler, target);
499    run_cargo(builder, cargo, builder.config.free_args.clone(), &stamp, vec![], true, false);
500}
501
502tool_check_step!(Rustdoc {
503    path: "src/tools/rustdoc",
504    alt_path: "src/librustdoc",
505    mode: |_builder| Mode::ToolRustc
506});
507// Clippy, miri and Rustfmt are hybrids. They are external tools, but use a git subtree instead
508// of a submodule. Since the SourceType only drives the deny-warnings
509// behavior, treat it as in-tree so that any new warnings in clippy will be
510// rejected.
511tool_check_step!(Clippy { path: "src/tools/clippy", mode: |_builder| Mode::ToolRustc });
512tool_check_step!(Miri { path: "src/tools/miri", mode: |_builder| Mode::ToolRustc });
513tool_check_step!(CargoMiri { path: "src/tools/miri/cargo-miri", mode: |_builder| Mode::ToolRustc });
514tool_check_step!(Rustfmt { path: "src/tools/rustfmt", mode: |_builder| Mode::ToolRustc });
515tool_check_step!(RustAnalyzer {
516    path: "src/tools/rust-analyzer",
517    mode: |_builder| Mode::ToolRustc,
518    allow_features: tool::RustAnalyzer::ALLOW_FEATURES,
519    enable_features: ["in-rust-tree"],
520});
521tool_check_step!(MiroptTestTools {
522    path: "src/tools/miropt-test-tools",
523    mode: |_builder| Mode::ToolBootstrap
524});
525// We want to test the local std
526tool_check_step!(TestFloatParse {
527    path: "src/tools/test-float-parse",
528    mode: |_builder| Mode::ToolStd,
529    allow_features: tool::TestFloatParse::ALLOW_FEATURES
530});
531tool_check_step!(FeaturesStatusDump {
532    path: "src/tools/features-status-dump",
533    mode: |_builder| Mode::ToolBootstrap
534});
535
536tool_check_step!(Bootstrap {
537    path: "src/bootstrap",
538    mode: |_builder| Mode::ToolBootstrap,
539    default: false
540});
541
542// `run-make-support` will be built as part of suitable run-make compiletest test steps, but support
543// check to make it easier to work on.
544tool_check_step!(RunMakeSupport {
545    path: "src/tools/run-make-support",
546    mode: |_builder| Mode::ToolBootstrap,
547    default: false
548});
549
550tool_check_step!(CoverageDump {
551    path: "src/tools/coverage-dump",
552    mode: |_builder| Mode::ToolBootstrap,
553    default: false
554});
555
556// Compiletest is implicitly "checked" when it gets built in order to run tests,
557// so this is mainly for people working on compiletest to run locally.
558tool_check_step!(Compiletest {
559    path: "src/tools/compiletest",
560    mode: |builder: &Builder<'_>| if builder.config.compiletest_use_stage0_libtest {
561        Mode::ToolBootstrap
562    } else {
563        Mode::ToolStd
564    },
565    allow_features: COMPILETEST_ALLOW_FEATURES,
566    default: false,
567});
568
569tool_check_step!(Linkchecker {
570    path: "src/tools/linkchecker",
571    mode: |_builder| Mode::ToolBootstrap,
572    default: false
573});