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