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