From ed8f0a407788fb5867ffa090f89af50f43b44188 Mon Sep 17 00:00:00 2001 From: Owen Voorhees Date: Wed, 2 Sep 2026 14:20:28 -0700 Subject: [PATCH 1/2] Enable SwiftPM BSP by default when using 6.5 toolchains --- .../BuildServerManager.swift | 21 +++++++++++++++---- .../SKTestSupport/SwiftPMTestProject.swift | 11 ++++++++-- Tests/SourceKitLSPTests/WorkspaceTests.swift | 9 ++++---- 3 files changed, 30 insertions(+), 11 deletions(-) diff --git a/Sources/BuildServerIntegration/BuildServerManager.swift b/Sources/BuildServerIntegration/BuildServerManager.swift index b0c2d375a..cc13b94d9 100644 --- a/Sources/BuildServerIntegration/BuildServerManager.swift +++ b/Sources/BuildServerIntegration/BuildServerManager.swift @@ -289,14 +289,17 @@ private extension BuildServerSpec { selectedBuildSystem = .swiftbuild case (.native, _): selectedBuildSystem = .native - // If there is no explicit preference and we're background indexing, default to native - // for now. + // If there is no explicit preference and we're background indexing, use the toolchain version to pick a default. case (nil, true): - selectedBuildSystem = .native + selectedBuildSystem = await SwiftPMBuildSystem.defaultBuildSystem(toolchainRegistry: toolchainRegistry) // If there is no explicit preference and we're not background indexing, we should // attempt to match the user's manually intiated builds to maximize compatibility. case (nil, false): - selectedBuildSystem = inferredBuildSystem ?? .native + if let inferredBuildSystem { + selectedBuildSystem = inferredBuildSystem + } else { + selectedBuildSystem = await SwiftPMBuildSystem.defaultBuildSystem(toolchainRegistry: toolchainRegistry) + } } // Choose the appropiate adapter based on the build system we just selected. switch selectedBuildSystem { @@ -2124,3 +2127,13 @@ private extension OnBuildLogMessageNotification { } } } + +extension SwiftPMBuildSystem { + package static func defaultBuildSystem(toolchainRegistry: ToolchainRegistry) async -> SwiftPMBuildSystem { + let toolchain = await toolchainRegistry.preferredToolchain(containing: [\.swift]) + guard let swiftVersion = try? await toolchain?.swiftVersion, swiftVersion >= SwiftVersion(6, 5) else { + return .native + } + return .swiftbuild + } +} diff --git a/Sources/SKTestSupport/SwiftPMTestProject.swift b/Sources/SKTestSupport/SwiftPMTestProject.swift index 71a0c2e81..4f0feed05 100644 --- a/Sources/SKTestSupport/SwiftPMTestProject.swift +++ b/Sources/SKTestSupport/SwiftPMTestProject.swift @@ -10,6 +10,7 @@ // //===----------------------------------------------------------------------===// +import BuildServerIntegration package import Foundation @_spi(SourceKitLSP) package import LanguageServerProtocol @_spi(SourceKitLSP) import SKLogging @@ -241,9 +242,15 @@ package class SwiftPMTestProject: MultiFileTestProject { /// Build a SwiftPM package package manifest is located in the directory at `path`. package static func build( at path: URL, - buildSystem: SwiftPMBuildSystem = .native, + buildSystem: SwiftPMBuildSystem? = nil, extraArguments: [String] = [] ) async throws { + let resolvedBuildSystem: SwiftPMBuildSystem + if let buildSystem { + resolvedBuildSystem = buildSystem + } else { + resolvedBuildSystem = await SwiftPMBuildSystem.defaultBuildSystem(toolchainRegistry: ToolchainRegistry.forTesting) + } guard let swift = await ToolchainRegistry.forTesting.default?.swift else { throw Error.swiftNotFound } @@ -255,7 +262,7 @@ package class SwiftPMTestProject: MultiFileTestProject { "--build-tests", "-Xswiftc", "-index-ignore-system-modules", "-Xcc", "-index-ignore-system-symbols", - "--build-system", buildSystem.rawValue, + "--build-system", resolvedBuildSystem.rawValue, ] + extraArguments if let globalModuleCache = try globalModuleCache { arguments += [ diff --git a/Tests/SourceKitLSPTests/WorkspaceTests.swift b/Tests/SourceKitLSPTests/WorkspaceTests.swift index 0dfcc59cc..999da79dc 100644 --- a/Tests/SourceKitLSPTests/WorkspaceTests.swift +++ b/Tests/SourceKitLSPTests/WorkspaceTests.swift @@ -1394,11 +1394,10 @@ final class WorkspaceTests: SourceKitLSPTestCase { workspace: DocumentURI(project.scratchDirectory) ) ) - if project.testClient.server.options.swiftPMOrDefault.buildSystem == .swiftbuild { - XCTAssertEqual(outputPaths.outputPaths.map { $0.suffix(7) }.sorted(), ["FileA.o", "FileB.o"]) - } else { - XCTAssertEqual(outputPaths.outputPaths.map { $0.suffix(13) }.sorted(), ["FileA.swift.o", "FileB.swift.o"]) - } + XCTAssertTrue( + outputPaths.outputPaths.map { $0.suffix(7) }.sorted() == ["FileA.o", "FileB.o"] + || outputPaths.outputPaths.map { $0.suffix(13) }.sorted() == ["FileA.swift.o", "FileB.swift.o"] + ) } func testOrphanedClangLanguageServiceShutdown() async throws { From 519cd78ce1e94569e547d2f97fcaece0d303f528 Mon Sep 17 00:00:00 2001 From: Owen Voorhees Date: Tue, 29 Sep 2026 11:14:34 -0700 Subject: [PATCH 2/2] [DNM] use per-test module cache --- .../IndexedSingleSwiftFileTestProject.swift | 8 ++++++-- Sources/SKTestSupport/SwiftPMTestProject.swift | 10 ++++++++-- Sources/SKTestSupport/TestSourceKitLSPClient.swift | 12 +++++++++--- Sources/SKTestSupport/Utils.swift | 8 ++++++-- 4 files changed, 29 insertions(+), 9 deletions(-) diff --git a/Sources/SKTestSupport/IndexedSingleSwiftFileTestProject.swift b/Sources/SKTestSupport/IndexedSingleSwiftFileTestProject.swift index 623eb9c33..0ebc77aa1 100644 --- a/Sources/SKTestSupport/IndexedSingleSwiftFileTestProject.swift +++ b/Sources/SKTestSupport/IndexedSingleSwiftFileTestProject.swift @@ -101,9 +101,10 @@ package struct IndexedSingleSwiftFileTestProject { "-index-store-path", try indexURL.filePath, "-typecheck", ] - if let globalModuleCache = try globalModuleCache { + let moduleCache = try uniqueTestModuleCache() + if let moduleCache { compilerArguments += [ - "-module-cache-path", try globalModuleCache.filePath, + "-module-cache-path", try moduleCache.filePath, ] } if !indexSystemModules { @@ -207,6 +208,9 @@ package struct IndexedSingleSwiftFileTestProject { cleanUp: { if cleanUp { try? FileManager.default.removeItem(at: testWorkspaceDirectory) + if let moduleCache { + try? FileManager.default.removeItem(at: moduleCache) + } } } ) diff --git a/Sources/SKTestSupport/SwiftPMTestProject.swift b/Sources/SKTestSupport/SwiftPMTestProject.swift index 4f0feed05..9f8280b2d 100644 --- a/Sources/SKTestSupport/SwiftPMTestProject.swift +++ b/Sources/SKTestSupport/SwiftPMTestProject.swift @@ -264,9 +264,15 @@ package class SwiftPMTestProject: MultiFileTestProject { "-Xcc", "-index-ignore-system-symbols", "--build-system", resolvedBuildSystem.rawValue, ] + extraArguments - if let globalModuleCache = try globalModuleCache { + let moduleCache = try uniqueTestModuleCache() + defer { + if let moduleCache, cleanScratchDirectories { + try? FileManager.default.removeItem(at: moduleCache) + } + } + if let moduleCache { arguments += [ - "-Xswiftc", "-module-cache-path", "-Xswiftc", try globalModuleCache.filePath, + "-Xswiftc", "-module-cache-path", "-Xswiftc", try moduleCache.filePath, ] } let argumentsCopy = arguments diff --git a/Sources/SKTestSupport/TestSourceKitLSPClient.swift b/Sources/SKTestSupport/TestSourceKitLSPClient.swift index cf626b99c..d9b52c928 100644 --- a/Sources/SKTestSupport/TestSourceKitLSPClient.swift +++ b/Sources/SKTestSupport/TestSourceKitLSPClient.swift @@ -170,9 +170,10 @@ package final class TestSourceKitLSPClient: MessageHandler, Sendable { } else { try await SourceKitLSPOptions.testDefault() } - if let globalModuleCache = try globalModuleCache { + let moduleCache = try uniqueTestModuleCache() + if let moduleCache { options.swiftPMOrDefault.swiftCompilerFlags = - (options.swiftPMOrDefault.swiftCompilerFlags ?? []) + ["-module-cache-path", try globalModuleCache.filePath] + (options.swiftPMOrDefault.swiftCompilerFlags ?? []) + ["-module-cache-path", try moduleCache.filePath] } options.backgroundIndexing = enableBackgroundIndexing if options.sourcekitdRequestTimeout == nil { @@ -196,7 +197,12 @@ package final class TestSourceKitLSPClient: MessageHandler, Sendable { } ) - self.cleanUp = cleanUp + self.cleanUp = { + if let moduleCache, cleanScratchDirectories { + try? FileManager.default.removeItem(at: moduleCache) + } + cleanUp() + } self.usePullDiagnostics = usePullDiagnostics self.serverToClientConnection.start(handler: WeakMessageHandler(self)) diff --git a/Sources/SKTestSupport/Utils.swift b/Sources/SKTestSupport/Utils.swift index 8e8c897b7..c0e4dccb3 100644 --- a/Sources/SKTestSupport/Utils.swift +++ b/Sources/SKTestSupport/Utils.swift @@ -112,7 +112,7 @@ package func withTestScratchDir( return try await body(scratchDirectory) } -var globalModuleCache: URL? { +private var testModuleCacheRoot: URL? { get throws { if let customModuleCache = ProcessInfo.processInfo.environment["SOURCEKIT_LSP_TEST_MODULE_CACHE"] { if customModuleCache.isEmpty { @@ -121,6 +121,10 @@ var globalModuleCache: URL? { return URL(fileURLWithPath: customModuleCache) } return try FileManager.default.temporaryDirectory.realpath - .appending(components: "sourcekit-lsp-test-scratch", "shared-module-cache") + .appending(components: "sourcekit-lsp-test-scratch", "module-caches") } } + +func uniqueTestModuleCache() throws -> URL? { + return try testModuleCacheRoot?.appending(component: UUID().uuidString, directoryHint: .isDirectory) +}