Skip to content

Commit 6e92d07

Browse files
authored
allow mirror between source control and registry dependencies (#6017) (#6115)
motivation: ease the transition to registry based dependencies changes: * extend the mirror API to allow mirror between source control and registry dependencies * make sure target based dependencies take potentially different identity into account * add tests
1 parent 147a3f2 commit 6e92d07

File tree

10 files changed

+820
-82
lines changed

10 files changed

+820
-82
lines changed

Sources/PackageGraph/DependencyMirrors.swift

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
import Foundation
1414
import OrderedCollections
15+
import PackageModel
1516
import TSCBasic
1617

1718
/// A collection of dependency mirrors.
@@ -148,6 +149,27 @@ public final class DependencyMirrors: Equatable {
148149
return sorted?.first
149150
}
150151
}
152+
153+
public func effectiveIdentity(for identity: PackageIdentity) throws -> PackageIdentity {
154+
// TODO: cache
155+
let mirrorIndex = try self.mapping.reduce(into: [PackageIdentity: PackageIdentity](), { partial, item in
156+
try partial[parseLocation(item.key)] = parseLocation(item.value)
157+
})
158+
159+
return mirrorIndex[identity] ?? identity
160+
}
161+
162+
private func parseLocation(_ location: String) throws -> PackageIdentity {
163+
if PackageIdentity.plain(location).scopeAndName != nil {
164+
return PackageIdentity.plain(location)
165+
} else if let path = try? AbsolutePath(validating: location) {
166+
return PackageIdentity(path: path)
167+
} else if let url = URL(string: location) {
168+
return PackageIdentity(url: url)
169+
} else {
170+
throw StringError("invalid location \(location), cannot extract identity")
171+
}
172+
}
151173
}
152174

153175
extension DependencyMirrors: Collection {

Sources/PackageLoading/ManifestJSONParser.swift

Lines changed: 103 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,13 @@ enum ManifestJSONParser {
5454
manifest.swiftLanguageVersions = try Self.parseSwiftLanguageVersion(package)
5555
manifest.products = try package.getArray("products").map(ProductDescription.init(v4:))
5656
manifest.providers = try? package.getArray("providers").map(SystemPackageProviderDescription.init(v4:))
57-
manifest.targets = try package.getArray("targets").map(Self.parseTarget(json:))
58-
manifest.dependencies = try package.getArray("dependencies").map{
57+
manifest.targets = try package.getArray("targets").map {
58+
try Self.parseTarget(
59+
json: $0,
60+
identityResolver: identityResolver
61+
)
62+
}
63+
manifest.dependencies = try package.getArray("dependencies").map {
5964
try Self.parseDependency(
6065
json: $0,
6166
toolsVersion: toolsVersion,
@@ -97,29 +102,35 @@ enum ManifestJSONParser {
97102
if type == "fileSystem" {
98103
let name: String? = kindJSON.get("name")
99104
let path: String = try kindJSON.get("path")
100-
return try Self.makeFileSystemDependency(
105+
return try Self.parseFileSystemDependency(
101106
packageKind: packageKind,
102107
at: path,
103108
name: name,
104109
identityResolver: identityResolver,
105-
fileSystem: fileSystem)
110+
fileSystem: fileSystem
111+
)
106112
} else if type == "sourceControl" {
107113
let name: String? = kindJSON.get("name")
108114
let location: String = try kindJSON.get("location")
109115
let requirementJSON: JSON = try kindJSON.get("requirement")
110116
let requirement = try PackageDependency.SourceControl.Requirement(v4: requirementJSON)
111-
return try Self.makeSourceControlDependency(
117+
return try Self.parseSourceControlDependency(
112118
packageKind: packageKind,
113119
at: location,
114120
name: name,
115121
requirement: requirement,
116122
identityResolver: identityResolver,
117-
fileSystem: fileSystem)
123+
fileSystem: fileSystem
124+
)
118125
} else if type == "registry" {
119126
let identity: String = try kindJSON.get("identity")
120127
let requirementJSON: JSON = try kindJSON.get("requirement")
121128
let requirement = try PackageDependency.Registry.Requirement(v4: requirementJSON)
122-
return .registry(identity: .plain(identity), requirement: requirement, productFilter: .everything)
129+
return try Self.parseRegistryDependency(
130+
identity: .plain(identity),
131+
requirement: requirement,
132+
identityResolver: identityResolver
133+
)
123134
} else {
124135
throw InternalError("Unknown dependency type \(kindJSON)")
125136
}
@@ -133,27 +144,28 @@ enum ManifestJSONParser {
133144
let requirementType: String = try requirementJSON.get(String.self, forKey: "type")
134145
switch requirementType {
135146
case "localPackage":
136-
return try Self.makeFileSystemDependency(
147+
return try Self.parseFileSystemDependency(
137148
packageKind: packageKind,
138149
at: url,
139150
name: name,
140151
identityResolver: identityResolver,
141-
fileSystem: fileSystem)
142-
152+
fileSystem: fileSystem
153+
)
143154
default:
144155
let requirement = try PackageDependency.SourceControl.Requirement(v4: requirementJSON)
145-
return try Self.makeSourceControlDependency(
156+
return try Self.parseSourceControlDependency(
146157
packageKind: packageKind,
147158
at: url,
148159
name: name,
149160
requirement: requirement,
150161
identityResolver: identityResolver,
151-
fileSystem: fileSystem)
162+
fileSystem: fileSystem
163+
)
152164
}
153165
}
154166
}
155167

156-
private static func makeFileSystemDependency(
168+
private static func parseFileSystemDependency(
157169
packageKind: PackageReference.Kind,
158170
at location: String,
159171
name: String?,
@@ -174,7 +186,7 @@ enum ManifestJSONParser {
174186
productFilter: .everything)
175187
}
176188

177-
private static func makeSourceControlDependency(
189+
private static func parseSourceControlDependency(
178190
packageKind: PackageReference.Kind,
179191
at location: String,
180192
name: String?,
@@ -186,8 +198,25 @@ enum ManifestJSONParser {
186198
var location = try sanitizeDependencyLocation(fileSystem: fileSystem, packageKind: packageKind, dependencyLocation: location)
187199
// location mapping (aka mirrors) if any
188200
location = identityResolver.mappedLocation(for: location)
189-
// a package in a git location, may be a remote URL or on disk
190-
if let localPath = try? AbsolutePath(validating: location) {
201+
if PackageIdentity.plain(location).scopeAndName != nil {
202+
// re-mapped to registry
203+
let identity = PackageIdentity.plain(location)
204+
let registryRequirement: PackageDependency.Registry.Requirement
205+
switch requirement {
206+
case .branch, .revision:
207+
throw StringError("invalid mapping of source control to registry, requirement information mismatch: cannot map branch or revision based dependencies to registry.")
208+
case .exact(let value):
209+
registryRequirement = .exact(value)
210+
case .range(let value):
211+
registryRequirement = .range(value)
212+
}
213+
return .registry(
214+
identity: identity,
215+
requirement: registryRequirement,
216+
productFilter: .everything
217+
)
218+
} else if let localPath = try? AbsolutePath(validating: location) {
219+
// a package in a git location, may be a remote URL or on disk
191220
// in the future this will check with the registries for the identity of the URL
192221
let identity = try identityResolver.resolveIdentity(for: localPath)
193222
return .localSourceControl(
@@ -212,6 +241,43 @@ enum ManifestJSONParser {
212241
}
213242
}
214243

244+
private static func parseRegistryDependency(
245+
identity: PackageIdentity,
246+
requirement: PackageDependency.Registry.Requirement,
247+
identityResolver: IdentityResolver
248+
) throws -> PackageDependency {
249+
// location mapping (aka mirrors) if any
250+
let location = identityResolver.mappedLocation(for: identity.description)
251+
if PackageIdentity.plain(location).scopeAndName != nil {
252+
// re-mapped to registry
253+
let identity = PackageIdentity.plain(location)
254+
return .registry(
255+
identity: identity,
256+
requirement: requirement,
257+
productFilter: .everything
258+
)
259+
} else if let url = URL(string: location){
260+
// in the future this will check with the registries for the identity of the URL
261+
let identity = try identityResolver.resolveIdentity(for: url)
262+
let sourceControlRequirement: PackageDependency.SourceControl.Requirement
263+
switch requirement {
264+
case .exact(let value):
265+
sourceControlRequirement = .exact(value)
266+
case .range(let value):
267+
sourceControlRequirement = .range(value)
268+
}
269+
return .remoteSourceControl(
270+
identity: identity,
271+
nameForTargetDependencyResolutionOnly: identity.description,
272+
url: url,
273+
requirement: sourceControlRequirement,
274+
productFilter: .everything
275+
)
276+
} else {
277+
throw StringError("invalid location: \(location)")
278+
}
279+
}
280+
215281
private static func sanitizeDependencyLocation(fileSystem: TSCBasic.FileSystem, packageKind: PackageReference.Kind, dependencyLocation: String) throws -> String {
216282
if dependencyLocation.hasPrefix("~/") {
217283
// If the dependency URL starts with '~/', try to expand it.
@@ -310,7 +376,10 @@ enum ManifestJSONParser {
310376
return platforms
311377
}
312378

313-
private static func parseTarget(json: JSON) throws -> TargetDescription {
379+
private static func parseTarget(
380+
json: JSON,
381+
identityResolver: IdentityResolver
382+
) throws -> TargetDescription {
314383
let providers = try? json
315384
.getArray("providers")
316385
.map(SystemPackageProviderDescription.init(v4:))
@@ -319,7 +388,12 @@ enum ManifestJSONParser {
319388

320389
let dependencies = try json
321390
.getArray("dependencies")
322-
.map(TargetDescription.Dependency.init(v4:))
391+
.map {
392+
try TargetDescription.Dependency.init(
393+
v4: $0,
394+
identityResolver: identityResolver
395+
)
396+
}
323397

324398
let sources: [String]? = try? json.get("sources")
325399
try sources?.forEach{ _ = try RelativePath(validating: $0) }
@@ -620,7 +694,7 @@ extension TargetDescription.TargetType {
620694
}
621695

622696
extension TargetDescription.Dependency {
623-
fileprivate init(v4 json: JSON) throws {
697+
fileprivate init(v4 json: JSON, identityResolver: IdentityResolver) throws {
624698
let type = try json.get(String.self, forKey: "type")
625699
let condition = try (try? json.getJSON("condition")).flatMap(PackageConditionDescription.init(v4:))
626700

@@ -631,7 +705,16 @@ extension TargetDescription.Dependency {
631705
case "product":
632706
let name = try json.get(String.self, forKey: "name")
633707
let moduleAliases: [String: String]? = try? json.get("moduleAliases")
634-
self = .product(name: name, package: json.get("package"), moduleAliases: moduleAliases, condition: condition)
708+
var package: String? = json.get("package")
709+
if let packageName = package {
710+
package = try identityResolver.mappedIdentity(for: .plain(packageName)).description
711+
}
712+
self = .product(
713+
name: name,
714+
package: package,
715+
moduleAliases: moduleAliases,
716+
condition: condition
717+
)
635718

636719
case "byname":
637720
self = try .byName(name: json.get("name"), condition: condition)

Sources/PackageModel/IdentityResolver.swift

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,19 @@ public protocol IdentityResolver {
1919
func resolveIdentity(for url: URL) throws -> PackageIdentity
2020
func resolveIdentity(for path: AbsolutePath) throws -> PackageIdentity
2121
func mappedLocation(for location: String) -> String
22+
func mappedIdentity(for identity: PackageIdentity) throws -> PackageIdentity
2223
}
2324

2425
public struct DefaultIdentityResolver: IdentityResolver {
2526
let locationMapper: (String) -> String
27+
let identityMapper: (PackageIdentity) throws -> PackageIdentity
2628

27-
public init(locationMapper: @escaping (String) -> String = { $0 }) {
29+
public init(
30+
locationMapper: @escaping (String) -> String = { $0 },
31+
identityMapper: @escaping (PackageIdentity) throws -> PackageIdentity = { $0 }
32+
) {
2833
self.locationMapper = locationMapper
34+
self.identityMapper = identityMapper
2935
}
3036

3137
public func resolveIdentity(for packageKind: PackageReference.Kind) throws -> PackageIdentity {
@@ -66,6 +72,10 @@ public struct DefaultIdentityResolver: IdentityResolver {
6672
}
6773

6874
public func mappedLocation(for location: String) -> String {
69-
return self.locationMapper(location)
75+
self.locationMapper(location)
76+
}
77+
78+
public func mappedIdentity(for identity: PackageIdentity) throws -> PackageIdentity {
79+
try self.identityMapper(identity)
7080
}
7181
}

0 commit comments

Comments
 (0)