1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209
|
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift open source project
//
// Copyright (c) 2014-2020 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
import Basics
import Dispatch
import PackageModel
import struct TSCUtility.Version
/// A container of packages.
///
/// This is the top-level unit of package resolution, i.e. the unit at which
/// versions are associated.
///
/// It represents a package container (e.g., a source repository) which can be
/// identified unambiguously and which contains a set of available package
/// versions and the ability to retrieve the dependency constraints for each of
/// those versions.
///
/// We use the "container" terminology here to differentiate between two
/// conceptual notions of what the package is: (1) informally, the repository
/// containing the package, but from which a package cannot be loaded by itself
/// and (2) the repository at a particular version, at which point the package
/// can be loaded and dependencies enumerated.
///
/// This is also designed in such a way to extend naturally to multiple packages
/// being contained within a single repository, should we choose to support that
/// later.
public protocol PackageContainer {
/// The identifier for the package.
var package: PackageReference { get }
var shouldInvalidatePinnedVersions: Bool { get }
/// Returns true if the tools version is compatible at the given version.
func isToolsVersionCompatible(at version: Version) -> Bool
/// Returns the tools version for the given version
func toolsVersion(for version: Version) throws -> ToolsVersion
/// Get the list of versions which are available for the package.
///
/// The list will be returned in sorted order, with the latest version *first*.
/// All versions will not be requested at once. Resolver will request the next one only
/// if the previous one did not satisfy all constraints.
func toolsVersionsAppropriateVersionsDescending() throws -> [Version]
/// Get the list of versions in the repository sorted in the ascending order, that is the earliest
/// version appears first.
func versionsAscending() throws -> [Version]
/// Get the list of versions in the repository sorted in the descending order, that is the latest
/// version appears first.
func versionsDescending() throws -> [Version]
// FIXME: We should perhaps define some particularly useful error codes
// here, so the resolver can handle errors more meaningfully.
//
/// Fetch the declared dependencies for a particular version.
///
/// This property is expected to be efficient to access, and cached by the
/// client if necessary.
///
/// - Precondition: `versions.contains(version)`
/// - Throws: If the version could not be resolved; this will abort
/// dependency resolution completely.
func getDependencies(at version: Version, productFilter: ProductFilter) throws -> [PackageContainerConstraint]
/// Fetch the declared dependencies for a particular revision.
///
/// This property is expected to be efficient to access, and cached by the
/// client if necessary.
///
/// - Throws: If the revision could not be resolved; this will abort
/// dependency resolution completely.
func getDependencies(at revision: String, productFilter: ProductFilter) throws -> [PackageContainerConstraint]
/// Fetch the dependencies of an unversioned package container.
///
/// NOTE: This method should not be called on a versioned container.
func getUnversionedDependencies(productFilter: ProductFilter) throws -> [PackageContainerConstraint]
/// Get the updated identifier at a bound version.
///
/// This can be used by the containers to fill in the missing information that is obtained
/// after the container is available. The updated identifier is returned in result of the
/// dependency resolution.
func loadPackageReference(at boundVersion: BoundVersion) throws -> PackageReference
}
extension PackageContainer {
public func reversedVersions() throws -> [Version] {
try self.versionsDescending()
}
public func versionsDescending() throws -> [Version] {
try self.versionsAscending().reversed()
}
public var shouldInvalidatePinnedVersions: Bool {
return true
}
}
public protocol CustomPackageContainer: PackageContainer {
/// Retrieve the package using this package container.
func retrieve(
at version: Version,
progressHandler: ((_ bytesReceived: Int64, _ totalBytes: Int64?) -> Void)?,
observabilityScope: ObservabilityScope
) throws -> AbsolutePath
/// Get the custom file system for this package container.
func getFileSystem() throws -> FileSystem?
}
public extension CustomPackageContainer {
func retrieve(at version: Version, observabilityScope: ObservabilityScope) throws -> AbsolutePath {
return try self.retrieve(at: version, progressHandler: .none, observabilityScope: observabilityScope)
}
}
// MARK: - PackageContainerConstraint
/// An individual constraint onto a container.
public struct PackageContainerConstraint: Equatable, Hashable {
/// The identifier for the container the constraint is on.
public let package: PackageReference
/// The constraint requirement.
public let requirement: PackageRequirement
/// The required products.
public let products: ProductFilter
/// Create a constraint requiring the given `container` satisfying the
/// `requirement`.
public init(package: PackageReference, requirement: PackageRequirement, products: ProductFilter) {
self.package = package
self.requirement = requirement
self.products = products
}
/// Create a constraint requiring the given `container` satisfying the
/// `versionRequirement`.
public init(package: PackageReference, versionRequirement: VersionSetSpecifier, products: ProductFilter) {
self.init(package: package, requirement: .versionSet(versionRequirement), products: products)
}
}
extension PackageContainerConstraint: CustomStringConvertible {
public var description: String {
return "Constraint(\(self.package), \(requirement), \(products)"
}
}
// MARK: - PackageContainerProvider
/// An interface for resolving package containers.
public protocol PackageContainerProvider {
/// Get the container for a particular identifier asynchronously.
@available(*, noasync, message: "Use the async alternative")
func getContainer(
for package: PackageReference,
updateStrategy: ContainerUpdateStrategy,
observabilityScope: ObservabilityScope,
on queue: DispatchQueue,
completion: @escaping (Result<PackageContainer, Error>) -> Void
)
}
public extension PackageContainerProvider {
func getContainer(
for package: PackageReference,
updateStrategy: ContainerUpdateStrategy,
observabilityScope: ObservabilityScope,
on queue: DispatchQueue
) async throws -> PackageContainer {
try await safe_async {
self.getContainer(
for: package,
updateStrategy: updateStrategy,
observabilityScope: observabilityScope,
on: queue,
completion: $0)
}
}
}
/// Only used for source control containers and as such a mirror of RepositoryUpdateStrategy
/// This duplication is unfortunate - ideally this is not a concern of the ContainerProvider at all
/// but it is required give how PackageContainerProvider currently integrated into the resolver
public enum ContainerUpdateStrategy {
case never
case always
case ifNeeded(revision: String)
}
|