@@ -103,6 +103,9 @@ public struct PubGrubDependencyResolver {
103
103
/// Reference to the pins store, if provided.
104
104
private let pins : PinsStore . Pins
105
105
106
+ /// The packages that are available in a prebuilt form in SDK or a toolchain
107
+ private let availableLibraries : [ LibraryMetadata ]
108
+
106
109
/// The container provider used to load package containers.
107
110
private let provider : ContainerProvider
108
111
@@ -121,13 +124,15 @@ public struct PubGrubDependencyResolver {
121
124
public init (
122
125
provider: PackageContainerProvider ,
123
126
pins: PinsStore . Pins = [ : ] ,
127
+ availableLibraries: [ LibraryMetadata ] = [ ] ,
124
128
skipDependenciesUpdates: Bool = false ,
125
129
prefetchBasedOnResolvedFile: Bool = false ,
126
130
observabilityScope: ObservabilityScope ,
127
131
delegate: DependencyResolverDelegate ? = nil
128
132
) {
129
133
self . packageContainerProvider = provider
130
134
self . pins = pins
135
+ self . availableLibraries = availableLibraries
131
136
self . skipDependenciesUpdates = skipDependenciesUpdates
132
137
self . prefetchBasedOnResolvedFile = prefetchBasedOnResolvedFile
133
138
self . provider = ContainerProvider (
@@ -140,11 +145,7 @@ public struct PubGrubDependencyResolver {
140
145
}
141
146
142
147
/// Execute the resolution algorithm to find a valid assignment of versions.
143
- public func solve( constraints: [ Constraint ] , availableLibraries: [ LibraryMetadata ] , preferPrebuiltLibraries: Bool ) -> Result < [ DependencyResolverBinding ] , Error > {
144
- if !preferPrebuiltLibraries {
145
- self . provider. removeCachedContainers ( for: availableLibraries. flatMap { $0. identities. map { $0. ref } } )
146
- }
147
-
148
+ public func solve( constraints: [ Constraint ] ) -> Result < [ DependencyResolverBinding ] , Error > {
148
149
// the graph resolution root
149
150
let root : DependencyResolutionNode
150
151
if constraints. count == 1 , let constraint = constraints. first, constraint. package . kind. isRoot {
@@ -159,26 +160,24 @@ public struct PubGrubDependencyResolver {
159
160
}
160
161
161
162
do {
162
- // Use empty `availableLibraries` for the rest of resolving if we don't prefer them.
163
- let availableLibraries = preferPrebuiltLibraries ? availableLibraries : [ ]
164
- // strips state
165
- let bindings = try self . solve ( root: root, constraints: constraints, availableLibraries: availableLibraries) . bindings. filter {
166
- return $0. package . matchingPrebuiltLibrary ( in: availableLibraries) == nil
163
+ // Strip packages that have prebuilt libraries only if they match library version.
164
+ //
165
+ // FIXME: This is built on assumption that libraries are part of the SDK and are
166
+ // always available in include/library paths, but what happens if they are
167
+ // part of a toolchain instead? Builder needs an indicator that certain path
168
+ // has to be included when building packages that depend on prebuilt libraries.
169
+ let bindings = try self . solve ( root: root, constraints: constraints) . bindings. filter {
170
+ if case let . version( version) = $0. boundVersion {
171
+ if let library = $0. package . matchingPrebuiltLibrary ( in: self . availableLibraries) {
172
+ return . init( stringLiteral: library. version) != version
173
+ }
174
+ }
175
+ return true
167
176
}
168
177
return . success( bindings)
169
178
} catch {
170
179
// If version solving failing, build the user-facing diagnostic.
171
180
if let pubGrubError = error as? PubgrubError , let rootCause = pubGrubError. rootCause, let incompatibilities = pubGrubError. incompatibilities {
172
- let incompatiblePackages = incompatibilities. map ( { $0. key. package } )
173
- if incompatiblePackages. contains ( where: { $0. matchingPrebuiltLibrary ( in: availableLibraries) != nil } ) {
174
- return . failure(
175
- PubgrubError . potentiallyUnresovableDueToPrebuiltLibrary (
176
- incompatiblePackages,
177
- pubGrubError. description
178
- )
179
- )
180
- }
181
-
182
181
do {
183
182
var builder = DiagnosticReportBuilder (
184
183
root: root,
@@ -199,12 +198,12 @@ public struct PubGrubDependencyResolver {
199
198
/// Find a set of dependencies that fit the given constraints. If dependency
200
199
/// resolution is unable to provide a result, an error is thrown.
201
200
/// - Warning: It is expected that the root package reference has been set before this is called.
202
- internal func solve( root: DependencyResolutionNode , constraints: [ Constraint ] , availableLibraries : [ LibraryMetadata ] = [ ] ) throws -> (
201
+ internal func solve( root: DependencyResolutionNode , constraints: [ Constraint ] ) throws -> (
203
202
bindings: [ DependencyResolverBinding ] ,
204
203
state: State
205
204
) {
206
205
// first process inputs
207
- let inputs = try self . processInputs ( root: root, with: constraints, availableLibraries : availableLibraries )
206
+ let inputs = try self . processInputs ( root: root, with: constraints)
208
207
209
208
// Prefetch the containers if prefetching is enabled.
210
209
if self . prefetchBasedOnResolvedFile {
@@ -214,7 +213,7 @@ public struct PubGrubDependencyResolver {
214
213
let pins = self . pins. values
215
214
. map ( \. packageRef)
216
215
. filter { !inputs. overriddenPackages. keys. contains ( $0) }
217
- self . provider. prefetch ( containers: pins, availableLibraries: availableLibraries)
216
+ self . provider. prefetch ( containers: pins, availableLibraries: self . availableLibraries)
218
217
}
219
218
220
219
let state = State ( root: root, overriddenPackages: inputs. overriddenPackages)
@@ -230,7 +229,7 @@ public struct PubGrubDependencyResolver {
230
229
state. addIncompatibility ( incompatibility, at: . topLevel)
231
230
}
232
231
233
- try self . run ( state: state, availableLibraries : availableLibraries )
232
+ try self . run ( state: state)
234
233
235
234
let decisions = state. solution. assignments. filter ( \. isDecision)
236
235
var flattenedAssignments : [ PackageReference : ( binding: BoundVersion , products: ProductFilter ) ] = [ : ]
@@ -250,7 +249,7 @@ public struct PubGrubDependencyResolver {
250
249
let products = assignment. term. node. productFilter
251
250
252
251
// TODO: replace with async/await when available
253
- let container = try temp_await { provider. getContainer ( for: assignment. term. node. package , availableLibraries : availableLibraries , completion: $0) }
252
+ let container = try temp_await { provider. getContainer ( for: assignment. term. node. package , completion: $0) }
254
253
let updatePackage = try container. underlying. loadPackageReference ( at: boundVersion)
255
254
256
255
if var existing = flattenedAssignments [ updatePackage] {
@@ -272,7 +271,7 @@ public struct PubGrubDependencyResolver {
272
271
// Add overridden packages to the result.
273
272
for (package , override) in state. overriddenPackages {
274
273
// TODO: replace with async/await when available
275
- let container = try temp_await { provider. getContainer ( for: package , availableLibraries : availableLibraries , completion: $0) }
274
+ let container = try temp_await { provider. getContainer ( for: package , completion: $0) }
276
275
let updatePackage = try container. underlying. loadPackageReference ( at: override. version)
277
276
finalAssignments. append ( . init(
278
277
package : updatePackage,
@@ -288,8 +287,7 @@ public struct PubGrubDependencyResolver {
288
287
289
288
private func processInputs(
290
289
root: DependencyResolutionNode ,
291
- with constraints: [ Constraint ] ,
292
- availableLibraries: [ LibraryMetadata ]
290
+ with constraints: [ Constraint ]
293
291
) throws -> (
294
292
overriddenPackages: [ PackageReference : ( version: BoundVersion , products: ProductFilter ) ] ,
295
293
rootIncompatibilities: [ Incompatibility ]
@@ -331,10 +329,10 @@ public struct PubGrubDependencyResolver {
331
329
// Process dependencies of this package.
332
330
//
333
331
// We collect all version-based dependencies in a separate structure so they can
334
- // be process at the end. This allows us to override them when there is a non-version
332
+ // be processed at the end. This allows us to override them when there is a non-version
335
333
// based (unversioned/branch-based) constraint present in the graph.
336
334
// TODO: replace with async/await when available
337
- let container = try temp_await { provider. getContainer ( for: node. package , availableLibraries : availableLibraries , completion: $0) }
335
+ let container = try temp_await { provider. getContainer ( for: node. package , completion: $0) }
338
336
for dependency in try container. underlying. getUnversionedDependencies ( productFilter: node. productFilter) {
339
337
if let versionedBasedConstraints = VersionBasedConstraint . constraints ( dependency) {
340
338
for constraint in versionedBasedConstraints {
@@ -382,7 +380,7 @@ public struct PubGrubDependencyResolver {
382
380
// Process dependencies of this package, similar to the first phase but branch-based dependencies
383
381
// are not allowed to contain local/unversioned packages.
384
382
// TODO: replace with async/await when avail
385
- let container = try temp_await { provider. getContainer ( for: package , availableLibraries : availableLibraries , completion: $0) }
383
+ let container = try temp_await { provider. getContainer ( for: package , completion: $0) }
386
384
387
385
// If there is a pin for this revision-based dependency, get
388
386
// the dependencies at the pinned revision instead of using
@@ -465,7 +463,7 @@ public struct PubGrubDependencyResolver {
465
463
/// decisions if nothing else is left to be done.
466
464
/// After this method returns `solution` is either populated with a list of
467
465
/// final version assignments or an error is thrown.
468
- private func run( state: State , availableLibraries : [ LibraryMetadata ] ) throws {
466
+ private func run( state: State ) throws {
469
467
var next : DependencyResolutionNode ? = state. root
470
468
471
469
while let nxt = next {
@@ -474,13 +472,13 @@ public struct PubGrubDependencyResolver {
474
472
// initiate prefetch of known packages that will be used to make the decision on the next step
475
473
self . provider. prefetch (
476
474
containers: state. solution. undecided. map ( \. node. package ) ,
477
- availableLibraries: availableLibraries
475
+ availableLibraries: self . availableLibraries
478
476
)
479
477
480
478
// If decision making determines that no more decisions are to be
481
479
// made, it returns nil to signal that version solving is done.
482
480
// TODO: replace with async/await when available
483
- next = try temp_await { self . makeDecision ( state: state, availableLibraries : availableLibraries , completion: $0) }
481
+ next = try temp_await { self . makeDecision ( state: state, completion: $0) }
484
482
}
485
483
}
486
484
@@ -658,7 +656,6 @@ public struct PubGrubDependencyResolver {
658
656
659
657
private func computeCounts(
660
658
for terms: [ Term ] ,
661
- availableLibraries: [ LibraryMetadata ] ,
662
659
completion: @escaping ( Result < [ Term : Int ] , Error > ) -> Void
663
660
) {
664
661
if terms. isEmpty {
@@ -670,7 +667,7 @@ public struct PubGrubDependencyResolver {
670
667
671
668
terms. forEach { term in
672
669
sync. enter ( )
673
- provider. getContainer ( for: term. node. package , availableLibraries : availableLibraries ) { result in
670
+ provider. getContainer ( for: term. node. package ) { result in
674
671
defer { sync. leave ( ) }
675
672
results [ term] = result. flatMap { container in Result ( catching: { try container. versionCount ( term. requirement) } ) }
676
673
}
@@ -687,7 +684,6 @@ public struct PubGrubDependencyResolver {
687
684
688
685
internal func makeDecision(
689
686
state: State ,
690
- availableLibraries: [ LibraryMetadata ] = [ ] ,
691
687
completion: @escaping ( Result < DependencyResolutionNode ? , Error > ) -> Void
692
688
) {
693
689
// If there are no more undecided terms, version solving is complete.
@@ -696,9 +692,33 @@ public struct PubGrubDependencyResolver {
696
692
return completion ( . success( nil ) )
697
693
}
698
694
695
+ // If prebuilt libraries are available, let's attempt their versions first before going for
696
+ // the latest viable version in the package. This way we archive multiple goals - prioritize
697
+ // prebuilt libraries if they satisfy all requirements, avoid counting and building package
698
+ // manifests and avoid (re-)building packages.
699
+ //
700
+ // Since the conflict resolution learns from incorrect terms this wouldn't be re-attempted.
701
+ if !self . availableLibraries. isEmpty {
702
+ let start = DispatchTime . now ( )
703
+ for pkgTerm in undecided {
704
+ let package = pkgTerm. node. package
705
+ guard let library = package . matchingPrebuiltLibrary ( in: self . availableLibraries) else {
706
+ continue
707
+ }
708
+
709
+ let version = Version ( stringLiteral: library. version)
710
+
711
+ if pkgTerm. requirement. contains ( version) {
712
+ self . delegate? . didResolve ( term: pkgTerm, version: version, duration: start. distance ( to: . now( ) ) )
713
+ state. decide ( pkgTerm. node, at: version)
714
+ return completion ( . success( pkgTerm. node) )
715
+ }
716
+ }
717
+ }
718
+
699
719
// Prefer packages with least number of versions that fit the current requirements so we
700
720
// get conflicts (if any) sooner.
701
- self . computeCounts ( for: undecided, availableLibraries : availableLibraries ) { result in
721
+ self . computeCounts ( for: undecided) { result in
702
722
do {
703
723
let start = DispatchTime . now ( )
704
724
let counts = try result. get ( )
@@ -762,16 +782,13 @@ public extension PubGrubDependencyResolver {
762
782
enum PubgrubError : Swift . Error , CustomStringConvertible {
763
783
case _unresolvable( Incompatibility , [ DependencyResolutionNode : [ Incompatibility ] ] )
764
784
case unresolvable( String )
765
- case potentiallyUnresovableDueToPrebuiltLibrary( [ PackageReference ] , String )
766
785
767
786
public var description : String {
768
787
switch self {
769
788
case . _unresolvable( let rootCause, _) :
770
789
return rootCause. description
771
790
case . unresolvable( let error) :
772
791
return error
773
- case . potentiallyUnresovableDueToPrebuiltLibrary( _, let error) :
774
- return error
775
792
}
776
793
}
777
794
@@ -781,8 +798,6 @@ public extension PubGrubDependencyResolver {
781
798
return rootCause
782
799
case . unresolvable:
783
800
return nil
784
- case . potentiallyUnresovableDueToPrebuiltLibrary:
785
- return nil
786
801
}
787
802
}
788
803
@@ -792,8 +807,6 @@ public extension PubGrubDependencyResolver {
792
807
return incompatibilities
793
808
case . unresolvable:
794
809
return nil
795
- case . potentiallyUnresovableDueToPrebuiltLibrary:
796
- return nil
797
810
}
798
811
}
799
812
}
0 commit comments