13
13
#if compiler(>=6)
14
14
package import BuildServerProtocol
15
15
import Dispatch
16
- import Foundation
16
+ package import Foundation
17
17
package import LanguageServerProtocol
18
18
package import LanguageServerProtocolExtensions
19
19
import SKLogging
@@ -23,8 +23,7 @@ package import SwiftExtensions
23
23
package import ToolchainRegistry
24
24
import TSCExtensions
25
25
26
- package import struct TSCBasic. AbsolutePath
27
- package import struct TSCBasic. RelativePath
26
+ import struct TSCBasic. RelativePath
28
27
#else
29
28
import BuildServerProtocol
30
29
import Dispatch
@@ -38,7 +37,6 @@ import SwiftExtensions
38
37
import ToolchainRegistry
39
38
import TSCExtensions
40
39
41
- import struct TSCBasic. AbsolutePath
42
40
import struct TSCBasic. RelativePath
43
41
#endif
44
42
@@ -138,31 +136,31 @@ private enum BuildSystemAdapter {
138
136
139
137
private extension BuildSystemSpec {
140
138
private static func createBuiltInBuildSystemAdapter(
141
- projectRoot: AbsolutePath ,
139
+ projectRoot: URL ,
142
140
messagesToSourceKitLSPHandler: any MessageHandler ,
143
141
buildSystemTestHooks: BuildSystemTestHooks ,
144
142
_ createBuildSystem: @Sendable ( _ connectionToSourceKitLSP: any Connection ) async throws -> BuiltInBuildSystem ?
145
143
) async -> BuildSystemAdapter ? {
146
144
let connectionToSourceKitLSP = LocalConnection (
147
- receiverName: " BuildSystemManager for \( projectRoot. asURL . lastPathComponent) "
145
+ receiverName: " BuildSystemManager for \( projectRoot. lastPathComponent) "
148
146
)
149
147
connectionToSourceKitLSP. start ( handler: messagesToSourceKitLSPHandler)
150
148
151
149
let buildSystem = await orLog ( " Creating build system " ) {
152
150
try await createBuildSystem ( connectionToSourceKitLSP)
153
151
}
154
152
guard let buildSystem else {
155
- logger. log ( " Failed to create build system at \( projectRoot. pathString ) " )
153
+ logger. log ( " Failed to create build system at \( projectRoot) " )
156
154
return nil
157
155
}
158
- logger. log ( " Created \( type ( of: buildSystem) , privacy: . public) at \( projectRoot. pathString ) " )
156
+ logger. log ( " Created \( type ( of: buildSystem) , privacy: . public) at \( projectRoot) " )
159
157
let buildSystemAdapter = BuiltInBuildSystemAdapter (
160
158
underlyingBuildSystem: buildSystem,
161
159
connectionToSourceKitLSP: connectionToSourceKitLSP,
162
160
buildSystemTestHooks: buildSystemTestHooks
163
161
)
164
162
let connectionToBuildSystem = LocalConnection (
165
- receiverName: " \( type ( of: buildSystem) ) for \( projectRoot. asURL . lastPathComponent) "
163
+ receiverName: " \( type ( of: buildSystem) ) for \( projectRoot. lastPathComponent) "
166
164
)
167
165
connectionToBuildSystem. start ( handler: buildSystemAdapter)
168
166
return . builtIn( buildSystemAdapter, connectionToBuildSystem: connectionToBuildSystem)
@@ -185,10 +183,10 @@ private extension BuildSystemSpec {
185
183
)
186
184
}
187
185
guard let buildSystem else {
188
- logger. log ( " Failed to create external build system at \( projectRoot. pathString ) " )
186
+ logger. log ( " Failed to create external build system at \( projectRoot) " )
189
187
return nil
190
188
}
191
- logger. log ( " Created external build server at \( projectRoot. pathString ) " )
189
+ logger. log ( " Created external build server at \( projectRoot) " )
192
190
return . external( buildSystem)
193
191
case . compilationDatabase:
194
192
return await Self . createBuiltInBuildSystemAdapter (
@@ -245,7 +243,7 @@ package actor BuildSystemManager: QueueBasedMessageHandler {
245
243
/// For compilation databases it is the root folder based on which the compilation database was found.
246
244
///
247
245
/// `nil` if the `BuildSystemManager` does not have an underlying build system.
248
- package let projectRoot : AbsolutePath ?
246
+ package let projectRoot : URL ?
249
247
250
248
/// The files for which the delegate has requested change notifications, ie. the files for which the delegate wants to
251
249
/// get `fileBuildSettingsChanged` and `filesDependenciesUpdated` callbacks.
@@ -340,7 +338,10 @@ package actor BuildSystemManager: QueueBasedMessageHandler {
340
338
let files : [ DocumentURI : SourceFileInfo ]
341
339
342
340
/// The source directories in the workspace, ie. all `SourceItem`s that have `kind == .directory`.
343
- let directories : [ DocumentURI : SourceFileInfo ]
341
+ ///
342
+ /// `pathComponents` is the result of `key.fileURL?.pathComponents`. We frequently need these path components to
343
+ /// determine if a file is descendent of the directory and computing them from the `DocumentURI` is expensive.
344
+ let directories : [ DocumentURI : ( pathComponents: [ String ] ? , info: SourceFileInfo ) ]
344
345
}
345
346
346
347
private let cachedSourceFilesAndDirectories = Cache < SourceFilesAndDirectoriesKey , SourceFilesAndDirectories > ( )
@@ -402,7 +403,7 @@ package actor BuildSystemManager: QueueBasedMessageHandler {
402
403
displayName: " SourceKit-LSP " ,
403
404
version: " " ,
404
405
bspVersion: " 2.2.0 " ,
405
- rootUri: URI ( buildSystemSpec. projectRoot. asURL ) ,
406
+ rootUri: URI ( buildSystemSpec. projectRoot) ,
406
407
capabilities: BuildClientCapabilities ( languageIds: [ . c, . cpp, . objective_c, . objective_cpp, . swift] )
407
408
)
408
409
)
@@ -608,7 +609,7 @@ package actor BuildSystemManager: QueueBasedMessageHandler {
608
609
in target: BuildTargetIdentifier ? ,
609
610
language: Language
610
611
) async -> Toolchain ? {
611
- let toolchainPath = await orLog ( " Getting toolchain from build targets " ) { ( ) -> AbsolutePath ? in
612
+ let toolchainPath = await orLog ( " Getting toolchain from build targets " ) { ( ) -> URL ? in
612
613
guard let target else {
613
614
return nil
614
615
}
@@ -624,7 +625,7 @@ package actor BuildSystemManager: QueueBasedMessageHandler {
624
625
logger. error ( " Toolchain is not a file URL " )
625
626
return nil
626
627
}
627
- return try AbsolutePath ( validating : toolchainUrl. filePath )
628
+ return toolchainUrl
628
629
}
629
630
if let toolchainPath {
630
631
if let toolchain = await self . toolchainRegistry. toolchain ( withPath: toolchainPath) {
@@ -634,7 +635,7 @@ package actor BuildSystemManager: QueueBasedMessageHandler {
634
635
}
635
636
636
637
switch language {
637
- case . swift:
638
+ case . swift, . markdown , . tutorial :
638
639
return await toolchainRegistry. preferredToolchain ( containing: [ \. sourcekitd, \. swift, \. swiftc] )
639
640
case . c, . cpp, . objective_c, . objective_cpp:
640
641
return await toolchainRegistry. preferredToolchain ( containing: [ \. clang, \. clangd] )
@@ -681,14 +682,12 @@ package actor BuildSystemManager: QueueBasedMessageHandler {
681
682
if let targets = filesAndDirectories. files [ document] ? . targets {
682
683
result. formUnion ( targets)
683
684
}
684
- if !filesAndDirectories. directories. isEmpty,
685
- let documentPath = AbsolutePath ( validatingOrNil: try ? document. fileURL? . filePath)
686
- {
687
- for (directory, info) in filesAndDirectories. directories {
688
- guard let directoryPath = AbsolutePath ( validatingOrNil: try ? directory. fileURL? . filePath) else {
685
+ if !filesAndDirectories. directories. isEmpty, let documentPathComponents = document. fileURL? . pathComponents {
686
+ for (directory, ( directoryPathComponents, info) ) in filesAndDirectories. directories {
687
+ guard let directoryPathComponents, let directoryPath = directory. fileURL else {
689
688
continue
690
689
}
691
- if documentPath . isDescendant ( of: directoryPath ) {
690
+ if isDescendant ( documentPathComponents , of: directoryPathComponents ) {
692
691
result. formUnion ( info. targets)
693
692
}
694
693
}
@@ -1013,19 +1012,21 @@ package actor BuildSystemManager: QueueBasedMessageHandler {
1013
1012
return [ ]
1014
1013
}
1015
1014
1015
+ let request = BuildTargetSourcesRequest ( targets: targets. sorted { $0. uri. stringValue < $1. uri. stringValue } )
1016
+
1016
1017
// If we have a cached request for a superset of the targets, serve the result from that cache entry.
1017
1018
let fromSuperset = await orLog ( " Getting source files from superset request " ) {
1018
- try await cachedTargetSources. get ( isolation: self ) { request in
1019
- targets. isSubset ( of: request. targets)
1020
- } transform: { response in
1021
- return BuildTargetSourcesResponse ( items: response. items. filter { targets. contains ( $0. target) } )
1022
- }
1019
+ try await cachedTargetSources. getDerived (
1020
+ isolation: self ,
1021
+ request,
1022
+ canReuseKey: { targets. isSubset ( of: $0. targets) } ,
1023
+ transform: { BuildTargetSourcesResponse ( items: $0. items. filter { targets. contains ( $0. target) } ) }
1024
+ )
1023
1025
}
1024
1026
if let fromSuperset {
1025
1027
return fromSuperset. items
1026
1028
}
1027
1029
1028
- let request = BuildTargetSourcesRequest ( targets: targets. sorted { $0. uri. stringValue < $1. uri. stringValue } )
1029
1030
let response = try await cachedTargetSources. get ( request, isolation: self ) { request in
1030
1031
try await buildSystemAdapter. send ( request)
1031
1032
}
@@ -1059,7 +1060,7 @@ package actor BuildSystemManager: QueueBasedMessageHandler {
1059
1060
1060
1061
return try await cachedSourceFilesAndDirectories. get ( key, isolation: self ) { key in
1061
1062
var files : [ DocumentURI : SourceFileInfo ] = [ : ]
1062
- var directories : [ DocumentURI : SourceFileInfo ] = [ : ]
1063
+ var directories : [ DocumentURI : ( pathComponents : [ String ] ? , info : SourceFileInfo ) ] = [ : ]
1063
1064
for sourcesItem in key. sourcesItems {
1064
1065
let target = targets [ sourcesItem. target] ? . target
1065
1066
let isPartOfRootProject = !( target? . tags. contains ( . dependency) ?? false )
@@ -1081,7 +1082,9 @@ package actor BuildSystemManager: QueueBasedMessageHandler {
1081
1082
case . file:
1082
1083
files [ sourceItem. uri] = info. merging ( files [ sourceItem. uri] )
1083
1084
case . directory:
1084
- directories [ sourceItem. uri] = info. merging ( directories [ sourceItem. uri] )
1085
+ directories [ sourceItem. uri] = (
1086
+ sourceItem. uri. fileURL? . pathComponents, info. merging ( directories [ sourceItem. uri] ? . info)
1087
+ )
1085
1088
}
1086
1089
}
1087
1090
}
@@ -1230,3 +1233,12 @@ package actor BuildSystemManager: QueueBasedMessageHandler {
1230
1233
}
1231
1234
}
1232
1235
}
1236
+
1237
+ /// Returns `true` if the path components `selfPathComponents`, retrieved from `URL.pathComponents` are a descendent
1238
+ /// of the other path components.
1239
+ ///
1240
+ /// This operates directly on path components instead of `URL`s because computing the path components of a URL is
1241
+ /// expensive and this allows us to cache the path components.
1242
+ private func isDescendant( _ selfPathComponents: [ String ] , of otherPathComponents: [ String ] ) -> Bool {
1243
+ return selfPathComponents. dropLast ( ) . starts ( with: otherPathComponents)
1244
+ }
0 commit comments