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::{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    /// Override `Builder::kind` on cargo invocations.
24    ///
25    /// By default, `Builder::kind` is propagated as the subcommand to the cargo invocations.
26    /// However, there are cases when this is not desirable. For example, when running `x clippy $tool_name`,
27    /// passing `Builder::kind` to cargo invocations would run clippy on the entire compiler and library,
28    /// which is not useful if we only want to lint a few crates with specific rules.
29    override_build_kind: Option<Kind>,
30}
31
32impl Std {
33    pub fn new(target: TargetSelection) -> Self {
34        Self { target, crates: vec![], override_build_kind: None }
35    }
36
37    pub fn build_kind(mut self, kind: Option<Kind>) -> Self {
38        self.override_build_kind = kind;
39        self
40    }
41}
42
43impl Step for Std {
44    type Output = ();
45    const DEFAULT: bool = true;
46
47    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
48        run.crate_or_deps("sysroot")
49            .crate_or_deps("coretests")
50            .crate_or_deps("alloctests")
51            .path("library")
52    }
53
54    fn make_run(run: RunConfig<'_>) {
55        let crates = std_crates_for_run_make(&run);
56        run.builder.ensure(Std { target: run.target, crates, override_build_kind: None });
57    }
58
59    fn run(self, builder: &Builder<'_>) {
60        builder.require_submodule("library/stdarch", None);
61
62        let target = self.target;
63        let compiler = builder.compiler(builder.top_stage, builder.config.build);
64
65        let mut cargo = builder::Cargo::new(
66            builder,
67            compiler,
68            Mode::Std,
69            SourceType::InTree,
70            target,
71            self.override_build_kind.unwrap_or(builder.kind),
72        );
73
74        std_cargo(builder, target, compiler.stage, &mut cargo);
75        if matches!(builder.config.cmd, Subcommand::Fix) {
76            // By default, cargo tries to fix all targets. Tell it not to fix tests until we've added `test` to the sysroot.
77            cargo.arg("--lib");
78        }
79
80        for krate in &*self.crates {
81            cargo.arg("-p").arg(krate);
82        }
83
84        let _guard = builder.msg_check(
85            format_args!("library artifacts{}", crate_description(&self.crates)),
86            target,
87        );
88
89        let stamp = build_stamp::libstd_stamp(builder, compiler, target).with_prefix("check");
90        run_cargo(builder, cargo, builder.config.free_args.clone(), &stamp, vec![], true, false);
91
92        // We skip populating the sysroot in non-zero stage because that'll lead
93        // to rlib/rmeta conflicts if std gets built during this session.
94        if compiler.stage == 0 {
95            let libdir = builder.sysroot_target_libdir(compiler, target);
96            let hostdir = builder.sysroot_target_libdir(compiler, compiler.host);
97            add_to_sysroot(builder, &libdir, &hostdir, &stamp);
98        }
99        drop(_guard);
100
101        // don't run on std twice with x.py clippy
102        // don't check test dependencies if we haven't built libtest
103        if builder.kind == Kind::Clippy || !self.crates.iter().any(|krate| krate == "test") {
104            return;
105        }
106
107        // Then run cargo again, once we've put the rmeta files for the library
108        // crates into the sysroot. This is needed because e.g., core's tests
109        // depend on `libtest` -- Cargo presumes it will exist, but it doesn't
110        // since we initialize with an empty sysroot.
111        //
112        // Currently only the "libtest" tree of crates does this.
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        // If we're not in stage 0, tests and examples will fail to compile
123        // from `core` definitions being loaded from two different `libcore`
124        // .rmeta and .rlib files.
125        if compiler.stage == 0 {
126            cargo.arg("--all-targets");
127        }
128
129        std_cargo(builder, target, compiler.stage, &mut cargo);
130
131        // Explicitly pass -p for all dependencies krates -- this will force cargo
132        // to also check the tests/benches/examples for these crates, rather
133        // than just the leaf crate.
134        for krate in &*self.crates {
135            cargo.arg("-p").arg(krate);
136        }
137
138        let stamp = build_stamp::libstd_stamp(builder, compiler, target).with_prefix("check-test");
139        let _guard = builder.msg_check("library test/bench/example targets", target);
140        run_cargo(builder, cargo, builder.config.free_args.clone(), &stamp, vec![], true, false);
141    }
142}
143
144#[derive(Debug, Clone, PartialEq, Eq, Hash)]
145pub struct Rustc {
146    pub target: TargetSelection,
147    /// Whether to build only a subset of crates.
148    ///
149    /// This shouldn't be used from other steps; see the comment on [`compile::Rustc`].
150    ///
151    /// [`compile::Rustc`]: crate::core::build_steps::compile::Rustc
152    crates: Vec<String>,
153    /// Override `Builder::kind` on cargo invocations.
154    ///
155    /// By default, `Builder::kind` is propagated as the subcommand to the cargo invocations.
156    /// However, there are cases when this is not desirable. For example, when running `x clippy $tool_name`,
157    /// passing `Builder::kind` to cargo invocations would run clippy on the entire compiler and library,
158    /// which is not useful if we only want to lint a few crates with specific rules.
159    override_build_kind: Option<Kind>,
160}
161
162impl Rustc {
163    pub fn new(target: TargetSelection, builder: &Builder<'_>) -> Self {
164        let crates = builder
165            .in_tree_crates("rustc-main", Some(target))
166            .into_iter()
167            .map(|krate| krate.name.to_string())
168            .collect();
169        Self { target, crates, override_build_kind: None }
170    }
171
172    pub fn build_kind(mut self, build_kind: Option<Kind>) -> Self {
173        self.override_build_kind = build_kind;
174        self
175    }
176}
177
178impl Step for Rustc {
179    type Output = ();
180    const ONLY_HOSTS: bool = true;
181    const DEFAULT: bool = true;
182
183    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
184        run.crate_or_deps("rustc-main").path("compiler")
185    }
186
187    fn make_run(run: RunConfig<'_>) {
188        let crates = run.make_run_crates(Alias::Compiler);
189        run.builder.ensure(Rustc { target: run.target, crates, override_build_kind: None });
190    }
191
192    /// Builds the compiler.
193    ///
194    /// This will build the compiler for a particular stage of the build using
195    /// the `compiler` targeting the `target` architecture. The artifacts
196    /// created will also be linked into the sysroot directory.
197    fn run(self, builder: &Builder<'_>) {
198        let compiler = builder.compiler(builder.top_stage, builder.config.build);
199        let target = self.target;
200
201        if compiler.stage != 0 {
202            // If we're not in stage 0, then we won't have a std from the beta
203            // compiler around. That means we need to make sure there's one in
204            // the sysroot for the compiler to find. Otherwise, we're going to
205            // fail when building crates that need to generate code (e.g., build
206            // scripts and their dependencies).
207            builder.ensure(crate::core::build_steps::compile::Std::new(compiler, compiler.host));
208            builder.ensure(crate::core::build_steps::compile::Std::new(compiler, target));
209        } else {
210            builder.ensure(Std::new(target).build_kind(self.override_build_kind));
211        }
212
213        let mut cargo = builder::Cargo::new(
214            builder,
215            compiler,
216            Mode::Rustc,
217            SourceType::InTree,
218            target,
219            self.override_build_kind.unwrap_or(builder.kind),
220        );
221
222        rustc_cargo(builder, &mut cargo, target, &compiler, &self.crates);
223
224        // For ./x.py clippy, don't run with --all-targets because
225        // linting tests and benchmarks can produce very noisy results
226        if builder.kind != Kind::Clippy {
227            cargo.arg("--all-targets");
228        }
229
230        // Explicitly pass -p for all compiler crates -- this will force cargo
231        // to also check the tests/benches/examples for these crates, rather
232        // than just the leaf crate.
233        for krate in &*self.crates {
234            cargo.arg("-p").arg(krate);
235        }
236
237        let _guard = builder.msg_check(
238            format_args!("compiler artifacts{}", crate_description(&self.crates)),
239            target,
240        );
241
242        let stamp = build_stamp::librustc_stamp(builder, compiler, target).with_prefix("check");
243
244        run_cargo(builder, cargo, builder.config.free_args.clone(), &stamp, vec![], true, false);
245
246        let libdir = builder.sysroot_target_libdir(compiler, target);
247        let hostdir = builder.sysroot_target_libdir(compiler, compiler.host);
248        add_to_sysroot(builder, &libdir, &hostdir, &stamp);
249    }
250}
251
252#[derive(Debug, Clone, PartialEq, Eq, Hash)]
253pub struct CodegenBackend {
254    pub target: TargetSelection,
255    pub backend: &'static str,
256}
257
258impl Step for CodegenBackend {
259    type Output = ();
260    const ONLY_HOSTS: bool = true;
261    const DEFAULT: bool = true;
262
263    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
264        run.paths(&["compiler/rustc_codegen_cranelift", "compiler/rustc_codegen_gcc"])
265    }
266
267    fn make_run(run: RunConfig<'_>) {
268        for &backend in &["cranelift", "gcc"] {
269            run.builder.ensure(CodegenBackend { target: run.target, backend });
270        }
271    }
272
273    fn run(self, builder: &Builder<'_>) {
274        // FIXME: remove once https://github.com/rust-lang/rust/issues/112393 is resolved
275        if builder.build.config.vendor && self.backend == "gcc" {
276            println!("Skipping checking of `rustc_codegen_gcc` with vendoring enabled.");
277            return;
278        }
279
280        let compiler = builder.compiler(builder.top_stage, builder.config.build);
281        let target = self.target;
282        let backend = self.backend;
283
284        builder.ensure(Rustc::new(target, builder));
285
286        let mut cargo = builder::Cargo::new(
287            builder,
288            compiler,
289            Mode::Codegen,
290            SourceType::InTree,
291            target,
292            builder.kind,
293        );
294
295        cargo
296            .arg("--manifest-path")
297            .arg(builder.src.join(format!("compiler/rustc_codegen_{backend}/Cargo.toml")));
298        rustc_cargo_env(builder, &mut cargo, target, compiler.stage);
299
300        let _guard = builder.msg_check(backend, target);
301
302        let stamp = build_stamp::codegen_backend_stamp(builder, compiler, target, backend)
303            .with_prefix("check");
304
305        run_cargo(builder, cargo, builder.config.free_args.clone(), &stamp, vec![], true, false);
306    }
307}
308
309#[derive(Debug, Clone, PartialEq, Eq, Hash)]
310pub struct RustAnalyzer {
311    pub target: TargetSelection,
312}
313
314impl Step for RustAnalyzer {
315    type Output = ();
316    const ONLY_HOSTS: bool = true;
317    const DEFAULT: bool = true;
318
319    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
320        let builder = run.builder;
321        run.path("src/tools/rust-analyzer").default_condition(
322            builder
323                .config
324                .tools
325                .as_ref()
326                .is_none_or(|tools| tools.iter().any(|tool| tool == "rust-analyzer")),
327        )
328    }
329
330    fn make_run(run: RunConfig<'_>) {
331        run.builder.ensure(RustAnalyzer { target: run.target });
332    }
333
334    fn run(self, builder: &Builder<'_>) {
335        let compiler = builder.compiler(builder.top_stage, builder.config.build);
336        let target = self.target;
337
338        builder.ensure(Rustc::new(target, builder));
339
340        let mut cargo = prepare_tool_cargo(
341            builder,
342            compiler,
343            Mode::ToolRustc,
344            target,
345            builder.kind,
346            "src/tools/rust-analyzer",
347            SourceType::InTree,
348            &["in-rust-tree".to_owned()],
349        );
350
351        cargo.allow_features(crate::core::build_steps::tool::RustAnalyzer::ALLOW_FEATURES);
352
353        // For ./x.py clippy, don't check those targets because
354        // linting tests and benchmarks can produce very noisy results
355        if builder.kind != Kind::Clippy {
356            // can't use `--all-targets` because `--examples` doesn't work well
357            cargo.arg("--bins");
358            cargo.arg("--tests");
359            cargo.arg("--benches");
360        }
361
362        // Cargo's output path in a given stage, compiled by a particular
363        // compiler for the specified target.
364        let stamp = BuildStamp::new(&builder.cargo_out(compiler, Mode::ToolRustc, target))
365            .with_prefix("rust-analyzer-check");
366
367        let _guard = builder.msg_check("rust-analyzer artifacts", target);
368        run_cargo(builder, cargo, builder.config.free_args.clone(), &stamp, vec![], true, false);
369    }
370}
371
372macro_rules! tool_check_step {
373    (
374        $name:ident {
375            // The part of this path after the final '/' is also used as a display name.
376            path: $path:literal
377            $(, alt_path: $alt_path:literal )*
378            $(, default: $default:literal )?
379            $( , )?
380        }
381    ) => {
382        #[derive(Debug, Clone, PartialEq, Eq, Hash)]
383        pub struct $name {
384            pub target: TargetSelection,
385        }
386
387        impl Step for $name {
388            type Output = ();
389            const ONLY_HOSTS: bool = true;
390            /// Most of the tool-checks using this macro are run by default.
391            const DEFAULT: bool = true $( && $default )?;
392
393            fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
394                run.paths(&[ $path, $( $alt_path ),* ])
395            }
396
397            fn make_run(run: RunConfig<'_>) {
398                run.builder.ensure($name { target: run.target });
399            }
400
401            fn run(self, builder: &Builder<'_>) {
402                let Self { target } = self;
403                run_tool_check_step(builder, target, stringify!($name), $path);
404            }
405        }
406    }
407}
408
409/// Used by the implementation of `Step::run` in `tool_check_step!`.
410fn run_tool_check_step(
411    builder: &Builder<'_>,
412    target: TargetSelection,
413    step_type_name: &str,
414    path: &str,
415) {
416    let display_name = path.rsplit('/').next().unwrap();
417    let compiler = builder.compiler(builder.top_stage, builder.config.build);
418
419    builder.ensure(Rustc::new(target, builder));
420
421    let mut cargo = prepare_tool_cargo(
422        builder,
423        compiler,
424        Mode::ToolRustc,
425        target,
426        builder.kind,
427        path,
428        // Currently, all of the tools that use this macro/function are in-tree.
429        // If support for out-of-tree tools is re-added in the future, those
430        // steps should probably be marked non-default so that the default
431        // checks aren't affected by toolstate being broken.
432        SourceType::InTree,
433        &[],
434    );
435
436    // For ./x.py clippy, don't run with --all-targets because
437    // linting tests and benchmarks can produce very noisy results
438    if builder.kind != Kind::Clippy {
439        cargo.arg("--all-targets");
440    }
441
442    let stamp = BuildStamp::new(&builder.cargo_out(compiler, Mode::ToolRustc, target))
443        .with_prefix(&format!("{}-check", step_type_name.to_lowercase()));
444
445    let _guard = builder.msg_check(format!("{display_name} artifacts"), target);
446    run_cargo(builder, cargo, builder.config.free_args.clone(), &stamp, vec![], true, false);
447}
448
449tool_check_step!(Rustdoc { path: "src/tools/rustdoc", alt_path: "src/librustdoc" });
450// Clippy, miri and Rustfmt are hybrids. They are external tools, but use a git subtree instead
451// of a submodule. Since the SourceType only drives the deny-warnings
452// behavior, treat it as in-tree so that any new warnings in clippy will be
453// rejected.
454tool_check_step!(Clippy { path: "src/tools/clippy" });
455tool_check_step!(Miri { path: "src/tools/miri" });
456tool_check_step!(CargoMiri { path: "src/tools/miri/cargo-miri" });
457tool_check_step!(Rustfmt { path: "src/tools/rustfmt" });
458tool_check_step!(MiroptTestTools { path: "src/tools/miropt-test-tools" });
459tool_check_step!(TestFloatParse { path: "src/etc/test-float-parse" });
460tool_check_step!(FeaturesStatusDump { path: "src/tools/features-status-dump" });
461
462tool_check_step!(Bootstrap { path: "src/bootstrap", default: false });
463
464// `run-make-support` will be built as part of suitable run-make compiletest test steps, but support
465// check to make it easier to work on.
466tool_check_step!(RunMakeSupport { path: "src/tools/run-make-support", default: false });
467
468// Compiletest is implicitly "checked" when it gets built in order to run tests,
469// so this is mainly for people working on compiletest to run locally.
470tool_check_step!(Compiletest { path: "src/tools/compiletest", default: false });