Add Builder::std method for building a standard library

This commit is contained in:
Jakub Beránek 2025-06-16 15:03:01 +02:00
parent 58d5e11690
commit 450a2d8dc1
No known key found for this signature in database
GPG key ID: 909CD0D26483516B
10 changed files with 84 additions and 41 deletions

View file

@ -1,6 +1,5 @@
//! Implementation of compiling the compiler and standard library, in "check"-based modes.
use crate::core::build_steps::compile;
use crate::core::build_steps::compile::{
add_to_sysroot, run_cargo, rustc_cargo, rustc_cargo_env, std_cargo, std_crates_for_run_make,
};
@ -89,7 +88,7 @@ impl Step for Std {
}
// Reuse the stage0 libstd
builder.ensure(compile::Std::new(compiler, target));
builder.std(compiler, target);
return;
}
@ -223,8 +222,8 @@ impl Step for Rustc {
// the sysroot for the compiler to find. Otherwise, we're going to
// fail when building crates that need to generate code (e.g., build
// scripts and their dependencies).
builder.ensure(crate::core::build_steps::compile::Std::new(compiler, compiler.host));
builder.ensure(crate::core::build_steps::compile::Std::new(compiler, target));
builder.std(compiler, compiler.host);
builder.std(compiler, target);
} else {
builder.ensure(Std::new(target));
}

View file

@ -1,8 +1,8 @@
//! Implementation of running clippy on the compiler, standard library and various tools.
use super::check;
use super::compile::{run_cargo, rustc_cargo, std_cargo};
use super::tool::{SourceType, prepare_tool_cargo};
use super::{check, compile};
use crate::builder::{Builder, ShouldRun};
use crate::core::build_steps::compile::std_crates_for_run_make;
use crate::core::builder;
@ -214,8 +214,8 @@ impl Step for Rustc {
// the sysroot for the compiler to find. Otherwise, we're going to
// fail when building crates that need to generate code (e.g., build
// scripts and their dependencies).
builder.ensure(compile::Std::new(compiler, compiler.host));
builder.ensure(compile::Std::new(compiler, target));
builder.std(compiler, compiler.host);
builder.std(compiler, target);
} else {
builder.ensure(check::Std::new(target));
}

View file

@ -213,7 +213,7 @@ impl Step for Std {
{
trace!(?compiler_to_use, ?compiler, "compiler != compiler_to_use, uplifting library");
builder.ensure(Std::new(compiler_to_use, target));
builder.std(compiler_to_use, target);
let msg = if compiler_to_use.host == target {
format!(
"Uplifting library (stage{} -> stage{})",
@ -690,7 +690,7 @@ pub fn std_cargo(builder: &Builder<'_>, target: TargetSelection, stage: u32, car
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
struct StdLink {
pub struct StdLink {
pub compiler: Compiler,
pub target_compiler: Compiler,
pub target: TargetSelection,
@ -701,7 +701,7 @@ struct StdLink {
}
impl StdLink {
fn from_std(std: Std, host_compiler: Compiler) -> Self {
pub fn from_std(std: Std, host_compiler: Compiler) -> Self {
Self {
compiler: host_compiler,
target_compiler: std.compiler,
@ -1067,7 +1067,7 @@ impl Step for Rustc {
// Build a standard library for `target` using the `build_compiler`.
// This will be the standard library that the rustc which we build *links to*.
builder.ensure(Std::new(build_compiler, target));
builder.std(build_compiler, target);
if builder.config.keep_stage.contains(&build_compiler.stage) {
trace!(stage = build_compiler.stage, "`keep-stage` requested");
@ -1108,10 +1108,10 @@ impl Step for Rustc {
// build scripts and proc macros.
// If we are not cross-compiling, the Std build above will be the same one as the one we
// prepare here.
builder.ensure(Std::new(
builder.std(
builder.compiler(self.build_compiler.stage, builder.config.host_target),
builder.config.host_target,
));
);
let mut cargo = builder::Cargo::new(
builder,
@ -2079,7 +2079,7 @@ impl Step for Assemble {
if builder.download_rustc() {
trace!("`download-rustc` requested, reusing CI compiler for stage > 0");
builder.ensure(Std::new(target_compiler, target_compiler.host));
builder.std(target_compiler, target_compiler.host);
let sysroot =
builder.ensure(Sysroot { compiler: target_compiler, force_recompile: false });
// Ensure that `libLLVM.so` ends up in the newly created target directory,
@ -2087,7 +2087,7 @@ impl Step for Assemble {
dist::maybe_install_llvm_target(builder, target_compiler.host, &sysroot);
// Lower stages use `ci-rustc-sysroot`, not stageN
if target_compiler.stage == builder.top_stage {
builder.info(&format!("Creating a sysroot for stage{stage} compiler (use `rustup toolchain link 'name' build/host/stage{stage}`)", stage=target_compiler.stage));
builder.info(&format!("Creating a sysroot for stage{stage} compiler (use `rustup toolchain link 'name' build/host/stage{stage}`)", stage = target_compiler.stage));
}
let mut precompiled_compiler = target_compiler;

View file

@ -711,7 +711,7 @@ impl Step for Std {
return None;
}
builder.ensure(compile::Std::new(compiler, target));
builder.std(compiler, target);
let mut tarball = Tarball::new(builder, "rust-std", &target.triple);
tarball.include_target_in_component_name(true);

View file

@ -804,7 +804,7 @@ impl Step for Rustc {
// Build the standard library, so that proc-macros can use it.
// (Normally, only the metadata would be necessary, but proc-macros are special since they run at compile-time.)
let compiler = builder.compiler(stage, builder.config.host_target);
builder.ensure(compile::Std::new(compiler, builder.config.host_target));
builder.std(compiler, builder.config.host_target);
let _guard = builder.msg_sysroot_tool(
Kind::Doc,
@ -947,7 +947,7 @@ macro_rules! tool_doc {
t!(fs::create_dir_all(&out));
let compiler = builder.compiler(stage, builder.config.host_target);
builder.ensure(compile::Std::new(compiler, target));
builder.std(compiler, target);
if true $(&& $rustc_tool)? {
// Build rustc docs so that we generate relative links.
@ -1195,7 +1195,7 @@ impl Step for RustcBook {
let rustc = builder.rustc(self.compiler);
// The tool runs `rustc` for extracting output examples, so it needs a
// functional sysroot.
builder.ensure(compile::Std::new(self.compiler, self.target));
builder.std(self.compiler, self.target);
let mut cmd = builder.tool_cmd(Tool::LintDocs);
cmd.arg("--src");
cmd.arg(builder.src.join("compiler"));
@ -1272,7 +1272,7 @@ impl Step for Reference {
// This is needed for generating links to the standard library using
// the mdbook-spec plugin.
builder.ensure(compile::Std::new(self.compiler, builder.config.host_target));
builder.std(self.compiler, builder.config.host_target);
// Run rustbook/mdbook to generate the HTML pages.
builder.ensure(RustbookSrc {

View file

@ -1,7 +1,7 @@
use std::env::consts::EXE_EXTENSION;
use std::fmt::{Display, Formatter};
use crate::core::build_steps::compile::{Std, Sysroot};
use crate::core::build_steps::compile::Sysroot;
use crate::core::build_steps::tool::{RustcPerf, Rustdoc};
use crate::core::builder::Builder;
use crate::core::config::DebuginfoLevel;
@ -152,7 +152,7 @@ Consider setting `rust.debuginfo-level = 1` in `bootstrap.toml`."#);
}
let compiler = builder.compiler(builder.top_stage, builder.config.host_target);
builder.ensure(Std::new(compiler, builder.config.host_target));
builder.std(compiler, builder.config.host_target);
if let Some(opts) = args.cmd.shared_opts()
&& opts.profiles.contains(&Profile::Doc)

View file

@ -10,7 +10,7 @@ use std::{env, fs, iter};
use clap_complete::shells;
use crate::core::build_steps::compile::run_cargo;
use crate::core::build_steps::compile::{Std, run_cargo};
use crate::core::build_steps::doc::DocumentationFormat;
use crate::core::build_steps::gcc::{Gcc, add_cg_gcc_cargo_flags};
use crate::core::build_steps::llvm::get_llvm_version;
@ -544,7 +544,7 @@ impl Step for Miri {
// We also need sysroots, for Miri and for the host (the latter for build scripts).
// This is for the tests so everything is done with the target compiler.
let miri_sysroot = Miri::build_miri_sysroot(builder, target_compiler, target);
builder.ensure(compile::Std::new(target_compiler, host));
builder.std(target_compiler, host);
let host_sysroot = builder.sysroot(target_compiler);
// Miri has its own "target dir" for ui test dependencies. Make sure it gets cleared when
@ -709,7 +709,7 @@ impl Step for CompiletestTest {
// We need `ToolStd` for the locally-built sysroot because
// compiletest uses unstable features of the `test` crate.
builder.ensure(compile::Std::new(compiler, host));
builder.std(compiler, host);
let mut cargo = tool::prepare_tool_cargo(
builder,
compiler,
@ -1009,7 +1009,7 @@ impl Step for RustdocGUI {
}
fn run(self, builder: &Builder<'_>) {
builder.ensure(compile::Std::new(self.compiler, self.target));
builder.std(self.compiler, self.target);
let mut cmd = builder.tool_cmd(Tool::RustdocGUITest);
@ -1634,7 +1634,7 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the
if suite == "mir-opt" {
builder.ensure(compile::Std::new(compiler, compiler.host).is_for_mir_opt_tests(true));
} else {
builder.ensure(compile::Std::new(compiler, compiler.host));
builder.std(compiler, compiler.host);
}
let mut cmd = builder.tool_cmd(Tool::Compiletest);
@ -1642,7 +1642,7 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the
if suite == "mir-opt" {
builder.ensure(compile::Std::new(compiler, target).is_for_mir_opt_tests(true));
} else {
builder.ensure(compile::Std::new(compiler, target));
builder.std(compiler, target);
}
builder.ensure(RemoteCopyLibs { compiler, target });
@ -2177,7 +2177,7 @@ impl BookTest {
fn run_ext_doc(self, builder: &Builder<'_>) {
let compiler = self.compiler;
builder.ensure(compile::Std::new(compiler, compiler.host));
builder.std(compiler, compiler.host);
// mdbook just executes a binary named "rustdoc", so we need to update
// PATH so that it points to our rustdoc.
@ -2263,7 +2263,7 @@ impl BookTest {
let compiler = self.compiler;
let host = self.compiler.host;
builder.ensure(compile::Std::new(compiler, host));
builder.std(compiler, host);
let _guard =
builder.msg(Kind::Test, compiler.stage, format!("book {}", self.name), host, host);
@ -2410,7 +2410,7 @@ impl Step for ErrorIndex {
drop(guard);
// The tests themselves need to link to std, so make sure it is
// available.
builder.ensure(compile::Std::new(compiler, compiler.host));
builder.std(compiler, compiler.host);
markdown_test(builder, compiler, &output);
}
}
@ -2473,7 +2473,7 @@ impl Step for CrateLibrustc {
}
fn run(self, builder: &Builder<'_>) {
builder.ensure(compile::Std::new(self.compiler, self.target));
builder.std(self.compiler, self.target);
// To actually run the tests, delegate to a copy of the `Crate` step.
builder.ensure(Crate {
@ -2641,7 +2641,7 @@ impl Step for Crate {
// Prepare sysroot
// See [field@compile::Std::force_recompile].
builder.ensure(compile::Std::new(compiler, compiler.host).force_recompile(true));
builder.ensure(Std::new(compiler, compiler.host).force_recompile(true));
// If we're not doing a full bootstrap but we're testing a stage2
// version of libstd, then what we're actually testing is the libstd
@ -2767,7 +2767,7 @@ impl Step for CrateRustdoc {
// using `download-rustc`, the rustc_private artifacts may be in a *different sysroot* from
// the target rustdoc (`ci-rustc-sysroot` vs `stage2`). In that case, we need to ensure this
// explicitly to make sure it ends up in the stage2 sysroot.
builder.ensure(compile::Std::new(compiler, target));
builder.std(compiler, target);
builder.ensure(compile::Rustc::new(compiler, target));
let mut cargo = tool::prepare_tool_cargo(
@ -2911,7 +2911,7 @@ impl Step for RemoteCopyLibs {
return;
}
builder.ensure(compile::Std::new(compiler, target));
builder.std(compiler, target);
builder.info(&format!("REMOTE copy libs to emulator ({target})"));
@ -3101,7 +3101,7 @@ impl Step for TierCheck {
/// Tests the Platform Support page in the rustc book.
fn run(self, builder: &Builder<'_>) {
builder.ensure(compile::Std::new(self.compiler, self.compiler.host));
builder.std(self.compiler, self.compiler.host);
let mut cargo = tool::prepare_tool_cargo(
builder,
self.compiler,
@ -3334,7 +3334,7 @@ impl Step for CodegenCranelift {
let compiler = self.compiler;
let target = self.target;
builder.ensure(compile::Std::new(compiler, target));
builder.std(compiler, target);
// If we're not doing a full bootstrap but we're testing a stage2
// version of libstd, then what we're actually testing is the libstd

View file

@ -122,14 +122,14 @@ impl Step for ToolBuild {
Mode::ToolRustc => {
// If compiler was forced, its artifacts should be prepared earlier.
if !self.compiler.is_forced_compiler() {
builder.ensure(compile::Std::new(self.compiler, self.compiler.host));
builder.std(self.compiler, self.compiler.host);
builder.ensure(compile::Rustc::new(self.compiler, target));
}
}
Mode::ToolStd => {
// If compiler was forced, its artifacts should be prepared earlier.
if !self.compiler.is_forced_compiler() {
builder.ensure(compile::Std::new(self.compiler, target))
builder.std(self.compiler, target)
}
}
Mode::ToolBootstrap => {} // uses downloaded stage0 compiler libs

View file

@ -3,8 +3,8 @@ use std::ffi::{OsStr, OsString};
use std::path::{Path, PathBuf};
use super::{Builder, Kind};
use crate::core::build_steps::test;
use crate::core::build_steps::tool::SourceType;
use crate::core::build_steps::{compile, test};
use crate::core::config::SplitDebuginfo;
use crate::core::config::flags::Color;
use crate::utils::build_stamp;
@ -842,7 +842,7 @@ impl Builder<'_> {
// If this is for `miri-test`, prepare the sysroots.
if cmd_kind == Kind::MiriTest {
self.ensure(compile::Std::new(compiler, compiler.host));
self.std(compiler, compiler.host);
let host_sysroot = self.sysroot(compiler);
let miri_sysroot = test::Miri::build_miri_sysroot(self, compiler, target);
cargo.env("MIRI_SYSROOT", &miri_sysroot);

View file

@ -15,6 +15,7 @@ use tracing::instrument;
pub use self::cargo::{Cargo, cargo_profile_var};
pub use crate::Compiler;
use crate::core::build_steps::compile::{Std, StdLink};
use crate::core::build_steps::{
check, clean, clippy, compile, dist, doc, gcc, install, llvm, run, setup, test, tool, vendor,
};
@ -1350,6 +1351,49 @@ impl<'a> Builder<'a> {
resolved_compiler
}
/// Obtain a standard library for the given target that will be built by the passed compiler.
/// The standard library will be linked to the sysroot of the passed compiler.
///
/// Prefer using this method rather than manually invoking `Std::new`.
#[cfg_attr(
feature = "tracing",
instrument(
level = "trace",
name = "Builder::std",
target = "STD",
skip_all,
fields(
compiler = ?compiler,
target = ?target,
),
),
)]
pub fn std(&self, compiler: Compiler, target: TargetSelection) {
// 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
// actually build anything.
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.
You have to build a stage1 compiler for `{}` first, and then use it to build a standard library for `{target}`.
",
compiler.host
)
}
// We still need to link the prebuilt standard library into the ephemeral stage0 sysroot
self.ensure(StdLink::from_std(Std::new(compiler, target), compiler));
} 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.
self.ensure(Std::new(compiler, target));
}
}
pub fn sysroot(&self, compiler: Compiler) -> PathBuf {
self.ensure(compile::Sysroot::new(compiler))
}