Skip to content

Commit 9ccbfdf

Browse files
authored
refactor managed artifacts (#3699)
motivation: ManagedArtifacts are based on location instead of on identity, refactor them to be based on identity as part of the transition to identity based logic changes: * transition ManagedArtifacts underlying hashmap to be based on identity * update call sites to use package identity instead of location when reading/writing managed artifacts * adjust tests where needed
1 parent 6f8a435 commit 9ccbfdf

File tree

5 files changed

+151
-150
lines changed

5 files changed

+151
-150
lines changed

Sources/SPMTestSupport/MockWorkspace.swift

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -379,7 +379,7 @@ public final class MockWorkspace {
379379
public func set(
380380
pins: [PackageReference: CheckoutState] = [:],
381381
managedDependencies: [ManagedDependency] = [],
382-
managedArtifacts: [ManagedArtifact] = []
382+
managedArtifacts: [Workspace.ManagedArtifact] = []
383383
) throws {
384384
let workspace = try self.getOrCreateWorkspace()
385385
let pinsStore = try workspace.pinsStore.load()
@@ -463,36 +463,44 @@ public final class MockWorkspace {
463463
}
464464

465465
public struct ManagedArtifactResult {
466-
public let managedArtifacts: ManagedArtifacts
466+
public let managedArtifacts: Workspace.ManagedArtifacts
467467

468-
public init(_ managedArtifacts: ManagedArtifacts) {
468+
public init(_ managedArtifacts: Workspace.ManagedArtifacts) {
469469
self.managedArtifacts = managedArtifacts
470470
}
471471

472+
public func checkNotPresent(packageName: String, targetName: String, file: StaticString = #file, line: UInt = #line) {
473+
self.checkNotPresent(packageIdentity: .plain(packageName), targetName: targetName, file : file, line: line)
474+
}
475+
472476
public func checkNotPresent(
473-
packageName: String,
477+
packageIdentity: PackageIdentity,
474478
targetName: String,
475479
file: StaticString = #file,
476480
line: UInt = #line
477481
) {
478-
let artifact = self.managedArtifacts[packageName: packageName, targetName: targetName]
479-
XCTAssert(artifact == nil, "Unexpectedly found \(packageName).\(targetName) in managed artifacts", file: file, line: line)
482+
let artifact = self.managedArtifacts[packageIdentity: packageIdentity, targetName: targetName]
483+
XCTAssert(artifact == nil, "Unexpectedly found \(packageIdentity).\(targetName) in managed artifacts", file: file, line: line)
480484
}
481485

482486
public func checkEmpty(file: StaticString = #file, line: UInt = #line) {
483487
XCTAssertEqual(self.managedArtifacts.count, 0, file: file, line: line)
484488
}
485489

490+
public func check(packageName: String, targetName: String, source: Workspace.ManagedArtifact.Source, path: AbsolutePath, file: StaticString = #file, line: UInt = #line) {
491+
self.check(packageIdentity: .plain(packageName), targetName: targetName, source: source, path: path, file: file, line: line)
492+
}
493+
486494
public func check(
487-
packageName: String,
495+
packageIdentity: PackageIdentity,
488496
targetName: String,
489-
source: ManagedArtifact.Source,
497+
source: Workspace.ManagedArtifact.Source,
490498
path: AbsolutePath,
491499
file: StaticString = #file,
492500
line: UInt = #line
493501
) {
494-
guard let artifact = managedArtifacts[packageName: packageName, targetName: targetName] else {
495-
XCTFail("\(packageName).\(targetName) does not exists", file: file, line: line)
502+
guard let artifact = managedArtifacts[packageIdentity: packageIdentity, targetName: targetName] else {
503+
XCTFail("\(packageIdentity).\(targetName) does not exists", file: file, line: line)
496504
return
497505
}
498506
XCTAssertEqual(artifact.path, path)

Sources/Workspace/ManagedArtifact.swift

Lines changed: 96 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -14,83 +14,83 @@ import SourceControl
1414
import TSCBasic
1515
import TSCUtility
1616

17-
/// A downloaded artifact managed by the workspace.
18-
public struct ManagedArtifact {
19-
/// The package reference.
20-
public let packageRef: PackageReference
21-
22-
/// The name of the binary target the artifact corresponds to.
23-
public let targetName: String
24-
25-
/// The source of the artifact (local or remote).
26-
public let source: Source
27-
28-
/// The path of the artifact on disk
29-
public let path: AbsolutePath
30-
31-
public init(
32-
packageRef: PackageReference,
33-
targetName: String,
34-
source: Source,
35-
path: AbsolutePath
36-
) {
37-
self.packageRef = packageRef
38-
self.targetName = targetName
39-
self.source = source
40-
self.path = path
41-
}
17+
extension Workspace {
18+
/// A downloaded artifact managed by the workspace.
19+
public struct ManagedArtifact {
20+
/// The package reference.
21+
public let packageRef: PackageReference
22+
23+
/// The name of the binary target the artifact corresponds to.
24+
public let targetName: String
25+
26+
/// The source of the artifact (local or remote).
27+
public let source: Source
28+
29+
/// The path of the artifact on disk
30+
public let path: AbsolutePath
31+
32+
public init(
33+
packageRef: PackageReference,
34+
targetName: String,
35+
source: Source,
36+
path: AbsolutePath
37+
) {
38+
self.packageRef = packageRef
39+
self.targetName = targetName
40+
self.source = source
41+
self.path = path
42+
}
4243

43-
/// Create an artifact downloaded from a remote url.
44-
public static func remote(
45-
packageRef: PackageReference,
46-
targetName: String,
47-
url: String,
48-
checksum: String,
49-
path: AbsolutePath
50-
) -> ManagedArtifact {
51-
return ManagedArtifact(
52-
packageRef: packageRef,
53-
targetName: targetName,
54-
source: .remote(url: url, checksum: checksum),
55-
path: path
56-
)
57-
}
44+
/// Create an artifact downloaded from a remote url.
45+
public static func remote(
46+
packageRef: PackageReference,
47+
targetName: String,
48+
url: String,
49+
checksum: String,
50+
path: AbsolutePath
51+
) -> ManagedArtifact {
52+
return ManagedArtifact(
53+
packageRef: packageRef,
54+
targetName: targetName,
55+
source: .remote(url: url, checksum: checksum),
56+
path: path
57+
)
58+
}
5859

59-
/// Create an artifact present locally on the filesystem.
60-
public static func local(
61-
packageRef: PackageReference,
62-
targetName: String,
63-
path: AbsolutePath
64-
) -> ManagedArtifact {
65-
return ManagedArtifact(
66-
packageRef: packageRef,
67-
targetName: targetName,
68-
source: .local,
69-
path: path
70-
)
71-
}
60+
/// Create an artifact present locally on the filesystem.
61+
public static func local(
62+
packageRef: PackageReference,
63+
targetName: String,
64+
path: AbsolutePath
65+
) -> ManagedArtifact {
66+
return ManagedArtifact(
67+
packageRef: packageRef,
68+
targetName: targetName,
69+
source: .local,
70+
path: path
71+
)
72+
}
7273

73-
/// Represents the source of the artifact.
74-
public enum Source: Equatable {
74+
/// Represents the source of the artifact.
75+
public enum Source: Equatable {
7576

76-
/// Represents a remote artifact, with the url it was downloaded from, its checksum, and its path relative to
77-
/// the workspace artifacts path.
78-
case remote(url: String, checksum: String)
77+
/// Represents a remote artifact, with the url it was downloaded from, its checksum, and its path relative to
78+
/// the workspace artifacts path.
79+
case remote(url: String, checksum: String)
7980

80-
/// Represents a locally available artifact, with its path relative to its package.
81-
case local
81+
/// Represents a locally available artifact, with its path relative to its package.
82+
case local
83+
}
8284
}
8385
}
8486

85-
// MARK: - CustomStringConvertible
86-
87-
extension ManagedArtifact: CustomStringConvertible {
87+
extension Workspace.ManagedArtifact: CustomStringConvertible {
8888
public var description: String {
8989
return "<ManagedArtifact: \(self.packageRef.name).\(self.targetName) \(self.source) \(self.path)>"
9090
}
9191
}
9292

93-
extension ManagedArtifact.Source: CustomStringConvertible {
93+
extension Workspace.ManagedArtifact.Source: CustomStringConvertible {
9494
public var description: String {
9595
switch self {
9696
case .local:
@@ -101,63 +101,59 @@ extension ManagedArtifact.Source: CustomStringConvertible {
101101
}
102102
}
103103

104-
// MARK: -
105-
106-
/// A collection of managed artifacts which have been downloaded.
107-
public final class ManagedArtifacts {
104+
// MARK: - ManagedArtifacts
108105

109-
/// A mapping from package url, to target name, to ManagedArtifact.
110-
private var artifactMap: [String: [String: ManagedArtifact]]
111-
112-
internal var artifacts: AnyCollection<ManagedArtifact> {
113-
AnyCollection(artifactMap.values.lazy.flatMap({ $0.values }))
114-
}
106+
extension Workspace {
107+
/// A collection of managed artifacts which have been downloaded.
108+
public final class ManagedArtifacts {
109+
/// A mapping from package identity, to target name, to ManagedArtifact.
110+
private var artifactMap: [PackageIdentity: [String: ManagedArtifact]]
115111

116-
init(artifactMap: [String: [String: ManagedArtifact]] = [:]) {
117-
self.artifactMap = artifactMap
118-
}
112+
internal var artifacts: AnyCollection<ManagedArtifact> {
113+
AnyCollection(self.artifactMap.values.lazy.flatMap{ $0.values })
114+
}
119115

120-
public subscript(packageURL packageURL: String, targetName targetName: String) -> ManagedArtifact? {
121-
artifactMap[packageURL]?[targetName]
122-
}
116+
init(_ artifacts: [ManagedArtifact] = []) {
117+
let artifactsByPackagePath = Dictionary(grouping: artifacts, by: { $0.packageRef.identity })
118+
self.artifactMap = artifactsByPackagePath.mapValues{ artifacts in
119+
Dictionary(uniqueKeysWithValues: artifacts.map{ ($0.targetName, $0) })
120+
}
121+
}
123122

124-
public subscript(packageName packageName: String, targetName targetName: String) -> ManagedArtifact? {
125-
artifacts.first(where: { $0.packageRef.name == packageName && $0.targetName == targetName })
126-
}
123+
public subscript(packageIdentity packageIdentity: PackageIdentity, targetName targetName: String) -> ManagedArtifact? {
124+
self.artifactMap[packageIdentity]?[targetName]
125+
}
127126

128-
public func add(_ artifact: ManagedArtifact) {
129-
artifactMap[artifact.packageRef.location, default: [:]][artifact.targetName] = artifact
130-
}
127+
public func add(_ artifact: ManagedArtifact) {
128+
self.artifactMap[artifact.packageRef.identity, default: [:]][artifact.targetName] = artifact
129+
}
131130

132-
public func remove(packageURL: String, targetName: String) {
133-
artifactMap[packageURL]?[targetName] = nil
131+
public func remove(packageIdentity: PackageIdentity, targetName: String) {
132+
self.artifactMap[packageIdentity]?[targetName] = nil
133+
}
134134
}
135135
}
136136

137-
// MARK: - Collection
138-
139-
extension ManagedArtifacts: Collection {
137+
extension Workspace.ManagedArtifacts: Collection {
140138
public var startIndex: AnyIndex {
141-
artifacts.startIndex
139+
self.artifacts.startIndex
142140
}
143141

144142
public var endIndex: AnyIndex {
145-
artifacts.endIndex
143+
self.artifacts.endIndex
146144
}
147145

148-
public subscript(index: AnyIndex) -> ManagedArtifact {
149-
artifacts[index]
146+
public subscript(index: AnyIndex) -> Workspace.ManagedArtifact {
147+
self.artifacts[index]
150148
}
151149

152150
public func index(after index: AnyIndex) -> AnyIndex {
153-
artifacts.index(after: index)
151+
self.artifacts.index(after: index)
154152
}
155153
}
156154

157-
// MARK: - CustomStringConvertible
158-
159-
extension ManagedArtifacts: CustomStringConvertible {
155+
extension Workspace.ManagedArtifacts: CustomStringConvertible {
160156
public var description: String {
161-
"<ManagedArtifacts: \(Array(artifacts))>"
157+
"<ManagedArtifacts: \(Array(self.artifacts))>"
162158
}
163159
}

Sources/Workspace/Workspace.swift

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1672,8 +1672,8 @@ extension Workspace {
16721672
}
16731673

16741674
for artifact in manifestArtifacts.local {
1675-
let existingArtifact = state.artifacts[
1676-
packageURL: artifact.packageRef.location,
1675+
let existingArtifact = self.state.artifacts[
1676+
packageIdentity: artifact.packageRef.identity,
16771677
targetName: artifact.targetName
16781678
]
16791679

@@ -1686,8 +1686,8 @@ extension Workspace {
16861686
}
16871687

16881688
for artifact in manifestArtifacts.remote {
1689-
let existingArtifact = state.artifacts[
1690-
packageURL: artifact.packageRef.location,
1689+
let existingArtifact = self.state.artifacts[
1690+
packageIdentity: artifact.packageRef.identity,
16911691
targetName: artifact.targetName
16921692
]
16931693

@@ -1712,7 +1712,7 @@ extension Workspace {
17121712
// Remove the artifacts and directories which are not needed anymore.
17131713
diagnostics.wrap {
17141714
for artifact in artifactsToRemove {
1715-
state.artifacts.remove(packageURL: artifact.packageRef.location, targetName: artifact.targetName)
1715+
state.artifacts.remove(packageIdentity: artifact.packageRef.identity, targetName: artifact.targetName)
17161716

17171717
if case .remote = artifact.source {
17181718
try fileSystem.removeFileTree(artifact.path)
@@ -2878,7 +2878,7 @@ private struct ArchiveIndexFile: Decodable {
28782878
}
28792879
}
28802880

2881-
private extension ManagedArtifact {
2881+
private extension Workspace.ManagedArtifact {
28822882
var originURL: String? {
28832883
switch self.source {
28842884
case .remote(let url, _):

0 commit comments

Comments
 (0)