Skip to content

Commit 5a4b4cc

Browse files
committed
test: allow multiple dependencies with the same name but a different kind in the sat resolver
1 parent a787a23 commit 5a4b4cc

File tree

2 files changed

+166
-62
lines changed

2 files changed

+166
-62
lines changed

crates/resolver-tests/src/sat.rs

Lines changed: 84 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
22
use std::fmt::Write;
33

4+
use cargo::core::dependency::DepKind;
45
use cargo::core::{Dependency, FeatureMap, FeatureValue, PackageId, Summary};
56
use cargo::util::interning::{InternedString, INTERNED_DEFAULT};
67
use varisat::ExtendFormula;
@@ -59,29 +60,36 @@ fn sat_at_most_one_by_key<K: std::hash::Hash + Eq>(
5960
by_keys
6061
}
6162

62-
fn find_compatible_dep_summaries_by_name_in_toml(
63+
fn find_all_compatible_dep_summaries(
6364
pkg_dependencies: &[Dependency],
6465
by_name: &HashMap<InternedString, Vec<Summary>>,
65-
) -> HashMap<InternedString, Vec<Summary>> {
66+
check_dev_dependencies: bool,
67+
) -> HashMap<InternedString, HashMap<DepKind, Vec<Summary>>> {
6668
let empty_vec = vec![];
6769

68-
pkg_dependencies
69-
.iter()
70-
.map(|dep| {
71-
let name_in_toml = dep.name_in_toml();
70+
let mut compatible_dep_summaries_map = HashMap::<_, HashMap<_, _>>::new();
7271

73-
let compatible_summaries = by_name
74-
.get(&dep.package_name())
75-
.unwrap_or(&empty_vec)
76-
.iter()
77-
.filter(|s| dep.matches_id(s.package_id()))
78-
.filter(|s| dep.features().iter().all(|f| s.features().contains_key(f)))
79-
.cloned()
80-
.collect::<Vec<_>>();
72+
for dep in pkg_dependencies {
73+
if dep.kind() == DepKind::Development && !check_dev_dependencies {
74+
continue;
75+
}
8176

82-
(name_in_toml, compatible_summaries)
83-
})
84-
.collect()
77+
let compatible_summaries = by_name
78+
.get(&dep.package_name())
79+
.unwrap_or(&empty_vec)
80+
.iter()
81+
.filter(|s| dep.matches_id(s.package_id()))
82+
.filter(|s| dep.features().iter().all(|f| s.features().contains_key(f)))
83+
.cloned()
84+
.collect::<Vec<_>>();
85+
86+
compatible_dep_summaries_map
87+
.entry(dep.name_in_toml())
88+
.or_default()
89+
.insert(dep.kind(), compatible_summaries);
90+
}
91+
92+
compatible_dep_summaries_map
8593
}
8694

8795
fn process_pkg_features(
@@ -90,7 +98,7 @@ fn process_pkg_features(
9098
var_for_is_packages_features_used: &HashMap<PackageId, HashMap<InternedString, varisat::Var>>,
9199
pkg_feature_var_map: &HashMap<InternedString, varisat::Var>,
92100
pkg_features: &FeatureMap,
93-
compatible_dep_summaries_by_name_in_toml: &HashMap<InternedString, Vec<Summary>>,
101+
compatible_dep_summaries_map: &HashMap<InternedString, HashMap<DepKind, Vec<Summary>>>,
94102
) {
95103
// add clauses for package features
96104
for (&feature_name, feature_values) in pkg_features {
@@ -105,47 +113,61 @@ fn process_pkg_features(
105113
]);
106114
}
107115
FeatureValue::Dep { dep_name } => {
108-
let dep_clause = compatible_dep_summaries_by_name_in_toml[&dep_name]
109-
.iter()
110-
.map(|dep| var_for_is_packages_used[&dep.package_id()].positive())
111-
.chain([pkg_feature_var.negative()])
112-
.collect::<Vec<_>>();
116+
// add a clause for each dependency with the provided name (normal/build/dev)
117+
for compatible_dep_summaries in compatible_dep_summaries_map
118+
.get(&dep_name)
119+
.into_iter()
120+
.flat_map(|map| map.values())
121+
{
122+
let dep_clause = compatible_dep_summaries
123+
.iter()
124+
.map(|dep| var_for_is_packages_used[&dep.package_id()].positive())
125+
.chain([pkg_feature_var.negative()])
126+
.collect::<Vec<_>>();
113127

114-
solver.add_clause(&dep_clause);
128+
solver.add_clause(&dep_clause);
129+
}
115130
}
116131
FeatureValue::DepFeature {
117132
dep_name,
118133
dep_feature: dep_feature_name,
119134
weak,
120135
} => {
121-
let mut dep_clause = Vec::new();
122-
123-
for dep in &compatible_dep_summaries_by_name_in_toml[&dep_name] {
124-
let dep_feature_var_map =
125-
&var_for_is_packages_features_used[&dep.package_id()];
126-
127-
let Some(dep_feature_var) = dep_feature_var_map.get(&dep_feature_name)
128-
else {
129-
continue;
130-
};
131-
132-
let dep_var = var_for_is_packages_used[&dep.package_id()];
133-
134-
solver.add_clause(&[
135-
pkg_feature_var.negative(),
136-
dep_var.negative(),
137-
dep_feature_var.positive(),
138-
]);
136+
// add clauses for each dependency with the provided name (normal/build/dev)
137+
for compatible_dep_summaries in compatible_dep_summaries_map
138+
.get(&dep_name)
139+
.into_iter()
140+
.flat_map(|map| map.values())
141+
{
142+
let mut dep_clause = Vec::new();
143+
144+
for dep in compatible_dep_summaries {
145+
let dep_feature_var_map =
146+
&var_for_is_packages_features_used[&dep.package_id()];
147+
148+
let Some(dep_feature_var) = dep_feature_var_map.get(&dep_feature_name)
149+
else {
150+
continue;
151+
};
152+
153+
let dep_var = var_for_is_packages_used[&dep.package_id()];
154+
155+
solver.add_clause(&[
156+
pkg_feature_var.negative(),
157+
dep_var.negative(),
158+
dep_feature_var.positive(),
159+
]);
160+
161+
if !weak {
162+
dep_clause.push(dep_var.positive());
163+
}
164+
}
139165

140166
if !weak {
141-
dep_clause.push(dep_var.positive());
167+
dep_clause.push(pkg_feature_var.negative());
168+
solver.add_clause(&dep_clause);
142169
}
143170
}
144-
145-
if !weak {
146-
dep_clause.push(pkg_feature_var.negative());
147-
solver.add_clause(&dep_clause);
148-
}
149171
}
150172
}
151173
}
@@ -158,11 +180,15 @@ fn process_pkg_dependencies(
158180
var_for_is_packages_features_used: &HashMap<PackageId, HashMap<InternedString, varisat::Var>>,
159181
pkg_var: varisat::Var,
160182
pkg_dependencies: &[Dependency],
161-
compatible_dep_summaries_by_name_in_toml: &HashMap<InternedString, Vec<Summary>>,
183+
compatible_dep_summaries_map: &HashMap<InternedString, HashMap<DepKind, Vec<Summary>>>,
162184
) {
163185
for dep in pkg_dependencies {
164-
let compatible_dep_summaries =
165-
&compatible_dep_summaries_by_name_in_toml[&dep.name_in_toml()];
186+
let Some(compatible_dep_summaries) = compatible_dep_summaries_map
187+
.get(&dep.name_in_toml())
188+
.and_then(|map| map.get(&dep.kind()))
189+
else {
190+
continue;
191+
};
166192

167193
// add clauses for package dependency features
168194
for dep_summary in compatible_dep_summaries {
@@ -272,16 +298,16 @@ impl SatResolver {
272298
let pkg_dependencies = pkg.dependencies();
273299
let pkg_features = pkg.features();
274300

275-
let compatible_dep_summaries_by_name_in_toml =
276-
find_compatible_dep_summaries_by_name_in_toml(pkg_dependencies, &by_name);
301+
let compatible_dep_summaries_map =
302+
find_all_compatible_dep_summaries(pkg_dependencies, &by_name, false);
277303

278304
process_pkg_features(
279305
&mut solver,
280306
&var_for_is_packages_used,
281307
&var_for_is_packages_features_used,
282308
&var_for_is_packages_features_used[&pkg_id],
283309
pkg_features,
284-
&compatible_dep_summaries_by_name_in_toml,
310+
&compatible_dep_summaries_map,
285311
);
286312

287313
process_pkg_dependencies(
@@ -290,7 +316,7 @@ impl SatResolver {
290316
&var_for_is_packages_features_used,
291317
var_for_is_packages_used[&pkg_id],
292318
pkg_dependencies,
293-
&compatible_dep_summaries_by_name_in_toml,
319+
&compatible_dep_summaries_map,
294320
);
295321
}
296322

@@ -330,16 +356,16 @@ impl SatResolver {
330356

331357
old_root_vars.push(root_var);
332358

333-
let compatible_dep_summaries_by_name_in_toml =
334-
find_compatible_dep_summaries_by_name_in_toml(root_dependencies, &by_name);
359+
let compatible_dep_summaries_map =
360+
find_all_compatible_dep_summaries(root_dependencies, &by_name, true);
335361

336362
process_pkg_dependencies(
337363
solver,
338364
var_for_is_packages_used,
339365
var_for_is_packages_features_used,
340366
root_var,
341367
root_dependencies,
342-
&compatible_dep_summaries_by_name_in_toml,
368+
&compatible_dep_summaries_map,
343369
);
344370

345371
solver.assume(&assumption);

crates/resolver-tests/tests/validated.rs

Lines changed: 82 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
use cargo::core::Dependency;
1+
use cargo::core::{dependency::DepKind, Dependency};
22

33
use resolver_tests::{
4-
helpers::{dep, dep_req, pkg, pkg_dep, pkg_dep_with, registry, ToDep},
4+
helpers::{dep, dep_kind, dep_req, dep_req_kind, pkg, pkg_dep, pkg_dep_with, registry, ToDep},
55
pkg, resolve, resolve_and_validated,
66
sat::SatResolver,
77
};
@@ -216,8 +216,86 @@ fn conflict_weak_features() {
216216

217217
let deps = vec![dep("dep").with(&["a1", "a2"])];
218218

219-
// The following asserts should be updated when support for weak dependencies
220-
// is added to the dependency resolver.
219+
// Weak dependencies are not supported yet in the dependency resolver
221220
assert!(resolve(deps.clone(), &reg).is_err());
222221
assert!(SatResolver::new(&reg).sat_resolve(&deps));
223222
}
223+
224+
#[test]
225+
fn multiple_dep_kinds() {
226+
let reg = registry(vec![
227+
pkg(("a-sys", "1.0.0")),
228+
pkg(("a-sys", "2.0.0")),
229+
pkg_dep_with(
230+
"dep1",
231+
vec![
232+
dep_req("a-sys", "1.0.0").opt(),
233+
dep_req_kind("a-sys", "2.0.0", DepKind::Build).opt(),
234+
],
235+
&[("default", &["dep:a-sys"])],
236+
),
237+
pkg_dep_with(
238+
"dep2",
239+
vec![
240+
dep_req("a-sys", "1.0.0").opt(),
241+
dep_req_kind("a-sys", "2.0.0", DepKind::Development).rename("a-sys-dev"),
242+
],
243+
&[("default", &["dep:a-sys", "a-sys-dev/bad"])],
244+
),
245+
]);
246+
247+
let deps = vec![dep("dep1")];
248+
let mut sat_resolver = SatResolver::new(&reg);
249+
assert!(resolve_and_validated(deps, &reg, &mut sat_resolver).is_err());
250+
251+
let deps = vec![dep("dep2")];
252+
let mut sat_resolver = SatResolver::new(&reg);
253+
assert!(resolve_and_validated(deps, &reg, &mut sat_resolver).is_ok());
254+
255+
let deps = vec![
256+
dep_req("a-sys", "1.0.0"),
257+
dep_req_kind("a-sys", "2.0.0", DepKind::Build).rename("a2"),
258+
];
259+
let mut sat_resolver = SatResolver::new(&reg);
260+
assert!(resolve_and_validated(deps, &reg, &mut sat_resolver).is_err());
261+
262+
let deps = vec![
263+
dep_req("a-sys", "1.0.0"),
264+
dep_req_kind("a-sys", "2.0.0", DepKind::Development).rename("a2"),
265+
];
266+
let mut sat_resolver = SatResolver::new(&reg);
267+
assert!(resolve_and_validated(deps, &reg, &mut sat_resolver).is_err());
268+
}
269+
270+
#[test]
271+
fn multiple_dep_kinds_with_different_packages() {
272+
let reg = registry(vec![
273+
pkg_dep_with("a", vec![], &[("f", &[])]),
274+
pkg_dep_with("b", vec![], &[("f", &[])]),
275+
pkg_dep_with("c", vec![], &[("g", &[])]),
276+
pkg_dep_with(
277+
"dep1",
278+
vec![
279+
"a".opt().rename("x").with(&["f"]),
280+
dep_kind("b", DepKind::Build).opt().rename("x").with(&["f"]),
281+
],
282+
&[("default", &["x"])],
283+
),
284+
pkg_dep_with(
285+
"dep2",
286+
vec![
287+
"a".opt().rename("x").with(&["f"]),
288+
dep_kind("c", DepKind::Build).opt().rename("x").with(&["f"]),
289+
],
290+
&[("default", &["x"])],
291+
),
292+
]);
293+
294+
let deps = vec!["dep1".with(&["default"])];
295+
let mut sat_resolver = SatResolver::new(&reg);
296+
assert!(resolve_and_validated(deps, &reg, &mut sat_resolver).is_ok());
297+
298+
let deps = vec!["dep2".with(&["default"])];
299+
let mut sat_resolver = SatResolver::new(&reg);
300+
assert!(resolve_and_validated(deps, &reg, &mut sat_resolver).is_err());
301+
}

0 commit comments

Comments
 (0)