Skip to content

Commit

Permalink
Add prefer-index feature (#47)
Browse files Browse the repository at this point in the history
* Add additional bug

* Add support for retrieving features via index

* Fix lint

* Fix deny
  • Loading branch information
Jake-Shadle committed Nov 1, 2022
1 parent 33a8e7d commit 7e86bf7
Show file tree
Hide file tree
Showing 7 changed files with 903 additions and 803 deletions.
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

0 comments on commit 7e86bf7

Please sign in to comment.