Skip to content

Commit d8f988b

Browse files
authored
Merge pull request #1380 from ahoppen/dont-prepare-pacakge-manifest
Don’t run a `swift build` command to prepare a package manifest
2 parents 98718d4 + e56c71f commit d8f988b

File tree

8 files changed

+76
-19
lines changed

8 files changed

+76
-19
lines changed

Sources/SKSwiftPMWorkspace/SwiftPMBuildSystem.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -550,6 +550,11 @@ extension SwiftPMBuildSystem: SKCore.BuildSystem {
550550
singleTarget target: ConfiguredTarget,
551551
indexProcessDidProduceResult: @Sendable (IndexProcessResult) -> Void
552552
) async throws {
553+
if target == .forPackageManifest {
554+
// Nothing to prepare for package manifests.
555+
return
556+
}
557+
553558
// TODO (indexing): Add a proper 'prepare' job in SwiftPM instead of building the target.
554559
// https://github.com/apple/sourcekit-lsp/issues/1254
555560
guard let toolchain = await toolchainRegistry.default else {

Sources/SKTestSupport/TestSourceKitLSPClient.swift

Lines changed: 33 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ extension SourceKitLSPServer.Options {
2727
public static let testDefault = Self(swiftPublishDiagnosticsDebounceDuration: 0)
2828
}
2929

30+
fileprivate struct NotificationTimeoutError: Error, CustomStringConvertible {
31+
var description: String = "Failed to receive next notification within timeout"
32+
}
33+
3034
/// A mock SourceKit-LSP client (aka. a mock editor) that behaves like an editor
3135
/// for testing purposes.
3236
///
@@ -229,21 +233,17 @@ public final class TestSourceKitLSPClient: MessageHandler {
229233
///
230234
/// - Note: This also returns any notifications sent before the call to
231235
/// `nextNotification`.
232-
public func nextNotification(timeout: TimeInterval = defaultTimeout) async throws -> any NotificationType {
233-
struct TimeoutError: Error, CustomStringConvertible {
234-
var description: String = "Failed to receive next notification within timeout"
235-
}
236-
236+
public func nextNotification(timeout: Duration = .seconds(defaultTimeout)) async throws -> any NotificationType {
237237
return try await withThrowingTaskGroup(of: (any NotificationType).self) { taskGroup in
238238
taskGroup.addTask {
239239
for await notification in self.notifications {
240240
return notification
241241
}
242-
throw TimeoutError()
242+
throw NotificationTimeoutError()
243243
}
244244
taskGroup.addTask {
245-
try await Task.sleep(nanoseconds: UInt64(timeout * 1_000_000_000))
246-
throw TimeoutError()
245+
try await Task.sleep(for: timeout)
246+
throw NotificationTimeoutError()
247247
}
248248
let result = try await taskGroup.next()!
249249
taskGroup.cancelAll()
@@ -256,7 +256,7 @@ public final class TestSourceKitLSPClient: MessageHandler {
256256
/// If the next notification is not a `PublishDiagnosticsNotification`, this
257257
/// methods throws.
258258
public func nextDiagnosticsNotification(
259-
timeout: TimeInterval = defaultTimeout
259+
timeout: Duration = .seconds(defaultTimeout)
260260
) async throws -> PublishDiagnosticsNotification {
261261
guard !usePullDiagnostics else {
262262
struct PushDiagnosticsError: Error, CustomStringConvertible {
@@ -272,7 +272,7 @@ public final class TestSourceKitLSPClient: MessageHandler {
272272
public func nextNotification<ExpectedNotificationType: NotificationType>(
273273
ofType: ExpectedNotificationType.Type,
274274
satisfying predicate: (ExpectedNotificationType) -> Bool = { _ in true },
275-
timeout: TimeInterval = defaultTimeout
275+
timeout: Duration = .seconds(defaultTimeout)
276276
) async throws -> ExpectedNotificationType {
277277
while true {
278278
let nextNotification = try await nextNotification(timeout: timeout)
@@ -282,6 +282,29 @@ public final class TestSourceKitLSPClient: MessageHandler {
282282
}
283283
}
284284

285+
/// Asserts that the test client does not receive a notification of the given type and satisfying the given predicate
286+
/// within the given duration.
287+
///
288+
/// For stable tests, the code that triggered the notification should be run before this assertion instead of relying
289+
/// on the duration.
290+
///
291+
/// The duration should not be 0 because we need to allow `nextNotification` some time to get the notification out of
292+
/// the `notifications` `AsyncStream`.
293+
public func assertDoesNotReceiveNotification<ExpectedNotificationType: NotificationType>(
294+
ofType: ExpectedNotificationType.Type,
295+
satisfying predicate: (ExpectedNotificationType) -> Bool = { _ in true },
296+
within duration: Duration = .seconds(0.2)
297+
) async throws {
298+
do {
299+
let notification = try await nextNotification(
300+
ofType: ExpectedNotificationType.self,
301+
satisfying: predicate,
302+
timeout: duration
303+
)
304+
XCTFail("Did not expect to receive notification but received \(notification)")
305+
} catch is NotificationTimeoutError {}
306+
}
307+
285308
/// Handle the next request of the given type that is sent to the client.
286309
///
287310
/// The request handler will only handle a single request. If the request is called again, the request handler won't

Sources/SemanticIndex/SemanticIndexManager.swift

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -394,24 +394,30 @@ public final actor SemanticIndexManager {
394394
let id = UUID()
395395
let task = Task(priority: priority) {
396396
await withLoggingScope("preparation") {
397+
defer {
398+
if inProgressPrepareForEditorTask?.id == id {
399+
inProgressPrepareForEditorTask = nil
400+
self.indexProgressStatusDidChange()
401+
}
402+
}
397403
// Should be kept in sync with `prepareFileForEditorFunctionality`
398404
guard let target = await buildSystemManager.canonicalConfiguredTarget(for: uri) else {
399405
return
400406
}
401407
if Task.isCancelled {
402408
return
403409
}
410+
if await preparationUpToDateTracker.isUpToDate(target) {
411+
// If the target is up-to-date, there is nothing to prepare.
412+
return
413+
}
404414
if inProgressPrepareForEditorTask?.id == id {
405415
if inProgressPrepareForEditorTask?.state != .determiningCanonicalConfiguredTarget {
406416
logger.fault("inProgressPrepareForEditorTask is in unexpected state")
407417
}
408418
inProgressPrepareForEditorTask?.state = .preparingTarget
409419
}
410420
await self.prepare(targets: [target], priority: nil)
411-
if inProgressPrepareForEditorTask?.id == id {
412-
inProgressPrepareForEditorTask = nil
413-
self.indexProgressStatusDidChange()
414-
}
415421
}
416422
}
417423
inProgressPrepareForEditorTask?.task.cancel()

Tests/SourceKitDTests/CrashRecoveryTests.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ final class CrashRecoveryTests: XCTestCase {
9090

9191
await swiftLanguageService._crash()
9292

93-
let crashedNotification = try await testClient.nextNotification(ofType: WorkDoneProgress.self, timeout: 5)
93+
let crashedNotification = try await testClient.nextNotification(ofType: WorkDoneProgress.self, timeout: .seconds(5))
9494
XCTAssertEqual(
9595
crashedNotification.value,
9696
.begin(
@@ -107,7 +107,7 @@ final class CrashRecoveryTests: XCTestCase {
107107
_ = try? await testClient.send(hoverRequest)
108108
let semanticFunctionalityRestoredNotification = try await testClient.nextNotification(
109109
ofType: WorkDoneProgress.self,
110-
timeout: 30
110+
timeout: .seconds(30)
111111
)
112112
XCTAssertEqual(semanticFunctionalityRestoredNotification.value, .end(WorkDoneProgressEnd()))
113113

Tests/SourceKitLSPTests/BackgroundIndexingTests.swift

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -931,4 +931,27 @@ final class BackgroundIndexingTests: XCTestCase {
931931
)
932932
XCTAssertEqual((result?.changes?.keys).map(Set.init), [uri, try project.uri(for: "Client.swift")])
933933
}
934+
935+
func testDontPreparePackageManifest() async throws {
936+
let project = try await SwiftPMTestProject(
937+
files: [
938+
"Lib.swift": ""
939+
],
940+
enableBackgroundIndexing: true
941+
)
942+
943+
_ = try await project.testClient.nextNotification(
944+
ofType: LogMessageNotification.self,
945+
satisfying: { $0.message.contains("Preparing MyLibrary") }
946+
)
947+
948+
// Opening the package manifest shouldn't cause any `swift build` calls to prepare them because they are not part of
949+
// a target that can be prepared.
950+
let (uri, _) = try project.openDocument("Package.swift")
951+
_ = try await project.testClient.send(DocumentDiagnosticsRequest(textDocument: TextDocumentIdentifier(uri)))
952+
try await project.testClient.assertDoesNotReceiveNotification(
953+
ofType: LogMessageNotification.self,
954+
satisfying: { $0.message.contains("Preparing") }
955+
)
956+
}
934957
}

Tests/SourceKitLSPTests/BuildSystemTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ final class BuildSystemTests: XCTestCase {
193193

194194
var receivedCorrectDiagnostic = false
195195
for _ in 0..<Int(defaultTimeout) {
196-
let refreshedDiags = try await testClient.nextDiagnosticsNotification(timeout: 1)
196+
let refreshedDiags = try await testClient.nextDiagnosticsNotification(timeout: .seconds(1))
197197
if refreshedDiags.diagnostics.count == 0, try text == documentManager.latestSnapshot(doc).text {
198198
receivedCorrectDiagnostic = true
199199
break

Tests/SourceKitLSPTests/LocalSwiftTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1427,6 +1427,6 @@ final class LocalSwiftTests: XCTestCase {
14271427
XCTAssertEqual(diag.message, "Cannot find 'bar' in scope")
14281428

14291429
// Ensure that we don't get a second `PublishDiagnosticsNotification`
1430-
await assertThrowsError(try await testClient.nextDiagnosticsNotification(timeout: 2))
1430+
await assertThrowsError(try await testClient.nextDiagnosticsNotification(timeout: .seconds(2)))
14311431
}
14321432
}

Tests/SourceKitLSPTests/MainFilesProviderTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ final class MainFilesProviderTests: XCTestCase {
219219
// diagnostics.
220220
var receivedCorrectDiagnostic = false
221221
for _ in 0..<Int(defaultTimeout) {
222-
let refreshedDiags = try await project.testClient.nextDiagnosticsNotification(timeout: 1)
222+
let refreshedDiags = try await project.testClient.nextDiagnosticsNotification(timeout: .seconds(1))
223223
if let diagnostic = refreshedDiags.diagnostics.only,
224224
diagnostic.message == "Unused variable 'fromMyFancyLibrary'"
225225
{

0 commit comments

Comments
 (0)