Skip to content

Commit 724eb37

Browse files
authored
reload workspace state before reloading package graph (swiftlang#5510) (swiftlang#5515)
1 parent 731548b commit 724eb37

File tree

3 files changed

+99
-1
lines changed

3 files changed

+99
-1
lines changed

Sources/Workspace/Workspace+State.swift

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,14 +65,22 @@ public final class WorkspaceState {
6565
try self.save()
6666
}
6767

68+
// marked public for testing
6869
public func save() throws {
6970
try self.storage.save(dependencies: self.dependencies, artifacts: self.artifacts)
7071
}
7172

7273
/// Returns true if the state file exists on the filesystem.
73-
public func stateFileExists() -> Bool {
74+
func stateFileExists() -> Bool {
7475
return self.storage.fileExists()
7576
}
77+
78+
/// Returns true if the state file exists on the filesystem.
79+
func reload() throws {
80+
let storedState = try self.storage.load()
81+
self.dependencies = storedState.dependencies
82+
self.artifacts = storedState.artifacts
83+
}
7684
}
7785

7886
// MARK: - Serialization

Sources/Workspace/Workspace.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1069,6 +1069,10 @@ extension Workspace {
10691069
customXCTestMinimumDeploymentTargets: [PackageModel.Platform: PlatformVersion]? = .none,
10701070
observabilityScope: ObservabilityScope
10711071
) throws -> PackageGraph {
1072+
// reload state in case it was modified externally (eg by another process) before reloading the graph
1073+
// long running host processes (ie IDEs) need this in case other SwiftPM processes (ie CLI) made changes to the state
1074+
// such hosts processes call loadPackageGraph to make sure the workspace state is correct
1075+
try self.state.reload()
10721076

10731077
// Perform dependency resolution, if required.
10741078
let manifests: DependencyManifests

Tests/WorkspaceTests/WorkspaceTests.swift

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2757,6 +2757,92 @@ final class WorkspaceTests: XCTestCase {
27572757
}
27582758
}
27592759

2760+
func testStateModified() throws {
2761+
let sandbox = AbsolutePath("/tmp/ws/")
2762+
let fs = InMemoryFileSystem()
2763+
2764+
let workspace = try MockWorkspace(
2765+
sandbox: sandbox,
2766+
fileSystem: fs,
2767+
roots: [
2768+
MockPackage(
2769+
name: "Root",
2770+
targets: [
2771+
MockTarget(name: "Root", dependencies: [
2772+
.product(name: "Foo", package: "foo"),
2773+
.product(name: "Bar", package: "bar")
2774+
]),
2775+
],
2776+
dependencies: [
2777+
.sourceControl(url: "https://scm.com/org/foo", requirement: .upToNextMajor(from: "1.0.0")),
2778+
.sourceControl(url: "https://scm.com/org/bar", requirement: .upToNextMajor(from: "1.0.0")),
2779+
]
2780+
),
2781+
],
2782+
packages: [
2783+
MockPackage(
2784+
name: "Foo",
2785+
url: "https://scm.com/org/foo",
2786+
targets: [
2787+
MockTarget(name: "Foo"),
2788+
],
2789+
products: [
2790+
MockProduct(name: "Foo", targets: ["Foo"]),
2791+
],
2792+
versions: [nil, "1.0.0", "1.1.0"]
2793+
),
2794+
MockPackage(
2795+
name: "Bar",
2796+
url: "https://scm.com/org/bar",
2797+
targets: [
2798+
MockTarget(name: "Bar"),
2799+
],
2800+
products: [
2801+
MockProduct(name: "Bar", targets: ["Bar"]),
2802+
],
2803+
versions: ["1.0.0", "1.1.0", "1.2.0"]
2804+
),
2805+
]
2806+
)
2807+
2808+
try workspace.checkPackageGraph(roots: ["Root"], deps: []) { graph, diagnostics in
2809+
XCTAssertNoDiagnostics(diagnostics)
2810+
PackageGraphTester(graph) { result in
2811+
result.check(packages: "Bar", "Foo", "Root")
2812+
}
2813+
}
2814+
2815+
let underlying = try workspace.getOrCreateWorkspace()
2816+
let fooEditPath = sandbox.appending(components: ["edited", "foo"])
2817+
2818+
// mimic external process putting a dependency into edit mode
2819+
do {
2820+
try fs.createDirectory(fooEditPath, recursive: true)
2821+
try fs.writeFileContents(fooEditPath.appending(component: "Package.swift"), bytes: "// swift-tools-version: 5.6")
2822+
2823+
let fooState = underlying.state.dependencies[.plain("foo")]!
2824+
let externalState = WorkspaceState(fileSystem: fs, storageDirectory: underlying.state.storagePath.parentDirectory, initializationWarningHandler: { _ in })
2825+
externalState.dependencies.remove(fooState.packageRef.identity)
2826+
externalState.dependencies.add(try fooState.edited(subpath: .init("foo"), unmanagedPath: fooEditPath))
2827+
try externalState.save()
2828+
}
2829+
2830+
// reload graph after "external" change
2831+
try workspace.checkPackageGraph(roots: ["Root"], deps: []) { graph, diagnostics in
2832+
PackageGraphTester(graph) { result in
2833+
result.check(packages: "Bar", "Foo", "Root")
2834+
}
2835+
}
2836+
2837+
do {
2838+
let fooState = underlying.state.dependencies[.plain("foo")]!
2839+
guard case .edited(basedOn: _, unmanagedPath: fooEditPath) = fooState.state else {
2840+
XCTFail("'\(fooState.packageRef.identity)' dependency expected to be in edit mode, but was: \(fooState)")
2841+
return
2842+
}
2843+
}
2844+
}
2845+
27602846
func testSkipUpdate() throws {
27612847
let sandbox = AbsolutePath("/tmp/ws/")
27622848
let fs = InMemoryFileSystem()

0 commit comments

Comments
 (0)