Skip to content

Commit 8255e83

Browse files
committed
futureproof with a PackageResolutionStatistics
1 parent 446fadf commit 8255e83

File tree

5 files changed

+84
-21
lines changed

5 files changed

+84
-21
lines changed

examples/caching_dependency_provider.rs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@
22

33
use std::cell::RefCell;
44

5-
use pubgrub::{resolve, Dependencies, DependencyProvider, OfflineDependencyProvider, Ranges};
5+
use pubgrub::{
6+
resolve, Dependencies, DependencyProvider, OfflineDependencyProvider,
7+
PackageResolutionStatistics, Ranges,
8+
};
69

710
type NumVS = Ranges<u32>;
811

@@ -57,8 +60,13 @@ impl<DP: DependencyProvider<M = String>> DependencyProvider for CachingDependenc
5760

5861
type Priority = DP::Priority;
5962

60-
fn prioritize(&self, package: &DP::P, ranges: &DP::VS, conflict_count: u32) -> Self::Priority {
61-
self.remote_dependencies.prioritize(package, ranges, conflict_count)
63+
fn prioritize(
64+
&self,
65+
package: &DP::P,
66+
ranges: &DP::VS,
67+
statis: &PackageResolutionStatistics,
68+
) -> Self::Priority {
69+
self.remote_dependencies.prioritize(package, ranges, statis)
6270
}
6371

6472
type Err = DP::Err;

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,7 @@ pub use report::{
227227
DefaultStringReportFormatter, DefaultStringReporter, DerivationTree, Derived, External,
228228
ReportFormatter, Reporter,
229229
};
230-
pub use solver::{resolve, Dependencies, DependencyProvider};
230+
pub use solver::{resolve, Dependencies, DependencyProvider, PackageResolutionStatistics};
231231
pub use term::Term;
232232
pub use type_aliases::{DependencyConstraints, Map, SelectedDependencies, Set};
233233
pub use version::{SemanticVersion, VersionParseError};

src/provider.rs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@ use std::cmp::Reverse;
22
use std::collections::BTreeMap;
33
use std::convert::Infallible;
44

5-
use crate::{Dependencies, DependencyConstraints, DependencyProvider, Map, Package, VersionSet};
5+
use crate::{
6+
Dependencies, DependencyConstraints, DependencyProvider, Map, Package,
7+
PackageResolutionStatistics, VersionSet,
8+
};
69

710
/// A basic implementation of [DependencyProvider].
811
#[derive(Debug, Clone, Default)]
@@ -95,7 +98,12 @@ impl<P: Package, VS: VersionSet> DependencyProvider for OfflineDependencyProvide
9598
type Priority = (u32, Reverse<u32>);
9699

97100
#[inline]
98-
fn prioritize(&self, package: &P, range: &VS, conflict_count: u32) -> Self::Priority {
101+
fn prioritize(
102+
&self,
103+
package: &P,
104+
range: &VS,
105+
statis: &PackageResolutionStatistics,
106+
) -> Self::Priority {
99107
let version_count = self
100108
.dependencies
101109
.get(package)
@@ -104,7 +112,7 @@ impl<P: Package, VS: VersionSet> DependencyProvider for OfflineDependencyProvide
104112
if version_count == 0 {
105113
return (u32::MAX, Reverse(0));
106114
}
107-
(conflict_count, Reverse(version_count as u32))
115+
(statis.conflict_count(), Reverse(version_count as u32))
108116
}
109117

110118
#[inline]

src/solver.rs

Lines changed: 45 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -97,11 +97,8 @@ pub fn resolve<DP: DependencyProvider>(
9797

9898
let Some(highest_priority_pkg) =
9999
state.partial_solution.pick_highest_priority_pkg(|p, r| {
100-
dependency_provider.prioritize(
101-
&state.package_store[p],
102-
r,
103-
state.conflict_count.get(&p).cloned().unwrap_or_default(),
104-
)
100+
let statis = PackageResolutionStatistics::new(p, &state.conflict_count);
101+
dependency_provider.prioritize(&state.package_store[p], r, &statis)
105102
})
106103
else {
107104
return Ok(state
@@ -203,6 +200,46 @@ pub enum Dependencies<P: Package, VS: VersionSet, M: Eq + Clone + Debug + Displa
203200
Available(DependencyConstraints<P, VS>),
204201
}
205202

203+
/// Some statistics about how much trouble the resolver has had with a package.
204+
pub struct PackageResolutionStatistics {
205+
discovery_order: u32,
206+
conflict_count: u32,
207+
}
208+
209+
impl PackageResolutionStatistics {
210+
fn new<P: Package>(pid: Id<P>, conflict_count: &Map<Id<P>, u32>) -> Self {
211+
Self {
212+
discovery_order: pid.into_raw() as u32,
213+
conflict_count: conflict_count.get(&pid).cloned().unwrap_or_default(),
214+
}
215+
}
216+
217+
/// The number of packages resolution new about the first time this package was mentioned.
218+
///
219+
/// The root package will return `0`. It's direct dependencies will start at `1` and go up from there.
220+
/// Prioritizing based on this value directly will lead to a depth first search of the resolution graph.
221+
/// Prioritizing based on the reverse of this value will lead to a breadth first search of the resolution graph.
222+
///
223+
/// Note: The exact values depend on implementation details of PubGrub and its dependencies.
224+
/// So should not be relied on and may change between any lock file update.
225+
pub fn discovery_order(&self) -> u32 {
226+
self.discovery_order
227+
}
228+
229+
/// The number of times this package was involved in a conflict that caused a back jump.
230+
///
231+
/// When resolution is proceeding normally, this value will stay at `0` for all packages.
232+
/// Therefore, using this for prioritization will not affect the properties of simple cases
233+
/// like checking a lock file.
234+
/// Prioritizing based on this value directly allows the resolver to focus on the packages
235+
/// it is having the most problems with.
236+
///
237+
/// Note: The exact values depend on implementation details of PubGrub. So should not be relied on and may change.
238+
pub fn conflict_count(&self) -> u32 {
239+
self.conflict_count
240+
}
241+
}
242+
206243
/// Trait that allows the algorithm to retrieve available packages and their dependencies.
207244
/// An implementor needs to be supplied to the [resolve] function.
208245
pub trait DependencyProvider {
@@ -238,8 +275,8 @@ pub trait DependencyProvider {
238275
///
239276
/// Every time such a decision must be made, the resolver looks at all the potential valid
240277
/// packages that have changed, and a asks the dependency provider how important each one is.
241-
/// For each one it calls `prioritize` with the name of the package and the current set of
242-
/// acceptable versions.
278+
/// For each one it calls `prioritize` with the name of the package, the current set of
279+
/// acceptable versions, and some statistics about how much trouble the resolver has had with that package.
243280
/// The resolver will then pick the package with the highes priority from all the potential valid
244281
/// packages.
245282
///
@@ -262,7 +299,7 @@ pub trait DependencyProvider {
262299
&self,
263300
package: &Self::P,
264301
range: &Self::VS,
265-
conflict_count: u32,
302+
statis: &PackageResolutionStatistics,
266303
) -> Self::Priority;
267304
/// The type returned from `prioritize`. The resolver does not care what type this is
268305
/// as long as it can pick a largest one and clone it.

tests/proptest.rs

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ use proptest::string::string_regex;
1313

1414
use pubgrub::{
1515
resolve, DefaultStringReporter, Dependencies, DependencyProvider, DerivationTree, External,
16-
OfflineDependencyProvider, Package, PubGrubError, Ranges, Reporter, SelectedDependencies,
17-
VersionSet,
16+
OfflineDependencyProvider, Package, PackageResolutionStatistics, PubGrubError, Ranges,
17+
Reporter, SelectedDependencies, VersionSet,
1818
};
1919

2020
use crate::sat_dependency_provider::SatResolve;
@@ -49,8 +49,13 @@ impl<P: Package, VS: VersionSet> DependencyProvider for OldestVersionsDependency
4949

5050
type Priority = <OfflineDependencyProvider<P, VS> as DependencyProvider>::Priority;
5151

52-
fn prioritize(&self, package: &P, range: &VS, conflict_count: u32) -> Self::Priority {
53-
self.0.prioritize(package, range, conflict_count)
52+
fn prioritize(
53+
&self,
54+
package: &P,
55+
range: &VS,
56+
statis: &PackageResolutionStatistics,
57+
) -> Self::Priority {
58+
self.0.prioritize(package, range, statis)
5459
}
5560

5661
type Err = Infallible;
@@ -104,8 +109,13 @@ impl<DP: DependencyProvider> DependencyProvider for TimeoutDependencyProvider<DP
104109

105110
type Priority = DP::Priority;
106111

107-
fn prioritize(&self, package: &DP::P, range: &DP::VS, conflict_count: u32) -> Self::Priority {
108-
self.dp.prioritize(package, range, conflict_count)
112+
fn prioritize(
113+
&self,
114+
package: &DP::P,
115+
range: &DP::VS,
116+
statis: &PackageResolutionStatistics,
117+
) -> Self::Priority {
118+
self.dp.prioritize(package, range, statis)
109119
}
110120

111121
type Err = DP::Err;

0 commit comments

Comments
 (0)