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