]> git.lizzy.rs Git - rust.git/blob - src/bootstrap/check.rs
Auto merge of #81937 - ssomers:btree_drainy_refactor_9b, r=Mark-Simulacrum
[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::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 { fix, .. } = builder.config.cmd {
24         // disable the most spammy clippy lints
25         let ignored_lints = vec![
26             "many_single_char_names", // there are a lot in stdarch
27             "collapsible_if",
28             "type_complexity",
29             "missing_safety_doc", // almost 3K warnings
30             "too_many_arguments",
31             "needless_lifetimes", // people want to keep the lifetimes
32             "wrong_self_convention",
33         ];
34         let mut args = vec![];
35         if fix {
36             #[rustfmt::skip]
37             args.extend(strings(&[
38                 "--fix", "-Zunstable-options",
39                 // FIXME: currently, `--fix` gives an error while checking tests for libtest,
40                 // possibly because libtest is not yet built in the sysroot.
41                 // As a workaround, avoid checking tests and benches when passed --fix.
42                 "--lib", "--bins", "--examples",
43             ]));
44         }
45         args.extend(strings(&["--", "--cap-lints", "warn"]));
46         args.extend(ignored_lints.iter().map(|lint| format!("-Aclippy::{}", lint)));
47         args
48     } else {
49         vec![]
50     }
51 }
52
53 fn cargo_subcommand(kind: Kind) -> &'static str {
54     match kind {
55         Kind::Check => "check",
56         Kind::Clippy => "clippy",
57         Kind::Fix => "fix",
58         _ => unreachable!(),
59     }
60 }
61
62 impl Step for Std {
63     type Output = ();
64     const DEFAULT: bool = true;
65     const ENABLE_DOWNLOAD_RUSTC: bool = true;
66
67     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
68         run.all_krates("test")
69     }
70
71     fn make_run(run: RunConfig<'_>) {
72         run.builder.ensure(Std { target: run.target });
73     }
74
75     fn run(self, builder: &Builder<'_>) {
76         let target = self.target;
77         let compiler = builder.compiler(builder.top_stage, builder.config.build);
78
79         let mut cargo = builder.cargo(
80             compiler,
81             Mode::Std,
82             SourceType::InTree,
83             target,
84             cargo_subcommand(builder.kind),
85         );
86         std_cargo(builder, target, compiler.stage, &mut cargo);
87
88         builder.info(&format!(
89             "Checking stage{} std artifacts ({} -> {})",
90             builder.top_stage, &compiler.host, target
91         ));
92         run_cargo(
93             builder,
94             cargo,
95             args(builder),
96             &libstd_stamp(builder, compiler, target),
97             vec![],
98             true,
99         );
100
101         // We skip populating the sysroot in non-zero stage because that'll lead
102         // to rlib/rmeta conflicts if std gets built during this session.
103         if compiler.stage == 0 {
104             let libdir = builder.sysroot_libdir(compiler, target);
105             let hostdir = builder.sysroot_libdir(compiler, compiler.host);
106             add_to_sysroot(&builder, &libdir, &hostdir, &libstd_stamp(builder, compiler, target));
107         }
108
109         // Then run cargo again, once we've put the rmeta files for the library
110         // crates into the sysroot. This is needed because e.g., core's tests
111         // depend on `libtest` -- Cargo presumes it will exist, but it doesn't
112         // since we initialize with an empty sysroot.
113         //
114         // Currently only the "libtest" tree of crates does this.
115
116         if let Subcommand::Check { all_targets: true, .. } = builder.config.cmd {
117             let mut cargo = builder.cargo(
118                 compiler,
119                 Mode::Std,
120                 SourceType::InTree,
121                 target,
122                 cargo_subcommand(builder.kind),
123             );
124             std_cargo(builder, target, compiler.stage, &mut cargo);
125             cargo.arg("--all-targets");
126
127             // Explicitly pass -p for all dependencies krates -- this will force cargo
128             // to also check the tests/benches/examples for these crates, rather
129             // than just the leaf crate.
130             for krate in builder.in_tree_crates("test", Some(target)) {
131                 cargo.arg("-p").arg(krate.name);
132             }
133
134             builder.info(&format!(
135                 "Checking stage{} std test/bench/example targets ({} -> {})",
136                 builder.top_stage, &compiler.host, target
137             ));
138             run_cargo(
139                 builder,
140                 cargo,
141                 args(builder),
142                 &libstd_test_stamp(builder, compiler, target),
143                 vec![],
144                 true,
145             );
146         }
147     }
148 }
149
150 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
151 pub struct Rustc {
152     pub target: TargetSelection,
153 }
154
155 impl Step for Rustc {
156     type Output = ();
157     const ONLY_HOSTS: bool = true;
158     const DEFAULT: bool = true;
159     const ENABLE_DOWNLOAD_RUSTC: bool = true;
160
161     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
162         run.all_krates("rustc-main")
163     }
164
165     fn make_run(run: RunConfig<'_>) {
166         run.builder.ensure(Rustc { target: run.target });
167     }
168
169     /// Builds the compiler.
170     ///
171     /// This will build the compiler for a particular stage of the build using
172     /// the `compiler` targeting the `target` architecture. The artifacts
173     /// created will also be linked into the sysroot directory.
174     fn run(self, builder: &Builder<'_>) {
175         let compiler = builder.compiler(builder.top_stage, builder.config.build);
176         let target = self.target;
177
178         if compiler.stage != 0 {
179             // If we're not in stage 0, then we won't have a std from the beta
180             // compiler around. That means we need to make sure there's one in
181             // the sysroot for the compiler to find. Otherwise, we're going to
182             // fail when building crates that need to generate code (e.g., build
183             // scripts and their dependencies).
184             builder.ensure(crate::compile::Std { target: compiler.host, compiler });
185             builder.ensure(crate::compile::Std { target, compiler });
186         } else {
187             builder.ensure(Std { target });
188         }
189
190         let mut cargo = builder.cargo(
191             compiler,
192             Mode::Rustc,
193             SourceType::InTree,
194             target,
195             cargo_subcommand(builder.kind),
196         );
197         rustc_cargo(builder, &mut cargo, target);
198         if let Subcommand::Check { all_targets: true, .. } = builder.config.cmd {
199             cargo.arg("--all-targets");
200         }
201
202         // Explicitly pass -p for all compiler krates -- this will force cargo
203         // to also check the tests/benches/examples for these crates, rather
204         // than just the leaf crate.
205         for krate in builder.in_tree_crates("rustc-main", Some(target)) {
206             cargo.arg("-p").arg(krate.name);
207         }
208
209         builder.info(&format!(
210             "Checking stage{} compiler artifacts ({} -> {})",
211             builder.top_stage, &compiler.host, target
212         ));
213         run_cargo(
214             builder,
215             cargo,
216             args(builder),
217             &librustc_stamp(builder, compiler, target),
218             vec![],
219             true,
220         );
221
222         let libdir = builder.sysroot_libdir(compiler, target);
223         let hostdir = builder.sysroot_libdir(compiler, compiler.host);
224         add_to_sysroot(&builder, &libdir, &hostdir, &librustc_stamp(builder, compiler, target));
225     }
226 }
227
228 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
229 pub struct CodegenBackend {
230     pub target: TargetSelection,
231     pub backend: Interned<String>,
232 }
233
234 impl Step for CodegenBackend {
235     type Output = ();
236     const ONLY_HOSTS: bool = true;
237     const DEFAULT: bool = true;
238     const ENABLE_DOWNLOAD_RUSTC: bool = true;
239
240     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
241         run.paths(&["compiler/rustc_codegen_cranelift", "rustc_codegen_cranelift"])
242     }
243
244     fn make_run(run: RunConfig<'_>) {
245         for &backend in &[INTERNER.intern_str("cranelift")] {
246             run.builder.ensure(CodegenBackend { target: run.target, backend });
247         }
248     }
249
250     fn run(self, builder: &Builder<'_>) {
251         let compiler = builder.compiler(builder.top_stage, builder.config.build);
252         let target = self.target;
253         let backend = self.backend;
254
255         builder.ensure(Rustc { target });
256
257         let mut cargo = builder.cargo(
258             compiler,
259             Mode::Codegen,
260             SourceType::Submodule,
261             target,
262             cargo_subcommand(builder.kind),
263         );
264         cargo
265             .arg("--manifest-path")
266             .arg(builder.src.join(format!("compiler/rustc_codegen_{}/Cargo.toml", backend)));
267         rustc_cargo_env(builder, &mut cargo, target);
268
269         builder.info(&format!(
270             "Checking stage{} {} artifacts ({} -> {})",
271             builder.top_stage, backend, &compiler.host.triple, target.triple
272         ));
273
274         run_cargo(
275             builder,
276             cargo,
277             args(builder),
278             &codegen_backend_stamp(builder, compiler, target, backend),
279             vec![],
280             true,
281         );
282     }
283 }
284
285 macro_rules! tool_check_step {
286     ($name:ident, $path:expr, $source_type:expr) => {
287         #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
288         pub struct $name {
289             pub target: TargetSelection,
290         }
291
292         impl Step for $name {
293             type Output = ();
294             const ONLY_HOSTS: bool = true;
295             const DEFAULT: bool = true;
296             const ENABLE_DOWNLOAD_RUSTC: bool = true;
297
298             fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
299                 run.path($path)
300             }
301
302             fn make_run(run: RunConfig<'_>) {
303                 run.builder.ensure($name { target: run.target });
304             }
305
306             fn run(self, builder: &Builder<'_>) {
307                 let compiler = builder.compiler(builder.top_stage, builder.config.build);
308                 let target = self.target;
309
310                 builder.ensure(Rustc { target });
311
312                 let mut cargo = prepare_tool_cargo(
313                     builder,
314                     compiler,
315                     Mode::ToolRustc,
316                     target,
317                     cargo_subcommand(builder.kind),
318                     $path,
319                     $source_type,
320                     &[],
321                 );
322
323                 if let Subcommand::Check { all_targets: true, .. } = builder.config.cmd {
324                     cargo.arg("--all-targets");
325                 }
326
327                 // Enable internal lints for clippy and rustdoc
328                 // NOTE: this intentionally doesn't enable lints for any other tools,
329                 // see https://github.com/rust-lang/rust/pull/80573#issuecomment-754010776
330                 if $path == "src/tools/rustdoc" || $path == "src/tools/clippy" {
331                     cargo.rustflag("-Zunstable-options");
332                 }
333
334                 builder.info(&format!(
335                     "Checking stage{} {} artifacts ({} -> {})",
336                     builder.top_stage,
337                     stringify!($name).to_lowercase(),
338                     &compiler.host.triple,
339                     target.triple
340                 ));
341                 run_cargo(
342                     builder,
343                     cargo,
344                     args(builder),
345                     &stamp(builder, compiler, target),
346                     vec![],
347                     true,
348                 );
349
350                 let libdir = builder.sysroot_libdir(compiler, target);
351                 let hostdir = builder.sysroot_libdir(compiler, compiler.host);
352                 add_to_sysroot(&builder, &libdir, &hostdir, &stamp(builder, compiler, target));
353
354                 /// Cargo's output path in a given stage, compiled by a particular
355                 /// compiler for the specified target.
356                 fn stamp(
357                     builder: &Builder<'_>,
358                     compiler: Compiler,
359                     target: TargetSelection,
360                 ) -> PathBuf {
361                     builder
362                         .cargo_out(compiler, Mode::ToolRustc, target)
363                         .join(format!(".{}-check.stamp", stringify!($name).to_lowercase()))
364                 }
365             }
366         }
367     };
368 }
369
370 tool_check_step!(Rustdoc, "src/tools/rustdoc", SourceType::InTree);
371 // Clippy is a hybrid. It is an external tool, but uses a git subtree instead
372 // of a submodule. Since the SourceType only drives the deny-warnings
373 // behavior, treat it as in-tree so that any new warnings in clippy will be
374 // rejected.
375 tool_check_step!(Clippy, "src/tools/clippy", SourceType::InTree);
376
377 tool_check_step!(Bootstrap, "src/bootstrap", SourceType::InTree);
378
379 /// Cargo's output path for the standard library in a given stage, compiled
380 /// by a particular compiler for the specified target.
381 fn libstd_stamp(builder: &Builder<'_>, compiler: Compiler, target: TargetSelection) -> PathBuf {
382     builder.cargo_out(compiler, Mode::Std, target).join(".libstd-check.stamp")
383 }
384
385 /// Cargo's output path for the standard library in a given stage, compiled
386 /// by a particular compiler for the specified target.
387 fn libstd_test_stamp(
388     builder: &Builder<'_>,
389     compiler: Compiler,
390     target: TargetSelection,
391 ) -> PathBuf {
392     builder.cargo_out(compiler, Mode::Std, target).join(".libstd-check-test.stamp")
393 }
394
395 /// Cargo's output path for librustc in a given stage, compiled by a particular
396 /// compiler for the specified target.
397 fn librustc_stamp(builder: &Builder<'_>, compiler: Compiler, target: TargetSelection) -> PathBuf {
398     builder.cargo_out(compiler, Mode::Rustc, target).join(".librustc-check.stamp")
399 }
400
401 /// Cargo's output path for librustc_codegen_llvm in a given stage, compiled by a particular
402 /// compiler for the specified target and backend.
403 fn codegen_backend_stamp(
404     builder: &Builder<'_>,
405     compiler: Compiler,
406     target: TargetSelection,
407     backend: Interned<String>,
408 ) -> PathBuf {
409     builder
410         .cargo_out(compiler, Mode::Codegen, target)
411         .join(format!(".librustc_codegen_{}-check.stamp", backend))
412 }