Skip to content

[6.1] Allow workspace options to affect build system search #1952

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Jan 25, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 22 additions & 11 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -80,10 +80,12 @@ var targets: [Target] = [
"SwiftExtensions",
"ToolchainRegistry",
"TSCExtensions",
.product(name: "SwiftPM-auto", package: "swift-package-manager"),
.product(name: "SwiftPMDataModel-auto", package: "swift-package-manager"),
.product(name: "SwiftToolsSupport-auto", package: "swift-tools-support-core"),
],
]
+ swiftPMDependency([
.product(name: "SwiftPM-auto", package: "swift-package-manager"),
.product(name: "SwiftPMDataModel-auto", package: "swift-package-manager"),
]),
exclude: ["CMakeLists.txt"],
swiftSettings: globalSwiftSettings
),
Expand Down Expand Up @@ -387,8 +389,10 @@ var targets: [Target] = [
.product(name: "IndexStoreDB", package: "indexstore-db"),
.product(name: "Crypto", package: "swift-crypto"),
.product(name: "SwiftToolsSupport-auto", package: "swift-tools-support-core"),
.product(name: "SwiftPM-auto", package: "swift-package-manager"),
]
+ swiftPMDependency([
.product(name: "SwiftPM-auto", package: "swift-package-manager")
])
+ swiftSyntaxDependencies([
"SwiftBasicFormat", "SwiftDiagnostics", "SwiftIDEUtils", "SwiftParser", "SwiftParserDiagnostics",
"SwiftRefactor", "SwiftSyntax",
Expand Down Expand Up @@ -449,8 +453,6 @@ var targets: [Target] = [
"SKUtilities",
"SwiftExtensions",
"TSCExtensions",
.product(name: "SwiftPMDataModel-auto", package: "swift-package-manager"),
.product(name: "SwiftToolsSupport-auto", package: "swift-tools-support-core"),
],
exclude: ["CMakeLists.txt"],
swiftSettings: globalSwiftSettings
Expand All @@ -461,8 +463,6 @@ var targets: [Target] = [
dependencies: [
"SKTestSupport",
"ToolchainRegistry",
.product(name: "SwiftPMDataModel-auto", package: "swift-package-manager"),
.product(name: "SwiftToolsSupport-auto", package: "swift-tools-support-core"),
],
swiftSettings: globalSwiftSettings
),
Expand Down Expand Up @@ -524,6 +524,13 @@ func swiftSyntaxDependencies(_ names: [String]) -> [Target.Dependency] {
}
}

func swiftPMDependency<T>(_ values: [T]) -> [T] {
if noSwiftPMDependency {
return []
}
return values
}

// MARK: - Parse build arguments

func hasEnvironmentVariable(_ name: String) -> Bool {
Expand Down Expand Up @@ -558,6 +565,9 @@ var buildDynamicSwiftSyntaxLibrary: Bool { hasEnvironmentVariable("SWIFTSYNTAX_B
/// the `swift test` invocation so that all pre-built modules can be found.
var buildOnlyTests: Bool { hasEnvironmentVariable("SOURCEKIT_LSP_BUILD_ONLY_TESTS") }

/// Build SourceKit-LSP without a dependency on SwiftPM, ie. without support for SwiftPM projects.
var noSwiftPMDependency: Bool { hasEnvironmentVariable("SOURCEKIT_LSP_NO_SWIFTPM_DEPENDENCY") }

// MARK: - Dependencies

// When building with the swift build-script, use local dependencies whose contents are controlled
Expand All @@ -570,25 +580,26 @@ var dependencies: [Package.Dependency] {
} else if useLocalDependencies {
return [
.package(path: "../indexstore-db"),
.package(name: "swift-package-manager", path: "../swiftpm"),
.package(path: "../swift-tools-support-core"),
.package(path: "../swift-argument-parser"),
.package(path: "../swift-syntax"),
.package(path: "../swift-crypto"),
]
] + swiftPMDependency([.package(name: "swift-package-manager", path: "../swiftpm")])
} else {
let relatedDependenciesBranch = "release/6.1"

return [
.package(url: "https://github.com/swiftlang/indexstore-db.git", branch: relatedDependenciesBranch),
.package(url: "https://github.com/swiftlang/swift-package-manager.git", branch: relatedDependenciesBranch),
.package(url: "https://github.com/apple/swift-tools-support-core.git", branch: relatedDependenciesBranch),
.package(url: "https://github.com/apple/swift-argument-parser.git", from: "1.4.0"),
.package(url: "https://github.com/swiftlang/swift-syntax.git", branch: relatedDependenciesBranch),
.package(url: "https://github.com/apple/swift-crypto.git", from: "3.0.0"),
// Not a build dependency. Used so the "Format Source Code" command plugin can be used to format sourcekit-lsp
.package(url: "https://github.com/swiftlang/swift-format.git", branch: relatedDependenciesBranch),
]
+ swiftPMDependency([
.package(url: "https://github.com/swiftlang/swift-package-manager.git", branch: relatedDependenciesBranch)
])
}
}

Expand Down
77 changes: 32 additions & 45 deletions Sources/BuildSystemIntegration/BuildSystemManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -142,10 +142,9 @@ private enum BuildSystemAdapter {
}

private extension BuildSystemSpec {
private static func createBuiltInBuildSystemAdapter(
projectRoot: URL,
private func createBuiltInBuildSystemAdapter(
messagesToSourceKitLSPHandler: any MessageHandler,
buildSystemTestHooks: BuildSystemTestHooks,
buildSystemHooks: BuildSystemHooks,
_ createBuildSystem: @Sendable (_ connectionToSourceKitLSP: any Connection) async throws -> BuiltInBuildSystem?
) async -> BuildSystemAdapter? {
let connectionToSourceKitLSP = LocalConnection(
Expand All @@ -164,7 +163,7 @@ private extension BuildSystemSpec {
let buildSystemAdapter = BuiltInBuildSystemAdapter(
underlyingBuildSystem: buildSystem,
connectionToSourceKitLSP: connectionToSourceKitLSP,
buildSystemTestHooks: buildSystemTestHooks
buildSystemHooks: buildSystemHooks
)
let connectionToBuildSystem = LocalConnection(
receiverName: "\(type(of: buildSystem)) for \(projectRoot.lastPathComponent)"
Expand All @@ -178,14 +177,15 @@ private extension BuildSystemSpec {
func createBuildSystemAdapter(
toolchainRegistry: ToolchainRegistry,
options: SourceKitLSPOptions,
buildSystemTestHooks testHooks: BuildSystemTestHooks,
buildSystemHooks: BuildSystemHooks,
messagesToSourceKitLSPHandler: any MessageHandler
) async -> BuildSystemAdapter? {
switch self.kind {
case .buildServer:
let buildSystem = await orLog("Creating external build system") {
try await ExternalBuildSystemAdapter(
projectRoot: projectRoot,
configPath: configPath,
messagesToSourceKitLSPHandler: messagesToSourceKitLSPHandler
)
}
Expand All @@ -196,40 +196,38 @@ private extension BuildSystemSpec {
logger.log("Created external build server at \(projectRoot)")
return .external(buildSystem)
case .compilationDatabase:
return await Self.createBuiltInBuildSystemAdapter(
projectRoot: projectRoot,
return await createBuiltInBuildSystemAdapter(
messagesToSourceKitLSPHandler: messagesToSourceKitLSPHandler,
buildSystemTestHooks: testHooks
buildSystemHooks: buildSystemHooks
) { connectionToSourceKitLSP in
CompilationDatabaseBuildSystem(
projectRoot: projectRoot,
searchPaths: (options.compilationDatabaseOrDefault.searchPaths ?? []).compactMap {
try? RelativePath(validating: $0)
},
try CompilationDatabaseBuildSystem(
configPath: configPath,
connectionToSourceKitLSP: connectionToSourceKitLSP
)
}
case .swiftPM:
return await Self.createBuiltInBuildSystemAdapter(
projectRoot: projectRoot,
#if canImport(PackageModel)
return await createBuiltInBuildSystemAdapter(
messagesToSourceKitLSPHandler: messagesToSourceKitLSPHandler,
buildSystemTestHooks: testHooks
buildSystemHooks: buildSystemHooks
) { connectionToSourceKitLSP in
try await SwiftPMBuildSystem(
projectRoot: projectRoot,
toolchainRegistry: toolchainRegistry,
options: options,
connectionToSourceKitLSP: connectionToSourceKitLSP,
testHooks: testHooks.swiftPMTestHooks
testHooks: buildSystemHooks.swiftPMTestHooks
)
}
case .testBuildSystem:
return await Self.createBuiltInBuildSystemAdapter(
projectRoot: projectRoot,
#else
return nil
#endif
case .injected(let injector):
return await createBuiltInBuildSystemAdapter(
messagesToSourceKitLSPHandler: messagesToSourceKitLSPHandler,
buildSystemTestHooks: testHooks
buildSystemHooks: buildSystemHooks
) { connectionToSourceKitLSP in
TestBuildSystem(projectRoot: projectRoot, connectionToSourceKitLSP: connectionToSourceKitLSP)
await injector.createBuildSystem(projectRoot: projectRoot, connectionToSourceKitLSP: connectionToSourceKitLSP)
}
}
}
Expand All @@ -244,13 +242,14 @@ package actor BuildSystemManager: QueueBasedMessageHandler {

package let messageHandlingQueue = AsyncQueue<BuildSystemMessageDependencyTracker>()

/// The root of the project that this build system manages.
/// The path to the main configuration file (or directory) that this build system manages.
///
/// For example, in 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.
/// Some examples:
/// - The path to `Package.swift` for SwiftPM packages
/// - The path to `compile_commands.json` for a JSON compilation database
///
/// `nil` if the `BuildSystemManager` does not have an underlying build system.
package let projectRoot: URL?
package let configPath: URL?

/// The files for which the delegate has requested change notifications, ie. the files for which the delegate wants to
/// get `fileBuildSettingsChanged` and `filesDependenciesUpdated` callbacks.
Expand All @@ -271,19 +270,6 @@ package actor BuildSystemManager: QueueBasedMessageHandler {
}
}

/// If the underlying build system is a `TestBuildSystem`, return it. Otherwise, `nil`
///
/// - Important: For testing purposes only.
package var testBuildSystem: TestBuildSystem? {
get async {
switch buildSystemAdapter {
case .builtIn(let builtInBuildSystemAdapter, _): return await builtInBuildSystemAdapter.testBuildSystem
case .external: return nil
case nil: return nil
}
}
}

/// Provider of file to main file mappings.
private var mainFilesProvider: MainFilesProvider?

Expand Down Expand Up @@ -363,16 +349,16 @@ package actor BuildSystemManager: QueueBasedMessageHandler {
toolchainRegistry: ToolchainRegistry,
options: SourceKitLSPOptions,
connectionToClient: BuildSystemManagerConnectionToClient,
buildSystemTestHooks: BuildSystemTestHooks
buildSystemHooks: BuildSystemHooks
) async {
self.toolchainRegistry = toolchainRegistry
self.options = options
self.connectionToClient = connectionToClient
self.projectRoot = buildSystemSpec?.projectRoot
self.configPath = buildSystemSpec?.configPath
self.buildSystemAdapter = await buildSystemSpec?.createBuildSystemAdapter(
toolchainRegistry: toolchainRegistry,
options: options,
buildSystemTestHooks: buildSystemTestHooks,
buildSystemHooks: buildSystemHooks,
messagesToSourceKitLSPHandler: WeakMessageHandler(self)
)

Expand Down Expand Up @@ -422,13 +408,14 @@ package actor BuildSystemManager: QueueBasedMessageHandler {
logger.log("Launched a legacy BSP server. Using push-based build settings model.")
let legacyBuildServer = await LegacyBuildServerBuildSystem(
projectRoot: buildSystemSpec.projectRoot,
configPath: buildSystemSpec.configPath,
initializationData: initializeResponse,
externalBuildSystemAdapter
)
let adapter = BuiltInBuildSystemAdapter(
underlyingBuildSystem: legacyBuildServer,
connectionToSourceKitLSP: legacyBuildServer.connectionToSourceKitLSP,
buildSystemTestHooks: buildSystemTestHooks
buildSystemHooks: buildSystemHooks
)
let connectionToBuildSystem = LocalConnection(receiverName: "Legacy BSP server")
connectionToBuildSystem.start(handler: adapter)
Expand Down Expand Up @@ -688,8 +675,8 @@ package actor BuildSystemManager: QueueBasedMessageHandler {
result.formUnion(targets)
}
if !filesAndDirectories.directories.isEmpty, let documentPathComponents = document.fileURL?.pathComponents {
for (directory, (directoryPathComponents, info)) in filesAndDirectories.directories {
guard let directoryPathComponents, let directoryPath = directory.fileURL else {
for (_, (directoryPathComponents, info)) in filesAndDirectories.directories {
guard let directoryPathComponents else {
continue
}
if isDescendant(documentPathComponents, of: directoryPathComponents) {
Expand Down
33 changes: 29 additions & 4 deletions Sources/BuildSystemIntegration/BuildSystemTestHooks.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,48 @@

#if compiler(>=6)
package import LanguageServerProtocol
package import Foundation
#else
import LanguageServerProtocol
import Foundation
#endif

package struct BuildSystemTestHooks: Sendable {
package struct SwiftPMTestHooks: Sendable {
package var reloadPackageDidStart: (@Sendable () async -> Void)?
package var reloadPackageDidFinish: (@Sendable () async -> Void)?

package init(
reloadPackageDidStart: (@Sendable () async -> Void)? = nil,
reloadPackageDidFinish: (@Sendable () async -> Void)? = nil
) {
self.reloadPackageDidStart = reloadPackageDidStart
self.reloadPackageDidFinish = reloadPackageDidFinish
}
}

/// When running SourceKit-LSP in-process, allows the creator of `SourceKitLSPServer` to create the build system instead
/// of SourceKit-LSP creating build systems as needed.
package protocol BuildSystemInjector: Sendable {
func createBuildSystem(projectRoot: URL, connectionToSourceKitLSP: any Connection) async -> BuiltInBuildSystem
}

package struct BuildSystemHooks: Sendable {
package var swiftPMTestHooks: SwiftPMTestHooks

/// A hook that will be executed before a request is handled by a `BuiltInBuildSystem`.
///
/// This allows requests to be artificially delayed.
package var handleRequest: (@Sendable (any RequestType) async -> Void)?
package var preHandleRequest: (@Sendable (any RequestType) async -> Void)?

package var buildSystemInjector: BuildSystemInjector?

package init(
swiftPMTestHooks: SwiftPMTestHooks = SwiftPMTestHooks(),
handleRequest: (@Sendable (any RequestType) async -> Void)? = nil
preHandleRequest: (@Sendable (any RequestType) async -> Void)? = nil,
buildSystemInjector: BuildSystemInjector? = nil
) {
self.swiftPMTestHooks = swiftPMTestHooks
self.handleRequest = handleRequest
self.preHandleRequest = preHandleRequest
self.buildSystemInjector = buildSystemInjector
}
}
Loading