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
|
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift open source project
//
// Copyright (c) 2014-2017 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 PackageModel
import enum TSCUtility.Git
/// Represents the input to the package graph root.
public struct PackageGraphRootInput {
/// The list of root packages.
public let packages: [AbsolutePath]
/// Top level dependencies to the graph.
public let dependencies: [PackageDependency]
/// Create a package graph root.
public init(packages: [AbsolutePath], dependencies: [PackageDependency] = []) {
self.packages = packages
self.dependencies = dependencies
}
}
/// Represents the inputs to the package graph.
public struct PackageGraphRoot {
/// The root packages.
public let packages: [PackageIdentity: (reference: PackageReference, manifest: Manifest)]
/// The root manifests.
public var manifests: [PackageIdentity: Manifest] {
return self.packages.compactMapValues { $0.manifest }
}
/// The root package references.
public var packageReferences: [PackageReference] {
return self.packages.values.map { $0.reference }
}
private let _dependencies: [PackageDependency]
/// The top level dependencies.
public var dependencies: [PackageDependency] {
guard let dependencyMapper else {
return self._dependencies
}
return self._dependencies.map { dependency in
do {
return try dependencyMapper.mappedDependency(
MappablePackageDependency(
dependency,
parentPackagePath: localFileSystem.currentWorkingDirectory ?? .root
),
fileSystem: localFileSystem
)
} catch {
observabilityScope.emit(warning: "could not map dependency \(dependency.identity): \(error.interpolationDescription)")
return dependency
}
}
}
private let dependencyMapper: DependencyMapper?
private let observabilityScope: ObservabilityScope
/// Create a package graph root.
/// Note this quietly skip inputs for which manifests are not found. this could be because the manifest failed to load or for some other reasons
// FIXME: This API behavior wrt to non-found manifests is fragile, but required by IDEs
// it may lead to incorrect assumption in downstream code which may expect an error if a manifest was not found
// we should refactor this API to more clearly return errors for inputs that do not have a corresponding manifest
public init(
input: PackageGraphRootInput,
manifests: [AbsolutePath: Manifest],
explicitProduct: String? = nil,
dependencyMapper: DependencyMapper? = nil,
observabilityScope: ObservabilityScope
) {
self.packages = input.packages.reduce(into: .init(), { partial, inputPath in
if let manifest = manifests[inputPath] {
let packagePath = manifest.path.parentDirectory
let identity = PackageIdentity(path: packagePath) // this does not use the identity resolver which is fine since these are the root packages
partial[identity] = (.root(identity: identity, path: packagePath), manifest)
}
})
// FIXME: Deprecate special casing once the manifest supports declaring used executable products.
// Special casing explicit products like this is necessary to pass the test suite and satisfy backwards compatibility.
// However, changing the dependencies based on the command line arguments may force pins to temporarily change,
// which can become a nuisance.
// Such pin switching can currently be worked around by declaring the executable product as a dependency of a dummy target.
// But in the future it might be worth providing a way of declaring them in the manifest without a dummy target,
// at which time the current special casing can be deprecated.
var adjustedDependencies = input.dependencies
if let explicitProduct {
// FIXME: `dependenciesRequired` modifies manifests and prevents conversion of `Manifest` to a value type
for dependency in manifests.values.lazy.map({ $0.dependenciesRequired(for: .everything) }).joined() {
adjustedDependencies.append(dependency.filtered(by: .specific([explicitProduct])))
}
}
self._dependencies = adjustedDependencies
self.dependencyMapper = dependencyMapper
self.observabilityScope = observabilityScope
}
/// Returns the constraints imposed by root manifests + dependencies.
public func constraints() throws -> [PackageContainerConstraint] {
let constraints = self.packageReferences.map {
PackageContainerConstraint(package: $0, requirement: .unversioned, products: .everything)
}
let depend = try dependencies.map{
PackageContainerConstraint(
package: $0.packageRef,
requirement: try $0.toConstraintRequirement(),
products: $0.productFilter
)
}
return constraints + depend
}
}
extension PackageDependency {
/// Returns the constraint requirement representation.
public func toConstraintRequirement() throws -> PackageRequirement {
switch self {
case .fileSystem:
return .unversioned
case .sourceControl(let settings):
return try settings.requirement.toConstraintRequirement()
case .registry(let settings):
return try settings.requirement.toConstraintRequirement()
}
}
}
extension PackageDependency.SourceControl.Requirement {
/// Returns the constraint requirement representation.
public func toConstraintRequirement() throws -> PackageRequirement {
switch self {
case .range(let range):
return .versionSet(.range(range))
case .revision(let identifier):
return .revision(identifier)
case .branch(let name):
return .revision(name)
case .exact(let version):
return .versionSet(.exact(version))
}
}
}
extension PackageDependency.Registry.Requirement {
/// Returns the constraint requirement representation.
public func toConstraintRequirement() throws -> PackageRequirement {
switch self {
case .range(let range):
return .versionSet(.range(range))
case .exact(let version):
return .versionSet(.exact(version))
}
}
}
|