9
9
*/
10
10
11
11
import TSCBasic
12
- import struct PackageModel. PackageReference
12
+ import PackageModel
13
13
import struct TSCUtility. Version
14
14
import class Foundation. NSDate
15
15
@@ -111,7 +111,7 @@ public protocol PackageContainer {
111
111
/// - Precondition: `versions.contains(version)`
112
112
/// - Throws: If the version could not be resolved; this will abort
113
113
/// dependency resolution completely.
114
- func getDependencies( at version: Version ) throws -> [ PackageContainerConstraint ]
114
+ func getDependencies( at version: Version , productFilter : ProductFilter ) throws -> [ PackageContainerConstraint ]
115
115
116
116
/// Fetch the declared dependencies for a particular revision.
117
117
///
@@ -120,12 +120,12 @@ public protocol PackageContainer {
120
120
///
121
121
/// - Throws: If the revision could not be resolved; this will abort
122
122
/// dependency resolution completely.
123
- func getDependencies( at revision: String ) throws -> [ PackageContainerConstraint ]
123
+ func getDependencies( at revision: String , productFilter : ProductFilter ) throws -> [ PackageContainerConstraint ]
124
124
125
125
/// Fetch the dependencies of an unversioned package container.
126
126
///
127
127
/// NOTE: This method should not be called on a versioned container.
128
- func getUnversionedDependencies( ) throws -> [ PackageContainerConstraint ]
128
+ func getUnversionedDependencies( productFilter : ProductFilter ) throws -> [ PackageContainerConstraint ]
129
129
130
130
/// Get the updated identifier at a bound version.
131
131
///
@@ -154,21 +154,25 @@ public struct PackageContainerConstraint: CustomStringConvertible, Equatable, Ha
154
154
/// The constraint requirement.
155
155
public let requirement : PackageRequirement
156
156
157
+ /// The required products.
158
+ public let products : ProductFilter
159
+
157
160
/// Create a constraint requiring the given `container` satisfying the
158
161
/// `requirement`.
159
- public init ( container identifier: PackageReference , requirement: PackageRequirement ) {
162
+ public init ( container identifier: PackageReference , requirement: PackageRequirement , products : ProductFilter ) {
160
163
self . identifier = identifier
161
164
self . requirement = requirement
165
+ self . products = products
162
166
}
163
167
164
168
/// Create a constraint requiring the given `container` satisfying the
165
169
/// `versionRequirement`.
166
- public init ( container identifier: PackageReference , versionRequirement: VersionSetSpecifier ) {
167
- self . init ( container: identifier, requirement: . versionSet( versionRequirement) )
170
+ public init ( container identifier: PackageReference , versionRequirement: VersionSetSpecifier , products : ProductFilter ) {
171
+ self . init ( container: identifier, requirement: . versionSet( versionRequirement) , products : products )
168
172
}
169
173
170
174
public var description : String {
171
- return " Constraint( \( identifier) , \( requirement) ) "
175
+ return " Constraint( \( identifier) , \( requirement) , \( products ) "
172
176
}
173
177
}
174
178
@@ -209,7 +213,7 @@ public enum BoundVersion: Equatable, CustomStringConvertible {
209
213
}
210
214
211
215
public class DependencyResolver {
212
- public typealias Binding = ( container: PackageReference , binding: BoundVersion )
216
+ public typealias Binding = ( container: PackageReference , binding: BoundVersion , products : ProductFilter )
213
217
214
218
/// The dependency resolver result.
215
219
public enum Result {
@@ -220,3 +224,134 @@ public class DependencyResolver {
220
224
case error( Swift . Error )
221
225
}
222
226
}
227
+
228
+ /// A node in the dependency resolution graph.
229
+ ///
230
+ /// See the documentation of each case for more detailed descriptions of each kind and how they interact.
231
+ ///
232
+ /// - SeeAlso: `GraphLoadingNode`
233
+ public enum DependencyResolutionNode : Equatable , Hashable , CustomStringConvertible {
234
+
235
+ /// An empty package node.
236
+ ///
237
+ /// This node indicates that a package needs to be present, but does not indicate that any of its contents are needed.
238
+ ///
239
+ /// Empty package nodes are always leaf nodes; they have no dependencies.
240
+ case empty( package : PackageReference )
241
+
242
+ /// A product node.
243
+ ///
244
+ /// This node indicates that a particular product in a particular package is required.
245
+ ///
246
+ /// Product nodes always have dependencies. A product node has...
247
+ ///
248
+ /// - one implicit dependency on its own package at an exact version (as an empty package node).
249
+ /// This dependency is what ensures the resolver does not select two products from the same package at different versions.
250
+ /// - zero or more dependencies on the product nodes of other packages.
251
+ /// These are all the external products required to build all of the targets vended by this product.
252
+ /// They derive from the manifest.
253
+ ///
254
+ /// Tools versions before 5.2 do not know which products belong to which packages, so each product is required from every dependency.
255
+ /// Since a non‐existant product ends up with only its implicit dependency on its own package,
256
+ /// only whichever package contains the product will end up adding additional constraints.
257
+ /// See `ProductFilter` and `Manifest.register(...)`.
258
+ case product( String , package : PackageReference )
259
+
260
+ /// A root node.
261
+ ///
262
+ /// This node indicates a root node in the graph, which is required no matter what.
263
+ ///
264
+ /// Root nodes may have dependencies. A root node has...
265
+ ///
266
+ /// - zero or more dependencies on each external product node required to build any of its targets (vended or not).
267
+ /// - zero or more dependencies directly on external empty package nodes.
268
+ /// This special case occurs when a dependecy is declared but not used.
269
+ /// It is a warning condition, and builds do not actually need these dependencies.
270
+ /// However, forcing the graph to resolve and fetch them anyway allows the diagnostics passes access
271
+ /// to the information needed in order to provide actionable suggestions to help the user stitch up the dependency declarations properly.
272
+ case root( package : PackageReference )
273
+
274
+ /// The package.
275
+ public var package : PackageReference {
276
+ switch self {
277
+ case . empty( let package ) , . product( _, let package ) , . root( let package ) :
278
+ return package
279
+ }
280
+ }
281
+
282
+ /// The name of the specific product if the node is a product node, otherwise `nil`.
283
+ public var specificProduct : String ? {
284
+ switch self {
285
+ case . empty, . root:
286
+ return nil
287
+ case . product( let product, _) :
288
+ return product
289
+ }
290
+ }
291
+
292
+ // To ensure cyclical dependencies are detected properly,
293
+ // hashing cannot include whether the node behaves as a root.
294
+ private struct Identity : Equatable , Hashable {
295
+ fileprivate let package : PackageReference
296
+ fileprivate let specificProduct : String ?
297
+ }
298
+ private var identity : Identity {
299
+ return Identity ( package : package , specificProduct: specificProduct)
300
+ }
301
+ public static func == ( lhs: DependencyResolutionNode , rhs: DependencyResolutionNode ) -> Bool {
302
+ return lhs. identity == rhs. identity
303
+ }
304
+ public func hash( into hasher: inout Hasher ) {
305
+ hasher. combine ( identity)
306
+ }
307
+
308
+ /// Assembles the product filter to use on the manifest for this node to determine it’s dependencies.
309
+ internal func productFilter( ) -> ProductFilter {
310
+ switch self {
311
+ case . empty:
312
+ return . specific( [ ] )
313
+ case . product( let product, _) :
314
+ return . specific( [ product] )
315
+ case . root:
316
+ return . everything
317
+ }
318
+ }
319
+
320
+ /// Returns the dependency that a product has on its own package, if relevant.
321
+ ///
322
+ /// This is the constraint that requires all products from a package resolve to the same version.
323
+ internal func versionLock( version: Version ) -> RepositoryPackageConstraint ? {
324
+ // Don’t create a version lock for anything but a product.
325
+ guard specificProduct != nil else { return nil }
326
+ return RepositoryPackageConstraint (
327
+ container: package ,
328
+ versionRequirement: . exact( version) ,
329
+ products: . specific( [ ] )
330
+ )
331
+ }
332
+
333
+ /// Returns the dependency that a product has on its own package, if relevant.
334
+ ///
335
+ /// This is the constraint that requires all products from a package resolve to the same revision.
336
+ internal func revisionLock( revision: String ) -> RepositoryPackageConstraint ? {
337
+ // Don’t create a revision lock for anything but a product.
338
+ guard specificProduct != nil else { return nil }
339
+ return RepositoryPackageConstraint (
340
+ container: package ,
341
+ requirement: . revision( revision) ,
342
+ products: . specific( [ ] )
343
+ )
344
+ }
345
+
346
+ public var description : String {
347
+ return " \( package . name) \( productFilter ( ) ) "
348
+ }
349
+
350
+ public func nameForDiagnostics( ) -> String {
351
+ if let product = specificProduct {
352
+ return " \( package . name) [ \( product) ] "
353
+ } else {
354
+ return " \( package . name) "
355
+ }
356
+ }
357
+ }
0 commit comments