Skip to content

Commit 316352e

Browse files
committed
Fix a bug involving optional non-dev dependencies
1 parent 5a0af11 commit 316352e

1 file changed

Lines changed: 33 additions & 39 deletions

File tree

src/common.rs

Lines changed: 33 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use anyhow::bail;
22
use cargo_metadata::{
3-
semver::VersionReq, CargoOpt::AllFeatures, CargoOpt::NoDefaultFeatures, Dependency,
4-
DependencyKind, Metadata, MetadataCommand, Package, PackageId,
3+
CargoOpt::AllFeatures, CargoOpt::NoDefaultFeatures, DependencyKind, Metadata, MetadataCommand,
4+
NodeDep, Package, PackageId,
55
};
66
use std::collections::{HashMap, HashSet};
77

@@ -85,12 +85,12 @@ fn sourced_dependencies_from_metadata(
8585
}
8686
}
8787

88-
for pkg in meta.workspace_members {
89-
*how.get_mut(&pkg).unwrap() = PkgSource::Local;
88+
for pkg in &meta.workspace_members {
89+
*how.get_mut(pkg).unwrap() = PkgSource::Local;
9090
}
9191

9292
if no_dev {
93-
(how, what) = extract_non_dev_dependencies(&mut how, &mut what);
93+
(how, what) = extract_non_dev_dependencies(&meta, &mut how, &mut what);
9494
}
9595

9696
let dependencies: Vec<_> = how
@@ -107,42 +107,30 @@ fn sourced_dependencies_from_metadata(
107107
Ok(dependencies)
108108
}
109109

110-
#[derive(Eq, Hash, PartialEq)]
111-
struct Dep {
112-
name: String,
113-
req: VersionReq,
114-
}
115-
116-
impl Dep {
117-
fn from_cargo_metadata_dependency(dep: &Dependency) -> Self {
118-
Self {
119-
name: dep.name.clone(),
120-
req: dep.req.clone(),
121-
}
122-
}
123-
124-
fn matches(&self, pkg: &Package) -> bool {
125-
self.name == pkg.name && self.req.matches(&pkg.version)
126-
}
127-
}
128-
129-
/// Start with the `PkgSource::Local` packages, then iteratively add non-dev-dependencies until no more
130-
/// packages can be added, and return the results.
131-
///
132-
/// Note that matching dependencies to packages is "best effort." The fields that Cargo uses to
133-
/// determine a package's id are its name, version, and source:
134-
/// https://github.com/rust-lang/cargo/blob/dd5134c7a59e3a3b8587f1ef04a930185d2ca503/src/cargo/core/package_id.rs#L29-L31
110+
/// Start with the `PkgSource::Local` packages, then iteratively add non-dev-dependencies until no
111+
/// more packages can be added, and return the results.
135112
///
136-
/// When matching dependencies to packages, we use the package's name and version, but not its source
137-
/// (see [`Dep`]). Experiments suggest that source strings can vary. So comparing them seems risky.
138-
/// Also, it is better to err on the side of inclusion.
113+
/// This function uses the resolved dependency graph from `cargo metadata` to determine which
114+
/// dependencies are actually used. This function does _not_ use the declared dependencies, which
115+
/// may include optional dependencies that aren't actually used.
139116
fn extract_non_dev_dependencies(
117+
meta: &Metadata,
140118
how: &mut HashMap<PackageId, PkgSource>,
141119
what: &mut HashMap<PackageId, Package>,
142120
) -> (HashMap<PackageId, PkgSource>, HashMap<PackageId, Package>) {
143121
let mut how_new = HashMap::new();
144122
let mut what_new = HashMap::new();
145123

124+
let Some(resolve) = &meta.resolve else {
125+
return (HashMap::new(), HashMap::new());
126+
};
127+
128+
let node_deps: HashMap<&PackageId, &[NodeDep]> = resolve
129+
.nodes
130+
.iter()
131+
.map(|node| (&node.id, node.deps.as_slice()))
132+
.collect();
133+
146134
let mut ids = how
147135
.iter()
148136
.filter_map(|(id, source)| {
@@ -158,19 +146,25 @@ fn extract_non_dev_dependencies(
158146
let mut deps = HashSet::new();
159147

160148
for id in ids.drain(..) {
161-
for dep in &what.get(&id).unwrap().dependencies {
162-
if dep.kind != DependencyKind::Development {
163-
deps.insert(Dep::from_cargo_metadata_dependency(dep));
149+
if let Some(node_deps) = node_deps.get(&id) {
150+
for dep in *node_deps {
151+
if dep
152+
.dep_kinds
153+
.iter()
154+
.any(|info| info.kind != DependencyKind::Development)
155+
{
156+
deps.insert(&dep.pkg);
157+
}
164158
}
165159
}
166160

167161
how_new.insert(id.clone(), how.remove(&id).unwrap());
168162
what_new.insert(id.clone(), what.remove(&id).unwrap());
169163
}
170164

171-
for pkg in what.values() {
172-
if deps.iter().any(|dep| dep.matches(pkg)) {
173-
ids.push(pkg.id.clone());
165+
for pkg_id in what.keys() {
166+
if deps.contains(pkg_id) {
167+
ids.push(pkg_id.clone());
174168
}
175169
}
176170
}

0 commit comments

Comments
 (0)