Support building SourceKit-LSP without a dependency on SwiftPM

This commit is contained in:
Alex Hoppen
2025-01-08 13:08:31 +01:00
parent 718c0ee809
commit 5326852ea8
10 changed files with 209 additions and 109 deletions

View File

@@ -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
),
@@ -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",
@@ -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
@@ -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
),
@@ -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 {
@@ -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
@@ -570,18 +580,16 @@ 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 = "main"
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),
@@ -589,6 +597,9 @@ var dependencies: [Package.Dependency] {
// 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)
])
}
}

View File

@@ -210,6 +210,7 @@ private extension BuildSystemSpec {
)
}
case .swiftPM:
#if canImport(PackageModel)
return await Self.createBuiltInBuildSystemAdapter(
projectRoot: projectRoot,
messagesToSourceKitLSPHandler: messagesToSourceKitLSPHandler,
@@ -223,6 +224,9 @@ private extension BuildSystemSpec {
testHooks: testHooks.swiftPMTestHooks
)
}
#else
return nil
#endif
case .testBuildSystem:
return await Self.createBuiltInBuildSystemAdapter(
projectRoot: projectRoot,

View File

@@ -16,6 +16,19 @@ package import LanguageServerProtocol
import LanguageServerProtocol
#endif
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
}
}
package struct BuildSystemTestHooks: Sendable {
package var swiftPMTestHooks: SwiftPMTestHooks

View File

@@ -10,20 +10,92 @@
//
//===----------------------------------------------------------------------===//
import Foundation
import LanguageServerProtocol
import SKLogging
#if compiler(>=6)
package import BuildServerProtocol
import LanguageServerProtocol
import SKLogging
#else
import BuildServerProtocol
import LanguageServerProtocol
import SKLogging
#endif
extension BuildTargetIdentifier {
package static let dummy: BuildTargetIdentifier = BuildTargetIdentifier(uri: try! URI(string: "dummy://dummy"))
}
package enum BuildDestinationIdentifier {
case host
case target
/// A string that can be used to identify the build triple in a `BuildTargetIdentifier`.
///
/// `BuildSystemManager.canonicalBuildTargetIdentifier` picks the canonical target based on alphabetical
/// ordering. We rely on the string "destination" being ordered before "tools" so that we prefer a
/// `destination` (or "target") target over a `tools` (or "host") target.
var id: String {
switch self {
case .host:
return "tools"
case .target:
return "destination"
}
}
}
extension BuildTargetIdentifier {
/// - Important: *For testing only*
package init(target: String, destination: BuildDestinationIdentifier) throws {
var components = URLComponents()
components.scheme = "swiftpm"
components.host = "target"
components.queryItems = [
URLQueryItem(name: "target", value: target),
URLQueryItem(name: "destination", value: destination.id),
]
struct FailedToConvertSwiftBuildTargetToUrlError: Swift.Error, CustomStringConvertible {
var target: String
var destination: String
var description: String {
return "Failed to generate URL for target: \(target), destination: \(destination)"
}
}
guard let url = components.url else {
throw FailedToConvertSwiftBuildTargetToUrlError(target: target, destination: destination.id)
}
self.init(uri: URI(url))
}
fileprivate static let forPackageManifest = BuildTargetIdentifier(uri: try! URI(string: "swiftpm://package-manifest"))
fileprivate var targetProperties: (target: String, runDestination: String) {
get throws {
struct InvalidTargetIdentifierError: Swift.Error, CustomStringConvertible {
var target: BuildTargetIdentifier
var description: String {
return "Invalid target identifier \(target)"
}
}
guard let components = URLComponents(url: self.uri.arbitrarySchemeURL, resolvingAgainstBaseURL: false) else {
throw InvalidTargetIdentifierError(target: self)
}
let target = components.queryItems?.last(where: { $0.name == "target" })?.value
let runDestination = components.queryItems?.last(where: { $0.name == "destination" })?.value
guard let target, let runDestination else {
throw InvalidTargetIdentifierError(target: self)
}
return (target, runDestination)
}
}
}
#if compiler(>=6)
extension BuildTargetIdentifier: CustomLogStringConvertible {
package var description: String {

View File

@@ -59,9 +59,13 @@ package func determineBuildSystem(
return BuildSystemSpec(kind: .compilationDatabase, projectRoot: projectRoot)
}
case .swiftPM:
#if canImport(PackageModel)
if let projectRoot = SwiftPMBuildSystem.projectRoot(for: workspaceFolderUrl, options: options) {
return BuildSystemSpec(kind: .swiftPM, projectRoot: projectRoot)
}
#else
return nil
#endif
}
}

View File

@@ -10,53 +10,41 @@
//
//===----------------------------------------------------------------------===//
#if compiler(>=6)
#if canImport(PackageModel)
import Basics
@preconcurrency import Build
package import BuildServerProtocol
import Dispatch
package import Foundation
package import LanguageServerProtocol
import LanguageServerProtocolExtensions
@preconcurrency import PackageGraph
import PackageLoading
import PackageModel
import SKLogging
package import SKOptions
@preconcurrency package import SPMBuildCore
import SourceControl
package import SourceKitLSPAPI
import SwiftExtensions
package import ToolchainRegistry
import TSCExtensions
@preconcurrency import Workspace
import struct TSCBasic.AbsolutePath
import class TSCBasic.Process
#if compiler(>=6)
package import BuildServerProtocol
package import BuildSystemIntegration
package import Foundation
package import LanguageServerProtocol
package import SKOptions
package import SourceKitLSPAPI
package import ToolchainRegistry
package import class ToolchainRegistry.Toolchain
#else
import Basics
@preconcurrency import Build
import BuildServerProtocol
import Dispatch
import BuildSystemIntegration
import Foundation
import LanguageServerProtocol
import LanguageServerProtocolExtensions
@preconcurrency import PackageGraph
import PackageLoading
import PackageModel
import SKLogging
import SKOptions
@preconcurrency import SPMBuildCore
import SourceControl
import SourceKitLSPAPI
import SwiftExtensions
import ToolchainRegistry
import TSCExtensions
@preconcurrency import Workspace
import struct TSCBasic.AbsolutePath
import class TSCBasic.Process
import class ToolchainRegistry.Toolchain
#endif
@@ -78,51 +66,18 @@ fileprivate extension Basics.Diagnostic.Severity {
}
}
fileprivate extension BuildDestination {
/// A string that can be used to identify the build triple in a `BuildTargetIdentifier`.
///
/// `BuildSystemManager.canonicalBuildTargetIdentifier` picks the canonical target based on alphabetical
/// ordering. We rely on the string "destination" being ordered before "tools" so that we prefer a
/// `destination` (or "target") target over a `tools` (or "host") target.
var id: String {
switch self {
case .host:
return "tools"
case .target:
return "destination"
extension BuildDestinationIdentifier {
init(_ destination: BuildDestination) {
switch destination {
case .target: self = .target
case .host: self = .host
}
}
}
extension BuildTargetIdentifier {
fileprivate init(_ buildTarget: any SwiftBuildTarget) throws {
try self.init(target: buildTarget.name, destination: buildTarget.destination)
}
/// - Important: *For testing only*
package init(target: String, destination: BuildDestination) throws {
var components = URLComponents()
components.scheme = "swiftpm"
components.host = "target"
components.queryItems = [
URLQueryItem(name: "target", value: target),
URLQueryItem(name: "destination", value: destination.id),
]
struct FailedToConvertSwiftBuildTargetToUrlError: Swift.Error, CustomStringConvertible {
var target: String
var destination: String
var description: String {
return "Failed to generate URL for target: \(target), destination: \(destination)"
}
}
guard let url = components.url else {
throw FailedToConvertSwiftBuildTargetToUrlError(target: target, destination: destination.id)
}
self.init(uri: URI(url))
try self.init(target: buildTarget.name, destination: BuildDestinationIdentifier(buildTarget.destination))
}
fileprivate static let forPackageManifest = BuildTargetIdentifier(uri: try! URI(string: "swiftpm://package-manifest"))
@@ -159,19 +114,6 @@ fileprivate extension TSCBasic.AbsolutePath {
fileprivate let preparationTaskID: AtomicUInt32 = AtomicUInt32(initialValue: 0)
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
}
}
/// Swift Package Manager build system and workspace support.
///
/// This class implements the `BuiltInBuildSystem` interface to provide the build settings for a Swift
@@ -793,3 +735,5 @@ fileprivate extension URL {
(try? resourceValues(forKeys: [.isDirectoryKey]))?.isDirectory == true
}
}
#endif

View File

@@ -10,6 +10,8 @@
//
//===----------------------------------------------------------------------===//
#if canImport(PackageModel)
import Foundation
import LanguageServerProtocol
import PackageModel
@@ -298,3 +300,5 @@ fileprivate extension FunctionCallExprSyntax {
return memberAccess.declName.baseName.text
}
}
#endif

View File

@@ -14,15 +14,20 @@ import SwiftRefactor
/// List of all of the syntactic code action providers, which can be used
/// to produce code actions using only the swift-syntax tree of a file.
let allSyntaxCodeActions: [SyntaxCodeActionProvider.Type] = [
AddDocumentation.self,
AddSeparatorsToIntegerLiteral.self,
ConvertIntegerLiteral.self,
ConvertJSONToCodableStruct.self,
ConvertStringConcatenationToStringInterpolation.self,
FormatRawStringLiteral.self,
MigrateToNewIfLetSyntax.self,
OpaqueParameterToGeneric.self,
PackageManifestEdits.self,
RemoveSeparatorsFromIntegerLiteral.self,
]
let allSyntaxCodeActions: [SyntaxCodeActionProvider.Type] = {
var result: [SyntaxCodeActionProvider.Type] = [
AddDocumentation.self,
AddSeparatorsToIntegerLiteral.self,
ConvertIntegerLiteral.self,
ConvertJSONToCodableStruct.self,
ConvertStringConcatenationToStringInterpolation.self,
FormatRawStringLiteral.self,
MigrateToNewIfLetSyntax.self,
OpaqueParameterToGeneric.self,
RemoveSeparatorsFromIntegerLiteral.self,
]
#if canImport(PackageModel)
result.append(PackageManifestEdits.self)
#endif
return result
}()

View File

@@ -10,7 +10,7 @@
//
//===----------------------------------------------------------------------===//
import Build
#if canImport(PackageModel)
import BuildServerProtocol
@_spi(Testing) import BuildSystemIntegration
import LanguageServerProtocol
@@ -19,6 +19,7 @@ import PackageModel
import SKOptions
import SKTestSupport
import SourceKitLSP
@preconcurrency import SPMBuildCore
import SwiftExtensions
import TSCBasic
import TSCExtensions
@@ -27,11 +28,6 @@ import XCTest
import struct Basics.AbsolutePath
import struct Basics.Triple
import struct PackageModel.BuildFlags
#if canImport(SPMBuildCore)
@preconcurrency import SPMBuildCore
#endif
private var hostTriple: Triple {
get async throws {
@@ -1243,3 +1239,4 @@ fileprivate extension URL {
return result
}
}
#endif

View File

@@ -15,9 +15,55 @@ import BuildSystemIntegration
import LanguageServerProtocol
import SKLogging
import SemanticIndex
import SourceKitLSPAPI
import XCTest
enum BuildDestination {
case host
case target
/// A string that can be used to identify the build triple in a `BuildTargetIdentifier`.
///
/// `BuildSystemManager.canonicalBuildTargetIdentifier` picks the canonical target based on alphabetical
/// ordering. We rely on the string "destination" being ordered before "tools" so that we prefer a
/// `destination` (or "target") target over a `tools` (or "host") target.
var id: String {
switch self {
case .host:
return "tools"
case .target:
return "destination"
}
}
}
extension BuildTargetIdentifier {
/// - Important: *For testing only*
init(target: String, destination: BuildDestination) throws {
var components = URLComponents()
components.scheme = "swiftpm"
components.host = "target"
components.queryItems = [
URLQueryItem(name: "target", value: target),
URLQueryItem(name: "destination", value: destination.id),
]
struct FailedToConvertSwiftBuildTargetToUrlError: Swift.Error, CustomStringConvertible {
var target: String
var destination: String
var description: String {
return "Failed to generate URL for target: \(target), destination: \(destination)"
}
}
guard let url = components.url else {
throw FailedToConvertSwiftBuildTargetToUrlError(target: target, destination: destination.id)
}
self.init(uri: URI(url))
}
}
struct ExpectedPreparation {
let target: BuildTargetIdentifier