|
| 1 | +/* |
| 2 | + This source file is part of the Swift.org open source project |
| 3 | + |
| 4 | + Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors |
| 5 | + Licensed under Apache License v2.0 with Runtime Library Exception |
| 6 | + |
| 7 | + See http://swift.org/LICENSE.txt for license information |
| 8 | + See http://swift.org/CONTRIBUTORS.txt for Swift project authors |
| 9 | +*/ |
| 10 | + |
| 11 | +import TSCBasic |
| 12 | +import PackageModel |
| 13 | +import struct TSCUtility.Version |
| 14 | + |
| 15 | +/// A node in the dependency resolution graph. |
| 16 | +/// |
| 17 | +/// See the documentation of each case for more detailed descriptions of each kind and how they interact. |
| 18 | +/// |
| 19 | +/// - SeeAlso: `GraphLoadingNode` |
| 20 | +public enum DependencyResolutionNode { |
| 21 | + |
| 22 | + /// An empty package node. |
| 23 | + /// |
| 24 | + /// This node indicates that a package needs to be present, but does not indicate that any of its contents are needed. |
| 25 | + /// |
| 26 | + /// Empty package nodes are always leaf nodes; they have no dependencies. |
| 27 | + case empty(package: PackageReference) |
| 28 | + |
| 29 | + /// A product node. |
| 30 | + /// |
| 31 | + /// This node indicates that a particular product in a particular package is required. |
| 32 | + /// |
| 33 | + /// Product nodes always have dependencies. A product node has... |
| 34 | + /// |
| 35 | + /// - one implicit dependency on its own package at an exact version (as an empty package node). |
| 36 | + /// This dependency is what ensures the resolver does not select two products from the same package at different versions. |
| 37 | + /// - zero or more dependencies on the product nodes of other packages. |
| 38 | + /// These are all the external products required to build all of the targets vended by this product. |
| 39 | + /// They derive from the manifest. |
| 40 | + /// |
| 41 | + /// Tools versions before 5.2 do not know which products belong to which packages, so each product is required from every dependency. |
| 42 | + /// Since a non‐existant product ends up with only its implicit dependency on its own package, |
| 43 | + /// only whichever package contains the product will end up adding additional constraints. |
| 44 | + /// See `ProductFilter` and `Manifest.register(...)`. |
| 45 | + case product(String, package: PackageReference) |
| 46 | + |
| 47 | + /// A root node. |
| 48 | + /// |
| 49 | + /// This node indicates a root node in the graph, which is required no matter what. |
| 50 | + /// |
| 51 | + /// Root nodes may have dependencies. A root node has... |
| 52 | + /// |
| 53 | + /// - zero or more dependencies on each external product node required to build any of its targets (vended or not). |
| 54 | + /// - zero or more dependencies directly on external empty package nodes. |
| 55 | + /// This special case occurs when a dependecy is declared but not used. |
| 56 | + /// It is a warning condition, and builds do not actually need these dependencies. |
| 57 | + /// However, forcing the graph to resolve and fetch them anyway allows the diagnostics passes access |
| 58 | + /// to the information needed in order to provide actionable suggestions to help the user stitch up the dependency declarations properly. |
| 59 | + case root(package: PackageReference) |
| 60 | + |
| 61 | + /// The package. |
| 62 | + public var package: PackageReference { |
| 63 | + switch self { |
| 64 | + case .empty(let package), .product(_, let package), .root(let package): |
| 65 | + return package |
| 66 | + } |
| 67 | + } |
| 68 | + |
| 69 | + /// The name of the specific product if the node is a product node, otherwise `nil`. |
| 70 | + public var specificProduct: String? { |
| 71 | + switch self { |
| 72 | + case .empty, .root: |
| 73 | + return nil |
| 74 | + case .product(let product, _): |
| 75 | + return product |
| 76 | + } |
| 77 | + } |
| 78 | + |
| 79 | + /// Assembles the product filter to use on the manifest for this node to determine its dependencies. |
| 80 | + public var productFilter: ProductFilter { |
| 81 | + switch self { |
| 82 | + case .empty: |
| 83 | + return .specific([]) |
| 84 | + case .product(let product, _): |
| 85 | + return .specific([product]) |
| 86 | + case .root: |
| 87 | + return .everything |
| 88 | + } |
| 89 | + } |
| 90 | + |
| 91 | + /// Returns the dependency that a product has on its own package, if relevant. |
| 92 | + /// |
| 93 | + /// This is the constraint that requires all products from a package resolve to the same version. |
| 94 | + internal func versionLock(version: Version) -> RepositoryPackageConstraint? { |
| 95 | + // Don’t create a version lock for anything but a product. |
| 96 | + guard specificProduct != nil else { return nil } |
| 97 | + return RepositoryPackageConstraint( |
| 98 | + container: package, |
| 99 | + versionRequirement: .exact(version), |
| 100 | + products: .specific([]) |
| 101 | + ) |
| 102 | + } |
| 103 | + |
| 104 | + /// Returns the dependency that a product has on its own package, if relevant. |
| 105 | + /// |
| 106 | + /// This is the constraint that requires all products from a package resolve to the same revision. |
| 107 | + internal func revisionLock(revision: String) -> RepositoryPackageConstraint? { |
| 108 | + // Don’t create a revision lock for anything but a product. |
| 109 | + guard specificProduct != nil else { return nil } |
| 110 | + return RepositoryPackageConstraint( |
| 111 | + container: package, |
| 112 | + requirement: .revision(revision), |
| 113 | + products: .specific([]) |
| 114 | + ) |
| 115 | + } |
| 116 | +} |
| 117 | + |
| 118 | +extension DependencyResolutionNode: Equatable { |
| 119 | + public static func ==(lhs: DependencyResolutionNode, rhs: DependencyResolutionNode) -> Bool { |
| 120 | + return (lhs.package, lhs.specificProduct) == (rhs.package, rhs.specificProduct) |
| 121 | + } |
| 122 | +} |
| 123 | + |
| 124 | +extension DependencyResolutionNode: Hashable { |
| 125 | + public func hash(into hasher: inout Hasher) { |
| 126 | + hasher.combine(package) |
| 127 | + hasher.combine(specificProduct) |
| 128 | + } |
| 129 | +} |
| 130 | + |
| 131 | +extension DependencyResolutionNode: CustomStringConvertible { |
| 132 | + public var description: String { |
| 133 | + return "\(package.name)\(productFilter)" |
| 134 | + } |
| 135 | +} |
0 commit comments