1 //! Implementation of compiling the compiler and standard library, in "check"-based modes.
3 use crate::builder::{Builder, Kind, RunConfig, ShouldRun, Step};
4 use crate::cache::Interned;
5 use crate::compile::{add_to_sysroot, run_cargo, rustc_cargo, rustc_cargo_env, std_cargo};
6 use crate::config::TargetSelection;
7 use crate::tool::{prepare_tool_cargo, SourceType};
9 use crate::{Compiler, Mode, Subcommand};
10 use std::path::{Path, PathBuf};
12 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
14 pub target: TargetSelection,
17 /// Returns args for the subcommand itself (not for cargo)
18 fn args(builder: &Builder<'_>) -> Vec<String> {
19 fn strings<'a>(arr: &'a [&str]) -> impl Iterator<Item = String> + 'a {
20 arr.iter().copied().map(String::from)
23 if let Subcommand::Clippy {
30 } = &builder.config.cmd
32 // disable the most spammy clippy lints
33 let ignored_lints = vec![
34 "many_single_char_names", // there are a lot in stdarch
37 "missing_safety_doc", // almost 3K warnings
39 "needless_lifetimes", // people want to keep the lifetimes
40 "wrong_self_convention",
42 let mut args = vec![];
45 args.extend(strings(&[
46 "--fix", "-Zunstable-options",
47 // FIXME: currently, `--fix` gives an error while checking tests for libtest,
48 // possibly because libtest is not yet built in the sysroot.
49 // As a workaround, avoid checking tests and benches when passed --fix.
50 "--lib", "--bins", "--examples",
53 args.extend(strings(&["--", "--cap-lints", "warn"]));
54 args.extend(ignored_lints.iter().map(|lint| format!("-Aclippy::{}", lint)));
55 let mut clippy_lint_levels: Vec<String> = Vec::new();
56 clippy_lint_allow.iter().for_each(|v| clippy_lint_levels.push(format!("-A{}", v)));
57 clippy_lint_deny.iter().for_each(|v| clippy_lint_levels.push(format!("-D{}", v)));
58 clippy_lint_warn.iter().for_each(|v| clippy_lint_levels.push(format!("-W{}", v)));
59 clippy_lint_forbid.iter().for_each(|v| clippy_lint_levels.push(format!("-F{}", v)));
60 args.extend(clippy_lint_levels);
67 fn cargo_subcommand(kind: Kind) -> &'static str {
69 Kind::Check => "check",
70 Kind::Clippy => "clippy",
78 const DEFAULT: bool = true;
80 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
81 run.all_krates("test").path("library")
84 fn make_run(run: RunConfig<'_>) {
85 run.builder.ensure(Std { target: run.target });
88 fn run(self, builder: &Builder<'_>) {
89 builder.update_submodule(&Path::new("library").join("stdarch"));
91 let target = self.target;
92 let compiler = builder.compiler(builder.top_stage, builder.config.build);
94 let mut cargo = builder.cargo(
99 cargo_subcommand(builder.kind),
101 std_cargo(builder, target, compiler.stage, &mut cargo);
102 cargo.args(args(builder));
104 builder.info(&format!(
105 "Checking stage{} library artifacts ({} -> {})",
106 builder.top_stage, &compiler.host, target
108 run_cargo(builder, cargo, &libstd_stamp(builder, compiler, target), vec![], true);
110 // We skip populating the sysroot in non-zero stage because that'll lead
111 // to rlib/rmeta conflicts if std gets built during this session.
112 if compiler.stage == 0 {
113 let libdir = builder.sysroot_libdir(compiler, target);
114 let hostdir = builder.sysroot_libdir(compiler, compiler.host);
115 add_to_sysroot(&builder, &libdir, &hostdir, &libstd_stamp(builder, compiler, target));
118 // don't run on std twice with x.py clippy
119 if builder.kind == Kind::Clippy {
123 // Then run cargo again, once we've put the rmeta files for the library
124 // crates into the sysroot. This is needed because e.g., core's tests
125 // depend on `libtest` -- Cargo presumes it will exist, but it doesn't
126 // since we initialize with an empty sysroot.
128 // Currently only the "libtest" tree of crates does this.
129 let mut cargo = builder.cargo(
134 cargo_subcommand(builder.kind),
137 // If we're not in stage 0, tests and examples will fail to compile
138 // from `core` definitions being loaded from two different `libcore`
139 // .rmeta and .rlib files.
140 if compiler.stage == 0 {
141 cargo.arg("--all-targets");
144 std_cargo(builder, target, compiler.stage, &mut cargo);
146 // Explicitly pass -p for all dependencies krates -- this will force cargo
147 // to also check the tests/benches/examples for these crates, rather
148 // than just the leaf crate.
149 for krate in builder.in_tree_crates("test", Some(target)) {
150 cargo.arg("-p").arg(krate.name);
152 cargo.args(args(builder));
154 builder.info(&format!(
155 "Checking stage{} library test/bench/example targets ({} -> {})",
156 builder.top_stage, &compiler.host, target
158 run_cargo(builder, cargo, &libstd_test_stamp(builder, compiler, target), vec![], true);
162 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
164 pub target: TargetSelection,
167 impl Step for Rustc {
169 const ONLY_HOSTS: bool = true;
170 const DEFAULT: bool = true;
172 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
173 run.all_krates("rustc-main").path("compiler")
176 fn make_run(run: RunConfig<'_>) {
177 run.builder.ensure(Rustc { target: run.target });
180 /// Builds the compiler.
182 /// This will build the compiler for a particular stage of the build using
183 /// the `compiler` targeting the `target` architecture. The artifacts
184 /// created will also be linked into the sysroot directory.
185 fn run(self, builder: &Builder<'_>) {
186 let compiler = builder.compiler(builder.top_stage, builder.config.build);
187 let target = self.target;
189 if compiler.stage != 0 {
190 // If we're not in stage 0, then we won't have a std from the beta
191 // compiler around. That means we need to make sure there's one in
192 // the sysroot for the compiler to find. Otherwise, we're going to
193 // fail when building crates that need to generate code (e.g., build
194 // scripts and their dependencies).
195 builder.ensure(crate::compile::Std::new(compiler, compiler.host));
196 builder.ensure(crate::compile::Std::new(compiler, target));
198 builder.ensure(Std { target });
201 let mut cargo = builder.cargo(
206 cargo_subcommand(builder.kind),
208 rustc_cargo(builder, &mut cargo, target);
210 // For ./x.py clippy, don't run with --all-targets because
211 // linting tests and benchmarks can produce very noisy results
212 if builder.kind != Kind::Clippy {
213 cargo.arg("--all-targets");
216 // Explicitly pass -p for all compiler krates -- this will force cargo
217 // to also check the tests/benches/examples for these crates, rather
218 // than just the leaf crate.
219 for krate in builder.in_tree_crates("rustc-main", Some(target)) {
220 cargo.arg("-p").arg(krate.name);
222 cargo.args(args(builder));
224 builder.info(&format!(
225 "Checking stage{} compiler artifacts ({} -> {})",
226 builder.top_stage, &compiler.host, target
228 run_cargo(builder, cargo, &librustc_stamp(builder, compiler, target), vec![], true);
230 let libdir = builder.sysroot_libdir(compiler, target);
231 let hostdir = builder.sysroot_libdir(compiler, compiler.host);
232 add_to_sysroot(&builder, &libdir, &hostdir, &librustc_stamp(builder, compiler, target));
236 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
237 pub struct CodegenBackend {
238 pub target: TargetSelection,
239 pub backend: Interned<String>,
242 impl Step for CodegenBackend {
244 const ONLY_HOSTS: bool = true;
245 const DEFAULT: bool = true;
247 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
248 run.paths(&["compiler/rustc_codegen_cranelift", "compiler/rustc_codegen_gcc"])
251 fn make_run(run: RunConfig<'_>) {
252 for &backend in &[INTERNER.intern_str("cranelift"), INTERNER.intern_str("gcc")] {
253 run.builder.ensure(CodegenBackend { target: run.target, backend });
257 fn run(self, builder: &Builder<'_>) {
258 let compiler = builder.compiler(builder.top_stage, builder.config.build);
259 let target = self.target;
260 let backend = self.backend;
262 builder.ensure(Rustc { target });
264 let mut cargo = builder.cargo(
269 cargo_subcommand(builder.kind),
272 .arg("--manifest-path")
273 .arg(builder.src.join(format!("compiler/rustc_codegen_{}/Cargo.toml", backend)));
274 rustc_cargo_env(builder, &mut cargo, target);
275 cargo.args(args(builder));
277 builder.info(&format!(
278 "Checking stage{} {} artifacts ({} -> {})",
279 builder.top_stage, backend, &compiler.host.triple, target.triple
285 &codegen_backend_stamp(builder, compiler, target, backend),
292 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
293 pub struct RustAnalyzer {
294 pub target: TargetSelection,
297 impl Step for RustAnalyzer {
299 const ONLY_HOSTS: bool = true;
300 const DEFAULT: bool = true;
302 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
303 run.path("src/tools/rust-analyzer")
306 fn make_run(run: RunConfig<'_>) {
307 run.builder.ensure(RustAnalyzer { target: run.target });
310 fn run(self, builder: &Builder<'_>) {
311 let compiler = builder.compiler(builder.top_stage, builder.config.build);
312 let target = self.target;
314 builder.ensure(Std { target });
316 let mut cargo = prepare_tool_cargo(
321 cargo_subcommand(builder.kind),
322 "src/tools/rust-analyzer",
324 &["rust-analyzer/in-rust-tree".to_owned()],
328 "-Zallow-features=proc_macro_internals,proc_macro_diagnostic,proc_macro_span",
331 // For ./x.py clippy, don't check those targets because
332 // linting tests and benchmarks can produce very noisy results
333 if builder.kind != Kind::Clippy {
334 // can't use `--all-targets` because `--examples` doesn't work well
336 cargo.arg("--tests");
337 cargo.arg("--benches");
340 cargo.args(args(builder));
342 builder.info(&format!(
343 "Checking stage{} {} artifacts ({} -> {})",
344 compiler.stage, "rust-analyzer", &compiler.host.triple, target.triple
346 run_cargo(builder, cargo, &stamp(builder, compiler, target), vec![], true);
348 /// Cargo's output path in a given stage, compiled by a particular
349 /// compiler for the specified target.
350 fn stamp(builder: &Builder<'_>, compiler: Compiler, target: TargetSelection) -> PathBuf {
351 builder.cargo_out(compiler, Mode::ToolStd, target).join(".rust-analyzer-check.stamp")
356 macro_rules! tool_check_step {
357 ($name:ident, $path:literal, $($alias:literal, )* $source_type:path $(, $default:literal )?) => {
358 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
360 pub target: TargetSelection,
363 impl Step for $name {
365 const ONLY_HOSTS: bool = true;
366 // don't ever check out-of-tree tools by default, they'll fail when toolstate is broken
367 const DEFAULT: bool = matches!($source_type, SourceType::InTree) $( && $default )?;
369 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
370 run.paths(&[ $path, $($alias),* ])
373 fn make_run(run: RunConfig<'_>) {
374 run.builder.ensure($name { target: run.target });
377 fn run(self, builder: &Builder<'_>) {
378 let compiler = builder.compiler(builder.top_stage, builder.config.build);
379 let target = self.target;
381 builder.ensure(Rustc { target });
383 let mut cargo = prepare_tool_cargo(
388 cargo_subcommand(builder.kind),
394 // For ./x.py clippy, don't run with --all-targets because
395 // linting tests and benchmarks can produce very noisy results
396 if builder.kind != Kind::Clippy {
397 cargo.arg("--all-targets");
400 cargo.args(args(builder));
402 // Enable internal lints for clippy and rustdoc
403 // NOTE: this doesn't enable lints for any other tools unless they explicitly add `#![warn(rustc::internal)]`
404 // See https://github.com/rust-lang/rust/pull/80573#issuecomment-754010776
405 cargo.rustflag("-Zunstable-options");
407 builder.info(&format!(
408 "Checking stage{} {} artifacts ({} -> {})",
410 stringify!($name).to_lowercase(),
411 &compiler.host.triple,
417 &stamp(builder, compiler, target),
422 /// Cargo's output path in a given stage, compiled by a particular
423 /// compiler for the specified target.
425 builder: &Builder<'_>,
427 target: TargetSelection,
430 .cargo_out(compiler, Mode::ToolRustc, target)
431 .join(format!(".{}-check.stamp", stringify!($name).to_lowercase()))
438 tool_check_step!(Rustdoc, "src/tools/rustdoc", "src/librustdoc", SourceType::InTree);
439 // Clippy, miri and Rustfmt are hybrids. They are external tools, but use a git subtree instead
440 // of a submodule. Since the SourceType only drives the deny-warnings
441 // behavior, treat it as in-tree so that any new warnings in clippy will be
443 tool_check_step!(Clippy, "src/tools/clippy", SourceType::InTree);
444 tool_check_step!(Miri, "src/tools/miri", SourceType::InTree);
445 tool_check_step!(CargoMiri, "src/tools/miri/cargo-miri", SourceType::InTree);
446 tool_check_step!(Rls, "src/tools/rls", SourceType::InTree);
447 tool_check_step!(Rustfmt, "src/tools/rustfmt", SourceType::InTree);
448 tool_check_step!(MiroptTestTools, "src/tools/miropt-test-tools", SourceType::InTree);
450 tool_check_step!(Bootstrap, "src/bootstrap", SourceType::InTree, false);
452 /// Cargo's output path for the standard library in a given stage, compiled
453 /// by a particular compiler for the specified target.
454 fn libstd_stamp(builder: &Builder<'_>, compiler: Compiler, target: TargetSelection) -> PathBuf {
455 builder.cargo_out(compiler, Mode::Std, target).join(".libstd-check.stamp")
458 /// Cargo's output path for the standard library in a given stage, compiled
459 /// by a particular compiler for the specified target.
460 fn libstd_test_stamp(
461 builder: &Builder<'_>,
463 target: TargetSelection,
465 builder.cargo_out(compiler, Mode::Std, target).join(".libstd-check-test.stamp")
468 /// Cargo's output path for librustc in a given stage, compiled by a particular
469 /// compiler for the specified target.
470 fn librustc_stamp(builder: &Builder<'_>, compiler: Compiler, target: TargetSelection) -> PathBuf {
471 builder.cargo_out(compiler, Mode::Rustc, target).join(".librustc-check.stamp")
474 /// Cargo's output path for librustc_codegen_llvm in a given stage, compiled by a particular
475 /// compiler for the specified target and backend.
476 fn codegen_backend_stamp(
477 builder: &Builder<'_>,
479 target: TargetSelection,
480 backend: Interned<String>,
483 .cargo_out(compiler, Mode::Codegen, target)
484 .join(format!(".librustc_codegen_{}-check.stamp", backend))