@@ -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 = PathDeps :: from_workspace ( ws) ?;
158158 let mut checksums = HashMap :: new ( ) ;
159159
160160 let mut version = match self . version {
@@ -202,14 +202,18 @@ 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. source . as_deref ( ) . copied ( ) . or_else ( || {
206+ path_deps
207+ . get_path_dep ( & pkg. name , & pkg. version )
208+ . map ( |package_id| package_id. source_id ( ) )
209+ } ) {
206210 // We failed to find a local package in the workspace.
207211 // It must have been removed and should be ignored.
208212 None => {
209213 debug ! ( "path dependency now missing {} v{}" , pkg. name, pkg. version) ;
210214 continue ;
211215 }
212- Some ( & source) => PackageId :: try_new ( & pkg. name , & pkg. version , source) ?,
216+ Some ( source) => PackageId :: try_new ( & pkg. name , & pkg. version , source) ?,
213217 } ;
214218
215219 // If a package has a checksum listed directly on it then record
@@ -364,8 +368,12 @@ 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 ) ) {
368- Some ( & src) => PackageId :: try_new ( & pkg. name , & pkg. version , src) ?,
371+ let id = match pkg. source . as_deref ( ) . copied ( ) . or_else ( || {
372+ path_deps
373+ . get_path_dep ( & pkg. name , & pkg. version )
374+ . map ( |package_id| package_id. source_id ( ) )
375+ } ) {
376+ Some ( src) => PackageId :: try_new ( & pkg. name , & pkg. version , src) ?,
369377 None => continue ,
370378 } ;
371379 unused_patches. push ( id) ;
@@ -408,68 +416,98 @@ impl EncodableResolve {
408416 }
409417}
410418
411- fn build_path_deps ( ws : & Workspace < ' _ > ) -> CargoResult < HashMap < String , SourceId > > {
412- // If a crate is **not** a path source, then we're probably in a situation
413- // such as `cargo install` with a lock file from a remote dependency. In
414- // that case we don't need to fixup any path dependencies (as they're not
415- // actually path dependencies any more), so we ignore them.
416- let members = ws
417- . members ( )
418- . filter ( |p| p. package_id ( ) . source_id ( ) . is_path ( ) )
419- . collect :: < Vec < _ > > ( ) ;
420-
421- let mut ret = HashMap :: new ( ) ;
422- let mut visited = HashSet :: new ( ) ;
423- for member in members. iter ( ) {
424- ret. insert (
425- member. package_id ( ) . name ( ) . to_string ( ) ,
426- member. package_id ( ) . source_id ( ) ,
427- ) ;
428- visited. insert ( member. package_id ( ) . source_id ( ) ) ;
429- }
430- for member in members. iter ( ) {
431- build_pkg ( member, ws, & mut ret, & mut visited) ;
419+ struct PathDeps {
420+ path_deps : HashMap < String , Vec < PackageId > > ,
421+ }
422+
423+ impl PathDeps {
424+ /// Get a best-effort path dependency with given constraints.
425+ ///
426+ /// If no exact version match, pickup a random version for patch updating trial
427+ fn get_path_dep ( & self , name : & str , version : & str ) -> Option < & PackageId > {
428+ let pkg_version = semver:: Version :: parse ( version) . ok ( ) ?;
429+ self . path_deps . get ( name) . and_then ( |versions| {
430+ versions
431+ . iter ( )
432+ . find ( |package_id| {
433+ package_id. version ( ) . cmp_precedence ( & pkg_version) == std:: cmp:: Ordering :: Equal
434+ } )
435+ . or_else ( || versions. last ( ) )
436+ } )
432437 }
433- for deps in ws. root_patch ( ) ?. values ( ) {
434- for dep in deps {
438+
439+ /// Return all path dependencies recursively in given workspace
440+ fn from_workspace ( ws : & Workspace < ' _ > ) -> CargoResult < Self > {
441+ // If a crate is **not** a path source, then we're probably in a situation
442+ // such as `cargo install` with a lock file from a remote dependency. In
443+ // that case we don't need to fixup any path dependencies (as they're not
444+ // actually path dependencies any more), so we ignore them.
445+ let members = ws
446+ . members ( )
447+ . filter ( |p| p. package_id ( ) . source_id ( ) . is_path ( ) )
448+ . collect :: < Vec < _ > > ( ) ;
449+
450+ let mut ret = HashSet :: new ( ) ;
451+ let mut visited = HashSet :: new ( ) ;
452+ for member in members. iter ( ) {
453+ ret. insert ( member. package_id ( ) ) ;
454+ visited. insert ( member. package_id ( ) . source_id ( ) ) ;
455+ }
456+ for member in members. iter ( ) {
457+ build_pkg ( member, ws, & mut ret, & mut visited) ;
458+ }
459+ for deps in ws. root_patch ( ) ?. values ( ) {
460+ for dep in deps {
461+ build_dep ( dep, ws, & mut ret, & mut visited) ;
462+ }
463+ }
464+ for ( _, dep) in ws. root_replace ( ) {
435465 build_dep ( dep, ws, & mut ret, & mut visited) ;
436466 }
437- }
438- for ( _, dep) in ws. root_replace ( ) {
439- build_dep ( dep, ws, & mut ret, & mut visited) ;
440- }
441467
442- return Ok ( ret) ;
468+ let path_deps = {
469+ // Mapping from package name to package ids
470+ let mut deps: HashMap < String , Vec < PackageId > > = HashMap :: new ( ) ;
471+ for package_id in ret {
472+ deps. entry ( package_id. name ( ) . to_string ( ) )
473+ . or_default ( )
474+ . push ( package_id) ;
475+ }
476+ deps
477+ } ;
478+
479+ return Ok ( Self { path_deps } ) ;
443480
444- fn build_pkg (
445- pkg : & Package ,
446- ws : & Workspace < ' _ > ,
447- ret : & mut HashMap < String , SourceId > ,
448- visited : & mut HashSet < SourceId > ,
449- ) {
450- for dep in pkg. dependencies ( ) {
451- build_dep ( dep, ws, ret, visited) ;
481+ fn build_pkg (
482+ pkg : & Package ,
483+ ws : & Workspace < ' _ > ,
484+ ret : & mut HashSet < PackageId > ,
485+ visited : & mut HashSet < SourceId > ,
486+ ) {
487+ for dep in pkg. dependencies ( ) {
488+ build_dep ( dep, ws, ret, visited) ;
489+ }
452490 }
453- }
454491
455- fn build_dep (
456- dep : & Dependency ,
457- ws : & Workspace < ' _ > ,
458- ret : & mut HashMap < String , SourceId > ,
459- visited : & mut HashSet < SourceId > ,
460- ) {
461- let id = dep. source_id ( ) ;
462- if visited. contains ( & id) || !id. is_path ( ) {
463- return ;
492+ fn build_dep (
493+ dep : & Dependency ,
494+ ws : & Workspace < ' _ > ,
495+ ret : & mut HashSet < PackageId > ,
496+ visited : & mut HashSet < SourceId > ,
497+ ) {
498+ let id = dep. source_id ( ) ;
499+ if visited. contains ( & id) || !id. is_path ( ) {
500+ return ;
501+ }
502+ let path = match id. url ( ) . to_file_path ( ) {
503+ Ok ( p) => p. join ( "Cargo.toml" ) ,
504+ Err ( _) => return ,
505+ } ;
506+ let Ok ( pkg) = ws. load ( & path) else { return } ;
507+ ret. insert ( pkg. package_id ( ) ) ;
508+ visited. insert ( pkg. package_id ( ) . source_id ( ) ) ;
509+ build_pkg ( & pkg, ws, ret, visited) ;
464510 }
465- let path = match id. url ( ) . to_file_path ( ) {
466- Ok ( p) => p. join ( "Cargo.toml" ) ,
467- Err ( _) => return ,
468- } ;
469- let Ok ( pkg) = ws. load ( & path) else { return } ;
470- ret. insert ( pkg. name ( ) . to_string ( ) , pkg. package_id ( ) . source_id ( ) ) ;
471- visited. insert ( pkg. package_id ( ) . source_id ( ) ) ;
472- build_pkg ( & pkg, ws, ret, visited) ;
473511 }
474512}
475513
0 commit comments