23
23
//! * Never try to activate a crate version which is incompatible. This means we
24
24
//! only try crates which will actually satisfy a dependency and we won't ever
25
25
//! try to activate a crate that's semver compatible with something else
26
- //! activated (as we're only allowed to have one).
26
+ //! activated (as we're only allowed to have one) nor try to activate a crate
27
+ //! that has the same links attribute as something else
28
+ //! activated.
27
29
//! * Always try to activate the highest version crate first. The default
28
30
//! dependency in Cargo (e.g. when you write `foo = "0.1.2"`) is
29
31
//! semver-compatible, so selecting the highest version possible will allow us
@@ -325,11 +327,12 @@ enum GraphNode {
325
327
// possible.
326
328
#[ derive( Clone ) ]
327
329
struct Context < ' a > {
328
- // TODO: Both this and the map below are super expensive to clone. We should
330
+ // TODO: Both this and the two maps below are super expensive to clone. We should
329
331
// switch to persistent hash maps if we can at some point or otherwise
330
332
// make these much cheaper to clone in general.
331
333
activations : Activations ,
332
334
resolve_features : HashMap < PackageId , HashSet < String > > ,
335
+ links : HashMap < String , PackageId > ,
333
336
334
337
// These are two cheaply-cloneable lists (O(1) clone) which are effectively
335
338
// hash maps but are built up as "construction lists". We'll iterate these
@@ -354,9 +357,10 @@ pub fn resolve(summaries: &[(Summary, Method)],
354
357
let cx = Context {
355
358
resolve_graph : RcList :: new ( ) ,
356
359
resolve_features : HashMap :: new ( ) ,
360
+ links : HashMap :: new ( ) ,
357
361
resolve_replacements : RcList :: new ( ) ,
358
362
activations : HashMap :: new ( ) ,
359
- replacements : replacements ,
363
+ replacements,
360
364
warnings : RcList :: new ( ) ,
361
365
} ;
362
366
let _p = profile:: start ( "resolving" ) ;
@@ -416,13 +420,13 @@ fn activate(cx: &mut Context,
416
420
candidate. summary . package_id ( ) . clone ( ) ) ) ;
417
421
}
418
422
419
- let activated = cx. flag_activated ( & candidate. summary , method) ;
423
+ let activated = cx. flag_activated ( & candidate. summary , method) ? ;
420
424
421
425
let candidate = match candidate. replace {
422
426
Some ( replace) => {
423
427
cx. resolve_replacements . push ( ( candidate. summary . package_id ( ) . clone ( ) ,
424
428
replace. package_id ( ) . clone ( ) ) ) ;
425
- if cx. flag_activated ( & replace, method) && activated {
429
+ if cx. flag_activated ( & replace, method) ? && activated {
426
430
return Ok ( None ) ;
427
431
}
428
432
trace ! ( "activating {} (replacing {})" , replace. package_id( ) ,
@@ -456,7 +460,7 @@ impl<T> RcVecIter<T> {
456
460
fn new ( vec : Rc < Vec < T > > ) -> RcVecIter < T > {
457
461
RcVecIter {
458
462
rest : 0 ..vec. len ( ) ,
459
- vec : vec ,
463
+ vec,
460
464
}
461
465
}
462
466
}
@@ -528,6 +532,21 @@ impl Ord for DepsFrame {
528
532
}
529
533
}
530
534
535
+ #[ derive( Clone , PartialOrd , Ord , PartialEq , Eq ) ]
536
+ enum ConflictReason {
537
+ Semver ,
538
+ Links ( String ) ,
539
+ }
540
+
541
+ impl ConflictReason {
542
+ fn is_links ( & self ) -> bool {
543
+ match self {
544
+ & ConflictReason :: Semver => false ,
545
+ & ConflictReason :: Links ( _) => true ,
546
+ }
547
+ }
548
+ }
549
+
531
550
#[ derive( Clone ) ]
532
551
struct BacktrackFrame < ' a > {
533
552
cur : usize ,
@@ -543,7 +562,7 @@ struct BacktrackFrame<'a> {
543
562
struct RemainingCandidates {
544
563
remaining : RcVecIter < Candidate > ,
545
564
// note: change to RcList or something if clone is to expensive
546
- conflicting_prev_active : HashSet < PackageId > ,
565
+ conflicting_prev_active : HashMap < PackageId , ConflictReason > ,
547
566
// This is a inlined peekable generator
548
567
has_another : Option < Candidate > ,
549
568
}
@@ -552,31 +571,40 @@ impl RemainingCandidates {
552
571
fn new ( candidates : & Rc < Vec < Candidate > > ) -> RemainingCandidates {
553
572
RemainingCandidates {
554
573
remaining : RcVecIter :: new ( Rc :: clone ( candidates) ) ,
555
- conflicting_prev_active : HashSet :: new ( ) ,
574
+ conflicting_prev_active : HashMap :: new ( ) ,
556
575
has_another : None ,
557
576
}
558
577
}
559
578
560
- fn next ( & mut self , prev_active : & [ Summary ] ) -> Result < ( Candidate , bool ) , HashSet < PackageId > > {
579
+ fn next ( & mut self , prev_active : & [ Summary ] , links : & HashMap < String , PackageId > ) -> Result < ( Candidate , bool ) , HashMap < PackageId , ConflictReason > > {
561
580
// Filter the set of candidates based on the previously activated
562
581
// versions for this dependency. We can actually use a version if it
563
582
// precisely matches an activated version or if it is otherwise
564
583
// incompatible with all other activated versions. Note that we
565
584
// define "compatible" here in terms of the semver sense where if
566
585
// the left-most nonzero digit is the same they're considered
567
- // compatible.
586
+ // compatible unless we have a `*-sys` crate (defined by having a
587
+ // linked attribute) then we can only have one version.
568
588
//
569
589
// When we are done we return the set of previously activated
570
590
// that conflicted with the ones we tried. If any of these change
571
591
// then we would have considered different candidates.
572
592
use std:: mem:: replace;
573
593
for ( _, b) in self . remaining . by_ref ( ) {
594
+ if let Some ( link) = b. summary . links ( ) {
595
+ if let Some ( a) = links. get ( link) {
596
+ if a != b. summary . package_id ( ) {
597
+ self . conflicting_prev_active . insert ( a. clone ( ) , ConflictReason :: Links ( link. to_owned ( ) ) ) ;
598
+ continue
599
+ }
600
+ }
601
+ }
574
602
if let Some ( a) = prev_active
575
603
. iter ( )
576
604
. find ( |a| compatible ( a. version ( ) , b. summary . version ( ) ) )
577
605
{
578
606
if * a != b. summary {
579
- self . conflicting_prev_active . insert ( a. package_id ( ) . clone ( ) ) ;
607
+ self . conflicting_prev_active . insert ( a. package_id ( ) . clone ( ) , ConflictReason :: Semver ) ;
580
608
continue ;
581
609
}
582
610
}
@@ -675,15 +703,15 @@ fn activate_deps_loop<'a>(
675
703
trace ! ( "{}[{}]>{} {} candidates" , parent. name( ) , cur, dep. name( ) , candidates. len( ) ) ;
676
704
trace ! ( "{}[{}]>{} {} prev activations" , parent. name( ) , cur, dep. name( ) , cx. prev_active( & dep) . len( ) ) ;
677
705
let mut remaining_candidates = RemainingCandidates :: new ( & candidates) ;
678
- let next = remaining_candidates. next ( cx. prev_active ( & dep) ) ;
706
+ let next = remaining_candidates. next ( cx. prev_active ( & dep) , & cx . links ) ;
679
707
680
708
// Alright, for each candidate that's gotten this far, it meets the
681
709
// following requirements:
682
710
//
683
711
// 1. The version matches the dependency requirement listed for this
684
712
// package
685
713
// 2. There are no activated versions for this package which are
686
- // semver-compatible, or there's an activated version which is
714
+ // semver/links -compatible, or there's an activated version which is
687
715
// precisely equal to `candidate`.
688
716
//
689
717
// This means that we're going to attempt to activate each candidate in
@@ -768,15 +796,15 @@ fn find_candidate<'a>(
768
796
dep : & mut Dependency ,
769
797
features : & mut Rc < Vec < String > > ,
770
798
remaining_candidates : & mut RemainingCandidates ,
771
- conflicting_activations : & mut HashSet < PackageId > ,
799
+ conflicting_activations : & mut HashMap < PackageId , ConflictReason > ,
772
800
) -> Option < ( Candidate , bool ) > {
773
801
while let Some ( mut frame) = backtrack_stack. pop ( ) {
774
- let next= frame. remaining_candidates . next ( frame. context_backup . prev_active ( & frame. dep ) ) ;
802
+ let next= frame. remaining_candidates . next ( frame. context_backup . prev_active ( & frame. dep ) , & frame . context_backup . links ) ;
775
803
if frame. context_backup . is_active ( parent. package_id ( ) )
776
804
&& conflicting_activations
777
805
. iter ( )
778
806
// note: a lot of redundant work in is_active for similar debs
779
- . all ( |con| frame. context_backup . is_active ( con) )
807
+ . all ( |( con, _ ) | frame. context_backup . is_active ( con) )
780
808
{
781
809
continue ;
782
810
}
@@ -798,7 +826,7 @@ fn activation_error(cx: &Context,
798
826
registry : & mut Registry ,
799
827
parent : & Summary ,
800
828
dep : & Dependency ,
801
- conflicting_activations : HashSet < PackageId > ,
829
+ conflicting_activations : HashMap < PackageId , ConflictReason > ,
802
830
candidates : & [ Candidate ] ,
803
831
config : Option < & Config > ) -> CargoError {
804
832
let graph = cx. graph ( ) ;
@@ -814,26 +842,53 @@ fn activation_error(cx: &Context,
814
842
dep_path_desc
815
843
} ;
816
844
if !candidates. is_empty ( ) {
817
- let mut msg = format ! ( "failed to select a version for `{}`.\n \
818
- all possible versions conflict with \
819
- previously selected packages.\n ",
820
- dep. name( ) ) ;
821
- msg. push_str ( "required by " ) ;
845
+ let mut msg = format ! ( "failed to select a version for `{}`." , dep. name( ) ) ;
846
+ msg. push_str ( "\n ... required by " ) ;
822
847
msg. push_str ( & describe_path ( parent. package_id ( ) ) ) ;
823
- let mut conflicting_activations: Vec < _ > = conflicting_activations. iter ( ) . collect ( ) ;
824
- conflicting_activations. sort_unstable ( ) ;
825
- for v in conflicting_activations. iter ( ) . rev ( ) {
826
- msg. push_str ( "\n previously selected " ) ;
827
- msg. push_str ( & describe_path ( v) ) ;
828
- }
829
848
830
- msg. push_str ( "\n possible versions to select: " ) ;
849
+ msg. push_str ( "\n versions that meet the requirements `" ) ;
850
+ msg. push_str ( & dep. version_req ( ) . to_string ( ) ) ;
851
+ msg. push_str ( "` are: " ) ;
831
852
msg. push_str ( & candidates. iter ( )
832
853
. map ( |v| v. summary . version ( ) )
833
854
. map ( |v| v. to_string ( ) )
834
855
. collect :: < Vec < _ > > ( )
835
856
. join ( ", " ) ) ;
836
857
858
+ let mut conflicting_activations: Vec < _ > = conflicting_activations. iter ( ) . collect ( ) ;
859
+ conflicting_activations. sort_unstable ( ) ;
860
+ let ( links_errors, other_errors) : ( Vec < _ > , Vec < _ > ) = conflicting_activations. drain ( ..) . rev ( ) . partition ( |& ( _, r) | r. is_links ( ) ) ;
861
+
862
+ for & ( p, r) in & links_errors {
863
+ match r {
864
+ & ConflictReason :: Links ( ref link) => {
865
+ msg. push_str ( "\n \n the package `" ) ;
866
+ msg. push_str ( dep. name ( ) ) ;
867
+ msg. push_str ( "` links to the native library `" ) ;
868
+ msg. push_str ( & link) ;
869
+ msg. push_str ( "`, but it conflicts with a previous package which links to `" ) ;
870
+ msg. push_str ( & link) ;
871
+ msg. push_str ( "` as well:\n " ) ;
872
+ } ,
873
+ _ => ( ) ,
874
+ }
875
+ msg. push_str ( & describe_path ( p) ) ;
876
+ }
877
+
878
+ if links_errors. is_empty ( ) {
879
+ msg. push_str ( "\n \n all possible versions conflict with \
880
+ previously selected packages.") ;
881
+ }
882
+
883
+ for & ( p, _) in & other_errors {
884
+ msg. push_str ( "\n \n previously selected " ) ;
885
+ msg. push_str ( & describe_path ( p) ) ;
886
+ }
887
+
888
+ msg. push_str ( "\n \n failed to select a version for `" ) ;
889
+ msg. push_str ( dep. name ( ) ) ;
890
+ msg. push_str ( "` which could resolve this conflict" ) ;
891
+
837
892
return format_err ! ( "{}" , msg)
838
893
}
839
894
@@ -1044,12 +1099,12 @@ fn build_requirements<'a, 'b: 'a>(s: &'a Summary, method: &'b Method)
1044
1099
}
1045
1100
1046
1101
impl < ' a > Context < ' a > {
1047
- // Activate this summary by inserting it into our list of known activations.
1048
- //
1049
- // Returns if this summary with the given method is already activated.
1102
+ /// Activate this summary by inserting it into our list of known activations.
1103
+ ///
1104
+ /// Returns true if this summary with the given method is already activated.
1050
1105
fn flag_activated ( & mut self ,
1051
1106
summary : & Summary ,
1052
- method : & Method ) -> bool {
1107
+ method : & Method ) -> CargoResult < bool > {
1053
1108
let id = summary. package_id ( ) ;
1054
1109
let prev = self . activations
1055
1110
. entry ( id. name ( ) . to_string ( ) )
@@ -1058,26 +1113,31 @@ impl<'a> Context<'a> {
1058
1113
. or_insert ( Vec :: new ( ) ) ;
1059
1114
if !prev. iter ( ) . any ( |c| c == summary) {
1060
1115
self . resolve_graph . push ( GraphNode :: Add ( id. clone ( ) ) ) ;
1116
+ if let Some ( link) = summary. links ( ) {
1117
+ ensure ! ( self . links. insert( link. to_owned( ) , id. clone( ) ) . is_none( ) ,
1118
+ "Attempting to resolve a with more then one crate with the links={}. \n \
1119
+ This will not build as is. Consider rebuilding the .lock file.", link) ;
1120
+ }
1061
1121
prev. push ( summary. clone ( ) ) ;
1062
- return false
1122
+ return Ok ( false )
1063
1123
}
1064
1124
debug ! ( "checking if {} is already activated" , summary. package_id( ) ) ;
1065
1125
let ( features, use_default) = match * method {
1066
1126
Method :: Required { features, uses_default_features, .. } => {
1067
1127
( features, uses_default_features)
1068
1128
}
1069
- Method :: Everything => return false ,
1129
+ Method :: Everything => return Ok ( false ) ,
1070
1130
} ;
1071
1131
1072
1132
let has_default_feature = summary. features ( ) . contains_key ( "default" ) ;
1073
- match self . resolve_features . get ( id) {
1133
+ Ok ( match self . resolve_features . get ( id) {
1074
1134
Some ( prev) => {
1075
1135
features. iter ( ) . all ( |f| prev. contains ( f) ) &&
1076
1136
( !use_default || prev. contains ( "default" ) ||
1077
1137
!has_default_feature)
1078
1138
}
1079
1139
None => features. is_empty ( ) && ( !use_default || !has_default_feature)
1080
- }
1140
+ } )
1081
1141
}
1082
1142
1083
1143
fn build_deps ( & mut self ,
@@ -1189,7 +1249,7 @@ impl<'a> Context<'a> {
1189
1249
. unwrap_or ( & [ ] )
1190
1250
}
1191
1251
1192
- fn is_active ( & mut self , id : & PackageId ) -> bool {
1252
+ fn is_active ( & self , id : & PackageId ) -> bool {
1193
1253
self . activations . get ( id. name ( ) )
1194
1254
. and_then ( |v| v. get ( id. source_id ( ) ) )
1195
1255
. map ( |v| v. iter ( ) . any ( |s| s. package_id ( ) == id) )
0 commit comments