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