mirror of
https://github.com/rust-lang/cargo.git
synced 2025-09-25 11:14:46 +00:00
test: refactor sat resolver clauses computation
Add new intermediate boolean variables for each dependency and each dependency feature declared in a package. This is used to simplify computation of the sat clauses. Add support for multiple dependencies with the same name, if their kind or target is different. A non-weak dependency feature for an optional dependency now activates a feature of the same name in the sat resolver.
This commit is contained in:
parent
04e4270758
commit
870f6d31d7
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -2936,6 +2936,7 @@ name = "resolver-tests"
|
|||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cargo",
|
"cargo",
|
||||||
|
"cargo-platform 0.1.8",
|
||||||
"cargo-util",
|
"cargo-util",
|
||||||
"cargo-util-schemas",
|
"cargo-util-schemas",
|
||||||
"proptest",
|
"proptest",
|
||||||
|
@ -6,6 +6,7 @@ publish = false
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
cargo.workspace = true
|
cargo.workspace = true
|
||||||
|
cargo-platform.workspace = true
|
||||||
cargo-util-schemas.workspace = true
|
cargo-util-schemas.workspace = true
|
||||||
cargo-util.workspace = true
|
cargo-util.workspace = true
|
||||||
proptest.workspace = true
|
proptest.workspace = true
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
|
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
|
|
||||||
|
use cargo::core::dependency::DepKind;
|
||||||
use cargo::core::{Dependency, FeatureMap, FeatureValue, PackageId, Summary};
|
use cargo::core::{Dependency, FeatureMap, FeatureValue, PackageId, Summary};
|
||||||
use cargo::util::interning::{InternedString, INTERNED_DEFAULT};
|
use cargo::util::interning::{InternedString, INTERNED_DEFAULT};
|
||||||
|
use cargo_platform::Platform;
|
||||||
use varisat::ExtendFormula;
|
use varisat::ExtendFormula;
|
||||||
|
|
||||||
const fn num_bits<T>() -> usize {
|
const fn num_bits<T>() -> usize {
|
||||||
@ -34,7 +36,7 @@ fn sat_at_most_one(solver: &mut varisat::Solver<'_>, vars: &[varisat::Var]) {
|
|||||||
}
|
}
|
||||||
// There are more efficient ways to do it for large numbers of versions.
|
// There are more efficient ways to do it for large numbers of versions.
|
||||||
//
|
//
|
||||||
// use the "Binary Encoding" from
|
// Use the "Binary Encoding" from
|
||||||
// https://www.it.uu.se/research/group/astra/ModRef10/papers/Alan%20M.%20Frisch%20and%20Paul%20A.%20Giannoros.%20SAT%20Encodings%20of%20the%20At-Most-k%20Constraint%20-%20ModRef%202010.pdf
|
// https://www.it.uu.se/research/group/astra/ModRef10/papers/Alan%20M.%20Frisch%20and%20Paul%20A.%20Giannoros.%20SAT%20Encodings%20of%20the%20At-Most-k%20Constraint%20-%20ModRef%202010.pdf
|
||||||
let bits: Vec<varisat::Var> = solver.new_var_iter(log_bits(vars.len())).collect();
|
let bits: Vec<varisat::Var> = solver.new_var_iter(log_bits(vars.len())).collect();
|
||||||
for (i, p) in vars.iter().enumerate() {
|
for (i, p) in vars.iter().enumerate() {
|
||||||
@ -48,7 +50,7 @@ fn sat_at_most_one_by_key<K: std::hash::Hash + Eq>(
|
|||||||
solver: &mut varisat::Solver<'_>,
|
solver: &mut varisat::Solver<'_>,
|
||||||
data: impl Iterator<Item = (K, varisat::Var)>,
|
data: impl Iterator<Item = (K, varisat::Var)>,
|
||||||
) -> HashMap<K, Vec<varisat::Var>> {
|
) -> HashMap<K, Vec<varisat::Var>> {
|
||||||
// no two packages with the same keys set
|
// No two packages with the same keys set
|
||||||
let mut by_keys: HashMap<K, Vec<varisat::Var>> = HashMap::new();
|
let mut by_keys: HashMap<K, Vec<varisat::Var>> = HashMap::new();
|
||||||
for (p, v) in data {
|
for (p, v) in data {
|
||||||
by_keys.entry(p).or_default().push(v)
|
by_keys.entry(p).or_default().push(v)
|
||||||
@ -59,40 +61,130 @@ fn sat_at_most_one_by_key<K: std::hash::Hash + Eq>(
|
|||||||
by_keys
|
by_keys
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_compatible_dep_summaries_by_name_in_toml(
|
type DependencyVarMap<'a> =
|
||||||
|
HashMap<InternedString, HashMap<(DepKind, Option<&'a Platform>), varisat::Var>>;
|
||||||
|
|
||||||
|
type DependencyFeatureVarMap<'a> = HashMap<
|
||||||
|
InternedString,
|
||||||
|
HashMap<(DepKind, Option<&'a Platform>), HashMap<InternedString, varisat::Var>>,
|
||||||
|
>;
|
||||||
|
|
||||||
|
fn create_dependencies_vars<'a>(
|
||||||
|
solver: &mut varisat::Solver<'_>,
|
||||||
|
pkg_var: varisat::Var,
|
||||||
|
pkg_dependencies: &'a [Dependency],
|
||||||
|
pkg_features: &FeatureMap,
|
||||||
|
) -> (DependencyVarMap<'a>, DependencyFeatureVarMap<'a>) {
|
||||||
|
let mut var_for_is_dependencies_used = DependencyVarMap::new();
|
||||||
|
let mut var_for_is_dependencies_features_used = DependencyFeatureVarMap::new();
|
||||||
|
|
||||||
|
for dep in pkg_dependencies {
|
||||||
|
let (name, kind, platform) = (dep.name_in_toml(), dep.kind(), dep.platform());
|
||||||
|
|
||||||
|
var_for_is_dependencies_used
|
||||||
|
.entry(name)
|
||||||
|
.or_default()
|
||||||
|
.insert((kind, platform), solver.new_var());
|
||||||
|
|
||||||
|
let dep_feature_var_map = dep
|
||||||
|
.features()
|
||||||
|
.iter()
|
||||||
|
.map(|&f| (f, solver.new_var()))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
var_for_is_dependencies_features_used
|
||||||
|
.entry(name)
|
||||||
|
.or_default()
|
||||||
|
.insert((kind, platform), dep_feature_var_map);
|
||||||
|
}
|
||||||
|
|
||||||
|
for feature_values in pkg_features.values() {
|
||||||
|
for feature_value in feature_values {
|
||||||
|
let FeatureValue::DepFeature {
|
||||||
|
dep_name,
|
||||||
|
dep_feature,
|
||||||
|
weak: _,
|
||||||
|
} = *feature_value
|
||||||
|
else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
for dep_features_vars in var_for_is_dependencies_features_used
|
||||||
|
.get_mut(&dep_name)
|
||||||
|
.expect("feature dep name exists")
|
||||||
|
.values_mut()
|
||||||
|
{
|
||||||
|
dep_features_vars.insert(dep_feature, solver.new_var());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If a package dependency is used, then the package is used
|
||||||
|
for dep_var_map in var_for_is_dependencies_used.values() {
|
||||||
|
for dep_var in dep_var_map.values() {
|
||||||
|
solver.add_clause(&[dep_var.negative(), pkg_var.positive()]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If a dependency feature is used, then the dependency is used
|
||||||
|
for (&dep_name, map) in &mut var_for_is_dependencies_features_used {
|
||||||
|
for (&(dep_kind, dep_platform), dep_feature_var_map) in map {
|
||||||
|
for dep_feature_var in dep_feature_var_map.values() {
|
||||||
|
let dep_var_map = &var_for_is_dependencies_used[&dep_name];
|
||||||
|
let dep_var = dep_var_map[&(dep_kind, dep_platform)];
|
||||||
|
solver.add_clause(&[dep_feature_var.negative(), dep_var.positive()]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(
|
||||||
|
var_for_is_dependencies_used,
|
||||||
|
var_for_is_dependencies_features_used,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn process_pkg_dependencies(
|
||||||
|
solver: &mut varisat::Solver<'_>,
|
||||||
|
var_for_is_dependencies_used: &DependencyVarMap<'_>,
|
||||||
|
var_for_is_dependencies_features_used: &DependencyFeatureVarMap<'_>,
|
||||||
|
pkg_var: varisat::Var,
|
||||||
pkg_dependencies: &[Dependency],
|
pkg_dependencies: &[Dependency],
|
||||||
by_name: &HashMap<InternedString, Vec<Summary>>,
|
) {
|
||||||
) -> HashMap<InternedString, Vec<Summary>> {
|
// Add clauses for package dependencies
|
||||||
let empty_vec = vec![];
|
for dep in pkg_dependencies {
|
||||||
|
let (name, kind, platform) = (dep.name_in_toml(), dep.kind(), dep.platform());
|
||||||
|
let dep_var_map = &var_for_is_dependencies_used[&name];
|
||||||
|
let dep_var = dep_var_map[&(kind, platform)];
|
||||||
|
|
||||||
pkg_dependencies
|
if !dep.is_optional() {
|
||||||
.iter()
|
solver.add_clause(&[pkg_var.negative(), dep_var.positive()]);
|
||||||
.map(|dep| {
|
}
|
||||||
let name_in_toml = dep.name_in_toml();
|
|
||||||
|
|
||||||
let compatible_summaries = by_name
|
for &feature_name in dep.features() {
|
||||||
.get(&dep.package_name())
|
let dep_feature_var =
|
||||||
.unwrap_or(&empty_vec)
|
&var_for_is_dependencies_features_used[&name][&(kind, platform)][&feature_name];
|
||||||
.iter()
|
|
||||||
.filter(|s| dep.matches_id(s.package_id()))
|
|
||||||
.filter(|s| dep.features().iter().all(|f| s.features().contains_key(f)))
|
|
||||||
.cloned()
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
(name_in_toml, compatible_summaries)
|
solver.add_clause(&[dep_var.negative(), dep_feature_var.positive()]);
|
||||||
})
|
}
|
||||||
.collect()
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_pkg_features(
|
fn process_pkg_features(
|
||||||
solver: &mut varisat::Solver<'_>,
|
solver: &mut varisat::Solver<'_>,
|
||||||
var_for_is_packages_used: &HashMap<PackageId, varisat::Var>,
|
var_for_is_dependencies_used: &DependencyVarMap<'_>,
|
||||||
var_for_is_packages_features_used: &HashMap<PackageId, HashMap<InternedString, varisat::Var>>,
|
var_for_is_dependencies_features_used: &DependencyFeatureVarMap<'_>,
|
||||||
pkg_feature_var_map: &HashMap<InternedString, varisat::Var>,
|
pkg_feature_var_map: &HashMap<InternedString, varisat::Var>,
|
||||||
|
pkg_dependencies: &[Dependency],
|
||||||
pkg_features: &FeatureMap,
|
pkg_features: &FeatureMap,
|
||||||
compatible_dep_summaries_by_name_in_toml: &HashMap<InternedString, Vec<Summary>>,
|
check_dev_dependencies: bool,
|
||||||
) {
|
) {
|
||||||
// add clauses for package features
|
let optional_dependencies = pkg_dependencies
|
||||||
|
.iter()
|
||||||
|
.filter(|dep| dep.is_optional())
|
||||||
|
.map(|dep| (dep.kind(), dep.platform(), dep.name_in_toml()))
|
||||||
|
.collect::<HashSet<_>>();
|
||||||
|
|
||||||
|
// Add clauses for package features
|
||||||
for (&feature_name, feature_values) in pkg_features {
|
for (&feature_name, feature_values) in pkg_features {
|
||||||
for feature_value in feature_values {
|
for feature_value in feature_values {
|
||||||
let pkg_feature_var = pkg_feature_var_map[&feature_name];
|
let pkg_feature_var = pkg_feature_var_map[&feature_name];
|
||||||
@ -105,39 +197,56 @@ fn process_pkg_features(
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
FeatureValue::Dep { dep_name } => {
|
FeatureValue::Dep { dep_name } => {
|
||||||
let dep_clause = compatible_dep_summaries_by_name_in_toml[&dep_name]
|
// Add a clause for each dependency with the provided name (normal/build/dev with target)
|
||||||
.iter()
|
for (&(dep_kind, _), &dep_var) in &var_for_is_dependencies_used[&dep_name] {
|
||||||
.map(|dep| var_for_is_packages_used[&dep.package_id()].positive())
|
if dep_kind == DepKind::Development && !check_dev_dependencies {
|
||||||
.chain([pkg_feature_var.negative()])
|
continue;
|
||||||
.collect::<Vec<_>>();
|
}
|
||||||
|
solver.add_clause(&[pkg_feature_var.negative(), dep_var.positive()]);
|
||||||
solver.add_clause(&dep_clause);
|
}
|
||||||
}
|
}
|
||||||
FeatureValue::DepFeature {
|
FeatureValue::DepFeature {
|
||||||
dep_name,
|
dep_name,
|
||||||
dep_feature: dep_feature_name,
|
dep_feature: dep_feature_name,
|
||||||
weak,
|
weak,
|
||||||
} => {
|
} => {
|
||||||
for dep in &compatible_dep_summaries_by_name_in_toml[&dep_name] {
|
// Behavior of the feature:
|
||||||
let dep_var = var_for_is_packages_used[&dep.package_id()];
|
// * if dependency `dep_name` is not optional, its feature `"dep_feature_name"` is activated.
|
||||||
let dep_feature_var =
|
// * if dependency `dep_name` is optional:
|
||||||
var_for_is_packages_features_used[&dep.package_id()][&dep_feature_name];
|
// - if this is a weak dependency feature:
|
||||||
|
// - feature `"dep_feature_name"` of dependency `dep_name` is activated if `dep_name` has been activated via another feature.
|
||||||
|
// - if this is not a weak dependency feature:
|
||||||
|
// - feature `dep_name` is activated if it exists.
|
||||||
|
// - dependency `dep_name` is activated.
|
||||||
|
// - feature `"dep_feature_name"` of dependency `dep_name` is activated.
|
||||||
|
|
||||||
|
// Add clauses for each dependency with the provided name (normal/build/dev with target)
|
||||||
|
let dep_var_map = &var_for_is_dependencies_used[&dep_name];
|
||||||
|
for (&(dep_kind, dep_platform), &dep_var) in dep_var_map {
|
||||||
|
if dep_kind == DepKind::Development && !check_dev_dependencies {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let dep_feature_var = &var_for_is_dependencies_features_used[&dep_name]
|
||||||
|
[&(dep_kind, dep_platform)][&dep_feature_name];
|
||||||
|
|
||||||
solver.add_clause(&[
|
solver.add_clause(&[
|
||||||
pkg_feature_var.negative(),
|
pkg_feature_var.negative(),
|
||||||
dep_var.negative(),
|
dep_var.negative(),
|
||||||
dep_feature_var.positive(),
|
dep_feature_var.positive(),
|
||||||
]);
|
]);
|
||||||
}
|
|
||||||
|
|
||||||
if !weak {
|
let key = (dep_kind, dep_platform, dep_name);
|
||||||
let dep_clause = compatible_dep_summaries_by_name_in_toml[&dep_name]
|
if !weak && optional_dependencies.contains(&key) {
|
||||||
.iter()
|
solver.add_clause(&[pkg_feature_var.negative(), dep_var.positive()]);
|
||||||
.map(|dep| var_for_is_packages_used[&dep.package_id()].positive())
|
|
||||||
.chain([pkg_feature_var.negative()])
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
solver.add_clause(&dep_clause);
|
if let Some(other_feature_var) = pkg_feature_var_map.get(&dep_name) {
|
||||||
|
solver.add_clause(&[
|
||||||
|
pkg_feature_var.negative(),
|
||||||
|
other_feature_var.positive(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -145,48 +254,77 @@ fn process_pkg_features(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_pkg_dependencies(
|
fn process_compatible_dep_summaries(
|
||||||
solver: &mut varisat::Solver<'_>,
|
solver: &mut varisat::Solver<'_>,
|
||||||
|
var_for_is_dependencies_used: &DependencyVarMap<'_>,
|
||||||
|
var_for_is_dependencies_features_used: &DependencyFeatureVarMap<'_>,
|
||||||
var_for_is_packages_used: &HashMap<PackageId, varisat::Var>,
|
var_for_is_packages_used: &HashMap<PackageId, varisat::Var>,
|
||||||
var_for_is_packages_features_used: &HashMap<PackageId, HashMap<InternedString, varisat::Var>>,
|
var_for_is_packages_features_used: &HashMap<PackageId, HashMap<InternedString, varisat::Var>>,
|
||||||
pkg_var: varisat::Var,
|
by_name: &HashMap<InternedString, Vec<Summary>>,
|
||||||
pkg_dependencies: &[Dependency],
|
pkg_dependencies: &[Dependency],
|
||||||
compatible_dep_summaries_by_name_in_toml: &HashMap<InternedString, Vec<Summary>>,
|
check_dev_dependencies: bool,
|
||||||
) {
|
) {
|
||||||
for dep in pkg_dependencies {
|
for dep in pkg_dependencies {
|
||||||
let compatible_dep_summaries =
|
if dep.kind() == DepKind::Development && !check_dev_dependencies {
|
||||||
&compatible_dep_summaries_by_name_in_toml[&dep.name_in_toml()];
|
continue;
|
||||||
|
|
||||||
// add clauses for package dependency features
|
|
||||||
for dep_summary in compatible_dep_summaries {
|
|
||||||
let dep_package_id = dep_summary.package_id();
|
|
||||||
|
|
||||||
let default_feature = if dep.uses_default_features()
|
|
||||||
&& dep_summary.features().contains_key(&*INTERNED_DEFAULT)
|
|
||||||
{
|
|
||||||
Some(&INTERNED_DEFAULT)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
for &feature_name in default_feature.into_iter().chain(dep.features()) {
|
|
||||||
solver.add_clause(&[
|
|
||||||
pkg_var.negative(),
|
|
||||||
var_for_is_packages_used[&dep_package_id].negative(),
|
|
||||||
var_for_is_packages_features_used[&dep_package_id][&feature_name].positive(),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// active packages need to activate each of their non-optional dependencies
|
let (name, kind, platform) = (dep.name_in_toml(), dep.kind(), dep.platform());
|
||||||
if !dep.is_optional() {
|
let dep_var_map = &var_for_is_dependencies_used[&name];
|
||||||
let dep_clause = compatible_dep_summaries
|
let dep_var = dep_var_map[&(kind, platform)];
|
||||||
|
|
||||||
|
let dep_feature_var_map = &var_for_is_dependencies_features_used[&name][&(kind, platform)];
|
||||||
|
|
||||||
|
let compatible_summaries = by_name
|
||||||
|
.get(&dep.package_name())
|
||||||
|
.into_iter()
|
||||||
|
.flatten()
|
||||||
|
.filter(|s| dep.matches(s))
|
||||||
|
.filter(|s| dep.features().iter().all(|f| s.features().contains_key(f)))
|
||||||
|
.cloned()
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
// At least one compatible package should be activated
|
||||||
|
let dep_clause = compatible_summaries
|
||||||
|
.iter()
|
||||||
|
.map(|s| var_for_is_packages_used[&s.package_id()].positive())
|
||||||
|
.chain([dep_var.negative()])
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
solver.add_clause(&dep_clause);
|
||||||
|
|
||||||
|
for (&feature_name, &dep_feature_var) in dep_feature_var_map {
|
||||||
|
// At least one compatible package with the additional feature should be activated
|
||||||
|
let dep_feature_clause = compatible_summaries
|
||||||
.iter()
|
.iter()
|
||||||
.map(|d| var_for_is_packages_used[&d.package_id()].positive())
|
.filter_map(|s| {
|
||||||
.chain([pkg_var.negative()])
|
var_for_is_packages_features_used[&s.package_id()].get(&feature_name)
|
||||||
|
})
|
||||||
|
.map(|var| var.positive())
|
||||||
|
.chain([dep_feature_var.negative()])
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
solver.add_clause(&dep_clause);
|
solver.add_clause(&dep_feature_clause);
|
||||||
|
}
|
||||||
|
|
||||||
|
if dep.uses_default_features() {
|
||||||
|
// For the selected package for this dependency, the `"default"` feature should be activated if it exists
|
||||||
|
let mut dep_default_clause = vec![dep_var.negative()];
|
||||||
|
|
||||||
|
for s in &compatible_summaries {
|
||||||
|
let s_pkg_id = s.package_id();
|
||||||
|
let s_var = var_for_is_packages_used[&s_pkg_id];
|
||||||
|
let s_feature_var_map = &var_for_is_packages_features_used[&s_pkg_id];
|
||||||
|
|
||||||
|
if let Some(s_default_feature_var) = s_feature_var_map.get(&INTERNED_DEFAULT) {
|
||||||
|
dep_default_clause
|
||||||
|
.extend_from_slice(&[s_var.negative(), s_default_feature_var.positive()]);
|
||||||
|
} else {
|
||||||
|
dep_default_clause.push(s_var.positive());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
solver.add_clause(&dep_default_clause);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -209,44 +347,50 @@ pub struct SatResolver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl SatResolver {
|
impl SatResolver {
|
||||||
pub fn new(registry: &[Summary]) -> Self {
|
pub fn new<'a>(registry: impl IntoIterator<Item = &'a Summary>) -> Self {
|
||||||
|
let check_dev_dependencies = false;
|
||||||
|
|
||||||
|
let mut by_name: HashMap<InternedString, Vec<Summary>> = HashMap::new();
|
||||||
|
for pkg in registry {
|
||||||
|
by_name.entry(pkg.name()).or_default().push(pkg.clone())
|
||||||
|
}
|
||||||
|
|
||||||
let mut solver = varisat::Solver::new();
|
let mut solver = varisat::Solver::new();
|
||||||
|
|
||||||
// That represents each package version which is set to "true" if the packages in the lock file and "false" if it is unused.
|
// Create boolean variables for packages and packages features
|
||||||
let var_for_is_packages_used = registry
|
let mut var_for_is_packages_used = HashMap::new();
|
||||||
.iter()
|
let mut var_for_is_packages_features_used = HashMap::<_, HashMap<_, _>>::new();
|
||||||
.map(|s| (s.package_id(), solver.new_var()))
|
|
||||||
.collect::<HashMap<_, _>>();
|
|
||||||
|
|
||||||
// That represents each feature of each package version, which is set to "true" if the package feature is used.
|
for pkg in by_name.values().flatten() {
|
||||||
let var_for_is_packages_features_used = registry
|
let pkg_id = pkg.package_id();
|
||||||
.iter()
|
|
||||||
.map(|s| {
|
|
||||||
(
|
|
||||||
s.package_id(),
|
|
||||||
(s.features().keys().map(|&f| (f, solver.new_var()))).collect(),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.collect::<HashMap<_, HashMap<_, _>>>();
|
|
||||||
|
|
||||||
// if a package feature is used, then the package is used
|
var_for_is_packages_used.insert(pkg_id, solver.new_var());
|
||||||
for (package, pkg_feature_var_map) in &var_for_is_packages_features_used {
|
|
||||||
for (_, package_feature_var) in pkg_feature_var_map {
|
var_for_is_packages_features_used.insert(
|
||||||
let package_var = var_for_is_packages_used[package];
|
pkg_id,
|
||||||
solver.add_clause(&[package_feature_var.negative(), package_var.positive()]);
|
(pkg.features().keys().map(|&f| (f, solver.new_var()))).collect(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If a package feature is used, then the package is used
|
||||||
|
for (&pkg_id, pkg_feature_var_map) in &var_for_is_packages_features_used {
|
||||||
|
for pkg_feature_var in pkg_feature_var_map.values() {
|
||||||
|
let pkg_var = var_for_is_packages_used[&pkg_id];
|
||||||
|
solver.add_clause(&[pkg_feature_var.negative(), pkg_var.positive()]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// no two packages with the same links set
|
// No two packages with the same links set
|
||||||
sat_at_most_one_by_key(
|
sat_at_most_one_by_key(
|
||||||
&mut solver,
|
&mut solver,
|
||||||
registry
|
by_name
|
||||||
.iter()
|
.values()
|
||||||
|
.flatten()
|
||||||
.map(|s| (s.links(), var_for_is_packages_used[&s.package_id()]))
|
.map(|s| (s.links(), var_for_is_packages_used[&s.package_id()]))
|
||||||
.filter(|(l, _)| l.is_some()),
|
.filter(|(l, _)| l.is_some()),
|
||||||
);
|
);
|
||||||
|
|
||||||
// no two semver compatible versions of the same package
|
// No two semver compatible versions of the same package
|
||||||
sat_at_most_one_by_key(
|
sat_at_most_one_by_key(
|
||||||
&mut solver,
|
&mut solver,
|
||||||
var_for_is_packages_used
|
var_for_is_packages_used
|
||||||
@ -254,36 +398,43 @@ impl SatResolver {
|
|||||||
.map(|(p, &v)| (p.as_activations_key(), v)),
|
.map(|(p, &v)| (p.as_activations_key(), v)),
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut by_name: HashMap<InternedString, Vec<Summary>> = HashMap::new();
|
for pkg in by_name.values().flatten() {
|
||||||
|
|
||||||
for p in registry {
|
|
||||||
by_name.entry(p.name()).or_default().push(p.clone())
|
|
||||||
}
|
|
||||||
|
|
||||||
for pkg in registry {
|
|
||||||
let pkg_id = pkg.package_id();
|
let pkg_id = pkg.package_id();
|
||||||
let pkg_dependencies = pkg.dependencies();
|
let pkg_dependencies = pkg.dependencies();
|
||||||
let pkg_features = pkg.features();
|
let pkg_features = pkg.features();
|
||||||
|
let pkg_var = var_for_is_packages_used[&pkg_id];
|
||||||
|
|
||||||
let compatible_dep_summaries_by_name_in_toml =
|
// Create boolean variables for dependencies and dependencies features
|
||||||
find_compatible_dep_summaries_by_name_in_toml(pkg_dependencies, &by_name);
|
let (var_for_is_dependencies_used, var_for_is_dependencies_features_used) =
|
||||||
|
create_dependencies_vars(&mut solver, pkg_var, pkg_dependencies, pkg_features);
|
||||||
process_pkg_features(
|
|
||||||
&mut solver,
|
|
||||||
&var_for_is_packages_used,
|
|
||||||
&var_for_is_packages_features_used,
|
|
||||||
&var_for_is_packages_features_used[&pkg_id],
|
|
||||||
pkg_features,
|
|
||||||
&compatible_dep_summaries_by_name_in_toml,
|
|
||||||
);
|
|
||||||
|
|
||||||
process_pkg_dependencies(
|
process_pkg_dependencies(
|
||||||
&mut solver,
|
&mut solver,
|
||||||
|
&var_for_is_dependencies_used,
|
||||||
|
&var_for_is_dependencies_features_used,
|
||||||
|
pkg_var,
|
||||||
|
pkg_dependencies,
|
||||||
|
);
|
||||||
|
|
||||||
|
process_pkg_features(
|
||||||
|
&mut solver,
|
||||||
|
&var_for_is_dependencies_used,
|
||||||
|
&var_for_is_dependencies_features_used,
|
||||||
|
&var_for_is_packages_features_used[&pkg_id],
|
||||||
|
pkg_dependencies,
|
||||||
|
pkg_features,
|
||||||
|
check_dev_dependencies,
|
||||||
|
);
|
||||||
|
|
||||||
|
process_compatible_dep_summaries(
|
||||||
|
&mut solver,
|
||||||
|
&var_for_is_dependencies_used,
|
||||||
|
&var_for_is_dependencies_features_used,
|
||||||
&var_for_is_packages_used,
|
&var_for_is_packages_used,
|
||||||
&var_for_is_packages_features_used,
|
&var_for_is_packages_features_used,
|
||||||
var_for_is_packages_used[&pkg_id],
|
&by_name,
|
||||||
pkg_dependencies,
|
pkg_dependencies,
|
||||||
&compatible_dep_summaries_by_name_in_toml,
|
check_dev_dependencies,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -313,8 +464,31 @@ impl SatResolver {
|
|||||||
|
|
||||||
let root_var = solver.new_var();
|
let root_var = solver.new_var();
|
||||||
|
|
||||||
// root package is always used
|
// Create boolean variables for dependencies and dependencies features
|
||||||
// root vars from previous runs are deactivated
|
let (var_for_is_dependencies_used, var_for_is_dependencies_features_used) =
|
||||||
|
create_dependencies_vars(solver, root_var, root_dependencies, &FeatureMap::new());
|
||||||
|
|
||||||
|
process_pkg_dependencies(
|
||||||
|
solver,
|
||||||
|
&var_for_is_dependencies_used,
|
||||||
|
&var_for_is_dependencies_features_used,
|
||||||
|
root_var,
|
||||||
|
root_dependencies,
|
||||||
|
);
|
||||||
|
|
||||||
|
process_compatible_dep_summaries(
|
||||||
|
solver,
|
||||||
|
&var_for_is_dependencies_used,
|
||||||
|
&var_for_is_dependencies_features_used,
|
||||||
|
var_for_is_packages_used,
|
||||||
|
var_for_is_packages_features_used,
|
||||||
|
by_name,
|
||||||
|
root_dependencies,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Root package is always used.
|
||||||
|
// Root vars from previous runs are deactivated.
|
||||||
let assumption = old_root_vars
|
let assumption = old_root_vars
|
||||||
.iter()
|
.iter()
|
||||||
.map(|v| v.negative())
|
.map(|v| v.negative())
|
||||||
@ -323,18 +497,6 @@ impl SatResolver {
|
|||||||
|
|
||||||
old_root_vars.push(root_var);
|
old_root_vars.push(root_var);
|
||||||
|
|
||||||
let compatible_dep_summaries_by_name_in_toml =
|
|
||||||
find_compatible_dep_summaries_by_name_in_toml(root_dependencies, &by_name);
|
|
||||||
|
|
||||||
process_pkg_dependencies(
|
|
||||||
solver,
|
|
||||||
var_for_is_packages_used,
|
|
||||||
var_for_is_packages_features_used,
|
|
||||||
root_var,
|
|
||||||
root_dependencies,
|
|
||||||
&compatible_dep_summaries_by_name_in_toml,
|
|
||||||
);
|
|
||||||
|
|
||||||
solver.assume(&assumption);
|
solver.assume(&assumption);
|
||||||
|
|
||||||
solver
|
solver
|
||||||
@ -353,7 +515,7 @@ impl SatResolver {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// root vars from previous runs are deactivated
|
// Root vars from previous runs are deactivated
|
||||||
let assumption = (self.old_root_vars.iter().map(|v| v.negative()))
|
let assumption = (self.old_root_vars.iter().map(|v| v.negative()))
|
||||||
.chain(
|
.chain(
|
||||||
self.var_for_is_packages_used
|
self.var_for_is_packages_used
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
use cargo::core::Dependency;
|
use cargo::core::{dependency::DepKind, Dependency};
|
||||||
|
|
||||||
use resolver_tests::{
|
use resolver_tests::{
|
||||||
helpers::{dep, dep_req, pkg, pkg_dep, pkg_dep_with, registry, ToDep},
|
helpers::{
|
||||||
|
dep, dep_kind, dep_platform, dep_req, dep_req_kind, dep_req_platform, pkg, pkg_dep,
|
||||||
|
pkg_dep_with, registry, ToDep,
|
||||||
|
},
|
||||||
pkg, resolve, resolve_and_validated,
|
pkg, resolve, resolve_and_validated,
|
||||||
sat::SatResolver,
|
sat::SatResolver,
|
||||||
};
|
};
|
||||||
@ -165,6 +168,59 @@ fn missing_feature() {
|
|||||||
assert!(resolve_and_validated(vec!["a".with(&["f"])], ®, &mut sat_resolver).is_err());
|
assert!(resolve_and_validated(vec!["a".with(&["f"])], ®, &mut sat_resolver).is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn missing_dep_feature() {
|
||||||
|
let reg = registry(vec![
|
||||||
|
pkg("a"),
|
||||||
|
pkg_dep_with("dep", vec![dep("a")], &[("f", &["a/a"])]),
|
||||||
|
]);
|
||||||
|
|
||||||
|
let deps = vec![dep("dep").with(&["f"])];
|
||||||
|
let mut sat_resolver = SatResolver::new(®);
|
||||||
|
assert!(resolve_and_validated(deps, ®, &mut sat_resolver).is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn missing_weak_dep_feature() {
|
||||||
|
let reg = registry(vec![
|
||||||
|
pkg("a"),
|
||||||
|
pkg_dep_with("dep1", vec![dep("a")], &[("f", &["a/a"])]),
|
||||||
|
pkg_dep_with("dep2", vec!["a".opt()], &[("f", &["a/a"])]),
|
||||||
|
pkg_dep_with("dep3", vec!["a".opt()], &[("f", &["a?/a"])]),
|
||||||
|
pkg_dep_with("dep4", vec!["x".opt()], &[("f", &["x?/a"])]),
|
||||||
|
]);
|
||||||
|
|
||||||
|
let deps = vec![dep("dep1").with(&["f"])];
|
||||||
|
let mut sat_resolver = SatResolver::new(®);
|
||||||
|
assert!(resolve_and_validated(deps, ®, &mut sat_resolver).is_err());
|
||||||
|
|
||||||
|
let deps = vec![dep("dep2").with(&["f"])];
|
||||||
|
let mut sat_resolver = SatResolver::new(®);
|
||||||
|
assert!(resolve_and_validated(deps, ®, &mut sat_resolver).is_err());
|
||||||
|
|
||||||
|
let deps = vec![dep("dep2").with(&["a", "f"])];
|
||||||
|
let mut sat_resolver = SatResolver::new(®);
|
||||||
|
assert!(resolve_and_validated(deps, ®, &mut sat_resolver).is_err());
|
||||||
|
|
||||||
|
// Weak dependencies are not supported yet in the dependency resolver
|
||||||
|
let deps = vec![dep("dep3").with(&["f"])];
|
||||||
|
assert!(resolve(deps.clone(), ®).is_err());
|
||||||
|
assert!(SatResolver::new(®).sat_resolve(&deps));
|
||||||
|
|
||||||
|
let deps = vec![dep("dep3").with(&["a", "f"])];
|
||||||
|
let mut sat_resolver = SatResolver::new(®);
|
||||||
|
assert!(resolve_and_validated(deps, ®, &mut sat_resolver).is_err());
|
||||||
|
|
||||||
|
// Weak dependencies are not supported yet in the dependency resolver
|
||||||
|
let deps = vec![dep("dep4").with(&["f"])];
|
||||||
|
assert!(resolve(deps.clone(), ®).is_err());
|
||||||
|
assert!(SatResolver::new(®).sat_resolve(&deps));
|
||||||
|
|
||||||
|
let deps = vec![dep("dep4").with(&["x", "f"])];
|
||||||
|
let mut sat_resolver = SatResolver::new(®);
|
||||||
|
assert!(resolve_and_validated(deps, ®, &mut sat_resolver).is_err());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn conflict_feature_and_sys() {
|
fn conflict_feature_and_sys() {
|
||||||
let reg = registry(vec![
|
let reg = registry(vec![
|
||||||
@ -204,8 +260,269 @@ fn conflict_weak_features() {
|
|||||||
|
|
||||||
let deps = vec![dep("dep").with(&["a1", "a2"])];
|
let deps = vec![dep("dep").with(&["a1", "a2"])];
|
||||||
|
|
||||||
// The following asserts should be updated when support for weak dependencies
|
// Weak dependencies are not supported yet in the dependency resolver
|
||||||
// is added to the dependency resolver.
|
|
||||||
assert!(resolve(deps.clone(), ®).is_err());
|
assert!(resolve(deps.clone(), ®).is_err());
|
||||||
assert!(SatResolver::new(®).sat_resolve(&deps));
|
assert!(SatResolver::new(®).sat_resolve(&deps));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn multiple_dep_kinds_and_targets() {
|
||||||
|
let reg = registry(vec![
|
||||||
|
pkg(("a-sys", "1.0.0")),
|
||||||
|
pkg(("a-sys", "2.0.0")),
|
||||||
|
pkg_dep_with(
|
||||||
|
"dep1",
|
||||||
|
vec![
|
||||||
|
dep_req_platform("a-sys", "1.0.0", "cfg(all())").opt(),
|
||||||
|
dep_req("a-sys", "1.0.0").opt(),
|
||||||
|
dep_req_kind("a-sys", "2.0.0", DepKind::Build).opt(),
|
||||||
|
],
|
||||||
|
&[("default", &["dep:a-sys"])],
|
||||||
|
),
|
||||||
|
pkg_dep_with(
|
||||||
|
"dep2",
|
||||||
|
vec![
|
||||||
|
dep_req_platform("a-sys", "1.0.0", "cfg(all())").opt(),
|
||||||
|
dep_req("a-sys", "1.0.0").opt(),
|
||||||
|
dep_req_kind("a-sys", "2.0.0", DepKind::Development).rename("a-sys-dev"),
|
||||||
|
],
|
||||||
|
&[("default", &["dep:a-sys", "a-sys-dev/bad"])],
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
|
||||||
|
let deps = vec![dep("dep1")];
|
||||||
|
let mut sat_resolver = SatResolver::new(®);
|
||||||
|
assert!(resolve_and_validated(deps, ®, &mut sat_resolver).is_err());
|
||||||
|
|
||||||
|
let deps = vec![dep("dep2")];
|
||||||
|
let mut sat_resolver = SatResolver::new(®);
|
||||||
|
assert!(resolve_and_validated(deps, ®, &mut sat_resolver).is_ok());
|
||||||
|
|
||||||
|
let deps = vec![
|
||||||
|
dep_req("a-sys", "1.0.0"),
|
||||||
|
dep_req_kind("a-sys", "2.0.0", DepKind::Build).rename("a2"),
|
||||||
|
];
|
||||||
|
let mut sat_resolver = SatResolver::new(®);
|
||||||
|
assert!(resolve_and_validated(deps, ®, &mut sat_resolver).is_err());
|
||||||
|
|
||||||
|
let deps = vec![
|
||||||
|
dep_req("a-sys", "1.0.0"),
|
||||||
|
dep_req_kind("a-sys", "2.0.0", DepKind::Development).rename("a2"),
|
||||||
|
];
|
||||||
|
let mut sat_resolver = SatResolver::new(®);
|
||||||
|
assert!(resolve_and_validated(deps, ®, &mut sat_resolver).is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn multiple_dep_kinds_and_targets_with_different_packages() {
|
||||||
|
let reg = registry(vec![
|
||||||
|
pkg_dep_with("a", vec![], &[("f", &[])]),
|
||||||
|
pkg_dep_with("b", vec![], &[("f", &[])]),
|
||||||
|
pkg_dep_with("c", vec![], &[("g", &[])]),
|
||||||
|
pkg_dep_with(
|
||||||
|
"dep1",
|
||||||
|
vec![
|
||||||
|
"a".opt().rename("x").with(&["f"]),
|
||||||
|
dep_platform("a", "cfg(all())").opt().rename("x"),
|
||||||
|
dep_kind("b", DepKind::Build).opt().rename("x").with(&["f"]),
|
||||||
|
],
|
||||||
|
&[("default", &["x"])],
|
||||||
|
),
|
||||||
|
pkg_dep_with(
|
||||||
|
"dep2",
|
||||||
|
vec![
|
||||||
|
"a".opt().rename("x").with(&["f"]),
|
||||||
|
dep_platform("a", "cfg(all())").opt().rename("x"),
|
||||||
|
dep_kind("c", DepKind::Build).opt().rename("x").with(&["f"]),
|
||||||
|
],
|
||||||
|
&[("default", &["x"])],
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
|
||||||
|
let deps = vec!["dep1".with(&["default"])];
|
||||||
|
let mut sat_resolver = SatResolver::new(®);
|
||||||
|
assert!(resolve_and_validated(deps, ®, &mut sat_resolver).is_ok());
|
||||||
|
|
||||||
|
let deps = vec!["dep2".with(&["default"])];
|
||||||
|
let mut sat_resolver = SatResolver::new(®);
|
||||||
|
assert!(resolve_and_validated(deps, ®, &mut sat_resolver).is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn dep_feature_with_shadowing_feature() {
|
||||||
|
let reg = registry(vec![
|
||||||
|
pkg_dep_with("a", vec![], &[("b", &[])]),
|
||||||
|
pkg_dep_with(
|
||||||
|
"dep",
|
||||||
|
vec!["a".opt().rename("aa"), "c".opt()],
|
||||||
|
&[("default", &["aa/b"]), ("aa", &["c"])],
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
|
||||||
|
let deps = vec!["dep".with(&["default"])];
|
||||||
|
let mut sat_resolver = SatResolver::new(®);
|
||||||
|
assert!(resolve_and_validated(deps, ®, &mut sat_resolver).is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn dep_feature_not_optional_with_shadowing_feature() {
|
||||||
|
let reg = registry(vec![
|
||||||
|
pkg_dep_with("a", vec![], &[("b", &[])]),
|
||||||
|
pkg_dep_with(
|
||||||
|
"dep",
|
||||||
|
vec!["a".rename("aa"), "c".opt()],
|
||||||
|
&[("default", &["aa/b"]), ("aa", &["c"])],
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
|
||||||
|
let deps = vec!["dep".with(&["default"])];
|
||||||
|
let mut sat_resolver = SatResolver::new(®);
|
||||||
|
assert!(resolve_and_validated(deps, ®, &mut sat_resolver).is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn dep_feature_weak_with_shadowing_feature() {
|
||||||
|
let reg = registry(vec![
|
||||||
|
pkg_dep_with("a", vec![], &[("b", &[])]),
|
||||||
|
pkg_dep_with(
|
||||||
|
"dep",
|
||||||
|
vec!["a".opt().rename("aa"), "c".opt()],
|
||||||
|
&[("default", &["aa?/b"]), ("aa", &["c"])],
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
|
||||||
|
let deps = vec!["dep".with(&["default"])];
|
||||||
|
let mut sat_resolver = SatResolver::new(®);
|
||||||
|
assert!(resolve_and_validated(deps, ®, &mut sat_resolver).is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn dep_feature_duplicate_with_shadowing_feature() {
|
||||||
|
let reg = registry(vec![
|
||||||
|
pkg_dep_with("a", vec![], &[("b", &[])]),
|
||||||
|
pkg_dep_with(
|
||||||
|
"dep",
|
||||||
|
vec![
|
||||||
|
"a".opt().rename("aa"),
|
||||||
|
dep_kind("a", DepKind::Build).rename("aa"),
|
||||||
|
"c".opt(),
|
||||||
|
],
|
||||||
|
&[("default", &["aa/b"]), ("aa", &["c"])],
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
|
||||||
|
let deps = vec!["dep".with(&["default"])];
|
||||||
|
let mut sat_resolver = SatResolver::new(®);
|
||||||
|
assert!(resolve_and_validated(deps, ®, &mut sat_resolver).is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn optional_dep_features() {
|
||||||
|
let reg = registry(vec![
|
||||||
|
pkg_dep("a", vec!["bad".opt()]),
|
||||||
|
pkg_dep("b", vec!["a".opt().with(&["bad"])]),
|
||||||
|
pkg_dep("dep", vec![dep("a"), dep("b")]),
|
||||||
|
]);
|
||||||
|
|
||||||
|
let deps = vec![dep("dep")];
|
||||||
|
let mut sat_resolver = SatResolver::new(®);
|
||||||
|
assert!(resolve_and_validated(deps, ®, &mut sat_resolver).is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn optional_dep_features_with_rename() {
|
||||||
|
let reg = registry(vec![
|
||||||
|
pkg("x1"),
|
||||||
|
pkg_dep("a", vec!["x1".opt(), "x2".opt(), "x3".opt()]),
|
||||||
|
pkg_dep(
|
||||||
|
"dep1",
|
||||||
|
vec![
|
||||||
|
"a".opt().with(&["x1"]),
|
||||||
|
dep_kind("a", DepKind::Build).opt().with(&["x2"]),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
pkg_dep(
|
||||||
|
"dep2",
|
||||||
|
vec![
|
||||||
|
"a".opt().with(&["x1"]),
|
||||||
|
"a".opt().rename("a2").with(&["x3"]),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
|
||||||
|
let deps = vec!["dep1".with(&["a"])];
|
||||||
|
let mut sat_resolver = SatResolver::new(®);
|
||||||
|
assert!(resolve_and_validated(deps, ®, &mut sat_resolver).is_err());
|
||||||
|
|
||||||
|
let deps = vec!["dep2".with(&["a"])];
|
||||||
|
let mut sat_resolver = SatResolver::new(®);
|
||||||
|
assert!(resolve_and_validated(deps, ®, &mut sat_resolver).is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn optional_weak_dep_features() {
|
||||||
|
let reg = registry(vec![
|
||||||
|
pkg_dep("a", vec!["bad".opt()]),
|
||||||
|
pkg_dep("b", vec![dep("a")]),
|
||||||
|
pkg_dep_with("dep", vec!["a".opt(), dep("b")], &[("f", &["a?/bad"])]),
|
||||||
|
]);
|
||||||
|
|
||||||
|
let deps = vec!["dep".with(&["f"])];
|
||||||
|
|
||||||
|
// Weak dependencies are not supported yet in the dependency resolver
|
||||||
|
assert!(resolve(deps.clone(), ®).is_err());
|
||||||
|
assert!(SatResolver::new(®).sat_resolve(&deps));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn default_feature_multiple_major_versions() {
|
||||||
|
let reg = registry(vec![
|
||||||
|
pkg_dep_with(("a", "0.2.0"), vec![], &[("default", &[])]),
|
||||||
|
pkg(("a", "0.3.0")),
|
||||||
|
pkg_dep_with(("a", "0.4.0"), vec![], &[("default", &[])]),
|
||||||
|
pkg_dep(
|
||||||
|
"dep1",
|
||||||
|
vec![
|
||||||
|
dep_req("a", ">=0.2, <0.4").with_default(),
|
||||||
|
dep_req("a", "0.2").rename("a2").with(&[]),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
pkg_dep(
|
||||||
|
"dep2",
|
||||||
|
vec![
|
||||||
|
dep_req("a", ">=0.2, <0.4").with_default(),
|
||||||
|
dep_req("a", "0.3").rename("a2").with(&[]),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
pkg_dep(
|
||||||
|
"dep3",
|
||||||
|
vec![
|
||||||
|
dep_req("a", ">=0.2, <0.4").with_default(),
|
||||||
|
dep_req("a", "0.2").rename("a1").with(&[]),
|
||||||
|
dep_req("a", "0.3").rename("a2").with(&[]),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
pkg_dep("dep4", vec![dep_req("a", ">=0.2, <0.4").with_default()]),
|
||||||
|
pkg_dep("dep5", vec![dep_req("a", ">=0.3, <0.5").with_default()]),
|
||||||
|
]);
|
||||||
|
|
||||||
|
let deps = vec![dep("dep1")];
|
||||||
|
let mut sat_resolver = SatResolver::new(®);
|
||||||
|
assert!(resolve_and_validated(deps, ®, &mut sat_resolver).is_ok());
|
||||||
|
|
||||||
|
let deps = vec![dep("dep2")];
|
||||||
|
let mut sat_resolver = SatResolver::new(®);
|
||||||
|
assert!(resolve_and_validated(deps, ®, &mut sat_resolver).is_ok());
|
||||||
|
|
||||||
|
let deps = vec![dep("dep3")];
|
||||||
|
let mut sat_resolver = SatResolver::new(®);
|
||||||
|
assert!(resolve_and_validated(deps, ®, &mut sat_resolver).is_ok());
|
||||||
|
|
||||||
|
let deps = vec![dep("dep4")];
|
||||||
|
let mut sat_resolver = SatResolver::new(®);
|
||||||
|
assert!(resolve_and_validated(deps, ®, &mut sat_resolver).is_ok());
|
||||||
|
|
||||||
|
let deps = vec![dep("dep5")];
|
||||||
|
let mut sat_resolver = SatResolver::new(®);
|
||||||
|
assert!(resolve_and_validated(deps, ®, &mut sat_resolver).is_ok());
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user