Skip to content

Commit 15b9670

Browse files
committed
Split build systems for JSON compilation database and fixed compilation database
I feel like the implementations are actually simpler if we split them. This will also allow us to add more advanced logic to the JSON compilation database build system in the future, such as inferring the toolchain from the compile command.
1 parent bb699c9 commit 15b9670

15 files changed

+332
-270
lines changed

Sources/BuildSystemIntegration/BuildSystemManager.swift

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -195,12 +195,22 @@ private extension BuildSystemSpec {
195195
}
196196
logger.log("Created external build server at \(projectRoot)")
197197
return .external(buildSystem)
198-
case .compilationDatabase:
198+
case .jsonCompilationDatabase:
199199
return await createBuiltInBuildSystemAdapter(
200200
messagesToSourceKitLSPHandler: messagesToSourceKitLSPHandler,
201201
buildSystemHooks: buildSystemHooks
202202
) { connectionToSourceKitLSP in
203-
try CompilationDatabaseBuildSystem(
203+
try JSONCompilationDatabaseBuildSystem(
204+
configPath: configPath,
205+
connectionToSourceKitLSP: connectionToSourceKitLSP
206+
)
207+
}
208+
case .fixedCompilationDatabase:
209+
return await createBuiltInBuildSystemAdapter(
210+
messagesToSourceKitLSPHandler: messagesToSourceKitLSPHandler,
211+
buildSystemHooks: buildSystemHooks
212+
) { connectionToSourceKitLSP in
213+
try FixedCompilationDatabaseBuildSystem(
204214
configPath: configPath,
205215
connectionToSourceKitLSP: connectionToSourceKitLSP
206216
)

Sources/BuildSystemIntegration/BuiltInBuildSystemAdapter.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ import Foundation
2828
package struct BuildSystemSpec {
2929
package enum Kind {
3030
case buildServer
31-
case compilationDatabase
31+
case jsonCompilationDatabase
32+
case fixedCompilationDatabase
3233
case swiftPM
3334
case injected(BuildSystemInjector)
3435
}

Sources/BuildSystemIntegration/CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,12 @@ add_library(BuildSystemIntegration STATIC
99
BuiltInBuildSystem.swift
1010
BuiltInBuildSystemAdapter.swift
1111
CompilationDatabase.swift
12-
CompilationDatabaseBuildSystem.swift
1312
DetermineBuildSystem.swift
1413
ExternalBuildSystemAdapter.swift
1514
FallbackBuildSettings.swift
1615
FileBuildSettings.swift
16+
FixedCompilationDatabaseBuildSystem.swift
17+
JSONCompilationDatabaseBuildSystem.swift
1718
LegacyBuildServerBuildSystem.swift
1819
MainFilesProvider.swift
1920
PathPrefixMapping.swift

Sources/BuildSystemIntegration/CompilationDatabase.swift

Lines changed: 7 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ import WinSDK
3636
///
3737
/// See https://clang.llvm.org/docs/JSONCompilationDatabase.html
3838
package struct CompilationDatabaseCompileCommand: Equatable, Codable {
39-
4039
/// The working directory for the compilation.
4140
package var directory: String
4241

@@ -103,84 +102,6 @@ package struct CompilationDatabaseCompileCommand: Equatable, Codable {
103102
}
104103
}
105104

106-
/// A clang-compatible compilation database.
107-
///
108-
/// See https://clang.llvm.org/docs/JSONCompilationDatabase.html
109-
package protocol CompilationDatabase {
110-
typealias Command = CompilationDatabaseCompileCommand
111-
subscript(_ uri: DocumentURI) -> [Command] { get }
112-
var sourceItems: [SourceItem] { get }
113-
}
114-
115-
/// Loads a compilation database from `file`.
116-
package func tryLoadCompilationDatabase(
117-
file: URL
118-
) -> CompilationDatabase? {
119-
orLog("Failed to load compilation database") { () -> CompilationDatabase? in
120-
switch file.lastPathComponent {
121-
case JSONCompilationDatabase.dbName:
122-
return try JSONCompilationDatabase(file: file)
123-
case FixedCompilationDatabase.dbName:
124-
return try FixedCompilationDatabase(file: file)
125-
default:
126-
return nil
127-
}
128-
}
129-
}
130-
131-
/// Fixed clang-compatible compilation database (compile_flags.txt).
132-
///
133-
/// Each line in the file becomes a command line argument. Example:
134-
/// ```
135-
/// -xc++
136-
/// -I
137-
/// libwidget/include/
138-
/// ```
139-
///
140-
/// See https://clang.llvm.org/docs/JSONCompilationDatabase.html under Alternatives
141-
package struct FixedCompilationDatabase: CompilationDatabase, Equatable {
142-
package static let dbName: String = "compile_flags.txt"
143-
144-
private let fixedArgs: [String]
145-
private let directory: String
146-
147-
package subscript(path: DocumentURI) -> [CompilationDatabaseCompileCommand] {
148-
[Command(directory: directory, filename: path.pseudoPath, commandLine: fixedArgs + [path.pseudoPath])]
149-
}
150-
151-
package var sourceItems: [SourceItem] {
152-
return [
153-
SourceItem(uri: URI(filePath: directory, isDirectory: true), kind: .directory, generated: false)
154-
]
155-
}
156-
157-
/// Loads the compilation database located in `directory`, if any.
158-
/// - Returns: `nil` if `compile_flags.txt` was not found
159-
package init?(directory: URL) throws {
160-
let path = directory.appendingPathComponent(Self.dbName)
161-
try self.init(file: path)
162-
}
163-
164-
/// Loads the compilation database from `file`
165-
/// - Returns: `nil` if the file does not exist
166-
package init?(file: URL) throws {
167-
self.directory = try file.deletingLastPathComponent().filePath
168-
169-
let fileContents: String
170-
do {
171-
fileContents = try String(contentsOf: file, encoding: .utf8)
172-
} catch {
173-
return nil
174-
}
175-
176-
var fixedArgs: [String] = ["clang"]
177-
fileContents.enumerateLines { line, _ in
178-
fixedArgs.append(line.trimmingCharacters(in: .whitespacesAndNewlines))
179-
}
180-
self.fixedArgs = fixedArgs
181-
}
182-
}
183-
184105
/// The JSON clang-compatible compilation database.
185106
///
186107
/// Example:
@@ -196,11 +117,9 @@ package struct FixedCompilationDatabase: CompilationDatabase, Equatable {
196117
/// ```
197118
///
198119
/// See https://clang.llvm.org/docs/JSONCompilationDatabase.html
199-
package struct JSONCompilationDatabase: CompilationDatabase, Equatable, Codable {
200-
package static let dbName: String = "compile_commands.json"
201-
120+
package struct JSONCompilationDatabase: Equatable, Codable {
202121
private var pathToCommands: [DocumentURI: [Int]] = [:]
203-
private var commands: [CompilationDatabaseCompileCommand] = []
122+
var commands: [CompilationDatabaseCompileCommand] = []
204123

205124
package init(_ commands: [CompilationDatabaseCompileCommand] = []) {
206125
for command in commands {
@@ -211,27 +130,22 @@ package struct JSONCompilationDatabase: CompilationDatabase, Equatable, Codable
211130
package init(from decoder: Decoder) throws {
212131
var container = try decoder.unkeyedContainer()
213132
while !container.isAtEnd {
214-
self.add(try container.decode(Command.self))
133+
self.add(try container.decode(CompilationDatabaseCompileCommand.self))
215134
}
216135
}
217136

218137
/// Loads the compilation database located in `directory`, if any.
219138
///
220139
/// - Returns: `nil` if `compile_commands.json` was not found
221-
package init?(directory: URL) throws {
222-
let path = directory.appendingPathComponent(Self.dbName)
140+
package init(directory: URL) throws {
141+
let path = directory.appendingPathComponent(JSONCompilationDatabaseBuildSystem.dbName)
223142
try self.init(file: path)
224143
}
225144

226145
/// Loads the compilation database from `file`
227146
/// - Returns: `nil` if the file does not exist
228-
package init?(file: URL) throws {
229-
let data: Data
230-
do {
231-
data = try Data(contentsOf: file)
232-
} catch {
233-
return nil
234-
}
147+
package init(file: URL) throws {
148+
let data = try Data(contentsOf: file)
235149
self = try JSONDecoder().decode(JSONCompilationDatabase.self, from: data)
236150
}
237151

Sources/BuildSystemIntegration/DetermineBuildSystem.swift

Lines changed: 41 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,25 +11,56 @@
1111
//===----------------------------------------------------------------------===//
1212

1313
import Foundation
14-
15-
#if compiler(>=6)
16-
package import LanguageServerProtocol
1714
import SKLogging
18-
package import SKOptions
1915
import SwiftExtensions
16+
import TSCExtensions
2017
import ToolchainRegistry
2118

2219
import struct TSCBasic.AbsolutePath
20+
import struct TSCBasic.RelativePath
21+
22+
#if compiler(>=6)
23+
package import LanguageServerProtocol
24+
package import SKOptions
2325
#else
2426
import LanguageServerProtocol
25-
import SKLogging
2627
import SKOptions
27-
import SwiftExtensions
28-
import ToolchainRegistry
29-
30-
import struct TSCBasic.AbsolutePath
3128
#endif
3229

30+
private func searchForCompilationDatabaseConfig(
31+
in workspaceFolder: URL,
32+
options: SourceKitLSPOptions
33+
) -> BuildSystemSpec? {
34+
let searchPaths =
35+
(options.compilationDatabaseOrDefault.searchPaths ?? []).compactMap {
36+
try? RelativePath(validating: $0)
37+
} + [
38+
// These default search paths match the behavior of `clangd`
39+
try! RelativePath(validating: "."),
40+
try! RelativePath(validating: "build"),
41+
]
42+
43+
return
44+
searchPaths
45+
.lazy
46+
.compactMap { searchPath in
47+
let path = workspaceFolder.appending(searchPath)
48+
49+
let jsonPath = path.appendingPathComponent(JSONCompilationDatabaseBuildSystem.dbName)
50+
if FileManager.default.isFile(at: jsonPath) {
51+
return BuildSystemSpec(kind: .jsonCompilationDatabase, projectRoot: workspaceFolder, configPath: jsonPath)
52+
}
53+
54+
let fixedPath = path.appendingPathComponent(FixedCompilationDatabaseBuildSystem.dbName)
55+
if FileManager.default.isFile(at: fixedPath) {
56+
return BuildSystemSpec(kind: .fixedCompilationDatabase, projectRoot: workspaceFolder, configPath: fixedPath)
57+
}
58+
59+
return nil
60+
}
61+
.first
62+
}
63+
3364
/// Determine which build system should be started to handle the given workspace folder and at which folder that build
3465
/// system's project root is (see `BuiltInBuildSystem.projectRoot(for:options:)`). `onlyConsiderRoot` controls whether
3566
/// paths outside the root should be considered (eg. configuration files in the user's home directory).
@@ -66,7 +97,7 @@ package func determineBuildSystem(
6697
options: options
6798
)
6899
case .compilationDatabase:
69-
spec = CompilationDatabaseBuildSystem.searchForConfig(in: workspaceFolderUrl, options: options)
100+
spec = searchForCompilationDatabaseConfig(in: workspaceFolderUrl, options: options)
70101
case .swiftPM:
71102
#if canImport(PackageModel)
72103
spec = SwiftPMBuildSystem.searchForConfig(in: workspaceFolderUrl, options: options)

0 commit comments

Comments
 (0)