Skip to content

Commit 87d677b

Browse files
authored
Rollup merge of rust-lang#145310 - Kobzol:compiler-for-revamp, r=jieyouxu
Reduce usage of `compiler_for` in bootstrap While working on refactoring/fixing `dist` steps, I realized that `build.full-bootstrap` does much more than it should, and that it its documentation is wrong. It seems that the main purpose of this option should be to enable/disable stdlib/compiler uplifting (https://rust-lang.zulipchat.com/#narrow/channel/326414-t-infra.2Fbootstrap/topic/Purpose.20of.20.60build.2Efull-bootstrap.60/with/533985624), but currently it also affects staging, or more precisely which compiler will be used to build selected steps, because this option is used in the cursed `compiler_for` function. I would like to change the option it so that it *only* affects uplifting, and doesn't affect stage selection, which I (partially) did in this PR. I removed the usage of `compiler_for` from the `Std` and `Rustc` steps, and explicitly implemented uplifting, without going through `compiler_for`. The only remaining usages of `compiler_for` are in dist steps (which I'm currently refactoring, will send a PR later) and test steps (which I will take a look at after dist). After that we can finally remove the function. I tried to document the case when uplifting was happening during cross-compilation, which was very implicit before. I also did a slight change in the uplifting logic for rustc when cross-compiling. Before, we would attempt to uplift a stage1 rustc, but that is not really a thing when cross-compiling. r? `@jieyouxu`
2 parents a676c48 + f2c2d3e commit 87d677b

File tree

2 files changed

+86
-93
lines changed

2 files changed

+86
-93
lines changed

bootstrap.example.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -345,9 +345,9 @@
345345
# want to use vendoring. See https://forge.rust-lang.org/infra/other-installation-methods.html#source-code.
346346
#build.vendor = if "is a tarball source" && "vendor" dir exists && ".cargo/config.toml" file exists { true } else { false }
347347

348-
# Typically the build system will build the Rust compiler twice. The second
349-
# compiler, however, will simply use its own libraries to link against. If you
350-
# would rather to perform a full bootstrap, compiling the compiler three times,
348+
# If you build the compiler more than twice (stage3+) or the standard library more than once
349+
# (stage 2+), the third compiler and second library will get uplifted from stage2 and stage1,
350+
# respectively. If you would like to disable this uplifting, and rather perform a full bootstrap,
351351
# then you can set this option to true.
352352
#
353353
# This is only useful for verifying that rustc generates reproducible builds.

src/bootstrap/src/core/build_steps/compile.rs

Lines changed: 83 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,12 @@ use crate::{
3737
debug, trace,
3838
};
3939

40-
/// Build a standard library for the given `target` using the given `compiler`.
40+
/// Build a standard library for the given `target` using the given `build_compiler`.
4141
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
4242
pub struct Std {
4343
pub target: TargetSelection,
44-
pub compiler: Compiler,
44+
/// Compiler that builds the standard library.
45+
pub build_compiler: Compiler,
4546
/// Whether to build only a subset of crates in the standard library.
4647
///
4748
/// This shouldn't be used from other steps; see the comment on [`Rustc`].
@@ -54,10 +55,10 @@ pub struct Std {
5455
}
5556

5657
impl Std {
57-
pub fn new(compiler: Compiler, target: TargetSelection) -> Self {
58+
pub fn new(build_compiler: Compiler, target: TargetSelection) -> Self {
5859
Self {
5960
target,
60-
compiler,
61+
build_compiler,
6162
crates: Default::default(),
6263
force_recompile: false,
6364
extra_rust_args: &[],
@@ -120,7 +121,7 @@ impl Step for Std {
120121
trace!(force_recompile);
121122

122123
run.builder.ensure(Std {
123-
compiler: run.builder.compiler(run.builder.top_stage, run.build_triple()),
124+
build_compiler: run.builder.compiler(run.builder.top_stage, run.build_triple()),
124125
target: run.target,
125126
crates,
126127
force_recompile,
@@ -138,8 +139,8 @@ impl Step for Std {
138139
let target = self.target;
139140

140141
// We already have std ready to be used for stage 0.
141-
if self.compiler.stage == 0 {
142-
let compiler = self.compiler;
142+
if self.build_compiler.stage == 0 {
143+
let compiler = self.build_compiler;
143144
builder.ensure(StdLink::from_std(self, compiler));
144145

145146
return;
@@ -148,9 +149,10 @@ impl Step for Std {
148149
let build_compiler = if builder.download_rustc() && self.force_recompile {
149150
// When there are changes in the library tree with CI-rustc, we want to build
150151
// the stageN library and that requires using stageN-1 compiler.
151-
builder.compiler(self.compiler.stage.saturating_sub(1), builder.config.host_target)
152+
builder
153+
.compiler(self.build_compiler.stage.saturating_sub(1), builder.config.host_target)
152154
} else {
153-
self.compiler
155+
self.build_compiler
154156
};
155157

156158
// When using `download-rustc`, we already have artifacts for the host available. Don't
@@ -187,51 +189,50 @@ impl Step for Std {
187189

188190
let mut target_deps = builder.ensure(StartupObjects { compiler: build_compiler, target });
189191

190-
let compiler_to_use =
191-
builder.compiler_for(build_compiler.stage, build_compiler.host, target);
192-
trace!(?compiler_to_use);
193-
194-
if compiler_to_use != build_compiler
195-
// Never uplift std unless we have compiled stage 1; if stage 1 is compiled,
196-
// uplift it from there.
197-
//
198-
// FIXME: improve `fn compiler_for` to avoid adding stage condition here.
199-
&& build_compiler.stage > 1
192+
// Stage of the stdlib that we're building
193+
let stage = build_compiler.stage;
194+
195+
// If we're building a stage2+ libstd, full bootstrap is
196+
// disabled and we have a stage1 libstd already compiled for the given target,
197+
// then simply uplift a previously built stage1 library.
198+
if build_compiler.stage > 1
199+
&& !builder.config.full_bootstrap
200+
// This estimates if a stage1 libstd exists for the given target. If we're not
201+
// cross-compiling, it should definitely exist by the time we're building a stage2
202+
// libstd.
203+
// Or if we are cross-compiling, and we are building a cross-compiled rustc, then that
204+
// rustc needs to link to a cross-compiled libstd, so again we should have a stage1
205+
// libstd for the given target prepared.
206+
// Even if we guess wrong in the cross-compiled case, the worst that should happen is
207+
// that we build a fresh stage1 libstd below, and then we immediately uplift it, so we
208+
// don't pay the libstd build cost twice.
209+
&& (target == builder.host_target || builder.config.hosts.contains(&target))
200210
{
201-
trace!(
202-
?compiler_to_use,
203-
?build_compiler,
204-
"build_compiler != compiler_to_use, uplifting library"
205-
);
211+
let build_compiler_for_std_to_uplift = builder.compiler(1, builder.host_target);
212+
builder.std(build_compiler_for_std_to_uplift, target);
206213

207-
builder.std(compiler_to_use, target);
208-
let msg = if compiler_to_use.host == target {
214+
let msg = if build_compiler_for_std_to_uplift.host == target {
209215
format!(
210-
"Uplifting library (stage{} -> stage{})",
211-
compiler_to_use.stage, build_compiler.stage
216+
"Uplifting library (stage{} -> stage{stage})",
217+
build_compiler_for_std_to_uplift.stage
212218
)
213219
} else {
214220
format!(
215-
"Uplifting library (stage{}:{} -> stage{}:{})",
216-
compiler_to_use.stage, compiler_to_use.host, build_compiler.stage, target
221+
"Uplifting library (stage{}:{} -> stage{stage}:{target})",
222+
build_compiler_for_std_to_uplift.stage, build_compiler_for_std_to_uplift.host,
217223
)
218224
};
225+
219226
builder.info(&msg);
220227

221228
// Even if we're not building std this stage, the new sysroot must
222229
// still contain the third party objects needed by various targets.
223230
self.copy_extra_objects(builder, &build_compiler, target);
224231

225-
builder.ensure(StdLink::from_std(self, compiler_to_use));
232+
builder.ensure(StdLink::from_std(self, build_compiler_for_std_to_uplift));
226233
return;
227234
}
228235

229-
trace!(
230-
?compiler_to_use,
231-
?build_compiler,
232-
"compiler == compiler_to_use, handling not-cross-compile scenario"
233-
);
234-
235236
target_deps.extend(self.copy_extra_objects(builder, &build_compiler, target));
236237

237238
// We build a sysroot for mir-opt tests using the same trick that Miri does: A check build
@@ -299,7 +300,7 @@ impl Step for Std {
299300
}
300301

301302
fn metadata(&self) -> Option<StepMetadata> {
302-
Some(StepMetadata::build("std", self.target).built_by(self.compiler))
303+
Some(StepMetadata::build("std", self.target).built_by(self.build_compiler))
303304
}
304305
}
305306

@@ -665,6 +666,14 @@ pub fn std_cargo(builder: &Builder<'_>, target: TargetSelection, cargo: &mut Car
665666
cargo.rustdocflag("-Zcrate-attr=warn(rust_2018_idioms)");
666667
}
667668

669+
/// Link all libstd rlibs/dylibs into a sysroot of `target_compiler`.
670+
///
671+
/// Links those artifacts generated by `compiler` to the `stage` compiler's
672+
/// sysroot for the specified `host` and `target`.
673+
///
674+
/// Note that this assumes that `compiler` has already generated the libstd
675+
/// libraries for `target`, and this method will find them in the relevant
676+
/// output directory.
668677
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
669678
pub struct StdLink {
670679
pub compiler: Compiler,
@@ -680,7 +689,7 @@ impl StdLink {
680689
pub fn from_std(std: Std, host_compiler: Compiler) -> Self {
681690
Self {
682691
compiler: host_compiler,
683-
target_compiler: std.compiler,
692+
target_compiler: std.build_compiler,
684693
target: std.target,
685694
crates: std.crates,
686695
force_recompile: std.force_recompile,
@@ -951,14 +960,8 @@ impl Rustc {
951960
}
952961

953962
impl Step for Rustc {
954-
/// We return the stage of the "actual" compiler (not the uplifted one).
955-
///
956-
/// By "actual" we refer to the uplifting logic where we may not compile the requested stage;
957-
/// instead, we uplift it from the previous stages. Which can lead to bootstrap failures in
958-
/// specific situations where we request stage X from other steps. However we may end up
959-
/// uplifting it from stage Y, causing the other stage to fail when attempting to link with
960-
/// stage X which was never actually built.
961-
type Output = u32;
963+
type Output = ();
964+
962965
const IS_HOST: bool = true;
963966
const DEFAULT: bool = false;
964967

@@ -997,7 +1000,7 @@ impl Step for Rustc {
9971000
/// This will build the compiler for a particular stage of the build using
9981001
/// the `build_compiler` targeting the `target` architecture. The artifacts
9991002
/// created will also be linked into the sysroot directory.
1000-
fn run(self, builder: &Builder<'_>) -> u32 {
1003+
fn run(self, builder: &Builder<'_>) {
10011004
let build_compiler = self.build_compiler;
10021005
let target = self.target;
10031006

@@ -1013,7 +1016,7 @@ impl Step for Rustc {
10131016
&sysroot,
10141017
builder.config.ci_rustc_dev_contents(),
10151018
);
1016-
return build_compiler.stage;
1019+
return;
10171020
}
10181021

10191022
// Build a standard library for `target` using the `build_compiler`.
@@ -1027,31 +1030,33 @@ impl Step for Rustc {
10271030
builder.info("WARNING: Use `--keep-stage-std` if you want to rebuild the compiler when it changes");
10281031
builder.ensure(RustcLink::from_rustc(self, build_compiler));
10291032

1030-
return build_compiler.stage;
1033+
return;
10311034
}
10321035

1033-
let compiler_to_use =
1034-
builder.compiler_for(build_compiler.stage, build_compiler.host, target);
1035-
if compiler_to_use != build_compiler {
1036-
builder.ensure(Rustc::new(compiler_to_use, target));
1037-
let msg = if compiler_to_use.host == target {
1038-
format!(
1039-
"Uplifting rustc (stage{} -> stage{})",
1040-
compiler_to_use.stage,
1041-
build_compiler.stage + 1
1042-
)
1036+
// The stage of the compiler that we're building
1037+
let stage = build_compiler.stage + 1;
1038+
1039+
// If we are building a stage3+ compiler, and full bootstrap is disabled, and we have a
1040+
// previous rustc available, we will uplift a compiler from a previous stage.
1041+
if build_compiler.stage >= 2
1042+
&& !builder.config.full_bootstrap
1043+
&& (target == builder.host_target || builder.hosts.contains(&target))
1044+
{
1045+
// If we're cross-compiling, the earliest rustc that we could have is stage 2.
1046+
// If we're not cross-compiling, then we should have rustc stage 1.
1047+
let stage_to_uplift = if target == builder.host_target { 1 } else { 2 };
1048+
let rustc_to_uplift = builder.compiler(stage_to_uplift, target);
1049+
let msg = if rustc_to_uplift.host == target {
1050+
format!("Uplifting rustc (stage{} -> stage{stage})", rustc_to_uplift.stage,)
10431051
} else {
10441052
format!(
1045-
"Uplifting rustc (stage{}:{} -> stage{}:{})",
1046-
compiler_to_use.stage,
1047-
compiler_to_use.host,
1048-
build_compiler.stage + 1,
1049-
target
1053+
"Uplifting rustc (stage{}:{} -> stage{stage}:{target})",
1054+
rustc_to_uplift.stage, rustc_to_uplift.host,
10501055
)
10511056
};
10521057
builder.info(&msg);
1053-
builder.ensure(RustcLink::from_rustc(self, compiler_to_use));
1054-
return compiler_to_use.stage;
1058+
builder.ensure(RustcLink::from_rustc(self, rustc_to_uplift));
1059+
return;
10551060
}
10561061

10571062
// Build a standard library for the current host target using the `build_compiler`.
@@ -1128,8 +1133,6 @@ impl Step for Rustc {
11281133
self,
11291134
builder.compiler(build_compiler.stage, builder.config.host_target),
11301135
));
1131-
1132-
build_compiler.stage
11331136
}
11341137

11351138
fn metadata(&self) -> Option<StepMetadata> {
@@ -1909,12 +1912,18 @@ impl Step for Sysroot {
19091912
}
19101913
}
19111914

1915+
/// Prepare a compiler sysroot.
1916+
///
1917+
/// The sysroot may contain various things useful for running the compiler, like linkers and
1918+
/// linker wrappers (LLD, LLVM bitcode linker, etc.).
1919+
///
1920+
/// This will assemble a compiler in `build/$target/stage$stage`.
19121921
#[derive(Debug, PartialOrd, Ord, Clone, PartialEq, Eq, Hash)]
19131922
pub struct Assemble {
19141923
/// The compiler which we will produce in this step. Assemble itself will
19151924
/// take care of ensuring that the necessary prerequisites to do so exist,
1916-
/// that is, this target can be a stage2 compiler and Assemble will build
1917-
/// previous stages for you.
1925+
/// that is, this can be e.g. a stage2 compiler and Assemble will build
1926+
/// the previous stages for you.
19181927
pub target_compiler: Compiler,
19191928
}
19201929

@@ -1932,11 +1941,6 @@ impl Step for Assemble {
19321941
});
19331942
}
19341943

1935-
/// Prepare a new compiler from the artifacts in `stage`
1936-
///
1937-
/// This will assemble a compiler in `build/$host/stage$stage`. The compiler
1938-
/// must have been previously produced by the `stage - 1` builder.build
1939-
/// compiler.
19401944
fn run(self, builder: &Builder<'_>) -> Compiler {
19411945
let target_compiler = self.target_compiler;
19421946

@@ -2065,7 +2069,7 @@ impl Step for Assemble {
20652069
target_compiler.stage - 1,
20662070
builder.config.host_target,
20672071
);
2068-
let mut build_compiler =
2072+
let build_compiler =
20692073
builder.compiler(target_compiler.stage - 1, builder.config.host_target);
20702074

20712075
// Build enzyme
@@ -2089,24 +2093,13 @@ impl Step for Assemble {
20892093
}
20902094

20912095
// Build the libraries for this compiler to link to (i.e., the libraries
2092-
// it uses at runtime). NOTE: Crates the target compiler compiles don't
2093-
// link to these. (FIXME: Is that correct? It seems to be correct most
2094-
// of the time but I think we do link to these for stage2/bin compilers
2095-
// when not performing a full bootstrap).
2096+
// it uses at runtime).
20962097
debug!(
20972098
?build_compiler,
20982099
"target_compiler.host" = ?target_compiler.host,
20992100
"building compiler libraries to link to"
21002101
);
2101-
let actual_stage = builder.ensure(Rustc::new(build_compiler, target_compiler.host));
2102-
// Current build_compiler.stage might be uplifted instead of being built; so update it
2103-
// to not fail while linking the artifacts.
2104-
debug!(
2105-
"(old) build_compiler.stage" = build_compiler.stage,
2106-
"(adjusted) build_compiler.stage" = actual_stage,
2107-
"temporarily adjusting `build_compiler.stage` to account for uplifted libraries"
2108-
);
2109-
build_compiler.stage = actual_stage;
2102+
builder.ensure(Rustc::new(build_compiler, target_compiler.host));
21102103

21112104
let stage = target_compiler.stage;
21122105
let host = target_compiler.host;

0 commit comments

Comments
 (0)