Skip to content

Commit 3a580a4

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

File tree

2 files changed

+55
-18
lines changed

2 files changed

+55
-18
lines changed

src/cargo/core/resolver/encode.rs

Lines changed: 51 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,33 @@ 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 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+
}
431+
return Some(version_source.values().next().unwrap());
432+
}
433+
None
434+
})
435+
}
408436
}
409437
}
410438

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

421-
let mut ret = HashMap::new();
451+
let mut ret: HashMap<String, HashMap<semver::Version, SourceId>> = HashMap::new();
422452
let mut visited = HashSet::new();
423453
for member in members.iter() {
424-
ret.insert(
425-
member.package_id().name().to_string(),
426-
member.package_id().source_id(),
427-
);
454+
ret.entry(member.package_id().name().to_string())
455+
.or_insert_with(HashMap::new)
456+
.insert(
457+
member.package_id().version().clone(),
458+
member.package_id().source_id(),
459+
);
428460
visited.insert(member.package_id().source_id());
429461
}
430462
for member in members.iter() {
@@ -444,7 +476,7 @@ fn build_path_deps(ws: &Workspace<'_>) -> CargoResult<HashMap<String, SourceId>>
444476
fn build_pkg(
445477
pkg: &Package,
446478
ws: &Workspace<'_>,
447-
ret: &mut HashMap<String, SourceId>,
479+
ret: &mut HashMap<String, HashMap<semver::Version, SourceId>>,
448480
visited: &mut HashSet<SourceId>,
449481
) {
450482
for dep in pkg.dependencies() {
@@ -455,7 +487,7 @@ fn build_path_deps(ws: &Workspace<'_>) -> CargoResult<HashMap<String, SourceId>>
455487
fn build_dep(
456488
dep: &Dependency,
457489
ws: &Workspace<'_>,
458-
ret: &mut HashMap<String, SourceId>,
490+
ret: &mut HashMap<String, HashMap<semver::Version, SourceId>>,
459491
visited: &mut HashSet<SourceId>,
460492
) {
461493
let id = dep.source_id();
@@ -467,7 +499,12 @@ fn build_path_deps(ws: &Workspace<'_>) -> CargoResult<HashMap<String, SourceId>>
467499
Err(_) => return,
468500
};
469501
let Ok(pkg) = ws.load(&path) else { return };
470-
ret.insert(pkg.name().to_string(), pkg.package_id().source_id());
502+
ret.entry(pkg.package_id().name().to_string())
503+
.or_insert_with(HashMap::new)
504+
.insert(
505+
pkg.package_id().version().clone(),
506+
pkg.package_id().source_id(),
507+
);
471508
visited.insert(pkg.package_id().source_id());
472509
build_pkg(&pkg, ws, ret, visited);
473510
}

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)