46
46
//! that we're implementing something that probably shouldn't be allocating all
47
47
//! over the place.
48
48
49
- use std:: collections :: HashSet ;
50
- use std:: collections:: hash_map :: HashMap ;
49
+ use std:: cmp :: Ordering ;
50
+ use std:: collections:: { HashSet , HashMap , BinaryHeap } ;
51
51
use std:: fmt;
52
52
use std:: ops:: Range ;
53
53
use std:: rc:: Rc ;
54
+
54
55
use semver;
55
56
56
57
use core:: { PackageId , Registry , SourceId , Summary , Dependency } ;
@@ -142,7 +143,6 @@ impl fmt::Debug for Resolve {
142
143
struct Context {
143
144
activations : HashMap < ( String , SourceId ) , Vec < Rc < Summary > > > ,
144
145
resolve : Resolve ,
145
- visited : HashSet < PackageId > ,
146
146
}
147
147
148
148
/// Builds the list of all packages required to build the first argument.
@@ -154,10 +154,11 @@ pub fn resolve(summary: &Summary, method: &Method,
154
154
let cx = Context {
155
155
resolve : Resolve :: new ( summary. package_id ( ) . clone ( ) ) ,
156
156
activations : HashMap :: new ( ) ,
157
- visited : HashSet :: new ( ) ,
158
157
} ;
159
158
let _p = profile:: start ( format ! ( "resolving: {}" , summary. package_id( ) ) ) ;
160
- activate_deps_loop ( cx, registry, summary, method)
159
+ let cx = try!( activate_deps_loop ( cx, registry, summary, method) ) ;
160
+ try!( check_cycles ( & cx) ) ;
161
+ Ok ( cx. resolve )
161
162
}
162
163
163
164
/// Attempts to activate the summary `parent` in the context `cx`.
@@ -174,13 +175,9 @@ fn activate(cx: &mut Context,
174
175
// Dependency graphs are required to be a DAG, so we keep a set of
175
176
// packages we're visiting and bail if we hit a dupe.
176
177
let id = parent. package_id ( ) . clone ( ) ;
177
- if !cx. visited . insert ( id. clone ( ) ) {
178
- bail ! ( "cyclic package dependency: package `{}` depends on itself" , id)
179
- }
180
178
181
179
// If we're already activated, then that was easy!
182
180
if cx. flag_activated ( & parent, method) {
183
- cx. visited . remove ( & id) ;
184
181
return Ok ( None ) ;
185
182
}
186
183
trace ! ( "activating {}" , parent. package_id( ) ) ;
@@ -207,17 +204,26 @@ impl<T> RcVecIter<T> {
207
204
vec : Rc :: new ( vec) ,
208
205
}
209
206
}
207
+
210
208
fn cur_index ( & self ) -> usize {
211
209
self . rest . start - 1
212
210
}
211
+
212
+ fn as_slice ( & self ) -> & [ T ] {
213
+ & self . vec [ self . rest . start ..self . rest . end ]
214
+ }
213
215
}
216
+
214
217
impl < T > Iterator for RcVecIter < T > where T : Clone {
215
218
type Item = ( usize , T ) ;
216
219
fn next ( & mut self ) -> Option < ( usize , T ) > {
217
220
self . rest . next ( ) . and_then ( |i| {
218
221
self . vec . get ( i) . map ( |val| ( i, val. clone ( ) ) )
219
222
} )
220
223
}
224
+ fn size_hint ( & self ) -> ( usize , Option < usize > ) {
225
+ self . rest . size_hint ( )
226
+ }
221
227
}
222
228
223
229
#[ derive( Clone ) ]
@@ -227,9 +233,46 @@ struct DepsFrame {
227
233
id : PackageId ,
228
234
}
229
235
236
+ impl DepsFrame {
237
+ /// Returns the least number of candidates that any of this frame's siblings
238
+ /// has.
239
+ ///
240
+ /// The `remaining_siblings` array is already sorted with the smallest
241
+ /// number of candidates at the front, so we just return the number of
242
+ /// candidates in that entry.
243
+ fn min_candidates ( & self ) -> usize {
244
+ self . remaining_siblings . as_slice ( ) . get ( 0 ) . map ( |& ( _, ref candidates, _) | {
245
+ candidates. len ( )
246
+ } ) . unwrap_or ( 0 )
247
+ }
248
+ }
249
+
250
+ impl PartialEq for DepsFrame {
251
+ fn eq ( & self , other : & DepsFrame ) -> bool {
252
+ self . min_candidates ( ) == other. min_candidates ( )
253
+ }
254
+ }
255
+
256
+ impl Eq for DepsFrame { }
257
+
258
+ impl PartialOrd for DepsFrame {
259
+ fn partial_cmp ( & self , other : & DepsFrame ) -> Option < Ordering > {
260
+ Some ( self . cmp ( other) )
261
+ }
262
+ }
263
+
264
+ impl Ord for DepsFrame {
265
+ fn cmp ( & self , other : & DepsFrame ) -> Ordering {
266
+ // the frame with the sibling that has the least number of candidates
267
+ // needs to get the bubbled up to the top of the heap we use below, so
268
+ // reverse the order of the comparison here.
269
+ other. min_candidates ( ) . cmp ( & self . min_candidates ( ) )
270
+ }
271
+ }
272
+
230
273
struct BacktrackFrame {
231
274
context_backup : Context ,
232
- deps_backup : Vec < DepsFrame > ,
275
+ deps_backup : BinaryHeap < DepsFrame > ,
233
276
remaining_candidates : RcVecIter < Rc < Summary > > ,
234
277
parent : Rc < Summary > ,
235
278
dep : Dependency ,
@@ -243,9 +286,16 @@ struct BacktrackFrame {
243
286
fn activate_deps_loop ( mut cx : Context ,
244
287
registry : & mut Registry ,
245
288
top : Rc < Summary > ,
246
- top_method : & Method ) -> CargoResult < Resolve > {
289
+ top_method : & Method ) -> CargoResult < Context > {
290
+ // Note that a `BinaryHeap` is used for the remaining dependencies that need
291
+ // activation. This heap is sorted such that the "largest value" is the most
292
+ // constrained dependency, or the one with the least candidates.
293
+ //
294
+ // This helps us get through super constrained portions of the dependency
295
+ // graph quickly and hopefully lock down what later larger dependencies can
296
+ // use (those with more candidates).
247
297
let mut backtrack_stack = Vec :: new ( ) ;
248
- let mut remaining_deps = Vec :: new ( ) ;
298
+ let mut remaining_deps = BinaryHeap :: new ( ) ;
249
299
remaining_deps. extend ( try!( activate ( & mut cx, registry, top, & top_method) ) ) ;
250
300
251
301
// Main resolution loop, this is the workhorse of the resolution algorithm.
@@ -268,10 +318,7 @@ fn activate_deps_loop(mut cx: Context,
268
318
remaining_deps. push ( deps_frame) ;
269
319
( parent, sibling)
270
320
}
271
- None => {
272
- cx. visited . remove ( & deps_frame. id ) ;
273
- continue
274
- }
321
+ None => continue ,
275
322
} ;
276
323
let ( mut parent, ( mut cur, ( mut dep, candidates, features) ) ) = frame;
277
324
assert ! ( !remaining_deps. is_empty( ) ) ;
@@ -353,32 +400,28 @@ fn activate_deps_loop(mut cx: Context,
353
400
candidate. version( ) ) ;
354
401
cx. resolve . graph . link ( parent. package_id ( ) . clone ( ) ,
355
402
candidate. package_id ( ) . clone ( ) ) ;
356
-
357
- // If we hit an intransitive dependency then clear out the visitation
358
- // list as we can't induce a cycle through transitive dependencies.
359
- if !dep. is_transitive ( ) {
360
- cx. visited . clear ( ) ;
361
- }
362
403
remaining_deps. extend ( try!( activate ( & mut cx, registry,
363
404
candidate, & method) ) ) ;
364
405
}
365
406
trace ! ( "resolved: {:?}" , cx. resolve) ;
366
- Ok ( cx. resolve )
407
+ Ok ( cx)
367
408
}
368
409
369
410
// Searches up `backtrack_stack` until it finds a dependency with remaining
370
411
// candidates. Resets `cx` and `remaining_deps` to that level and returns the
371
412
// next candidate. If all candidates have been exhausted, returns None.
372
413
fn find_candidate ( backtrack_stack : & mut Vec < BacktrackFrame > ,
373
- cx : & mut Context , remaining_deps : & mut Vec < DepsFrame > ,
374
- parent : & mut Rc < Summary > , cur : & mut usize ,
414
+ cx : & mut Context ,
415
+ remaining_deps : & mut BinaryHeap < DepsFrame > ,
416
+ parent : & mut Rc < Summary > ,
417
+ cur : & mut usize ,
375
418
dep : & mut Dependency ) -> Option < Rc < Summary > > {
376
419
while let Some ( mut frame) = backtrack_stack. pop ( ) {
377
420
if let Some ( ( _, candidate) ) = frame. remaining_candidates . next ( ) {
378
421
* cx = frame. context_backup . clone ( ) ;
379
422
* remaining_deps = frame. deps_backup . clone ( ) ;
380
423
* parent = frame. parent . clone ( ) ;
381
- * cur = remaining_deps. last ( ) . unwrap ( ) . remaining_siblings . cur_index ( ) ;
424
+ * cur = remaining_deps. peek ( ) . unwrap ( ) . remaining_siblings . cur_index ( ) ;
382
425
* dep = frame. dep . clone ( ) ;
383
426
backtrack_stack. push ( frame) ;
384
427
return Some ( candidate)
@@ -705,3 +748,51 @@ impl Context {
705
748
Ok ( ret)
706
749
}
707
750
}
751
+
752
+ fn check_cycles ( cx : & Context ) -> CargoResult < ( ) > {
753
+ let mut summaries = HashMap :: new ( ) ;
754
+ for summary in cx. activations . values ( ) . flat_map ( |v| v) {
755
+ summaries. insert ( summary. package_id ( ) , & * * summary) ;
756
+ }
757
+ return visit ( & cx. resolve ,
758
+ cx. resolve . root ( ) ,
759
+ & summaries,
760
+ & mut HashSet :: new ( ) ,
761
+ & mut HashSet :: new ( ) ) ;
762
+
763
+ fn visit < ' a > ( resolve : & ' a Resolve ,
764
+ id : & ' a PackageId ,
765
+ summaries : & HashMap < & ' a PackageId , & Summary > ,
766
+ visited : & mut HashSet < & ' a PackageId > ,
767
+ checked : & mut HashSet < & ' a PackageId > )
768
+ -> CargoResult < ( ) > {
769
+ // See if we visited ourselves
770
+ if !visited. insert ( id) {
771
+ bail ! ( "cyclic package dependency: package `{}` depends on itself" ,
772
+ id) ;
773
+ }
774
+
775
+ // If we've already checked this node no need to recurse again as we'll
776
+ // just conclude the same thing as last time, so we only execute the
777
+ // recursive step if we successfully insert into `checked`.
778
+ //
779
+ // Note that if we hit an intransitive dependency then we clear out the
780
+ // visitation list as we can't induce a cycle through transitive
781
+ // dependencies.
782
+ if checked. insert ( id) {
783
+ let summary = summaries[ id] ;
784
+ for dep in resolve. deps ( id) . into_iter ( ) . flat_map ( |a| a) {
785
+ let is_transitive = summary. dependencies ( ) . iter ( ) . any ( |d| {
786
+ d. matches_id ( dep) && d. is_transitive ( )
787
+ } ) ;
788
+ let mut empty = HashSet :: new ( ) ;
789
+ let visited = if is_transitive { & mut * visited} else { & mut empty} ;
790
+ try!( visit ( resolve, dep, summaries, visited, checked) ) ;
791
+ }
792
+ }
793
+
794
+ // Ok, we're done, no longer visiting our node any more
795
+ visited. remove ( id) ;
796
+ Ok ( ( ) )
797
+ }
798
+ }
0 commit comments