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
struct BacktrackFrame < ' a > {
532
551
cur : usize ,
533
552
context_backup : Context < ' a > ,
@@ -542,26 +561,35 @@ struct BacktrackFrame<'a> {
542
561
struct RemainingCandidates {
543
562
remaining : RcVecIter < Candidate > ,
544
563
// note: change to RcList or something if clone is to expensive
545
- conflicting_prev_active : HashSet < PackageId > ,
564
+ conflicting_prev_active : HashMap < PackageId , ConflictReason > ,
546
565
}
547
566
548
567
impl RemainingCandidates {
549
- fn next ( & mut self , prev_active : & [ Summary ] ) -> Result < Candidate , HashSet < PackageId > > {
568
+ fn next ( & mut self , prev_active : & [ Summary ] , links : & HashMap < String , PackageId > ) -> Result < Candidate , HashMap < PackageId , ConflictReason > > {
550
569
// Filter the set of candidates based on the previously activated
551
570
// versions for this dependency. We can actually use a version if it
552
571
// precisely matches an activated version or if it is otherwise
553
572
// incompatible with all other activated versions. Note that we
554
573
// define "compatible" here in terms of the semver sense where if
555
574
// the left-most nonzero digit is the same they're considered
556
- // compatible.
575
+ // compatible unless we have a `*-sys` crate (defined by having a
576
+ // linked attribute) then we can only have one version.
557
577
//
558
578
// When we are done we return the set of previously activated
559
579
// that conflicted with the ones we tried. If any of these change
560
580
// then we would have considered different candidates.
561
581
for ( _, b) in self . remaining . by_ref ( ) {
582
+ if let Some ( link) = b. summary . links ( ) {
583
+ if let Some ( a) = links. get ( link) {
584
+ if a != b. summary . package_id ( ) {
585
+ self . conflicting_prev_active . insert ( a. clone ( ) , ConflictReason :: Links ( link. to_owned ( ) ) ) ;
586
+ continue
587
+ }
588
+ }
589
+ }
562
590
if let Some ( a) = prev_active. iter ( ) . find ( |a| compatible ( a. version ( ) , b. summary . version ( ) ) ) {
563
591
if * a != b. summary {
564
- self . conflicting_prev_active . insert ( a. package_id ( ) . clone ( ) ) ;
592
+ self . conflicting_prev_active . insert ( a. package_id ( ) . clone ( ) , ConflictReason :: Semver ) ;
565
593
continue
566
594
}
567
595
}
@@ -660,10 +688,10 @@ fn activate_deps_loop<'a>(mut cx: Context<'a>,
660
688
dep. name( ) , prev_active. len( ) ) ;
661
689
let mut candidates = RemainingCandidates {
662
690
remaining : RcVecIter :: new ( Rc :: clone ( & candidates) ) ,
663
- conflicting_prev_active : HashSet :: new ( ) ,
691
+ conflicting_prev_active : HashMap :: new ( ) ,
664
692
} ;
665
- ( candidates. next ( prev_active) ,
666
- candidates. clone ( ) . next ( prev_active) . is_ok ( ) ,
693
+ ( candidates. next ( prev_active, & cx . links ) ,
694
+ candidates. clone ( ) . next ( prev_active, & cx . links ) . is_ok ( ) ,
667
695
candidates)
668
696
} ;
669
697
@@ -673,7 +701,7 @@ fn activate_deps_loop<'a>(mut cx: Context<'a>,
673
701
// 1. The version matches the dependency requirement listed for this
674
702
// package
675
703
// 2. There are no activated versions for this package which are
676
- // semver-compatible, or there's an activated version which is
704
+ // semver/links -compatible, or there's an activated version which is
677
705
// precisely equal to `candidate`.
678
706
//
679
707
// This means that we're going to attempt to activate each candidate in
@@ -688,7 +716,7 @@ fn activate_deps_loop<'a>(mut cx: Context<'a>,
688
716
cur,
689
717
context_backup : Context :: clone ( & cx) ,
690
718
deps_backup : <BinaryHeap < DepsFrame > >:: clone ( & remaining_deps) ,
691
- remaining_candidates : remaining_candidates ,
719
+ remaining_candidates,
692
720
parent : Summary :: clone ( & parent) ,
693
721
dep : Dependency :: clone ( & dep) ,
694
722
features : Rc :: clone ( & features) ,
@@ -756,21 +784,21 @@ fn find_candidate<'a>(
756
784
cur : & mut usize ,
757
785
dep : & mut Dependency ,
758
786
features : & mut Rc < Vec < String > > ,
759
- conflicting_activations : & mut HashSet < PackageId > ,
787
+ conflicting_activations : & mut HashMap < PackageId , ConflictReason > ,
760
788
) -> Option < Candidate > {
761
789
while let Some ( mut frame) = backtrack_stack. pop ( ) {
762
790
let ( next, has_another) = {
763
791
let prev_active = frame. context_backup . prev_active ( & frame. dep ) ;
764
792
(
765
- frame. remaining_candidates . next ( prev_active) ,
766
- frame. remaining_candidates . clone ( ) . next ( prev_active) . is_ok ( ) ,
793
+ frame. remaining_candidates . next ( prev_active, & frame . context_backup . links ) ,
794
+ frame. remaining_candidates . clone ( ) . next ( prev_active, & frame . context_backup . links ) . is_ok ( ) ,
767
795
)
768
796
} ;
769
797
if frame. context_backup . is_active ( parent. package_id ( ) )
770
798
&& conflicting_activations
771
799
. iter ( )
772
800
// note: a lot of redundant work in is_active for similar debs
773
- . all ( |con| frame. context_backup . is_active ( con) )
801
+ . all ( |( con, _ ) | frame. context_backup . is_active ( con) )
774
802
{
775
803
continue ;
776
804
}
@@ -800,7 +828,7 @@ fn activation_error(cx: &Context,
800
828
registry : & mut Registry ,
801
829
parent : & Summary ,
802
830
dep : & Dependency ,
803
- conflicting_activations : HashSet < PackageId > ,
831
+ conflicting_activations : HashMap < PackageId , ConflictReason > ,
804
832
candidates : & [ Candidate ] ,
805
833
config : Option < & Config > ) -> CargoError {
806
834
let graph = cx. graph ( ) ;
@@ -816,26 +844,53 @@ fn activation_error(cx: &Context,
816
844
dep_path_desc
817
845
} ;
818
846
if !candidates. is_empty ( ) {
819
- let mut msg = format ! ( "failed to select a version for `{}`.\n \
820
- all possible versions conflict with \
821
- previously selected packages.\n ",
822
- dep. name( ) ) ;
823
- msg. push_str ( "required by " ) ;
847
+ let mut msg = format ! ( "failed to select a version for `{}`." , dep. name( ) ) ;
848
+ msg. push_str ( "\n ... required by " ) ;
824
849
msg. push_str ( & describe_path ( parent. package_id ( ) ) ) ;
825
- let mut conflicting_activations: Vec < _ > = conflicting_activations. iter ( ) . collect ( ) ;
826
- conflicting_activations. sort_unstable ( ) ;
827
- for v in conflicting_activations. iter ( ) . rev ( ) {
828
- msg. push_str ( "\n previously selected " ) ;
829
- msg. push_str ( & describe_path ( v) ) ;
830
- }
831
850
832
- msg. push_str ( "\n possible versions to select: " ) ;
851
+ msg. push_str ( "\n versions that meet the requirements `" ) ;
852
+ msg. push_str ( & dep. version_req ( ) . to_string ( ) ) ;
853
+ msg. push_str ( "` are: " ) ;
833
854
msg. push_str ( & candidates. iter ( )
834
855
. map ( |v| v. summary . version ( ) )
835
856
. map ( |v| v. to_string ( ) )
836
857
. collect :: < Vec < _ > > ( )
837
858
. join ( ", " ) ) ;
838
859
860
+ let mut conflicting_activations: Vec < _ > = conflicting_activations. iter ( ) . collect ( ) ;
861
+ conflicting_activations. sort_unstable ( ) ;
862
+ let ( links_errors, other_errors) : ( Vec < _ > , Vec < _ > ) = conflicting_activations. drain ( ..) . rev ( ) . partition ( |& ( _, r) | r. is_links ( ) ) ;
863
+
864
+ for & ( p, r) in & links_errors {
865
+ match r {
866
+ & ConflictReason :: Links ( ref link) => {
867
+ msg. push_str ( "\n \n the package `" ) ;
868
+ msg. push_str ( dep. name ( ) ) ;
869
+ msg. push_str ( "` links to the native library `" ) ;
870
+ msg. push_str ( & link) ;
871
+ msg. push_str ( "`, but it conflicts with a previous package which links to `" ) ;
872
+ msg. push_str ( & link) ;
873
+ msg. push_str ( "` as well:\n " ) ;
874
+ } ,
875
+ _ => ( ) ,
876
+ }
877
+ msg. push_str ( & describe_path ( p) ) ;
878
+ }
879
+
880
+ if links_errors. is_empty ( ) {
881
+ msg. push_str ( "\n \n all possible versions conflict with \
882
+ previously selected packages.") ;
883
+ }
884
+
885
+ for & ( p, _) in & other_errors {
886
+ msg. push_str ( "\n \n previously selected " ) ;
887
+ msg. push_str ( & describe_path ( p) ) ;
888
+ }
889
+
890
+ msg. push_str ( "\n \n failed to select a version for `" ) ;
891
+ msg. push_str ( dep. name ( ) ) ;
892
+ msg. push_str ( "` which could resolve this conflict" ) ;
893
+
839
894
return format_err ! ( "{}" , msg)
840
895
}
841
896
@@ -1046,12 +1101,12 @@ fn build_requirements<'a, 'b: 'a>(s: &'a Summary, method: &'b Method)
1046
1101
}
1047
1102
1048
1103
impl < ' a > Context < ' a > {
1049
- // Activate this summary by inserting it into our list of known activations.
1050
- //
1051
- // Returns if this summary with the given method is already activated.
1104
+ /// Activate this summary by inserting it into our list of known activations.
1105
+ ///
1106
+ /// Returns true if this summary with the given method is already activated.
1052
1107
fn flag_activated ( & mut self ,
1053
1108
summary : & Summary ,
1054
- method : & Method ) -> bool {
1109
+ method : & Method ) -> CargoResult < bool > {
1055
1110
let id = summary. package_id ( ) ;
1056
1111
let prev = self . activations
1057
1112
. entry ( id. name ( ) . to_string ( ) )
@@ -1060,26 +1115,31 @@ impl<'a> Context<'a> {
1060
1115
. or_insert ( Vec :: new ( ) ) ;
1061
1116
if !prev. iter ( ) . any ( |c| c == summary) {
1062
1117
self . resolve_graph . push ( GraphNode :: Add ( id. clone ( ) ) ) ;
1118
+ if let Some ( link) = summary. links ( ) {
1119
+ ensure ! ( self . links. insert( link. to_owned( ) , id. clone( ) ) . is_none( ) ,
1120
+ "Attempting to resolve a with more then one crate with the links={}. \n \
1121
+ This will not build as is. Consider rebuilding the .lock file.", link) ;
1122
+ }
1063
1123
prev. push ( summary. clone ( ) ) ;
1064
- return false
1124
+ return Ok ( false )
1065
1125
}
1066
1126
debug ! ( "checking if {} is already activated" , summary. package_id( ) ) ;
1067
1127
let ( features, use_default) = match * method {
1068
1128
Method :: Required { features, uses_default_features, .. } => {
1069
1129
( features, uses_default_features)
1070
1130
}
1071
- Method :: Everything => return false ,
1131
+ Method :: Everything => return Ok ( false ) ,
1072
1132
} ;
1073
1133
1074
1134
let has_default_feature = summary. features ( ) . contains_key ( "default" ) ;
1075
- match self . resolve_features . get ( id) {
1135
+ Ok ( match self . resolve_features . get ( id) {
1076
1136
Some ( prev) => {
1077
1137
features. iter ( ) . all ( |f| prev. contains ( f) ) &&
1078
1138
( !use_default || prev. contains ( "default" ) ||
1079
1139
!has_default_feature)
1080
1140
}
1081
1141
None => features. is_empty ( ) && ( !use_default || !has_default_feature)
1082
- }
1142
+ } )
1083
1143
}
1084
1144
1085
1145
fn build_deps ( & mut self ,
@@ -1191,7 +1251,7 @@ impl<'a> Context<'a> {
1191
1251
. unwrap_or ( & [ ] )
1192
1252
}
1193
1253
1194
- fn is_active ( & mut self , id : & PackageId ) -> bool {
1254
+ fn is_active ( & self , id : & PackageId ) -> bool {
1195
1255
self . activations . get ( id. name ( ) )
1196
1256
. and_then ( |v| v. get ( id. source_id ( ) ) )
1197
1257
. map ( |v| v. iter ( ) . any ( |s| s. package_id ( ) == id) )
0 commit comments