@@ -69,7 +69,7 @@ pub struct Package {
6969 resolver : Option < Symbol > ,
7070}
7171
72- #[ derive( Debug , Clone ) ]
72+ #[ derive( Debug , Clone , PartialEq , Eq , PartialOrd , Ord ) ]
7373pub struct Dependency {
7474 pub mode : DependencyMode ,
7575 pub subst : Option < PM :: Substitution > ,
@@ -83,6 +83,16 @@ pub enum DependencyMode {
8383 DevOnly ,
8484}
8585
86+ /// Keeps information about external resolution request
87+ #[ derive( Debug , Clone ) ]
88+ pub struct ExternalRequest {
89+ mode : DependencyMode ,
90+ from : Symbol ,
91+ to : Symbol ,
92+ resolver : Symbol ,
93+ pkg_path : PathBuf ,
94+ }
95+
8696/// Wrapper struct to display a package as an inline table in the lock file (matching the
8797/// convention in the source manifest). This is necessary becase the `toml` crate does not
8898/// currently support serializing types as inline tables.
@@ -115,13 +125,16 @@ impl DependencyGraph {
115125
116126 // Ensure there's always a root node, even if it has no edges.
117127 graph. package_graph . add_node ( graph. root_package ) ;
118-
128+ // Collect external resolution requests and process them later to check for "safe"
129+ // overlapping packages existing in both externally and internally resolved graphs.
130+ let mut external_requests = vec ! [ ] ;
119131 graph
120132 . extend_graph (
121133 & PM :: DependencyKind :: default ( ) ,
122134 root_package,
123135 & root_path,
124136 dependency_cache,
137+ & mut external_requests,
125138 progress_output,
126139 )
127140 . with_context ( || {
@@ -131,6 +144,24 @@ impl DependencyGraph {
131144 )
132145 } ) ?;
133146
147+ for ExternalRequest {
148+ mode,
149+ from,
150+ to,
151+ resolver,
152+ pkg_path,
153+ } in external_requests
154+ {
155+ graph
156+ . resolve_externally ( mode, from, to, resolver, & pkg_path, progress_output)
157+ . with_context ( || {
158+ format ! (
159+ "Failed to resolve dependencies for package '{}'" ,
160+ graph. root_package
161+ )
162+ } ) ?
163+ }
164+
134165 graph. check_acyclic ( ) ?;
135166 graph. discover_always_deps ( ) ;
136167
@@ -402,16 +433,18 @@ impl DependencyGraph {
402433 entry. insert ( ext_pkg) ;
403434 }
404435
405- // Seeing the same package in `extension` as in `self`: Not OK, even if their
406- // sources match, because supporting this case requires handling complicated edge
407- // cases (confirming that the sub-graph rooted at this package is also the same).
408- Entry :: Occupied ( entry) => {
409- bail ! (
410- "Conflicting dependencies found:\n {0} = {1}\n {0} = {2}" ,
411- ext_name,
412- PackageWithResolverTOML ( entry. get( ) ) ,
413- PackageWithResolverTOML ( & ext_pkg) ,
414- ) ;
436+ // Seeing the same package in `extension` is OK only if it has the same set of
437+ // dependencies as the existing one.i
438+ Entry :: Occupied ( _) => {
439+ let ( self_deps, ext_deps) =
440+ pkg_deps_equal ( ext_name, & self . package_graph , & ext_graph) ;
441+ if self_deps != ext_deps {
442+ bail ! (
443+ "Conflicting dependencies found for '{ext_name}' during external resolution by '{resolver}':\n {}{}" ,
444+ format_deps( "\n External dependencies not found:" , self_deps) ,
445+ format_deps( "\n New external dependencies:" , ext_deps) ,
446+ ) ;
447+ }
415448 }
416449 }
417450 }
@@ -459,42 +492,41 @@ impl DependencyGraph {
459492 package : & PM :: SourceManifest ,
460493 package_path : & Path ,
461494 dependency_cache : & mut DependencyCache ,
495+ external_requests : & mut Vec < ExternalRequest > ,
462496 progress_output : & mut Progress ,
463497 ) -> Result < ( ) > {
464498 let from = package. package . name ;
465499 for ( to, dep) in & package. dependencies {
466500 match dep {
467- PM :: Dependency :: External ( resolver) => self . resolve_externally (
468- DependencyMode :: Always ,
501+ PM :: Dependency :: External ( resolver) => external_requests . push ( ExternalRequest {
502+ mode : DependencyMode :: Always ,
469503 from,
470- * to,
471- * resolver,
472- package_path,
473- progress_output,
474- ) ?,
475-
504+ to : * to,
505+ resolver : * resolver,
506+ pkg_path : package_path. to_path_buf ( ) ,
507+ } ) ,
476508 PM :: Dependency :: Internal ( dep) => self . resolve_internally (
477509 DependencyMode :: Always ,
478510 from,
479511 * to,
480512 parent,
481513 dep. clone ( ) ,
482514 dependency_cache,
515+ external_requests,
483516 progress_output,
484517 ) ?,
485518 }
486519 }
487520
488521 for ( to, dep) in & package. dev_dependencies {
489522 match dep {
490- PM :: Dependency :: External ( resolver) => self . resolve_externally (
491- DependencyMode :: DevOnly ,
523+ PM :: Dependency :: External ( resolver) => external_requests . push ( ExternalRequest {
524+ mode : DependencyMode :: DevOnly ,
492525 from,
493- * to,
494- * resolver,
495- package_path,
496- progress_output,
497- ) ?,
526+ to : * to,
527+ resolver : * resolver,
528+ pkg_path : package_path. to_path_buf ( ) ,
529+ } ) ,
498530
499531 PM :: Dependency :: Internal ( dep) => self . resolve_internally (
500532 DependencyMode :: DevOnly ,
@@ -503,6 +535,7 @@ impl DependencyGraph {
503535 parent,
504536 dep. clone ( ) ,
505537 dependency_cache,
538+ external_requests,
506539 progress_output,
507540 ) ?,
508541 }
@@ -602,6 +635,7 @@ impl DependencyGraph {
602635 parent : & PM :: DependencyKind ,
603636 dep : PM :: InternalDependency ,
604637 dependency_cache : & mut DependencyCache ,
638+ external_requests : & mut Vec < ExternalRequest > ,
605639 progress_output : & mut Progress ,
606640 ) -> Result < ( ) > {
607641 let PM :: InternalDependency {
@@ -618,7 +652,13 @@ impl DependencyGraph {
618652 } ;
619653
620654 pkg. kind . reroot ( parent) ?;
621- self . process_dependency ( pkg, to, dependency_cache, progress_output) ?;
655+ self . process_dependency (
656+ pkg,
657+ to,
658+ dependency_cache,
659+ external_requests,
660+ progress_output,
661+ ) ?;
622662 self . package_graph . add_edge (
623663 from,
624664 to,
@@ -641,6 +681,7 @@ impl DependencyGraph {
641681 pkg : Package ,
642682 name : PM :: PackageName ,
643683 dependency_cache : & mut DependencyCache ,
684+ external_requests : & mut Vec < ExternalRequest > ,
644685 progress_output : & mut Progress ,
645686 ) -> Result < ( ) > {
646687 let pkg = match self . package_table . entry ( name) {
@@ -676,6 +717,7 @@ impl DependencyGraph {
676717 & manifest,
677718 & pkg_path,
678719 dependency_cache,
720+ external_requests,
679721 progress_output,
680722 )
681723 . with_context ( || format ! ( "Resolving dependencies for package '{}'" , name) )
@@ -904,3 +946,35 @@ fn str_escape(s: &str) -> Result<String, fmt::Error> {
904946fn path_escape ( p : & Path ) -> Result < String , fmt:: Error > {
905947 str_escape ( p. to_str ( ) . ok_or ( fmt:: Error ) ?)
906948}
949+
950+ fn format_deps ( msg : & str , dependencies : Vec < ( & Dependency , PM :: PackageName ) > ) -> String {
951+ let mut s = "" . to_string ( ) ;
952+ if !dependencies. is_empty ( ) {
953+ s. push_str ( msg) ;
954+ for ( dep, pkg) in dependencies {
955+ s. push_str ( "\n \t " ) ;
956+ s. push_str ( format ! ( "{}" , DependencyTOML ( pkg, dep) ) . as_str ( ) ) ;
957+ }
958+ }
959+ s
960+ }
961+
962+ /// Checks if dependencies of a given package in two different dependency graph maps are the
963+ /// same.
964+ fn pkg_deps_equal < ' a > (
965+ pkg_name : Symbol ,
966+ pkg_graph : & ' a DiGraphMap < PM :: PackageName , Dependency > ,
967+ other_graph : & ' a DiGraphMap < PM :: PackageName , Dependency > ,
968+ ) -> (
969+ Vec < ( & ' a Dependency , PM :: PackageName ) > ,
970+ Vec < ( & ' a Dependency , PM :: PackageName ) > ,
971+ ) {
972+ let pkg_edges = BTreeSet :: from_iter ( pkg_graph. edges ( pkg_name) . map ( |( _, pkg, dep) | ( dep, pkg) ) ) ;
973+ let other_edges =
974+ BTreeSet :: from_iter ( other_graph. edges ( pkg_name) . map ( |( _, pkg, dep) | ( dep, pkg) ) ) ;
975+
976+ let ( pkg_deps, other_deps) : ( Vec < _ > , Vec < _ > ) = pkg_edges
977+ . symmetric_difference ( & other_edges)
978+ . partition ( |dep| pkg_edges. contains ( dep) ) ;
979+ ( pkg_deps, other_deps)
980+ }
0 commit comments