-
Notifications
You must be signed in to change notification settings - Fork 3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add an upgrade section #6
Open
mpizenberg
wants to merge
5
commits into
v03
Choose a base branch
from
upgrade
base: v03
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
f2acc5a
Add an upgrade section
mpizenberg 5b8a937
Fix typo
mpizenberg b98a246
Rework intro of v02_v03.md
mpizenberg f9116ba
Changes in upgrade/boundedrange section
mpizenberg c67bd01
Changes in upgrade/dependencyprovider section
mpizenberg File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
# Upgrading from a previous version | ||
|
||
Dealing with dependency management, we know that dependency upgrades are painful, especially with breaking changes. | ||
We aim to provide meaningful breaking changes, and to help as much as possible people who want to upgrade their usage of PubGrub. | ||
So we will try to provide a dedicated section for breaking changes with upgrades explanations and instructions. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
# Upgrading from v0.2 to v0.3 | ||
|
||
If you encounter any issue upgrading, don't hesitate to reach out on our [Zulip stream](https://rust-lang.zulipchat.com/#narrow/stream/260232-t-cargo.2FPubGrub). | ||
|
||
Version 0.2 was the first publicly advertised version of our pubgrub crate so it was developped as a minimum viable solution with sound foundations for people to start building on it. | ||
For the sake of simplicity, it was however overly constraining the types for versions and version sets, making it impossible to correctly represent complex version types such as semantic versions with pre-release tags. | ||
The goal with version 0.3 is to give more flexibility to implementors of dependency providers, by defining a `VersionSet` trait with minimum requirements to be compatible with the PubGrub version solving algorithm. | ||
Our previous `Range` type is now one possible implementation of that trait when more constraints are imposed, but the new `VersionSet` trait enables more flexible implementations. | ||
For comparison, let remind us how the v0.2 `Version` trait was defined: | ||
|
||
```rust | ||
/// Versions in pubgrub v0.2 have a minimal version (a "0" version) | ||
/// and are ordered such that every version has a next one. | ||
pub trait Version: Clone + Ord + Debug + Display { | ||
/// Returns the lowest version. | ||
fn lowest() -> Self; | ||
/// Returns the next version, the smallest strictly higher version. | ||
fn bump(&self) -> Self; | ||
} | ||
``` | ||
|
||
In v0.3, the new trait `VersionSet` is similar to v0.2 `Range` type. | ||
It now has an associated `V` type for the version type, which still requires `Ord`, but we got rid of the `lowest()` and `bump()` requirements! | ||
In the code and documentation, we often alias `VersionSet` to `VS` so if you see `VS::V`, it refers to the associated version type. | ||
|
||
```rust | ||
/// Trait describing sets of versions. | ||
pub trait VersionSet: Debug + Display + Clone + Eq { | ||
/// Version type associated with the sets manipulated. | ||
type V: Debug + Display + Clone + Ord; | ||
|
||
// Constructors | ||
/// Constructor for an empty set containing no version. | ||
fn empty() -> Self; | ||
/// Constructor for a set containing exactly one version. | ||
fn singleton(v: Self::V) -> Self; | ||
|
||
// Operations | ||
/// Compute the complement of this set. | ||
fn complement(&self) -> Self; | ||
/// Compute the intersection with another set. | ||
fn intersection(&self, other: &Self) -> Self; | ||
|
||
// Membership | ||
/// Evaluate membership of a version in this set. | ||
fn contains(&self, v: &Self::V) -> bool; | ||
|
||
// Automatically implemented functions ########################### | ||
|
||
/// Constructor for the set containing all versions. | ||
fn full() -> Self { ... } | ||
|
||
/// Compute the union with another set. | ||
fn union(&self, other: &Self) -> Self { ... } | ||
} | ||
``` | ||
|
||
This combined with the implementation freedom to handle sets how you want, instead of relying on our previous `Range` type gives a lot of possibilities. | ||
But with great power comes great responsability, meaning users have the responsability to implement their set operations correctly! | ||
|
||
## The new BoundedRange to replace the old Range | ||
|
||
Previously, the `Range<V: Version>` type was our implementation handling sets of version with efficient set operations for computing complements, intersections, and evaluating membership of versions. | ||
One of the main limitations of that type was the constraint that versions live in a discrete space, where one need to know exactly which version comes after any given version. | ||
This makes handling of pre-release tags not ergonomic since the `bump()` function had to produce the next possible pre-release. | ||
In cases where any character is allowed in tags, it could consist in adding the `\x00` character to the utf8 string, but as you can see that's not convenient and it does not feel right. | ||
|
||
The new `VersionSet` trait helps solve this particular issue, but also makes the pubgrub API more complex. | ||
However, there are still users who do not need the added complexity, such as for Elm versions. | ||
We care about complexity, so we also now provide the `BoundedRange<V: Ord>` type, which is our new default implementation of the `VersionSet` trait. | ||
At its heart, `BoundedRange` now stores an ordered collection of bounded intervals `( Bound<V>, Bound<V> )`. | ||
Our performance evaluation of this change indicates that the performance cost is negligeable when the version type is complex enough, like semantic versions, and is quite small (less than 10%) when the version type is minimalist like `u32`. | ||
This change brings two main advantages compared to previously: | ||
|
||
1. There is no need anymore to provide a smallest version, or a bump function for your version type. | ||
2. It is much more ergonomic to build pairs of inclusive bounds, or pairs of exclusive bounds. | ||
|
||
To illustrate (2), defining the `[v1, v2]` range was previously done with the need of a `bump()` call, since `[v1, v2]` is theoretically equivalent to `[v1, v2.bump()[` (right bound is excluded) under the v0.2 `Version` trait constraints. | ||
|
||
```rust | ||
Range::between(v1, v2.bump()) | ||
``` | ||
|
||
In v0.3, you can now leverage the official [`core::ops::RangeBounds`](https://doc.rust-lang.org/core/ops/trait.RangeBounds.html) trait. | ||
|
||
```rust | ||
BoundedRange::from_range_bounds(v1..=v2) | ||
``` | ||
|
||
To update the usage of the `Range` type in your code should be quite straightforward. | ||
There has been few naming changes for consistency, such as `Range::none()` becoming `BoundedRange::empty()` but every constructor previously available is still available now. | ||
Of course, if for any reason the provided `BoundedRange` type does not fit your constraints for handling sets of versions, you can provide your own implementation of the `VersionSet` trait thanks to the new v0.3 API. | ||
If you are especially interested in v0.3 for dealing with pre-releases, we strongly suggest you also read the [dedicated section of the guide for pre-releases](/limitations/prerelease_versions.md). | ||
|
||
## Changes in the DependencyProvider to choose package versions | ||
|
||
One fundamental piece of flexibility of the pubgrub crate is that it delegates all decision making to the caller in the `DependencyProvider` trait. | ||
|
||
```rust | ||
/// v0.2 dependency provider trait | ||
pub trait DependencyProvider<P: Package, V: Version> { | ||
/// Given candidates, choose the next package version attempt | ||
/// to continue building the solution. | ||
fn choose_package_version(&self, potential_packages) -> // (package, version) | ||
|
||
/// Retrieve the dependencies of the given package version. | ||
fn get_dependencies(&self, package, version) -> // dependencies | ||
} | ||
``` | ||
|
||
Previously, when choosing the next package to be added to the partial solution being built, the caller would be given a list of candidates, and it has the responsability to pick one package and version satisfying the provided candidates. | ||
That list of candidates typically grows everytime we encounter a new package and add its dependencies to candidates for the next choices. | ||
So depending on the resolution path chosen, it is possible to build a list of candidates that grows fast. | ||
Then, every round, the caller has to work again on the whole list of candidates to pick one package version. | ||
There is a smarter way to tackle this problem, consisting in puting candidates in a priority queue. | ||
Everytime the solver encounters a new candidate, it will ask the caller to rate the priority to give for the choice of that candidate. | ||
Then everytime there is a choice to be made, the algorithm picks the first candidate in the priority list, and simply asks the caller to pick one version in that version set. | ||
|
||
```rust | ||
/// v0.3 dependency provider trait | ||
pub trait DependencyProvider<P: Package, V: Version> { | ||
/// Compute the priority of a given package and range of versions for the solver. | ||
/// When deciding which package to pick next, the solver will choose the highest priority one. | ||
fn prioritize(&self, package, version_set) -> // priority | ||
|
||
/// Given a package and version constraints, pick the next version to choose. | ||
/// Can be the newest, oldest, or any thing you want depending on context. | ||
fn choose_version(&self, package, version_set) -> // version | ||
|
||
/// Retrieve the dependencies of the given package version. | ||
fn get_dependencies(&self, package, version) -> // dependencies | ||
} | ||
``` | ||
|
||
This new API is slightly more constraining than the old one, but it can give a significant performance boost. | ||
It's indeed turning a problem with complexity `N^2` (evaluating all potential packages at every choice) into one with complexity `N * log(N)` (inserting in a prioritized sorted list). | ||
If you have a situation where this API change prevents you from using pubgrub, please let us know! |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It could help to include an example of the types that might be commonly used for
Priority
.