Skip to content

Commit 42fbaf5

Browse files
committed
fix: Make path dependencies with the same name stays locked
1 parent a15a507 commit 42fbaf5

File tree

2 files changed

+56
-18
lines changed

2 files changed

+56
-18
lines changed

src/cargo/core/resolver/encode.rs

Lines changed: 52 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ impl EncodableResolve {
154154
/// primary uses is to be used with `resolve_with_previous` to guide the
155155
/// resolver to create a complete Resolve.
156156
pub fn into_resolve(self, original: &str, ws: &Workspace<'_>) -> CargoResult<Resolve> {
157-
let path_deps = build_path_deps(ws)?;
157+
let path_deps: HashMap<String, HashMap<semver::Version, SourceId>> = build_path_deps(ws)?;
158158
let mut checksums = HashMap::new();
159159

160160
let mut version = match self.version {
@@ -202,7 +202,11 @@ impl EncodableResolve {
202202
if !all_pkgs.insert(enc_id.clone()) {
203203
anyhow::bail!("package `{}` is specified twice in the lockfile", pkg.name);
204204
}
205-
let id = match pkg.source.as_deref().or_else(|| path_deps.get(&pkg.name)) {
205+
let id = match pkg
206+
.source
207+
.as_deref()
208+
.or_else(|| get_source_id(&path_deps, pkg))
209+
{
206210
// We failed to find a local package in the workspace.
207211
// It must have been removed and should be ignored.
208212
None => {
@@ -364,7 +368,11 @@ impl EncodableResolve {
364368

365369
let mut unused_patches = Vec::new();
366370
for pkg in self.patch.unused {
367-
let id = match pkg.source.as_deref().or_else(|| path_deps.get(&pkg.name)) {
371+
let id = match pkg
372+
.source
373+
.as_deref()
374+
.or_else(|| get_source_id(&path_deps, &pkg))
375+
{
368376
Some(&src) => PackageId::try_new(&pkg.name, &pkg.version, src)?,
369377
None => continue,
370378
};
@@ -395,7 +403,7 @@ impl EncodableResolve {
395403
version = ResolveVersion::V2;
396404
}
397405

398-
Ok(Resolve::new(
406+
return Ok(Resolve::new(
399407
g,
400408
replacements,
401409
HashMap::new(),
@@ -404,11 +412,34 @@ impl EncodableResolve {
404412
unused_patches,
405413
version,
406414
HashMap::new(),
407-
))
415+
));
416+
417+
fn get_source_id<'a>(
418+
path_deps: &'a HashMap<String, HashMap<semver::Version, SourceId>>,
419+
pkg: &'a EncodableDependency,
420+
) -> Option<&'a SourceId> {
421+
path_deps.iter().find_map(|(name, version_source)| {
422+
if name == &pkg.name {
423+
// If there are multiple candidates for the same name, it needs to be precisely determined by combining versions (See #13405).
424+
if version_source.len() > 1 {
425+
if let Ok(pkg_version) = pkg.version.parse::<semver::Version>() {
426+
if let Some(source_id) = version_source.get(&pkg_version) {
427+
return Some(source_id);
428+
}
429+
}
430+
return None;
431+
}
432+
return Some(version_source.values().next().unwrap());
433+
}
434+
None
435+
})
436+
}
408437
}
409438
}
410439

411-
fn build_path_deps(ws: &Workspace<'_>) -> CargoResult<HashMap<String, SourceId>> {
440+
fn build_path_deps(
441+
ws: &Workspace<'_>,
442+
) -> CargoResult<HashMap<String, HashMap<semver::Version, SourceId>>> {
412443
// If a crate is **not** a path source, then we're probably in a situation
413444
// such as `cargo install` with a lock file from a remote dependency. In
414445
// that case we don't need to fixup any path dependencies (as they're not
@@ -418,13 +449,15 @@ fn build_path_deps(ws: &Workspace<'_>) -> CargoResult<HashMap<String, SourceId>>
418449
.filter(|p| p.package_id().source_id().is_path())
419450
.collect::<Vec<_>>();
420451

421-
let mut ret = HashMap::new();
452+
let mut ret: HashMap<String, HashMap<semver::Version, SourceId>> = HashMap::new();
422453
let mut visited = HashSet::new();
423454
for member in members.iter() {
424-
ret.insert(
425-
member.package_id().name().to_string(),
426-
member.package_id().source_id(),
427-
);
455+
ret.entry(member.package_id().name().to_string())
456+
.or_insert_with(HashMap::new)
457+
.insert(
458+
member.package_id().version().clone(),
459+
member.package_id().source_id(),
460+
);
428461
visited.insert(member.package_id().source_id());
429462
}
430463
for member in members.iter() {
@@ -444,7 +477,7 @@ fn build_path_deps(ws: &Workspace<'_>) -> CargoResult<HashMap<String, SourceId>>
444477
fn build_pkg(
445478
pkg: &Package,
446479
ws: &Workspace<'_>,
447-
ret: &mut HashMap<String, SourceId>,
480+
ret: &mut HashMap<String, HashMap<semver::Version, SourceId>>,
448481
visited: &mut HashSet<SourceId>,
449482
) {
450483
for dep in pkg.dependencies() {
@@ -455,7 +488,7 @@ fn build_path_deps(ws: &Workspace<'_>) -> CargoResult<HashMap<String, SourceId>>
455488
fn build_dep(
456489
dep: &Dependency,
457490
ws: &Workspace<'_>,
458-
ret: &mut HashMap<String, SourceId>,
491+
ret: &mut HashMap<String, HashMap<semver::Version, SourceId>>,
459492
visited: &mut HashSet<SourceId>,
460493
) {
461494
let id = dep.source_id();
@@ -467,7 +500,12 @@ fn build_path_deps(ws: &Workspace<'_>) -> CargoResult<HashMap<String, SourceId>>
467500
Err(_) => return,
468501
};
469502
let Ok(pkg) = ws.load(&path) else { return };
470-
ret.insert(pkg.name().to_string(), pkg.package_id().source_id());
503+
ret.entry(pkg.package_id().name().to_string())
504+
.or_insert_with(HashMap::new)
505+
.insert(
506+
pkg.package_id().version().clone(),
507+
pkg.package_id().source_id(),
508+
);
471509
visited.insert(pkg.package_id().source_id());
472510
build_pkg(&pkg, ws, ret, visited);
473511
}

tests/testsuite/patch.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2939,12 +2939,12 @@ foo v0.0.0 ([ROOT]/foo)
29392939
"\
29402940
foo v0.0.0 ([ROOT]/foo)
29412941
├── bar v1.0.999 ([ROOT]/foo/bar-1-as-3)
2942-
│ └── bar v3.0.1
2942+
│ └── bar v3.0.0
29432943
└── bar v2.0.999 ([ROOT]/foo/bar-2-as-3)
2944-
└── bar v3.0.1
2944+
└── bar v3.0.0
29452945
",
29462946
)
29472947
.run();
29482948

2949-
assert_ne!(p.read_file("Cargo.lock"), p.read_file("Cargo.lock.orig"));
2950-
}
2949+
assert_eq!(p.read_file("Cargo.lock"), p.read_file("Cargo.lock.orig"));
2950+
}

0 commit comments

Comments
 (0)