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