File: FixedCompilationDatabaseBuildSystem.swift

package info (click to toggle)
swiftlang 6.2.3-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 2,856,264 kB
  • sloc: cpp: 9,995,718; ansic: 2,234,019; asm: 1,092,167; python: 313,940; objc: 82,726; f90: 80,126; lisp: 38,373; pascal: 25,580; sh: 20,378; ml: 5,058; perl: 4,751; makefile: 4,725; awk: 3,535; javascript: 3,018; xml: 918; fortran: 664; cs: 573; ruby: 396
file content (139 lines) | stat: -rw-r--r-- 5,430 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//

package import BuildServerProtocol
package import Foundation
package import LanguageServerProtocol
import SKLogging
import SwiftExtensions

func lastIndexStorePathArgument(in compilerArgs: [String]) -> String? {
  if let indexStorePathIndex = compilerArgs.lastIndex(of: "-index-store-path"),
    indexStorePathIndex + 1 < compilerArgs.count
  {
    return compilerArgs[indexStorePathIndex + 1]
  }
  return nil
}

/// A `BuildSystem` that provides compiler arguments from a `compile_flags.txt` file.
package actor FixedCompilationDatabaseBuildSystem: BuiltInBuildSystem {
  package static let dbName = "compile_flags.txt"

  private let connectionToSourceKitLSP: any Connection

  package let configPath: URL

  /// The compiler arguments from the fixed compilation database.
  ///
  /// Note that this does not contain the path to a compiler.
  var compilerArgs: [String]

  // Watch for all all changes to `compile_flags.txt` and `compile_flags.txt` instead of just the one at
  // `configPath` so that we cover the following semi-common scenario:
  // The user has a build that stores `compile_flags.txt` in `mybuild`. In order to pick it  up, they create a
  // symlink from `<project root>/compile_flags.txt` to `mybuild/compile_flags.txt`.  We want to get notified
  // about the change to `mybuild/compile_flags.txt` because it effectively changes the contents of
  // `<project root>/compile_flags.txt`.
  package let fileWatchers: [FileSystemWatcher] = [
    FileSystemWatcher(globPattern: "**/compile_flags.txt", kind: [.create, .change, .delete])
  ]

  package var indexStorePath: URL? {
    guard let indexStorePath = lastIndexStorePathArgument(in: compilerArgs) else {
      return nil
    }
    return URL(fileURLWithPath: indexStorePath, relativeTo: configPath.deletingLastPathComponent())
  }

  package var indexDatabasePath: URL? {
    indexStorePath?.deletingLastPathComponent().appendingPathComponent("IndexDatabase")
  }

  package nonisolated var supportsPreparationAndOutputPaths: Bool { false }

  private static func parseCompileFlags(at configPath: URL) throws -> [String] {
    let fileContents: String = try String(contentsOf: configPath, encoding: .utf8)

    var compilerArgs: [String] = []
    fileContents.enumerateLines { line, _ in
      compilerArgs.append(line.trimmingCharacters(in: .whitespacesAndNewlines))
    }
    return compilerArgs
  }

  package init(
    configPath: URL,
    connectionToSourceKitLSP: any Connection
  ) throws {
    self.connectionToSourceKitLSP = connectionToSourceKitLSP
    self.configPath = configPath
    self.compilerArgs = try Self.parseCompileFlags(at: configPath)
  }

  package func buildTargets(request: WorkspaceBuildTargetsRequest) async throws -> WorkspaceBuildTargetsResponse {
    return WorkspaceBuildTargetsResponse(targets: [
      BuildTarget(
        id: .dummy,
        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: [])
    }
    return BuildTargetSourcesResponse(items: [
      SourcesItem(
        target: .dummy,
        sources: [SourceItem(uri: URI(configPath.deletingLastPathComponent()), kind: .directory, generated: false)]
      )
    ])
  }

  package func didChangeWatchedFiles(notification: OnWatchedFilesDidChangeNotification) {
    if notification.changes.contains(where: { $0.uri.fileURL?.lastPathComponent == Self.dbName }) {
      self.reloadCompilationDatabase()
    }
  }

  package func prepare(request: BuildTargetPrepareRequest) async throws -> VoidResponse {
    throw ResponseError.methodNotFound(BuildTargetPrepareRequest.method)
  }

  package func sourceKitOptions(
    request: TextDocumentSourceKitOptionsRequest
  ) async throws -> TextDocumentSourceKitOptionsResponse? {
    return TextDocumentSourceKitOptionsResponse(
      compilerArguments: compilerArgs + [request.textDocument.uri.pseudoPath],
      workingDirectory: try? configPath.deletingLastPathComponent().filePath
    )
  }

  package func waitForBuildSystemUpdates(request: WorkspaceWaitForBuildSystemUpdatesRequest) async -> VoidResponse {
    return VoidResponse()
  }

  /// The compilation database has been changed on disk.
  /// Reload it and notify the delegate about build setting changes.
  private func reloadCompilationDatabase() {
    orLog("Reloading fixed compilation database") {
      self.compilerArgs = try Self.parseCompileFlags(at: configPath)
      connectionToSourceKitLSP.send(OnBuildTargetDidChangeNotification(changes: nil))
    }
  }
}