Skip to content

Commit f8ba1cb

Browse files
committed
Auto merge of #9639 - djmitche:issue9535, r=Eh2406
Prefer patched versions of dependencies When selecting among several versions of a paackage, prefer versions from `[patch]` sections over other versions, similar to how locked versions are preferred. Patches come in the form of a Dependency and not a PackageId, so this preference is expressed with `prefer_patch_deps`, distinct from `try_to_use`. Fixes #9535
2 parents 98d5d10 + bd4a353 commit f8ba1cb

File tree

5 files changed

+98
-11
lines changed

5 files changed

+98
-11
lines changed

crates/resolver-tests/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@ pub fn resolve_with_config_raw(
184184
&[],
185185
&mut registry,
186186
&HashSet::new(),
187+
&HashMap::new(),
187188
Some(config),
188189
true,
189190
);

src/cargo/core/resolver/dep_cache.rs

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ pub struct RegistryQueryer<'a> {
2929
pub registry: &'a mut (dyn Registry + 'a),
3030
replacements: &'a [(PackageIdSpec, Dependency)],
3131
try_to_use: &'a HashSet<PackageId>,
32+
prefer_patch_deps: &'a HashMap<InternedString, HashSet<Dependency>>,
3233
/// If set the list of dependency candidates will be sorted by minimal
3334
/// versions first. That allows `cargo update -Z minimal-versions` which will
3435
/// specify minimum dependency versions to be used.
@@ -49,12 +50,14 @@ impl<'a> RegistryQueryer<'a> {
4950
registry: &'a mut dyn Registry,
5051
replacements: &'a [(PackageIdSpec, Dependency)],
5152
try_to_use: &'a HashSet<PackageId>,
53+
prefer_patch_deps: &'a HashMap<InternedString, HashSet<Dependency>>,
5254
minimal_versions: bool,
5355
) -> Self {
5456
RegistryQueryer {
5557
registry,
5658
replacements,
5759
try_to_use,
60+
prefer_patch_deps,
5861
minimal_versions,
5962
registry_cache: HashMap::new(),
6063
summary_cache: HashMap::new(),
@@ -164,14 +167,22 @@ impl<'a> RegistryQueryer<'a> {
164167
}
165168
}
166169

167-
// When we attempt versions for a package we'll want to do so in a
168-
// sorted fashion to pick the "best candidates" first. Currently we try
169-
// prioritized summaries (those in `try_to_use`) and failing that we
170-
// list everything from the maximum version to the lowest version.
170+
// When we attempt versions for a package we'll want to do so in a sorted fashion to pick
171+
// the "best candidates" first. Currently we try prioritized summaries (those in
172+
// `try_to_use` or `prefer_patch_deps`) and failing that we list everything from the
173+
// maximum version to the lowest version.
174+
let should_prefer = |package_id: &PackageId| {
175+
self.try_to_use.contains(package_id)
176+
|| self
177+
.prefer_patch_deps
178+
.get(&package_id.name())
179+
.map(|deps| deps.iter().any(|d| d.matches_id(*package_id)))
180+
.unwrap_or(false)
181+
};
171182
ret.sort_unstable_by(|a, b| {
172-
let a_in_previous = self.try_to_use.contains(&a.package_id());
173-
let b_in_previous = self.try_to_use.contains(&b.package_id());
174-
let previous_cmp = a_in_previous.cmp(&b_in_previous).reverse();
183+
let prefer_a = should_prefer(&a.package_id());
184+
let prefer_b = should_prefer(&b.package_id());
185+
let previous_cmp = prefer_a.cmp(&prefer_b).reverse();
175186
match previous_cmp {
176187
Ordering::Equal => {
177188
let cmp = a.version().cmp(b.version());

src/cargo/core/resolver/mod.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ use crate::core::PackageIdSpec;
5858
use crate::core::{Dependency, PackageId, Registry, Summary};
5959
use crate::util::config::Config;
6060
use crate::util::errors::CargoResult;
61+
use crate::util::interning::InternedString;
6162
use crate::util::profile;
6263

6364
use self::context::Context;
@@ -106,6 +107,10 @@ mod types;
106107
/// when sorting candidates to activate, but otherwise this isn't used
107108
/// anywhere else.
108109
///
110+
/// * `prefer_patch_deps` - this is a collection of dependencies on `[patch]`es, which
111+
/// should be preferred much like `try_to_use`, but are in the form of Dependencies
112+
/// and not PackageIds.
113+
///
109114
/// * `config` - a location to print warnings and such, or `None` if no warnings
110115
/// should be printed
111116
///
@@ -124,6 +129,7 @@ pub fn resolve(
124129
replacements: &[(PackageIdSpec, Dependency)],
125130
registry: &mut dyn Registry,
126131
try_to_use: &HashSet<PackageId>,
132+
prefer_patch_deps: &HashMap<InternedString, HashSet<Dependency>>,
127133
config: Option<&Config>,
128134
check_public_visible_dependencies: bool,
129135
) -> CargoResult<Resolve> {
@@ -133,7 +139,13 @@ pub fn resolve(
133139
Some(config) => config.cli_unstable().minimal_versions,
134140
None => false,
135141
};
136-
let mut registry = RegistryQueryer::new(registry, replacements, try_to_use, minimal_versions);
142+
let mut registry = RegistryQueryer::new(
143+
registry,
144+
replacements,
145+
try_to_use,
146+
prefer_patch_deps,
147+
minimal_versions,
148+
);
137149
let cx = activate_deps_loop(cx, &mut registry, summaries, config)?;
138150

139151
let mut cksums = HashMap::new();

src/cargo/ops/resolve.rs

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ use crate::util::errors::CargoResult;
2727
use crate::util::{profile, CanonicalUrl};
2828
use anyhow::Context as _;
2929
use log::{debug, trace};
30-
use std::collections::HashSet;
30+
use std::collections::{HashMap, HashSet};
3131

3232
/// Result for `resolve_ws_with_opts`.
3333
pub struct WorkspaceResolve<'cfg> {
@@ -242,11 +242,23 @@ pub fn resolve_with_previous<'cfg>(
242242
}
243243
};
244244

245-
// This is a set of PackageIds of `[patch]` entries that should not be
246-
// locked.
245+
// This is a set of PackageIds of `[patch]` entries, and some related locked PackageIds, for
246+
// which locking should be avoided (but which will be preferred when searching dependencies,
247+
// via prefer_patch_deps below)
247248
let mut avoid_patch_ids = HashSet::new();
249+
250+
// This is a set of Dependency's of `[patch]` entries that should be preferred when searching
251+
// dependencies.
252+
let mut prefer_patch_deps = HashMap::new();
253+
248254
if register_patches {
249255
for (url, patches) in ws.root_patch()?.iter() {
256+
for patch in patches {
257+
prefer_patch_deps
258+
.entry(patch.package_name())
259+
.or_insert_with(HashSet::new)
260+
.insert(patch.clone());
261+
}
250262
let previous = match previous {
251263
Some(r) => r,
252264
None => {
@@ -353,6 +365,7 @@ pub fn resolve_with_previous<'cfg>(
353365
}
354366
}
355367
debug!("avoid_patch_ids={:?}", avoid_patch_ids);
368+
debug!("prefer_patch_deps={:?}", prefer_patch_deps);
356369

357370
let keep = |p: &PackageId| pre_patch_keep(p) && !avoid_patch_ids.contains(p);
358371

@@ -426,6 +439,7 @@ pub fn resolve_with_previous<'cfg>(
426439
&replace,
427440
registry,
428441
&try_to_use,
442+
&prefer_patch_deps,
429443
Some(ws.config()),
430444
ws.unstable_features()
431445
.require(Feature::public_dependency())

tests/testsuite/patch.rs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -441,6 +441,55 @@ fn unused() {
441441
);
442442
}
443443

444+
#[cargo_test]
445+
fn prefer_patch_version() {
446+
Package::new("bar", "0.1.2").publish();
447+
448+
let p = project()
449+
.file(
450+
"Cargo.toml",
451+
r#"
452+
[package]
453+
name = "foo"
454+
version = "0.0.1"
455+
authors = []
456+
457+
[dependencies]
458+
bar = "0.1.0"
459+
460+
[patch.crates-io]
461+
bar = { path = "bar" }
462+
"#,
463+
)
464+
.file("src/lib.rs", "")
465+
.file("bar/Cargo.toml", &basic_manifest("bar", "0.1.1"))
466+
.file("bar/src/lib.rs", "")
467+
.build();
468+
469+
p.cargo("build")
470+
.with_stderr(
471+
"\
472+
[UPDATING] `[ROOT][..]` index
473+
[COMPILING] bar v0.1.1 ([CWD]/bar)
474+
[COMPILING] foo v0.0.1 ([CWD])
475+
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
476+
",
477+
)
478+
.run();
479+
p.cargo("build")
480+
.with_stderr(
481+
"\
482+
[FINISHED] [..]
483+
",
484+
)
485+
.run();
486+
487+
// there should be no patch.unused in the toml file
488+
let lock = p.read_lockfile();
489+
let toml: toml::Value = toml::from_str(&lock).unwrap();
490+
assert!(toml.get("patch").is_none());
491+
}
492+
444493
#[cargo_test]
445494
fn unused_from_config() {
446495
Package::new("bar", "0.1.0").publish();

0 commit comments

Comments
 (0)