Skip to content

Commit

Permalink
Allow overriding the workspace buildsystem per workspace
Browse files Browse the repository at this point in the history
Merge options prior to determining the buildsystem to use for a
workspace, rather than after. Also fix up a few workspace type comments
which referred to the shorthand names.
  • Loading branch information
bnbarham committed Dec 14, 2024
1 parent bb13147 commit 6925afe
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 14 deletions.
2 changes: 1 addition & 1 deletion Documentation/Configuration File.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ The structure of the file is currently not guaranteed to be stable. Options may
- `logLevel: "debug"|"info"|"default"|"error"|"fault"`: The level from which one onwards log messages should be written.
- `privacyLevel: "public"|"private"|"sensitive"`: Whether potentially sensitive information should be redacted. Default is `public`, which redacts potentially sensitive information.
- `inputMirrorDirectory: string`: Write all input received by SourceKit-LSP on stdin to a file in this directory. Useful to record and replay an entire SourceKit-LSP session.
- `defaultWorkspaceType: "buildserver"|"compdb"|"swiftpm"`: Overrides workspace type selection logic.
- `defaultWorkspaceType: "swiftPM"|"compilationDatabase"|"buildServer"`: Overrides workspace type selection logic.
- `generatedFilesPath: string`: Directory in which generated interfaces and macro expansions should be stored.
- `backgroundIndexing: bool`: Explicitly enable or disable background indexing.
- `backgroundPreparationMode: "build"|"noLazy"|"enabled"`: Determines how background indexing should prepare a target. Possible values are:
Expand Down
13 changes: 12 additions & 1 deletion Sources/SKOptions/SourceKitLSPOptions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ public struct SourceKitLSPOptions: Sendable, Codable, Equatable {
set { logging = newValue }
}

/// Default workspace type (buildserver|compdb|swiftpm). Overrides workspace type selection logic.
/// Default workspace type (swiftPM|compilationDatabase|buildServer). Overrides workspace type selection logic.
public var defaultWorkspaceType: WorkspaceType?
public var generatedFilesPath: String?

Expand Down Expand Up @@ -444,6 +444,17 @@ public struct SourceKitLSPOptions: Sendable, Codable, Equatable {
)
}

public static func merging(base: SourceKitLSPOptions, workspaceFolder: DocumentURI) -> SourceKitLSPOptions {
return SourceKitLSPOptions.merging(
base: base,
override: SourceKitLSPOptions(
path: workspaceFolder.fileURL?
.appendingPathComponent(".sourcekit-lsp")
.appendingPathComponent("config.json")
)
)
}

public var generatedFilesAbsolutePath: URL {
if let generatedFilesPath {
return URL(fileURLWithPath: generatedFilesPath)
Expand Down
22 changes: 10 additions & 12 deletions Sources/SourceKitLSP/SourceKitLSPServer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,8 @@ package actor SourceKitLSPServer {
// was added to it and thus currently doesn't know that it can handle that file. In that case, we shouldn't open
// a new workspace for the same root. Instead, the existing workspace's build system needs to be reloaded.
let uri = DocumentURI(url)

let options = SourceKitLSPOptions.merging(base: self.options, workspaceFolder: uri)
guard let buildSystemSpec = determineBuildSystem(forWorkspaceFolder: uri, options: self.options) else {
continue
}
Expand All @@ -231,7 +233,7 @@ package actor SourceKitLSPServer {
guard
let workspace = await orLog(
"Creating workspace",
{ try await createWorkspace(workspaceFolder: uri, buildSystemSpec: buildSystemSpec) }
{ try await createWorkspace(workspaceFolder: uri, options: options, buildSystemSpec: buildSystemSpec) }
)
else {
continue
Expand Down Expand Up @@ -821,22 +823,15 @@ extension SourceKitLSPServer {
/// If the build system that was determined for the workspace does not satisfy `condition`, `nil` is returned.
private func createWorkspace(
workspaceFolder: DocumentURI,
options: SourceKitLSPOptions,
buildSystemSpec: BuildSystemSpec?
) async throws -> Workspace {
guard let capabilityRegistry = capabilityRegistry else {
struct NoCapabilityRegistryError: Error {}
logger.log("Cannot open workspace before server is initialized")
throw NoCapabilityRegistryError()
}
let testHooks = self.testHooks
let options = SourceKitLSPOptions.merging(
base: self.options,
override: SourceKitLSPOptions(
path: workspaceFolder.fileURL?
.appendingPathComponent(".sourcekit-lsp")
.appendingPathComponent("config.json")
)
)

logger.log("Creating workspace at \(workspaceFolder.forLogging) with options: \(options.forLogging)")
logger.logFullObjectInMultipleLogMessages(header: "Options for workspace", options.loggingProxy)

Expand All @@ -848,7 +843,7 @@ extension SourceKitLSPServer {
buildSystemSpec: buildSystemSpec,
toolchainRegistry: self.toolchainRegistry,
options: options,
testHooks: testHooks,
testHooks: self.testHooks,
indexTaskScheduler: indexTaskScheduler
)
return workspace
Expand All @@ -857,9 +852,12 @@ extension SourceKitLSPServer {
/// Determines the build system for the given workspace folder and creates a `Workspace` that uses this inferred build
/// system.
private func createWorkspaceWithInferredBuildSystem(workspaceFolder: DocumentURI) async throws -> Workspace {
let options = SourceKitLSPOptions.merging(base: self.options, workspaceFolder: workspaceFolder)
let buildSystemSpec = determineBuildSystem(forWorkspaceFolder: workspaceFolder, options: options)
return try await self.createWorkspace(
workspaceFolder: workspaceFolder,
buildSystemSpec: determineBuildSystem(forWorkspaceFolder: workspaceFolder, options: self.options)
options: options,
buildSystemSpec: buildSystemSpec
)
}

Expand Down
57 changes: 57 additions & 0 deletions Tests/SourceKitLSPTests/WorkspaceTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
//
//===----------------------------------------------------------------------===//

import BuildSystemIntegration
import Foundation
import LanguageServerProtocol
import SKLogging
Expand Down Expand Up @@ -967,4 +968,60 @@ final class WorkspaceTests: XCTestCase {
["Cannot convert value of type 'Int' to specified type 'String'"]
)
}

func testWorkspaceOptionsOverrideBuildSystem() async throws {
let swiftc = try await unwrap(ToolchainRegistry.forTesting.default?.swiftc)
let sdkArgs =
if let defaultSDKPath {
"""
-sdk '\(defaultSDKPath)'
"""
} else {
""
}

let project = try await MultiFileTestProject(files: [
".sourcekit-lsp/config.json": """
{
"defaultWorkspaceType": "compilationDatabase"
}
""",
"Foo.swift": """
#if HAVE_SETTINGS
#error("Have settings")
#endif
""",
"Sources/MyLib/Bar.swift": "",
"build/compile_commands.json": """
[
{
"directory": "$TEST_DIR",
"command": "\(swiftc.filePath) $TEST_DIR/Foo.swift -DHAVE_SETTINGS \(sdkArgs)",
"file": "Foo.swift",
"output": "build/Foo.swift.o"
},
]
""",
"Package.swift": """
// swift-tools-version: 5.7
import PackageDescription
let package = Package(
name: "MyLib",
targets: [
.target(name: "MyLib"),
]
)
""",
])
let (uri, _) = try project.openDocument("Foo.swift")
let diagnostics = try await project.testClient.send(
DocumentDiagnosticsRequest(textDocument: TextDocumentIdentifier(uri))
)
XCTAssertEqual(
diagnostics.fullReport?.items.map(\.message),
["Have settings"]
)
}
}

0 comments on commit 6925afe

Please sign in to comment.