]> git.lizzy.rs Git - rust.git/blob - src/bootstrap/check.rs
Add `build_helper` crate to share code between tidy and bootstrap
[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, false);
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(
159             builder,
160             cargo,
161             &libstd_test_stamp(builder, compiler, target),
162             vec![],
163             true,
164             false,
165         );
166     }
167 }
168
169 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
170 pub struct Rustc {
171     pub target: TargetSelection,
172 }
173
174 impl Step for Rustc {
175     type Output = ();
176     const ONLY_HOSTS: bool = true;
177     const DEFAULT: bool = true;
178
179     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
180         run.all_krates("rustc-main").path("compiler")
181     }
182
183     fn make_run(run: RunConfig<'_>) {
184         run.builder.ensure(Rustc { target: run.target });
185     }
186
187     /// Builds the compiler.
188     ///
189     /// This will build the compiler for a particular stage of the build using
190     /// the `compiler` targeting the `target` architecture. The artifacts
191     /// created will also be linked into the sysroot directory.
192     fn run(self, builder: &Builder<'_>) {
193         let compiler = builder.compiler(builder.top_stage, builder.config.build);
194         let target = self.target;
195
196         if compiler.stage != 0 {
197             // If we're not in stage 0, then we won't have a std from the beta
198             // compiler around. That means we need to make sure there's one in
199             // the sysroot for the compiler to find. Otherwise, we're going to
200             // fail when building crates that need to generate code (e.g., build
201             // scripts and their dependencies).
202             builder.ensure(crate::compile::Std::new(compiler, compiler.host));
203             builder.ensure(crate::compile::Std::new(compiler, target));
204         } else {
205             builder.ensure(Std { target });
206         }
207
208         let mut cargo = builder.cargo(
209             compiler,
210             Mode::Rustc,
211             SourceType::InTree,
212             target,
213             cargo_subcommand(builder.kind),
214         );
215         rustc_cargo(builder, &mut cargo, target);
216
217         // For ./x.py clippy, don't run with --all-targets because
218         // linting tests and benchmarks can produce very noisy results
219         if builder.kind != Kind::Clippy {
220             cargo.arg("--all-targets");
221         }
222
223         // Explicitly pass -p for all compiler krates -- this will force cargo
224         // to also check the tests/benches/examples for these crates, rather
225         // than just the leaf crate.
226         for krate in builder.in_tree_crates("rustc-main", Some(target)) {
227             cargo.arg("-p").arg(krate.name);
228         }
229         cargo.args(args(builder));
230
231         builder.info(&format!(
232             "Checking stage{} compiler artifacts ({} -> {})",
233             builder.top_stage, &compiler.host, target
234         ));
235         run_cargo(builder, cargo, &librustc_stamp(builder, compiler, target), vec![], true, false);
236
237         let libdir = builder.sysroot_libdir(compiler, target);
238         let hostdir = builder.sysroot_libdir(compiler, compiler.host);
239         add_to_sysroot(&builder, &libdir, &hostdir, &librustc_stamp(builder, compiler, target));
240     }
241 }
242
243 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
244 pub struct CodegenBackend {
245     pub target: TargetSelection,
246     pub backend: Interned<String>,
247 }
248
249 impl Step for CodegenBackend {
250     type Output = ();
251     const ONLY_HOSTS: bool = true;
252     const DEFAULT: bool = true;
253
254     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
255         run.paths(&["compiler/rustc_codegen_cranelift", "compiler/rustc_codegen_gcc"])
256     }
257
258     fn make_run(run: RunConfig<'_>) {
259         for &backend in &[INTERNER.intern_str("cranelift"), INTERNER.intern_str("gcc")] {
260             run.builder.ensure(CodegenBackend { target: run.target, backend });
261         }
262     }
263
264     fn run(self, builder: &Builder<'_>) {
265         let compiler = builder.compiler(builder.top_stage, builder.config.build);
266         let target = self.target;
267         let backend = self.backend;
268
269         builder.ensure(Rustc { target });
270
271         let mut cargo = builder.cargo(
272             compiler,
273             Mode::Codegen,
274             SourceType::InTree,
275             target,
276             cargo_subcommand(builder.kind),
277         );
278         cargo
279             .arg("--manifest-path")
280             .arg(builder.src.join(format!("compiler/rustc_codegen_{}/Cargo.toml", backend)));
281         rustc_cargo_env(builder, &mut cargo, target);
282         cargo.args(args(builder));
283
284         builder.info(&format!(
285             "Checking stage{} {} artifacts ({} -> {})",
286             builder.top_stage, backend, &compiler.host.triple, target.triple
287         ));
288
289         run_cargo(
290             builder,
291             cargo,
292             &codegen_backend_stamp(builder, compiler, target, backend),
293             vec![],
294             true,
295             false,
296         );
297     }
298 }
299
300 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
301 pub struct RustAnalyzer {
302     pub target: TargetSelection,
303 }
304
305 impl Step for RustAnalyzer {
306     type Output = ();
307     const ONLY_HOSTS: bool = true;
308     const DEFAULT: bool = true;
309
310     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
311         run.path("src/tools/rust-analyzer")
312     }
313
314     fn make_run(run: RunConfig<'_>) {
315         run.builder.ensure(RustAnalyzer { target: run.target });
316     }
317
318     fn run(self, builder: &Builder<'_>) {
319         let compiler = builder.compiler(builder.top_stage, builder.config.build);
320         let target = self.target;
321
322         builder.ensure(Std { target });
323
324         let mut cargo = prepare_tool_cargo(
325             builder,
326             compiler,
327             Mode::ToolStd,
328             target,
329             cargo_subcommand(builder.kind),
330             "src/tools/rust-analyzer",
331             SourceType::InTree,
332             &["rust-analyzer/in-rust-tree".to_owned()],
333         );
334
335         cargo.rustflag(
336             "-Zallow-features=proc_macro_internals,proc_macro_diagnostic,proc_macro_span",
337         );
338
339         // For ./x.py clippy, don't check those targets because
340         // linting tests and benchmarks can produce very noisy results
341         if builder.kind != Kind::Clippy {
342             // can't use `--all-targets` because `--examples` doesn't work well
343             cargo.arg("--bins");
344             cargo.arg("--tests");
345             cargo.arg("--benches");
346         }
347
348         cargo.args(args(builder));
349
350         builder.info(&format!(
351             "Checking stage{} {} artifacts ({} -> {})",
352             compiler.stage, "rust-analyzer", &compiler.host.triple, target.triple
353         ));
354         run_cargo(builder, cargo, &stamp(builder, compiler, target), vec![], true, false);
355
356         /// Cargo's output path in a given stage, compiled by a particular
357         /// compiler for the specified target.
358         fn stamp(builder: &Builder<'_>, compiler: Compiler, target: TargetSelection) -> PathBuf {
359             builder.cargo_out(compiler, Mode::ToolStd, target).join(".rust-analyzer-check.stamp")
360         }
361     }
362 }
363
364 macro_rules! tool_check_step {
365     ($name:ident, $path:literal, $($alias:literal, )* $source_type:path $(, $default:literal )?) => {
366         #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
367         pub struct $name {
368             pub target: TargetSelection,
369         }
370
371         impl Step for $name {
372             type Output = ();
373             const ONLY_HOSTS: bool = true;
374             // don't ever check out-of-tree tools by default, they'll fail when toolstate is broken
375             const DEFAULT: bool = matches!($source_type, SourceType::InTree) $( && $default )?;
376
377             fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
378                 run.paths(&[ $path, $($alias),* ])
379             }
380
381             fn make_run(run: RunConfig<'_>) {
382                 run.builder.ensure($name { target: run.target });
383             }
384
385             fn run(self, builder: &Builder<'_>) {
386                 let compiler = builder.compiler(builder.top_stage, builder.config.build);
387                 let target = self.target;
388
389                 builder.ensure(Rustc { target });
390
391                 let mut cargo = prepare_tool_cargo(
392                     builder,
393                     compiler,
394                     Mode::ToolRustc,
395                     target,
396                     cargo_subcommand(builder.kind),
397                     $path,
398                     $source_type,
399                     &[],
400                 );
401
402                 // For ./x.py clippy, don't run with --all-targets because
403                 // linting tests and benchmarks can produce very noisy results
404                 if builder.kind != Kind::Clippy {
405                     cargo.arg("--all-targets");
406                 }
407
408                 cargo.args(args(builder));
409
410                 // Enable internal lints for clippy and rustdoc
411                 // NOTE: this doesn't enable lints for any other tools unless they explicitly add `#![warn(rustc::internal)]`
412                 // See https://github.com/rust-lang/rust/pull/80573#issuecomment-754010776
413                 cargo.rustflag("-Zunstable-options");
414
415                 builder.info(&format!(
416                     "Checking stage{} {} artifacts ({} -> {})",
417                     builder.top_stage,
418                     stringify!($name).to_lowercase(),
419                     &compiler.host.triple,
420                     target.triple
421                 ));
422                 run_cargo(
423                     builder,
424                     cargo,
425                     &stamp(builder, compiler, target),
426                     vec![],
427                     true,
428                     false,
429                 );
430
431                 /// Cargo's output path in a given stage, compiled by a particular
432                 /// compiler for the specified target.
433                 fn stamp(
434                     builder: &Builder<'_>,
435                     compiler: Compiler,
436                     target: TargetSelection,
437                 ) -> PathBuf {
438                     builder
439                         .cargo_out(compiler, Mode::ToolRustc, target)
440                         .join(format!(".{}-check.stamp", stringify!($name).to_lowercase()))
441                 }
442             }
443         }
444     };
445 }
446
447 tool_check_step!(Rustdoc, "src/tools/rustdoc", "src/librustdoc", SourceType::InTree);
448 // Clippy, miri and Rustfmt are hybrids. They are external tools, but use a git subtree instead
449 // of a submodule. Since the SourceType only drives the deny-warnings
450 // behavior, treat it as in-tree so that any new warnings in clippy will be
451 // rejected.
452 tool_check_step!(Clippy, "src/tools/clippy", SourceType::InTree);
453 tool_check_step!(Miri, "src/tools/miri", SourceType::InTree);
454 tool_check_step!(CargoMiri, "src/tools/miri/cargo-miri", SourceType::InTree);
455 tool_check_step!(Rls, "src/tools/rls", SourceType::InTree);
456 tool_check_step!(Rustfmt, "src/tools/rustfmt", SourceType::InTree);
457 tool_check_step!(MiroptTestTools, "src/tools/miropt-test-tools", SourceType::InTree);
458
459 tool_check_step!(Bootstrap, "src/bootstrap", SourceType::InTree, false);
460
461 /// Cargo's output path for the standard library in a given stage, compiled
462 /// by a particular compiler for the specified target.
463 fn libstd_stamp(builder: &Builder<'_>, compiler: Compiler, target: TargetSelection) -> PathBuf {
464     builder.cargo_out(compiler, Mode::Std, target).join(".libstd-check.stamp")
465 }
466
467 /// Cargo's output path for the standard library in a given stage, compiled
468 /// by a particular compiler for the specified target.
469 fn libstd_test_stamp(
470     builder: &Builder<'_>,
471     compiler: Compiler,
472     target: TargetSelection,
473 ) -> PathBuf {
474     builder.cargo_out(compiler, Mode::Std, target).join(".libstd-check-test.stamp")
475 }
476
477 /// Cargo's output path for librustc in a given stage, compiled by a particular
478 /// compiler for the specified target.
479 fn librustc_stamp(builder: &Builder<'_>, compiler: Compiler, target: TargetSelection) -> PathBuf {
480     builder.cargo_out(compiler, Mode::Rustc, target).join(".librustc-check.stamp")
481 }
482
483 /// Cargo's output path for librustc_codegen_llvm in a given stage, compiled by a particular
484 /// compiler for the specified target and backend.
485 fn codegen_backend_stamp(
486     builder: &Builder<'_>,
487     compiler: Compiler,
488     target: TargetSelection,
489     backend: Interned<String>,
490 ) -> PathBuf {
491     builder
492         .cargo_out(compiler, Mode::Codegen, target)
493         .join(format!(".librustc_codegen_{}-check.stamp", backend))
494 }