12
12
13
13
import BuildServerProtocol
14
14
import Dispatch
15
+ import Foundation
15
16
import LanguageServerProtocol
16
17
import SKLogging
17
18
import SKOptions
@@ -69,7 +70,9 @@ fileprivate class RequestCache<Request: RequestType & Hashable, Result: Sendable
69
70
/// Since some `BuildSystem`s may require a bit of a time to compute their arguments asynchronously,
70
71
/// this class has a configurable `buildSettings` timeout which denotes the amount of time to give
71
72
/// the build system before applying the fallback arguments.
72
- package actor BuildSystemManager : BuiltInBuildSystemAdapterDelegate {
73
+ package actor BuildSystemManager : QueueBasedMessageHandler {
74
+ package static let signpostLoggingCategory : String = " build-system-manager-message-handling "
75
+
73
76
/// The files for which the delegate has requested change notifications, ie.
74
77
/// the files for which the delegate wants to get `filesDependenciesUpdated`
75
78
/// callbacks if the file's build settings.
@@ -141,25 +144,35 @@ package actor BuildSystemManager: BuiltInBuildSystemAdapterDelegate {
141
144
}
142
145
}
143
146
147
+ private let connectionFromBuildSystemToSourceKitLSP : LocalConnection
148
+ private var connectionToBuildSystem : LocalConnection ?
149
+
144
150
package init (
145
151
buildSystemKind: BuildSystemKind ? ,
146
152
toolchainRegistry: ToolchainRegistry ,
147
153
options: SourceKitLSPOptions ,
148
- swiftpmTestHooks: SwiftPMTestHooks ,
149
- reloadPackageStatusCallback: @Sendable @escaping ( ReloadPackageStatus) async -> Void
154
+ buildSystemTestHooks: BuildSystemTestHooks
150
155
) async {
151
156
self . fallbackBuildSystem = FallbackBuildSystem ( options: options. fallbackBuildSystemOrDefault)
152
157
self . toolchainRegistry = toolchainRegistry
153
158
self . options = options
154
159
self . projectRoot = buildSystemKind? . projectRoot
160
+ connectionFromBuildSystemToSourceKitLSP = LocalConnection ( receiverName: " BuildSystemManager " )
161
+ connectionFromBuildSystemToSourceKitLSP. start ( handler: self )
155
162
self . buildSystem = await BuiltInBuildSystemAdapter (
156
163
buildSystemKind: buildSystemKind,
157
164
toolchainRegistry: toolchainRegistry,
158
165
options: options,
159
- swiftpmTestHooks: swiftpmTestHooks,
160
- reloadPackageStatusCallback: reloadPackageStatusCallback,
161
- messageHandler: self
166
+ buildSystemTestHooks: buildSystemTestHooks,
167
+ connectionToSourceKitLSP: connectionFromBuildSystemToSourceKitLSP
162
168
)
169
+ if let buildSystem {
170
+ let connectionFromSourceKitLSPToBuildSystem = LocalConnection ( receiverName: " \( type ( of: buildSystem) ) " )
171
+ connectionFromSourceKitLSPToBuildSystem. start ( handler: buildSystem)
172
+ self . connectionToBuildSystem = connectionFromSourceKitLSPToBuildSystem
173
+ } else {
174
+ self . connectionToBuildSystem = nil
175
+ }
163
176
// The debounce duration of 500ms was chosen arbitrarily without any measurements.
164
177
self . filesDependenciesUpdatedDebouncer = Debouncer (
165
178
debounceDuration: . milliseconds( 500 ) ,
@@ -179,15 +192,15 @@ package actor BuildSystemManager: BuiltInBuildSystemAdapterDelegate {
179
192
180
193
// FIXME: (BSP migration) Forward file watch patterns from this initialize request to the client
181
194
initializeResult = Task { ( ) -> InitializeBuildResponse ? in
182
- guard let buildSystem else {
195
+ guard let connectionToBuildSystem else {
183
196
return nil
184
197
}
185
198
guard let buildSystemKind else {
186
199
logger. fault ( " Created build system without a build system kind? " )
187
200
return nil
188
201
}
189
202
return await orLog ( " Initializing build system " ) {
190
- try await buildSystem . send (
203
+ try await connectionToBuildSystem . send (
191
204
InitializeBuildRequest (
192
205
displayName: " SourceKit-LSP " ,
193
206
version: " unknown " ,
@@ -200,8 +213,13 @@ package actor BuildSystemManager: BuiltInBuildSystemAdapterDelegate {
200
213
}
201
214
}
202
215
216
+ deinit {
217
+ connectionFromBuildSystemToSourceKitLSP. close ( )
218
+ connectionToBuildSystem? . close ( )
219
+ }
220
+
203
221
package func filesDidChange( _ events: [ FileEvent ] ) async {
204
- await self . buildSystem ? . send ( BuildServerProtocol . DidChangeWatchedFilesNotification ( changes: events) )
222
+ connectionToBuildSystem ? . send ( BuildServerProtocol . DidChangeWatchedFilesNotification ( changes: events) )
205
223
206
224
var targetsWithUpdatedDependencies : Set < BuildTargetIdentifier > = [ ]
207
225
// If a Swift file within a target is updated, reload all the other files within the target since they might be
@@ -245,25 +263,51 @@ package actor BuildSystemManager: BuiltInBuildSystemAdapterDelegate {
245
263
await self . filesDependenciesUpdatedDebouncer. scheduleCall ( filesWithUpdatedDependencies)
246
264
}
247
265
248
- /// Implementation of `MessageHandler`, handling notifications from the build system.
249
- ///
250
- /// - Important: Do not call directly.
251
- package func handle ( _ notification: some LanguageServerProtocol . NotificationType ) async {
266
+ // FIXME: (BSP Migration) Can we use more fine-grained dependency tracking here?
267
+ package let messageHandlingQueue = AsyncQueue < Serial > ( )
268
+
269
+ package func handleImpl ( _ notification: some NotificationType ) async {
252
270
switch notification {
253
271
case let notification as DidChangeBuildTargetNotification :
254
272
await self . didChangeBuildTarget ( notification: notification)
255
273
case let notification as BuildServerProtocol . LogMessageNotification :
256
274
await self . logMessage ( notification: notification)
275
+ case let notification as BuildServerProtocol . WorkDoneProgress :
276
+ await self . workDoneProgress ( notification: notification)
257
277
default :
258
278
logger. error ( " Ignoring unknown notification \( type ( of: notification) . method) " )
259
279
}
260
280
}
261
281
262
- /// Implementation of `MessageHandler`, handling requests from the build system.
263
- ///
264
- /// - Important: Do not call directly.
265
- package nonisolated func handle< R: RequestType > ( _ request: R ) async throws -> R . Response {
266
- throw ResponseError . methodNotFound ( R . method)
282
+ package func handleImpl< Request: RequestType > ( _ request: RequestAndReply < Request > ) async {
283
+ switch request {
284
+ case let request as RequestAndReply < BuildServerProtocol . CreateWorkDoneProgressRequest > :
285
+ await request. reply { try await self . createWorkDoneProgress ( request: request. params) }
286
+ default :
287
+ await request. reply { throw ResponseError . methodNotFound ( Request . method) }
288
+ }
289
+ }
290
+
291
+ private func createWorkDoneProgress(
292
+ request: BuildServerProtocol . CreateWorkDoneProgressRequest
293
+ ) async throws -> BuildServerProtocol . CreateWorkDoneProgressRequest . Response {
294
+ guard let delegate else {
295
+ throw ResponseError . unknown ( " Connection to client closed " )
296
+ }
297
+ guard await delegate. clientSupportsWorkDoneProgress else {
298
+ throw ResponseError . unknown ( " Client does not support work done progress " )
299
+ }
300
+ await delegate. waitUntilInitialized ( )
301
+ return try await delegate. sendRequestToClient ( request as LanguageServerProtocol . CreateWorkDoneProgressRequest )
302
+ }
303
+
304
+ private func workDoneProgress( notification: BuildServerProtocol . WorkDoneProgress ) async {
305
+ guard let delegate else {
306
+ logger. fault ( " Ignoring work done progress form build system because connection to client closed " )
307
+ return
308
+ }
309
+ await delegate. waitUntilInitialized ( )
310
+ delegate. sendNotificationToClient ( notification as LanguageServerProtocol . WorkDoneProgress )
267
311
}
268
312
269
313
/// - Note: Needed so we can set the delegate from a different isolation context.
@@ -365,7 +409,7 @@ package actor BuildSystemManager: BuiltInBuildSystemAdapterDelegate {
365
409
366
410
/// Returns all the `ConfiguredTarget`s that the document is part of.
367
411
package func targets( for document: DocumentURI ) async -> [ BuildTargetIdentifier ] {
368
- guard let buildSystem else {
412
+ guard let connectionToBuildSystem else {
369
413
return [ ]
370
414
}
371
415
@@ -374,7 +418,7 @@ package actor BuildSystemManager: BuiltInBuildSystemAdapterDelegate {
374
418
let request = InverseSourcesRequest ( textDocument: TextDocumentIdentifier ( uri: document) )
375
419
do {
376
420
let response = try await cachedTargetsForDocument. get ( request) { document in
377
- return try await buildSystem . send ( request)
421
+ return try await connectionToBuildSystem . send ( request)
378
422
}
379
423
return response. targets
380
424
} catch {
@@ -430,7 +474,7 @@ package actor BuildSystemManager: BuiltInBuildSystemAdapterDelegate {
430
474
in target: BuildTargetIdentifier ? ,
431
475
language: Language
432
476
) async throws -> FileBuildSettings ? {
433
- guard let buildSystem , let target else {
477
+ guard let connectionToBuildSystem , let target else {
434
478
return nil
435
479
}
436
480
let request = SourceKitOptionsRequest ( textDocument: TextDocumentIdentifier ( uri: document) , target: target)
@@ -441,7 +485,7 @@ package actor BuildSystemManager: BuiltInBuildSystemAdapterDelegate {
441
485
// very quickly from `settings(for:language:)`.
442
486
// https://github.com/apple/sourcekit-lsp/issues/1181
443
487
let response = try await cachedSourceKitOptions. get ( request) { request in
444
- try await buildSystem . send ( request)
488
+ try await connectionToBuildSystem . send ( request)
445
489
}
446
490
guard let response else {
447
491
return nil
@@ -514,8 +558,10 @@ package actor BuildSystemManager: BuiltInBuildSystemAdapterDelegate {
514
558
515
559
package func waitForUpToDateBuildGraph( ) async {
516
560
await orLog ( " Waiting for build system updates " ) {
517
- let _: VoidResponse ? = try await self . buildSystem ? . send ( WaitForBuildSystemUpdatesRequest ( ) )
561
+ let _: VoidResponse ? = try await connectionToBuildSystem ? . send ( WaitForBuildSystemUpdatesRequest ( ) )
518
562
}
563
+ // Handle any messages the build system might have sent us while updating.
564
+ await self . messageHandlingQueue. async { } . valuePropagatingCancellation
519
565
}
520
566
521
567
/// The root targets of the project have depth of 0 and all target dependencies have a greater depth than the target
@@ -574,7 +620,7 @@ package actor BuildSystemManager: BuiltInBuildSystemAdapterDelegate {
574
620
targets: [ BuildTargetIdentifier ] ,
575
621
logMessageToIndexLog: @escaping @Sendable ( _ taskID: IndexTaskID , _ message: String ) -> Void
576
622
) async throws {
577
- let _: VoidResponse ? = try await buildSystem ? . send ( PrepareTargetsRequest ( targets: targets) )
623
+ let _: VoidResponse ? = try await connectionToBuildSystem ? . send ( PrepareTargetsRequest ( targets: targets) )
578
624
await orLog ( " Calling fileDependenciesUpdated " ) {
579
625
let filesInPreparedTargets = try await self . sourceFiles ( in: targets) . flatMap ( \. sources) . map ( \. uri)
580
626
await filesDependenciesUpdatedDebouncer. scheduleCall ( Set ( filesInPreparedTargets) )
@@ -592,13 +638,13 @@ package actor BuildSystemManager: BuiltInBuildSystemAdapterDelegate {
592
638
}
593
639
594
640
package func buildTargets( ) async throws -> [ BuildTargetIdentifier : ( target: BuildTarget , depth: Int ) ] {
595
- guard let buildSystem else {
641
+ guard let connectionToBuildSystem else {
596
642
return [ : ]
597
643
}
598
644
599
645
let request = BuildTargetsRequest ( )
600
646
let result = try await cachedBuildTargets. get ( request) { request in
601
- let buildTargets = try await buildSystem . send ( request) . targets
647
+ let buildTargets = try await connectionToBuildSystem . send ( request) . targets
602
648
let depths = await self . targetDepths ( for: buildTargets)
603
649
var result : [ BuildTargetIdentifier : ( target: BuildTarget , depth: Int ) ] = [ : ]
604
650
result. reserveCapacity ( buildTargets. count)
@@ -622,7 +668,7 @@ package actor BuildSystemManager: BuiltInBuildSystemAdapterDelegate {
622
668
}
623
669
624
670
package func sourceFiles( in targets: some Sequence < BuildTargetIdentifier > ) async throws -> [ SourcesItem ] {
625
- guard let buildSystem else {
671
+ guard let connectionToBuildSystem else {
626
672
return [ ]
627
673
}
628
674
@@ -632,7 +678,7 @@ package actor BuildSystemManager: BuiltInBuildSystemAdapterDelegate {
632
678
let sortedTargets = targets. sorted { $0. uri. stringValue < $1. uri. stringValue }
633
679
let request = BuildTargetSourcesRequest ( targets: sortedTargets)
634
680
let response = try await cachedTargetSources. get ( request) { request in
635
- try await buildSystem . send ( request)
681
+ try await connectionToBuildSystem . send ( request)
636
682
}
637
683
return response. items
638
684
}
0 commit comments