diff --git a/Sources/BuildSystemIntegration/BuildServerBuildSystem.swift b/Sources/BuildSystemIntegration/BuildServerBuildSystem.swift index 3df98174..b2eb3dd0 100644 --- a/Sources/BuildSystemIntegration/BuildServerBuildSystem.swift +++ b/Sources/BuildSystemIntegration/BuildServerBuildSystem.swift @@ -15,6 +15,7 @@ import Foundation import LanguageServerProtocol import LanguageServerProtocolJSONRPC import SKLogging +import SKOptions import SKSupport import SwiftExtensions import ToolchainRegistry @@ -259,6 +260,13 @@ private func readReponseDataKey(data: LSPAny?, key: String) -> String? { } extension BuildServerBuildSystem: BuiltInBuildSystem { + static package func projectRoot(for workspaceFolder: AbsolutePath, options: SourceKitLSPOptions) -> AbsolutePath? { + guard localFileSystem.isFile(workspaceFolder.appending(component: "buildServer.json")) else { + return nil + } + return workspaceFolder + } + package nonisolated var supportsPreparation: Bool { false } /// The build settings for the given file. diff --git a/Sources/BuildSystemIntegration/BuildSystem.swift b/Sources/BuildSystemIntegration/BuildSystem.swift index ebb7d949..f7c57ed0 100644 --- a/Sources/BuildSystemIntegration/BuildSystem.swift +++ b/Sources/BuildSystemIntegration/BuildSystem.swift @@ -13,6 +13,7 @@ import BuildServerProtocol import LanguageServerProtocol import SKLogging +import SKOptions import ToolchainRegistry import struct TSCBasic.AbsolutePath @@ -67,6 +68,13 @@ package struct PrepareNotSupportedError: Error, CustomStringConvertible { /// For example, a SwiftPMWorkspace provides compiler arguments for the files /// contained in a SwiftPM package root directory. package protocol BuiltInBuildSystem: AnyObject, Sendable { + /// When opening an LSP workspace at `workspaceFolder`, determine the directory in which a project of this build system + /// starts. For example, a user might open the `Sources` folder of a SwiftPM project, then the project root is the + /// directory containing `Package.swift`. + /// + /// Returns `nil` if the build system can't handle the given workspace folder + static func projectRoot(for workspaceFolder: AbsolutePath, options: SourceKitLSPOptions) -> AbsolutePath? + /// The root of the project that this build system manages. For example, for SwiftPM packages, this is the folder /// containing Package.swift. For compilation databases it is the root folder based on which the compilation database /// was found. diff --git a/Sources/BuildSystemIntegration/CompilationDatabaseBuildSystem.swift b/Sources/BuildSystemIntegration/CompilationDatabaseBuildSystem.swift index cf149936..05937175 100644 --- a/Sources/BuildSystemIntegration/CompilationDatabaseBuildSystem.swift +++ b/Sources/BuildSystemIntegration/CompilationDatabaseBuildSystem.swift @@ -14,6 +14,7 @@ import BuildServerProtocol import Dispatch import LanguageServerProtocol import SKLogging +import SKOptions import SKSupport import ToolchainRegistry @@ -100,6 +101,13 @@ package actor CompilationDatabaseBuildSystem { } extension CompilationDatabaseBuildSystem: BuiltInBuildSystem { + static package func projectRoot(for workspaceFolder: AbsolutePath, options: SourceKitLSPOptions) -> AbsolutePath? { + if tryLoadCompilationDatabase(directory: workspaceFolder) != nil { + return workspaceFolder + } + return nil + } + package nonisolated var supportsPreparation: Bool { false } package var indexDatabasePath: AbsolutePath? { diff --git a/Sources/BuildSystemIntegration/SwiftPMBuildSystem.swift b/Sources/BuildSystemIntegration/SwiftPMBuildSystem.swift index 66397adf..1f2a5f49 100644 --- a/Sources/BuildSystemIntegration/SwiftPMBuildSystem.swift +++ b/Sources/BuildSystemIntegration/SwiftPMBuildSystem.swift @@ -212,10 +212,6 @@ package actor SwiftPMBuildSystem { // MARK: Build system options (set once and not modified) - /// The path at which the LSP workspace is opened. This might be a subdirectory of the path that contains the - /// `Package.swift`. - private let workspacePath: TSCAbsolutePath - /// The directory containing `Package.swift`. package let projectRoot: TSCAbsolutePath @@ -244,23 +240,47 @@ package actor SwiftPMBuildSystem { /// greater depth. private var targets: [BuildTargetIdentifier: (buildTarget: SwiftBuildTarget, depth: Int)] = [:] + static package func projectRoot( + for path: TSCBasic.AbsolutePath, + options: SourceKitLSPOptions + ) -> TSCBasic.AbsolutePath? { + guard var path = try? resolveSymlinks(path) else { + return nil + } + while true { + let packagePath = path.appending(component: "Package.swift") + if localFileSystem.isFile(packagePath) { + let contents = try? localFileSystem.readFileContents(packagePath) + if contents?.cString.contains("PackageDescription") == true { + return path + } + } + + if path.isRoot { + return nil + } + path = path.parentDirectory + } + return nil + } + /// Creates a build system using the Swift Package Manager, if this workspace is a package. /// /// - Parameters: - /// - workspace: The workspace root path. + /// - 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( - workspacePath: TSCAbsolutePath, + projectRoot: TSCAbsolutePath, toolchainRegistry: ToolchainRegistry, fileSystem: FileSystem = localFileSystem, options: SourceKitLSPOptions, reloadPackageStatusCallback: @escaping (ReloadPackageStatus) async -> Void = { _ in }, testHooks: SwiftPMTestHooks ) async throws { - self.workspacePath = workspacePath + self.projectRoot = projectRoot self.options = options self.fileSystem = fileSystem let toolchain = await toolchainRegistry.preferredToolchain(containing: [ @@ -273,12 +293,6 @@ package actor SwiftPMBuildSystem { self.toolchain = toolchain self.testHooks = testHooks - guard let packageRoot = findPackageDirectory(containing: workspacePath, fileSystem) else { - throw Error.noManifest(workspacePath: workspacePath) - } - - self.projectRoot = try resolveSymlinks(packageRoot) - guard let destinationToolchainBinDir = toolchain.swiftc?.parentDirectory else { throw Error.cannotDetermineHostToolchain } @@ -306,11 +320,11 @@ package actor SwiftPMBuildSystem { let destinationSwiftPMToolchain = try UserToolchain(swiftSDK: destinationSDK) var location = try Workspace.Location( - forRootPackage: AbsolutePath(packageRoot), + forRootPackage: AbsolutePath(projectRoot), fileSystem: fileSystem ) if options.backgroundIndexingOrDefault { - location.scratchDirectory = AbsolutePath(packageRoot.appending(component: ".index-build")) + location.scratchDirectory = AbsolutePath(projectRoot.appending(component: ".index-build")) } else if let scratchDirectory = options.swiftPMOrDefault.scratchPath, let scratchDirectoryPath = try? AbsolutePath(validating: scratchDirectory) { @@ -391,18 +405,15 @@ package actor SwiftPMBuildSystem { /// - 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?( - uri: DocumentURI, + projectRoot: TSCBasic.AbsolutePath, toolchainRegistry: ToolchainRegistry, options: SourceKitLSPOptions, reloadPackageStatusCallback: @escaping (ReloadPackageStatus) async -> Void, testHooks: SwiftPMTestHooks ) async { - guard let fileURL = uri.fileURL else { - return nil - } do { try await self.init( - workspacePath: try TSCAbsolutePath(validating: fileURL.path), + projectRoot: projectRoot, toolchainRegistry: toolchainRegistry, fileSystem: localFileSystem, options: options, @@ -412,7 +423,7 @@ package actor SwiftPMBuildSystem { } catch Error.noManifest { return nil } catch { - logger.error("Failed to create SwiftPMWorkspace at \(uri.forLogging): \(error.forLogging)") + logger.error("Failed to create SwiftPMWorkspace at \(projectRoot.pathString): \(error.forLogging)") return nil } } @@ -569,13 +580,13 @@ extension SwiftPMBuildSystem: BuildSystemIntegration.BuiltInBuildSystem { // with the `.cpp` file. return FileBuildSettings( compilerArguments: try await compilerArguments(for: DocumentURI(substituteFile), in: buildTarget), - workingDirectory: workspacePath.pathString + workingDirectory: projectRoot.pathString ).patching(newFile: try resolveSymlinks(path).pathString, originalFile: substituteFile.absoluteString) } return FileBuildSettings( compilerArguments: try await compilerArguments(for: uri, in: buildTarget), - workingDirectory: workspacePath.pathString + workingDirectory: projectRoot.pathString ) } @@ -683,7 +694,7 @@ extension SwiftPMBuildSystem: BuildSystemIntegration.BuiltInBuildSystem { logger.debug("Preparing '\(target.forLogging)' using \(self.toolchain.identifier)") var arguments = [ swift.pathString, "build", - "--package-path", workspacePath.pathString, + "--package-path", projectRoot.pathString, "--scratch-path", self.workspace.location.scratchDirectory.pathString, "--disable-index-store", "--target", try target.targetProperties.target, @@ -874,29 +885,6 @@ extension SwiftPMBuildSystem: BuildSystemIntegration.BuiltInBuildSystem { } } -/// Find a Swift Package root directory that contains the given path, if any. -private func findPackageDirectory( - containing path: TSCAbsolutePath, - _ fileSystem: FileSystem -) -> TSCAbsolutePath? { - var path = path - while true { - let packagePath = path.appending(component: "Package.swift") - if fileSystem.isFile(packagePath) { - let contents = try? fileSystem.readFileContents(packagePath) - if contents?.cString.contains("PackageDescription") == true { - return path - } - } - - if path.isRoot { - return nil - } - path = path.parentDirectory - } - return path -} - extension Basics.Diagnostic.Severity { var asLogLevel: LogLevel { switch self { diff --git a/Sources/SourceKitLSP/CreateBuildSystem.swift b/Sources/SourceKitLSP/CreateBuildSystem.swift index bf0dcc61..fad35d5e 100644 --- a/Sources/SourceKitLSP/CreateBuildSystem.swift +++ b/Sources/SourceKitLSP/CreateBuildSystem.swift @@ -19,62 +19,71 @@ import ToolchainRegistry import struct TSCBasic.AbsolutePath import struct TSCBasic.RelativePath -/// Tries to create a build system for a workspace at the given location, with the given parameters. +fileprivate extension WorkspaceType { + var buildSystemType: BuiltInBuildSystem.Type { + switch self { + case .buildServer: return BuildServerBuildSystem.self + case .compilationDatabase: return CompilationDatabaseBuildSystem.self + case .swiftPM: return SwiftPMBuildSystem.self + } + } +} + +/// Determine which build system should be started to handle the given workspace folder and at which folder that build +/// system's project root is (see `BuiltInBuildSystem.projectRoot(for:options:)`). +/// +/// Returns `nil` if no build system can handle this workspace folder. +func determineBuildSystem( + forWorkspaceFolder workspaceFolder: DocumentURI, + options: SourceKitLSPOptions +) -> (WorkspaceType, projectRoot: AbsolutePath)? { + var buildSystemPreference: [WorkspaceType] = [ + .buildServer, .swiftPM, .compilationDatabase, + ] + if let defaultBuildSystem = options.defaultWorkspaceType { + buildSystemPreference.removeAll(where: { $0 == defaultBuildSystem }) + buildSystemPreference.insert(defaultBuildSystem, at: 0) + } + guard let workspaceFolderUrl = workspaceFolder.fileURL, + let workspaceFolderPath = try? AbsolutePath(validating: workspaceFolderUrl.path) + else { + return nil + } + for buildSystemType in buildSystemPreference { + if let projectRoot = buildSystemType.buildSystemType.projectRoot(for: workspaceFolderPath, options: options) { + return (buildSystemType, projectRoot) + } + } + + return nil +} + +/// Create a build system of the given type. func createBuildSystem( - rootUri: DocumentURI, + ofType buildSystemType: WorkspaceType, + projectRoot: AbsolutePath, options: SourceKitLSPOptions, testHooks: TestHooks, toolchainRegistry: ToolchainRegistry, reloadPackageStatusCallback: @Sendable @escaping (ReloadPackageStatus) async -> Void ) async -> BuiltInBuildSystem? { - guard let rootUrl = rootUri.fileURL, let rootPath = try? AbsolutePath(validating: rootUrl.path) else { - // We assume that workspaces are directories. This is only true for URLs not for URIs in general. - // Simply skip setting up the build integration in this case. - logger.error( - "Cannot setup build integration at URI \(rootUri.forLogging) because the URI it is not a valid file URL" + switch buildSystemType { + case .buildServer: + return await BuildServerBuildSystem(projectRoot: projectRoot) + case .compilationDatabase: + return CompilationDatabaseBuildSystem( + projectRoot: projectRoot, + searchPaths: (options.compilationDatabaseOrDefault.searchPaths ?? []).compactMap { + try? RelativePath(validating: $0) + } ) - return nil - } - func createSwiftPMBuildSystem(rootUri: DocumentURI) async -> SwiftPMBuildSystem? { + case .swiftPM: return await SwiftPMBuildSystem( - uri: rootUri, + projectRoot: projectRoot, toolchainRegistry: toolchainRegistry, options: options, reloadPackageStatusCallback: reloadPackageStatusCallback, testHooks: testHooks.swiftpmTestHooks ) } - - func createCompilationDatabaseBuildSystem(rootPath: AbsolutePath) -> CompilationDatabaseBuildSystem? { - return CompilationDatabaseBuildSystem( - projectRoot: rootPath, - searchPaths: (options.compilationDatabaseOrDefault.searchPaths ?? []).compactMap { - try? RelativePath(validating: $0) - } - ) - } - - func createBuildServerBuildSystem(rootPath: AbsolutePath) async -> BuildServerBuildSystem? { - return await BuildServerBuildSystem(projectRoot: rootPath) - } - - let defaultBuildSystem: BuiltInBuildSystem? = - switch options.defaultWorkspaceType { - case .buildServer: await createBuildServerBuildSystem(rootPath: rootPath) - case .compilationDatabase: createCompilationDatabaseBuildSystem(rootPath: rootPath) - case .swiftPM: await createSwiftPMBuildSystem(rootUri: rootUri) - case nil: nil - } - if let defaultBuildSystem { - return defaultBuildSystem - } else if let buildServer = await createBuildServerBuildSystem(rootPath: rootPath) { - return buildServer - } else if let swiftpm = await createSwiftPMBuildSystem(rootUri: rootUri) { - return swiftpm - } else if let compdb = createCompilationDatabaseBuildSystem(rootPath: rootPath) { - return compdb - } else { - logger.error("Could not set up a build system at '\(rootUri.forLogging)'") - return nil - } } diff --git a/Sources/SourceKitLSP/SourceKitLSPServer.swift b/Sources/SourceKitLSP/SourceKitLSPServer.swift index f13bff54..995e55d5 100644 --- a/Sources/SourceKitLSP/SourceKitLSPServer.swift +++ b/Sources/SourceKitLSP/SourceKitLSPServer.swift @@ -273,21 +273,29 @@ package actor SourceKitLSPServer { } let rootURLs = workspacesAndIsImplicit.filter { !$0.isImplicit }.compactMap { $0.workspace.rootUri?.fileURL } while url.pathComponents.count > 1 && rootURLs.contains(where: { $0.isPrefix(of: url) }) { + defer { + url.deleteLastPathComponent() + } // Ignore workspaces that have the same project root as an existing workspace. // This might happen if there is an existing SwiftPM workspace that hasn't been reloaded after a new file // 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 workspace = await self.createWorkspace(WorkspaceFolder(uri: DocumentURI(url))) { buildSystem in - guard let buildSystem else { - // If we didn't create a build system, `url` is not capable of handling the document. - return false - } - return !projectRoots.contains(await buildSystem.projectRoot) + let uri = DocumentURI(url) + guard let buildSystemKind = determineBuildSystem(forWorkspaceFolder: uri, options: self.options) else { + continue } - if let workspace { - return workspace + guard !projectRoots.contains(buildSystemKind.projectRoot) else { + continue } - url.deleteLastPathComponent() + guard + let workspace = await orLog( + "Creating workspace", + { try await createWorkspace(workspaceFolder: uri, buildSystemKind: buildSystemKind) } + ) + else { + continue + } + return workspace } return nil } @@ -933,35 +941,41 @@ extension SourceKitLSPServer { /// /// If the build system that was determined for the workspace does not satisfy `condition`, `nil` is returned. private func createWorkspace( - _ workspaceFolder: WorkspaceFolder, - condition: (BuiltInBuildSystem?) async -> Bool = { _ in true } - ) async -> Workspace? { + workspaceFolder: DocumentURI, + buildSystemKind: (type: WorkspaceType, projectRoot: AbsolutePath)? + ) async throws -> Workspace { guard let capabilityRegistry = capabilityRegistry else { + struct NoCapabilityRegistryError: Error {} logger.log("Cannot open workspace before server is initialized") - return nil + throw NoCapabilityRegistryError() } let testHooks = self.testHooks let options = SourceKitLSPOptions.merging( base: self.options, override: SourceKitLSPOptions( - path: workspaceFolder.uri.fileURL? + path: workspaceFolder.fileURL? .appendingPathComponent(".sourcekit-lsp") .appendingPathComponent("config.json") ) ) - logger.log("Creating workspace at \(workspaceFolder.uri.forLogging) with options: \(options.forLogging)") - let buildSystem = await createBuildSystem( - rootUri: workspaceFolder.uri, - options: options, - testHooks: testHooks, - toolchainRegistry: toolchainRegistry, - reloadPackageStatusCallback: { [weak self] status in - await self?.reloadPackageStatusCallback(status) - } - ) - guard await condition(buildSystem) else { - return nil + logger.log("Creating workspace at \(workspaceFolder.forLogging) with options: \(options.forLogging)") + + let buildSystem: BuiltInBuildSystem? + if let (buildSystemType, projectRoot) = buildSystemKind { + buildSystem = await createBuildSystem( + ofType: buildSystemType, + projectRoot: projectRoot, + options: options, + testHooks: testHooks, + toolchainRegistry: toolchainRegistry, + reloadPackageStatusCallback: { [weak self] status in + await self?.reloadPackageStatusCallback(status) + } + ) + } else { + buildSystem = nil } + await orLog("Initial build graph generation") { // Schedule an initial generation of the build graph. Once the build graph is loaded, the build system will send // call `fileHandlingCapabilityChanged`, which allows us to move documents to a workspace with this build @@ -977,12 +991,12 @@ extension SourceKitLSPServer { "" } logger.log( - "Created workspace at \(workspaceFolder.uri.forLogging) as \(buildSystemType, privacy: .public) with project root \(projectRoot ?? "")" + "Created workspace at \(workspaceFolder.forLogging) as \(buildSystemType, privacy: .public) with project root \(projectRoot ?? "")" ) - let workspace = try? await Workspace( + let workspace = try await Workspace( documentManager: self.documentManager, - rootUri: workspaceFolder.uri, + rootUri: workspaceFolder, capabilityRegistry: capabilityRegistry, buildSystem: buildSystem, toolchainRegistry: self.toolchainRegistry, @@ -999,7 +1013,7 @@ extension SourceKitLSPServer { self?.indexProgressManager.indexProgressStatusDidChange() } ) - if let workspace, options.backgroundIndexingOrDefault, workspace.semanticIndexManager == nil, + if options.backgroundIndexingOrDefault, workspace.semanticIndexManager == nil, !self.didSendBackgroundIndexingNotSupportedNotification { self.sendNotificationToClient( @@ -1072,20 +1086,34 @@ extension SourceKitLSPServer { await workspaceQueue.async { [testHooks] in if let workspaceFolders = req.workspaceFolders { - self.workspacesAndIsImplicit += await workspaceFolders.asyncCompactMap { - guard let workspace = await self.createWorkspace($0) else { - return nil + self.workspacesAndIsImplicit += await workspaceFolders.asyncCompactMap { workspaceFolder in + await orLog("Creating workspace from workspaceFolders") { + let workspace = try await self.createWorkspace( + workspaceFolder: workspaceFolder.uri, + buildSystemKind: determineBuildSystem(forWorkspaceFolder: workspaceFolder.uri, options: self.options) + ) + return (workspace: workspace, isImplicit: false) } - return (workspace: workspace, isImplicit: false) } } else if let uri = req.rootURI { - let workspaceFolder = WorkspaceFolder(uri: uri) - if let workspace = await self.createWorkspace(workspaceFolder) { + let workspace = await orLog("Creating workspace from rootURI") { + try await self.createWorkspace( + workspaceFolder: uri, + buildSystemKind: determineBuildSystem(forWorkspaceFolder: uri, options: self.options) + ) + } + if let workspace { self.workspacesAndIsImplicit.append((workspace: workspace, isImplicit: false)) } } else if let path = req.rootPath { - let workspaceFolder = WorkspaceFolder(uri: DocumentURI(URL(fileURLWithPath: path))) - if let workspace = await self.createWorkspace(workspaceFolder) { + let uri = DocumentURI(URL(fileURLWithPath: path)) + let workspace = await orLog("Creating workspace from rootPath") { + try await self.createWorkspace( + workspaceFolder: uri, + buildSystemKind: determineBuildSystem(forWorkspaceFolder: uri, options: self.options) + ) + } + if let workspace { self.workspacesAndIsImplicit.append((workspace: workspace, isImplicit: false)) } } @@ -1535,7 +1563,14 @@ extension SourceKitLSPServer { } } if let added = notification.event.added { - let newWorkspaces = await added.asyncCompactMap { await self.createWorkspace($0) } + let newWorkspaces = await added.asyncCompactMap { workspaceFolder in + await orLog("Creating workspace after workspace folder change") { + try await self.createWorkspace( + workspaceFolder: workspaceFolder.uri, + buildSystemKind: determineBuildSystem(forWorkspaceFolder: workspaceFolder.uri, options: self.options) + ) + } + } for workspace in newWorkspaces { await workspace.buildSystemManager.setDelegate(self) } diff --git a/Tests/BuildSystemIntegrationTests/BuildSystemManagerTests.swift b/Tests/BuildSystemIntegrationTests/BuildSystemManagerTests.swift index 93152a25..548dd81a 100644 --- a/Tests/BuildSystemIntegrationTests/BuildSystemManagerTests.swift +++ b/Tests/BuildSystemIntegrationTests/BuildSystemManagerTests.swift @@ -447,6 +447,13 @@ private final actor ManualMainFilesProvider: MainFilesProvider { /// A simple `BuildSystem` that wraps a dictionary, for testing. @MainActor class ManualBuildSystem: BuiltInBuildSystem { + static nonisolated func projectRoot( + for workspaceFolder: AbsolutePath, + options: SourceKitLSPOptions + ) -> AbsolutePath? { + return workspaceFolder + } + var projectRoot = try! AbsolutePath(validating: "/") var map: [DocumentURI: FileBuildSettings] = [:] diff --git a/Tests/BuildSystemIntegrationTests/SwiftPMBuildSystemTests.swift b/Tests/BuildSystemIntegrationTests/SwiftPMBuildSystemTests.swift index fa683945..39d25f30 100644 --- a/Tests/BuildSystemIntegrationTests/SwiftPMBuildSystemTests.swift +++ b/Tests/BuildSystemIntegrationTests/SwiftPMBuildSystemTests.swift @@ -47,16 +47,7 @@ final class SwiftPMBuildSystemTests: XCTestCase { ] ) let packageRoot = tempDir.appending(component: "pkg") - let tr = ToolchainRegistry.forTesting - await assertThrowsError( - try await SwiftPMBuildSystem( - workspacePath: packageRoot, - toolchainRegistry: tr, - fileSystem: fs, - options: SourceKitLSPOptions(), - testHooks: SwiftPMTestHooks() - ) - ) + XCTAssertNil(SwiftPMBuildSystem.projectRoot(for: packageRoot, options: .testDefault())) } } @@ -77,7 +68,7 @@ final class SwiftPMBuildSystemTests: XCTestCase { let packageRoot = tempDir.appending(component: "pkg") let tr = ToolchainRegistry.forTesting let buildSystem = try await SwiftPMBuildSystem( - workspacePath: packageRoot, + projectRoot: packageRoot, toolchainRegistry: tr, fileSystem: fs, options: SourceKitLSPOptions(), @@ -107,7 +98,7 @@ final class SwiftPMBuildSystemTests: XCTestCase { let packageRoot = tempDir.appending(component: "pkg") await assertThrowsError( try await SwiftPMBuildSystem( - workspacePath: packageRoot, + projectRoot: packageRoot, toolchainRegistry: ToolchainRegistry(toolchains: []), fileSystem: fs, options: SourceKitLSPOptions(), @@ -138,7 +129,7 @@ final class SwiftPMBuildSystemTests: XCTestCase { let packageRoot = try resolveSymlinks(tempDir.appending(component: "pkg")) let tr = ToolchainRegistry.forTesting let swiftpmBuildSystem = try await SwiftPMBuildSystem( - workspacePath: packageRoot, + projectRoot: packageRoot, toolchainRegistry: tr, fileSystem: fs, options: SourceKitLSPOptions(), @@ -203,7 +194,7 @@ final class SwiftPMBuildSystemTests: XCTestCase { let packageRoot = try resolveSymlinks(tempDir.appending(component: "pkg")) let tr = ToolchainRegistry.forTesting let swiftpmBuildSystem = try await SwiftPMBuildSystem( - workspacePath: packageRoot, + projectRoot: packageRoot, toolchainRegistry: tr, fileSystem: localFileSystem, options: SourceKitLSPOptions(), @@ -266,7 +257,7 @@ final class SwiftPMBuildSystemTests: XCTestCase { ) let swiftpmBuildSystem = try await SwiftPMBuildSystem( - workspacePath: packageRoot, + projectRoot: packageRoot, toolchainRegistry: tr, fileSystem: fs, options: SourceKitLSPOptions(swiftPM: options), @@ -313,7 +304,7 @@ final class SwiftPMBuildSystemTests: XCTestCase { ) let swiftpmBuildSystem = try await SwiftPMBuildSystem( - workspacePath: tempDir.appending(component: "pkg"), + projectRoot: tempDir.appending(component: "pkg"), toolchainRegistry: tr, fileSystem: fs, options: SourceKitLSPOptions(swiftPM: options), @@ -348,7 +339,7 @@ final class SwiftPMBuildSystemTests: XCTestCase { let packageRoot = tempDir.appending(component: "pkg") let tr = ToolchainRegistry.forTesting let swiftpmBuildSystem = try await SwiftPMBuildSystem( - workspacePath: packageRoot, + projectRoot: packageRoot, toolchainRegistry: tr, fileSystem: fs, options: SourceKitLSPOptions(), @@ -385,7 +376,7 @@ final class SwiftPMBuildSystemTests: XCTestCase { let packageRoot = try resolveSymlinks(tempDir.appending(component: "pkg")) let tr = ToolchainRegistry.forTesting let swiftpmBuildSystem = try await SwiftPMBuildSystem( - workspacePath: packageRoot, + projectRoot: packageRoot, toolchainRegistry: tr, fileSystem: fs, options: SourceKitLSPOptions(), @@ -434,7 +425,7 @@ final class SwiftPMBuildSystemTests: XCTestCase { let packageRoot = try resolveSymlinks(tempDir.appending(component: "pkg")) let tr = ToolchainRegistry.forTesting let swiftpmBuildSystem = try await SwiftPMBuildSystem( - workspacePath: packageRoot, + projectRoot: packageRoot, toolchainRegistry: tr, fileSystem: fs, options: SourceKitLSPOptions(), @@ -489,7 +480,7 @@ final class SwiftPMBuildSystemTests: XCTestCase { let packageRoot = tempDir.appending(component: "pkg") let tr = ToolchainRegistry.forTesting let swiftpmBuildSystem = try await SwiftPMBuildSystem( - workspacePath: packageRoot, + projectRoot: packageRoot, toolchainRegistry: tr, fileSystem: fs, options: SourceKitLSPOptions(), @@ -533,7 +524,7 @@ final class SwiftPMBuildSystemTests: XCTestCase { let packageRoot = try resolveSymlinks(tempDir.appending(component: "pkg")) let tr = ToolchainRegistry.forTesting let swiftpmBuildSystem = try await SwiftPMBuildSystem( - workspacePath: packageRoot, + projectRoot: packageRoot, toolchainRegistry: tr, fileSystem: fs, options: SourceKitLSPOptions(), @@ -614,7 +605,7 @@ final class SwiftPMBuildSystemTests: XCTestCase { ) let packageRoot = tempDir.appending(component: "pkg") let swiftpmBuildSystem = try await SwiftPMBuildSystem( - workspacePath: packageRoot, + projectRoot: packageRoot, toolchainRegistry: ToolchainRegistry.forTesting, fileSystem: fs, options: SourceKitLSPOptions(), @@ -666,7 +657,7 @@ final class SwiftPMBuildSystemTests: XCTestCase { let tr = ToolchainRegistry.forTesting let swiftpmBuildSystem = try await SwiftPMBuildSystem( - workspacePath: packageRoot, + projectRoot: XCTUnwrap(SwiftPMBuildSystem.projectRoot(for: packageRoot, options: .testDefault())), toolchainRegistry: tr, fileSystem: fs, options: SourceKitLSPOptions(), @@ -734,7 +725,7 @@ final class SwiftPMBuildSystemTests: XCTestCase { ) let swiftpmBuildSystem = try await SwiftPMBuildSystem( - workspacePath: symlinkRoot, + projectRoot: XCTUnwrap(SwiftPMBuildSystem.projectRoot(for: symlinkRoot, options: .testDefault())), toolchainRegistry: ToolchainRegistry.forTesting, fileSystem: fs, options: SourceKitLSPOptions(), @@ -774,7 +765,7 @@ final class SwiftPMBuildSystemTests: XCTestCase { let packageRoot = try resolveSymlinks(tempDir.appending(component: "pkg")) let tr = ToolchainRegistry.forTesting let swiftpmBuildSystem = try await SwiftPMBuildSystem( - workspacePath: packageRoot, + projectRoot: packageRoot, toolchainRegistry: tr, fileSystem: fs, options: SourceKitLSPOptions(), @@ -796,9 +787,8 @@ final class SwiftPMBuildSystemTests: XCTestCase { } func testNestedInvalidPackageSwift() async throws { - let fs = InMemoryFileSystem() try await withTestScratchDir { tempDir in - try fs.createFiles( + try localFileSystem.createFiles( root: tempDir, files: [ "pkg/Sources/lib/Package.swift": "// not a valid package", @@ -812,17 +802,10 @@ final class SwiftPMBuildSystemTests: XCTestCase { """, ] ) - let packageRoot = try resolveSymlinks(tempDir.appending(components: "pkg", "Sources", "lib")) - let tr = ToolchainRegistry.forTesting - let swiftpmBuildSystem = try await SwiftPMBuildSystem( - workspacePath: packageRoot, - toolchainRegistry: tr, - fileSystem: fs, - options: SourceKitLSPOptions(), - testHooks: SwiftPMTestHooks() - ) + let workspaceRoot = try resolveSymlinks(tempDir.appending(components: "pkg", "Sources", "lib")) + let projectRoot = SwiftPMBuildSystem.projectRoot(for: workspaceRoot, options: .testDefault()) - assertEqual(await swiftpmBuildSystem.projectRoot, try resolveSymlinks(tempDir.appending(component: "pkg"))) + assertEqual(projectRoot, try resolveSymlinks(tempDir.appending(component: "pkg"))) } } @@ -850,7 +833,7 @@ final class SwiftPMBuildSystemTests: XCTestCase { let packageRoot = tempDir.appending(component: "pkg") let tr = ToolchainRegistry.forTesting let swiftpmBuildSystem = try await SwiftPMBuildSystem( - workspacePath: packageRoot, + projectRoot: packageRoot, toolchainRegistry: tr, fileSystem: fs, options: SourceKitLSPOptions(), diff --git a/Tests/SourceKitLSPTests/BuildSystemTests.swift b/Tests/SourceKitLSPTests/BuildSystemTests.swift index a3e818e6..1164fb4a 100644 --- a/Tests/SourceKitLSPTests/BuildSystemTests.swift +++ b/Tests/SourceKitLSPTests/BuildSystemTests.swift @@ -24,6 +24,10 @@ import XCTest /// Build system to be used for testing BuildSystem and BuildSystemDelegate functionality with SourceKitLSPServer /// and other components. actor TestBuildSystem: BuiltInBuildSystem { + static func projectRoot(for workspaceFolder: AbsolutePath, options: SourceKitLSPOptions) -> AbsolutePath? { + return workspaceFolder + } + let projectRoot: AbsolutePath = try! AbsolutePath(validating: "/") let indexStorePath: AbsolutePath? = nil let indexDatabasePath: AbsolutePath? = nil