Skip to content

Commit 5667965

Browse files
authored
improve correctness by using identity more broadly throughout the code (#3244)
motivation: improve correctness by using identity more broadly throughout the code, prepare to package registry changes: * refector as PackageDependencyDescription with "scm" and "local" cases, this reflects better the true meaning and associated data with the two options and prepares us to adding a third option for registry * create new abstraction IdentityResolver, with default implementation that follows existing identity resolution logic * move mirror logic into IdentityResolver, and simplify code that used mirrors * move all logic to generate identity, apply mirrors to PackageDependencyDescription so it is handles as soon as possible in the call stack * adjust call-sites * adjust and add tests for mirror
1 parent 8a9ac47 commit 5667965

File tree

51 files changed

+1065
-575
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+1065
-575
lines changed

Examples/package-info/Sources/package-info/main.swift

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,10 @@ let packagePath = localFileSystem.currentWorkingDirectory!
3131
// There are several levels of information available.
3232
// Each takes longer to load than the level above it, but provides more detail.
3333
let diagnostics = DiagnosticsEngine()
34-
let manifest = try tsc_await { ManifestLoader.loadManifest(at: packagePath, kind: .local, swiftCompiler: swiftCompiler, swiftCompilerFlags: [], on: .global(), completion: $0) }
35-
let loadedPackage = try tsc_await { PackageBuilder.loadPackage(at: packagePath, swiftCompiler: swiftCompiler, swiftCompilerFlags: [], diagnostics: diagnostics, on: .global(), completion: $0) }
36-
let graph = try Workspace.loadGraph(packagePath: packagePath, swiftCompiler: swiftCompiler, swiftCompilerFlags: [], diagnostics: diagnostics)
34+
let identityResolver = DefaultIdentityResolver()
35+
let manifest = try tsc_await { ManifestLoader.loadManifest(at: packagePath, kind: .local, swiftCompiler: swiftCompiler, swiftCompilerFlags: [], identityResolver: identityResolver, on: .global(), completion: $0) }
36+
let loadedPackage = try tsc_await { PackageBuilder.loadPackage(at: packagePath, swiftCompiler: swiftCompiler, swiftCompilerFlags: [], identityResolver: identityResolver, diagnostics: diagnostics, on: .global(), completion: $0) }
37+
let graph = try Workspace.loadGraph(packagePath: packagePath, swiftCompiler: swiftCompiler, swiftCompilerFlags: [], identityResolver: identityResolver, diagnostics: diagnostics)
3738

3839
// EXAMPLES
3940
// ========
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// swift-tools-version:5.3
2+
3+
import PackageDescription
4+
5+
let package = Package(
6+
name: "App",
7+
dependencies: [
8+
.package(name: "Foo", url: "../Foo", .branch("main")),
9+
.package(name: "Bar", url: "../Bar", .branch("main")),
10+
],
11+
targets: [
12+
.target(name: "App", dependencies: [
13+
.product(name: "Foo", package: "Foo"),
14+
.product(name: "Bar", package: "Bar"),
15+
], path: "./")
16+
]
17+
)
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import Foo
2+
import Bar
3+
4+
public func main() {
5+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
public func hello() {
2+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// swift-tools-version:4.2
2+
import PackageDescription
3+
4+
let package = Package(
5+
name: "Bar",
6+
products: [
7+
.library(name: "Bar", targets: ["Bar"]),
8+
],
9+
targets: [
10+
.target(name: "Bar", path: "./"),
11+
]
12+
)
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
public func hello() {
2+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// swift-tools-version:4.2
2+
import PackageDescription
3+
4+
let package = Package(
5+
name: "Bar",
6+
products: [
7+
.library(name: "Bar", targets: ["Bar"]),
8+
],
9+
targets: [
10+
.target(name: "Bar", path: "./"),
11+
]
12+
)
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
public func hello() {
2+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// swift-tools-version:4.2
2+
import PackageDescription
3+
4+
let package = Package(
5+
name: "Foo",
6+
products: [
7+
.library(name: "Foo", targets: ["Foo"]),
8+
],
9+
targets: [
10+
.target(name: "Foo", path: "./"),
11+
]
12+
)

Sources/Commands/Describe.swift

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,14 +100,22 @@ fileprivate struct DescribedPackage: Encodable {
100100

101101
/// Represents a package dependency for the sole purpose of generating a description.
102102
struct DescribedPackageDependency: Encodable {
103+
let identity: PackageIdentity
103104
let name: String?
104105
let url: String?
105106
let requirement: PackageDependencyDescription.Requirement?
106107

107108
init(from dependency: PackageDependencyDescription) {
109+
self.identity = dependency.identity
108110
self.name = dependency.explicitNameForTargetDependencyResolutionOnly
109-
self.url = dependency.location
110-
self.requirement = dependency.requirement
111+
switch dependency {
112+
case .local(let data):
113+
self.url = data.path.pathString
114+
self.requirement = nil
115+
case .scm(let data):
116+
self.url = data.location
117+
self.requirement = data.requirement
118+
}
111119
}
112120
}
113121

Sources/Commands/SwiftTool.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -784,6 +784,7 @@ public class SwiftTool {
784784
case (false, .shared):
785785
cachePath = try self.getCachePath().map{ $0.appending(component: "manifests") }
786786
}
787+
787788
return try ManifestLoader(
788789
// Always use the host toolchain's resources for parsing manifest.
789790
manifestResources: self._hostToolchain.get().manifestResources,

Sources/PackageGraph/LocalPackageContainer.swift

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import TSCUtility
2525
/// Examples: Root packages, local dependencies, edited packages.
2626
public final class LocalPackageContainer: PackageContainer {
2727
public let package: PackageReference
28-
private let mirrors: DependencyMirrors
28+
private let identityResolver: IdentityResolver
2929
private let manifestLoader: ManifestLoaderProtocol
3030
private let toolsVersionLoader: ToolsVersionLoaderProtocol
3131
private let currentToolsVersion: ToolsVersion
@@ -53,6 +53,7 @@ public final class LocalPackageContainer: PackageContainer {
5353
version: nil,
5454
revision: nil,
5555
toolsVersion: toolsVersion,
56+
identityResolver: identityResolver,
5657
fileSystem: fileSystem,
5758
diagnostics: nil,
5859
on: .global(),
@@ -62,7 +63,7 @@ public final class LocalPackageContainer: PackageContainer {
6263
}
6364

6465
public func getUnversionedDependencies(productFilter: ProductFilter) throws -> [PackageContainerConstraint] {
65-
return try loadManifest().dependencyConstraints(productFilter: productFilter, mirrors: mirrors)
66+
return try loadManifest().dependencyConstraints(productFilter: productFilter)
6667
}
6768

6869
public func getUpdatedIdentifier(at boundVersion: BoundVersion) throws -> PackageReference {
@@ -73,15 +74,15 @@ public final class LocalPackageContainer: PackageContainer {
7374

7475
public init(
7576
package: PackageReference,
76-
mirrors: DependencyMirrors,
77+
identityResolver: IdentityResolver,
7778
manifestLoader: ManifestLoaderProtocol,
7879
toolsVersionLoader: ToolsVersionLoaderProtocol,
7980
currentToolsVersion: ToolsVersion,
8081
fileSystem: FileSystem = localFileSystem
8182
) {
8283
assert(URL.scheme(package.location) == nil, "unexpected scheme \(URL.scheme(package.location)!) in \(package.location)")
8384
self.package = package
84-
self.mirrors = mirrors
85+
self.identityResolver = identityResolver
8586
self.manifestLoader = manifestLoader
8687
self.toolsVersionLoader = toolsVersionLoader
8788
self.currentToolsVersion = currentToolsVersion

Sources/PackageGraph/PackageGraph+Loading.swift

Lines changed: 29 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ extension PackageGraph {
2020
/// Load the package graph for the given package path.
2121
public static func load(
2222
root: PackageGraphRoot,
23-
mirrors: DependencyMirrors = [:],
23+
identityResolver: IdentityResolver,
2424
additionalFileRules: [FileRuleDescription] = [],
2525
externalManifests: [Manifest],
2626
requiredDependencies: Set<PackageReference> = [],
@@ -39,25 +39,24 @@ extension PackageGraph {
3939
// the URL but that shouldn't be needed after <rdar://problem/33693433>
4040
// Ensure that identity and package name are the same once we have an
4141
// API to specify identity in the manifest file
42-
let manifestMapSequence = (root.manifests + externalManifests).map({ (PackageIdentity(url: $0.packageLocation), $0) })
42+
let manifestMapSequence = (root.manifests + externalManifests).map({ (identityResolver.resolveIdentity(for: $0.packageLocation), $0) })
4343
let manifestMap = Dictionary(uniqueKeysWithValues: manifestMapSequence)
4444
let successors: (GraphLoadingNode) -> [GraphLoadingNode] = { node in
45-
node.requiredDependencies().compactMap({ dependency in
46-
let url = mirrors.effectiveURL(forURL: dependency.location)
47-
return manifestMap[PackageIdentity(url: url)].map { manifest in
45+
node.requiredDependencies().compactMap{ dependency in
46+
return manifestMap[dependency.identity].map { manifest in
4847
GraphLoadingNode(manifest: manifest, productFilter: dependency.productFilter)
4948
}
50-
})
49+
}
5150
}
5251

5352
// Construct the root manifest and root dependencies set.
5453
let rootManifestSet = Set(root.manifests)
55-
let rootDependencies = Set(root.dependencies.compactMap({
56-
manifestMap[PackageIdentity(url: $0.location)]
57-
}))
54+
let rootDependencies = Set(root.dependencies.compactMap{
55+
manifestMap[$0.identity]
56+
})
5857
let rootManifestNodes = root.manifests.map { GraphLoadingNode(manifest: $0, productFilter: .everything) }
5958
let rootDependencyNodes = root.dependencies.lazy.compactMap { (dependency: PackageDependencyDescription) -> GraphLoadingNode? in
60-
guard let manifest = manifestMap[PackageIdentity(url: dependency.location)] else { return nil }
59+
guard let manifest = manifestMap[dependency.identity] else { return nil }
6160
return GraphLoadingNode(manifest: manifest, productFilter: dependency.productFilter)
6261
}
6362
let inputManifests = rootManifestNodes + rootDependencyNodes
@@ -130,7 +129,7 @@ extension PackageGraph {
130129
// Resolve dependencies and create resolved packages.
131130
let resolvedPackages = try createResolvedPackages(
132131
allManifests: allManifests,
133-
mirrors: mirrors,
132+
identityResolver: identityResolver,
134133
manifestToPackage: manifestToPackage,
135134
rootManifestSet: rootManifestSet,
136135
unsafeAllowedPackages: unsafeAllowedPackages,
@@ -188,7 +187,7 @@ private func checkAllDependenciesAreUsed(_ rootPackages: [ResolvedPackage], _ di
188187
/// Create resolved packages from the loaded packages.
189188
private func createResolvedPackages(
190189
allManifests: [GraphLoadingNode],
191-
mirrors: DependencyMirrors,
190+
identityResolver: IdentityResolver,
192191
manifestToPackage: [Manifest: Package],
193192
// FIXME: This shouldn't be needed once <rdar://problem/33693433> is fixed.
194193
rootManifestSet: Set<Manifest>,
@@ -211,7 +210,7 @@ private func createResolvedPackages(
211210

212211
// Create a map of package builders keyed by the package identity.
213212
let packageMapByIdentity: [PackageIdentity: ResolvedPackageBuilder] = packageBuilders.spm_createDictionary{
214-
let identity = PackageIdentity(url: $0.package.manifest.packageLocation)
213+
let identity = identityResolver.resolveIdentity(for: $0.package.manifest.packageLocation)
215214
return (identity, $0)
216215
}
217216
let packageMapByName: [String: ResolvedPackageBuilder] = packageBuilders.spm_createDictionary{ ($0.package.name, $0) }
@@ -223,8 +222,15 @@ private func createResolvedPackages(
223222
var dependencies = [ResolvedPackageBuilder]()
224223
// Establish the manifest-declared package dependencies.
225224
package.manifest.dependenciesRequired(for: packageBuilder.productFilter).forEach { dependency in
226-
let dependencyURL = mirrors.effectiveURL(forURL: dependency.location)
227-
let dependencyIdentity = PackageIdentity(url: dependencyURL)
225+
let dependencyIdentity = dependency.identity
226+
// FIXME: change this validation logic to use identity instead of location
227+
let dependencyLocation: String
228+
switch dependency {
229+
case .local(let data):
230+
dependencyLocation = data.path.pathString
231+
case .scm(let data):
232+
dependencyLocation = data.location
233+
}
228234

229235
// Use the package name to lookup the dependency. The package name will be present in packages with tools version >= 5.2.
230236
if let explicitDependencyName = dependency.explicitNameForTargetDependencyResolutionOnly, let resolvedPackage = packageMapByName[explicitDependencyName] {
@@ -234,7 +240,7 @@ private func createResolvedPackages(
234240
// FIXME: this works but the way we find out about this is based on a side effect, need to improve it when working on identity
235241
let error = PackageGraphError.dependencyAlreadySatisfiedByName(
236242
dependencyPackageName: package.name,
237-
dependencyURL: dependencyURL,
243+
dependencyLocation: dependencyLocation,
238244
otherDependencyURL: resolvedPackage.package.manifest.packageLocation,
239245
name: explicitDependencyName)
240246
let diagnosticLocation = PackageLocation.Local(name: package.name, packagePath: package.path)
@@ -251,7 +257,7 @@ private func createResolvedPackages(
251257
guard !dependencies.contains(resolvedPackage) else {
252258
let error = PackageGraphError.dependencyAlreadySatisfiedByIdentifier(
253259
dependencyPackageName: package.name,
254-
dependencyURL: dependencyURL,
260+
dependencyLocation: dependencyLocation,
255261
otherDependencyURL: resolvedPackage.package.manifest.packageLocation,
256262
identity: dependencyIdentity)
257263
let diagnosticLocation = PackageLocation.Local(name: package.name, packagePath: package.path)
@@ -262,10 +268,10 @@ private func createResolvedPackages(
262268
// check if this resolvedPackage url is the same as the dependency one
263269
// if not, this means that the dependencies share the same identity
264270
// FIXME: this works but the way we find out about this is based on a side effect, need to improve it when working on identity
265-
if resolvedPackage.package.manifest.packageLocation != dependencyURL {
271+
if resolvedPackage.package.manifest.packageLocation != dependencyLocation {
266272
let error = PackageGraphError.dependencyAlreadySatisfiedByIdentifier(
267273
dependencyPackageName: package.name,
268-
dependencyURL: dependencyURL,
274+
dependencyLocation: dependencyLocation,
269275
otherDependencyURL: resolvedPackage.package.manifest.packageLocation,
270276
identity: dependencyIdentity)
271277
let diagnosticLocation = PackageLocation.Local(name: package.name, packagePath: package.path)
@@ -274,7 +280,7 @@ private func createResolvedPackages(
274280
let error = PackageGraphError.incorrectPackageDependencyName(
275281
dependencyPackageName: package.name,
276282
dependencyName: explicitDependencyName,
277-
dependencyURL: dependencyURL,
283+
dependencyLocation: dependencyLocation,
278284
resolvedPackageName: resolvedPackage.package.name,
279285
resolvedPackageURL: resolvedPackage.package.manifest.packageLocation)
280286
let diagnosticLocation = PackageLocation.Local(name: package.name, packagePath: package.path)
@@ -414,14 +420,11 @@ private func createResolvedPackages(
414420
// explicitly reference the package containing the product, or for the product, package and
415421
// dependency to share the same name. We don't check this in manifest loading for root-packages so
416422
// we can provide a more detailed diagnostic here.
417-
let referencedPackageURL = mirrors.effectiveURL(forURL: product.packageBuilder.package.manifest.packageLocation)
418-
let referencedPackageIdentity = PackageIdentity(url: referencedPackageURL)
423+
let referencedPackageIdentity = identityResolver.resolveIdentity(for: product.packageBuilder.package.manifest.packageLocation)
419424
guard let packageDependency = (packageBuilder.package.manifest.dependencies.first { package in
420-
let packageURL = mirrors.effectiveURL(forURL: package.location)
421-
let packageIdentity = PackageIdentity(url: packageURL)
422-
return packageIdentity == referencedPackageIdentity
425+
return package.identity == referencedPackageIdentity
423426
}) else {
424-
throw InternalError("dependency reference for \(referencedPackageURL) not found")
427+
throw InternalError("dependency reference for \(product.packageBuilder.package.manifest.packageLocation) not found")
425428
}
426429

427430
let packageName = product.packageBuilder.package.name

Sources/PackageGraph/PackageGraph.swift

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,13 @@ enum PackageGraphError: Swift.Error {
2525
case productDependencyIncorrectPackage(name: String, package: String)
2626

2727
/// The package dependency name does not match the package name.
28-
case incorrectPackageDependencyName(dependencyPackageName: String, dependencyName: String, dependencyURL: String, resolvedPackageName: String, resolvedPackageURL: String)
28+
case incorrectPackageDependencyName(dependencyPackageName: String, dependencyName: String, dependencyLocation: String, resolvedPackageName: String, resolvedPackageURL: String)
2929

3030
/// The package dependency already satisfied by a different dependency package
31-
case dependencyAlreadySatisfiedByIdentifier(dependencyPackageName: String, dependencyURL: String, otherDependencyURL: String, identity: PackageIdentity)
31+
case dependencyAlreadySatisfiedByIdentifier(dependencyPackageName: String, dependencyLocation: String, otherDependencyURL: String, identity: PackageIdentity)
3232

3333
/// The package dependency already satisfied by a different dependency package
34-
case dependencyAlreadySatisfiedByName(dependencyPackageName: String, dependencyURL: String, otherDependencyURL: String, name: String)
34+
case dependencyAlreadySatisfiedByName(dependencyPackageName: String, dependencyLocation: String, otherDependencyURL: String, name: String)
3535

3636
/// The product dependency was found but the package name was not referenced correctly (tools version > 5.2).
3737
case productDependencyMissingPackage(
@@ -233,12 +233,12 @@ fileprivate extension PackageDependencyDescription {
233233
parameters.append("name: \"\(name)\"")
234234
}
235235

236-
if requirement == .localPackage {
237-
parameters.append("path: \"\(self.location)\"")
238-
} else {
239-
parameters.append("url: \"\(self.location)\"")
240-
241-
switch requirement {
236+
switch self {
237+
case .local(let data):
238+
parameters.append("path: \"\(data.path)\"")
239+
case .scm(let data):
240+
parameters.append("url: \"\(data.location)\"")
241+
switch data.requirement {
242242
case .branch(let branch):
243243
parameters.append(".branch(\"\(branch)\")")
244244
case .exact(let version):
@@ -253,8 +253,6 @@ fileprivate extension PackageDependencyDescription {
253253
} else {
254254
parameters.append(".upToNextMinor(\"\(range.lowerBound)\"..<\"\(range.upperBound)\")")
255255
}
256-
case .localPackage:
257-
fatalError("handled above")
258256
}
259257
}
260258

0 commit comments

Comments
 (0)