Skip to content

Commit d874cf5

Browse files
committed
Avoid copying rustc artifacts to the build compiler sysroot during check
1 parent 745b7ab commit d874cf5

File tree

2 files changed

+140
-41
lines changed

2 files changed

+140
-41
lines changed

src/bootstrap/src/bin/rustc.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,16 @@ fn main() {
179179
}
180180
}
181181

182+
// Here we pass additional paths that essentially act as a sysroot.
183+
// These are used to load rustc crates (e.g. `extern crate rustc_ast;`)
184+
// for rustc_private tools, so that we do not have to copy them into the
185+
// actual sysroot of the compiler that builds the tool.
186+
if let Ok(dirs) = env::var("RUSTC_ADDITIONAL_SYSROOT_PATHS") {
187+
for dir in dirs.split(",") {
188+
cmd.arg(format!("-L{dir}"));
189+
}
190+
}
191+
182192
// Force all crates compiled by this compiler to (a) be unstable and (b)
183193
// allow the `rustc_private` feature to link to other unstable crates
184194
// also in the sysroot. We also do this for host crates, since those

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

Lines changed: 130 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
//! Implementation of compiling the compiler and standard library, in "check"-based modes.
22
3+
use std::fs;
4+
use std::path::PathBuf;
5+
36
use crate::core::build_steps::compile::{
47
add_to_sysroot, run_cargo, rustc_cargo, rustc_cargo_env, std_cargo, std_crates_for_run_make,
58
};
@@ -9,11 +12,11 @@ use crate::core::build_steps::tool::{
912
prepare_tool_cargo,
1013
};
1114
use crate::core::builder::{
12-
self, Alias, Builder, Kind, RunConfig, ShouldRun, Step, StepMetadata, crate_description,
15+
self, Alias, Builder, Cargo, Kind, RunConfig, ShouldRun, Step, StepMetadata, crate_description,
1316
};
1417
use crate::core::config::TargetSelection;
1518
use crate::utils::build_stamp::{self, BuildStamp};
16-
use crate::{Compiler, Mode, Subcommand};
19+
use crate::{Compiler, Mode, Subcommand, t};
1720

1821
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1922
pub struct Std {
@@ -64,7 +67,8 @@ impl Step for Std {
6467

6568
let crates = std_crates_for_run_make(&run);
6669
run.builder.ensure(Std {
67-
build_compiler: prepare_compiler_for_check(run.builder, run.target, Mode::Std),
70+
build_compiler: prepare_compiler_for_check(run.builder, run.target, Mode::Std)
71+
.build_compiler,
6872
target: run.target,
6973
crates,
7074
});
@@ -145,8 +149,65 @@ impl Step for Std {
145149
}
146150
}
147151

148-
/// Checks rustc using `build_compiler` and copies the built
149-
/// .rmeta files into the sysroot of `build_compiler`.
152+
/// Represents a proof that rustc was checked.
153+
/// Contains directories that contain .rmeta files generated by checking rustc for a specific
154+
/// target.
155+
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
156+
struct RustcRmetaSysroot {
157+
host_dir: PathBuf,
158+
target_dir: PathBuf,
159+
}
160+
161+
impl RustcRmetaSysroot {
162+
/// Configure the given cargo invocation so that the compiled crate will be able to use
163+
/// rustc rmeta artifacts that were previously generated.
164+
fn configure_cargo(&self, cargo: &mut Cargo) {
165+
cargo.env(
166+
"RUSTC_ADDITIONAL_SYSROOT_PATHS",
167+
format!("{},{}", self.host_dir.display(), self.target_dir.display()),
168+
);
169+
}
170+
}
171+
172+
/// Checks rustc using the given `build_compiler` for the given `target`, and produces
173+
/// a sysroot in the build directory that stores the generated .rmeta files.
174+
///
175+
/// This step exists so that we can separate the generated .rmeta artifacts into a separate
176+
/// directory. Before, we used to copy them into the sysroot of `build_compiler`, but that
177+
/// "pollutes" its sysroot, which is problematic especially for external stage0 rustc.
178+
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
179+
struct PrepareRustcRmetaSysroot {
180+
build_compiler: Compiler,
181+
target: TargetSelection,
182+
}
183+
184+
impl Step for PrepareRustcRmetaSysroot {
185+
type Output = RustcRmetaSysroot;
186+
187+
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
188+
run.never()
189+
}
190+
191+
fn run(self, builder: &Builder<'_>) -> Self::Output {
192+
// Check rustc
193+
let stamp = builder.ensure(Rustc::new(builder, self.build_compiler, self.target));
194+
195+
// Copy the generated rmeta artifacts to a separate directory
196+
let dir = builder
197+
.out
198+
.join(self.build_compiler.host)
199+
.join(format!("stage{}-rustc-check-artifacts", self.build_compiler.stage + 1));
200+
let host_dir = dir.join("host");
201+
let target_dir = dir.join(self.target);
202+
let _ = fs::remove_dir_all(&dir);
203+
t!(fs::create_dir_all(&dir));
204+
add_to_sysroot(builder, &target_dir, &host_dir, &stamp);
205+
206+
RustcRmetaSysroot { host_dir, target_dir }
207+
}
208+
}
209+
210+
/// Checks rustc using `build_compiler`.
150211
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
151212
pub struct Rustc {
152213
/// Compiler that will check this rustc.
@@ -172,7 +233,7 @@ impl Rustc {
172233
}
173234

174235
impl Step for Rustc {
175-
type Output = ();
236+
type Output = BuildStamp;
176237
const ONLY_HOSTS: bool = true;
177238
const DEFAULT: bool = true;
178239

@@ -184,7 +245,8 @@ impl Step for Rustc {
184245
let crates = run.make_run_crates(Alias::Compiler);
185246
run.builder.ensure(Rustc {
186247
target: run.target,
187-
build_compiler: prepare_compiler_for_check(run.builder, run.target, Mode::Rustc),
248+
build_compiler: prepare_compiler_for_check(run.builder, run.target, Mode::Rustc)
249+
.build_compiler,
188250
crates,
189251
});
190252
}
@@ -196,7 +258,7 @@ impl Step for Rustc {
196258
/// created will also be linked into the sysroot directory.
197259
///
198260
/// If we check a stage 2 compiler, we will have to first build a stage 1 compiler to check it.
199-
fn run(self, builder: &Builder<'_>) {
261+
fn run(self, builder: &Builder<'_>) -> Self::Output {
200262
let build_compiler = self.build_compiler;
201263
let target = self.target;
202264

@@ -238,53 +300,62 @@ impl Step for Rustc {
238300

239301
run_cargo(builder, cargo, builder.config.free_args.clone(), &stamp, vec![], true, false);
240302

241-
let libdir = builder.sysroot_target_libdir(build_compiler, target);
242-
let hostdir = builder.sysroot_target_libdir(build_compiler, build_compiler.host);
243-
add_to_sysroot(builder, &libdir, &hostdir, &stamp);
303+
stamp
244304
}
245305

246306
fn metadata(&self) -> Option<StepMetadata> {
247307
Some(StepMetadata::check("rustc", self.target).built_by(self.build_compiler))
248308
}
249309
}
250310

311+
/// Represents a compiler that can check something.
312+
/// It optionally includes .rmeta artifacts from rustc that was already checked using
313+
/// `build_compiler`.
314+
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
315+
struct CompilerForCheck {
316+
build_compiler: Compiler,
317+
rustc_rmeta_sysroot: Option<RustcRmetaSysroot>,
318+
}
319+
251320
/// Prepares a compiler that will check something with the given `mode`.
252321
fn prepare_compiler_for_check(
253322
builder: &Builder<'_>,
254323
target: TargetSelection,
255324
mode: Mode,
256-
) -> Compiler {
325+
) -> CompilerForCheck {
257326
let host = builder.host_target;
258327

259-
match mode {
328+
let mut rmeta_sysroot = None;
329+
let build_compiler = match mode {
260330
Mode::ToolBootstrap => builder.compiler(0, host),
261331
Mode::ToolTarget => get_tool_target_compiler(builder, ToolTargetBuildMode::Build(target)),
262332
Mode::ToolStd => {
263333
if builder.config.compile_time_deps {
264334
// When --compile-time-deps is passed, we can't use any rustc
265335
// other than the bootstrap compiler. Luckily build scripts and
266336
// proc macros for tools are unlikely to need nightly.
267-
return builder.compiler(0, host);
337+
builder.compiler(0, host)
338+
} else {
339+
// These tools require the local standard library to be checked
340+
let build_compiler = builder.compiler(builder.top_stage, host);
341+
342+
// We need to build the host stdlib to check the tool itself.
343+
// We need to build the target stdlib so that the tool can link to it.
344+
builder.std(build_compiler, host);
345+
// We could only check this library in theory, but `check::Std` doesn't copy rmetas
346+
// into `build_compiler`'s sysroot to avoid clashes with `.rlibs`, so we build it
347+
// instead.
348+
builder.std(build_compiler, target);
349+
build_compiler
268350
}
269-
270-
// These tools require the local standard library to be checked
271-
let build_compiler = builder.compiler(builder.top_stage, host);
272-
273-
// We need to build the host stdlib to check the tool itself.
274-
// We need to build the target stdlib so that the tool can link to it.
275-
builder.std(build_compiler, host);
276-
// We could only check this library in theory, but `check::Std` doesn't copy rmetas
277-
// into `build_compiler`'s sysroot to avoid clashes with `.rlibs`, so we build it
278-
// instead.
279-
builder.std(build_compiler, target);
280-
build_compiler
281351
}
282352
Mode::ToolRustc | Mode::Codegen => {
283353
// FIXME: this is a hack, see description of Mode::Rustc below
284354
let stage = if host == target { builder.top_stage - 1 } else { builder.top_stage };
285355
// When checking tool stage N, we check it with compiler stage N-1
286356
let build_compiler = builder.compiler(stage, host);
287-
builder.ensure(Rustc::new(builder, build_compiler, target));
357+
rmeta_sysroot =
358+
Some(builder.ensure(PrepareRustcRmetaSysroot { build_compiler, target }));
288359
build_compiler
289360
}
290361
Mode::Rustc => {
@@ -304,15 +375,17 @@ fn prepare_compiler_for_check(
304375
// stage 0 stdlib is used to compile build scripts and proc macros.
305376
builder.compiler(builder.top_stage, host)
306377
}
307-
}
378+
};
379+
CompilerForCheck { build_compiler, rustc_rmeta_sysroot: rmeta_sysroot }
308380
}
309381

310382
/// Checks a single codegen backend.
311383
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
312384
pub struct CodegenBackend {
313-
pub build_compiler: Compiler,
314-
pub target: TargetSelection,
315-
pub backend: &'static str,
385+
build_compiler: Compiler,
386+
rmeta_sysroot: RustcRmetaSysroot,
387+
target: TargetSelection,
388+
backend: &'static str,
316389
}
317390

318391
impl Step for CodegenBackend {
@@ -326,9 +399,17 @@ impl Step for CodegenBackend {
326399

327400
fn make_run(run: RunConfig<'_>) {
328401
// FIXME: only check the backend(s) that were actually selected in run.paths
329-
let build_compiler = prepare_compiler_for_check(run.builder, run.target, Mode::Codegen);
402+
let CompilerForCheck { build_compiler, rustc_rmeta_sysroot: rmeta_sysroot } =
403+
prepare_compiler_for_check(run.builder, run.target, Mode::Codegen);
404+
let rmeta_sysroot =
405+
rmeta_sysroot.expect("Rustc rmeta sysroot missing for checking codegen");
330406
for &backend in &["cranelift", "gcc"] {
331-
run.builder.ensure(CodegenBackend { build_compiler, target: run.target, backend });
407+
run.builder.ensure(CodegenBackend {
408+
build_compiler,
409+
rmeta_sysroot: rmeta_sysroot.clone(),
410+
target: run.target,
411+
backend,
412+
});
332413
}
333414
}
334415

@@ -351,6 +432,7 @@ impl Step for CodegenBackend {
351432
target,
352433
builder.kind,
353434
);
435+
self.rmeta_sysroot.configure_cargo(&mut cargo);
354436

355437
cargo
356438
.arg("--manifest-path")
@@ -388,8 +470,8 @@ macro_rules! tool_check_step {
388470
) => {
389471
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
390472
pub struct $name {
391-
pub build_compiler: Compiler,
392-
pub target: TargetSelection,
473+
compiler: CompilerForCheck,
474+
target: TargetSelection,
393475
}
394476

395477
impl Step for $name {
@@ -407,31 +489,31 @@ macro_rules! tool_check_step {
407489
let builder = run.builder;
408490
let mode = $mode(builder);
409491

410-
let build_compiler = prepare_compiler_for_check(run.builder, target, mode);
492+
let compiler = prepare_compiler_for_check(run.builder, target, mode);
411493

412494
// It doesn't make sense to cross-check bootstrap tools
413495
if mode == Mode::ToolBootstrap && target != run.builder.host_target {
414496
println!("WARNING: not checking bootstrap tool {} for target {target} as it is a bootstrap (host-only) tool", stringify!($path));
415497
return;
416498
};
417499

418-
run.builder.ensure($name { target, build_compiler });
500+
run.builder.ensure($name { target, compiler });
419501
}
420502

421503
fn run(self, builder: &Builder<'_>) {
422-
let Self { target, build_compiler } = self;
504+
let Self { target, compiler } = self;
423505
let allow_features = {
424506
let mut _value = "";
425507
$( _value = $allow_features; )?
426508
_value
427509
};
428510
let extra_features: &[&str] = &[$($($enable_features),*)?];
429511
let mode = $mode(builder);
430-
run_tool_check_step(builder, build_compiler, target, $path, mode, allow_features, extra_features);
512+
run_tool_check_step(builder, compiler, target, $path, mode, allow_features, extra_features);
431513
}
432514

433515
fn metadata(&self) -> Option<StepMetadata> {
434-
Some(StepMetadata::check(stringify!($name), self.target).built_by(self.build_compiler))
516+
Some(StepMetadata::check(stringify!($name), self.target).built_by(self.compiler.build_compiler))
435517
}
436518
}
437519
}
@@ -440,7 +522,7 @@ macro_rules! tool_check_step {
440522
/// Used by the implementation of `Step::run` in `tool_check_step!`.
441523
fn run_tool_check_step(
442524
builder: &Builder<'_>,
443-
build_compiler: Compiler,
525+
compiler: CompilerForCheck,
444526
target: TargetSelection,
445527
path: &str,
446528
mode: Mode,
@@ -449,6 +531,8 @@ fn run_tool_check_step(
449531
) {
450532
let display_name = path.rsplit('/').next().unwrap();
451533

534+
let CompilerForCheck { build_compiler, rustc_rmeta_sysroot: rmeta_sysroot } = compiler;
535+
452536
let extra_features = extra_features.iter().map(|f| f.to_string()).collect::<Vec<String>>();
453537
let mut cargo = prepare_tool_cargo(
454538
builder,
@@ -465,6 +549,11 @@ fn run_tool_check_step(
465549
&extra_features,
466550
);
467551
cargo.allow_features(allow_features);
552+
if mode == Mode::ToolRustc {
553+
let rmeta_sysroot =
554+
rmeta_sysroot.expect("rustc rmeta sysroot missing for a ToolRustc tool");
555+
rmeta_sysroot.configure_cargo(&mut cargo);
556+
}
468557

469558
// FIXME: check bootstrap doesn't currently work when multiple targets are checked
470559
// FIXME: rust-analyzer does not work with --all-targets

0 commit comments

Comments
 (0)