diff --git a/examples/caching_dependency_provider.rs b/examples/caching_dependency_provider.rs index 0968f1f7..e8eae5d2 100644 --- a/examples/caching_dependency_provider.rs +++ b/examples/caching_dependency_provider.rs @@ -2,7 +2,10 @@ use std::cell::RefCell; -use pubgrub::{resolve, Dependencies, DependencyProvider, OfflineDependencyProvider, Ranges}; +use pubgrub::{ + resolve, Dependencies, DependencyProvider, OfflineDependencyProvider, + PackageResolutionStatistics, Ranges, +}; type NumVS = Ranges; @@ -57,8 +60,13 @@ impl> DependencyProvider for CachingDependenc type Priority = DP::Priority; - fn prioritize(&self, package: &DP::P, ranges: &DP::VS, conflict_count: u32) -> Self::Priority { - self.remote_dependencies.prioritize(package, ranges, conflict_count) + fn prioritize( + &self, + package: &DP::P, + ranges: &DP::VS, + statis: &PackageResolutionStatistics, + ) -> Self::Priority { + self.remote_dependencies.prioritize(package, ranges, statis) } type Err = DP::Err; diff --git a/src/lib.rs b/src/lib.rs index cc1c943f..e2c46fa3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -227,7 +227,7 @@ pub use report::{ DefaultStringReportFormatter, DefaultStringReporter, DerivationTree, Derived, External, ReportFormatter, Reporter, }; -pub use solver::{resolve, Dependencies, DependencyProvider}; +pub use solver::{resolve, Dependencies, DependencyProvider, PackageResolutionStatistics}; pub use term::Term; pub use type_aliases::{DependencyConstraints, Map, SelectedDependencies, Set}; pub use version::{SemanticVersion, VersionParseError}; diff --git a/src/provider.rs b/src/provider.rs index ae806992..c07d343c 100644 --- a/src/provider.rs +++ b/src/provider.rs @@ -2,7 +2,10 @@ use std::cmp::Reverse; use std::collections::BTreeMap; use std::convert::Infallible; -use crate::{Dependencies, DependencyConstraints, DependencyProvider, Map, Package, VersionSet}; +use crate::{ + Dependencies, DependencyConstraints, DependencyProvider, Map, Package, + PackageResolutionStatistics, VersionSet, +}; /// A basic implementation of [DependencyProvider]. #[derive(Debug, Clone, Default)] @@ -95,7 +98,12 @@ impl DependencyProvider for OfflineDependencyProvide type Priority = (u32, Reverse); #[inline] - fn prioritize(&self, package: &P, range: &VS, conflict_count: u32) -> Self::Priority { + fn prioritize( + &self, + package: &P, + range: &VS, + statis: &PackageResolutionStatistics, + ) -> Self::Priority { let version_count = self .dependencies .get(package) @@ -104,7 +112,7 @@ impl DependencyProvider for OfflineDependencyProvide if version_count == 0 { return (u32::MAX, Reverse(0)); } - (conflict_count, Reverse(version_count as u32)) + (statis.conflict_count(), Reverse(version_count as u32)) } #[inline] diff --git a/src/solver.rs b/src/solver.rs index 19a65074..6546f479 100644 --- a/src/solver.rs +++ b/src/solver.rs @@ -97,11 +97,8 @@ pub fn resolve( let Some(highest_priority_pkg) = state.partial_solution.pick_highest_priority_pkg(|p, r| { - dependency_provider.prioritize( - &state.package_store[p], - r, - state.conflict_count.get(&p).cloned().unwrap_or_default(), - ) + let statis = PackageResolutionStatistics::new(p, &state.conflict_count); + dependency_provider.prioritize(&state.package_store[p], r, &statis) }) else { return Ok(state @@ -203,6 +200,46 @@ pub enum Dependencies), } +/// Some statistics about how much trouble the resolver has had with a package. +pub struct PackageResolutionStatistics { + discovery_order: u32, + conflict_count: u32, +} + +impl PackageResolutionStatistics { + fn new(pid: Id

, conflict_count: &Map, u32>) -> Self { + Self { + discovery_order: pid.into_raw() as u32, + conflict_count: conflict_count.get(&pid).cloned().unwrap_or_default(), + } + } + + /// The number of packages resolution new about the first time this package was mentioned. + /// + /// The root package will return `0`. It's direct dependencies will start at `1` and go up from there. + /// Prioritizing based on this value directly will lead to a depth first search of the resolution graph. + /// Prioritizing based on the reverse of this value will lead to a breadth first search of the resolution graph. + /// + /// Note: The exact values depend on implementation details of PubGrub and its dependencies. + /// So should not be relied on and may change between any lock file update. + pub fn discovery_order(&self) -> u32 { + self.discovery_order + } + + /// The number of times this package was involved in a conflict that caused a back jump. + /// + /// When resolution is proceeding normally, this value will stay at `0` for all packages. + /// Therefore, using this for prioritization will not affect the properties of simple cases + /// like checking a lock file. + /// Prioritizing based on this value directly allows the resolver to focus on the packages + /// it is having the most problems with. + /// + /// Note: The exact values depend on implementation details of PubGrub. So should not be relied on and may change. + pub fn conflict_count(&self) -> u32 { + self.conflict_count + } +} + /// Trait that allows the algorithm to retrieve available packages and their dependencies. /// An implementor needs to be supplied to the [resolve] function. pub trait DependencyProvider { @@ -238,8 +275,8 @@ pub trait DependencyProvider { /// /// Every time such a decision must be made, the resolver looks at all the potential valid /// packages that have changed, and a asks the dependency provider how important each one is. - /// For each one it calls `prioritize` with the name of the package and the current set of - /// acceptable versions. + /// For each one it calls `prioritize` with the name of the package, the current set of + /// acceptable versions, and some statistics about how much trouble the resolver has had with that package. /// The resolver will then pick the package with the highes priority from all the potential valid /// packages. /// @@ -262,7 +299,7 @@ pub trait DependencyProvider { &self, package: &Self::P, range: &Self::VS, - conflict_count: u32, + statis: &PackageResolutionStatistics, ) -> Self::Priority; /// The type returned from `prioritize`. The resolver does not care what type this is /// as long as it can pick a largest one and clone it. diff --git a/tests/proptest.rs b/tests/proptest.rs index 57fa3956..c8357d1e 100644 --- a/tests/proptest.rs +++ b/tests/proptest.rs @@ -13,8 +13,8 @@ use proptest::string::string_regex; use pubgrub::{ resolve, DefaultStringReporter, Dependencies, DependencyProvider, DerivationTree, External, - OfflineDependencyProvider, Package, PubGrubError, Ranges, Reporter, SelectedDependencies, - VersionSet, + OfflineDependencyProvider, Package, PackageResolutionStatistics, PubGrubError, Ranges, + Reporter, SelectedDependencies, VersionSet, }; use crate::sat_dependency_provider::SatResolve; @@ -49,8 +49,13 @@ impl DependencyProvider for OldestVersionsDependency type Priority = as DependencyProvider>::Priority; - fn prioritize(&self, package: &P, range: &VS, conflict_count: u32) -> Self::Priority { - self.0.prioritize(package, range, conflict_count) + fn prioritize( + &self, + package: &P, + range: &VS, + statis: &PackageResolutionStatistics, + ) -> Self::Priority { + self.0.prioritize(package, range, statis) } type Err = Infallible; @@ -104,8 +109,13 @@ impl DependencyProvider for TimeoutDependencyProvider Self::Priority { - self.dp.prioritize(package, range, conflict_count) + fn prioritize( + &self, + package: &DP::P, + range: &DP::VS, + statis: &PackageResolutionStatistics, + ) -> Self::Priority { + self.dp.prioritize(package, range, statis) } type Err = DP::Err;