Skip to content

Commit 5bb7b2f

Browse files
committed
Platform-specific-ness is carried via deps
If e.g. an optional feature is only enabled on certain platforms, meaning a transitive dependency is only enabled on certain platforms, currently that information can't be worked out from the output of `cargo metadata`. With this change, when a dependency is configured for a specific platform, its dependencies are by default also configured for that specific platform, unless they explicitly have a different platform attached to them. This makes it easier to translate crates.io packages for building with non-cargo build systems, because this platform-specific behaviour of feature resolution is now observable in `cargo metadata`, rather than requiring those systems to track and re-implement feature resolution themselves with regard to platforms. Fixes #9863.
1 parent 7fbbf4e commit 5bb7b2f

File tree

5 files changed

+303
-10
lines changed

5 files changed

+303
-10
lines changed

src/cargo/core/resolver/dep_cache.rs

+17-5
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ use crate::util::errors::CargoResult;
2121
use crate::util::interning::InternedString;
2222

2323
use anyhow::Context as _;
24+
use cargo_platform::Platform;
2425
use log::debug;
2526
use std::collections::{BTreeSet, HashMap, HashSet};
2627
use std::rc::Rc;
@@ -37,7 +38,7 @@ pub struct RegistryQueryer<'a> {
3738
registry_cache: HashMap<Dependency, Rc<Vec<Summary>>>,
3839
/// a cache of `Dependency`s that are required for a `Summary`
3940
summary_cache: HashMap<
40-
(Option<PackageId>, Summary, ResolveOpts),
41+
(Option<(PackageId, Option<Platform>)>, Summary, ResolveOpts),
4142
Rc<(HashSet<InternedString>, Rc<Vec<DepInfo>>)>,
4243
>,
4344
/// all the cases we ended up using a supplied replacement
@@ -189,36 +190,47 @@ impl<'a> RegistryQueryer<'a> {
189190
pub fn build_deps(
190191
&mut self,
191192
cx: &Context,
192-
parent: Option<PackageId>,
193+
parent: Option<(PackageId, Option<Platform>)>,
193194
candidate: &Summary,
194195
opts: &ResolveOpts,
195196
) -> ActivateResult<Rc<(HashSet<InternedString>, Rc<Vec<DepInfo>>)>> {
196197
// if we have calculated a result before, then we can just return it,
197198
// as it is a "pure" query of its arguments.
198199
if let Some(out) = self
199200
.summary_cache
200-
.get(&(parent, candidate.clone(), opts.clone()))
201+
.get(&(parent.clone(), candidate.clone(), opts.clone()))
201202
.cloned()
202203
{
203204
return Ok(out);
204205
}
206+
207+
let (parent_package_id, parent_platform) = match parent.clone() {
208+
Some((package_id, platform)) => (Some(package_id), platform),
209+
None => (None, None),
210+
};
211+
205212
// First, figure out our set of dependencies based on the requested set
206213
// of features. This also calculates what features we're going to enable
207214
// for our own dependencies.
208-
let (used_features, deps) = resolve_features(parent, candidate, opts)?;
215+
let (used_features, deps) = resolve_features(parent_package_id, candidate, opts)?;
209216

210217
// Next, transform all dependencies into a list of possible candidates
211218
// which can satisfy that dependency.
212219
let mut deps = deps
213220
.into_iter()
214-
.map(|(dep, features)| {
221+
.map(|(mut dep, features)| {
215222
let candidates = self.query(&dep).with_context(|| {
216223
format!(
217224
"failed to get `{}` as a dependency of {}",
218225
dep.package_name(),
219226
describe_path_in_context(cx, &candidate.package_id()),
220227
)
221228
})?;
229+
if dep.platform().is_none() && parent_platform.is_some() {
230+
// If the parent has a specific target, the child will also only be used in that target.
231+
// Tracking this allows for analysing platform-specificness of transitive dependencies in `cargo metadata`.
232+
dep.set_platform(parent_platform.clone());
233+
}
222234
Ok((dep, candidates, features))
223235
})
224236
.collect::<CargoResult<Vec<DepInfo>>>()?;

src/cargo/core/resolver/mod.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -654,8 +654,12 @@ fn activate(
654654
};
655655

656656
let now = Instant::now();
657-
let (used_features, deps) =
658-
&*registry.build_deps(cx, parent.map(|p| p.0.package_id()), &candidate, opts)?;
657+
let (used_features, deps) = &*registry.build_deps(
658+
cx,
659+
parent.map(|(summary, dep)| (summary.package_id(), dep.platform().map(|p| p.to_owned()))),
660+
&candidate,
661+
opts,
662+
)?;
659663

660664
// Record what list of features is active for this package.
661665
if !used_features.is_empty() {

tests/testsuite/features2.rs

-1
Original file line numberDiff line numberDiff line change
@@ -1905,7 +1905,6 @@ fn doc_optional() {
19051905
"\
19061906
[UPDATING] [..]
19071907
[DOWNLOADING] crates ...
1908-
[DOWNLOADED] spin v1.0.0 [..]
19091908
[DOWNLOADED] bar v1.0.0 [..]
19101909
[DOCUMENTING] bar v1.0.0
19111910
[CHECKING] bar v1.0.0

tests/testsuite/metadata.rs

+280-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
33
use cargo_test_support::install::cargo_home;
44
use cargo_test_support::paths::CargoPathExt;
5-
use cargo_test_support::registry::Package;
5+
use cargo_test_support::registry::{Dependency, Package};
66
use cargo_test_support::{basic_bin_manifest, basic_lib_manifest, main_file, project, rustc_host};
77
use serde_json::json;
88

@@ -3070,6 +3070,285 @@ fn dep_kinds_workspace() {
30703070
.run();
30713071
}
30723072

3073+
#[cargo_test]
3074+
fn target_specific_feature() {
3075+
Package::new("optdep", "1.0.0").publish();
3076+
Package::new("common", "1.0.0")
3077+
.add_dep(Dependency::new("optdep", "1.0").optional(true))
3078+
.publish();
3079+
3080+
let p = project()
3081+
.file(
3082+
"Cargo.toml",
3083+
r#"
3084+
[package]
3085+
name = "foo"
3086+
version = "1.0.0"
3087+
3088+
[dependencies]
3089+
common = "1.0"
3090+
3091+
[target.x86_64-unknown-linux-gnu.dependencies]
3092+
common = { version = "1.0.0" }
3093+
3094+
[target.x86_64-apple-darwin.dependencies]
3095+
common = { version = "1.0.0", features = ["optdep"] }
3096+
"#,
3097+
)
3098+
.file("src/lib.rs", "")
3099+
.build();
3100+
3101+
p.cargo("metadata")
3102+
.with_json(
3103+
r#"{
3104+
"metadata": null,
3105+
"packages": [
3106+
{
3107+
"authors": [],
3108+
"categories": [],
3109+
"default_run": null,
3110+
"dependencies": [
3111+
{
3112+
"features": [],
3113+
"kind": null,
3114+
"name": "optdep",
3115+
"optional": false,
3116+
"registry": null,
3117+
"rename": null,
3118+
"req": "^1.0",
3119+
"source": "registry+https://github.com/rust-lang/crates.io-index",
3120+
"target": null,
3121+
"uses_default_features": true
3122+
}
3123+
],
3124+
"description": null,
3125+
"documentation": null,
3126+
"edition": "2015",
3127+
"features": {},
3128+
"homepage": null,
3129+
"id": "common 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
3130+
"keywords": [],
3131+
"license": null,
3132+
"license_file": null,
3133+
"links": null,
3134+
"manifest_path": "[..]/common-1.0.0/Cargo.toml",
3135+
"metadata": null,
3136+
"name": "common",
3137+
"publish": null,
3138+
"readme": null,
3139+
"repository": null,
3140+
"rust_version": null,
3141+
"source": "registry+https://github.com/rust-lang/crates.io-index",
3142+
"targets": [
3143+
{
3144+
"crate_types": [
3145+
"lib"
3146+
],
3147+
"doc": true,
3148+
"doctest": true,
3149+
"edition": "2015",
3150+
"kind": [
3151+
"lib"
3152+
],
3153+
"name": "common",
3154+
"src_path": "[..]/common-1.0.0/src/lib.rs",
3155+
"test": true
3156+
}
3157+
],
3158+
"version": "1.0.0"
3159+
},
3160+
{
3161+
"authors": [],
3162+
"categories": [],
3163+
"default_run": null,
3164+
"dependencies": [
3165+
{
3166+
"features": [],
3167+
"kind": null,
3168+
"name": "common",
3169+
"optional": false,
3170+
"registry": null,
3171+
"rename": null,
3172+
"req": "^1.0",
3173+
"source": "registry+https://github.com/rust-lang/crates.io-index",
3174+
"target": null,
3175+
"uses_default_features": true
3176+
},
3177+
{
3178+
"features": [
3179+
"optdep"
3180+
],
3181+
"kind": null,
3182+
"name": "common",
3183+
"optional": false,
3184+
"registry": null,
3185+
"rename": null,
3186+
"req": "^1.0.0",
3187+
"source": "registry+https://github.com/rust-lang/crates.io-index",
3188+
"target": "x86_64-apple-darwin",
3189+
"uses_default_features": true
3190+
},
3191+
{
3192+
"features": [],
3193+
"kind": null,
3194+
"name": "common",
3195+
"optional": false,
3196+
"registry": null,
3197+
"rename": null,
3198+
"req": "^1.0.0",
3199+
"source": "registry+https://github.com/rust-lang/crates.io-index",
3200+
"target": "x86_64-unknown-linux-gnu",
3201+
"uses_default_features": true
3202+
}
3203+
],
3204+
"description": null,
3205+
"documentation": null,
3206+
"edition": "2015",
3207+
"features": {},
3208+
"homepage": null,
3209+
"id": "foo 1.0.0 (path+file://[..]/foo)",
3210+
"keywords": [],
3211+
"license": null,
3212+
"license_file": null,
3213+
"links": null,
3214+
"manifest_path": "[..]/foo/Cargo.toml",
3215+
"metadata": null,
3216+
"name": "foo",
3217+
"publish": null,
3218+
"readme": null,
3219+
"repository": null,
3220+
"rust_version": null,
3221+
"source": null,
3222+
"targets": [
3223+
{
3224+
"crate_types": [
3225+
"lib"
3226+
],
3227+
"doc": true,
3228+
"doctest": true,
3229+
"edition": "2015",
3230+
"kind": [
3231+
"lib"
3232+
],
3233+
"name": "foo",
3234+
"src_path": "[..]/foo/src/lib.rs",
3235+
"test": true
3236+
}
3237+
],
3238+
"version": "1.0.0"
3239+
},
3240+
{
3241+
"authors": [],
3242+
"categories": [],
3243+
"default_run": null,
3244+
"dependencies": [],
3245+
"description": null,
3246+
"documentation": null,
3247+
"edition": "2015",
3248+
"features": {},
3249+
"homepage": null,
3250+
"id": "optdep 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
3251+
"keywords": [],
3252+
"license": null,
3253+
"license_file": null,
3254+
"links": null,
3255+
"manifest_path": "[..]/optdep-1.0.0/Cargo.toml",
3256+
"metadata": null,
3257+
"name": "optdep",
3258+
"publish": null,
3259+
"readme": null,
3260+
"repository": null,
3261+
"rust_version": null,
3262+
"source": "registry+https://github.com/rust-lang/crates.io-index",
3263+
"targets": [
3264+
{
3265+
"crate_types": [
3266+
"lib"
3267+
],
3268+
"doc": true,
3269+
"doctest": true,
3270+
"edition": "2015",
3271+
"kind": [
3272+
"lib"
3273+
],
3274+
"name": "optdep",
3275+
"src_path": "[..]/optdep-1.0.0/src/lib.rs",
3276+
"test": true
3277+
}
3278+
],
3279+
"version": "1.0.0"
3280+
}
3281+
],
3282+
"resolve": {
3283+
"nodes": [
3284+
{
3285+
"dependencies": [
3286+
"optdep 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)"
3287+
],
3288+
"deps": [
3289+
{
3290+
"dep_kinds": [
3291+
{
3292+
"kind": null,
3293+
"target": "x86_64-apple-darwin"
3294+
}
3295+
],
3296+
"name": "optdep",
3297+
"pkg": "optdep 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)"
3298+
}
3299+
],
3300+
"features": [
3301+
"optdep"
3302+
],
3303+
"id": "common 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)"
3304+
},
3305+
{
3306+
"dependencies": [
3307+
"common 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)"
3308+
],
3309+
"deps": [
3310+
{
3311+
"dep_kinds": [
3312+
{
3313+
"kind": null,
3314+
"target": null
3315+
},
3316+
{
3317+
"kind": null,
3318+
"target": "x86_64-apple-darwin"
3319+
},
3320+
{
3321+
"kind": null,
3322+
"target": "x86_64-unknown-linux-gnu"
3323+
}
3324+
],
3325+
"name": "common",
3326+
"pkg": "common 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)"
3327+
}
3328+
],
3329+
"features": [],
3330+
"id": "foo 1.0.0 (path+file://[..]/foo)"
3331+
},
3332+
{
3333+
"dependencies": [],
3334+
"deps": [],
3335+
"features": [],
3336+
"id": "optdep 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)"
3337+
}
3338+
],
3339+
"root": "foo 1.0.0 (path+file://[..]/foo)"
3340+
},
3341+
"target_directory": "[..]/foo/target",
3342+
"version": 1,
3343+
"workspace_members": [
3344+
"foo 1.0.0 (path+file://[..]/foo)"
3345+
],
3346+
"workspace_root": "[..]/foo"
3347+
}"#,
3348+
)
3349+
.run();
3350+
}
3351+
30733352
// Creating non-utf8 path is an OS-specific pain, so let's run this only on
30743353
// linux, where arbitrary bytes work.
30753354
#[cfg(target_os = "linux")]

tests/testsuite/tree.rs

-1
Original file line numberDiff line numberDiff line change
@@ -1479,7 +1479,6 @@ fn itarget_opt_dep() {
14791479
"\
14801480
foo v1.0.0 ([..]/foo)
14811481
└── common v1.0.0
1482-
└── optdep v1.0.0
14831482
",
14841483
)
14851484
.run();

0 commit comments

Comments
 (0)