Skip to content

Commit 814b8e6

Browse files
committed
Only check std in cross-compilation instead of building it
1 parent ddf39ca commit 814b8e6

File tree

3 files changed

+153
-57
lines changed

3 files changed

+153
-57
lines changed

src/bootstrap/src/core/build_steps/check.rs

Lines changed: 144 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! Implementation of compiling the compiler and standard library, in "check"-based modes.
22
33
use std::fs;
4-
use std::path::PathBuf;
4+
use std::path::{Path, PathBuf};
55

66
use crate::core::build_steps::compile::{
77
add_to_sysroot, run_cargo, rustc_cargo, rustc_cargo_env, std_cargo, std_crates_for_run_make,
@@ -36,7 +36,7 @@ impl Std {
3636
}
3737

3838
impl Step for Std {
39-
type Output = ();
39+
type Output = BuildStamp;
4040
const DEFAULT: bool = true;
4141

4242
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
@@ -70,7 +70,7 @@ impl Step for Std {
7070
});
7171
}
7272

73-
fn run(self, builder: &Builder<'_>) {
73+
fn run(self, builder: &Builder<'_>) -> Self::Output {
7474
let build_compiler = self.build_compiler;
7575
let target = self.target;
7676

@@ -101,14 +101,23 @@ impl Step for Std {
101101
target,
102102
);
103103

104-
let stamp = build_stamp::libstd_stamp(builder, build_compiler, target).with_prefix("check");
105-
run_cargo(builder, cargo, builder.config.free_args.clone(), &stamp, vec![], true, false);
104+
let check_stamp =
105+
build_stamp::libstd_stamp(builder, build_compiler, target).with_prefix("check");
106+
run_cargo(
107+
builder,
108+
cargo,
109+
builder.config.free_args.clone(),
110+
&check_stamp,
111+
vec![],
112+
true,
113+
false,
114+
);
106115

107116
drop(_guard);
108117

109118
// don't check test dependencies if we haven't built libtest
110119
if !self.crates.iter().any(|krate| krate == "test") {
111-
return;
120+
return check_stamp;
112121
}
113122

114123
// Then run cargo again, once we've put the rmeta files for the library
@@ -145,6 +154,7 @@ impl Step for Std {
145154
target,
146155
);
147156
run_cargo(builder, cargo, builder.config.free_args.clone(), &stamp, vec![], true, false);
157+
check_stamp
148158
}
149159

150160
fn metadata(&self) -> Option<StepMetadata> {
@@ -156,12 +166,28 @@ impl Step for Std {
156166
/// Contains directories with .rmeta files generated by checking rustc for a specific
157167
/// target.
158168
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
159-
struct RustcRmetaSysroot {
169+
struct RmetaSysroot {
160170
host_dir: PathBuf,
161171
target_dir: PathBuf,
162172
}
163173

164-
impl RustcRmetaSysroot {
174+
impl RmetaSysroot {
175+
/// Copy rmeta artifacts from the given `stamp` into a sysroot located at `directory`.
176+
fn from_stamp(
177+
builder: &Builder<'_>,
178+
stamp: BuildStamp,
179+
target: TargetSelection,
180+
directory: &Path,
181+
) -> Self {
182+
let host_dir = directory.join("host");
183+
let target_dir = directory.join(target);
184+
let _ = fs::remove_dir_all(directory);
185+
t!(fs::create_dir_all(directory));
186+
add_to_sysroot(builder, &target_dir, &host_dir, &stamp);
187+
188+
Self { host_dir, target_dir }
189+
}
190+
165191
/// Configure the given cargo invocation so that the compiled crate will be able to use
166192
/// rustc .rmeta artifacts that were previously generated.
167193
fn configure_cargo(&self, cargo: &mut Cargo) {
@@ -180,42 +206,90 @@ impl RustcRmetaSysroot {
180206
/// "pollute" it (that is especially problematic for the external stage0 rustc).
181207
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
182208
struct PrepareRustcRmetaSysroot {
183-
build_compiler: Compiler,
209+
build_compiler: CompilerForCheck,
184210
target: TargetSelection,
185211
}
186212

213+
impl PrepareRustcRmetaSysroot {
214+
fn new(build_compiler: CompilerForCheck, target: TargetSelection) -> Self {
215+
Self { build_compiler, target }
216+
}
217+
}
218+
187219
impl Step for PrepareRustcRmetaSysroot {
188-
type Output = RustcRmetaSysroot;
220+
type Output = RmetaSysroot;
189221

190222
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
191223
run.never()
192224
}
193225

194226
fn run(self, builder: &Builder<'_>) -> Self::Output {
195227
// Check rustc
196-
let stamp =
197-
builder.ensure(Rustc::from_build_compiler(self.build_compiler, self.target, vec![]));
228+
let stamp = builder.ensure(Rustc::from_build_compiler(
229+
self.build_compiler.clone(),
230+
self.target,
231+
vec![],
232+
));
233+
234+
let build_compiler = self.build_compiler.build_compiler();
235+
236+
// Copy the generated rmeta artifacts to a separate directory
237+
let dir = builder
238+
.out
239+
.join(build_compiler.host)
240+
.join(format!("stage{}-rustc-rmeta-artifacts", build_compiler.stage + 1));
241+
RmetaSysroot::from_stamp(builder, stamp, self.target, &dir)
242+
}
243+
}
244+
245+
/// Checks std using the given `build_compiler` for the given `target`, and produces
246+
/// a sysroot in the build directory that stores the generated .rmeta files.
247+
///
248+
/// This step exists so that we can store the generated .rmeta artifacts into a separate
249+
/// directory, instead of copying them into the sysroot of `build_compiler`, which would
250+
/// "pollute" it (that is especially problematic for the external stage0 rustc).
251+
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
252+
struct PrepareStdRmetaSysroot {
253+
build_compiler: Compiler,
254+
target: TargetSelection,
255+
}
256+
257+
impl PrepareStdRmetaSysroot {
258+
fn new(build_compiler: Compiler, target: TargetSelection) -> Self {
259+
Self { build_compiler, target }
260+
}
261+
}
262+
263+
impl Step for PrepareStdRmetaSysroot {
264+
type Output = RmetaSysroot;
265+
266+
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
267+
run.never()
268+
}
269+
270+
fn run(self, builder: &Builder<'_>) -> Self::Output {
271+
// Check std
272+
let stamp = builder.ensure(Std {
273+
build_compiler: self.build_compiler,
274+
target: self.target,
275+
crates: vec![],
276+
});
198277

199278
// Copy the generated rmeta artifacts to a separate directory
200279
let dir = builder
201280
.out
202281
.join(self.build_compiler.host)
203-
.join(format!("stage{}-rustc-check-artifacts", self.build_compiler.stage + 1));
204-
let host_dir = dir.join("host");
205-
let target_dir = dir.join(self.target);
206-
let _ = fs::remove_dir_all(&dir);
207-
t!(fs::create_dir_all(&dir));
208-
add_to_sysroot(builder, &target_dir, &host_dir, &stamp);
282+
.join(format!("stage{}-std-rmeta-artifacts", self.build_compiler.stage));
209283

210-
RustcRmetaSysroot { host_dir, target_dir }
284+
RmetaSysroot::from_stamp(builder, stamp, self.target, &dir)
211285
}
212286
}
213287

214288
/// Checks rustc using `build_compiler`.
215289
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
216290
pub struct Rustc {
217291
/// Compiler that will check this rustc.
218-
pub build_compiler: Compiler,
292+
pub build_compiler: CompilerForCheck,
219293
pub target: TargetSelection,
220294
/// Whether to build only a subset of crates.
221295
///
@@ -227,13 +301,12 @@ pub struct Rustc {
227301

228302
impl Rustc {
229303
pub fn new(builder: &Builder<'_>, target: TargetSelection, crates: Vec<String>) -> Self {
230-
let build_compiler =
231-
prepare_compiler_for_check(builder, target, Mode::Rustc).build_compiler;
304+
let build_compiler = prepare_compiler_for_check(builder, target, Mode::Rustc);
232305
Self::from_build_compiler(build_compiler, target, crates)
233306
}
234307

235308
fn from_build_compiler(
236-
build_compiler: Compiler,
309+
build_compiler: CompilerForCheck,
237310
target: TargetSelection,
238311
crates: Vec<String>,
239312
) -> Self {
@@ -263,7 +336,7 @@ impl Step for Rustc {
263336
///
264337
/// If we check a stage 2 compiler, we will have to first build a stage 1 compiler to check it.
265338
fn run(self, builder: &Builder<'_>) -> Self::Output {
266-
let build_compiler = self.build_compiler;
339+
let build_compiler = self.build_compiler.build_compiler;
267340
let target = self.target;
268341

269342
let mut cargo = builder::Cargo::new(
@@ -276,6 +349,7 @@ impl Step for Rustc {
276349
);
277350

278351
rustc_cargo(builder, &mut cargo, target, &build_compiler, &self.crates);
352+
self.build_compiler.configure_cargo(&mut cargo);
279353

280354
// Explicitly pass -p for all compiler crates -- this will force cargo
281355
// to also check the tests/benches/examples for these crates, rather
@@ -288,7 +362,7 @@ impl Step for Rustc {
288362
Kind::Check,
289363
format_args!("compiler artifacts{}", crate_description(&self.crates)),
290364
Mode::Rustc,
291-
self.build_compiler,
365+
self.build_compiler.build_compiler(),
292366
target,
293367
);
294368

@@ -301,7 +375,8 @@ impl Step for Rustc {
301375
}
302376

303377
fn metadata(&self) -> Option<StepMetadata> {
304-
let metadata = StepMetadata::check("rustc", self.target).built_by(self.build_compiler);
378+
let metadata = StepMetadata::check("rustc", self.target)
379+
.built_by(self.build_compiler.build_compiler());
305380
let metadata = if self.crates.is_empty() {
306381
metadata
307382
} else {
@@ -322,7 +397,8 @@ impl Step for Rustc {
322397
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
323398
pub struct CompilerForCheck {
324399
build_compiler: Compiler,
325-
rustc_rmeta_sysroot: Option<RustcRmetaSysroot>,
400+
rustc_rmeta_sysroot: Option<RmetaSysroot>,
401+
std_rmeta_sysroot: Option<RmetaSysroot>,
326402
}
327403

328404
impl CompilerForCheck {
@@ -336,6 +412,30 @@ impl CompilerForCheck {
336412
if let Some(sysroot) = &self.rustc_rmeta_sysroot {
337413
sysroot.configure_cargo(cargo);
338414
}
415+
if let Some(sysroot) = &self.std_rmeta_sysroot {
416+
sysroot.configure_cargo(cargo);
417+
}
418+
}
419+
}
420+
421+
/// Prepare the standard library for checking something (that requires stdlib) using
422+
/// `build_compiler`.
423+
fn prepare_std(
424+
builder: &Builder<'_>,
425+
build_compiler: Compiler,
426+
target: TargetSelection,
427+
) -> Option<RmetaSysroot> {
428+
// We need to build the host stdlib even if we only check, to compile build scripts and proc
429+
// macros
430+
builder.std(build_compiler, builder.host_target);
431+
432+
// If we're cross-compiling, we generate the rmeta files for the given target
433+
// This check has to be here, because if we generate both .so and .rmeta files, rustc will fail,
434+
// as it will have multiple candidates for linking.
435+
if builder.host_target != target {
436+
Some(builder.ensure(PrepareStdRmetaSysroot::new(build_compiler, target)))
437+
} else {
438+
None
339439
}
340440
}
341441

@@ -347,9 +447,13 @@ pub fn prepare_compiler_for_check(
347447
) -> CompilerForCheck {
348448
let host = builder.host_target;
349449

350-
let mut rmeta_sysroot = None;
450+
let mut rustc_rmeta_sysroot = None;
451+
let mut std_rmeta_sysroot = None;
351452
let build_compiler = match mode {
352453
Mode::ToolBootstrap => builder.compiler(0, host),
454+
// We could also only check std here and use `prepare_std`, but `ToolTarget` is currently
455+
// only used for running in-tree Clippy on bootstrap tools, so it does not seem worth it to
456+
// optimize it. Therefore, here we build std for the target, instead of just checking it.
353457
Mode::ToolTarget => get_tool_target_compiler(builder, ToolTargetBuildMode::Build(target)),
354458
Mode::ToolStd => {
355459
if builder.config.compile_time_deps {
@@ -360,14 +464,7 @@ pub fn prepare_compiler_for_check(
360464
} else {
361465
// These tools require the local standard library to be checked
362466
let build_compiler = builder.compiler(builder.top_stage, host);
363-
364-
// We need to build the host stdlib to check the tool itself.
365-
// We need to build the target stdlib so that the tool can link to it.
366-
builder.std(build_compiler, host);
367-
// We could only check this library in theory, but `check::Std` doesn't copy rmetas
368-
// into `build_compiler`'s sysroot to avoid clashes with `.rlibs`, so we build it
369-
// instead.
370-
builder.std(build_compiler, target);
467+
std_rmeta_sysroot = prepare_std(builder, build_compiler, target);
371468
build_compiler
372469
}
373470
}
@@ -376,11 +473,14 @@ pub fn prepare_compiler_for_check(
376473
// return the build compiler that was used to check rustc.
377474
// We do not need to check examples/tests/etc. of Rustc for rustc_private, so we pass
378475
// an empty set of crates, which will avoid using `cargo -p`.
379-
let check = Rustc::new(builder, target, vec![]);
380-
let build_compiler = check.build_compiler;
381-
builder.ensure(check);
382-
rmeta_sysroot =
383-
Some(builder.ensure(PrepareRustcRmetaSysroot { build_compiler, target }));
476+
let compiler_for_rustc = prepare_compiler_for_check(builder, target, Mode::Rustc);
477+
rustc_rmeta_sysroot = Some(
478+
builder.ensure(PrepareRustcRmetaSysroot::new(compiler_for_rustc.clone(), target)),
479+
);
480+
let build_compiler = compiler_for_rustc.build_compiler();
481+
482+
// To check a rustc_private tool, we also need to check std that it will link to
483+
std_rmeta_sysroot = prepare_std(builder, build_compiler, target);
384484
build_compiler
385485
}
386486
Mode::Rustc => {
@@ -394,15 +494,8 @@ pub fn prepare_compiler_for_check(
394494
let stage = if host == target { builder.top_stage - 1 } else { builder.top_stage };
395495
let build_compiler = builder.compiler(stage, host);
396496

397-
// Build host std for compiling build scripts
398-
builder.std(build_compiler, build_compiler.host);
399-
400-
// Build target std so that the checked rustc can link to it during the check
401-
// FIXME: maybe we can a way to only do a check of std here?
402-
// But for that we would have to copy the stdlib rmetas to the sysroot of the build
403-
// compiler, which conflicts with std rlibs, if we also build std.
404-
builder.std(build_compiler, target);
405-
497+
// To check rustc, we need to check std that it will link to
498+
std_rmeta_sysroot = prepare_std(builder, build_compiler, target);
406499
build_compiler
407500
}
408501
Mode::Std => {
@@ -412,7 +505,7 @@ pub fn prepare_compiler_for_check(
412505
builder.compiler(builder.top_stage, host)
413506
}
414507
};
415-
CompilerForCheck { build_compiler, rustc_rmeta_sysroot: rmeta_sysroot }
508+
CompilerForCheck { build_compiler, rustc_rmeta_sysroot, std_rmeta_sysroot }
416509
}
417510

418511
/// Check the Cranelift codegen backend.

0 commit comments

Comments
 (0)