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