@@ -455,6 +455,7 @@ fn activate(cx: &mut Context,
455455 let deps = cx. build_deps ( registry, parent, & candidate, method) ?;
456456 let frame = DepsFrame {
457457 parent : candidate,
458+ just_for_error_messages : false ,
458459 remaining_siblings : RcVecIter :: new ( Rc :: new ( deps) ) ,
459460 } ;
460461 Ok ( Some ( ( frame, now. elapsed ( ) ) ) )
@@ -501,6 +502,7 @@ impl<T> Iterator for RcVecIter<T> where T: Clone {
501502#[ derive( Clone ) ]
502503struct DepsFrame {
503504 parent : Summary ,
505+ just_for_error_messages : bool ,
504506 remaining_siblings : RcVecIter < DepInfo > ,
505507}
506508
@@ -520,6 +522,7 @@ impl DepsFrame {
520522
521523impl PartialEq for DepsFrame {
522524 fn eq ( & self , other : & DepsFrame ) -> bool {
525+ self . just_for_error_messages == other. just_for_error_messages &&
523526 self . min_candidates ( ) == other. min_candidates ( )
524527 }
525528}
@@ -534,14 +537,16 @@ impl PartialOrd for DepsFrame {
534537
535538impl Ord for DepsFrame {
536539 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+ )
541546 }
542547}
543548
544- #[ derive( Clone , PartialOrd , Ord , PartialEq , Eq ) ]
549+ #[ derive( Debug , Clone , PartialOrd , Ord , PartialEq , Eq ) ]
545550enum ConflictReason {
546551 Semver ,
547552 Links ( String ) ,
@@ -763,7 +768,6 @@ impl RemainingCandidates {
763768 . ok_or_else ( || self . conflicting_prev_active . clone ( ) )
764769 }
765770}
766-
767771/// Recursively activates the dependencies for `top`, in depth-first order,
768772/// backtracking across possible candidates for each dependency as necessary.
769773///
@@ -784,6 +788,18 @@ fn activate_deps_loop(
784788 // use (those with more candidates).
785789 let mut backtrack_stack = Vec :: new ( ) ;
786790 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 ( ) ;
787803 for & ( ref summary, ref method) in summaries {
788804 debug ! ( "initial activation: {}" , summary. package_id( ) ) ;
789805 let candidate = Candidate {
@@ -838,6 +854,8 @@ fn activate_deps_loop(
838854 }
839855 }
840856
857+ let just_here_for_the_error_messages = deps_frame. just_for_error_messages ;
858+
841859 let frame = match deps_frame. remaining_siblings . next ( ) {
842860 Some ( sibling) => {
843861 let parent = Summary :: clone ( & deps_frame. parent ) ;
@@ -852,9 +870,30 @@ fn activate_deps_loop(
852870 trace ! ( "{}[{}]>{} {} candidates" , parent. name( ) , cur, dep. name( ) , candidates. len( ) ) ;
853871 trace ! ( "{}[{}]>{} {} prev activations" , parent. name( ) , cur, dep. name( ) , cx. prev_active( & dep) . len( ) ) ;
854872
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+
855886 let mut remaining_candidates = RemainingCandidates :: new ( & candidates) ;
856887 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.
857891 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 ;
858897
859898 while !successfully_activated {
860899 let next = remaining_candidates. next ( cx. prev_active ( & dep) , & cx. links ) ;
@@ -875,20 +914,37 @@ fn activate_deps_loop(
875914 conflicting_activations. extend ( conflicting) ;
876915 // This dependency has no valid candidate. Backtrack until we
877916 // 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.
880918 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+
881930 find_candidate (
882931 & 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 ( || {
892948 activation_error (
893949 & cx,
894950 registry. registry ,
@@ -901,6 +957,10 @@ fn activate_deps_loop(
901957 } )
902958 } ) ?;
903959
960+ if just_here_for_the_error_messages && !backtracked && has_another {
961+ continue
962+ }
963+
904964 // We have a candidate. Clone a `BacktrackFrame`
905965 // so we can add it to the `backtrack_stack` if activation succeeds.
906966 // We clone now in case `activate` changes `cx` and then fails.
@@ -919,6 +979,7 @@ fn activate_deps_loop(
919979 None
920980 } ;
921981
982+ let pid = candidate. summary . package_id ( ) . clone ( ) ;
922983 let method = Method :: Required {
923984 dev_deps : false ,
924985 features : & features,
@@ -930,8 +991,50 @@ fn activate_deps_loop(
930991 successfully_activated = res. is_ok ( ) ;
931992
932993 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+ }
9351038 deps_time += dur;
9361039 }
9371040 Ok ( None ) => ( ) ,
@@ -968,17 +1071,11 @@ fn activate_deps_loop(
9681071/// If the outcome could differ, resets `cx` and `remaining_deps` to that
9691072/// level and returns the next candidate.
9701073/// If all candidates have been exhausted, returns None.
971- fn find_candidate (
1074+ fn find_candidate < ' a > (
9721075 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 ) > {
9821079 while let Some ( mut frame) = backtrack_stack. pop ( ) {
9831080 let next= frame. remaining_candidates . next ( frame. context_backup . prev_active ( & frame. dep ) , & frame. context_backup . links ) ;
9841081 if frame. context_backup . is_active ( parent. package_id ( ) )
@@ -990,15 +1087,7 @@ fn find_candidate(
9901087 continue ;
9911088 }
9921089 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) ) ;
10021091 }
10031092 }
10041093 None
0 commit comments