Skip to content

Commit 6cc2cc4

Browse files
committed
Implement reloadPackageStatusCallback using BSP messages
1 parent 66f24e3 commit 6cc2cc4

22 files changed

+425
-354
lines changed

Sources/BuildServerProtocol/CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ add_library(BuildServerProtocol STATIC
1111
RegisterForChangeNotifications.swift
1212
ShutdownBuild.swift
1313
SourceKitOptionsRequest.swift
14-
WaitForBuildSystemUpdates.swift)
14+
WaitForBuildSystemUpdates.swift
15+
WorkDoneProgress.swift)
1516
set_target_properties(BuildServerProtocol PROPERTIES
1617
INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_Swift_MODULE_DIRECTORY})
1718
target_link_libraries(BuildServerProtocol PRIVATE
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2022 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
import LanguageServerProtocol
14+
15+
public typealias CreateWorkDoneProgressRequest = LanguageServerProtocol.CreateWorkDoneProgressRequest
16+
public typealias WorkDoneProgress = LanguageServerProtocol.WorkDoneProgress

Sources/BuildSystemIntegration/BuildServerBuildSystem.swift

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ package actor BuildServerBuildSystem: MessageHandler {
6767
package private(set) var indexDatabasePath: AbsolutePath?
6868
package private(set) var indexStorePath: AbsolutePath?
6969

70-
package weak var messageHandler: BuiltInBuildSystemMessageHandler?
70+
package let connectionToSourceKitLSP: any Connection
7171

7272
/// The build settings that have been received from the build server.
7373
private var buildSettings: [DocumentURI: SourceKitOptionsResponse] = [:]
@@ -76,7 +76,7 @@ package actor BuildServerBuildSystem: MessageHandler {
7676

7777
package init(
7878
projectRoot: AbsolutePath,
79-
messageHandler: BuiltInBuildSystemMessageHandler?,
79+
connectionToSourceKitLSP: any Connection,
8080
fileSystem: FileSystem = localFileSystem
8181
) async throws {
8282
let configPath = projectRoot.appending(component: "buildServer.json")
@@ -96,18 +96,18 @@ package actor BuildServerBuildSystem: MessageHandler {
9696
#endif
9797
self.projectRoot = projectRoot
9898
self.serverConfig = config
99-
self.messageHandler = messageHandler
99+
self.connectionToSourceKitLSP = connectionToSourceKitLSP
100100
try await self.initializeBuildServer()
101101
}
102102

103103
/// Creates a build system using the Build Server Protocol config.
104104
///
105105
/// - Returns: nil if `projectRoot` has no config or there is an error parsing it.
106-
package init?(projectRoot: AbsolutePath?, messageHandler: BuiltInBuildSystemMessageHandler?) async {
106+
package init?(projectRoot: AbsolutePath?, connectionToSourceKitLSP: any Connection) async {
107107
guard let projectRoot else { return nil }
108108

109109
do {
110-
try await self.init(projectRoot: projectRoot, messageHandler: messageHandler)
110+
try await self.init(projectRoot: projectRoot, connectionToSourceKitLSP: connectionToSourceKitLSP)
111111
} catch is FileSystemError {
112112
// config file was missing, no build server for this workspace
113113
return nil
@@ -218,8 +218,8 @@ package actor BuildServerBuildSystem: MessageHandler {
218218
reply(.failure(ResponseError.methodNotFound(R.method)))
219219
}
220220

221-
func handleBuildTargetsChanged(_ notification: DidChangeBuildTargetNotification) async {
222-
await self.messageHandler?.sendNotificationToSourceKitLSP(notification)
221+
func handleBuildTargetsChanged(_ notification: DidChangeBuildTargetNotification) {
222+
connectionToSourceKitLSP.send(notification)
223223
}
224224

225225
func handleFileOptionsChanged(_ notification: FileOptionsChangedNotification) async {
@@ -238,7 +238,7 @@ package actor BuildServerBuildSystem: MessageHandler {
238238
// FIXME: (BSP migration) When running in the legacy mode where teh BSP server pushes build settings to us, we could
239239
// consider having a separate target for each source file so that we can update individual targets instead of having
240240
// to send an update for all targets.
241-
await self.messageHandler?.sendNotificationToSourceKitLSP(DidChangeBuildTargetNotification(changes: nil))
241+
connectionToSourceKitLSP.send(DidChangeBuildTargetNotification(changes: nil))
242242
}
243243
}
244244

Sources/BuildSystemIntegration/BuildSystemDelegate.swift

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,20 @@ package protocol BuildSystemManagerDelegate: AnyObject, Sendable {
3131
func buildTargetsChanged(_ changes: [BuildTargetEvent]?) async
3232

3333
/// Log the given message to the client's index log.
34+
// FIXME: (BSP Migration) Use `sendNotificationToClient`
3435
func logMessageToIndexLog(taskID: IndexTaskID, message: String)
3536

37+
/// Whether the client can handle `WorkDoneProgress` requests.
38+
var clientSupportsWorkDoneProgress: Bool { get async }
39+
40+
func sendNotificationToClient(_ notification: some NotificationType)
41+
42+
func sendRequestToClient<R: RequestType>(_ request: R) async throws -> R.Response
43+
44+
/// Wait until the connection to the client has been initialized, ie. wait until `SourceKitLSPServer` has replied
45+
/// to the `initialize` request.
46+
func waitUntilInitialized() async
47+
3648
/// Notify the delegate that the list of source files in the build system might have changed.
3749
func sourceFilesDidChange() async
3850
}

Sources/BuildSystemIntegration/BuildSystemManager.swift

Lines changed: 83 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
import BuildServerProtocol
1414
import Dispatch
15+
import Foundation
1516
import LanguageServerProtocol
1617
import SKLogging
1718
import SKOptions
@@ -148,8 +149,7 @@ package actor BuildSystemManager: MessageHandler {
148149
buildSystemKind: BuildSystemKind?,
149150
toolchainRegistry: ToolchainRegistry,
150151
options: SourceKitLSPOptions,
151-
buildSystemTestHooks: BuildSystemTestHooks,
152-
reloadPackageStatusCallback: @Sendable @escaping (ReloadPackageStatus) async -> Void
152+
buildSystemTestHooks: BuildSystemTestHooks
153153
) async {
154154
self.fallbackBuildSystem = FallbackBuildSystem(options: options.fallbackBuildSystemOrDefault)
155155
self.toolchainRegistry = toolchainRegistry
@@ -162,8 +162,7 @@ package actor BuildSystemManager: MessageHandler {
162162
toolchainRegistry: toolchainRegistry,
163163
options: options,
164164
buildSystemTestHooks: buildSystemTestHooks,
165-
connectionToSourceKitLSP: connectionFromBuildSystemToSourceKitLSP,
166-
reloadPackageStatusCallback: reloadPackageStatusCallback
165+
connectionToSourceKitLSP: connectionFromBuildSystemToSourceKitLSP
167166
)
168167
if let buildSystem {
169168
let connectionFromSourceKitLSPToBuildSystem = LocalConnection(receiverName: "\(type(of: buildSystem))")
@@ -286,6 +285,8 @@ package actor BuildSystemManager: MessageHandler {
286285
await self.didChangeBuildTarget(notification: notification)
287286
case let notification as BuildServerProtocol.LogMessageNotification:
288287
await self.logMessage(notification: notification)
288+
case let notification as BuildServerProtocol.WorkDoneProgress:
289+
await self.workDoneProgress(notification: notification)
289290
default:
290291
logger.error("Ignoring unknown notification \(type(of: notification).method)")
291292
}
@@ -294,12 +295,87 @@ package actor BuildSystemManager: MessageHandler {
294295
/// Implementation of `MessageHandler`, handling requests from the build system.
295296
///
296297
/// - Important: Do not call directly.
297-
nonisolated package func handle<Request: RequestType>(
298+
nonisolated package func handle<R: RequestType>(
299+
_ params: R,
300+
id: RequestID,
301+
reply: @Sendable @escaping (LSPResult<R.Response>) -> Void
302+
) {
303+
let signposter = Logger(subsystem: LoggingScope.subsystem, category: "build-system-message-handling")
304+
.makeSignposter()
305+
let signpostID = signposter.makeSignpostID()
306+
let state = signposter.beginInterval("Request", id: signpostID, "\(R.self)")
307+
308+
messageHandlingQueue.async {
309+
signposter.emitEvent("Start handling", id: signpostID)
310+
await withTaskCancellationHandler {
311+
await self.handleImpl(params, id: id, reply: reply)
312+
signposter.endInterval("Request", state, "Done")
313+
} onCancel: {
314+
signposter.emitEvent("Cancelled", id: signpostID)
315+
}
316+
}
317+
}
318+
319+
private func handleImpl<Request: RequestType>(
298320
_ request: Request,
299321
id: RequestID,
300322
reply: @escaping @Sendable (LSPResult<Request.Response>) -> Void
301-
) {
302-
reply(.failure(ResponseError.methodNotFound(Request.method)))
323+
) async {
324+
let startDate = Date()
325+
326+
let request = RequestAndReply(request) { result in
327+
reply(result)
328+
let endDate = Date()
329+
Task {
330+
switch result {
331+
case .success(let response):
332+
logger.log(
333+
"""
334+
Succeeded (took \(endDate.timeIntervalSince(startDate) * 1000, privacy: .public)ms)
335+
\(Request.method, privacy: .public)
336+
\(response.forLogging)
337+
"""
338+
)
339+
case .failure(let error):
340+
logger.log(
341+
"""
342+
Failed (took \(endDate.timeIntervalSince(startDate) * 1000, privacy: .public)ms)
343+
\(Request.method, privacy: .public)(\(id, privacy: .public))
344+
\(error.forLogging, privacy: .private)
345+
"""
346+
)
347+
}
348+
}
349+
}
350+
351+
switch request {
352+
case let request as RequestAndReply<BuildServerProtocol.CreateWorkDoneProgressRequest>:
353+
await request.reply { try await self.createWorkDoneProgress(request: request.params) }
354+
default:
355+
await request.reply { throw ResponseError.methodNotFound(Request.method) }
356+
}
357+
}
358+
359+
private func createWorkDoneProgress(
360+
request: BuildServerProtocol.CreateWorkDoneProgressRequest
361+
) async throws -> BuildServerProtocol.CreateWorkDoneProgressRequest.Response {
362+
guard let delegate else {
363+
throw ResponseError.unknown("Connection to client closed")
364+
}
365+
guard await delegate.clientSupportsWorkDoneProgress else {
366+
throw ResponseError.unknown("Client does not support work done progress")
367+
}
368+
await delegate.waitUntilInitialized()
369+
return try await delegate.sendRequestToClient(request as LanguageServerProtocol.CreateWorkDoneProgressRequest)
370+
}
371+
372+
private func workDoneProgress(notification: BuildServerProtocol.WorkDoneProgress) async {
373+
guard let delegate else {
374+
logger.fault("Ignoring work done progress form build system because connection to client closed")
375+
return
376+
}
377+
await delegate.waitUntilInitialized()
378+
delegate.sendNotificationToClient(notification as LanguageServerProtocol.WorkDoneProgress)
303379
}
304380

305381
/// - Note: Needed so we can set the delegate from a different isolation context.

Sources/BuildSystemIntegration/BuiltInBuildSystemAdapter.swift

Lines changed: 9 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,6 @@ import ToolchainRegistry
2222
import struct TSCBasic.AbsolutePath
2323
import struct TSCBasic.RelativePath
2424

25-
// FIXME: (BSP Migration) This should be a MessageHandler once we have migrated all build system queries to BSP and can use
26-
// LocalConnection for the communication.
27-
package protocol BuiltInBuildSystemMessageHandler: AnyObject, Sendable {
28-
func sendNotificationToSourceKitLSP(_ notification: some NotificationType) async
29-
func sendRequestToSourceKitLSP<R: RequestType>(_ request: R) async throws -> R.Response
30-
}
31-
3225
package enum BuildSystemKind {
3326
case buildServer(projectRoot: AbsolutePath)
3427
case compilationDatabase(projectRoot: AbsolutePath)
@@ -51,37 +44,35 @@ private func createBuildSystem(
5144
options: SourceKitLSPOptions,
5245
buildSystemTestHooks: BuildSystemTestHooks,
5346
toolchainRegistry: ToolchainRegistry,
54-
messageHandler: BuiltInBuildSystemMessageHandler,
55-
reloadPackageStatusCallback: @Sendable @escaping (ReloadPackageStatus) async -> Void
47+
connectionToSourceKitLSP: any Connection
5648
) async -> BuiltInBuildSystem? {
5749
switch buildSystemKind {
5850
case .buildServer(let projectRoot):
59-
return await BuildServerBuildSystem(projectRoot: projectRoot, messageHandler: messageHandler)
51+
return await BuildServerBuildSystem(projectRoot: projectRoot, connectionToSourceKitLSP: connectionToSourceKitLSP)
6052
case .compilationDatabase(let projectRoot):
6153
return CompilationDatabaseBuildSystem(
6254
projectRoot: projectRoot,
6355
searchPaths: (options.compilationDatabaseOrDefault.searchPaths ?? []).compactMap {
6456
try? RelativePath(validating: $0)
6557
},
66-
messageHandler: messageHandler
58+
connectionToSourceKitLSP: connectionToSourceKitLSP
6759
)
6860
case .swiftPM(let projectRoot):
6961
return await SwiftPMBuildSystem(
7062
projectRoot: projectRoot,
7163
toolchainRegistry: toolchainRegistry,
7264
options: options,
73-
messageHandler: messageHandler,
74-
reloadPackageStatusCallback: reloadPackageStatusCallback,
65+
connectionToSourceKitLSP: connectionToSourceKitLSP,
7566
testHooks: buildSystemTestHooks.swiftPMTestHooks
7667
)
7768
case .testBuildSystem(let projectRoot):
78-
return TestBuildSystem(projectRoot: projectRoot, messageHandler: messageHandler)
69+
return TestBuildSystem(projectRoot: projectRoot, connectionToSourceKitLSP: connectionToSourceKitLSP)
7970
}
8071
}
8172

8273
/// A type that outwardly acts as a build server conforming to the Build System Integration Protocol and internally uses
8374
/// a `BuiltInBuildSystem` to satisfy the requests.
84-
package actor BuiltInBuildSystemAdapter: BuiltInBuildSystemMessageHandler, MessageHandler {
75+
package actor BuiltInBuildSystemAdapter: MessageHandler {
8576
/// The underlying build system
8677
// FIXME: (BSP Migration) This should be private, all messages should go through BSP. Only accessible from the outside for transition
8778
// purposes.
@@ -96,8 +87,7 @@ package actor BuiltInBuildSystemAdapter: BuiltInBuildSystemMessageHandler, Messa
9687
toolchainRegistry: ToolchainRegistry,
9788
options: SourceKitLSPOptions,
9889
buildSystemTestHooks: BuildSystemTestHooks,
99-
connectionToSourceKitLSP: LocalConnection,
100-
reloadPackageStatusCallback: @Sendable @escaping (ReloadPackageStatus) async -> Void
90+
connectionToSourceKitLSP: LocalConnection
10191
) async {
10292
guard let buildSystemKind else {
10393
return nil
@@ -109,8 +99,7 @@ package actor BuiltInBuildSystemAdapter: BuiltInBuildSystemMessageHandler, Messa
10999
options: options,
110100
buildSystemTestHooks: buildSystemTestHooks,
111101
toolchainRegistry: toolchainRegistry,
112-
messageHandler: self,
113-
reloadPackageStatusCallback: reloadPackageStatusCallback
102+
connectionToSourceKitLSP: connectionToSourceKitLSP
114103
)
115104
guard let buildSystem else {
116105
return nil
@@ -160,6 +149,7 @@ package actor BuiltInBuildSystemAdapter: BuiltInBuildSystemMessageHandler, Messa
160149
id: RequestID,
161150
reply: @Sendable @escaping (LSPResult<R.Response>) -> Void
162151
) {
152+
// FIXME: Can we share this between the different message handler implementations?
163153
let signposter = Logger(subsystem: LoggingScope.subsystem, category: "build-system-message-handling")
164154
.makeSignposter()
165155
let signpostID = signposter.makeSignpostID()
@@ -227,12 +217,4 @@ package actor BuiltInBuildSystemAdapter: BuiltInBuildSystemMessageHandler, Messa
227217
await request.reply { throw ResponseError.methodNotFound(Request.method) }
228218
}
229219
}
230-
231-
package func sendNotificationToSourceKitLSP(_ notification: some LanguageServerProtocol.NotificationType) async {
232-
connectionToSourceKitLSP.send(notification)
233-
}
234-
235-
package func sendRequestToSourceKitLSP<R: RequestType>(_ request: R) async throws -> R.Response {
236-
return try await connectionToSourceKitLSP.send(request)
237-
}
238220
}

Sources/BuildSystemIntegration/CompilationDatabaseBuildSystem.swift

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,7 @@ package actor CompilationDatabaseBuildSystem {
3838
}
3939
}
4040

41-
/// Callbacks that should be called if the list of possible test files has changed.
42-
package var testFilesDidChangeCallbacks: [() async -> Void] = []
43-
44-
package weak var messageHandler: BuiltInBuildSystemMessageHandler?
41+
package let connectionToSourceKitLSP: any Connection
4542

4643
package let projectRoot: AbsolutePath
4744

@@ -72,13 +69,13 @@ package actor CompilationDatabaseBuildSystem {
7269
package init?(
7370
projectRoot: AbsolutePath,
7471
searchPaths: [RelativePath],
75-
messageHandler: (any BuiltInBuildSystemMessageHandler)?,
72+
connectionToSourceKitLSP: any Connection,
7673
fileSystem: FileSystem = localFileSystem
7774
) {
7875
self.fileSystem = fileSystem
7976
self.projectRoot = projectRoot
8077
self.searchPaths = searchPaths
81-
self.messageHandler = messageHandler
78+
self.connectionToSourceKitLSP = connectionToSourceKitLSP
8279
if let compdb = tryLoadCompilationDatabase(directory: projectRoot, additionalSearchPaths: searchPaths, fileSystem) {
8380
self.compdb = compdb
8481
} else {
@@ -129,9 +126,9 @@ extension CompilationDatabaseBuildSystem: BuiltInBuildSystem {
129126
return BuildTargetSourcesResponse(items: [SourcesItem(target: .dummy, sources: sources)])
130127
}
131128

132-
package func didChangeWatchedFiles(notification: BuildServerProtocol.DidChangeWatchedFilesNotification) async {
129+
package func didChangeWatchedFiles(notification: BuildServerProtocol.DidChangeWatchedFilesNotification) {
133130
if notification.changes.contains(where: { self.fileEventShouldTriggerCompilationDatabaseReload(event: $0) }) {
134-
await self.reloadCompilationDatabase()
131+
self.reloadCompilationDatabase()
135132
}
136133
}
137134

@@ -194,16 +191,13 @@ extension CompilationDatabaseBuildSystem: BuiltInBuildSystem {
194191

195192
/// The compilation database has been changed on disk.
196193
/// Reload it and notify the delegate about build setting changes.
197-
private func reloadCompilationDatabase() async {
194+
private func reloadCompilationDatabase() {
198195
self.compdb = tryLoadCompilationDatabase(
199196
directory: projectRoot,
200197
additionalSearchPaths: searchPaths,
201198
self.fileSystem
202199
)
203200

204-
await messageHandler?.sendNotificationToSourceKitLSP(DidChangeBuildTargetNotification(changes: nil))
205-
for testFilesDidChangeCallback in testFilesDidChangeCallbacks {
206-
await testFilesDidChangeCallback()
207-
}
201+
connectionToSourceKitLSP.send(DidChangeBuildTargetNotification(changes: nil))
208202
}
209203
}

0 commit comments

Comments
 (0)