Skip to content

Commit 9473aeb

Browse files
authored
Merge pull request #1653 from ahoppen/build-system-creation
Create `BuiltInBuildSystem` in `BuildSystemAdapter`
2 parents 851feba + ea21175 commit 9473aeb

16 files changed

+424
-325
lines changed

Sources/BuildSystemIntegration/BuildServerBuildSystem.swift

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import Foundation
1515
import LanguageServerProtocol
1616
import LanguageServerProtocolJSONRPC
1717
import SKLogging
18+
import SKOptions
1819
import SKSupport
1920
import SwiftExtensions
2021
import ToolchainRegistry
@@ -76,15 +77,12 @@ package actor BuildServerBuildSystem: MessageHandler {
7677

7778
package weak var messageHandler: BuiltInBuildSystemMessageHandler?
7879

79-
package func setMessageHandler(_ messageHandler: any BuiltInBuildSystemMessageHandler) {
80-
self.messageHandler = messageHandler
81-
}
82-
8380
/// The build settings that have been received from the build server.
8481
private var buildSettings: [DocumentURI: FileBuildSettings] = [:]
8582

8683
package init(
8784
projectRoot: AbsolutePath,
85+
messageHandler: BuiltInBuildSystemMessageHandler?,
8886
fileSystem: FileSystem = localFileSystem
8987
) async throws {
9088
let configPath = projectRoot.appending(component: "buildServer.json")
@@ -104,17 +102,18 @@ package actor BuildServerBuildSystem: MessageHandler {
104102
#endif
105103
self.projectRoot = projectRoot
106104
self.serverConfig = config
105+
self.messageHandler = messageHandler
107106
try await self.initializeBuildServer()
108107
}
109108

110109
/// Creates a build system using the Build Server Protocol config.
111110
///
112111
/// - Returns: nil if `projectRoot` has no config or there is an error parsing it.
113-
package init?(projectRoot: AbsolutePath?) async {
112+
package init?(projectRoot: AbsolutePath?, messageHandler: BuiltInBuildSystemMessageHandler?) async {
114113
guard let projectRoot else { return nil }
115114

116115
do {
117-
try await self.init(projectRoot: projectRoot)
116+
try await self.init(projectRoot: projectRoot, messageHandler: messageHandler)
118117
} catch is FileSystemError {
119118
// config file was missing, no build server for this workspace
120119
return nil
@@ -259,6 +258,13 @@ private func readReponseDataKey(data: LSPAny?, key: String) -> String? {
259258
}
260259

261260
extension BuildServerBuildSystem: BuiltInBuildSystem {
261+
static package func projectRoot(for workspaceFolder: AbsolutePath, options: SourceKitLSPOptions) -> AbsolutePath? {
262+
guard localFileSystem.isFile(workspaceFolder.appending(component: "buildServer.json")) else {
263+
return nil
264+
}
265+
return workspaceFolder
266+
}
267+
262268
package nonisolated var supportsPreparation: Bool { false }
263269

264270
/// The build settings for the given file.

Sources/BuildSystemIntegration/BuildSystem.swift

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,11 @@
1313
import BuildServerProtocol
1414
import LanguageServerProtocol
1515
import SKLogging
16+
import SKOptions
1617
import ToolchainRegistry
1718

1819
import struct TSCBasic.AbsolutePath
20+
import struct TSCBasic.RelativePath
1921

2022
/// Defines how well a `BuildSystem` can handle a file with a given URI.
2123
package enum FileHandlingCapability: Comparable, Sendable {
@@ -67,6 +69,13 @@ package struct PrepareNotSupportedError: Error, CustomStringConvertible {
6769
/// For example, a SwiftPMWorkspace provides compiler arguments for the files
6870
/// contained in a SwiftPM package root directory.
6971
package protocol BuiltInBuildSystem: AnyObject, Sendable {
72+
/// When opening an LSP workspace at `workspaceFolder`, determine the directory in which a project of this build system
73+
/// starts. For example, a user might open the `Sources` folder of a SwiftPM project, then the project root is the
74+
/// directory containing `Package.swift`.
75+
///
76+
/// Returns `nil` if the build system can't handle the given workspace folder
77+
static func projectRoot(for workspaceFolder: AbsolutePath, options: SourceKitLSPOptions) -> AbsolutePath?
78+
7079
/// The root of the project that this build system manages. For example, for SwiftPM packages, this is the folder
7180
/// containing Package.swift. For compilation databases it is the root folder based on which the compilation database
7281
/// was found.
@@ -90,11 +99,6 @@ package protocol BuiltInBuildSystem: AnyObject, Sendable {
9099
/// context.
91100
func setDelegate(_ delegate: BuildSystemDelegate?) async
92101

93-
/// Set the message handler that is used to send messages from the build system to SourceKit-LSP.
94-
// FIXME: (BSP Migration) This should be set in the initializer but can't right now because BuiltInBuildSystemAdapter is not
95-
// responsible for creating the build system.
96-
func setMessageHandler(_ messageHandler: BuiltInBuildSystemMessageHandler) async
97-
98102
/// Whether the build system is capable of preparing a target for indexing, ie. if the `prepare` methods has been
99103
/// implemented.
100104
var supportsPreparation: Bool { get }

Sources/BuildSystemIntegration/BuildSystemManager.swift

Lines changed: 34 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import BuildServerProtocol
1414
import Dispatch
1515
import LanguageServerProtocol
1616
import SKLogging
17+
import SKOptions
1718
import SwiftExtensions
1819
import ToolchainRegistry
1920

@@ -76,10 +77,7 @@ package actor BuildSystemManager: BuiltInBuildSystemAdapterDelegate {
7677
/// The underlying primary build system.
7778
///
7879
/// - Important: The only time this should be modified is in the initializer. Afterwards, it must be constant.
79-
private(set) var buildSystem: BuiltInBuildSystemAdapter?
80-
81-
/// Timeout before fallback build settings are used.
82-
let fallbackSettingsTimeout: DispatchTimeInterval
80+
private(set) package var buildSystem: BuiltInBuildSystemAdapter?
8381

8482
/// The fallback build system. If present, used when the `buildSystem` is not
8583
/// set or cannot provide settings.
@@ -108,27 +106,49 @@ package actor BuildSystemManager: BuiltInBuildSystemAdapterDelegate {
108106
}
109107

110108
package var supportsPreparation: Bool {
111-
return buildSystem?.underlyingBuildSystem.supportsPreparation ?? false
109+
get async {
110+
return await buildSystem?.underlyingBuildSystem.supportsPreparation ?? false
111+
}
112112
}
113113

114-
/// Create a BuildSystemManager that wraps the given build system. The new
115-
/// manager will modify the delegate of the underlying build system.
116114
package init(
117-
buildSystem: BuiltInBuildSystem?,
115+
buildSystemKind: (WorkspaceType, projectRoot: AbsolutePath)?,
116+
toolchainRegistry: ToolchainRegistry,
117+
options: SourceKitLSPOptions,
118+
swiftpmTestHooks: SwiftPMTestHooks,
119+
reloadPackageStatusCallback: @Sendable @escaping (ReloadPackageStatus) async -> Void
120+
) async {
121+
self.fallbackBuildSystem = FallbackBuildSystem(options: options.fallbackBuildSystemOrDefault)
122+
self.toolchainRegistry = toolchainRegistry
123+
self.buildSystem = await BuiltInBuildSystemAdapter(
124+
buildSystemKind: buildSystemKind,
125+
toolchainRegistry: toolchainRegistry,
126+
options: options,
127+
swiftpmTestHooks: swiftpmTestHooks,
128+
reloadPackageStatusCallback: reloadPackageStatusCallback,
129+
messageHandler: self
130+
)
131+
await self.buildSystem?.underlyingBuildSystem.setDelegate(self)
132+
}
133+
134+
/// Create a BuildSystemManager that wraps the given build system.
135+
/// The new manager will modify the delegate of the underlying build system.
136+
///
137+
/// - Important: For testing purposes only
138+
package init(
139+
testBuildSystem: BuiltInBuildSystem?,
118140
fallbackBuildSystem: FallbackBuildSystem?,
119141
mainFilesProvider: MainFilesProvider?,
120-
toolchainRegistry: ToolchainRegistry,
121-
fallbackSettingsTimeout: DispatchTimeInterval = .seconds(3)
142+
toolchainRegistry: ToolchainRegistry
122143
) async {
123-
let buildSystemHasDelegate = await buildSystem?.delegate != nil
144+
let buildSystemHasDelegate = await testBuildSystem?.delegate != nil
124145
precondition(!buildSystemHasDelegate)
125146
self.fallbackBuildSystem = fallbackBuildSystem
126147
self.mainFilesProvider = mainFilesProvider
127148
self.toolchainRegistry = toolchainRegistry
128-
self.fallbackSettingsTimeout = fallbackSettingsTimeout
129149
self.buildSystem =
130-
if let buildSystem {
131-
await BuiltInBuildSystemAdapter(buildSystem: buildSystem, messageHandler: self)
150+
if let testBuildSystem {
151+
await BuiltInBuildSystemAdapter(testBuildSystem: testBuildSystem, messageHandler: self)
132152
} else {
133153
nil
134154
}

Sources/BuildSystemIntegration/BuiltInBuildSystemAdapter.swift

Lines changed: 74 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,12 @@
1313
import BuildServerProtocol
1414
import LanguageServerProtocol
1515
import SKLogging
16+
import SKOptions
1617
import SKSupport
18+
import ToolchainRegistry
19+
20+
import struct TSCBasic.AbsolutePath
21+
import struct TSCBasic.RelativePath
1722

1823
// FIXME: (BSP Migration) This should be a MessageHandler once we have migrated all build system queries to BSP and can use
1924
// LocalConnection for the communication.
@@ -29,22 +34,84 @@ package protocol BuiltInBuildSystemMessageHandler: AnyObject, Sendable {
2934
func sendRequestToSourceKitLSP<R: RequestType>(_ request: R) async throws -> R.Response
3035
}
3136

37+
/// Create a build system of the given type.
38+
private func createBuildSystem(
39+
ofType buildSystemType: WorkspaceType,
40+
projectRoot: AbsolutePath,
41+
options: SourceKitLSPOptions,
42+
swiftpmTestHooks: SwiftPMTestHooks,
43+
toolchainRegistry: ToolchainRegistry,
44+
messageHandler: BuiltInBuildSystemMessageHandler,
45+
reloadPackageStatusCallback: @Sendable @escaping (ReloadPackageStatus) async -> Void
46+
) async -> BuiltInBuildSystem? {
47+
switch buildSystemType {
48+
case .buildServer:
49+
return await BuildServerBuildSystem(projectRoot: projectRoot, messageHandler: messageHandler)
50+
case .compilationDatabase:
51+
return CompilationDatabaseBuildSystem(
52+
projectRoot: projectRoot,
53+
searchPaths: (options.compilationDatabaseOrDefault.searchPaths ?? []).compactMap {
54+
try? RelativePath(validating: $0)
55+
},
56+
messageHandler: messageHandler
57+
)
58+
case .swiftPM:
59+
return await SwiftPMBuildSystem(
60+
projectRoot: projectRoot,
61+
toolchainRegistry: toolchainRegistry,
62+
options: options,
63+
messageHandler: messageHandler,
64+
reloadPackageStatusCallback: reloadPackageStatusCallback,
65+
testHooks: swiftpmTestHooks
66+
)
67+
}
68+
}
69+
3270
/// A type that outwardly acts as a build server conforming to the Build System Integration Protocol and internally uses
3371
/// a `BuiltInBuildSystem` to satisfy the requests.
34-
actor BuiltInBuildSystemAdapter: BuiltInBuildSystemMessageHandler {
72+
package actor BuiltInBuildSystemAdapter: BuiltInBuildSystemMessageHandler {
3573
/// The underlying build system
3674
// FIXME: (BSP Migration) This should be private, all messages should go through BSP. Only accessible from the outside for transition
3775
// purposes.
38-
package let underlyingBuildSystem: BuiltInBuildSystem
76+
private(set) package var underlyingBuildSystem: BuiltInBuildSystem!
3977
private let messageHandler: any BuiltInBuildSystemAdapterDelegate
4078

41-
init(
42-
buildSystem: BuiltInBuildSystem,
79+
init?(
80+
buildSystemKind: (WorkspaceType, projectRoot: AbsolutePath)?,
81+
toolchainRegistry: ToolchainRegistry,
82+
options: SourceKitLSPOptions,
83+
swiftpmTestHooks: SwiftPMTestHooks,
84+
reloadPackageStatusCallback: @Sendable @escaping (ReloadPackageStatus) async -> Void,
4385
messageHandler: any BuiltInBuildSystemAdapterDelegate
4486
) async {
87+
guard let (buildSystemType, projectRoot) = buildSystemKind else {
88+
return nil
89+
}
90+
self.messageHandler = messageHandler
91+
92+
let buildSystem = await createBuildSystem(
93+
ofType: buildSystemType,
94+
projectRoot: projectRoot,
95+
options: options,
96+
swiftpmTestHooks: swiftpmTestHooks,
97+
toolchainRegistry: toolchainRegistry,
98+
messageHandler: self,
99+
reloadPackageStatusCallback: reloadPackageStatusCallback
100+
)
101+
guard let buildSystem else {
102+
return nil
103+
}
104+
45105
self.underlyingBuildSystem = buildSystem
106+
}
107+
108+
/// - Important: For testing purposes only
109+
init(
110+
testBuildSystem: BuiltInBuildSystem,
111+
messageHandler: any BuiltInBuildSystemAdapterDelegate
112+
) async {
113+
self.underlyingBuildSystem = testBuildSystem
46114
self.messageHandler = messageHandler
47-
await buildSystem.setMessageHandler(self)
48115
}
49116

50117
package func send<R: RequestType>(_ request: R) async throws -> R.Response {
@@ -89,7 +156,7 @@ actor BuiltInBuildSystemAdapter: BuiltInBuildSystemMessageHandler {
89156
}
90157
}
91158

92-
func sendNotificationToSourceKitLSP(_ notification: some LanguageServerProtocol.NotificationType) async {
159+
package func sendNotificationToSourceKitLSP(_ notification: some LanguageServerProtocol.NotificationType) async {
93160
logger.info(
94161
"""
95162
Received notification from build system
@@ -99,7 +166,7 @@ actor BuiltInBuildSystemAdapter: BuiltInBuildSystemMessageHandler {
99166
await messageHandler.handle(notification)
100167
}
101168

102-
func sendRequestToSourceKitLSP<R: RequestType>(_ request: R) async throws -> R.Response {
169+
package func sendRequestToSourceKitLSP<R: RequestType>(_ request: R) async throws -> R.Response {
103170
logger.info(
104171
"""
105172
Received request from build system

Sources/BuildSystemIntegration/CompilationDatabaseBuildSystem.swift

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import BuildServerProtocol
1414
import Dispatch
1515
import LanguageServerProtocol
1616
import SKLogging
17+
import SKOptions
1718
import SKSupport
1819
import ToolchainRegistry
1920

@@ -49,10 +50,6 @@ package actor CompilationDatabaseBuildSystem {
4950

5051
package weak var messageHandler: BuiltInBuildSystemMessageHandler?
5152

52-
package func setMessageHandler(_ messageHandler: any BuiltInBuildSystemMessageHandler) {
53-
self.messageHandler = messageHandler
54-
}
55-
5653
package let projectRoot: AbsolutePath
5754

5855
let searchPaths: [RelativePath]
@@ -86,11 +83,13 @@ package actor CompilationDatabaseBuildSystem {
8683
package init?(
8784
projectRoot: AbsolutePath,
8885
searchPaths: [RelativePath],
86+
messageHandler: (any BuiltInBuildSystemMessageHandler)?,
8987
fileSystem: FileSystem = localFileSystem
9088
) {
9189
self.fileSystem = fileSystem
9290
self.projectRoot = projectRoot
9391
self.searchPaths = searchPaths
92+
self.messageHandler = messageHandler
9493
if let compdb = tryLoadCompilationDatabase(directory: projectRoot, additionalSearchPaths: searchPaths, fileSystem) {
9594
self.compdb = compdb
9695
} else {
@@ -100,6 +99,13 @@ package actor CompilationDatabaseBuildSystem {
10099
}
101100

102101
extension CompilationDatabaseBuildSystem: BuiltInBuildSystem {
102+
static package func projectRoot(for workspaceFolder: AbsolutePath, options: SourceKitLSPOptions) -> AbsolutePath? {
103+
if tryLoadCompilationDatabase(directory: workspaceFolder) != nil {
104+
return workspaceFolder
105+
}
106+
return nil
107+
}
108+
103109
package nonisolated var supportsPreparation: Bool { false }
104110

105111
package var indexDatabasePath: AbsolutePath? {

0 commit comments

Comments
 (0)