Skip to content

Commit ec69d3f

Browse files
tomerdExample Example
authored andcommitted
identity improvments
motivation: improve correctlness by using identity more broadly throughout the code changes: * remove name from package-reference, replace with alternate-identity for secondary (manifest driven) identity * use identity to lookup managed-dependencies and pins instead of urls * refactor pins map serialization * adjust tests
1 parent ec407ac commit ec69d3f

21 files changed

+336
-193
lines changed

Sources/Commands/SwiftPackageTool.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -910,11 +910,11 @@ fileprivate func logPackageChanges(changes: [(PackageReference, Workspace.Packag
910910
let currentVersion = pins.pinsMap[package.identity]?.state.description ?? ""
911911
switch change {
912912
case let .added(state):
913-
stream <<< "+ \(package.name) \(state.requirement.prettyPrinted)"
913+
stream <<< "+ \(package.identity) \(state.requirement.prettyPrinted)"
914914
case let .updated(state):
915-
stream <<< "~ \(package.name) \(currentVersion) -> \(package.name) \(state.requirement.prettyPrinted)"
915+
stream <<< "~ \(package.identity) \(currentVersion) -> \(package.identity) \(state.requirement.prettyPrinted)"
916916
case .removed:
917-
stream <<< "- \(package.name) \(currentVersion)"
917+
stream <<< "- \(package.identity) \(currentVersion)"
918918
case .unchanged:
919919
continue
920920
}

Sources/Commands/SwiftTool.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ private class ToolWorkspaceDelegate: WorkspaceDelegate {
141141
let dependencies = packages.lazy.map({ "'\($0.path)'" }).joined(separator: ", ")
142142
self.stdoutStream <<< "the following dependencies were added: \(dependencies)"
143143
case .packageRequirementChange(let package, let state, let requirement):
144-
self.stdoutStream <<< "dependency '\(package.name)' was "
144+
self.stdoutStream <<< "dependency '\(package.identity)' was "
145145

146146
switch state {
147147
case .checkout(let checkoutState)?:

Sources/PackageGraph/DependencyResolutionNode.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,6 @@ extension DependencyResolutionNode: Hashable {
134134

135135
extension DependencyResolutionNode: CustomStringConvertible {
136136
public var description: String {
137-
return "\(package.name)\(productFilter)"
137+
return "\(self.package.identity)\(productFilter)"
138138
}
139139
}

Sources/PackageGraph/DependencyResolver.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import PackageModel
1313

1414
public enum DependencyResolverError: Error, Equatable {
1515
/// A revision-based dependency contains a local package dependency.
16-
case revisionDependencyContainsLocalPackage(dependency: String, localPackage: String)
16+
case revisionDependencyContainsLocalPackage(dependency: PackageIdentity, localPackage: PackageIdentity)
1717
}
1818

1919
public class DependencyResolver {

Sources/PackageGraph/LocalPackageContainer.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ public final class LocalPackageContainer: PackageContainer {
6666
public func getUpdatedIdentifier(at boundVersion: BoundVersion) throws -> PackageReference {
6767
assert(boundVersion == .unversioned, "Unexpected bound version \(boundVersion)")
6868
let manifest = try loadManifest()
69-
return identifier.with(newName: manifest.name)
69+
return identifier.with(alternateIdentity: PackageIdentity(name: manifest.name))
7070
}
7171

7272
public init(

Sources/PackageGraph/PinsStore.swift

Lines changed: 39 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,11 @@
88
See http://swift.org/CONTRIBUTORS.txt for Swift project authors
99
*/
1010

11+
import Basics
12+
import PackageModel
13+
import SourceControl
1114
import TSCBasic
1215
import TSCUtility
13-
import SourceControl
14-
import PackageModel
1516

1617
public final class PinsStore {
1718
public typealias PinsMap = [PackageIdentity: PinsStore.Pin]
@@ -133,28 +134,54 @@ extension PinsStore: JSONSerializable {
133134
extension PinsStore.Pin: JSONMappable, JSONSerializable {
134135
/// Create an instance from JSON data.
135136
public init(json: JSON) throws {
136-
let name: String? = json.get("package")
137-
let url: String = try json.get("repositoryURL")
138-
let identity = PackageIdentity(url: url)
139-
let ref = PackageReference(identity: identity, path: url)
140-
self.packageRef = name.flatMap(ref.with(newName:)) ?? ref
137+
// backwards compatibility 12/2020
138+
let location: String
139+
if let value: String = json.get("location") {
140+
location = value
141+
} else if let value: String = json.get("repositoryURL") {
142+
location = value
143+
} else {
144+
throw InternalError("unknown location")
145+
}
146+
147+
// backwards compatibility 12/2020
148+
let identity: PackageIdentity
149+
if let value: PackageIdentity = json.get("identity") {
150+
identity = value
151+
} else {
152+
identity = PackageIdentity(url: location)
153+
}
154+
155+
// backwards compatibility 12/2020
156+
var alternateIdentity: PackageIdentity? = nil
157+
if let value: PackageIdentity = json.get("alternate_identity") {
158+
alternateIdentity = value
159+
} else if let value: String = json.get("name") {
160+
alternateIdentity = PackageIdentity(name: value)
161+
}
162+
let package = PackageReference(identity: identity, path: location)
163+
self.packageRef = alternateIdentity.map{ package.with(alternateIdentity: $0) } ?? package
141164
self.state = try json.get("state")
142165
}
143166

144167
/// Convert the pin to JSON.
145168
public func toJSON() -> JSON {
146-
return .init([
147-
"package": packageRef.name.toJSON(),
148-
"repositoryURL": packageRef.path,
169+
var map: [String: JSONSerializable] = [
170+
"identity": packageRef.identity,
171+
"location": packageRef.path,
149172
"state": state
150-
])
173+
]
174+
if let alternateIdentity = packageRef.alternateIdentity {
175+
map["alternate_identity"] = alternateIdentity
176+
}
177+
return .init(map)
151178
}
152179
}
153180

154181
// MARK: - SimplePersistanceProtocol
155182

156183
extension PinsStore: SimplePersistanceProtocol {
157184
public func restore(from json: JSON) throws {
158-
self.pinsMap = try Dictionary(json.get("pins").map({ ($0.packageRef.identity, $0) }), uniquingKeysWith: { first, _ in throw StringError("duplicated entry for package \"\(first.packageRef.name)\"") })
185+
self.pinsMap = try Dictionary(json.get("pins").map({ ($0.packageRef.identity, $0) }), uniquingKeysWith: { first, _ in throw StringError("duplicated entry for package \"\(first.packageRef.identity)\"") })
159186
}
160187
}

Sources/PackageGraph/Pubgrub/PubgrubDependencyResolver.swift

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -152,11 +152,9 @@ public struct PubgrubDependencyResolver {
152152

153153
/// Execute the resolution algorithm to find a valid assignment of versions.
154154
public func solve(constraints: [Constraint]) -> Result<[DependencyResolver.Binding], Error> {
155-
let root = DependencyResolutionNode.root(package: PackageReference(
155+
let root = DependencyResolutionNode.root(package: .root(
156156
identity: PackageIdentity(url: "<synthesized-root>"),
157-
path: "<synthesized-root-path>",
158-
name: nil,
159-
kind: .root
157+
path: AbsolutePath("/synthesized-root-path")
160158
))
161159

162160
do {
@@ -248,7 +246,7 @@ public struct PubgrubDependencyResolver {
248246
}
249247
}
250248
var finalAssignments: [DependencyResolver.Binding]
251-
= flattenedAssignments.keys.sorted(by: { $0.name < $1.name }).map { package in
249+
= flattenedAssignments.keys.sorted(by: { $0.identity < $1.identity }).map { package in
252250
let details = flattenedAssignments[package]!
253251
return (container: package, binding: details.binding, products: details.products)
254252
}
@@ -400,8 +398,8 @@ public struct PubgrubDependencyResolver {
400398
constraints.append(dependency)
401399
case .unversioned:
402400
throw DependencyResolverError.revisionDependencyContainsLocalPackage(
403-
dependency: package.name,
404-
localPackage: dependency.identifier.name
401+
dependency: package.identity,
402+
localPackage: dependency.identifier.identity
405403
)
406404
}
407405
}
@@ -1364,7 +1362,7 @@ private final class PubGrubPackageContainer {
13641362
let versions: [Version] = try self.underlying.versionsAscending()
13651363

13661364
guard let idx = versions.firstIndex(of: firstVersion) else {
1367-
throw InternalError("from version \(firstVersion) not found in \(node.package.name)")
1365+
throw InternalError("from version \(firstVersion) not found in \(node.package.identity)")
13681366
}
13691367

13701368
let sync = DispatchGroup()
@@ -1380,7 +1378,7 @@ private final class PubGrubPackageContainer {
13801378
}
13811379

13821380
guard case .success = sync.wait(timeout: .now() + timeout) else {
1383-
throw StringError("timeout computing \(node.package.name) bounds")
1381+
throw StringError("timeout computing \(node.package.identity) bounds")
13841382
}
13851383

13861384
return (lowerBounds, upperBounds)
@@ -1419,7 +1417,7 @@ private final class ContainerProvider {
14191417
/// Get a cached container for the given identifier, asserting / throwing if not found.
14201418
func getCachedContainer(for package: PackageReference) throws -> PubGrubPackageContainer {
14211419
guard let container = self.containersCache[package] else {
1422-
throw InternalError("container for \(package.name) expected to be cached")
1420+
throw InternalError("container for \(package.identity) expected to be cached")
14231421
}
14241422
return container
14251423
}
@@ -1564,6 +1562,6 @@ private extension PackageRequirement {
15641562

15651563
private extension DependencyResolutionNode {
15661564
var nameForDiagnostics: String {
1567-
return "'\(package.name)'"
1565+
return "'\(self.package.identity)'"
15681566
}
15691567
}

Sources/PackageGraph/RepositoryPackageContainer.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,7 @@ public class RepositoryPackageContainer: PackageContainer, CustomStringConvertib
248248
}
249249

250250
let manifest = try self.loadManifest(at: revision, version: version)
251-
return self.identifier.with(newName: manifest.name)
251+
return self.identifier.with(alternateIdentity: PackageIdentity(name: manifest.name))
252252
}
253253

254254
/// Returns true if the tools version is valid and can be used by this

Sources/PackageModel/Manifest/PackageDependencyDescription.swift

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,12 @@ public struct PackageDependencyDescription: Equatable, Codable {
3030
}
3131
}
3232

33+
// FIXME: remove this and use identity instead
3334
/// The name of the package dependency explicitly defined in the manifest, if any.
3435
public let explicitName: String?
3536

36-
/// The name of the packagedependency,
37+
// FIXME: remove this and use identity instead
38+
/// The name of the package dependency,
3739
/// either explicitly defined in the manifest,
3840
/// or derived from the URL.
3941
///
@@ -57,6 +59,7 @@ public struct PackageDependencyDescription: Equatable, Codable {
5759
requirement: Requirement,
5860
productFilter: ProductFilter = .everything
5961
) {
62+
// FIXME: remove this / move elsewhere. need a better model for location instead of string URL for both AbsolutePath and URLs
6063
// FIXME: SwiftPM can't handle file URLs with file:// scheme so we need to
6164
// strip that. We need to design a URL data structure for SwiftPM.
6265
let filePrefix = "file://"
@@ -68,7 +71,7 @@ public struct PackageDependencyDescription: Equatable, Codable {
6871
}
6972

7073
self.explicitName = name
71-
self.name = name ?? PackageReference.computeDefaultName(fromURL: normalizedURL)
74+
self.name = name ?? LegacyPackageIdentity.computeDefaultName(fromURL: normalizedURL)
7275
self.url = normalizedURL
7376
self.requirement = requirement
7477
self.productFilter = productFilter

Sources/PackageModel/PackageIdentity.swift

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,27 @@ public struct PackageIdentity: Hashable, CustomStringConvertible {
5151
public init(path: AbsolutePath) {
5252
self.init(path.pathString)
5353
}
54+
55+
/// Creates a package identity from a string
56+
/// - Parameter name: An identity for the package.
57+
public init(name: String) {
58+
// use as-is!
59+
self.init(name)
60+
}
61+
}
62+
63+
extension PackageIdentity {
64+
public var mangledToC99ExtendedIdentifier: String {
65+
get {
66+
self.description.spm_mangledToC99ExtendedIdentifier()
67+
}
68+
}
69+
70+
public var mangledToBundleIdentifier: String {
71+
get {
72+
self.description.spm_mangledToBundleIdentifier()
73+
}
74+
}
5475
}
5576

5677
extension PackageIdentity: Comparable {
@@ -94,7 +115,34 @@ struct LegacyPackageIdentity: PackageIdentityProvider, Equatable {
94115

95116
/// Instantiates an instance of the conforming type from a string representation.
96117
public init(_ string: String) {
97-
self.description = PackageReference.computeDefaultName(fromURL: string).lowercased()
118+
self.description = Self.computeDefaultName(fromURL: string).lowercased()
119+
}
120+
121+
/// Compute the default name of a package given its URL.
122+
public static func computeDefaultName(fromURL url: String) -> String {
123+
#if os(Windows)
124+
let isSeparator : (Character) -> Bool = { $0 == "/" || $0 == "\\" }
125+
#else
126+
let isSeparator : (Character) -> Bool = { $0 == "/" }
127+
#endif
128+
129+
// Get the last path component of the URL.
130+
// Drop the last character in case it's a trailing slash.
131+
var endIndex = url.endIndex
132+
if let lastCharacter = url.last, isSeparator(lastCharacter) {
133+
endIndex = url.index(before: endIndex)
134+
}
135+
136+
let separatorIndex = url[..<endIndex].lastIndex(where: isSeparator)
137+
let startIndex = separatorIndex.map { url.index(after: $0) } ?? url.startIndex
138+
var lastComponent = url[startIndex..<endIndex]
139+
140+
// Strip `.git` suffix if present.
141+
if lastComponent.hasSuffix(".git") {
142+
lastComponent = lastComponent.dropLast(4)
143+
}
144+
145+
return String(lastComponent)
98146
}
99147
}
100148

0 commit comments

Comments
 (0)