@@ -76,10 +76,9 @@ public struct PubgrubDependencyResolver {
76
76
77
77
func decide( _ node: DependencyResolutionNode , at version: Version ) {
78
78
let term = Term ( node, . exact( version) )
79
- // FIXME: Shouldn't we check this _before_ making a decision?
80
- assert ( term. isValidDecision ( for: self . solution) )
81
-
82
79
self . lock. withLock {
80
+ // FIXME: Shouldn't we check this _before_ making a decision?
81
+ assert ( term. isValidDecision ( for: self . solution) )
83
82
self . solution. decide ( node, at: version)
84
83
}
85
84
}
@@ -168,7 +167,7 @@ public struct PubgrubDependencyResolver {
168
167
169
168
// If version solving failing, build the user-facing diagnostic.
170
169
if let pubGrubError = error as? PubgrubError , let rootCause = pubGrubError. rootCause, let state = pubGrubError. state {
171
- let builder = DiagnosticReportBuilder (
170
+ var builder = DiagnosticReportBuilder (
172
171
root: root,
173
172
incompatibilities: state. incompatibilities,
174
173
provider: self . provider
@@ -442,7 +441,6 @@ public struct PubgrubDependencyResolver {
442
441
return ( overriddenPackages, rootIncompatibilities)
443
442
}
444
443
445
- // 👀 IMPORANT CHANGE
446
444
/// Perform unit propagation, resolving conflicts if necessary and making
447
445
/// decisions if nothing else is left to be done.
448
446
/// After this method returns `solution` is either populated with a list of
@@ -616,27 +614,30 @@ public struct PubgrubDependencyResolver {
616
614
}
617
615
618
616
private func computeCounts( for terms: [ Term ] , completion: @escaping ( Result < [ Term : Int ] , Error > ) -> Void ) {
619
- let lock = Lock ( )
620
- var results = [ Term : Result < Int , Error > ] ( )
617
+ if terms. isEmpty {
618
+ return completion ( . success( [ : ] ) )
619
+ }
620
+
621
+ let sync = DispatchGroup ( )
622
+ let results = ThreadSafeKeyValueStore < Term , Result < Int , Error > > ( )
621
623
622
624
terms. forEach { term in
625
+ sync. enter ( )
623
626
provider. getContainer ( for: term. node. package ) { result in
624
- let result = result. flatMap { container in Result ( catching: { try container. versionCount ( term. requirement) } ) }
625
- lock. withLock {
626
- results [ term] = result
627
- if results. count == terms. count {
628
- do {
629
- completion ( . success( try results. mapValues { try $0. get ( ) } ) )
630
- } catch {
631
- completion ( . failure( error) )
632
- }
633
- }
634
- }
627
+ defer { sync. leave ( ) }
628
+ results [ term] = result. flatMap { container in Result ( catching: { try container. versionCount ( term. requirement) } ) }
629
+ }
630
+ }
631
+
632
+ sync. notify ( queue: self . queue) {
633
+ do {
634
+ completion ( . success( try results. mapValues { try $0. get ( ) } ) )
635
+ } catch {
636
+ completion ( . failure( error) )
635
637
}
636
638
}
637
639
}
638
640
639
- // 👀 IMPORANT CHANGE
640
641
internal func makeDecision( state: State , completion: @escaping ( Result < DependencyResolutionNode ? , Error > ) -> Void ) {
641
642
// If there are no more undecided terms, version solving is complete.
642
643
let undecided = state. solution. undecided
@@ -651,49 +652,44 @@ public struct PubgrubDependencyResolver {
651
652
let counts = try result. get ( )
652
653
// forced unwraps safe since we are testing for count and errors above
653
654
let pkgTerm = undecided. min { counts [ $0] ! < counts [ $1] ! } !
654
- provider. getContainer ( for: pkgTerm. node. package ) { result in
655
- do {
656
- let container = try result. get ( )
657
- // Get the best available version for this package.
658
- guard let version = try container. getBestAvailableVersion ( for: pkgTerm) else {
659
- state. addIncompatibility ( Incompatibility ( pkgTerm, root: state. root, cause: . noAvailableVersion) , at: . decisionMaking)
660
- return completion ( . success( pkgTerm. node) )
661
- }
662
-
663
- // Add all of this version's dependencies as incompatibilities.
664
- let depIncompatibilities = try container. incompatibilites (
665
- at: version,
666
- node: pkgTerm. node,
667
- overriddenPackages: state. overriddenPackages,
668
- root: state. root
669
- )
670
-
671
- var haveConflict = false
672
- for incompatibility in depIncompatibilities {
673
- // Add the incompatibility to our partial solution.
674
- state. addIncompatibility ( incompatibility, at: . decisionMaking)
675
-
676
- // Check if this incompatibility will statisfy the solution.
677
- haveConflict = haveConflict || incompatibility. terms. allSatisfy {
678
- // We only need to check if the terms other than this package
679
- // are satisfied because we _know_ that the terms matching
680
- // this package will be satisfied if we make this version
681
- // as a decision.
682
- $0. node == pkgTerm. node || state. solution. satisfies ( $0)
683
- }
684
- }
655
+ // at this point the container is cached
656
+ let container = try provider. getCachedContainer ( for: pkgTerm. node. package )
657
+ // Get the best available version for this package.
658
+ guard let version = try container. getBestAvailableVersion ( for: pkgTerm) else {
659
+ state. addIncompatibility ( Incompatibility ( pkgTerm, root: state. root, cause: . noAvailableVersion) , at: . decisionMaking)
660
+ return completion ( . success( pkgTerm. node) )
661
+ }
685
662
686
- // Decide this version if there was no conflict with its dependencies.
687
- if !haveConflict {
688
- log ( " decision: \( pkgTerm. node. package ) @ \( version) " )
689
- state. decide ( pkgTerm. node, at: version)
690
- }
663
+ // Add all of this version's dependencies as incompatibilities.
664
+ let depIncompatibilities = try container. incompatibilites (
665
+ at: version,
666
+ node: pkgTerm. node,
667
+ overriddenPackages: state. overriddenPackages,
668
+ root: state. root
669
+ )
691
670
692
- completion ( . success( pkgTerm. node) )
693
- } catch {
694
- completion ( . failure( error) )
671
+ var haveConflict = false
672
+ for incompatibility in depIncompatibilities {
673
+ // Add the incompatibility to our partial solution.
674
+ state. addIncompatibility ( incompatibility, at: . decisionMaking)
675
+
676
+ // Check if this incompatibility will statisfy the solution.
677
+ haveConflict = haveConflict || incompatibility. terms. allSatisfy {
678
+ // We only need to check if the terms other than this package
679
+ // are satisfied because we _know_ that the terms matching
680
+ // this package will be satisfied if we make this version
681
+ // as a decision.
682
+ $0. node == pkgTerm. node || state. solution. satisfies ( $0)
695
683
}
696
684
}
685
+
686
+ // Decide this version if there was no conflict with its dependencies.
687
+ if !haveConflict {
688
+ log ( " decision: \( pkgTerm. node. package ) @ \( version) " )
689
+ state. decide ( pkgTerm. node, at: version)
690
+ }
691
+
692
+ completion ( . success( pkgTerm. node) )
697
693
} catch {
698
694
completion ( . failure( error) )
699
695
}
@@ -715,7 +711,7 @@ public struct PubgrubDependencyResolver {
715
711
}
716
712
}
717
713
718
- private final class DiagnosticReportBuilder {
714
+ private struct DiagnosticReportBuilder {
719
715
let rootNode : DependencyResolutionNode
720
716
let incompatibilities : [ DependencyResolutionNode : [ Incompatibility ] ]
721
717
@@ -730,10 +726,10 @@ private final class DiagnosticReportBuilder {
730
726
self . provider = provider
731
727
}
732
728
733
- func makeErrorReport( for rootCause: Incompatibility ) throws -> String {
729
+ mutating func makeErrorReport( for rootCause: Incompatibility ) throws -> String {
734
730
/// Populate `derivations`.
735
731
func countDerivations( _ i: Incompatibility ) {
736
- derivations [ i, default: 0 ] += 1
732
+ self . derivations [ i, default: 0 ] += 1
737
733
if case . conflict( let cause) = i. cause {
738
734
countDerivations ( cause. conflict)
739
735
countDerivations ( cause. other)
@@ -773,7 +769,7 @@ private final class DiagnosticReportBuilder {
773
769
return stream. bytes. description
774
770
}
775
771
776
- private func visit(
772
+ private mutating func visit(
777
773
_ incompatibility: Incompatibility ,
778
774
isConclusion: Bool = false
779
775
) throws {
@@ -1038,21 +1034,21 @@ private final class DiagnosticReportBuilder {
1038
1034
/// the incompatibility and how it as derived. If `isNumbered` is true, a
1039
1035
/// line number will be assigned to this incompatibility so that it can be
1040
1036
/// referred to again.
1041
- private func record(
1037
+ private mutating func record(
1042
1038
_ incompatibility: Incompatibility ,
1043
1039
message: String ,
1044
1040
isNumbered: Bool
1045
1041
) {
1046
1042
var number = - 1
1047
1043
if isNumbered {
1048
1044
number = lineNumbers. count + 1
1049
- lineNumbers [ incompatibility] = number
1045
+ self . lineNumbers [ incompatibility] = number
1050
1046
}
1051
1047
let line = ( number: number, message: message)
1052
1048
if isNumbered {
1053
- lines. append ( line)
1049
+ self . lines. append ( line)
1054
1050
} else {
1055
- lines. insert ( line, at: 0 )
1051
+ self . lines. insert ( line, at: 0 )
1056
1052
}
1057
1053
}
1058
1054
}
@@ -1073,10 +1069,10 @@ private final class PubGrubPackageContainer {
1073
1069
1074
1070
/// The map of dependencies to version set that indicates the versions that have had their
1075
1071
/// incompatibilities emitted.
1076
- private var emittedIncompatibilities : [ PackageReference : VersionSetSpecifier ] = [ : ]
1072
+ private var emittedIncompatibilities = ThreadSafeKeyValueStore < PackageReference , VersionSetSpecifier > ( )
1077
1073
1078
1074
/// Whether we've emitted the incompatibilities for the pinned versions.
1079
- private var emittedPinnedVersionIncompatibilities : Bool = false
1075
+ private var emittedPinnedVersionIncompatibilities = ThreadSafeBox ( false )
1080
1076
1081
1077
init ( underlying: PackageContainer , pinsMap: PinsStore . PinsMap , queue: DispatchQueue ) {
1082
1078
self . underlying = underlying
@@ -1232,9 +1228,11 @@ private final class PubGrubPackageContainer {
1232
1228
if version == pinnedVersion, emittedIncompatibilities. isEmpty {
1233
1229
// We don't need to emit anything if we already emitted the incompatibilities at the
1234
1230
// pinned version.
1235
- if self . emittedPinnedVersionIncompatibilities { return [ ] }
1231
+ if self . emittedPinnedVersionIncompatibilities. get ( ) ?? false {
1232
+ return [ ]
1233
+ }
1236
1234
1237
- self . emittedPinnedVersionIncompatibilities = true
1235
+ self . emittedPinnedVersionIncompatibilities. put ( true )
1238
1236
1239
1237
// Since the pinned version is most likely to succeed, we don't compute bounds for its
1240
1238
// incompatibilities.
@@ -1282,7 +1280,6 @@ private final class PubGrubPackageContainer {
1282
1280
}
1283
1281
}
1284
1282
1285
- // 👀 IMPORANT CHANGE
1286
1283
/// Method for computing bounds of the given dependencies.
1287
1284
///
1288
1285
/// This will return a dictionary which contains mapping of a package dependency to its bound.
@@ -1297,7 +1294,7 @@ private final class PubGrubPackageContainer {
1297
1294
timeout: DispatchTimeInterval
1298
1295
) throws -> ( lowerBounds: [ PackageReference : Version ] , upperBounds: [ PackageReference : Version ] ) {
1299
1296
let preloadCount = 3
1300
-
1297
+
1301
1298
// nothing to do
1302
1299
if constraints. isEmpty {
1303
1300
return ( [ : ] , [ : ] )
@@ -1388,7 +1385,6 @@ private final class PubGrubPackageContainer {
1388
1385
}
1389
1386
}
1390
1387
1391
- // 👀 IMPORANT CHANGE
1392
1388
/// An utility class around PackageContainerProvider that allows "prefetching" the containers
1393
1389
/// in parallel. The basic idea is to kick off container fetching before starting the resolution
1394
1390
/// by using the list of URLs from the Package.resolved file.
@@ -1431,9 +1427,7 @@ private final class ContainerProvider {
1431
1427
func getContainer( for identifier: PackageReference , completion: @escaping ( Result < PubGrubPackageContainer , Error > ) -> Void ) {
1432
1428
// Return the cached container, if available.
1433
1429
if let container = self . containersCache [ identifier] {
1434
- return self . queue. async {
1435
- completion ( . success( container) )
1436
- }
1430
+ return completion ( . success( container) )
1437
1431
}
1438
1432
1439
1433
if let prefetchSync = self . prefetches [ identifier] {
@@ -1447,7 +1441,7 @@ private final class ContainerProvider {
1447
1441
} else {
1448
1442
// if prefetch failed, remove from list of prefetches and try again
1449
1443
self . prefetches [ identifier] = nil
1450
- self . getContainer ( for: identifier, completion: completion)
1444
+ return self . getContainer ( for: identifier, completion: completion)
1451
1445
}
1452
1446
}
1453
1447
} else {
0 commit comments