Implement reloadPackageStatusCallback using BSP messages

This commit is contained in:
Alex Hoppen
2024-08-25 08:28:52 -07:00
parent 66f24e3554
commit 6cc2cc4e95
22 changed files with 425 additions and 354 deletions

View File

@@ -45,14 +45,6 @@ fileprivate typealias AbsolutePath = Basics.AbsolutePath
@preconcurrency import SPMBuildCore
#endif
/// Parameter of `reloadPackageStatusCallback` in ``SwiftPMWorkspace``.
///
/// Informs the callback about whether `reloadPackage` started or finished executing.
package enum ReloadPackageStatus: Sendable {
case start
case end
}
/// A build target in SwiftPM
package typealias SwiftBuildTarget = SourceKitLSPAPI.BuildTarget
@@ -186,13 +178,7 @@ package actor SwiftPMBuildSystem {
/// issues in SwiftPM.
private let packageLoadingQueue = AsyncQueue<Serial>()
package weak var messageHandler: BuiltInBuildSystemMessageHandler?
/// This callback is informed when `reloadPackage` starts and ends executing.
private var reloadPackageStatusCallback: (ReloadPackageStatus) async -> Void
/// Callbacks that should be called if the list of possible test files has changed.
private var testFilesDidChangeCallbacks: [() async -> Void] = []
package let connectionToSourceKitLSP: any Connection
/// Whether the `SwiftPMBuildSystem` is pointed at a `.index-build` directory that's independent of the
/// user's build.
@@ -258,15 +244,13 @@ package actor SwiftPMBuildSystem {
/// - projectRoot: The directory containing `Package.swift`
/// - toolchainRegistry: The toolchain registry to use to provide the Swift compiler used for
/// manifest parsing and runtime support.
/// - reloadPackageStatusCallback: Will be informed when `reloadPackage` starts and ends executing.
/// - Throws: If there is an error loading the package, or no manifest is found.
package init(
projectRoot: TSCAbsolutePath,
toolchainRegistry: ToolchainRegistry,
fileSystem: FileSystem = localFileSystem,
options: SourceKitLSPOptions,
messageHandler: (any BuiltInBuildSystemMessageHandler)?,
reloadPackageStatusCallback: @escaping (ReloadPackageStatus) async -> Void = { _ in },
connectionToSourceKitLSP: any Connection,
testHooks: SwiftPMTestHooks
) async throws {
self.projectRoot = projectRoot
@@ -281,7 +265,7 @@ package actor SwiftPMBuildSystem {
self.toolchain = toolchain
self.testHooks = testHooks
self.messageHandler = messageHandler
self.connectionToSourceKitLSP = connectionToSourceKitLSP
guard let destinationToolchainBinDir = toolchain.swiftc?.parentDirectory else {
throw Error.cannotDetermineHostToolchain
@@ -373,8 +357,6 @@ package actor SwiftPMBuildSystem {
flags: buildFlags
)
self.reloadPackageStatusCallback = reloadPackageStatusCallback
packageLoadingQueue.async {
await orLog("Initial package loading") {
// Schedule an initial generation of the build graph. Once the build graph is loaded, the build system will send
@@ -387,15 +369,12 @@ package actor SwiftPMBuildSystem {
/// Creates a build system using the Swift Package Manager, if this workspace is a package.
///
/// - Parameters:
/// - reloadPackageStatusCallback: Will be informed when `reloadPackage` starts and ends executing.
/// - Returns: nil if `workspacePath` is not part of a package or there is an error.
package init?(
projectRoot: TSCBasic.AbsolutePath,
toolchainRegistry: ToolchainRegistry,
options: SourceKitLSPOptions,
messageHandler: any BuiltInBuildSystemMessageHandler,
reloadPackageStatusCallback: @escaping (ReloadPackageStatus) async -> Void,
connectionToSourceKitLSP: any Connection,
testHooks: SwiftPMTestHooks
) async {
do {
@@ -404,8 +383,7 @@ package actor SwiftPMBuildSystem {
toolchainRegistry: toolchainRegistry,
fileSystem: localFileSystem,
options: options,
messageHandler: messageHandler,
reloadPackageStatusCallback: reloadPackageStatusCallback,
connectionToSourceKitLSP: connectionToSourceKitLSP,
testHooks: testHooks
)
} catch Error.noManifest {
@@ -423,12 +401,18 @@ extension SwiftPMBuildSystem {
///
/// - Important: Must only be called on `packageLoadingQueue`.
private func reloadPackageAssumingOnPackageLoadingQueue() async throws {
await reloadPackageStatusCallback(.start)
let progressManager = WorkDoneProgressManager(
connectionToClient: self.connectionToSourceKitLSP,
waitUntilClientInitialized: {},
tokenPrefix: "package-reloading",
initialDebounce: options.workDoneProgressDebounceDurationOrDefault,
title: "SourceKit-LSP: Reloading Package"
)
await testHooks.reloadPackageDidStart?()
defer {
Task {
await progressManager?.end()
await testHooks.reloadPackageDidFinish?()
await reloadPackageStatusCallback(.end)
}
}
@@ -475,10 +459,7 @@ extension SwiftPMBuildSystem {
swiftPMTargets[targetIdentifier] = buildTarget
}
await messageHandler?.sendNotificationToSourceKitLSP(DidChangeBuildTargetNotification(changes: nil))
for testFilesDidChangeCallback in testFilesDidChangeCallbacks {
await testFilesDidChangeCallback()
}
connectionToSourceKitLSP.send(DidChangeBuildTargetNotification(changes: nil))
}
}
@@ -651,16 +632,13 @@ extension SwiftPMBuildSystem: BuildSystemIntegration.BuiltInBuildSystem {
}
private nonisolated func logMessageToIndexLog(_ taskID: IndexTaskID, _ message: String) {
// FIXME: When `messageHandler` is a Connection, we don't need to go via Task anymore
Task {
await self.messageHandler?.sendNotificationToSourceKitLSP(
BuildServerProtocol.LogMessageNotification(
type: .info,
task: TaskId(id: taskID.rawValue),
message: message
)
connectionToSourceKitLSP.send(
BuildServerProtocol.LogMessageNotification(
type: .info,
task: TaskId(id: taskID.rawValue),
message: message
)
}
)
}
private func prepare(singleTarget target: BuildTargetIdentifier) async throws {