Skip to content

Commit 87bc6c0

Browse files
committed
[Workspace] Improve pinAll logic
This correctly computes the pinning logic when there are managed dependencies which are not required in the package graph.
1 parent ee9dfa2 commit 87bc6c0

File tree

3 files changed

+94
-10
lines changed

3 files changed

+94
-10
lines changed

Sources/Workspace/Workspace.swift

Lines changed: 38 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,10 @@ public class Workspace {
143143

144144
/// Computes the identities which are declared in the manifests but aren't present in dependencies.
145145
func missingPackageIdentities() -> Set<String> {
146+
return computePackageIdentities().missing
147+
}
148+
149+
func computePackageIdentities() -> (required: Set<String>, missing: Set<String>) {
146150
let manifestsMap = Dictionary(items:
147151
root.manifests.map({ ($0.name.lowercased(), $0) }) +
148152
dependencies.map({ (PackageReference.computeIdentity(packageURL: $0.manifest.url), $0.manifest) }))
@@ -160,7 +164,9 @@ public class Workspace {
160164
// We should never have loaded a manifest we don't need.
161165
assert(availableIdentities.isSubset(of: requiredIdentities))
162166
// These are the missing package identities.
163-
return requiredIdentities.subtracting(availableIdentities)
167+
let missingIdentities = requiredIdentities.subtracting(availableIdentities)
168+
169+
return (requiredIdentities, missingIdentities)
164170
}
165171

166172
/// Returns constraints of the dependencies, including edited package constraints.
@@ -524,6 +530,7 @@ extension Workspace {
524530

525531
// Update the pins store.
526532
return pinAll(
533+
graphRoot: graphRoot,
527534
pinsStore: pinsStore,
528535
diagnostics: diagnostics)
529536
}
@@ -812,13 +819,21 @@ extension Workspace {
812819

813820
/// Pins all of the current managed dependencies at their checkout state.
814821
fileprivate func pinAll(
822+
graphRoot: PackageGraphRoot,
815823
pinsStore: PinsStore,
816824
diagnostics: DiagnosticsEngine
817825
) {
818-
// Reset the pinsStore and start pinning each dependency.
826+
// Reset the pinsStore and start pinning the required dependencies.
819827
pinsStore.unpinAll()
820-
for dependency in managedDependencies.values {
821-
pinsStore.pin(dependency)
828+
829+
let currentManifests = loadDependencyManifests(
830+
root: graphRoot, diagnostics: diagnostics)
831+
let requiredIdentities = currentManifests.computePackageIdentities().required
832+
833+
for dependency in managedDependencies.values {
834+
if requiredIdentities.contains(dependency.packageRef.identity) {
835+
pinsStore.pin(dependency)
836+
}
822837
}
823838
diagnostics.wrap({ try pinsStore.saveState() })
824839
}
@@ -1049,7 +1064,7 @@ extension Workspace {
10491064
// If we don't need resolution and there are no extra constraints,
10501065
// just validate pinsStore and return.
10511066
if !result.resolve && extraConstraints.isEmpty {
1052-
validatePinsStore(with: diagnostics)
1067+
validatePinsStore(dependencyManifests: currentManifests, diagnostics: diagnostics)
10531068
return currentManifests
10541069
}
10551070

@@ -1102,7 +1117,7 @@ extension Workspace {
11021117
}
11031118

11041119
// Update the pinsStore.
1105-
self.pinAll(pinsStore: pinsStore, diagnostics: diagnostics)
1120+
self.pinAll(graphRoot: graphRoot, pinsStore: pinsStore, diagnostics: diagnostics)
11061121

11071122
return loadDependencyManifests(root: graphRoot, diagnostics: diagnostics)
11081123
}
@@ -1190,20 +1205,33 @@ extension Workspace {
11901205
}
11911206

11921207
/// Validates that each checked out managed dependency has an entry in pinsStore.
1193-
private func validatePinsStore(with diagnostics: DiagnosticsEngine) {
1208+
private func validatePinsStore(dependencyManifests: DependencyManifests, diagnostics: DiagnosticsEngine) {
11941209
guard let pinsStore = diagnostics.wrap({ try pinsStore.load() }) else {
11951210
return
11961211
}
11971212

1213+
let pins = pinsStore.pinsMap.keys
1214+
let requiredIdentities = dependencyManifests.computePackageIdentities().required
1215+
11981216
for dependency in managedDependencies.values {
11991217
switch dependency.state {
12001218
case .checkout: break
12011219
case .edited, .local: continue
12021220
}
1203-
// If we find any checkout that is not in pins store, invoke pin all and return.
1204-
if pinsStore.pinsMap[dependency.packageRef.identity] == nil {
1205-
return self.pinAll(pinsStore: pinsStore, diagnostics: diagnostics)
1221+
1222+
let identity = dependency.packageRef.identity
1223+
1224+
if requiredIdentities.contains(identity) {
1225+
// If required identity contains this dependency, it should be in the pins store.
1226+
if pins.contains(identity) {
1227+
continue
1228+
}
1229+
} else if !pins.contains(identity) {
1230+
// Otherwise, it should *not* be in the pins store.
1231+
continue
12061232
}
1233+
1234+
return self.pinAll(graphRoot: dependencyManifests.root, pinsStore: pinsStore, diagnostics: diagnostics)
12071235
}
12081236
}
12091237

Tests/WorkspaceTests/WorkspaceTests.swift

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2174,6 +2174,61 @@ final class WorkspaceTests: XCTestCase {
21742174
result.check(dependency: "foo2", at: .local)
21752175
}
21762176
}
2177+
2178+
func testResolvedFileUpdate() throws {
2179+
let sandbox = AbsolutePath("/tmp/ws/")
2180+
let fs = InMemoryFileSystem()
2181+
2182+
let workspace = try TestWorkspace(
2183+
sandbox: sandbox,
2184+
fs: fs,
2185+
roots: [
2186+
TestPackage(
2187+
name: "Root",
2188+
targets: [
2189+
TestTarget(name: "Root", dependencies: []),
2190+
],
2191+
products: [],
2192+
dependencies: []
2193+
),
2194+
],
2195+
packages: [
2196+
TestPackage(
2197+
name: "Foo",
2198+
targets: [
2199+
TestTarget(name: "Foo"),
2200+
],
2201+
products: [
2202+
TestProduct(name: "Foo", targets: ["Foo"]),
2203+
],
2204+
versions: ["1.0.0"]
2205+
),
2206+
]
2207+
)
2208+
2209+
let deps: [TestWorkspace.PackageDependency] = [
2210+
.init(name: "Foo", requirement: .upToNextMajor(from: "1.0.0")),
2211+
]
2212+
workspace.checkPackageGraph(roots: ["Root"], deps: deps) { (_, diagnostics) in
2213+
XCTAssertNoDiagnostics(diagnostics)
2214+
}
2215+
workspace.checkManagedDependencies() { result in
2216+
result.check(dependency: "foo", at: .checkout(.version("1.0.0")))
2217+
}
2218+
workspace.checkResolved() { result in
2219+
result.check(dependency: "foo", at: .checkout(.version("1.0.0")))
2220+
}
2221+
2222+
workspace.checkPackageGraph(roots: ["Root"], deps: []) { (_, diagnostics) in
2223+
XCTAssertNoDiagnostics(diagnostics)
2224+
}
2225+
workspace.checkManagedDependencies() { result in
2226+
result.check(dependency: "foo", at: .checkout(.version("1.0.0")))
2227+
}
2228+
workspace.checkResolved() { result in
2229+
result.check(notPresent: "foo")
2230+
}
2231+
}
21772232
}
21782233

21792234
extension PackageGraph {

Tests/WorkspaceTests/XCTestManifests.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ extension WorkspaceTests {
6464
("testMultipleRootPackages", testMultipleRootPackages),
6565
("testResolutionFailureWithEditedDependency", testResolutionFailureWithEditedDependency),
6666
("testResolve", testResolve),
67+
("testResolvedFileUpdate", testResolvedFileUpdate),
6768
("testResolverCanHaveError", testResolverCanHaveError),
6869
("testRevisionVersionSwitch", testRevisionVersionSwitch),
6970
("testRootAsDependency1", testRootAsDependency1),

0 commit comments

Comments
 (0)