@@ -69,7 +69,7 @@ pub struct Package {
69
69
resolver : Option < Symbol > ,
70
70
}
71
71
72
- #[ derive( Debug , Clone ) ]
72
+ #[ derive( Debug , Clone , PartialEq , Eq , PartialOrd , Ord ) ]
73
73
pub struct Dependency {
74
74
pub mode : DependencyMode ,
75
75
pub subst : Option < PM :: Substitution > ,
@@ -83,6 +83,16 @@ pub enum DependencyMode {
83
83
DevOnly ,
84
84
}
85
85
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
+
86
96
/// Wrapper struct to display a package as an inline table in the lock file (matching the
87
97
/// convention in the source manifest). This is necessary becase the `toml` crate does not
88
98
/// currently support serializing types as inline tables.
@@ -115,13 +125,16 @@ impl DependencyGraph {
115
125
116
126
// Ensure there's always a root node, even if it has no edges.
117
127
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 ! [ ] ;
119
131
graph
120
132
. extend_graph (
121
133
& PM :: DependencyKind :: default ( ) ,
122
134
root_package,
123
135
& root_path,
124
136
dependency_cache,
137
+ & mut external_requests,
125
138
progress_output,
126
139
)
127
140
. with_context ( || {
@@ -131,6 +144,24 @@ impl DependencyGraph {
131
144
)
132
145
} ) ?;
133
146
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
+
134
165
graph. check_acyclic ( ) ?;
135
166
graph. discover_always_deps ( ) ;
136
167
@@ -402,16 +433,18 @@ impl DependencyGraph {
402
433
entry. insert ( ext_pkg) ;
403
434
}
404
435
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
+ }
415
448
}
416
449
}
417
450
}
@@ -459,42 +492,41 @@ impl DependencyGraph {
459
492
package : & PM :: SourceManifest ,
460
493
package_path : & Path ,
461
494
dependency_cache : & mut DependencyCache ,
495
+ external_requests : & mut Vec < ExternalRequest > ,
462
496
progress_output : & mut Progress ,
463
497
) -> Result < ( ) > {
464
498
let from = package. package . name ;
465
499
for ( to, dep) in & package. dependencies {
466
500
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 ,
469
503
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
+ } ) ,
476
508
PM :: Dependency :: Internal ( dep) => self . resolve_internally (
477
509
DependencyMode :: Always ,
478
510
from,
479
511
* to,
480
512
parent,
481
513
dep. clone ( ) ,
482
514
dependency_cache,
515
+ external_requests,
483
516
progress_output,
484
517
) ?,
485
518
}
486
519
}
487
520
488
521
for ( to, dep) in & package. dev_dependencies {
489
522
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 ,
492
525
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
+ } ) ,
498
530
499
531
PM :: Dependency :: Internal ( dep) => self . resolve_internally (
500
532
DependencyMode :: DevOnly ,
@@ -503,6 +535,7 @@ impl DependencyGraph {
503
535
parent,
504
536
dep. clone ( ) ,
505
537
dependency_cache,
538
+ external_requests,
506
539
progress_output,
507
540
) ?,
508
541
}
@@ -602,6 +635,7 @@ impl DependencyGraph {
602
635
parent : & PM :: DependencyKind ,
603
636
dep : PM :: InternalDependency ,
604
637
dependency_cache : & mut DependencyCache ,
638
+ external_requests : & mut Vec < ExternalRequest > ,
605
639
progress_output : & mut Progress ,
606
640
) -> Result < ( ) > {
607
641
let PM :: InternalDependency {
@@ -618,7 +652,13 @@ impl DependencyGraph {
618
652
} ;
619
653
620
654
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
+ ) ?;
622
662
self . package_graph . add_edge (
623
663
from,
624
664
to,
@@ -641,6 +681,7 @@ impl DependencyGraph {
641
681
pkg : Package ,
642
682
name : PM :: PackageName ,
643
683
dependency_cache : & mut DependencyCache ,
684
+ external_requests : & mut Vec < ExternalRequest > ,
644
685
progress_output : & mut Progress ,
645
686
) -> Result < ( ) > {
646
687
let pkg = match self . package_table . entry ( name) {
@@ -676,6 +717,7 @@ impl DependencyGraph {
676
717
& manifest,
677
718
& pkg_path,
678
719
dependency_cache,
720
+ external_requests,
679
721
progress_output,
680
722
)
681
723
. with_context ( || format ! ( "Resolving dependencies for package '{}'" , name) )
@@ -904,3 +946,35 @@ fn str_escape(s: &str) -> Result<String, fmt::Error> {
904
946
fn path_escape ( p : & Path ) -> Result < String , fmt:: Error > {
905
947
str_escape ( p. to_str ( ) . ok_or ( fmt:: Error ) ?)
906
948
}
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