Skip to content

Commit 0360733

Browse files
authored
Merge pull request #1728 from Rajveer100/bsp-connection-config
Update BSP connection build server config lookup path
2 parents b9dd254 + 7b1f59e commit 0360733

File tree

3 files changed

+122
-3
lines changed

3 files changed

+122
-3
lines changed

Sources/BuildSystemIntegration/ExternalBuildSystemAdapter.swift

Lines changed: 65 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@ struct ExecutableNotFoundError: Error {
5252
let executableName: String
5353
}
5454

55+
enum BuildServerNotFoundError: Error {
56+
case fileNotFound
57+
}
58+
5559
private struct BuildServerConfig: Codable {
5660
/// The name of the build tool.
5761
let name: String
@@ -94,7 +98,7 @@ actor ExternalBuildSystemAdapter {
9498
private var lastRestart: Date?
9599

96100
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 {
98102
return nil
99103
}
100104
return workspaceFolder
@@ -142,7 +146,10 @@ actor ExternalBuildSystemAdapter {
142146

143147
/// Create a new JSONRPCConnection to the build server.
144148
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+
146153
let serverConfig = try BuildServerConfig.load(from: configPath)
147154
var serverPath = try AbsolutePath(validating: serverConfig.argv[0], relativeTo: projectRoot)
148155
var serverArgs = Array(serverConfig.argv[1...])
@@ -178,6 +185,62 @@ actor ExternalBuildSystemAdapter {
178185
).connection
179186
}
180187

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+
181244
/// Restart the BSP server after it has crashed.
182245
private func handleBspServerCrash() async throws {
183246
// Set `connectionToBuildServer` to `nil` to indicate that there is currently no BSP server running.

Sources/SKTestSupport/BuildServerTestProject.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,11 +55,12 @@ private let skTestSupportInputsDirectory: URL = {
5555
package class BuildServerTestProject: MultiFileTestProject {
5656
package init(
5757
files: [RelativeFileLocation: String],
58+
buildServerConfigLocation: RelativeFileLocation = ".bsp/sourcekit-lsp.json",
5859
buildServer: String,
5960
testName: String = #function
6061
) async throws {
6162
var files = files
62-
files["buildServer.json"] = """
63+
files[buildServerConfigLocation] = """
6364
{
6465
"name": "client name",
6566
"version": "10",

Tests/BuildSystemIntegrationTests/BuildServerBuildSystemTests.swift

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,61 @@ final class BuildServerBuildSystemTests: XCTestCase {
305305
)
306306
return diagnostics.fullReport?.items.map(\.message) == ["DEBUG SET"]
307307
}
308+
}
308309

310+
func testBuildServerConfigAtLegacyLocation() async throws {
311+
let project = try await BuildServerTestProject(
312+
files: [
313+
"Test.swift": """
314+
#if DEBUG
315+
#error("DEBUG SET")
316+
#else
317+
#error("DEBUG NOT SET")
318+
#endif
319+
"""
320+
],
321+
buildServerConfigLocation: "buildServer.json",
322+
buildServer: """
323+
class BuildServer(AbstractBuildServer):
324+
def workspace_build_targets(self, request: Dict[str, object]) -> Dict[str, object]:
325+
return {
326+
"targets": [
327+
{
328+
"id": {"uri": "bsp://dummy"},
329+
"tags": [],
330+
"languageIds": [],
331+
"dependencies": [],
332+
"capabilities": {},
333+
}
334+
]
335+
}
336+
337+
def buildtarget_sources(self, request: Dict[str, object]) -> Dict[str, object]:
338+
return {
339+
"items": [
340+
{
341+
"target": {"uri": "bsp://dummy"},
342+
"sources": [
343+
{"uri": "$TEST_DIR_URL/Test.swift", "kind": 1, "generated": False}
344+
],
345+
}
346+
]
347+
}
348+
349+
def textdocument_sourcekitoptions(self, request: Dict[str, object]) -> Dict[str, object]:
350+
return {
351+
"compilerArguments": ["$TEST_DIR/Test.swift", "-DDEBUG", $SDK_ARGS]
352+
}
353+
"""
354+
)
355+
356+
let (uri, _) = try project.openDocument("Test.swift")
357+
358+
try await repeatUntilExpectedResult {
359+
let diags = try await project.testClient.send(
360+
DocumentDiagnosticsRequest(textDocument: TextDocumentIdentifier(uri))
361+
)
362+
return diags.fullReport?.items.map(\.message) == ["DEBUG SET"]
363+
}
309364
}
310365
}

0 commit comments

Comments
 (0)