Skip to content

Commit 74b1f09

Browse files
committed
Let the build system determine which toolchain to use for a document
This allows us to fix a toolchain when using a `SwiftPMBuildSystem`, which is critical to ensure that a target gets prepared using the same toolchain that is used to index it and that is used for sourcekitd.
1 parent 6348603 commit 74b1f09

File tree

9 files changed

+63
-36
lines changed

9 files changed

+63
-36
lines changed

Sources/SKCore/BuildServerBuildSystem.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,10 @@ extension BuildServerBuildSystem: BuildSystem {
275275
return nil
276276
}
277277

278+
public func toolchain(for uri: DocumentURI, _ language: Language) async -> SKCore.Toolchain? {
279+
return nil
280+
}
281+
278282
public func configuredTargets(for document: DocumentURI) async -> [ConfiguredTarget] {
279283
return [ConfiguredTarget(targetID: "dummy", runDestinationID: "dummy")]
280284
}

Sources/SKCore/BuildSystem.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,11 @@ public protocol BuildSystem: AnyObject, Sendable {
149149
/// If `nil` is returned, the language based on the file's extension.
150150
func defaultLanguage(for document: DocumentURI) async -> Language?
151151

152+
/// The toolchain that should be used to open the given document.
153+
///
154+
/// If `nil` is returned, then the default toolchain for the given language is used.
155+
func toolchain(for uri: DocumentURI, _ language: Language) async -> Toolchain?
156+
152157
/// Register the given file for build-system level change notifications, such
153158
/// as command line flag changes, dependency changes, etc.
154159
///

Sources/SKCore/BuildSystemManager.swift

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -100,9 +100,18 @@ extension BuildSystemManager {
100100

101101
/// Returns the toolchain that should be used to process the given document.
102102
public func toolchain(for uri: DocumentURI, _ language: Language) async -> Toolchain? {
103-
// To support multiple toolchains within a single workspace, we need to ask the build system which toolchain to use
104-
// for this document.
105-
return await toolchainRegistry.defaultToolchain(for: language)
103+
if let toolchain = await buildSystem?.toolchain(for: uri, language) {
104+
return toolchain
105+
}
106+
107+
switch language {
108+
case .swift:
109+
return await toolchainRegistry.preferredToolchain(containing: [\.sourcekitd, \.swift, \.swiftc])
110+
case .c, .cpp, .objective_c, .objective_cpp:
111+
return await toolchainRegistry.preferredToolchain(containing: [\.clang, \.clangd])
112+
default:
113+
return nil
114+
}
106115
}
107116

108117
/// - Note: Needed so we can set the delegate from a different isolation context.

Sources/SKCore/CompilationDatabaseBuildSystem.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,10 @@ extension CompilationDatabaseBuildSystem: BuildSystem {
121121
return nil
122122
}
123123

124+
public func toolchain(for uri: DocumentURI, _ language: Language) async -> SKCore.Toolchain? {
125+
return nil
126+
}
127+
124128
public func configuredTargets(for document: DocumentURI) async -> [ConfiguredTarget] {
125129
return [ConfiguredTarget(targetID: "dummy", runDestinationID: "dummy")]
126130
}

Sources/SKCore/ToolchainRegistry.swift

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -246,26 +246,14 @@ public final actor ToolchainRegistry {
246246
return darwinToolchainOverride ?? ToolchainRegistry.darwinDefaultToolchainIdentifier
247247
}
248248

249-
/// The toolchain to use for a document in the given language if the build system doesn't override it.
250-
func defaultToolchain(for language: Language) -> Toolchain? {
251-
let supportsLang = { (toolchain: Toolchain) -> Bool in
252-
// FIXME: the fact that we're looking at clangd/sourcekitd instead of the compiler indicates this method needs a parameter stating what kind of tool we're looking for.
253-
switch language {
254-
case .swift:
255-
return toolchain.sourcekitd != nil
256-
case .c, .cpp, .objective_c, .objective_cpp:
257-
return toolchain.clangd != nil
258-
default:
259-
return false
260-
}
261-
}
262-
263-
if let toolchain = self.default, supportsLang(toolchain) {
249+
/// Returns the preferred toolchain that contains all the tools at the given key paths.
250+
public func preferredToolchain(containing requiredTools: [KeyPath<Toolchain, AbsolutePath?>]) -> Toolchain? {
251+
if let toolchain = self.default, requiredTools.allSatisfy({ toolchain[keyPath: $0] != nil }) {
264252
return toolchain
265253
}
266254

267255
for toolchain in toolchains {
268-
if supportsLang(toolchain) {
256+
if requiredTools.allSatisfy({ toolchain[keyPath: $0] != nil }) {
269257
return toolchain
270258
}
271259
}

Sources/SKSwiftPMWorkspace/SwiftPMBuildSystem.swift

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -57,10 +57,12 @@ public typealias BuildServerTarget = BuildServerProtocol.BuildTarget
5757

5858
/// Same as `toolchainRegistry.default`.
5959
///
60-
/// Needed to work around a compiler crash that prevents us from accessing `toolchainRegistry.default` in
60+
/// Needed to work around a compiler crash that prevents us from accessing `toolchainRegistry.preferredToolchain` in
6161
/// `SwiftPMWorkspace.init`.
62-
private func getDefaultToolchain(_ toolchainRegistry: ToolchainRegistry) async -> SKCore.Toolchain? {
63-
return await toolchainRegistry.default
62+
private func preferredToolchain(_ toolchainRegistry: ToolchainRegistry) async -> SKCore.Toolchain? {
63+
return await toolchainRegistry.preferredToolchain(containing: [
64+
\.clang, \.clangd, \.sourcekitd, \.swift, \.swiftc,
65+
])
6466
}
6567

6668
fileprivate extension BuildTriple {
@@ -116,7 +118,7 @@ public actor SwiftPMBuildSystem {
116118
let workspace: Workspace
117119
public let buildParameters: BuildParameters
118120
let fileSystem: FileSystem
119-
private let toolchainRegistry: ToolchainRegistry
121+
private let toolchain: SKCore.Toolchain
120122

121123
var fileToTargets: [AbsolutePath: [SwiftBuildTarget]] = [:]
122124
var sourceDirToTargets: [AbsolutePath: [SwiftBuildTarget]] = [:]
@@ -173,7 +175,11 @@ public actor SwiftPMBuildSystem {
173175
) async throws {
174176
self.workspacePath = workspacePath
175177
self.fileSystem = fileSystem
176-
self.toolchainRegistry = toolchainRegistry
178+
guard let toolchain = await preferredToolchain(toolchainRegistry) else {
179+
throw Error.cannotDetermineHostToolchain
180+
}
181+
182+
self.toolchain = toolchain
177183
self.forceResolvedVersions = forceResolvedVersions
178184

179185
guard let packageRoot = findPackageDirectory(containing: workspacePath, fileSystem) else {
@@ -182,12 +188,12 @@ public actor SwiftPMBuildSystem {
182188

183189
self.projectRoot = try resolveSymlinks(packageRoot)
184190

185-
guard let destinationToolchainBinDir = await getDefaultToolchain(toolchainRegistry)?.swiftc?.parentDirectory else {
191+
guard let destinationToolchainBinDir = toolchain.swiftc?.parentDirectory else {
186192
throw Error.cannotDetermineHostToolchain
187193
}
188194

189195
let swiftSDK = try SwiftSDK.hostSwiftSDK(AbsolutePath(destinationToolchainBinDir))
190-
let toolchain = try UserToolchain(swiftSDK: swiftSDK)
196+
let swiftPMToolchain = try UserToolchain(swiftSDK: swiftSDK)
191197

192198
var location = try Workspace.Location(
193199
forRootPackage: AbsolutePath(packageRoot),
@@ -204,7 +210,7 @@ public actor SwiftPMBuildSystem {
204210
fileSystem: fileSystem,
205211
location: location,
206212
configuration: configuration,
207-
customHostToolchain: toolchain
213+
customHostToolchain: swiftPMToolchain
208214
)
209215

210216
let buildConfiguration: PackageModel.BuildConfiguration
@@ -216,9 +222,11 @@ public actor SwiftPMBuildSystem {
216222
}
217223

218224
self.buildParameters = try BuildParameters(
219-
dataPath: location.scratchDirectory.appending(component: toolchain.targetTriple.platformBuildPathComponent),
225+
dataPath: location.scratchDirectory.appending(
226+
component: swiftPMToolchain.targetTriple.platformBuildPathComponent
227+
),
220228
configuration: buildConfiguration,
221-
toolchain: toolchain,
229+
toolchain: swiftPMToolchain,
222230
flags: buildSetup.flags
223231
)
224232

@@ -407,6 +415,10 @@ extension SwiftPMBuildSystem: SKCore.BuildSystem {
407415
return nil
408416
}
409417

418+
public func toolchain(for uri: DocumentURI, _ language: Language) async -> SKCore.Toolchain? {
419+
return toolchain
420+
}
421+
410422
public func configuredTargets(for uri: DocumentURI) -> [ConfiguredTarget] {
411423
guard let url = uri.fileURL, let path = try? AbsolutePath(validating: url.path) else {
412424
// We can't determine targets for non-file URIs.
@@ -444,23 +456,20 @@ extension SwiftPMBuildSystem: SKCore.BuildSystem {
444456
// TODO (indexing): Support preparation of multiple targets at once.
445457
// https://github.com/apple/sourcekit-lsp/issues/1262
446458
for target in targets {
447-
try await prepare(singleTarget: target)
459+
await orLog("Preparing") { try await self.prepare(singleTarget: target) }
448460
}
449461
}
450462

451463
private func prepare(singleTarget target: ConfiguredTarget) async throws {
452464
// TODO (indexing): Add a proper 'prepare' job in SwiftPM instead of building the target.
453465
// https://github.com/apple/sourcekit-lsp/issues/1254
454-
guard let toolchain = await toolchainRegistry.default else {
455-
logger.error("Not preparing because not toolchain exists")
456-
return
457-
}
458466
guard let swift = toolchain.swift else {
459467
logger.error(
460-
"Not preparing because toolchain at \(toolchain.identifier) does not contain a Swift compiler"
468+
"Not preparing because toolchain at \(self.toolchain.identifier) does not contain a Swift compiler"
461469
)
462470
return
463471
}
472+
logger.debug("Preparing '\(target.targetID)' using \(self.toolchain.identifier)")
464473
let arguments = [
465474
swift.pathString, "build",
466475
"--package-path", workspacePath.pathString,

Sources/SourceKitLSP/SourceKitLSPServer.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -850,7 +850,7 @@ public actor SourceKitLSPServer {
850850
return nil
851851
}
852852

853-
logger.log("Using toolchain \(toolchain.displayName) (\(toolchain.identifier)) for \(uri.forLogging)")
853+
logger.log("Using toolchain \(toolchain.identifier) (\(toolchain.identifier)) for \(uri.forLogging)")
854854

855855
return workspace.documentService.withLock { documentService in
856856
if let concurrentlySetService = documentService[uri] {

Tests/SKCoreTests/BuildSystemManagerTests.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -453,6 +453,10 @@ class ManualBuildSystem: BuildSystem {
453453
return nil
454454
}
455455

456+
public func toolchain(for uri: DocumentURI, _ language: Language) async -> SKCore.Toolchain? {
457+
return nil
458+
}
459+
456460
public func configuredTargets(for document: DocumentURI) async -> [ConfiguredTarget] {
457461
return [ConfiguredTarget(targetID: "dummy", runDestinationID: "dummy")]
458462
}

Tests/SourceKitLSPTests/BuildSystemTests.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,10 @@ final class TestBuildSystem: BuildSystem {
5151
return nil
5252
}
5353

54+
public func toolchain(for uri: DocumentURI, _ language: Language) async -> SKCore.Toolchain? {
55+
return nil
56+
}
57+
5458
public func configuredTargets(for document: DocumentURI) async -> [ConfiguredTarget] {
5559
return [ConfiguredTarget(targetID: "dummy", runDestinationID: "dummy")]
5660
}

0 commit comments

Comments
 (0)