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

Add prefer-index feature #47

Merged
merged 4 commits into from Nov 1, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 6 additions & 0 deletions Cargo.toml
Expand Up @@ -18,6 +18,8 @@ exclude = [".github", "tests"]

[features]
default = []
# Uses the index as the source of truth for what features are available for a crate
prefer-index = ["crates-index"]
# Adds support for filtering target specific dependencies
targets = ["cfg-expr/targets"]
#features = []
Expand All @@ -27,6 +29,10 @@ targets = ["cfg-expr/targets"]
cargo_metadata = "0.15"
# Used to parse and evaluate cfg() expressions for dependencies
cfg-expr = "0.11"
# Allows inspection of the cargo registry index(ices)
crates-index = { version = "0.18", optional = true, default-features = false, features = [
"parallel",
] }
# Used to create and traverse graph structures
petgraph = "0.6"
# Used for checking version requirements
Expand Down
1 change: 1 addition & 0 deletions deny.toml
Expand Up @@ -9,6 +9,7 @@ allow = [
"Apache-2.0",
"Apache-2.0 WITH LLVM-exception",
"MIT",
"MPL-2.0",
"Unicode-DFS-2016",
]
copyleft = "deny"
Expand Down
89 changes: 84 additions & 5 deletions src/builder.rs
Expand Up @@ -767,13 +767,16 @@ impl Builder {
features: Vec<String>,
}

#[cfg(feature = "crates-index")]
let index = crates_index::Index::new_cargo_default().ok();

while let Some(pid) = pid_stack.pop() {
let is_in_workspace = workspace_members.binary_search(pid).is_ok();

let krate_index = nodes.binary_search_by(|n| n.id.cmp(pid)).unwrap();

let rnode = &nodes[krate_index];
let krate = &packages[krate_index];
let krate = &mut packages[krate_index];

if exclude.iter().any(|exc| exc.matches(krate)) {
continue;
Expand Down Expand Up @@ -805,6 +808,11 @@ impl Builder {
}
};

#[cfg(feature = "prefer-index")]
if let Some(index) = &index {
fix_features(index, krate);
}

// Cargo puts out a flat list of the enabled features, but we need
// to use the declared features on the crate itself to figure out
// the actual chain of features from one crate to another
Expand Down Expand Up @@ -860,9 +868,10 @@ impl Builder {
.features
.iter()
.filter_map(|feat| {
// This should never fail as cargo will not generate metadata if
// a feature is mentioned that doesn't exist, but still no
// reason to panic here
// This _should_ never fail in normal cases, however if the
// `prefer-index` feature is not enabled, it's possible for
// the resolved features to mention features that aren't in
// the actual crate manifest
let sub_feats: Vec<_> = krate
.features
.get(feat)?
Expand Down Expand Up @@ -1049,7 +1058,17 @@ impl Builder {
let mut features: Vec<String> = Vec::new();

while let Some(feat) = feature_stack.pop() {
for sf in &krate.features[feat] {
// This _should_ never fail in normal cases, however if the
// `prefer-index` feature is not enabled, it's possible for
// the resolved features to mention features that aren't in
// the actual crate manifest
let fs = if let Some(fs) = krate.features.get(feat) {
fs
} else {
// TODO: Show the user a warning, or just fail?
continue;
};
for sf in fs {
let pf = ParsedFeature::from(sf.as_str());

let (krate, feature) = match pf.feat() {
Expand Down Expand Up @@ -1439,6 +1458,66 @@ fn dep_names_match(krate_dep_name: &str, resolved_name: &str) -> bool {
}
}

/// Due to <https://github.com/rust-lang/cargo/issues/11319>, we can't actually
/// trust cargo to give us the correct package metadata, so we instead use the
/// (presumably) correct data from the the index
#[cfg(feature = "prefer-index")]
fn fix_features(index: &crates_index::Index, krate: &mut cm::Package) {
if krate
.source
.as_ref()
.map_or(true, |src| !src.is_crates_io())
{
return;
}

if let Some(entry) = index.crate_(&krate.name) {
let features = entry.versions().iter().find_map(|v| {
if let Ok(iv) = v.version().parse::<semver::Version>() {
if iv == krate.version {
Some(v.features())
} else {
None
}
} else {
None
}
});

if let Some(features) = features {
krate.features = features.clone();

// The index entry features might not have the `dep:<crate>`
// used with weak features if the crate version was
// published with cargo <1.60.0 version, so we need to
// manually fix that up since we depend on that format
let missing_deps: Vec<_> = krate
.features
.iter()
.flat_map(|(_, sf)| sf.iter())
.filter_map(|sf| {
let pf = ParsedFeature::from(sf.as_str());

if let Feature::Simple(simple) = pf.feat() {
if krate.features.contains_key(simple) {
None
} else {
Some(simple.to_owned())
}
} else {
None
}
})
.collect();

for missing in missing_deps {
let dep_feature = format!("dep:{missing}");
krate.features.insert(missing, vec![dep_feature]);
}
}
}
}

#[cfg(test)]
mod test {
use super::*;
Expand Down
2 changes: 1 addition & 1 deletion tests/bug.json

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions tests/bug/Cargo.toml
Expand Up @@ -3,6 +3,9 @@ name = "bug"
version = "0.1.0"
edition = "2021"

[dependencies]
conv = "=0.3.3"

[dependencies.wasmtime]
version = "2.0.0"
default-features = false
Expand Down
1 change: 1 addition & 0 deletions tests/misc.rs
Expand Up @@ -191,6 +191,7 @@ fn direct_dependencies() {
}

#[test]
#[cfg(feature = "prefer-index")]
fn bug_repro() {
let kb = krates::Builder::new();

Expand Down