Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support per pkg target for -Zbuild-std #10330

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
5 changes: 0 additions & 5 deletions src/cargo/core/compiler/build_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
66 changes: 42 additions & 24 deletions src/cargo/core/compiler/standard_lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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,
Expand All @@ -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)
}

Expand Down
28 changes: 28 additions & 0 deletions src/cargo/core/package.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<CompileKind> {
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 {
Expand Down
31 changes: 9 additions & 22 deletions src/cargo/ops/cargo_compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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() {
Expand Down
44 changes: 44 additions & 0 deletions tests/build-std/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
5 changes: 0 additions & 5 deletions tests/testsuite/mock-std/library/proc_macro/src/lib.rs
Original file line number Diff line number Diff line change
@@ -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() {
}
109 changes: 107 additions & 2 deletions tests/testsuite/standard_lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down Expand Up @@ -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]
Expand Down