]> git.lizzy.rs Git - rust.git/blob - src/bootstrap/check.rs
32e5d414061eccdfe402fcc7393bdbecfac493e3
[rust.git] / src / bootstrap / check.rs
1 //! Implementation of compiling the compiler and standard library, in "check"-based modes.
2
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};
8 use crate::INTERNER;
9 use crate::{Compiler, Mode, Subcommand};
10 use std::path::{Path, PathBuf};
11
12 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
13 pub struct Std {
14     pub target: TargetSelection,
15 }
16
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)
21     }
22
23     if let Subcommand::Clippy {
24         fix,
25         clippy_lint_allow,
26         clippy_lint_deny,
27         clippy_lint_warn,
28         clippy_lint_forbid,
29         ..
30     } = &builder.config.cmd
31     {
32         // disable the most spammy clippy lints
33         let ignored_lints = vec![
34             "many_single_char_names", // there are a lot in stdarch
35             "collapsible_if",
36             "type_complexity",
37             "missing_safety_doc", // almost 3K warnings
38             "too_many_arguments",
39             "needless_lifetimes", // people want to keep the lifetimes
40             "wrong_self_convention",
41         ];
42         let mut args = vec![];
43         if *fix {
44             #[rustfmt::skip]
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",
51             ]));
52         }
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);
61         args
62     } else {
63         vec![]
64     }
65 }
66
67 fn cargo_subcommand(kind: Kind) -> &'static str {
68     match kind {
69         Kind::Check => "check",
70         Kind::Clippy => "clippy",
71         Kind::Fix => "fix",
72         _ => unreachable!(),
73     }
74 }
75
76 impl Step for Std {
77     type Output = ();
78     const DEFAULT: bool = true;
79
80     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
81         run.all_krates("test").path("library")
82     }
83
84     fn make_run(run: RunConfig<'_>) {
85         run.builder.ensure(Std { target: run.target });
86     }
87
88     fn run(self, builder: &Builder<'_>) {
89         builder.update_submodule(&Path::new("library").join("stdarch"));
90
91         let target = self.target;
92         let compiler = builder.compiler(builder.top_stage, builder.config.build);
93
94         let mut cargo = builder.cargo(
95             compiler,
96             Mode::Std,
97             SourceType::InTree,
98             target,
99             cargo_subcommand(builder.kind),
100         );
101         std_cargo(builder, target, compiler.stage, &mut cargo);
102         cargo.args(args(builder));
103
104         builder.info(&format!(
105             "Checking stage{} library artifacts ({} -> {})",
106             builder.top_stage, &compiler.host, target
107         ));
108         run_cargo(builder, cargo, &libstd_stamp(builder, compiler, target), vec![], true);
109
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));
116         }
117
118         // don't run on std twice with x.py clippy
119         if builder.kind == Kind::Clippy {
120             return;
121         }
122
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.
127         //
128         // Currently only the "libtest" tree of crates does this.
129         let mut cargo = builder.cargo(
130             compiler,
131             Mode::Std,
132             SourceType::InTree,
133             target,
134             cargo_subcommand(builder.kind),
135         );
136
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");
142         }
143
144         std_cargo(builder, target, compiler.stage, &mut cargo);
145
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);
151         }
152         cargo.args(args(builder));
153
154         builder.info(&format!(
155             "Checking stage{} library test/bench/example targets ({} -> {})",
156             builder.top_stage, &compiler.host, target
157         ));
158         run_cargo(builder, cargo, &libstd_test_stamp(builder, compiler, target), vec![], true);
159     }
160 }
161
162 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
163 pub struct Rustc {
164     pub target: TargetSelection,
165 }
166
167 impl Step for Rustc {
168     type Output = ();
169     const ONLY_HOSTS: bool = true;
170     const DEFAULT: bool = true;
171
172     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
173         run.all_krates("rustc-main").path("compiler")
174     }
175
176     fn make_run(run: RunConfig<'_>) {
177         run.builder.ensure(Rustc { target: run.target });
178     }
179
180     /// Builds the compiler.
181     ///
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;
188
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));
197         } else {
198             builder.ensure(Std { target });
199         }
200
201         let mut cargo = builder.cargo(
202             compiler,
203             Mode::Rustc,
204             SourceType::InTree,
205             target,
206             cargo_subcommand(builder.kind),
207         );
208         rustc_cargo(builder, &mut cargo, target);
209
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");
214         }
215
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);
221         }
222         cargo.args(args(builder));
223
224         builder.info(&format!(
225             "Checking stage{} compiler artifacts ({} -> {})",
226             builder.top_stage, &compiler.host, target
227         ));
228         run_cargo(builder, cargo, &librustc_stamp(builder, compiler, target), vec![], true);
229
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));
233     }
234 }
235
236 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
237 pub struct CodegenBackend {
238     pub target: TargetSelection,
239     pub backend: Interned<String>,
240 }
241
242 impl Step for CodegenBackend {
243     type Output = ();
244     const ONLY_HOSTS: bool = true;
245     const DEFAULT: bool = true;
246
247     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
248         run.paths(&["compiler/rustc_codegen_cranelift", "compiler/rustc_codegen_gcc"])
249     }
250
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 });
254         }
255     }
256
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;
261
262         builder.ensure(Rustc { target });
263
264         let mut cargo = builder.cargo(
265             compiler,
266             Mode::Codegen,
267             SourceType::InTree,
268             target,
269             cargo_subcommand(builder.kind),
270         );
271         cargo
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));
276
277         builder.info(&format!(
278             "Checking stage{} {} artifacts ({} -> {})",
279             builder.top_stage, backend, &compiler.host.triple, target.triple
280         ));
281
282         run_cargo(
283             builder,
284             cargo,
285             &codegen_backend_stamp(builder, compiler, target, backend),
286             vec![],
287             true,
288         );
289     }
290 }
291
292 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
293 pub struct RustAnalyzer {
294     pub target: TargetSelection,
295 }
296
297 impl Step for RustAnalyzer {
298     type Output = ();
299     const ONLY_HOSTS: bool = true;
300     const DEFAULT: bool = true;
301
302     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
303         run.path("src/tools/rust-analyzer")
304     }
305
306     fn make_run(run: RunConfig<'_>) {
307         run.builder.ensure(RustAnalyzer { target: run.target });
308     }
309
310     fn run(self, builder: &Builder<'_>) {
311         let compiler = builder.compiler(builder.top_stage, builder.config.build);
312         let target = self.target;
313
314         builder.ensure(Std { target });
315
316         let mut cargo = prepare_tool_cargo(
317             builder,
318             compiler,
319             Mode::ToolStd,
320             target,
321             cargo_subcommand(builder.kind),
322             "src/tools/rust-analyzer",
323             SourceType::InTree,
324             &["rust-analyzer/in-rust-tree".to_owned()],
325         );
326
327         cargo.rustflag(
328             "-Zallow-features=proc_macro_internals,proc_macro_diagnostic,proc_macro_span",
329         );
330
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
335             cargo.arg("--bins");
336             cargo.arg("--tests");
337             cargo.arg("--benches");
338         }
339
340         cargo.args(args(builder));
341
342         builder.info(&format!(
343             "Checking stage{} {} artifacts ({} -> {})",
344             compiler.stage, "rust-analyzer", &compiler.host.triple, target.triple
345         ));
346         run_cargo(builder, cargo, &stamp(builder, compiler, target), vec![], true);
347
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")
352         }
353     }
354 }
355
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)]
359         pub struct $name {
360             pub target: TargetSelection,
361         }
362
363         impl Step for $name {
364             type Output = ();
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 )?;
368
369             fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
370                 run.paths(&[ $path, $($alias),* ])
371             }
372
373             fn make_run(run: RunConfig<'_>) {
374                 run.builder.ensure($name { target: run.target });
375             }
376
377             fn run(self, builder: &Builder<'_>) {
378                 let compiler = builder.compiler(builder.top_stage, builder.config.build);
379                 let target = self.target;
380
381                 builder.ensure(Rustc { target });
382
383                 let mut cargo = prepare_tool_cargo(
384                     builder,
385                     compiler,
386                     Mode::ToolRustc,
387                     target,
388                     cargo_subcommand(builder.kind),
389                     $path,
390                     $source_type,
391                     &[],
392                 );
393
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");
398                 }
399
400                 cargo.args(args(builder));
401
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");
406
407                 builder.info(&format!(
408                     "Checking stage{} {} artifacts ({} -> {})",
409                     builder.top_stage,
410                     stringify!($name).to_lowercase(),
411                     &compiler.host.triple,
412                     target.triple
413                 ));
414                 run_cargo(
415                     builder,
416                     cargo,
417                     &stamp(builder, compiler, target),
418                     vec![],
419                     true,
420                 );
421
422                 /// Cargo's output path in a given stage, compiled by a particular
423                 /// compiler for the specified target.
424                 fn stamp(
425                     builder: &Builder<'_>,
426                     compiler: Compiler,
427                     target: TargetSelection,
428                 ) -> PathBuf {
429                     builder
430                         .cargo_out(compiler, Mode::ToolRustc, target)
431                         .join(format!(".{}-check.stamp", stringify!($name).to_lowercase()))
432                 }
433             }
434         }
435     };
436 }
437
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
442 // rejected.
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);
449
450 tool_check_step!(Bootstrap, "src/bootstrap", SourceType::InTree, false);
451
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")
456 }
457
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<'_>,
462     compiler: Compiler,
463     target: TargetSelection,
464 ) -> PathBuf {
465     builder.cargo_out(compiler, Mode::Std, target).join(".libstd-check-test.stamp")
466 }
467
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")
472 }
473
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<'_>,
478     compiler: Compiler,
479     target: TargetSelection,
480     backend: Interned<String>,
481 ) -> PathBuf {
482     builder
483         .cargo_out(compiler, Mode::Codegen, target)
484         .join(format!(".librustc_codegen_{}-check.stamp", backend))
485 }