@@ -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 let _ = getConfigPath ( for : workspaceFolder ) 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,48 @@ actor ExternalBuildSystemAdapter {
178
185
) . connection
179
186
}
180
187
188
+ private static func getConfigPath( for workspaceFolder: AbsolutePath ? = nil ) -> AbsolutePath ? {
189
+ var buildServerConfigLocations : [ AbsolutePath ? ] = [ ]
190
+ if let workspaceFolder = workspaceFolder {
191
+ buildServerConfigLocations. append ( workspaceFolder. appending ( component: " .bsp " ) )
192
+ }
193
+
194
+ #if os(Windows)
195
+ let localAppDataPath = AbsolutePath ( validatingOrNil: ProcessInfo . processInfo. environment [ " LOCALAPPDATA " ] ) ? . appending ( component: " bsp " )
196
+ let programDataPath = AbsolutePath ( validatingOrNil: ProcessInfo . processInfo. environment [ " PROGRAMDATA " ] ) ? . appending ( component: " bsp " )
197
+
198
+ buildServerConfigLocations. append ( contentsOf: [ localAppDataPath, programDataPath] )
199
+ #else
200
+ let xdgDataHomePath = AbsolutePath ( validatingOrNil: ProcessInfo . processInfo. environment [ " XDG_DATA_HOME " ] ) ? . appending ( component: " bsp " )
201
+ let xdgDataDirsPath = AbsolutePath ( validatingOrNil: ProcessInfo . processInfo. environment [ " XDG_DATA_DIRS " ] ) ? . appending ( component: " bsp " )
202
+
203
+ buildServerConfigLocations. append ( contentsOf: [ xdgDataHomePath, xdgDataDirsPath] )
204
+
205
+ if let libraryUrl = FileManager . default. urls ( for: . applicationSupportDirectory, in: . userDomainMask) . first {
206
+ let libraryPath = AbsolutePath ( validatingOrNil: libraryUrl. absoluteString) ? . appending ( component: " bsp " )
207
+ buildServerConfigLocations. append ( libraryPath)
208
+ }
209
+ #endif
210
+
211
+ for buildServerConfigLocation in buildServerConfigLocations {
212
+ guard let buildServerConfigLocation else {
213
+ continue
214
+ }
215
+ let fileManager = FileManager . default
216
+ do {
217
+ let items = try fileManager. contentsOfDirectory ( atPath: buildServerConfigLocation. pathString)
218
+ let jsonFiles = items. filter { $0. hasSuffix ( " .json " ) }
219
+
220
+ if let configFilePath = jsonFiles. sorted ( ) . first {
221
+ return buildServerConfigLocation. appending ( component: configFilePath)
222
+ }
223
+ } catch {
224
+ logger. error ( " Failed to read build server config file at \( buildServerConfigLocation) : \( error) " )
225
+ }
226
+ }
227
+ return nil
228
+ }
229
+
181
230
/// Restart the BSP server after it has crashed.
182
231
private func handleBspServerCrash( ) async throws {
183
232
// Set `connectionToBuildServer` to `nil` to indicate that there is currently no BSP server running.
0 commit comments