@@ -52,6 +52,10 @@ struct ExecutableNotFoundError: Error {
52
52
let executableName : String
53
53
}
54
54
55
+ enum BuildServerNotFoundError : Error {
56
+ case fileNotFound
57
+ }
58
+
55
59
private struct BuildServerConfig : Codable {
56
60
/// The name of the build tool.
57
61
let name : String
@@ -94,7 +98,7 @@ actor ExternalBuildSystemAdapter {
94
98
private var lastRestart : Date ?
95
99
96
100
static package func projectRoot( for workspaceFolder: AbsolutePath , options: SourceKitLSPOptions ) -> AbsolutePath ? {
97
- guard localFileSystem . isFile ( workspaceFolder . appending ( component : " buildServer.json " ) ) else {
101
+ guard getConfigPath ( for : workspaceFolder ) != nil else {
98
102
return nil
99
103
}
100
104
return workspaceFolder
@@ -142,7 +146,10 @@ actor ExternalBuildSystemAdapter {
142
146
143
147
/// Create a new JSONRPCConnection to the build server.
144
148
private func createConnectionToBspServer( ) async throws -> JSONRPCConnection {
145
- let configPath = projectRoot. appending ( component: " buildServer.json " )
149
+ guard let configPath = ExternalBuildSystemAdapter . getConfigPath ( for: self . projectRoot) else {
150
+ throw BuildServerNotFoundError . fileNotFound
151
+ }
152
+
146
153
let serverConfig = try BuildServerConfig . load ( from: configPath)
147
154
var serverPath = try AbsolutePath ( validating: serverConfig. argv [ 0 ] , relativeTo: projectRoot)
148
155
var serverArgs = Array ( serverConfig. argv [ 1 ... ] )
@@ -178,6 +185,62 @@ actor ExternalBuildSystemAdapter {
178
185
) . connection
179
186
}
180
187
188
+ private static func getConfigPath( for workspaceFolder: AbsolutePath ? = nil ) -> AbsolutePath ? {
189
+ var buildServerConfigLocations : [ URL ? ] = [ ]
190
+ if let workspaceFolder = workspaceFolder {
191
+ buildServerConfigLocations. append ( workspaceFolder. appending ( component: " .bsp " ) . asURL)
192
+ }
193
+
194
+ #if os(Windows)
195
+ if let localAppData = ProcessInfo . processInfo. environment [ " LOCALAPPDATA " ] {
196
+ buildServerConfigLocations. append ( URL ( fileURLWithPath: localAppData) . appendingPathComponent ( " bsp " ) )
197
+ }
198
+ if let programData = ProcessInfo . processInfo. environment [ " PROGRAMDATA " ] {
199
+ buildServerConfigLocations. append ( URL ( fileURLWithPath: programData) . appendingPathComponent ( " bsp " ) )
200
+ }
201
+ #else
202
+ if let xdgDataHome = ProcessInfo . processInfo. environment [ " XDG_DATA_HOME " ] {
203
+ buildServerConfigLocations. append ( URL ( fileURLWithPath: xdgDataHome) . appendingPathComponent ( " bsp " ) )
204
+ }
205
+
206
+ if let libraryUrl = FileManager . default. urls ( for: . applicationSupportDirectory, in: . userDomainMask) . first {
207
+ buildServerConfigLocations. append ( libraryUrl. appendingPathComponent ( " bsp " ) )
208
+ }
209
+
210
+ if let xdgDataDirs = ProcessInfo . processInfo. environment [ " XDG_DATA_DIRS " ] {
211
+ buildServerConfigLocations += xdgDataDirs. split ( separator: " : " ) . map { xdgDataDir in
212
+ URL ( fileURLWithPath: String ( xdgDataDir) ) . appendingPathComponent ( " bsp " )
213
+ }
214
+ }
215
+
216
+ if let libraryUrl = FileManager . default. urls ( for: . applicationSupportDirectory, in: . systemDomainMask) . first {
217
+ buildServerConfigLocations. append ( libraryUrl. appendingPathComponent ( " bsp " ) )
218
+ }
219
+ #endif
220
+
221
+ for case let buildServerConfigLocation? in buildServerConfigLocations {
222
+ let jsonFiles =
223
+ try ? FileManager . default. contentsOfDirectory ( at: buildServerConfigLocation, includingPropertiesForKeys: nil )
224
+ . filter { $0. pathExtension == " json " }
225
+
226
+ if let configFileURL = jsonFiles? . sorted ( by: { $0. lastPathComponent < $1. lastPathComponent } ) . first,
227
+ let configFilePath = AbsolutePath ( validatingOrNil: configFileURL. path)
228
+ {
229
+ return configFilePath
230
+ }
231
+ }
232
+
233
+ // Pre Swift 6.1 SourceKit-LSP looked for `buildServer.json` in the project root. Maintain this search location for
234
+ // compatibility even though it's not a standard BSP search location.
235
+ if let workspaceFolder = workspaceFolder,
236
+ localFileSystem. isFile ( workspaceFolder. appending ( component: " buildServer.json " ) )
237
+ {
238
+ return workspaceFolder. appending ( component: " buildServer.json " )
239
+ }
240
+
241
+ return nil
242
+ }
243
+
181
244
/// Restart the BSP server after it has crashed.
182
245
private func handleBspServerCrash( ) async throws {
183
246
// Set `connectionToBuildServer` to `nil` to indicate that there is currently no BSP server running.
0 commit comments