@@ -36,12 +36,18 @@ use crate::{
36
36
index:: { Id , IndexDependency , PackageIndex } ,
37
37
lock:: { LockFile , LockFileEntry } ,
38
38
manifest:: Realization ,
39
- Dependency , ManifestFile , Precise ,
39
+ util:: semver_to_pg,
40
+ Dependency , IndexPrecise , ManifestFile , Precise ,
40
41
} ;
41
42
42
43
type VersionRange = pubgrub:: range:: Range < SemanticVersion > ;
43
44
44
45
pub struct PackageRegistry {
46
+ // The packages whose versions were locked in a lockfile; we'll try to prefer using
47
+ // those same versions. We won't absolutely insist on it, because if the manifest
48
+ // changed (or some path-dependency changed) then the old locked versions might not
49
+ // resolve anymore.
50
+ previously_locked : HashMap < VirtualPackage , SemanticVersion > ,
45
51
index : PackageIndex ,
46
52
realized_unversioned : Realization ,
47
53
}
@@ -51,7 +57,8 @@ impl PackageRegistry {
51
57
& ' a self ,
52
58
package : & VirtualPackage ,
53
59
) -> impl Iterator < Item = SemanticVersion > + ' a {
54
- match package {
60
+ let locked_version = self . previously_locked . get ( package) . cloned ( ) ;
61
+ let rest = match package {
55
62
VirtualPackage :: Package ( Package :: Unversioned ( _) ) => {
56
63
Box :: new ( std:: iter:: once ( SemanticVersion :: zero ( ) ) ) as Box < dyn Iterator < Item = _ > >
57
64
}
@@ -81,7 +88,12 @@ impl PackageRegistry {
81
88
82
89
Box :: new ( iter)
83
90
}
84
- }
91
+ } ;
92
+
93
+ // Put the locked version first, and then the other versions in any order (filtering to ensure that the locked version isn't repeated).
94
+ locked_version
95
+ . into_iter ( )
96
+ . chain ( rest. filter ( move |v| Some ( v) != locked_version. as_ref ( ) ) )
85
97
}
86
98
87
99
pub fn dep ( & self , pkg : & Package , version : & SemanticVersion , dep_id : & Id ) -> VersionReq {
@@ -270,6 +282,22 @@ impl std::fmt::Display for Package {
270
282
}
271
283
}
272
284
285
+ // Makes the precise less precise, by returning the bucket that it falls into.
286
+ impl From < Precise > for Package {
287
+ fn from ( p : Precise ) -> Self {
288
+ match p {
289
+ Precise :: Git { repo, .. } => {
290
+ Package :: Unversioned ( UnversionedPackage :: Git { url : repo } )
291
+ }
292
+ Precise :: Path { path } => Package :: Unversioned ( UnversionedPackage :: Path { path } ) ,
293
+ Precise :: Index { id, version } => Package :: Bucket ( Bucket {
294
+ id,
295
+ version : semver_to_pg ( version) . into ( ) ,
296
+ } ) ,
297
+ }
298
+ }
299
+ }
300
+
273
301
#[ derive( Debug , Clone , Eq , PartialEq , Hash ) ]
274
302
pub enum VirtualPackage {
275
303
Package ( Package ) ,
@@ -319,10 +347,25 @@ impl DependencyProvider<VirtualPackage, SemanticVersion> for PackageRegistry {
319
347
& self ,
320
348
potential_packages : impl Iterator < Item = ( T , U ) > ,
321
349
) -> Result < ( T , Option < SemanticVersion > ) , Box < dyn std:: error:: Error > > {
322
- Ok ( pubgrub:: solver:: choose_package_with_fewest_versions (
323
- |p| self . list_versions ( p) ,
324
- potential_packages,
325
- ) )
350
+ // We try to choose the package with the fewest available versions, as the pubgrub
351
+ // docs recommend this as a reasonably-performant heuristic. We count a previously locked package
352
+ // as having one version (even if we'd theoretically be willing to ignore the lock).
353
+ let count_valid = |( p, range) : & ( T , U ) | {
354
+ if self . previously_locked . contains_key ( p. borrow ( ) ) {
355
+ 1
356
+ } else {
357
+ self . list_versions ( p. borrow ( ) )
358
+ . filter ( |v| range. borrow ( ) . contains ( v) )
359
+ . count ( )
360
+ }
361
+ } ;
362
+ let ( pkg, range) = potential_packages
363
+ . min_by_key ( count_valid)
364
+ . expect ( "potential_packages gave us an empty iterator" ) ;
365
+ let version = self
366
+ . list_versions ( pkg. borrow ( ) )
367
+ . find ( |v| range. borrow ( ) . contains ( v) ) ;
368
+ Ok ( ( pkg, version) )
326
369
}
327
370
328
371
fn get_dependencies (
@@ -428,6 +471,72 @@ pub struct Resolution {
428
471
}
429
472
430
473
pub fn resolve ( manifest : & ManifestFile ) -> Result < Resolution , Error > {
474
+ resolve_with_lock ( manifest, & LockFile :: default ( ) )
475
+ }
476
+
477
+ fn previously_locked (
478
+ top_level : & Package ,
479
+ lock : & LockFile ,
480
+ ) -> HashMap < VirtualPackage , SemanticVersion > {
481
+ fn precise_to_index ( p : & Precise ) -> Option < IndexPrecise > {
482
+ match p {
483
+ Precise :: Index { id, version } => Some ( IndexPrecise {
484
+ id : id. clone ( ) ,
485
+ version : version. clone ( ) ,
486
+ } ) ,
487
+ _ => None ,
488
+ }
489
+ }
490
+
491
+ // A list of (package: Package, version of the package: SemanticVersion, dependency: IndexPrecise)
492
+ let pkg_deps = lock
493
+ . dependencies
494
+ . values ( )
495
+ . filter_map ( precise_to_index)
496
+ // FIXME: another place we're hardcoding an unversioned version
497
+ . map ( |dep| ( top_level. clone ( ) , SemanticVersion :: zero ( ) , dep) )
498
+ . chain ( lock. packages . iter ( ) . flat_map ( |( parent, entry) | {
499
+ let pkg = Package :: from ( parent. clone ( ) ) ;
500
+ let pkg_version = parent
501
+ . version ( )
502
+ . map ( semver_to_pg)
503
+ . unwrap_or ( SemanticVersion :: zero ( ) ) ;
504
+ entry
505
+ . dependencies
506
+ . values ( )
507
+ . filter_map ( precise_to_index)
508
+ . map ( move |v| ( pkg. clone ( ) , pkg_version, v) )
509
+ } ) ) ;
510
+
511
+ // Because of the virtual package system, each parent->dep needs two entries
512
+ // in the locked map: one for recording which of the "union" packages the parent
513
+ // depends on, and another for recording which precise version the "union" package depends on.
514
+ pkg_deps
515
+ . flat_map ( |( pkg, version, dep) | {
516
+ let dep_version = semver_to_pg ( dep. version ) ;
517
+ let dep_bucket: BucketVersion = dep_version. into ( ) ;
518
+ [
519
+ (
520
+ VirtualPackage :: Union {
521
+ source : pkg,
522
+ source_version : version,
523
+ target : dep. id . clone ( ) ,
524
+ } ,
525
+ dep_bucket. into ( ) ,
526
+ ) ,
527
+ (
528
+ VirtualPackage :: Package ( Package :: Bucket ( Bucket {
529
+ id : dep. id ,
530
+ version : dep_bucket,
531
+ } ) ) ,
532
+ dep_version,
533
+ ) ,
534
+ ]
535
+ } )
536
+ . collect ( )
537
+ }
538
+
539
+ pub fn resolve_with_lock ( manifest : & ManifestFile , lock : & LockFile ) -> Result < Resolution , Error > {
431
540
let mut realization = Realization :: default ( ) ;
432
541
433
542
// pubgrub insists on resolving a top-level package. We'll represent it as a `Path` dependency,
@@ -448,10 +557,14 @@ pub fn resolve(manifest: &ManifestFile) -> Result<Resolution, Error> {
448
557
} ;
449
558
realization
450
559
. precise
451
- . insert ( top_level_dep. into ( ) , precise. clone ( ) ) ;
560
+ . insert ( top_level_dep. clone ( ) . into ( ) , precise. clone ( ) ) ;
452
561
realization. manifests . insert ( precise, manifest. clone ( ) ) ;
453
562
454
563
let registry = PackageRegistry {
564
+ previously_locked : dbg ! ( previously_locked(
565
+ & Package :: Unversioned ( top_level_dep) ,
566
+ lock
567
+ ) ) ,
455
568
index : PackageIndex :: new ( ) ,
456
569
realized_unversioned : realization,
457
570
} ;
@@ -488,6 +601,12 @@ pub fn resolve(manifest: &ManifestFile) -> Result<Resolution, Error> {
488
601
}
489
602
490
603
impl Resolution {
604
+ /// Finds the precise resolved version of this dependency.
605
+ ///
606
+ /// # Panics
607
+ ///
608
+ /// Panics if the dependency was not part of the dependency tree that this resolution
609
+ /// was generated for.
491
610
pub fn precise ( & self , dep : & Dependency ) -> Precise {
492
611
match dep {
493
612
Dependency :: Git { url } => Precise :: Git {
@@ -510,6 +629,7 @@ impl Resolution {
510
629
}
511
630
}
512
631
632
+ /// Returns all the dependencies of a package, along with their package-local names.
513
633
pub fn dependencies ( & self , pkg : & Precise ) -> HashMap < Ident , Precise > {
514
634
match pkg {
515
635
p @ Precise :: Git { .. } | p @ Precise :: Path { .. } => {
@@ -546,6 +666,7 @@ impl Resolution {
546
666
}
547
667
}
548
668
669
+ /// Returns all the resolved packages in the dependency tree.
549
670
pub fn all_precises ( & self ) -> Vec < Precise > {
550
671
let mut ret: Vec < _ > = self . realization . precise . values ( ) . cloned ( ) . collect ( ) ;
551
672
ret. sort ( ) ;
@@ -595,7 +716,7 @@ impl Resolution {
595
716
)
596
717
} )
597
718
} )
598
- . collect ( ) , //,realized_packages.chain(index_packages).collect(),
719
+ . collect ( ) ,
599
720
} )
600
721
}
601
722
0 commit comments