Skip to content

Commit

Permalink
refactor(build-std): defer std crates to build
Browse files Browse the repository at this point in the history
Instead of altering the original `-Zbuild-std` arg value,
this defers the timing of detereming which crate to build
to when Cargo generates std root units.
  • Loading branch information
weihanglo committed Dec 6, 2024
1 parent e18b64f commit 30d11ce
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 55 deletions.
63 changes: 26 additions & 37 deletions src/cargo/core/compiler/standard_lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,54 +9,40 @@ use crate::core::resolver::HasDevUnits;
use crate::core::{PackageId, PackageSet, Resolve, Workspace};
use crate::ops::{self, Packages};
use crate::util::errors::CargoResult;
use crate::GlobalContext;

use std::collections::{HashMap, HashSet};
use std::path::PathBuf;

use super::BuildConfig;

/// Parse the `-Zbuild-std` flag.
pub fn parse_unstable_flag(value: Option<&str>) -> Vec<String> {
fn std_crates<'a>(crates: &'a [String], units: Option<&[Unit]>) -> HashSet<&'a str> {
let mut crates = HashSet::from_iter(crates.iter().map(|s| s.as_str()));
// This is a temporary hack until there is a more principled way to
// declare dependencies in Cargo.toml.
let value = value.unwrap_or("std");
let mut crates: HashSet<&str> = value.split(',').collect();
if crates.is_empty() {
crates.insert("std");
}
if crates.contains("std") {
crates.insert("core");
crates.insert("alloc");
crates.insert("proc_macro");
crates.insert("panic_unwind");
crates.insert("compiler_builtins");
} else if crates.contains("core") {
crates.insert("compiler_builtins");
}
crates.into_iter().map(|s| s.to_string()).collect()
}

pub(crate) fn std_crates(gctx: &GlobalContext, units: Option<&[Unit]>) -> Option<Vec<String>> {
let crates = gctx.cli_unstable().build_std.as_ref()?.clone();

// Only build libtest if it looks like it is needed.
let mut crates = crates.clone();
// If we know what units we're building, we can filter for libtest depending on the jobs.
if let Some(units) = units {
if units
.iter()
.any(|unit| unit.mode.is_rustc_test() && unit.target.harness())
{
// Only build libtest when libstd is built (libtest depends on libstd)
if crates.iter().any(|c| c == "std") && !crates.iter().any(|c| c == "test") {
crates.push("test".to_string());
// Only build libtest if it looks like it is needed (libtest depends on libstd)
// If we know what units we're building, we can filter for libtest depending on the jobs.
if let Some(units) = units {
if units
.iter()
.any(|unit| unit.mode.is_rustc_test() && unit.target.harness())
{
crates.insert("test");
}
}
} else {
// We don't know what jobs are going to be run, so download libtest just in case.
if !crates.iter().any(|c| c == "test") {
crates.push("test".to_string())
}
} else if crates.contains("core") {
crates.insert("compiler_builtins");
}

Some(crates)
crates
}

/// Resolve the standard library dependencies.
Expand All @@ -66,14 +52,16 @@ pub fn resolve_std<'gctx>(
build_config: &BuildConfig,
crates: &[String],
) -> CargoResult<(PackageSet<'gctx>, Resolve, ResolvedFeatures)> {
let crates = std_crates(crates, None);

if build_config.build_plan {
ws.gctx()
.shell()
.warn("-Zbuild-std does not currently fully support --build-plan")?;
}

// check that targets support building std
if crates.contains(&"std".to_string()) {
if crates.contains("std") {
let unsupported_targets = target_data.get_unsupported_std_targets();
if !unsupported_targets.is_empty() {
anyhow::bail!(
Expand All @@ -95,7 +83,7 @@ pub fn resolve_std<'gctx>(
std_ws.set_require_optional_deps(false);
// `sysroot` is not in the default set because it is optional, but it needs
// to be part of the resolve in case we do need it or `libtest`.
let mut spec_pkgs = Vec::from(crates);
let mut spec_pkgs: Vec<String> = crates.iter().map(|s| s.to_string()).collect();
spec_pkgs.push("sysroot".to_string());
let spec = Packages::Packages(spec_pkgs);
let specs = spec.to_package_id_specs(&std_ws)?;
Expand Down Expand Up @@ -128,11 +116,13 @@ pub fn resolve_std<'gctx>(
))
}

/// Generate a list of root `Unit`s for the standard library.
/// Generates a map of root units for the standard library for each kind requested.
///
/// The given slice of crate names is the root set.
/// * `crates` is the arg value from `-Zbuild-std`.
/// * `units` is the root units of the build.
pub fn generate_std_roots(
crates: &[String],
units: &[Unit],
std_resolve: &Resolve,
std_features: &ResolvedFeatures,
kinds: &[CompileKind],
Expand All @@ -141,8 +131,7 @@ pub fn generate_std_roots(
profiles: &Profiles,
target_data: &RustcTargetData<'_>,
) -> CargoResult<HashMap<CompileKind, Vec<Unit>>> {
// Generate the root Units for the standard library.
let std_ids = crates
let std_ids = std_crates(crates, Some(units))
.iter()
.map(|crate_name| std_resolve.query(crate_name))
.collect::<CargoResult<Vec<PackageId>>>()?;
Expand Down
18 changes: 1 addition & 17 deletions src/cargo/core/features.rs
Original file line number Diff line number Diff line change
Expand Up @@ -759,7 +759,6 @@ unstable_cli_options!(
avoid_dev_deps: bool = ("Avoid installing dev-dependencies if possible"),
binary_dep_depinfo: bool = ("Track changes to dependency artifacts"),
bindeps: bool = ("Allow Cargo packages to depend on bin, cdylib, and staticlib crates, and use the artifacts built by those crates"),
#[serde(deserialize_with = "deserialize_build_std")]
build_std: Option<Vec<String>> = ("Enable Cargo to compile the standard library itself as part of a crate graph compilation"),
build_std_features: Option<Vec<String>> = ("Configure features enabled for the standard library itself when building the standard library"),
cargo_lints: bool = ("Enable the `[lints.cargo]` table"),
Expand Down Expand Up @@ -873,19 +872,6 @@ const STABILIZED_LINTS: &str = "The `[lints]` table is now always available.";
const STABILIZED_CHECK_CFG: &str =
"Compile-time checking of conditional (a.k.a. `-Zcheck-cfg`) is now always enabled.";

fn deserialize_build_std<'de, D>(deserializer: D) -> Result<Option<Vec<String>>, D::Error>
where
D: serde::Deserializer<'de>,
{
let Some(crates) = <Option<Vec<String>>>::deserialize(deserializer)? else {
return Ok(None);
};
let v = crates.join(",");
Ok(Some(
crate::core::compiler::standard_lib::parse_unstable_flag(Some(&v)),
))
}

#[derive(Debug, Copy, Clone, Default, Deserialize, Ord, PartialOrd, Eq, PartialEq)]
#[serde(default)]
pub struct GitFeatures {
Expand Down Expand Up @@ -1257,9 +1243,7 @@ impl CliUnstable {
"avoid-dev-deps" => self.avoid_dev_deps = parse_empty(k, v)?,
"binary-dep-depinfo" => self.binary_dep_depinfo = parse_empty(k, v)?,
"bindeps" => self.bindeps = parse_empty(k, v)?,
"build-std" => {
self.build_std = Some(crate::core::compiler::standard_lib::parse_unstable_flag(v))
}
"build-std" => self.build_std = Some(parse_list(v)),
"build-std-features" => self.build_std_features = Some(parse_list(v)),
"cargo-lints" => self.cargo_lints = parse_empty(k, v)?,
"codegen-backend" => self.codegen_backend = parse_empty(k, v)?,
Expand Down
3 changes: 2 additions & 1 deletion src/cargo/ops/cargo_compile/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -398,10 +398,11 @@ pub fn create_bcx<'a, 'gctx>(
Vec::new()
};

let std_roots = if let Some(crates) = standard_lib::std_crates(gctx, Some(&units)) {
let std_roots = if let Some(crates) = gctx.cli_unstable().build_std.as_ref() {
let (std_resolve, std_features) = std_resolve_features.as_ref().unwrap();
standard_lib::generate_std_roots(
&crates,
&units,
std_resolve,
std_features,
&explicit_host_kinds,
Expand Down

0 comments on commit 30d11ce

Please sign in to comment.