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