diff --git a/src/cargo/core/compiler/standard_lib.rs b/src/cargo/core/compiler/standard_lib.rs index 6b76a5681be..7248495cec9 100644 --- a/src/cargo/core/compiler/standard_lib.rs +++ b/src/cargo/core/compiler/standard_lib.rs @@ -8,6 +8,7 @@ use crate::core::resolver::HasDevUnits; use crate::core::{Dependency, PackageId, PackageSet, Resolve, SourceId, Workspace}; use crate::ops::{self, Packages}; use crate::util::errors::CargoResult; +use std::collections::hash_map::Entry; use std::collections::{HashMap, HashSet}; use std::env; use std::path::PathBuf; @@ -133,7 +134,8 @@ pub fn generate_std_roots( crates: &[String], std_resolve: &Resolve, std_features: &ResolvedFeatures, - kinds: &[CompileKind], + requested_kinds: &[CompileKind], + explicit_host_kind: CompileKind, package_set: &PackageSet<'_>, interner: &UnitInterner, profiles: &Profiles, @@ -147,41 +149,58 @@ pub fn generate_std_roots( 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() - .iter() - .find(|t| t.is_lib()) - .expect("std has a lib"); - let unit_for = UnitFor::new_normal(); - // I don't think we need to bother with Check here, the difference - // in time is minimal, and the difference in caching is - // significant. - 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 std_pkg_infos: Vec<_> = std_pkgs + .iter() + .map(|pkg| { + let lib = pkg + .targets() + .iter() + .find(|t| t.is_lib()) + .expect("std has a lib"); + let unit_for = UnitFor::new_normal(); + let features = + std_features.activated_features(pkg.package_id(), FeaturesFor::NormalOrDev); + (pkg, lib, unit_for, features) + }) + .collect(); + + for kind in package_set + .packages() + .flat_map(|pkg| pkg.explicit_kinds(requested_kinds, explicit_host_kind)) + { + let e = match ret.entry(kind) { + Entry::Vacant(e) => e, + Entry::Occupied(_) => continue, + }; + let units = std_pkg_infos.iter().map(|(pkg, lib, unit_for, features)| { + // I don't think we need to bother with Check here, the difference + // in time is minimal, and the difference in caching is + // significant. + let mode = CompileMode::Build; let profile = profiles.get_profile( pkg.package_id(), /*is_member*/ false, /*is_local*/ false, - unit_for, + *unit_for, mode, - *kind, + kind, ); - list.push(interner.intern( + interner.intern( pkg, lib, profile, - *kind, + kind, mode, features.clone(), /*is_std*/ true, /*dep_hash*/ 0, - )); - } + ) + }); + + e.insert(units.collect()); } + Ok(ret) } diff --git a/src/cargo/core/package.rs b/src/cargo/core/package.rs index 0ebe0277e78..e7bb403aca6 100644 --- a/src/cargo/core/package.rs +++ b/src/cargo/core/package.rs @@ -186,6 +186,34 @@ impl Package { self.targets().iter().any(|t| t.is_custom_build()) } + /// Returns explicit kinds either forced by `forced-target` in `Cargo.toml`, + /// fallback to `default-target`, or specified in cli parameters. + /// + /// If `--target` has not been specified, then the return value + /// is the same as if `--target $HOST` was specified. See + /// `rebuild_unit_graph_shared` for why callers want to do this. + /// However, if the package has its own `package.default-target` + /// key, then that gets used instead of `$HOST` + pub fn explicit_kinds( + &self, + requested_kinds: &[CompileKind], + explicit_host_kind: CompileKind, + ) -> Vec { + if let Some(k) = self.manifest().forced_kind() { + vec![k] + } else { + requested_kinds + .iter() + .map(|kind| match kind { + CompileKind::Host => { + self.manifest().default_kind().unwrap_or(explicit_host_kind) + } + CompileKind::Target(t) => CompileKind::Target(*t), + }) + .collect() + } + } + pub fn map_source(self, to_replace: SourceId, replace_with: SourceId) -> Package { Package { inner: Rc::new(PackageInner { diff --git a/src/cargo/ops/cargo_compile.rs b/src/cargo/ops/cargo_compile.rs index c34276a5fc1..c762952157b 100644 --- a/src/cargo/ops/cargo_compile.rs +++ b/src/cargo/ops/cargo_compile.rs @@ -404,12 +404,7 @@ pub fn create_bcx<'a, 'cfg>( .shell() .warn("-Zbuild-std does not currently fully support --build-plan")?; } - if build_config.requested_kinds[0].is_host() { - // TODO: This should eventually be fixed. Unfortunately it is not - // easy to get the host triple in BuildConfig. Consider changing - // requested_target to an enum, or some other approach. - anyhow::bail!("-Zbuild-std requires --target"); - } + let (std_package_set, std_resolve, std_features) = standard_lib::resolve_std(ws, &target_data, &build_config.requested_kinds, crates)?; pkg_set.add_set(std_package_set); @@ -476,14 +471,6 @@ pub fn create_bcx<'a, 'cfg>( // assuming `--target $HOST` was specified. See // `rebuild_unit_graph_shared` for more on why this is done. let explicit_host_kind = CompileKind::Target(CompileTarget::new(&target_data.rustc.host)?); - let explicit_host_kinds: Vec<_> = build_config - .requested_kinds - .iter() - .map(|kind| match kind { - CompileKind::Host => explicit_host_kind, - CompileKind::Target(t) => CompileKind::Target(*t), - }) - .collect(); // Passing `build_config.requested_kinds` instead of // `explicit_host_kinds` here so that `generate_targets` can do @@ -562,7 +549,8 @@ pub fn create_bcx<'a, 'cfg>( &crates, std_resolve, std_features, - &explicit_host_kinds, + &build_config.requested_kinds, + explicit_host_kind, &pkg_set, interner, &profiles, @@ -1059,25 +1047,7 @@ fn generate_targets( let features_for = FeaturesFor::from_for_host(target.proc_macro()); let features = resolved_features.activated_features(pkg.package_id(), features_for); - // If `--target` has not been specified, then the unit - // graph is built almost like if `--target $HOST` was - // specified. See `rebuild_unit_graph_shared` for more on - // why this is done. However, if the package has its own - // `package.target` key, then this gets used instead of - // `$HOST` - let explicit_kinds = if let Some(k) = pkg.manifest().forced_kind() { - vec![k] - } else { - requested_kinds - .iter() - .map(|kind| match kind { - CompileKind::Host => { - pkg.manifest().default_kind().unwrap_or(explicit_host_kind) - } - CompileKind::Target(t) => CompileKind::Target(*t), - }) - .collect() - }; + let explicit_kinds = pkg.explicit_kinds(requested_kinds, explicit_host_kind); for kind in explicit_kinds.iter() { let profile = profiles.get_profile( diff --git a/tests/build-std/main.rs b/tests/build-std/main.rs index c1355b317ce..c682f42201b 100644 --- a/tests/build-std/main.rs +++ b/tests/build-std/main.rs @@ -170,6 +170,50 @@ fn cross_custom() { .run(); } +/// like cross-custom but uses per-package-target instead +#[cargo_test(build_std)] +fn per_package_target() { + let p = project() + .file( + "Cargo.toml", + r#" + cargo-features = ["per-package-target"] + [package] + name = "foo" + version = "0.1.0" + edition = "2018" + default-target = "custom-target.json" + + [target.custom-target.dependencies] + dep = { path = "dep" } + "#, + ) + .file( + "src/lib.rs", + "#![no_std] pub fn f() -> u32 { dep::answer() }", + ) + .file("dep/Cargo.toml", &basic_manifest("dep", "0.1.0")) + .file("dep/src/lib.rs", "#![no_std] pub fn answer() -> u32 { 42 }") + .file( + "custom-target.json", + r#" + { + "llvm-target": "x86_64-unknown-none-gnu", + "data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128", + "arch": "x86_64", + "target-endian": "little", + "target-pointer-width": "64", + "target-c-int-width": "32", + "os": "none", + "linker-flavor": "ld.lld" + } + "#, + ) + .build(); + + p.cargo("build -v").build_std_arg("core").run(); +} + #[cargo_test(build_std)] fn custom_test_framework() { let p = project()