Skip to content

Commit

Permalink
Fix cyclic features (#49)
Browse files Browse the repository at this point in the history
* Add --json option

* Add cyclic features test

* Fix cyclic features
  • Loading branch information
Jake-Shadle committed Nov 2, 2022
1 parent bd88850 commit d880398
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 19 deletions.
53 changes: 35 additions & 18 deletions examples/graph.rs
Expand Up @@ -5,7 +5,7 @@ use std::fmt;
#[derive(Parser, Debug)]
struct Args {
#[arg(short, long)]
manifest_path: String,
manifest_path: Option<String>,
#[arg(long)]
features: Vec<String>,
#[arg(long)]
Expand All @@ -14,6 +14,8 @@ struct Args {
no_default_features: bool,
#[arg(long)]
no_dev: bool,
#[arg(long, conflicts_with = "manifest_path")]
json: Option<String>,
}

pub struct Simple {
Expand Down Expand Up @@ -41,27 +43,42 @@ impl fmt::Display for Simple {
fn main() {
let args = Args::parse();

let cmd = {
let mut cmd = krates::Cmd::new();
if args.all_features {
cmd.all_features();
}
if args.no_default_features {
cmd.no_default_features();
let graph: Graph = if let Some(manifest_path) = args.manifest_path {
let cmd = {
let mut cmd = krates::Cmd::new();
if args.all_features {
cmd.all_features();
}
if args.no_default_features {
cmd.no_default_features();
}
if !args.features.is_empty() {
cmd.features(args.features);
}
cmd.manifest_path(manifest_path);
cmd
};

let mut builder = krates::Builder::new();
if args.no_dev {
builder.ignore_kind(krates::DepKind::Dev, krates::Scope::All);
}
if !args.features.is_empty() {
cmd.features(args.features);

builder.build(cmd, krates::NoneFilter).unwrap()
} else if let Some(json) = args.json {
let mut builder = krates::Builder::new();
if args.no_dev {
builder.ignore_kind(krates::DepKind::Dev, krates::Scope::All);
}
cmd.manifest_path(args.manifest_path);
cmd
};

let mut builder = krates::Builder::new();
if args.no_dev {
builder.ignore_kind(krates::DepKind::Dev, krates::Scope::All);
}
let json = std::fs::read(json).expect("failed to read json");
let md: krates::cm::Metadata =
serde_json::from_slice(&json).expect("failed to deserialize metadata from json");

let graph: Graph = builder.build(cmd, krates::NoneFilter).unwrap();
builder.build_with_metadata(md, krates::NoneFilter).unwrap()
} else {
panic!("must specify either --manifest-path or --json");
};

let dot = krates::petgraph::dot::Dot::new(graph.graph()).to_string();

Expand Down
8 changes: 7 additions & 1 deletion src/builder.rs
Expand Up @@ -1055,6 +1055,10 @@ impl Builder {
// enabled explicitly on each edge
let mut feature_stack: Vec<_> = rnode.features.iter().map(|s| s.as_str()).collect();

// Apparently features can have cycles, so we need to ensure we don't enter an
// infinite loop https://github.com/EmbarkStudios/krates/issues/48
let mut simple_features = std::collections::BTreeSet::new();

let mut features: Vec<String> = Vec::new();

while let Some(feat) = feature_stack.pop() {
Expand All @@ -1073,7 +1077,9 @@ impl Builder {

let (krate, feature) = match pf.feat() {
Feature::Simple(feat) => {
feature_stack.push(feat);
if simple_features.insert(feat) {
feature_stack.push(feat);
}
continue;
},
Feature::Krate(_krate) => { continue; }
Expand Down
15 changes: 15 additions & 0 deletions tests/features.rs
Expand Up @@ -218,3 +218,18 @@ fn ensure_weak_features_dont_add_edges() {
]
);
}

/// Ensures we handle cyclic features
#[test]
fn handles_cyclic_features() {
let mut cmd = krates::Cmd::new();
cmd.manifest_path("tests/features/Cargo.toml")
.no_default_features()
.features(feats!(["cycle"]));

let mut builder = krates::Builder::new();
builder.include_targets([("x86_64-unknown-linux-musl", vec![])]);
let md: krates::Krates = builder.build(cmd, krates::NoneFilter).unwrap();

assert_features!(md, "features-galore", ["cycle", "midi", "subfeatcycle"]);
}
2 changes: 2 additions & 0 deletions tests/features/Cargo.toml
Expand Up @@ -35,3 +35,5 @@ stream = ["reqest?/stream"]
tls = ["tls-no-reqwest", "reqest?/rustls-tls"]
tls-no-reqwest = ["rustls"]
zlib = ["git/zlib-ng-compat", "reqest?/deflate"]
cycle = ["subfeatcycle", "midi"]
subfeatcycle = ["cycle"]

0 comments on commit d880398

Please sign in to comment.