mirror of
https://github.com/apple/sourcekit-lsp.git
synced 2026-03-02 18:23:24 +01:00
The implementation of which file’s dependencies have been updated is common across all build systems and thus build systems shouldn’t need to implement this logic. This also allows us to remove `BuildSystemDelegate`.
226 lines
7.3 KiB
Swift
226 lines
7.3 KiB
Swift
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See https://swift.org/LICENSE.txt for license information
|
|
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
import BuildServerProtocol
|
|
import Dispatch
|
|
import LanguageServerProtocol
|
|
import SKLogging
|
|
import SKOptions
|
|
import SKSupport
|
|
import ToolchainRegistry
|
|
|
|
import struct Foundation.URL
|
|
import struct TSCBasic.AbsolutePath
|
|
import protocol TSCBasic.FileSystem
|
|
import struct TSCBasic.RelativePath
|
|
import var TSCBasic.localFileSystem
|
|
|
|
/// A `BuildSystem` based on loading clang-compatible compilation database(s).
|
|
///
|
|
/// Provides build settings from a `CompilationDatabase` found by searching a project. For now, only
|
|
/// one compilation database, located at the project root.
|
|
package actor CompilationDatabaseBuildSystem {
|
|
/// The compilation database.
|
|
var compdb: CompilationDatabase? = nil {
|
|
didSet {
|
|
// Build settings have changed and thus the index store path might have changed.
|
|
// Recompute it on demand.
|
|
_indexStorePath = nil
|
|
}
|
|
}
|
|
|
|
/// Callbacks that should be called if the list of possible test files has changed.
|
|
package var testFilesDidChangeCallbacks: [() async -> Void] = []
|
|
|
|
package weak var messageHandler: BuiltInBuildSystemMessageHandler?
|
|
|
|
package let projectRoot: AbsolutePath
|
|
|
|
let searchPaths: [RelativePath]
|
|
|
|
let fileSystem: FileSystem
|
|
|
|
private var _indexStorePath: AbsolutePath?
|
|
package var indexStorePath: AbsolutePath? {
|
|
if let indexStorePath = _indexStorePath {
|
|
return indexStorePath
|
|
}
|
|
|
|
if let allCommands = self.compdb?.allCommands {
|
|
for command in allCommands {
|
|
let args = command.commandLine
|
|
for i in args.indices.reversed() {
|
|
if args[i] == "-index-store-path" && i != args.endIndex - 1 {
|
|
_indexStorePath = try? AbsolutePath(validating: args[i + 1])
|
|
return _indexStorePath
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
package init?(
|
|
projectRoot: AbsolutePath,
|
|
searchPaths: [RelativePath],
|
|
messageHandler: (any BuiltInBuildSystemMessageHandler)?,
|
|
fileSystem: FileSystem = localFileSystem
|
|
) {
|
|
self.fileSystem = fileSystem
|
|
self.projectRoot = projectRoot
|
|
self.searchPaths = searchPaths
|
|
self.messageHandler = messageHandler
|
|
if let compdb = tryLoadCompilationDatabase(directory: projectRoot, additionalSearchPaths: searchPaths, fileSystem) {
|
|
self.compdb = compdb
|
|
} else {
|
|
return nil
|
|
}
|
|
}
|
|
}
|
|
|
|
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? {
|
|
indexStorePath?.parentDirectory.appending(component: "IndexDatabase")
|
|
}
|
|
|
|
package func buildTargets(request: BuildTargetsRequest) async throws -> BuildTargetsResponse {
|
|
return BuildTargetsResponse(targets: [
|
|
BuildTarget(
|
|
id: .dummy,
|
|
displayName: nil,
|
|
baseDirectory: nil,
|
|
tags: [.test],
|
|
capabilities: BuildTargetCapabilities(),
|
|
// Be conservative with the languages that might be used in the target. SourceKit-LSP doesn't use this property.
|
|
languageIds: [.c, .cpp, .objective_c, .objective_cpp, .swift],
|
|
dependencies: []
|
|
)
|
|
])
|
|
}
|
|
|
|
package func buildTargetSources(request: BuildTargetSourcesRequest) async throws -> BuildTargetSourcesResponse {
|
|
guard request.targets.contains(.dummy) else {
|
|
return BuildTargetSourcesResponse(items: [])
|
|
}
|
|
guard let compdb else {
|
|
return BuildTargetSourcesResponse(items: [])
|
|
}
|
|
let sources = compdb.allCommands.map {
|
|
SourceItem(uri: $0.uri, kind: .file, generated: false)
|
|
}
|
|
return BuildTargetSourcesResponse(items: [SourcesItem(target: .dummy, sources: sources)])
|
|
}
|
|
|
|
package func didChangeWatchedFiles(notification: BuildServerProtocol.DidChangeWatchedFilesNotification) async {
|
|
if notification.changes.contains(where: { self.fileEventShouldTriggerCompilationDatabaseReload(event: $0) }) {
|
|
await self.reloadCompilationDatabase()
|
|
}
|
|
}
|
|
|
|
package func inverseSources(request: InverseSourcesRequest) -> InverseSourcesResponse {
|
|
return InverseSourcesResponse(targets: [BuildTargetIdentifier.dummy])
|
|
}
|
|
|
|
package func prepare(request: PrepareTargetsRequest) async throws -> VoidResponse {
|
|
throw PrepareNotSupportedError()
|
|
}
|
|
|
|
package func sourceKitOptions(request: SourceKitOptionsRequest) async throws -> SourceKitOptionsResponse? {
|
|
guard let db = database(for: request.textDocument.uri), let cmd = db[request.textDocument.uri].first else {
|
|
return nil
|
|
}
|
|
return SourceKitOptionsResponse(
|
|
compilerArguments: Array(cmd.commandLine.dropFirst()),
|
|
workingDirectory: cmd.directory
|
|
)
|
|
}
|
|
|
|
package func toolchain(for uri: DocumentURI, _ language: Language) async -> Toolchain? {
|
|
return nil
|
|
}
|
|
|
|
package func scheduleBuildGraphGeneration() {}
|
|
|
|
package func waitForUpToDateBuildGraph() async {}
|
|
|
|
package func topologicalSort(of targets: [BuildTargetIdentifier]) -> [BuildTargetIdentifier]? {
|
|
return nil
|
|
}
|
|
|
|
package func targets(dependingOn targets: [BuildTargetIdentifier]) -> [BuildTargetIdentifier]? {
|
|
return nil
|
|
}
|
|
|
|
private func database(for uri: DocumentURI) -> CompilationDatabase? {
|
|
if let url = uri.fileURL, let path = try? AbsolutePath(validating: url.path) {
|
|
return database(for: path)
|
|
}
|
|
return compdb
|
|
}
|
|
|
|
private func database(for path: AbsolutePath) -> CompilationDatabase? {
|
|
if compdb == nil {
|
|
var dir = path
|
|
while !dir.isRoot {
|
|
dir = dir.parentDirectory
|
|
if let db = tryLoadCompilationDatabase(directory: dir, additionalSearchPaths: searchPaths, fileSystem) {
|
|
compdb = db
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
if compdb == nil {
|
|
logger.error("Could not open compilation database for \(path)")
|
|
}
|
|
|
|
return compdb
|
|
}
|
|
|
|
private func fileEventShouldTriggerCompilationDatabaseReload(event: FileEvent) -> Bool {
|
|
switch event.uri.fileURL?.lastPathComponent {
|
|
case "compile_commands.json", "compile_flags.txt":
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
/// The compilation database has been changed on disk.
|
|
/// Reload it and notify the delegate about build setting changes.
|
|
private func reloadCompilationDatabase() async {
|
|
self.compdb = tryLoadCompilationDatabase(
|
|
directory: projectRoot,
|
|
additionalSearchPaths: searchPaths,
|
|
self.fileSystem
|
|
)
|
|
|
|
await messageHandler?.sendNotificationToSourceKitLSP(DidChangeBuildTargetNotification(changes: nil))
|
|
for testFilesDidChangeCallback in testFilesDidChangeCallbacks {
|
|
await testFilesDidChangeCallback()
|
|
}
|
|
}
|
|
|
|
package func addSourceFilesDidChangeCallback(_ callback: @escaping () async -> Void) async {
|
|
testFilesDidChangeCallbacks.append(callback)
|
|
}
|
|
}
|