mirror of
https://github.com/rust-lang/cargo.git
synced 2025-10-01 11:30:39 +00:00
Auto merge of #5200 - klausi:minimal_versions, r=Eh2406
feat(resolver): Add CLI option to resolve minimal version dependencies Fixes #4100 Test cases are still missing. We need to come up with a plan what cases we want to cover. Thanks a lot to @Eh2406 for very helpful instructions to kick this off.
This commit is contained in:
commit
bcd0300f0e
@ -285,6 +285,7 @@ pub struct CliUnstable {
|
||||
pub offline: bool,
|
||||
pub no_index_update: bool,
|
||||
pub avoid_dev_deps: bool,
|
||||
pub minimal_versions: bool,
|
||||
}
|
||||
|
||||
impl CliUnstable {
|
||||
@ -317,6 +318,7 @@ impl CliUnstable {
|
||||
"offline" => self.offline = true,
|
||||
"no-index-update" => self.no_index_update = true,
|
||||
"avoid-dev-deps" => self.avoid_dev_deps = true,
|
||||
"minimal-versions" => self.minimal_versions = true,
|
||||
_ => bail!("unknown `-Z` flag specified: {}", k),
|
||||
}
|
||||
|
||||
|
@ -430,7 +430,11 @@ pub fn resolve(
|
||||
warnings: RcList::new(),
|
||||
};
|
||||
let _p = profile::start("resolving");
|
||||
let mut registry = RegistryQueryer::new(registry, replacements, try_to_use);
|
||||
let minimal_versions = match config {
|
||||
Some(config) => config.cli_unstable().minimal_versions,
|
||||
None => false,
|
||||
};
|
||||
let mut registry = RegistryQueryer::new(registry, replacements, try_to_use, minimal_versions);
|
||||
let cx = activate_deps_loop(cx, &mut registry, summaries, config)?;
|
||||
|
||||
let mut resolve = Resolve {
|
||||
@ -683,6 +687,10 @@ struct RegistryQueryer<'a> {
|
||||
try_to_use: &'a HashSet<&'a PackageId>,
|
||||
// TODO: with nll the Rc can be removed
|
||||
cache: HashMap<Dependency, Rc<Vec<Candidate>>>,
|
||||
// If set the list of dependency candidates will be sorted by minimal
|
||||
// versions first. That allows `cargo update -Z minimal-versions` which will
|
||||
// specify minimum depedency versions to be used.
|
||||
minimal_versions: bool,
|
||||
}
|
||||
|
||||
impl<'a> RegistryQueryer<'a> {
|
||||
@ -690,12 +698,14 @@ impl<'a> RegistryQueryer<'a> {
|
||||
registry: &'a mut Registry,
|
||||
replacements: &'a [(PackageIdSpec, Dependency)],
|
||||
try_to_use: &'a HashSet<&'a PackageId>,
|
||||
minimal_versions: bool,
|
||||
) -> Self {
|
||||
RegistryQueryer {
|
||||
registry,
|
||||
replacements,
|
||||
cache: HashMap::new(),
|
||||
try_to_use,
|
||||
minimal_versions,
|
||||
}
|
||||
}
|
||||
|
||||
@ -795,9 +805,20 @@ impl<'a> RegistryQueryer<'a> {
|
||||
ret.sort_unstable_by(|a, b| {
|
||||
let a_in_previous = self.try_to_use.contains(a.summary.package_id());
|
||||
let b_in_previous = self.try_to_use.contains(b.summary.package_id());
|
||||
let a = (a_in_previous, a.summary.version());
|
||||
let b = (b_in_previous, b.summary.version());
|
||||
a.cmp(&b).reverse()
|
||||
let previous_cmp = a_in_previous.cmp(&b_in_previous).reverse();
|
||||
match previous_cmp {
|
||||
Ordering::Equal => {
|
||||
let cmp = a.summary.version().cmp(&b.summary.version());
|
||||
if self.minimal_versions == true {
|
||||
// Lower version ordered first.
|
||||
cmp
|
||||
} else {
|
||||
// Higher version ordered first.
|
||||
cmp.reverse()
|
||||
}
|
||||
}
|
||||
_ => previous_cmp,
|
||||
}
|
||||
});
|
||||
|
||||
let out = Rc::new(ret);
|
||||
|
@ -5,13 +5,26 @@ use hamcrest::{assert_that, contains, is_not};
|
||||
use cargo::core::source::{GitReference, SourceId};
|
||||
use cargo::core::dependency::Kind::{self, Development};
|
||||
use cargo::core::{Dependency, PackageId, Registry, Summary};
|
||||
use cargo::util::{CargoResult, ToUrl};
|
||||
use cargo::util::{CargoResult, Config, ToUrl};
|
||||
use cargo::core::resolver::{self, Method};
|
||||
|
||||
use cargotest::ChannelChanger;
|
||||
use cargotest::support::{execs, project};
|
||||
use cargotest::support::registry::Package;
|
||||
|
||||
fn resolve(
|
||||
pkg: &PackageId,
|
||||
deps: Vec<Dependency>,
|
||||
registry: &[Summary],
|
||||
) -> CargoResult<Vec<PackageId>> {
|
||||
resolve_with_config(pkg, deps, registry, None)
|
||||
}
|
||||
|
||||
fn resolve_with_config(
|
||||
pkg: &PackageId,
|
||||
deps: Vec<Dependency>,
|
||||
registry: &[Summary],
|
||||
config: Option<&Config>,
|
||||
) -> CargoResult<Vec<PackageId>> {
|
||||
struct MyRegistry<'a>(&'a [Summary]);
|
||||
impl<'a> Registry for MyRegistry<'a> {
|
||||
@ -38,7 +51,7 @@ fn resolve(
|
||||
&[],
|
||||
&mut registry,
|
||||
&HashSet::new(),
|
||||
None,
|
||||
config,
|
||||
false,
|
||||
)?;
|
||||
let res = resolve.iter().cloned().collect();
|
||||
@ -327,6 +340,87 @@ fn test_resolving_maximum_version_with_transitive_deps() {
|
||||
assert_that(&res, is_not(contains(names(&[("util", "1.1.1")]))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_resolving_minimum_version_with_transitive_deps() {
|
||||
// When the minimal-versions config option is specified then the lowest
|
||||
// possible version of a package should be selected. "util 1.0.0" can't be
|
||||
// selected because of the requirements of "bar", so the minimum version
|
||||
// must be 1.1.1.
|
||||
let reg = registry(vec![
|
||||
pkg!(("util", "1.2.2")),
|
||||
pkg!(("util", "1.0.0")),
|
||||
pkg!(("util", "1.1.1")),
|
||||
pkg!("foo" => [dep_req("util", "1.0.0")]),
|
||||
pkg!("bar" => [dep_req("util", ">=1.0.1")]),
|
||||
]);
|
||||
|
||||
let mut config = Config::default().unwrap();
|
||||
config
|
||||
.configure(
|
||||
1,
|
||||
None,
|
||||
&None,
|
||||
false,
|
||||
false,
|
||||
&["minimal-versions".to_string()],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let res = resolve_with_config(
|
||||
&pkg_id("root"),
|
||||
vec![dep_req("foo", "1.0.0"), dep_req("bar", "1.0.0")],
|
||||
®,
|
||||
Some(&config),
|
||||
).unwrap();
|
||||
|
||||
assert_that(
|
||||
&res,
|
||||
contains(names(&[
|
||||
("root", "1.0.0"),
|
||||
("foo", "1.0.0"),
|
||||
("bar", "1.0.0"),
|
||||
("util", "1.1.1"),
|
||||
])),
|
||||
);
|
||||
assert_that(&res, is_not(contains(names(&[("util", "1.2.2")]))));
|
||||
assert_that(&res, is_not(contains(names(&[("util", "1.0.0")]))));
|
||||
}
|
||||
|
||||
// Ensure that the "-Z minimal-versions" CLI option works and the minimal
|
||||
// version of a dependency ends up in the lock file.
|
||||
#[test]
|
||||
fn minimal_version_cli() {
|
||||
Package::new("dep", "1.0.0").publish();
|
||||
Package::new("dep", "1.1.0").publish();
|
||||
|
||||
let p = project("foo")
|
||||
.file(
|
||||
"Cargo.toml",
|
||||
r#"
|
||||
[package]
|
||||
name = "foo"
|
||||
authors = []
|
||||
version = "0.0.1"
|
||||
|
||||
[dependencies]
|
||||
dep = "1.0"
|
||||
"#,
|
||||
)
|
||||
.file("src/main.rs", "fn main() {}")
|
||||
.build();
|
||||
|
||||
assert_that(
|
||||
p.cargo("generate-lockfile")
|
||||
.masquerade_as_nightly_cargo()
|
||||
.arg("-Zminimal-versions"),
|
||||
execs().with_status(0),
|
||||
);
|
||||
|
||||
let lock = p.read_lockfile();
|
||||
|
||||
assert!(lock.contains("dep 1.0.0"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn resolving_incompat_versions() {
|
||||
let reg = registry(vec![
|
||||
|
Loading…
x
Reference in New Issue
Block a user