diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 7657db7b865..9b7fa428136 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -161,6 +161,8 @@ jobs: - run: rustup update --no-self-update stable - run: rustup update --no-self-update ${{ matrix.rust }} && rustup default ${{ matrix.rust }} - run: rustup target add ${{ matrix.other }} + - run: rustup target add aarch64-unknown-none # need this for build-std mock tests + if: startsWith(matrix.rust, 'nightly') - run: rustup component add rustc-dev llvm-tools-preview rust-docs if: startsWith(matrix.rust, 'nightly') - run: sudo apt update -y && sudo apt install lldb gcc-multilib libsecret-1-0 libsecret-1-dev -y diff --git a/src/cargo/core/compiler/standard_lib.rs b/src/cargo/core/compiler/standard_lib.rs index 441d970715d..5e9fe6af494 100644 --- a/src/cargo/core/compiler/standard_lib.rs +++ b/src/cargo/core/compiler/standard_lib.rs @@ -9,54 +9,38 @@ 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 { +fn std_crates<'a>(crates: &'a [String], default: &'static str, units: &[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(default); + } 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> { - 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 { + // 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 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()); - } - } - } 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()) + crates.insert("test"); } + } else if crates.contains("core") { + crates.insert("compiler_builtins"); } - Some(crates) + crates } /// Resolve the standard library dependencies. @@ -64,7 +48,6 @@ pub fn resolve_std<'gctx>( ws: &Workspace<'gctx>, target_data: &mut RustcTargetData<'gctx>, build_config: &BuildConfig, - crates: &[String], ) -> CargoResult<(PackageSet<'gctx>, Resolve, ResolvedFeatures)> { if build_config.build_plan { ws.gctx() @@ -72,17 +55,6 @@ pub fn resolve_std<'gctx>( .warn("-Zbuild-std does not currently fully support --build-plan")?; } - // check that targets support building std - if crates.contains(&"std".to_string()) { - let unsupported_targets = target_data.get_unsupported_std_targets(); - if !unsupported_targets.is_empty() { - anyhow::bail!( - "building std is not supported on the following targets: {}", - unsupported_targets.join(", ") - ) - } - } - let src_path = detect_sysroot_src_path(target_data)?; let std_ws_manifest_path = src_path.join("Cargo.toml"); let gctx = ws.gctx(); @@ -93,12 +65,10 @@ pub fn resolve_std<'gctx>( // `[dev-dependencies]`. No need for us to generate a `Resolve` which has // those included because we'll never use them anyway. 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); - spec_pkgs.push("sysroot".to_string()); - let spec = Packages::Packages(spec_pkgs); - let specs = spec.to_package_id_specs(&std_ws)?; + // `sysroot` + the default feature set below should give us a good default + // Resolve, which includes `libtest` as well. + let specs = Packages::Packages(vec!["sysroot".into()]); + let specs = specs.to_package_id_specs(&std_ws)?; let features = match &gctx.cli_unstable().build_std_features { Some(list) => list.clone(), None => vec![ @@ -128,11 +98,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], @@ -141,15 +113,52 @@ pub fn generate_std_roots( profiles: &Profiles, target_data: &RustcTargetData<'_>, ) -> CargoResult>> { - // Generate the root Units for the standard library. - let std_ids = crates + // Generate a map of Units for each kind requested. + let mut ret = HashMap::new(); + let (core_only, maybe_std): (Vec<&CompileKind>, Vec<_>) = kinds.iter().partition(|kind| + // Only include targets that explicitly don't support std + target_data.info(**kind).supports_std == Some(false)); + for (default_crate, kinds) in [("core", core_only), ("std", maybe_std)] { + if kinds.is_empty() { + continue; + } + generate_roots( + &mut ret, + default_crate, + crates, + units, + std_resolve, + std_features, + &kinds, + package_set, + interner, + profiles, + target_data, + )?; + } + + Ok(ret) +} + +fn generate_roots( + ret: &mut HashMap>, + default: &'static str, + crates: &[String], + units: &[Unit], + std_resolve: &Resolve, + std_features: &ResolvedFeatures, + kinds: &[&CompileKind], + package_set: &PackageSet<'_>, + interner: &UnitInterner, + profiles: &Profiles, + target_data: &RustcTargetData<'_>, +) -> CargoResult<()> { + let std_ids = std_crates(crates, default, units) .iter() .map(|crate_name| std_resolve.query(crate_name)) .collect::>>()?; - // Convert PackageId to Package. let std_pkgs = package_set.get_many(std_ids)?; - // Generate a map of Units for each kind requested. - let mut ret = HashMap::new(); + for pkg in std_pkgs { let lib = pkg .targets() @@ -162,25 +171,26 @@ pub fn generate_std_roots( let mode = CompileMode::Build; let features = std_features.activated_features(pkg.package_id(), FeaturesFor::NormalOrDev); for kind in kinds { - let list = ret.entry(*kind).or_insert_with(Vec::new); - let unit_for = UnitFor::new_normal(*kind); + let kind = **kind; + let list = ret.entry(kind).or_insert_with(Vec::new); + let unit_for = UnitFor::new_normal(kind); let profile = profiles.get_profile( pkg.package_id(), /*is_member*/ false, /*is_local*/ false, unit_for, - *kind, + kind, ); list.push(interner.intern( pkg, lib, profile, - *kind, + kind, mode, features.clone(), - target_data.info(*kind).rustflags.clone(), - target_data.info(*kind).rustdocflags.clone(), - target_data.target_config(*kind).links_overrides.clone(), + target_data.info(kind).rustflags.clone(), + target_data.info(kind).rustdocflags.clone(), + target_data.target_config(kind).links_overrides.clone(), /*is_std*/ true, /*dep_hash*/ 0, IsArtifact::No, @@ -188,7 +198,7 @@ pub fn generate_std_roots( )); } } - Ok(ret) + Ok(()) } fn detect_sysroot_src_path(target_data: &RustcTargetData<'_>) -> CargoResult { diff --git a/src/cargo/core/features.rs b/src/cargo/core/features.rs index 55eae5f4795..4e17898d6e0 100644 --- a/src/cargo/core/features.rs +++ b/src/cargo/core/features.rs @@ -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> = ("Enable Cargo to compile the standard library itself as part of a crate graph compilation"), build_std_features: Option> = ("Configure features enabled for the standard library itself when building the standard library"), cargo_lints: bool = ("Enable the `[lints.cargo]` table"), @@ -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>, D::Error> -where - D: serde::Deserializer<'de>, -{ - let Some(crates) = >>::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 { @@ -1148,7 +1134,8 @@ impl CliUnstable { } } - fn parse_features(value: Option<&str>) -> Vec { + /// Parse a comma-separated list + fn parse_list(value: Option<&str>) -> Vec { match value { None => Vec::new(), Some("") => Vec::new(), @@ -1197,7 +1184,7 @@ impl CliUnstable { match k { // Permanently unstable features // Sorted alphabetically: - "allow-features" => self.allow_features = Some(parse_features(v).into_iter().collect()), + "allow-features" => self.allow_features = Some(parse_list(v).into_iter().collect()), "print-im-a-teapot" => self.print_im_a_teapot = parse_bool(k, v)?, // Stabilized features @@ -1216,7 +1203,7 @@ impl CliUnstable { // until we feel confident to remove entirely. // // See rust-lang/cargo#11168 - let feats = parse_features(v); + let feats = parse_list(v); let stab_is_not_empty = feats.iter().any(|feat| { matches!( feat.as_str(), @@ -1256,10 +1243,8 @@ 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-features" => self.build_std_features = Some(parse_features(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)?, "config-include" => self.config_include = parse_empty(k, v)?, diff --git a/src/cargo/ops/cargo_compile/mod.rs b/src/cargo/ops/cargo_compile/mod.rs index d03e529be74..4e011e7ec5a 100644 --- a/src/cargo/ops/cargo_compile/mod.rs +++ b/src/cargo/ops/cargo_compile/mod.rs @@ -289,9 +289,9 @@ pub fn create_bcx<'a, 'gctx>( resolved_features, } = resolve; - let std_resolve_features = if let Some(crates) = &gctx.cli_unstable().build_std { + let std_resolve_features = if gctx.cli_unstable().build_std.is_some() { let (std_package_set, std_resolve, std_features) = - standard_lib::resolve_std(ws, &mut target_data, &build_config, crates)?; + standard_lib::resolve_std(ws, &mut target_data, &build_config)?; pkg_set.add_set(std_package_set); Some((std_resolve, std_features)) } else { @@ -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, diff --git a/src/cargo/ops/cargo_fetch.rs b/src/cargo/ops/cargo_fetch.rs index 37b56438cd6..b1e596f6485 100644 --- a/src/cargo/ops/cargo_fetch.rs +++ b/src/cargo/ops/cargo_fetch.rs @@ -64,10 +64,8 @@ pub fn fetch<'a>( } // If -Zbuild-std was passed, download dependencies for the standard library. - // We don't know ahead of time what jobs we'll be running, so tell `std_crates` that. - if let Some(crates) = standard_lib::std_crates(gctx, None) { - let (std_package_set, _, _) = - standard_lib::resolve_std(ws, &mut data, &build_config, &crates)?; + if gctx.cli_unstable().build_std.is_some() { + let (std_package_set, _, _) = standard_lib::resolve_std(ws, &mut data, &build_config)?; packages.add_set(std_package_set); } diff --git a/tests/testsuite/standard_lib.rs b/tests/testsuite/standard_lib.rs index 214df036561..16645c17bce 100644 --- a/tests/testsuite/standard_lib.rs +++ b/tests/testsuite/standard_lib.rs @@ -389,8 +389,29 @@ fn check_core() { .run(); } -#[cargo_test(build_std_mock)] -fn test_std_on_unsupported_target() { +#[cargo_test(build_std_mock, requires = "rustup")] +fn build_std_with_no_arg_for_core_only_target() { + let has_rustup_aarch64_unknown_none = std::process::Command::new("rustup") + .args(["target", "list", "--installed"]) + .output() + .ok() + .map(|output| { + String::from_utf8(output.stdout) + .map(|stdout| stdout.contains("aarch64-unknown-none")) + .unwrap_or_default() + }) + .unwrap_or_default(); + if !has_rustup_aarch64_unknown_none { + let msg = + "to run this test, run `rustup target add aarch64-unknown-none --toolchain nightly`"; + if cargo_util::is_ci() { + panic!("{msg}"); + } else { + eprintln!("{msg}"); + } + return; + } + let setup = setup(); let p = project() @@ -405,14 +426,75 @@ fn test_std_on_unsupported_target() { ) .build(); - p.cargo("build") + p.cargo("build -v") .arg("--target=aarch64-unknown-none") - .arg("--target=x86_64-unknown-none") .build_std(&setup) - .with_status(101) - .with_stderr_data(str![[r#" -[ERROR] building std is not supported on the following targets: [..] -"#]]) + .with_stderr_data( + str![[r#" +[UPDATING] `dummy-registry` index +[DOWNLOADING] crates ... +[DOWNLOADED] registry-dep-using-std v1.0.0 (registry `dummy-registry`) +[DOWNLOADED] registry-dep-using-core v1.0.0 (registry `dummy-registry`) +[DOWNLOADED] registry-dep-using-alloc v1.0.0 (registry `dummy-registry`) +[COMPILING] compiler_builtins v0.1.0 ([..]/library/compiler_builtins) +[COMPILING] core v0.1.0 ([..]/library/core) +[COMPILING] foo v0.0.1 ([ROOT]/foo) +[RUNNING] `[..] rustc --crate-name compiler_builtins [..]--target aarch64-unknown-none[..]` +[RUNNING] `[..] rustc --crate-name core [..]--target aarch64-unknown-none[..]` +[RUNNING] `[..] rustc --crate-name foo [..]--target aarch64-unknown-none[..]` +[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s + +"#]] + .unordered(), + ) + .run(); + + p.cargo("clean").run(); + + // Also work for a mix of std and core-only targets, + // though not sure how common it is... + // + // Note that we don't download std dependencies for the second call + // because `-Zbuild-std` downloads them all also when building for core only. + p.cargo("build -v") + .arg("--target=aarch64-unknown-none") + .target_host() + .build_std(&setup) + .with_stderr_data( + str![[r#" +[UPDATING] `dummy-registry` index +[COMPILING] core v0.1.0 ([..]/library/core) +[COMPILING] dep_test v0.1.0 ([..]/dep_test) +[COMPILING] compiler_builtins v0.1.0 ([..]/library/compiler_builtins) +[COMPILING] proc_macro v0.1.0 ([..]/library/proc_macro) +[COMPILING] panic_unwind v0.1.0 ([..]/library/panic_unwind) +[COMPILING] rustc-std-workspace-core v1.9.0 ([..]/library/rustc-std-workspace-core) +[COMPILING] foo v0.0.1 ([ROOT]/foo) +[COMPILING] registry-dep-using-core v1.0.0 +[COMPILING] alloc v0.1.0 ([..]/library/alloc) +[COMPILING] rustc-std-workspace-alloc v1.9.0 ([..]/library/rustc-std-workspace-alloc) +[COMPILING] registry-dep-using-alloc v1.0.0 +[COMPILING] std v0.1.0 ([..]/library/std) +[RUNNING] `[..]rustc --crate-name compiler_builtins [..]--target aarch64-unknown-none[..]` +[RUNNING] `[..]rustc --crate-name core [..]--target aarch64-unknown-none[..]` +[RUNNING] `[..]rustc --crate-name foo [..]--target aarch64-unknown-none[..]` +[RUNNING] `[..]rustc --crate-name core [..]--target [HOST_TARGET][..]` +[RUNNING] `[..]rustc --crate-name dep_test [..]--target [HOST_TARGET][..]` +[RUNNING] `[..]rustc --crate-name proc_macro [..]--target [HOST_TARGET][..]` +[RUNNING] `[..]rustc --crate-name panic_unwind [..]--target [HOST_TARGET][..]` +[RUNNING] `[..]rustc --crate-name compiler_builtins [..]--target [HOST_TARGET][..]` +[RUNNING] `[..]rustc --crate-name rustc_std_workspace_core [..]--target [HOST_TARGET][..]` +[RUNNING] `[..]rustc --crate-name registry_dep_using_core [..]--target [HOST_TARGET][..]` +[RUNNING] `[..]rustc --crate-name alloc [..]--target [HOST_TARGET][..]` +[RUNNING] `[..]rustc --crate-name rustc_std_workspace_alloc [..]--target [HOST_TARGET][..]` +[RUNNING] `[..]rustc --crate-name registry_dep_using_alloc [..]--target [HOST_TARGET][..]` +[RUNNING] `[..]rustc --crate-name std [..]--target [HOST_TARGET][..]` +[RUNNING] `[..]rustc --crate-name foo [..]--target [HOST_TARGET][..]` +[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s + +"#]] + .unordered(), + ) .run(); }