@@ -25,15 +25,15 @@ import struct TSCBasic.AbsolutePath
25
25
import os
26
26
#endif
27
27
28
- fileprivate class RequestCache < Request: RequestType & Hashable > {
29
- private var storage : [ Request : Task < Request . Response , Error > ] = [ : ]
28
+ fileprivate class RequestCache < Request: RequestType & Hashable , Result : Sendable > {
29
+ private var storage : [ Request : Task < Result , Error > ] = [ : ]
30
30
31
31
func get(
32
32
_ key: Request ,
33
33
isolation: isolated any Actor = #isolation,
34
- compute: @Sendable @escaping ( Request) async throws ( Error ) -> Request . Response
35
- ) async throws ( Error) -> Request . Response {
36
- let task : Task < Request . Response , Error >
34
+ compute: @Sendable @escaping ( Request) async throws ( Error ) -> Result
35
+ ) async throws ( Error) -> Result {
36
+ let task : Task < Result , Error >
37
37
if let cached = storage [ key] {
38
38
task = cached
39
39
} else {
@@ -110,15 +110,15 @@ package actor BuildSystemManager: BuiltInBuildSystemAdapterDelegate {
110
110
/// Force-unwrapped optional because initializing it requires access to `self`.
111
111
private var filesDependenciesUpdatedDebouncer : Debouncer < Set < DocumentURI > > ! = nil
112
112
113
- private var cachedTargetsForDocument = RequestCache < InverseSourcesRequest > ( )
113
+ private var cachedTargetsForDocument = RequestCache < InverseSourcesRequest , InverseSourcesResponse > ( )
114
114
115
- private var cachedSourceKitOptions = RequestCache < SourceKitOptionsRequest > ( )
115
+ private var cachedSourceKitOptions = RequestCache < SourceKitOptionsRequest , SourceKitOptionsResponse ? > ( )
116
116
117
- private var cachedBuildTargets = RequestCache < BuildTargetsRequest > ( )
117
+ private var cachedBuildTargets = RequestCache <
118
+ BuildTargetsRequest , [ BuildTargetIdentifier : ( target: BuildTarget , depth: Int ) ]
119
+ > ( )
118
120
119
- private var cachedTargetSources = RequestCache < BuildTargetSourcesRequest > ( )
120
-
121
- private var cachedTargetDepths : ( buildTargets: [ BuildTarget ] , depths: [ BuildTargetIdentifier : Int ] ) ? = nil
121
+ private var cachedTargetSources = RequestCache < BuildTargetSourcesRequest , BuildTargetSourcesResponse > ( )
122
122
123
123
/// The root of the project that this build system manages. For example, for SwiftPM packages, this is the folder
124
124
/// containing Package.swift. For compilation databases it is the root folder based on which the compilation database
@@ -176,6 +176,8 @@ package actor BuildSystemManager: BuiltInBuildSystemAdapterDelegate {
176
176
}
177
177
await delegate. filesDependenciesUpdated ( changedWatchedFiles)
178
178
}
179
+
180
+ // FIXME: (BSP migration) Forward file watch patterns from this initialize request to the client
179
181
initializeResult = Task { ( ) -> InitializeBuildResponse ? in
180
182
guard let buildSystem else {
181
183
return nil
@@ -222,10 +224,9 @@ package actor BuildSystemManager: BuiltInBuildSystemAdapterDelegate {
222
224
if !options. backgroundIndexingOrDefault,
223
225
events. contains ( where: { $0. uri. fileURL? . pathExtension == " swiftmodule " } )
224
226
{
225
- let targets = await orLog ( " Getting build targets " ) {
226
- try await self . buildTargets ( )
227
+ await orLog ( " Getting build targets " ) {
228
+ targetsWithUpdatedDependencies . formUnion ( try await self . buildTargets ( ) . keys )
227
229
}
228
- targetsWithUpdatedDependencies. formUnion ( targets? . map ( \. id) ?? [ ] )
229
230
}
230
231
231
232
var filesWithUpdatedDependencies : Set < DocumentURI > = [ ]
@@ -271,9 +272,37 @@ package actor BuildSystemManager: BuiltInBuildSystemAdapterDelegate {
271
272
}
272
273
273
274
/// Returns the toolchain that should be used to process the given document.
274
- package func toolchain( for uri: DocumentURI , _ language: Language ) async -> Toolchain ? {
275
- if let toolchain = await buildSystem? . underlyingBuildSystem. toolchain ( for: uri, language) {
276
- return toolchain
275
+ package func toolchain(
276
+ for uri: DocumentURI ,
277
+ in target: BuildTargetIdentifier ? ,
278
+ language: Language
279
+ ) async -> Toolchain ? {
280
+ let toolchainPath = await orLog ( " Getting toolchain from build targets " ) { ( ) -> AbsolutePath ? in
281
+ guard let target else {
282
+ return nil
283
+ }
284
+ let targets = try await self . buildTargets ( )
285
+ guard let target = targets [ target] ? . target else {
286
+ logger. error ( " Failed to find target \( target. forLogging) to determine toolchain " )
287
+ return nil
288
+ }
289
+ guard target. dataKind == . sourceKit, case . dictionary( let data) = target. data else {
290
+ return nil
291
+ }
292
+ guard let toolchain = SourceKitBuildTarget ( fromLSPDictionary: data) . toolchain else {
293
+ return nil
294
+ }
295
+ guard let toolchainUrl = toolchain. fileURL else {
296
+ logger. error ( " Toolchain is not a file URL " )
297
+ return nil
298
+ }
299
+ return try AbsolutePath ( validating: toolchainUrl. path)
300
+ }
301
+ if let toolchainPath {
302
+ if let toolchain = await self . toolchainRegistry. toolchain ( withPath: toolchainPath) {
303
+ return toolchain
304
+ }
305
+ logger. error ( " Toolchain at \( toolchainPath) not registered in toolchain registry. " )
277
306
}
278
307
279
308
switch language {
@@ -484,15 +513,14 @@ package actor BuildSystemManager: BuiltInBuildSystemAdapterDelegate {
484
513
}
485
514
486
515
package func waitForUpToDateBuildGraph( ) async {
487
- await self . buildSystem? . underlyingBuildSystem. waitForUpToDateBuildGraph ( )
516
+ await orLog ( " Waiting for build system updates " ) {
517
+ let _: VoidResponse ? = try await self . buildSystem? . send ( WaitForBuildSystemUpdatesRequest ( ) )
518
+ }
488
519
}
489
520
490
521
/// The root targets of the project have depth of 0 and all target dependencies have a greater depth than the target
491
522
// itself.
492
523
private func targetDepths( for buildTargets: [ BuildTarget ] ) -> [ BuildTargetIdentifier : Int ] {
493
- if let cachedTargetDepths, cachedTargetDepths. buildTargets == buildTargets {
494
- return cachedTargetDepths. depths
495
- }
496
524
var nonRoots : Set < BuildTargetIdentifier > = [ ]
497
525
for buildTarget in buildTargets {
498
526
nonRoots. formUnion ( buildTarget. dependencies)
@@ -511,7 +539,6 @@ package actor BuildSystemManager: BuiltInBuildSystemAdapterDelegate {
511
539
}
512
540
}
513
541
}
514
- cachedTargetDepths = ( buildTargets, depths)
515
542
return depths
516
543
}
517
544
@@ -522,25 +549,25 @@ package actor BuildSystemManager: BuiltInBuildSystemAdapterDelegate {
522
549
///
523
550
/// `nil` if the build system doesn't support topological sorting of targets.
524
551
package func topologicalSort( of targets: [ BuildTargetIdentifier ] ) async throws -> [ BuildTargetIdentifier ] ? {
525
- guard let workspaceTargets = await orLog ( " Getting build targets for topological sort " , { try await buildTargets ( ) } )
552
+ guard let buildTargets = await orLog ( " Getting build targets for topological sort " , { try await buildTargets ( ) } )
526
553
else {
527
554
return nil
528
555
}
529
556
530
- let depths = targetDepths ( for: workspaceTargets)
531
557
return targets. sorted { ( lhs: BuildTargetIdentifier , rhs: BuildTargetIdentifier ) -> Bool in
532
- return depths [ lhs, default : 0 ] > depths [ rhs, default : 0 ]
558
+ return ( buildTargets [ lhs] ? . depth ?? 0 ) > ( buildTargets [ rhs] ? . depth ?? 0 )
533
559
}
534
560
}
535
561
536
562
/// Returns the list of targets that might depend on the given target and that need to be re-prepared when a file in
537
563
/// `target` is modified.
538
564
package func targets( dependingOn targetIds: Set < BuildTargetIdentifier > ) async -> [ BuildTargetIdentifier ] {
539
- guard let buildTargets = await orLog ( " Getting build targets for dependencies " , { try await self . buildTargets ( ) } )
565
+ guard
566
+ let buildTargets = await orLog ( " Getting build targets for dependencies " , { try await self . buildTargets ( ) . values } )
540
567
else {
541
568
return [ ]
542
569
}
543
- return buildTargets. filter { $0. dependencies. contains ( anyIn: targetIds) } . map { $0. id }
570
+ return buildTargets. filter { $0. target . dependencies. contains ( anyIn: targetIds) } . map { $0. target . id }
544
571
}
545
572
546
573
package func prepare(
@@ -564,26 +591,46 @@ package actor BuildSystemManager: BuiltInBuildSystemAdapterDelegate {
564
591
self . watchedFiles [ uri] = nil
565
592
}
566
593
567
- package func buildTargets( ) async throws -> [ BuildTarget ] {
594
+ package func buildTargets( ) async throws -> [ BuildTargetIdentifier : ( target : BuildTarget , depth : Int ) ] {
568
595
guard let buildSystem else {
569
- return [ ]
596
+ return [ : ]
570
597
}
571
598
572
599
let request = BuildTargetsRequest ( )
573
- let response = try await cachedBuildTargets. get ( request) { request in
574
- try await buildSystem. send ( request)
600
+ let result = try await cachedBuildTargets. get ( request) { request in
601
+ let buildTargets = try await buildSystem. send ( request) . targets
602
+ let depths = await self . targetDepths ( for: buildTargets)
603
+ var result : [ BuildTargetIdentifier : ( target: BuildTarget , depth: Int ) ] = [ : ]
604
+ result. reserveCapacity ( buildTargets. count)
605
+ for buildTarget in buildTargets {
606
+ guard result [ buildTarget. id] == nil else {
607
+ logger. error ( " Found two targets with the same ID \( buildTarget. id) " )
608
+ continue
609
+ }
610
+ let depth : Int
611
+ if let d = depths [ buildTarget. id] {
612
+ depth = d
613
+ } else {
614
+ logger. fault ( " Did not compute depth for target \( buildTarget. id) " )
615
+ depth = 0
616
+ }
617
+ result [ buildTarget. id] = ( buildTarget, depth)
618
+ }
619
+ return result
575
620
}
576
- return response . targets
621
+ return result
577
622
}
578
623
579
- package func sourceFiles( in targets: [ BuildTargetIdentifier ] ) async throws -> [ SourcesItem ] {
624
+ package func sourceFiles( in targets: some Sequence < BuildTargetIdentifier > ) async throws -> [ SourcesItem ] {
580
625
guard let buildSystem else {
581
626
return [ ]
582
627
}
583
628
584
629
// FIXME: (BSP migration) If we have a cached request for a superset of the targets, serve the result from that
585
630
// cache entry.
586
- let request = BuildTargetSourcesRequest . init ( targets: targets)
631
+ // Sort targets to help cache hits if we have two calls to `sourceFiles` with targets in different orders.
632
+ let sortedTargets = targets. sorted { $0. uri. stringValue < $1. uri. stringValue }
633
+ let request = BuildTargetSourcesRequest ( targets: sortedTargets)
587
634
let response = try await cachedTargetSources. get ( request) { request in
588
635
try await buildSystem. send ( request)
589
636
}
@@ -595,9 +642,8 @@ package actor BuildSystemManager: BuiltInBuildSystemAdapterDelegate {
595
642
// retrieving the source files for those targets.
596
643
// FIXME: (BSP Migration) Handle source files that are in multiple targets
597
644
let targets = try await self . buildTargets ( )
598
- let targetsById = Dictionary ( elements: targets, keyedBy: \. id)
599
- let sourceFiles = try await self . sourceFiles ( in: targets. map ( \. id) ) . flatMap { sourcesItem in
600
- let target = targetsById [ sourcesItem. target]
645
+ let sourceFiles = try await self . sourceFiles ( in: targets. keys) . flatMap { sourcesItem in
646
+ let target = targets [ sourcesItem. target] ? . target
601
647
return sourcesItem. sources. map { sourceItem in
602
648
SourceFileInfo (
603
649
uri: sourceItem. uri,
@@ -663,6 +709,7 @@ extension BuildSystemManager {
663
709
// FIXME: (BSP Migration) Communicate that the build target has changed to the `BuildSystemManagerDelegate` and make
664
710
// it responsible for figuring out which files are affected.
665
711
await delegate? . fileBuildSettingsChanged ( Set ( watchedFiles. keys) )
712
+ await self . delegate? . sourceFilesDidChange ( )
666
713
}
667
714
668
715
private func logMessage( notification: BuildServerProtocol . LogMessageNotification ) async {
0 commit comments