diff --git a/src/cargo/core/compiler/build_config.rs b/src/cargo/core/compiler/build_config.rs index 51e6512b161..d9c555c8088 100644 --- a/src/cargo/core/compiler/build_config.rs +++ b/src/cargo/core/compiler/build_config.rs @@ -84,11 +84,6 @@ impl BuildConfig { anyhow::bail!("jobs may not be 0"); } - if config.cli_unstable().build_std.is_some() && requested_kinds[0].is_host() { - // TODO: This should eventually be fixed. - anyhow::bail!("-Zbuild-std requires --target"); - } - Ok(BuildConfig { requested_kinds, jobs, diff --git a/src/cargo/core/compiler/standard_lib.rs b/src/cargo/core/compiler/standard_lib.rs index e0baebd516b..1aea2625b85 100644 --- a/src/cargo/core/compiler/standard_lib.rs +++ b/src/cargo/core/compiler/standard_lib.rs @@ -10,6 +10,7 @@ use crate::core::{Dependency, PackageId, PackageSet, Resolve, SourceId, Workspac use crate::ops::{self, Packages}; use crate::util::errors::CargoResult; use crate::Config; +use std::collections::hash_map::Entry; use std::collections::{HashMap, HashSet}; use std::env; use std::path::PathBuf; @@ -169,7 +170,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, @@ -183,43 +185,59 @@ 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"); - // 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::NormalOrDevOrArtifactTarget(None), - ); - for kind in kinds { - let list = ret.entry(*kind).or_insert_with(Vec::new); - let unit_for = UnitFor::new_normal(*kind); + + 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"); + // std does not have artifact dependencies at the moment + let unit_for = UnitFor::new_normal(explicit_host_kind); + let features = + std_features.activated_features(pkg.package_id(), FeaturesFor::default()); + (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, - *kind, + *unit_for, + kind, ); - list.push(interner.intern( + interner.intern( pkg, lib, profile, - *kind, + kind, mode, features.clone(), /*is_std*/ true, /*dep_hash*/ 0, IsArtifact::No, - )); - } + ) + }); + + e.insert(units.collect()); } + Ok(ret) } diff --git a/src/cargo/core/package.rs b/src/cargo/core/package.rs index f93c249e5da..6a5a78218b9 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 07dabce6d08..f2941cd971e 100644 --- a/src/cargo/ops/cargo_compile.rs +++ b/src/cargo/ops/cargo_compile.rs @@ -401,6 +401,12 @@ pub fn create_bcx<'a, 'cfg>( } = resolve; let std_resolve_features = if let Some(crates) = &config.cli_unstable().build_std { + if build_config.build_plan { + config + .shell() + .warn("-Zbuild-std does not currently fully support --build-plan")?; + } + let (std_package_set, std_resolve, std_features) = standard_lib::resolve_std(ws, &target_data, &build_config, crates)?; pkg_set.add_set(std_package_set); @@ -467,14 +473,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 @@ -549,7 +547,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, @@ -1011,19 +1010,7 @@ fn generate_targets( // 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 unit_for = if initial_target_mode.is_any_test() { 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() diff --git a/tests/testsuite/mock-std/library/proc_macro/src/lib.rs b/tests/testsuite/mock-std/library/proc_macro/src/lib.rs index 82a76840615..6f13220f587 100644 --- a/tests/testsuite/mock-std/library/proc_macro/src/lib.rs +++ b/tests/testsuite/mock-std/library/proc_macro/src/lib.rs @@ -1,11 +1,6 @@ #![feature(staged_api)] #![stable(since = "1.0.0", feature = "dummy")] -extern crate proc_macro; - -#[stable(since = "1.0.0", feature = "dummy")] -pub use proc_macro::*; - #[stable(since = "1.0.0", feature = "dummy")] pub fn custom_api() { } diff --git a/tests/testsuite/standard_lib.rs b/tests/testsuite/standard_lib.rs index 07b038b4a5d..133bada7a65 100644 --- a/tests/testsuite/standard_lib.rs +++ b/tests/testsuite/standard_lib.rs @@ -246,6 +246,10 @@ fn basic() { p.cargo("build").build_std(&setup).target_host().run(); p.cargo("run").build_std(&setup).target_host().run(); p.cargo("test").build_std(&setup).target_host().run(); + p.cargo("check -v").build_std(&setup).run(); + p.cargo("build").build_std(&setup).run(); + p.cargo("run").build_std(&setup).run(); + p.cargo("test").build_std(&setup).run(); } #[cargo_test] @@ -396,14 +400,115 @@ fn target_proc_macro() { "src/lib.rs", r#" extern crate proc_macro; - pub fn f() { + fn f() { let _ts = proc_macro::TokenStream::new(); } "#, ) + .file( + "Cargo.toml", + r#" + [package] + name = "pm" + version = "0.1.0" + + [lib] + proc-macro = true + "#, + ) .build(); - p.cargo("build -v").build_std(&setup).target_host().run(); + p.cargo("build -v").build_std(&setup).run(); +} + +// We already have `basic` which uses `proc_macro::custom_api()`. This case attempts to use +// `TokenStream` which would error because we are using the sysroot version. +#[cargo_test] +fn non_proc_macro_crate_uses_non_sysroot_proc_macro() { + let setup = match setup() { + Some(s) => s, + None => return, + }; + let p = project() + .file( + "src/lib.rs", + r#" + extern crate proc_macro; + fn f() { + let _ts = proc_macro::TokenStream::new(); + } + "#, + ) + .file( + "Cargo.toml", + r#" + [package] + name = "pm" + version = "0.1.0" + "#, + ) + .build(); + p.cargo("build -v").build_std(&setup).run_expect_error(); +} + +#[cargo_test] +fn intergrated_proc_macro() { + let setup = match setup() { + Some(s) => s, + None => return, + }; + let p = project() + .file( + "src/main.rs", + r#" + fn main() { + println!("The answer is {}", pm::m!()); + } + "#, + ) + .file( + "pm/src/lib.rs", + r#" + extern crate proc_macro; + use proc_macro::TokenStream; + + #[proc_macro] + pub fn m(_item: TokenStream) -> TokenStream { + "42".parse().unwrap() + } + "#, + ) + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [workspace] + members = ["pm"] + + [dependencies] + pm = { path = "./pm" } + "#, + ) + .file( + "pm/Cargo.toml", + r#" + [package] + name = "pm" + version = "0.1.0" + + [lib] + proc-macro = true + "#, + ) + .build(); + + p.cargo("run -v") + .build_std(&setup) + .with_stdout_contains("The answer is 42") + .run(); } #[cargo_test]