Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions src/bootstrap/src/core/build_steps/compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -167,8 +167,13 @@ impl Step for Std {
fn run(self, builder: &Builder<'_>) -> Self::Output {
let target = self.target;

// We already have std ready to be used for stage 0.
if self.build_compiler.stage == 0 {
// In most cases, we already have the std ready to be used for stage 0.
// However, if we are doing a local rebuild (so the build compiler can compile the standard
// library even on stage 0), and we're cross-compiling (so the stage0 standard library for
// *target* is not available), we still allow the stdlib to be built here.
if self.build_compiler.stage == 0
&& !(builder.local_rebuild && target != builder.host_target)
{
let compiler = self.build_compiler;
builder.ensure(StdLink::from_std(self, compiler));

Expand Down
28 changes: 18 additions & 10 deletions src/bootstrap/src/core/builder/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1435,22 +1435,30 @@ impl<'a> Builder<'a> {
// FIXME: make the `Std` step return some type-level "proof" that std was indeed built,
// and then require passing that to all Cargo invocations that we do.

// The "stage 0" std is always precompiled and comes with the stage0 compiler, so we have
// special logic for it, to avoid creating needless and confusing Std steps that don't
// The "stage 0" std is almost always precompiled and comes with the stage0 compiler, so we
// have special logic for it, to avoid creating needless and confusing Std steps that don't
// actually build anything.
// We only allow building the stage0 stdlib if we do a local rebuild, so the stage0 compiler
// actually comes from in-tree sources, and we're cross-compiling, so the stage0 for the
// given `target` is not available.
if compiler.stage == 0 {
if target != compiler.host {
panic!(
r"It is not possible to build the standard library for `{target}` using the stage0 compiler.
if self.local_rebuild {
self.ensure(Std::new(compiler, target))
} else {
panic!(
r"It is not possible to build the standard library for `{target}` using the stage0 compiler.
You have to build a stage1 compiler for `{}` first, and then use it to build a standard library for `{target}`.
Alternatively, you can set `build.local-rebuild=true` and use a stage0 compiler built from in-tree sources.
",
compiler.host
)
compiler.host
)
}
} else {
// We still need to link the prebuilt standard library into the ephemeral stage0 sysroot
self.ensure(StdLink::from_std(Std::new(compiler, target), compiler));
None
}

// We still need to link the prebuilt standard library into the ephemeral stage0 sysroot
self.ensure(StdLink::from_std(Std::new(compiler, target), compiler));
None
} else {
// This step both compiles the std and links it into the compiler's sysroot.
// Yes, it's quite magical and side-effecty.. would be nice to refactor later.
Expand Down
28 changes: 28 additions & 0 deletions src/bootstrap/src/core/builder/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -853,6 +853,18 @@ mod snapshot {
ctx.config("build").path("library").stage(0).run();
}

#[test]
fn build_library_stage_0_local_rebuild() {
let ctx = TestCtx::new();
insta::assert_snapshot!(
ctx.config("build")
.path("library")
.stage(0)
.targets(&[TEST_TRIPLE_1])
.args(&["--set", "build.local-rebuild=true"])
.render_steps(), @"[build] rustc 0 <host> -> std 0 <target1>");
}

#[test]
fn build_library_stage_1() {
let ctx = TestCtx::new();
Expand Down Expand Up @@ -1696,6 +1708,22 @@ mod snapshot {
");
}

#[test]
fn dist_library_stage_0_local_rebuild() {
let ctx = TestCtx::new();
insta::assert_snapshot!(
ctx.config("dist")
.path("rust-std")
.stage(0)
.targets(&[TEST_TRIPLE_1])
.args(&["--set", "build.local-rebuild=true"])
.render_steps(), @r"
[build] rustc 0 <host> -> std 0 <target1>
[build] rustc 0 <host> -> RustInstaller 1 <host>
[dist] rustc 0 <host> -> std 0 <target1>
");
}

#[test]
fn check_compiler_no_explicit_stage() {
let ctx = TestCtx::new();
Expand Down
35 changes: 21 additions & 14 deletions src/bootstrap/src/core/config/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -969,31 +969,38 @@ impl Config {
| Subcommand::Vendor { .. } => flags_stage.unwrap_or(0),
};

// Now check that the selected stage makes sense, and if not, print a warning and end
let local_rebuild = build_local_rebuild.unwrap_or(false);

let check_stage0 = |kind: &str| {
if local_rebuild {
eprintln!("WARNING: running {kind} in stage 0. This might not work as expected.");
} else {
eprintln!(
"ERROR: cannot {kind} anything on stage 0. Use at least stage 1 or set build.local-rebuild=true and use a stage0 compiler built from in-tree sources."
);
exit!(1);
}
};

// Now check that the selected stage makes sense, and if not, print an error and end
match (stage, &flags_cmd) {
(0, Subcommand::Build { .. }) => {
eprintln!("ERROR: cannot build anything on stage 0. Use at least stage 1.");
exit!(1);
check_stage0("build");
}
(0, Subcommand::Check { .. }) => {
eprintln!("ERROR: cannot check anything on stage 0. Use at least stage 1.");
exit!(1);
check_stage0("check");
}
(0, Subcommand::Doc { .. }) => {
eprintln!("ERROR: cannot document anything on stage 0. Use at least stage 1.");
exit!(1);
check_stage0("doc");
}
(0, Subcommand::Clippy { .. }) => {
eprintln!("ERROR: cannot run clippy on stage 0. Use at least stage 1.");
exit!(1);
check_stage0("clippy");
}
(0, Subcommand::Dist) => {
eprintln!("ERROR: cannot dist anything on stage 0. Use at least stage 1.");
exit!(1);
check_stage0("dist");
}
(0, Subcommand::Install) => {
eprintln!("ERROR: cannot install anything on stage 0. Use at least stage 1.");
exit!(1);
check_stage0("install");
}
_ => {}
}
Expand Down Expand Up @@ -1234,7 +1241,7 @@ impl Config {
llvm_use_libcxx: llvm_use_libcxx.unwrap_or(false),
llvm_use_linker,
llvm_version_suffix,
local_rebuild: build_local_rebuild.unwrap_or(false),
local_rebuild,
locked_deps: build_locked_deps.unwrap_or(false),
low_priority: build_low_priority.unwrap_or(false),
mandir: install_mandir.map(PathBuf::from),
Expand Down
5 changes: 5 additions & 0 deletions src/bootstrap/src/utils/change_tracker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -526,4 +526,9 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[
severity: ChangeSeverity::Info,
summary: "The `optimized-compiler-builtins` option now accepts a path to an existing compiler-rt builtins library.",
},
ChangeInfo {
change_id: 145876,
severity: ChangeSeverity::Info,
summary: "It is now possible to `check/build/dist` the standard stage 0 library if you use a stage0 rustc built from in-tree sources. This is useful for quickly cross-compiling the standard library. You have to enable build.local-rebuild for this to work.",
},
];
Loading