@@ -455,6 +455,7 @@ fn activate(cx: &mut Context,
455
455
let deps = cx. build_deps ( registry, parent, & candidate, method) ?;
456
456
let frame = DepsFrame {
457
457
parent : candidate,
458
+ just_for_error_messages : false ,
458
459
remaining_siblings : RcVecIter :: new ( Rc :: new ( deps) ) ,
459
460
} ;
460
461
Ok ( Some ( ( frame, now. elapsed ( ) ) ) )
@@ -501,6 +502,7 @@ impl<T> Iterator for RcVecIter<T> where T: Clone {
501
502
#[ derive( Clone ) ]
502
503
struct DepsFrame {
503
504
parent : Summary ,
505
+ just_for_error_messages : bool ,
504
506
remaining_siblings : RcVecIter < DepInfo > ,
505
507
}
506
508
@@ -520,6 +522,7 @@ impl DepsFrame {
520
522
521
523
impl PartialEq for DepsFrame {
522
524
fn eq ( & self , other : & DepsFrame ) -> bool {
525
+ self . just_for_error_messages == other. just_for_error_messages &&
523
526
self . min_candidates ( ) == other. min_candidates ( )
524
527
}
525
528
}
@@ -534,14 +537,16 @@ impl PartialOrd for DepsFrame {
534
537
535
538
impl Ord for DepsFrame {
536
539
fn cmp ( & self , other : & DepsFrame ) -> Ordering {
537
- // the frame with the sibling that has the least number of candidates
538
- // needs to get the bubbled up to the top of the heap we use below, so
539
- // reverse the order of the comparison here.
540
- other. min_candidates ( ) . cmp ( & self . min_candidates ( ) )
540
+ self . just_for_error_messages . cmp ( & other. just_for_error_messages ) . then_with ( ||
541
+ // the frame with the sibling that has the least number of candidates
542
+ // needs to get bubbled up to the top of the heap we use below, so
543
+ // reverse comparison here.
544
+ self . min_candidates ( ) . cmp ( & other. min_candidates ( ) ) . reverse ( )
545
+ )
541
546
}
542
547
}
543
548
544
- #[ derive( Clone , PartialOrd , Ord , PartialEq , Eq ) ]
549
+ #[ derive( Debug , Clone , PartialOrd , Ord , PartialEq , Eq ) ]
545
550
enum ConflictReason {
546
551
Semver ,
547
552
Links ( String ) ,
@@ -763,7 +768,6 @@ impl RemainingCandidates {
763
768
. ok_or_else ( || self . conflicting_prev_active . clone ( ) )
764
769
}
765
770
}
766
-
767
771
/// Recursively activates the dependencies for `top`, in depth-first order,
768
772
/// backtracking across possible candidates for each dependency as necessary.
769
773
///
@@ -784,6 +788,18 @@ fn activate_deps_loop(
784
788
// use (those with more candidates).
785
789
let mut backtrack_stack = Vec :: new ( ) ;
786
790
let mut remaining_deps = BinaryHeap :: new ( ) ;
791
+ // `past_conflicting_activations`is a cache of the reasons for each time we backtrack.
792
+ // for example after several backtracks we may have:
793
+ // past_conflicting_activations[`foo = "^1.0.2"`] = vac![map!{`foo=1.0.1`: Semver}, map!{`foo=1.0.1`: Semver}];
794
+ // This can be read as "we cannot find a candidate for dep `foo = "^1.0.2"` if either
795
+ // `foo=1.0.1` OR `foo=1.0.0` are activated"
796
+ // for another example after several backtracks we may have:
797
+ // past_conflicting_activations[`foo = ">=0.8.2, <=0.9.3"`] = vac![map!{`foo=0.8.1`: Semver, `foo=0.9.4`: Semver}];
798
+ // This can be read as "we cannot find a candidate for dep `foo = ">=0.8.2, <=0.9.3"` if both
799
+ // `foo=0.8.1` AND `foo=0.9.4` are activated" (better data structures are welcome but this works for now.)
800
+ // This is used to make sure we don't queue work we know will fail.
801
+ // See the discussion in https://github.com/rust-lang/cargo/pull/5168 for why this is so important
802
+ let mut past_conflicting_activations: HashMap < Dependency , Vec < HashMap < PackageId , ConflictReason > > > = HashMap :: new ( ) ;
787
803
for & ( ref summary, ref method) in summaries {
788
804
debug ! ( "initial activation: {}" , summary. package_id( ) ) ;
789
805
let candidate = Candidate {
@@ -838,6 +854,8 @@ fn activate_deps_loop(
838
854
}
839
855
}
840
856
857
+ let just_here_for_the_error_messages = deps_frame. just_for_error_messages ;
858
+
841
859
let frame = match deps_frame. remaining_siblings . next ( ) {
842
860
Some ( sibling) => {
843
861
let parent = Summary :: clone ( & deps_frame. parent ) ;
@@ -852,9 +870,30 @@ fn activate_deps_loop(
852
870
trace ! ( "{}[{}]>{} {} candidates" , parent. name( ) , cur, dep. name( ) , candidates. len( ) ) ;
853
871
trace ! ( "{}[{}]>{} {} prev activations" , parent. name( ) , cur, dep. name( ) , cx. prev_active( & dep) . len( ) ) ;
854
872
873
+ let just_here_for_the_error_messages = just_here_for_the_error_messages
874
+ && past_conflicting_activations
875
+ . get ( & dep)
876
+ . and_then ( |past_bad| {
877
+ past_bad. iter ( ) . find ( |conflicting| {
878
+ conflicting
879
+ . iter ( )
880
+ // note: a lot of redundant work in is_active for similar debs
881
+ . all ( |( con, _) | cx. is_active ( con) )
882
+ } )
883
+ } )
884
+ . is_some ( ) ;
885
+
855
886
let mut remaining_candidates = RemainingCandidates :: new ( & candidates) ;
856
887
let mut successfully_activated = false ;
888
+ // `conflicting_activations` stores all the reasons we were unable to activate candidates.
889
+ // One of these reasons will have to go away for backtracking to find a place to restart.
890
+ // It is also the list of things to explain in the error message if we fail to resolve.
857
891
let mut conflicting_activations = HashMap :: new ( ) ;
892
+ // When backtracking we don't fully update `conflicting_activations` especially for the
893
+ // cases that we didn't make a backtrack frame in the first place.
894
+ // This `backtracked` var stores whether we are continuing from a restored backtrack frame
895
+ // so that we can skip caching `conflicting_activations` in `past_conflicting_activations`
896
+ let mut backtracked = false ;
858
897
859
898
while !successfully_activated {
860
899
let next = remaining_candidates. next ( cx. prev_active ( & dep) , & cx. links ) ;
@@ -875,20 +914,37 @@ fn activate_deps_loop(
875
914
conflicting_activations. extend ( conflicting) ;
876
915
// This dependency has no valid candidate. Backtrack until we
877
916
// find a dependency that does have a candidate to try, and try
878
- // to activate that one. This resets the `remaining_deps` to
879
- // their state at the found level of the `backtrack_stack`.
917
+ // to activate that one.
880
918
trace ! ( "{}[{}]>{} -- no candidates" , parent. name( ) , cur, dep. name( ) ) ;
919
+
920
+ if !just_here_for_the_error_messages && !backtracked {
921
+ // if `just_here_for_the_error_messages` then skip as it is already known to be bad.
922
+ // if `backtracked` then `conflicting_activations` may not be complete so skip.
923
+ let past = past_conflicting_activations. entry ( dep. clone ( ) ) . or_insert_with ( Vec :: new) ;
924
+ if !past. contains ( & conflicting_activations) {
925
+ trace ! ( "{}[{}]>{} adding a skip {:?}" , parent. name( ) , cur, dep. name( ) , conflicting_activations) ;
926
+ past. push ( conflicting_activations. clone ( ) ) ;
927
+ }
928
+ }
929
+
881
930
find_candidate (
882
931
& mut backtrack_stack,
883
- & mut cx,
884
- & mut remaining_deps,
885
- & mut parent,
886
- & mut cur,
887
- & mut dep,
888
- & mut features,
889
- & mut remaining_candidates,
890
- & mut conflicting_activations,
891
- ) . ok_or_else ( || {
932
+ & parent,
933
+ & conflicting_activations,
934
+ ) . map ( |( candidate, has_another, frame) | {
935
+ // This resets the `remaining_deps` to
936
+ // their state at the found level of the `backtrack_stack`.
937
+ cur = frame. cur ;
938
+ cx = frame. context_backup ;
939
+ remaining_deps = frame. deps_backup ;
940
+ remaining_candidates = frame. remaining_candidates ;
941
+ parent = frame. parent ;
942
+ dep = frame. dep ;
943
+ features = frame. features ;
944
+ conflicting_activations = frame. conflicting_activations ;
945
+ backtracked = true ;
946
+ ( candidate, has_another)
947
+ } ) . ok_or_else ( || {
892
948
activation_error (
893
949
& cx,
894
950
registry. registry ,
@@ -901,6 +957,10 @@ fn activate_deps_loop(
901
957
} )
902
958
} ) ?;
903
959
960
+ if just_here_for_the_error_messages && !backtracked && has_another {
961
+ continue
962
+ }
963
+
904
964
// We have a candidate. Clone a `BacktrackFrame`
905
965
// so we can add it to the `backtrack_stack` if activation succeeds.
906
966
// We clone now in case `activate` changes `cx` and then fails.
@@ -919,6 +979,7 @@ fn activate_deps_loop(
919
979
None
920
980
} ;
921
981
982
+ let pid = candidate. summary . package_id ( ) . clone ( ) ;
922
983
let method = Method :: Required {
923
984
dev_deps : false ,
924
985
features : & features,
@@ -930,8 +991,50 @@ fn activate_deps_loop(
930
991
successfully_activated = res. is_ok ( ) ;
931
992
932
993
match res {
933
- Ok ( Some ( ( frame, dur) ) ) => {
934
- remaining_deps. push ( frame) ;
994
+ Ok ( Some ( ( mut frame, dur) ) ) => {
995
+ // at this point we have technical already activated
996
+ // but we may want to scrap it if it is not going to end well
997
+ let mut has_past_conflicting_dep = just_here_for_the_error_messages;
998
+ if !has_past_conflicting_dep {
999
+ if let Some ( conflicting) = frame. remaining_siblings . clone ( ) . filter_map ( |( _, ( deb, _, _) ) | {
1000
+ past_conflicting_activations. get ( & deb) . and_then ( |past_bad| {
1001
+ // for each dependency check all of its cashed conflicts
1002
+ past_bad. iter ( ) . find ( |conflicting| {
1003
+ conflicting
1004
+ . iter ( )
1005
+ // note: a lot of redundant work in is_active for similar debs
1006
+ . all ( |( con, _) | cx. is_active ( con) )
1007
+ } )
1008
+ } )
1009
+ } ) . next ( ) {
1010
+ // if any of them match than it will just backtrack to us
1011
+ // so let's save the effort.
1012
+ conflicting_activations. extend ( conflicting. clone ( ) ) ;
1013
+ has_past_conflicting_dep = true ;
1014
+ }
1015
+ }
1016
+ if !has_another && has_past_conflicting_dep && !backtracked {
1017
+ // we have not activated ANY candidates and
1018
+ // we are out of choices so add it to the cache
1019
+ // so our parent will know that we don't work
1020
+ let past = past_conflicting_activations. entry ( dep. clone ( ) ) . or_insert_with ( Vec :: new) ;
1021
+ if !past. contains ( & conflicting_activations) {
1022
+ trace ! ( "{}[{}]>{} adding a meta-skip {:?}" , parent. name( ) , cur, dep. name( ) , conflicting_activations) ;
1023
+ past. push ( conflicting_activations. clone ( ) ) ;
1024
+ }
1025
+ }
1026
+ // if not has_another we we activate for the better error messages
1027
+ frame. just_for_error_messages = has_past_conflicting_dep;
1028
+ if !has_past_conflicting_dep || ( !has_another && ( just_here_for_the_error_messages || find_candidate (
1029
+ & mut backtrack_stack. clone ( ) ,
1030
+ & parent,
1031
+ & conflicting_activations,
1032
+ ) . is_none ( ) ) ) {
1033
+ remaining_deps. push ( frame) ;
1034
+ } else {
1035
+ trace ! ( "{}[{}]>{} skipping {} " , parent. name( ) , cur, dep. name( ) , pid. version( ) ) ;
1036
+ successfully_activated = false ;
1037
+ }
935
1038
deps_time += dur;
936
1039
}
937
1040
Ok ( None ) => ( ) ,
@@ -968,17 +1071,11 @@ fn activate_deps_loop(
968
1071
/// If the outcome could differ, resets `cx` and `remaining_deps` to that
969
1072
/// level and returns the next candidate.
970
1073
/// If all candidates have been exhausted, returns None.
971
- fn find_candidate (
1074
+ fn find_candidate < ' a > (
972
1075
backtrack_stack : & mut Vec < BacktrackFrame > ,
973
- cx : & mut Context ,
974
- remaining_deps : & mut BinaryHeap < DepsFrame > ,
975
- parent : & mut Summary ,
976
- cur : & mut usize ,
977
- dep : & mut Dependency ,
978
- features : & mut Rc < Vec < String > > ,
979
- remaining_candidates : & mut RemainingCandidates ,
980
- conflicting_activations : & mut HashMap < PackageId , ConflictReason > ,
981
- ) -> Option < ( Candidate , bool ) > {
1076
+ parent : & Summary ,
1077
+ conflicting_activations : & HashMap < PackageId , ConflictReason > ,
1078
+ ) -> Option < ( Candidate , bool , BacktrackFrame ) > {
982
1079
while let Some ( mut frame) = backtrack_stack. pop ( ) {
983
1080
let next= frame. remaining_candidates . next ( frame. context_backup . prev_active ( & frame. dep ) , & frame. context_backup . links ) ;
984
1081
if frame. context_backup . is_active ( parent. package_id ( ) )
@@ -990,15 +1087,7 @@ fn find_candidate(
990
1087
continue ;
991
1088
}
992
1089
if let Ok ( ( candidate, has_another) ) = next {
993
- * cur = frame. cur ;
994
- * cx = frame. context_backup ;
995
- * remaining_deps = frame. deps_backup ;
996
- * parent = frame. parent ;
997
- * dep = frame. dep ;
998
- * features = frame. features ;
999
- * remaining_candidates = frame. remaining_candidates ;
1000
- * conflicting_activations = frame. conflicting_activations ;
1001
- return Some ( ( candidate, has_another) ) ;
1090
+ return Some ( ( candidate, has_another, frame) ) ;
1002
1091
}
1003
1092
}
1004
1093
None
0 commit comments