mirror of
https://github.com/apple/sourcekit-lsp.git
synced 2026-03-02 18:23:24 +01:00
1277 lines
43 KiB
Swift
1277 lines
43 KiB
Swift
//===----------------------------------------------------------------------===//
|
||
//
|
||
// This source file is part of the Swift.org open source project
|
||
//
|
||
// Copyright (c) 2014 - 2019 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
|
||
//
|
||
//===----------------------------------------------------------------------===//
|
||
|
||
#if !NO_SWIFTPM_DEPENDENCY
|
||
@_spi(SourceKitLSP) import BuildServerProtocol
|
||
@_spi(Testing) import BuildServerIntegration
|
||
@_spi(SourceKitLSP) import LanguageServerProtocol
|
||
@_spi(SourceKitLSP) import LanguageServerProtocolExtensions
|
||
@_spi(SourceKitLSP) import LanguageServerProtocolTransport
|
||
import PackageModel
|
||
import SKLogging
|
||
import SKOptions
|
||
import SKTestSupport
|
||
import SourceKitLSP
|
||
@preconcurrency import SPMBuildCore
|
||
import SwiftExtensions
|
||
import TSCBasic
|
||
import TSCExtensions
|
||
import ToolchainRegistry
|
||
import Foundation
|
||
import Testing
|
||
import struct Basics.AbsolutePath
|
||
import struct Basics.Triple
|
||
|
||
private var hostTriple: Triple {
|
||
get async throws {
|
||
let toolchain = try #require(
|
||
await ToolchainRegistry.forTesting.preferredToolchain(containing: [
|
||
\.clang, \.clangd, \.sourcekitd, \.swift, \.swiftc,
|
||
])
|
||
)
|
||
let destinationToolchainBinDir = try #require(toolchain.swiftc?.deletingLastPathComponent())
|
||
|
||
let hostSDK = try SwiftSDK.hostSwiftSDK(Basics.AbsolutePath(validating: destinationToolchainBinDir.filePath))
|
||
let hostSwiftPMToolchain = try UserToolchain(swiftSDK: hostSDK)
|
||
|
||
return hostSwiftPMToolchain.targetTriple
|
||
}
|
||
}
|
||
|
||
fileprivate extension SourceKitLSPOptions {
|
||
static var forTestingExperimentalSwiftPMBuildServer: Self {
|
||
SourceKitLSPOptions(swiftPM: SwiftPMOptions(buildSystem: .swiftbuild))
|
||
}
|
||
}
|
||
|
||
@Suite(.serialized, .configureLogging)
|
||
struct SwiftPMBuildServerTests {
|
||
@Test
|
||
func testNoPackage() async throws {
|
||
try await withTestScratchDir { tempDir in
|
||
try FileManager.default.createFiles(
|
||
root: tempDir,
|
||
files: [
|
||
"pkg/Sources/lib/a.swift": ""
|
||
]
|
||
)
|
||
let packageRoot = tempDir.appending(component: "pkg")
|
||
let buildServerSpec = SwiftPMBuildServer.searchForConfig(in: packageRoot, options: try await .testDefault())
|
||
#expect(buildServerSpec == nil)
|
||
}
|
||
}
|
||
|
||
@Test
|
||
func testNoToolchain() async throws {
|
||
try await withTestScratchDir { tempDir in
|
||
try FileManager.default.createFiles(
|
||
root: tempDir,
|
||
files: [
|
||
"pkg/Sources/lib/a.swift": "",
|
||
"pkg/Package.swift": """
|
||
// swift-tools-version:4.2
|
||
import PackageDescription
|
||
let package = Package(
|
||
name: "a",
|
||
targets: [.target(name: "lib")]
|
||
)
|
||
""",
|
||
]
|
||
)
|
||
let packageRoot = tempDir.appending(component: "pkg")
|
||
await expectThrowsError(
|
||
try await SwiftPMBuildServer(
|
||
projectRoot: packageRoot,
|
||
toolchainRegistry: ToolchainRegistry(toolchains: []),
|
||
options: SourceKitLSPOptions(),
|
||
connectionToSourceKitLSP: LocalConnection(receiverName: "dummy"),
|
||
testHooks: SwiftPMTestHooks()
|
||
)
|
||
)
|
||
}
|
||
}
|
||
|
||
@Test
|
||
func testRelativeScratchPath() async throws {
|
||
try await withTestScratchDir { tempDir in
|
||
try FileManager.default.createFiles(
|
||
root: tempDir,
|
||
files: [
|
||
"pkg/Sources/lib/a.swift": "",
|
||
"pkg/Package.swift": """
|
||
// swift-tools-version:4.2
|
||
import PackageDescription
|
||
let package = Package(
|
||
name: "a",
|
||
targets: [.target(name: "lib")]
|
||
)
|
||
""",
|
||
]
|
||
)
|
||
let packageRoot = tempDir.appending(component: "pkg")
|
||
let options = SourceKitLSPOptions(
|
||
swiftPM: .init(
|
||
scratchPath: "non_default_relative_build_path"
|
||
),
|
||
backgroundIndexing: false
|
||
)
|
||
let swiftpmBuildServer = try await SwiftPMBuildServer(
|
||
projectRoot: packageRoot,
|
||
toolchainRegistry: .forTesting,
|
||
options: options,
|
||
connectionToSourceKitLSP: LocalConnection(receiverName: "dummy"),
|
||
testHooks: SwiftPMTestHooks()
|
||
)
|
||
|
||
let dataPath = await swiftpmBuildServer.destinationBuildParameters.dataPath
|
||
let expectedScratchPath = packageRoot.appending(component: try #require(options.swiftPMOrDefault.scratchPath))
|
||
#expect(dataPath.asURL.isDescendant(of: expectedScratchPath))
|
||
}
|
||
}
|
||
|
||
@Test(
|
||
arguments: Platform.current == .windows
|
||
? [SourceKitLSPOptions()] : [SourceKitLSPOptions(), .forTestingExperimentalSwiftPMBuildServer]
|
||
)
|
||
func testBasicSwiftArgs(options: SourceKitLSPOptions) async throws {
|
||
try await withTestScratchDir { tempDir in
|
||
try FileManager.default.createFiles(
|
||
root: tempDir,
|
||
files: [
|
||
"pkg/Sources/lib/a.swift": "",
|
||
"pkg/Package.swift": """
|
||
// swift-tools-version:4.2
|
||
import PackageDescription
|
||
let package = Package(
|
||
name: "a",
|
||
targets: [.target(name: "lib")]
|
||
)
|
||
""",
|
||
]
|
||
)
|
||
let packageRoot = try tempDir.appending(component: "pkg").realpath
|
||
let buildServerManager = await BuildServerManager(
|
||
buildServerSpec: .swiftpmSpec(for: packageRoot),
|
||
toolchainRegistry: .forTesting,
|
||
options: options,
|
||
connectionToClient: DummyBuildServerManagerConnectionToClient(),
|
||
buildServerHooks: BuildServerHooks(),
|
||
createMainFilesProvider: { _, _ in nil }
|
||
)
|
||
await buildServerManager.waitForUpToDateBuildGraph()
|
||
|
||
let aswift =
|
||
packageRoot
|
||
.appending(components: "Sources", "lib", "a.swift")
|
||
let build = try await buildPath(root: packageRoot, platform: hostTriple.platformBuildPathComponent)
|
||
|
||
_ = try #require(await buildServerManager.initializationData?.indexDatabasePath)
|
||
_ = try #require(await buildServerManager.initializationData?.indexStorePath)
|
||
let arguments = try #require(
|
||
await buildServerManager.buildSettingsInferredFromMainFile(
|
||
for: DocumentURI(aswift),
|
||
language: .swift,
|
||
fallbackAfterTimeout: false
|
||
)
|
||
).compilerArguments
|
||
|
||
expectArgumentsContain("-module-name", "lib", arguments: arguments)
|
||
expectArgumentsContain("-parse-as-library", arguments: arguments)
|
||
|
||
expectArgumentsContain("-target", arguments: arguments) // Only one!
|
||
#if os(macOS)
|
||
let versionString = PackageModel.Platform.macOS.oldestSupportedVersion.versionString
|
||
if options.swiftPMOrDefault.buildSystem == .swiftbuild {
|
||
expectArgumentsContain(
|
||
"-target",
|
||
// Account for differences in macOS naming canonicalization
|
||
try await hostTriple.tripleString(forPlatformVersion: versionString).replacing("macosx", with: "macos"),
|
||
arguments: arguments
|
||
)
|
||
} else {
|
||
expectArgumentsContain(
|
||
"-target",
|
||
try await hostTriple.tripleString(forPlatformVersion: versionString),
|
||
arguments: arguments
|
||
)
|
||
}
|
||
expectArgumentsContain(
|
||
"-sdk",
|
||
arguments: arguments,
|
||
allowMultiple: options.swiftPMOrDefault.buildSystem == .swiftbuild
|
||
)
|
||
expectArgumentsContain("-F", arguments: arguments, allowMultiple: true)
|
||
#else
|
||
expectArgumentsContain("-target", try await hostTriple.tripleString, arguments: arguments)
|
||
#endif
|
||
|
||
if options.swiftPMOrDefault.buildSystem != .swiftbuild {
|
||
// Swift Build and the native build system setup search paths differently. We deliberately avoid testing implementation details of Swift Build here.
|
||
expectArgumentsContain("-I", try build.appending(component: "Modules").filePath, arguments: arguments)
|
||
}
|
||
|
||
expectArgumentsContain(try aswift.filePath, arguments: arguments)
|
||
}
|
||
}
|
||
|
||
@Test
|
||
func testCompilerArgumentsForFileThatContainsPlusCharacterURLEncoded() async throws {
|
||
try await withTestScratchDir { tempDir in
|
||
try FileManager.default.createFiles(
|
||
root: tempDir,
|
||
files: [
|
||
"pkg/Sources/lib/a.swift": "",
|
||
"pkg/Sources/lib/a+something.swift": "",
|
||
"pkg/Package.swift": """
|
||
// swift-tools-version:4.2
|
||
import PackageDescription
|
||
let package = Package(
|
||
name: "a",
|
||
targets: [.target(name: "lib")]
|
||
)
|
||
""",
|
||
]
|
||
)
|
||
let packageRoot = try tempDir.appending(component: "pkg").realpath
|
||
let buildServerManager = await BuildServerManager(
|
||
buildServerSpec: .swiftpmSpec(for: packageRoot),
|
||
toolchainRegistry: .forTesting,
|
||
options: SourceKitLSPOptions(),
|
||
connectionToClient: DummyBuildServerManagerConnectionToClient(),
|
||
buildServerHooks: BuildServerHooks(),
|
||
createMainFilesProvider: { _, _ in nil }
|
||
)
|
||
await buildServerManager.waitForUpToDateBuildGraph()
|
||
|
||
let aPlusSomething =
|
||
packageRoot
|
||
.appending(components: "Sources", "lib", "a+something.swift")
|
||
|
||
_ = try #require(await buildServerManager.initializationData?.indexStorePath)
|
||
let pathWithPlusEscaped = "\(try aPlusSomething.filePath.replacing("+", with: "%2B"))"
|
||
#if os(Windows)
|
||
let urlWithPlusEscaped = try #require(URL(string: "file:///\(pathWithPlusEscaped)"))
|
||
#else
|
||
let urlWithPlusEscaped = try #require(URL(string: "file://\(pathWithPlusEscaped)"))
|
||
#endif
|
||
let arguments = try #require(
|
||
await buildServerManager.buildSettingsInferredFromMainFile(
|
||
for: DocumentURI(urlWithPlusEscaped),
|
||
language: .swift,
|
||
fallbackAfterTimeout: false
|
||
)
|
||
)
|
||
.compilerArguments
|
||
|
||
// Check that we have both source files in the compiler arguments, which means that we didn't compute the compiler
|
||
// arguments for a+something.swift using substitute arguments from a.swift.
|
||
#expect(
|
||
try arguments.contains(aPlusSomething.filePath),
|
||
"Compiler arguments do not contain a+something.swift: \(arguments)"
|
||
)
|
||
#expect(
|
||
try arguments.contains(
|
||
packageRoot.appending(components: "Sources", "lib", "a.swift")
|
||
.filePath
|
||
),
|
||
"Compiler arguments do not contain a.swift: \(arguments)"
|
||
)
|
||
}
|
||
}
|
||
|
||
@Test
|
||
func testBuildSetup() async throws {
|
||
try await withTestScratchDir { tempDir in
|
||
try FileManager.default.createFiles(
|
||
root: tempDir,
|
||
files: [
|
||
"pkg/Sources/lib/a.swift": "",
|
||
"pkg/Package.swift": """
|
||
// swift-tools-version:4.2
|
||
import PackageDescription
|
||
let package = Package(
|
||
name: "a",
|
||
targets: [.target(name: "lib")]
|
||
)
|
||
""",
|
||
]
|
||
)
|
||
let packageRoot = tempDir.appending(component: "pkg")
|
||
|
||
let options = SourceKitLSPOptions.SwiftPMOptions(
|
||
configuration: .release,
|
||
scratchPath: try packageRoot.appending(component: "non_default_build_path").filePath,
|
||
cCompilerFlags: ["-m32"],
|
||
swiftCompilerFlags: ["-typecheck"]
|
||
)
|
||
|
||
let buildServerManager = await BuildServerManager(
|
||
buildServerSpec: .swiftpmSpec(for: packageRoot),
|
||
toolchainRegistry: .forTesting,
|
||
options: SourceKitLSPOptions(swiftPM: options),
|
||
connectionToClient: DummyBuildServerManagerConnectionToClient(),
|
||
buildServerHooks: BuildServerHooks(),
|
||
createMainFilesProvider: { _, _ in nil }
|
||
)
|
||
await buildServerManager.waitForUpToDateBuildGraph()
|
||
|
||
let aswift =
|
||
packageRoot
|
||
.appending(components: "Sources", "lib", "a.swift")
|
||
|
||
let arguments = try #require(
|
||
await buildServerManager.buildSettingsInferredFromMainFile(
|
||
for: DocumentURI(aswift),
|
||
language: .swift,
|
||
fallbackAfterTimeout: false
|
||
)
|
||
).compilerArguments
|
||
|
||
expectArgumentsContain("-typecheck", arguments: arguments)
|
||
expectArgumentsContain("-Xcc", "-m32", arguments: arguments)
|
||
expectArgumentsContain("-O", arguments: arguments)
|
||
}
|
||
}
|
||
|
||
@Test
|
||
func testManifestArgs() async throws {
|
||
try await withTestScratchDir { tempDir in
|
||
try FileManager.default.createFiles(
|
||
root: tempDir,
|
||
files: [
|
||
"pkg/Sources/lib/a.swift": "",
|
||
"pkg/Package.swift": """
|
||
// swift-tools-version:4.2
|
||
import PackageDescription
|
||
let package = Package(
|
||
name: "a",
|
||
targets: [.target(name: "lib")]
|
||
)
|
||
""",
|
||
]
|
||
)
|
||
let packageRoot = tempDir.appending(component: "pkg")
|
||
let buildServerManager = await BuildServerManager(
|
||
buildServerSpec: .swiftpmSpec(for: packageRoot),
|
||
toolchainRegistry: .forTesting,
|
||
options: SourceKitLSPOptions(),
|
||
connectionToClient: DummyBuildServerManagerConnectionToClient(),
|
||
buildServerHooks: BuildServerHooks(),
|
||
createMainFilesProvider: { _, _ in nil }
|
||
)
|
||
await buildServerManager.waitForUpToDateBuildGraph()
|
||
|
||
let source = try packageRoot.appending(component: "Package.swift").realpath
|
||
let arguments = try #require(
|
||
await buildServerManager.buildSettingsInferredFromMainFile(
|
||
for: DocumentURI(source),
|
||
language: .swift,
|
||
fallbackAfterTimeout: false
|
||
)
|
||
).compilerArguments
|
||
|
||
expectArgumentsContain("-swift-version", "4.2", arguments: arguments)
|
||
expectArgumentsContain(try source.filePath, arguments: arguments)
|
||
}
|
||
}
|
||
|
||
@Test
|
||
func testMultiFileSwift() async throws {
|
||
try await withTestScratchDir { tempDir in
|
||
try FileManager.default.createFiles(
|
||
root: tempDir,
|
||
files: [
|
||
"pkg/Sources/lib/a.swift": "",
|
||
"pkg/Sources/lib/b.swift": "",
|
||
"pkg/Package.swift": """
|
||
// swift-tools-version:4.2
|
||
import PackageDescription
|
||
let package = Package(name: "a",
|
||
targets: [.target(name: "lib")]
|
||
)
|
||
""",
|
||
]
|
||
)
|
||
let packageRoot = try tempDir.appending(component: "pkg").realpath
|
||
let buildServerManager = await BuildServerManager(
|
||
buildServerSpec: .swiftpmSpec(for: packageRoot),
|
||
toolchainRegistry: .forTesting,
|
||
options: SourceKitLSPOptions(),
|
||
connectionToClient: DummyBuildServerManagerConnectionToClient(),
|
||
buildServerHooks: BuildServerHooks(),
|
||
createMainFilesProvider: { _, _ in nil }
|
||
)
|
||
await buildServerManager.waitForUpToDateBuildGraph()
|
||
|
||
let aswift =
|
||
packageRoot
|
||
.appending(components: "Sources", "lib", "a.swift")
|
||
let bswift =
|
||
packageRoot
|
||
.appending(components: "Sources", "lib", "b.swift")
|
||
|
||
let argumentsA = try #require(
|
||
await buildServerManager.buildSettingsInferredFromMainFile(
|
||
for: DocumentURI(aswift),
|
||
language: .swift,
|
||
fallbackAfterTimeout: false
|
||
)
|
||
).compilerArguments
|
||
expectArgumentsContain(try aswift.filePath, arguments: argumentsA)
|
||
expectArgumentsContain(try bswift.filePath, arguments: argumentsA)
|
||
let argumentsB = try #require(
|
||
await buildServerManager.buildSettingsInferredFromMainFile(
|
||
for: DocumentURI(aswift),
|
||
language: .swift,
|
||
fallbackAfterTimeout: false
|
||
)
|
||
).compilerArguments
|
||
expectArgumentsContain(try aswift.filePath, arguments: argumentsB)
|
||
expectArgumentsContain(try bswift.filePath, arguments: argumentsB)
|
||
}
|
||
}
|
||
|
||
@Test
|
||
func testMultiTargetSwift() async throws {
|
||
try await withTestScratchDir { tempDir in
|
||
try FileManager.default.createFiles(
|
||
root: tempDir,
|
||
files: [
|
||
"pkg/Sources/libA/a.swift": "",
|
||
"pkg/Sources/libB/b.swift": "",
|
||
"pkg/Sources/libC/include/libC.h": "",
|
||
"pkg/Sources/libC/libC.c": "",
|
||
"pkg/Package.swift": """
|
||
// swift-tools-version:4.2
|
||
import PackageDescription
|
||
let package = Package(
|
||
name: "a",
|
||
targets: [
|
||
.target(name: "libA", dependencies: ["libB", "libC"]),
|
||
.target(name: "libB"),
|
||
.target(name: "libC"),
|
||
]
|
||
)
|
||
""",
|
||
]
|
||
)
|
||
let packageRoot = try tempDir.appending(component: "pkg").realpath
|
||
let buildServerManager = await BuildServerManager(
|
||
buildServerSpec: .swiftpmSpec(for: packageRoot),
|
||
toolchainRegistry: .forTesting,
|
||
options: SourceKitLSPOptions(),
|
||
connectionToClient: DummyBuildServerManagerConnectionToClient(),
|
||
buildServerHooks: BuildServerHooks(),
|
||
createMainFilesProvider: { _, _ in nil }
|
||
)
|
||
await buildServerManager.waitForUpToDateBuildGraph()
|
||
|
||
let aswift =
|
||
packageRoot
|
||
.appending(components: "Sources", "libA", "a.swift")
|
||
let bswift =
|
||
packageRoot
|
||
.appending(components: "Sources", "libB", "b.swift")
|
||
let arguments = try #require(
|
||
await buildServerManager.buildSettingsInferredFromMainFile(
|
||
for: DocumentURI(aswift),
|
||
language: .swift,
|
||
fallbackAfterTimeout: false
|
||
)
|
||
).compilerArguments
|
||
expectArgumentsContain(try aswift.filePath, arguments: arguments)
|
||
expectArgumentsDoNotContain(try bswift.filePath, arguments: arguments)
|
||
expectArgumentsContain(
|
||
"-Xcc",
|
||
"-I",
|
||
"-Xcc",
|
||
try packageRoot
|
||
.appending(components: "Sources", "libC", "include")
|
||
.filePath,
|
||
arguments: arguments
|
||
)
|
||
|
||
let argumentsB = try #require(
|
||
await buildServerManager.buildSettingsInferredFromMainFile(
|
||
for: DocumentURI(bswift),
|
||
language: .swift,
|
||
fallbackAfterTimeout: false
|
||
)
|
||
).compilerArguments
|
||
expectArgumentsContain(try bswift.filePath, arguments: argumentsB)
|
||
expectArgumentsDoNotContain(try aswift.filePath, arguments: argumentsB)
|
||
expectArgumentsDoNotContain(
|
||
"-I",
|
||
try packageRoot
|
||
.appending(components: "Sources", "libC", "include")
|
||
.filePath,
|
||
arguments: argumentsB
|
||
)
|
||
}
|
||
}
|
||
|
||
@Test
|
||
func testUnknownFile() async throws {
|
||
try await withTestScratchDir { tempDir in
|
||
try FileManager.default.createFiles(
|
||
root: tempDir,
|
||
files: [
|
||
"pkg/Sources/libA/a.swift": "",
|
||
"pkg/Sources/libB/b.swift": "",
|
||
"pkg/Package.swift": """
|
||
// swift-tools-version:4.2
|
||
import PackageDescription
|
||
let package = Package(
|
||
name: "a",
|
||
targets: [.target(name: "libA")]
|
||
)
|
||
""",
|
||
]
|
||
)
|
||
let packageRoot = tempDir.appending(component: "pkg")
|
||
let buildServerManager = await BuildServerManager(
|
||
buildServerSpec: .swiftpmSpec(for: packageRoot),
|
||
toolchainRegistry: .forTesting,
|
||
options: SourceKitLSPOptions(),
|
||
connectionToClient: DummyBuildServerManagerConnectionToClient(),
|
||
buildServerHooks: BuildServerHooks(),
|
||
createMainFilesProvider: { _, _ in nil }
|
||
)
|
||
await buildServerManager.waitForUpToDateBuildGraph()
|
||
|
||
let aswift =
|
||
packageRoot
|
||
.appending(components: "Sources", "libA", "a.swift")
|
||
let bswift =
|
||
packageRoot
|
||
.appending(components: "Sources", "libB", "b.swift")
|
||
_ = try #require(
|
||
await buildServerManager.buildSettingsInferredFromMainFile(
|
||
for: DocumentURI(aswift),
|
||
language: .swift,
|
||
fallbackAfterTimeout: false
|
||
)
|
||
)
|
||
#expect(
|
||
await buildServerManager.buildSettingsInferredFromMainFile(
|
||
for: DocumentURI(bswift),
|
||
language: .swift,
|
||
fallbackAfterTimeout: false
|
||
)?.isFallback == true
|
||
)
|
||
#expect(
|
||
await buildServerManager.buildSettingsInferredFromMainFile(
|
||
for: DocumentURI(URL(string: "https://www.apple.com")!),
|
||
language: .swift,
|
||
fallbackAfterTimeout: false
|
||
)?.isFallback == true
|
||
)
|
||
}
|
||
}
|
||
|
||
@Test
|
||
func testBasicCXXArgs() async throws {
|
||
try await withTestScratchDir { tempDir in
|
||
try FileManager.default.createFiles(
|
||
root: tempDir,
|
||
files: [
|
||
"pkg/Sources/lib/a.cpp": "",
|
||
"pkg/Sources/lib/b.cpp": "",
|
||
"pkg/Sources/lib/include/a.h": "",
|
||
"pkg/Package.swift": """
|
||
// swift-tools-version:4.2
|
||
import PackageDescription
|
||
let package = Package(
|
||
name: "a",
|
||
targets: [.target(name: "lib")],
|
||
cxxLanguageStandard: .cxx14
|
||
)
|
||
""",
|
||
]
|
||
)
|
||
let packageRoot = try tempDir.appending(component: "pkg").realpath
|
||
let buildServerManager = await BuildServerManager(
|
||
buildServerSpec: .swiftpmSpec(for: packageRoot),
|
||
toolchainRegistry: .forTesting,
|
||
options: SourceKitLSPOptions(),
|
||
connectionToClient: DummyBuildServerManagerConnectionToClient(),
|
||
buildServerHooks: BuildServerHooks(),
|
||
createMainFilesProvider: { _, _ in nil }
|
||
)
|
||
await buildServerManager.waitForUpToDateBuildGraph()
|
||
|
||
let acxx =
|
||
packageRoot
|
||
.appending(components: "Sources", "lib", "a.cpp")
|
||
let bcxx =
|
||
packageRoot
|
||
.appending(components: "Sources", "lib", "b.cpp")
|
||
let header =
|
||
packageRoot
|
||
.appending(components: "Sources", "lib", "include", "a.h")
|
||
let build = buildPath(root: packageRoot, platform: try await hostTriple.platformBuildPathComponent)
|
||
|
||
_ = try #require(await buildServerManager.initializationData?.indexStorePath)
|
||
|
||
for file in [acxx, header] {
|
||
let args = try #require(
|
||
await buildServerManager.buildSettingsInferredFromMainFile(
|
||
for: DocumentURI(file),
|
||
language: .cpp,
|
||
fallbackAfterTimeout: false
|
||
)
|
||
).compilerArguments
|
||
|
||
expectArgumentsContain("-std=c++14", arguments: args)
|
||
|
||
expectArgumentsDoNotContain("-arch", arguments: args)
|
||
expectArgumentsContain("-target", arguments: args) // Only one!
|
||
#if os(macOS)
|
||
let versionString = PackageModel.Platform.macOS.oldestSupportedVersion.versionString
|
||
expectArgumentsContain(
|
||
"-target",
|
||
try await hostTriple.tripleString(forPlatformVersion: versionString),
|
||
arguments: args
|
||
)
|
||
expectArgumentsContain("-isysroot", arguments: args)
|
||
expectArgumentsContain("-F", arguments: args, allowMultiple: true)
|
||
#else
|
||
expectArgumentsContain("-target", try await hostTriple.tripleString, arguments: args)
|
||
#endif
|
||
|
||
expectArgumentsContain(
|
||
"-I",
|
||
try packageRoot
|
||
.appending(components: "Sources", "lib", "include")
|
||
.filePath,
|
||
arguments: args
|
||
)
|
||
expectArgumentsDoNotContain("-I", try build.filePath, arguments: args)
|
||
expectArgumentsDoNotContain(try bcxx.filePath, arguments: args)
|
||
|
||
URL(fileURLWithPath: try build.appending(components: "lib.build", "a.cpp.o").filePath)
|
||
.withUnsafeFileSystemRepresentation {
|
||
expectArgumentsContain("-o", String(cString: $0!), arguments: args)
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
@Test
|
||
func testDeploymentTargetSwift() async throws {
|
||
try await withTestScratchDir { tempDir in
|
||
try FileManager.default.createFiles(
|
||
root: tempDir,
|
||
files: [
|
||
"pkg/Sources/lib/a.swift": "",
|
||
"pkg/Package.swift": """
|
||
// swift-tools-version:5.0
|
||
import PackageDescription
|
||
let package = Package(name: "a",
|
||
platforms: [.macOS(.v10_13)],
|
||
targets: [.target(name: "lib")]
|
||
)
|
||
""",
|
||
]
|
||
)
|
||
let packageRoot = tempDir.appending(component: "pkg")
|
||
let buildServerManager = await BuildServerManager(
|
||
buildServerSpec: .swiftpmSpec(for: packageRoot),
|
||
toolchainRegistry: .forTesting,
|
||
options: SourceKitLSPOptions(),
|
||
connectionToClient: DummyBuildServerManagerConnectionToClient(),
|
||
buildServerHooks: BuildServerHooks(),
|
||
createMainFilesProvider: { _, _ in nil }
|
||
)
|
||
await buildServerManager.waitForUpToDateBuildGraph()
|
||
|
||
let aswift =
|
||
packageRoot
|
||
.appending(components: "Sources", "lib", "a.swift")
|
||
let arguments = try #require(
|
||
await buildServerManager.buildSettingsInferredFromMainFile(
|
||
for: DocumentURI(aswift),
|
||
language: .swift,
|
||
fallbackAfterTimeout: false
|
||
)
|
||
).compilerArguments
|
||
expectArgumentsContain("-target", arguments: arguments) // Only one!
|
||
|
||
#if os(macOS)
|
||
try await expectArgumentsContain(
|
||
"-target",
|
||
hostTriple.tripleString(forPlatformVersion: "10.13"),
|
||
arguments: arguments
|
||
)
|
||
#else
|
||
expectArgumentsContain("-target", try await hostTriple.tripleString, arguments: arguments)
|
||
#endif
|
||
}
|
||
}
|
||
|
||
@Test
|
||
func testSymlinkInWorkspaceSwift() async throws {
|
||
try await withTestScratchDir { tempDir in
|
||
try FileManager.default.createFiles(
|
||
root: tempDir,
|
||
files: [
|
||
"pkg_real/Sources/lib/a.swift": "",
|
||
"pkg_real/Package.swift": """
|
||
// swift-tools-version:4.2
|
||
import PackageDescription
|
||
let package = Package(
|
||
name: "a",
|
||
targets: [.target(name: "lib")]
|
||
)
|
||
""",
|
||
]
|
||
)
|
||
let packageRoot = tempDir.appending(component: "pkg")
|
||
|
||
try FileManager.default.createSymbolicLink(
|
||
at: URL(fileURLWithPath: packageRoot.filePath),
|
||
withDestinationURL: URL(fileURLWithPath: tempDir.appending(component: "pkg_real").filePath)
|
||
)
|
||
|
||
let buildServerSpec = try #require(
|
||
SwiftPMBuildServer.searchForConfig(in: packageRoot, options: await .testDefault())
|
||
)
|
||
let buildServerManager = await BuildServerManager(
|
||
buildServerSpec: buildServerSpec,
|
||
toolchainRegistry: .forTesting,
|
||
options: SourceKitLSPOptions(),
|
||
connectionToClient: DummyBuildServerManagerConnectionToClient(),
|
||
buildServerHooks: BuildServerHooks(),
|
||
createMainFilesProvider: { _, _ in nil }
|
||
)
|
||
await buildServerManager.waitForUpToDateBuildGraph()
|
||
|
||
let aswiftSymlink =
|
||
packageRoot
|
||
.appending(components: "Sources", "lib", "a.swift")
|
||
let aswiftReal = try aswiftSymlink.realpath
|
||
let manifest = packageRoot.appending(component: "Package.swift")
|
||
|
||
let argumentsFromSymlink = try #require(
|
||
await buildServerManager.buildSettingsInferredFromMainFile(
|
||
for: DocumentURI(aswiftSymlink),
|
||
language: .swift,
|
||
fallbackAfterTimeout: false
|
||
)
|
||
).compilerArguments
|
||
|
||
// We opened the project from a symlink. The realpath isn't part of the project and we should thus not receive
|
||
// build settings for it.
|
||
#expect(
|
||
try #require(
|
||
await buildServerManager.buildSettingsInferredFromMainFile(
|
||
for: DocumentURI(aswiftReal),
|
||
language: .swift,
|
||
fallbackAfterTimeout: false
|
||
)
|
||
).isFallback
|
||
)
|
||
expectArgumentsContain(try aswiftSymlink.filePath, arguments: argumentsFromSymlink)
|
||
expectArgumentsDoNotContain(try aswiftReal.filePath, arguments: argumentsFromSymlink)
|
||
|
||
let argsManifest = try #require(
|
||
await buildServerManager.buildSettingsInferredFromMainFile(
|
||
for: DocumentURI(manifest),
|
||
language: .swift,
|
||
fallbackAfterTimeout: false
|
||
)
|
||
).compilerArguments
|
||
|
||
expectArgumentsContain(try manifest.filePath, arguments: argsManifest)
|
||
expectArgumentsDoNotContain(try manifest.realpath.filePath, arguments: argsManifest)
|
||
}
|
||
}
|
||
|
||
@Test
|
||
func testSymlinkInWorkspaceCXX() async throws {
|
||
try await withTestScratchDir { tempDir in
|
||
try FileManager.default.createFiles(
|
||
root: tempDir,
|
||
files: [
|
||
"pkg_real/Sources/lib/a.cpp": "",
|
||
"pkg_real/Sources/lib/b.cpp": "",
|
||
"pkg_real/Sources/lib/include/a.h": "",
|
||
"pkg_real/Package.swift": """
|
||
// swift-tools-version:4.2
|
||
import PackageDescription
|
||
let package = Package(
|
||
name: "a",
|
||
targets: [.target(name: "lib")],
|
||
cxxLanguageStandard: .cxx14
|
||
)
|
||
""",
|
||
]
|
||
)
|
||
|
||
let acpp = ["Sources", "lib", "a.cpp"]
|
||
let ah = ["Sources", "lib", "include", "a.h"]
|
||
|
||
let realRoot = tempDir.appending(component: "pkg_real")
|
||
let symlinkRoot = tempDir.appending(component: "pkg")
|
||
|
||
try FileManager.default.createSymbolicLink(
|
||
at: URL(fileURLWithPath: symlinkRoot.filePath),
|
||
withDestinationURL: URL(fileURLWithPath: tempDir.appending(component: "pkg_real").filePath)
|
||
)
|
||
|
||
let buildServerSpec = try #require(
|
||
SwiftPMBuildServer.searchForConfig(in: symlinkRoot, options: await .testDefault())
|
||
)
|
||
let buildServerManager = await BuildServerManager(
|
||
buildServerSpec: buildServerSpec,
|
||
toolchainRegistry: .forTesting,
|
||
options: SourceKitLSPOptions(),
|
||
connectionToClient: DummyBuildServerManagerConnectionToClient(),
|
||
buildServerHooks: BuildServerHooks(),
|
||
createMainFilesProvider: { _, _ in nil }
|
||
)
|
||
await buildServerManager.waitForUpToDateBuildGraph()
|
||
|
||
for file in [acpp, ah] {
|
||
let args = try #require(
|
||
await buildServerManager.buildSettingsInferredFromMainFile(
|
||
for: DocumentURI(symlinkRoot.appending(components: file)),
|
||
language: .cpp,
|
||
fallbackAfterTimeout: false
|
||
)?
|
||
.compilerArguments
|
||
)
|
||
expectArgumentsDoNotContain(try realRoot.appending(components: file).filePath, arguments: args)
|
||
expectArgumentsContain(try symlinkRoot.appending(components: file).filePath, arguments: args)
|
||
}
|
||
}
|
||
}
|
||
|
||
@Test
|
||
func testSwiftDerivedSources() async throws {
|
||
try await withTestScratchDir { tempDir in
|
||
try FileManager.default.createFiles(
|
||
root: tempDir,
|
||
files: [
|
||
"pkg/Sources/lib/a.swift": "",
|
||
"pkg/Sources/lib/a.txt": "",
|
||
"pkg/Package.swift": """
|
||
// swift-tools-version:5.3
|
||
import PackageDescription
|
||
let package = Package(
|
||
name: "a",
|
||
targets: [.target(name: "lib", resources: [.copy("a.txt")])]
|
||
)
|
||
""",
|
||
]
|
||
)
|
||
let packageRoot = try tempDir.appending(component: "pkg").realpath
|
||
let buildServerManager = await BuildServerManager(
|
||
buildServerSpec: .swiftpmSpec(for: packageRoot),
|
||
toolchainRegistry: .forTesting,
|
||
options: SourceKitLSPOptions(),
|
||
connectionToClient: DummyBuildServerManagerConnectionToClient(),
|
||
buildServerHooks: BuildServerHooks(),
|
||
createMainFilesProvider: { _, _ in nil }
|
||
)
|
||
await buildServerManager.waitForUpToDateBuildGraph()
|
||
|
||
let aswift =
|
||
packageRoot
|
||
.appending(components: "Sources", "lib", "a.swift")
|
||
let arguments = try #require(
|
||
await buildServerManager.buildSettingsInferredFromMainFile(
|
||
for: DocumentURI(aswift),
|
||
language: .swift,
|
||
fallbackAfterTimeout: false
|
||
)
|
||
)
|
||
.compilerArguments
|
||
expectArgumentsContain(try aswift.filePath, arguments: arguments)
|
||
_ = try #require(
|
||
arguments.firstIndex(where: {
|
||
$0.hasSuffix(".swift") && $0.contains("DerivedSources")
|
||
}),
|
||
"missing resource_bundle_accessor.swift from \(arguments)"
|
||
)
|
||
}
|
||
}
|
||
|
||
@Test
|
||
func testNestedInvalidPackageSwift() async throws {
|
||
try await withTestScratchDir { tempDir in
|
||
try FileManager.default.createFiles(
|
||
root: tempDir,
|
||
files: [
|
||
"pkg/Sources/lib/Package.swift": "// not a valid package",
|
||
"pkg/Package.swift": """
|
||
// swift-tools-version:4.2
|
||
import PackageDescription
|
||
let package = Package(
|
||
name: "a",
|
||
targets: [.target(name: "lib")]
|
||
)
|
||
""",
|
||
]
|
||
)
|
||
let workspaceRoot =
|
||
tempDir
|
||
.appending(components: "pkg", "Sources", "lib")
|
||
|
||
let buildServerSpec = SwiftPMBuildServer.searchForConfig(in: workspaceRoot, options: try await .testDefault())
|
||
#expect(buildServerSpec == nil)
|
||
}
|
||
}
|
||
|
||
@Test
|
||
func testPluginArgs() async throws {
|
||
try await withTestScratchDir { tempDir in
|
||
try FileManager.default.createFiles(
|
||
root: tempDir,
|
||
files: [
|
||
"pkg/Plugins/MyPlugin/a.swift": "",
|
||
"pkg/Sources/lib/lib.swift": "",
|
||
"pkg/Package.swift": """
|
||
// swift-tools-version:5.7
|
||
import PackageDescription
|
||
let package = Package(
|
||
name: "a",
|
||
targets: [
|
||
.target(name: "lib"),
|
||
.plugin(name: "MyPlugin", capability: .buildTool)
|
||
]
|
||
)
|
||
""",
|
||
]
|
||
)
|
||
let packageRoot = tempDir.appending(component: "pkg")
|
||
let buildServerManager = await BuildServerManager(
|
||
buildServerSpec: .swiftpmSpec(for: packageRoot),
|
||
toolchainRegistry: .forTesting,
|
||
options: SourceKitLSPOptions(),
|
||
connectionToClient: DummyBuildServerManagerConnectionToClient(),
|
||
buildServerHooks: BuildServerHooks(),
|
||
createMainFilesProvider: { _, _ in nil }
|
||
)
|
||
await buildServerManager.waitForUpToDateBuildGraph()
|
||
|
||
let aswift =
|
||
packageRoot
|
||
.appending(components: "Plugins", "MyPlugin", "a.swift")
|
||
|
||
_ = try #require(await buildServerManager.initializationData?.indexStorePath)
|
||
let arguments = try #require(
|
||
await buildServerManager.buildSettingsInferredFromMainFile(
|
||
for: DocumentURI(aswift),
|
||
language: .swift,
|
||
fallbackAfterTimeout: false
|
||
)
|
||
).compilerArguments
|
||
|
||
// Plugins get compiled with the same compiler arguments as the package manifest
|
||
expectArgumentsContain("-package-description-version", "5.7.0", arguments: arguments)
|
||
expectArgumentsContain(try aswift.filePath, arguments: arguments)
|
||
}
|
||
}
|
||
|
||
@Test
|
||
func testPackageWithDependencyWithoutResolving() async throws {
|
||
// This package has a dependency but we haven't run `swift package resolve`. We don't want to resolve packages from
|
||
// SourceKit-LSP because it has side-effects to the build directory.
|
||
// But even without the dependency checked out, we should be able to create a SwiftPMBuildServer and retrieve the
|
||
// existing source files.
|
||
let project = try await SwiftPMTestProject(
|
||
files: [
|
||
"Tests/PackageTests/PackageTests.swift": """
|
||
import Testing
|
||
|
||
1️⃣@Test func topLevelTestPassing() {}2️⃣
|
||
"""
|
||
],
|
||
manifest: """
|
||
let package = Package(
|
||
name: "MyLibrary",
|
||
dependencies: [.package(url: "https://github.com/swiftlang/swift-testing.git", branch: "main")],
|
||
targets: [
|
||
.testTarget(name: "PackageTests", dependencies: [.product(name: "Testing", package: "swift-testing")]),
|
||
]
|
||
)
|
||
"""
|
||
)
|
||
|
||
let tests = try await project.testClient.send(WorkspaceTestsRequest())
|
||
#expect(
|
||
tests == [
|
||
TestItem(
|
||
id: "PackageTests.topLevelTestPassing()",
|
||
label: "topLevelTestPassing()",
|
||
disabled: false,
|
||
style: "swift-testing",
|
||
location: try project.location(from: "1️⃣", to: "2️⃣", in: "PackageTests.swift"),
|
||
children: [],
|
||
tags: []
|
||
)
|
||
]
|
||
)
|
||
}
|
||
|
||
@Test
|
||
func testPackageLoadingWorkDoneProgress() async throws {
|
||
let didReceiveWorkDoneProgressNotification = WrappedSemaphore(name: "work done progress received")
|
||
let project = try await SwiftPMTestProject(
|
||
files: [
|
||
"MyLibrary/Test.swift": ""
|
||
],
|
||
capabilities: ClientCapabilities(window: WindowClientCapabilities(workDoneProgress: true)),
|
||
hooks: Hooks(
|
||
buildServerHooks: BuildServerHooks(
|
||
swiftPMTestHooks: SwiftPMTestHooks(reloadPackageDidStart: {
|
||
didReceiveWorkDoneProgressNotification.waitOrXCTFail()
|
||
})
|
||
)
|
||
),
|
||
pollIndex: false,
|
||
preInitialization: { testClient in
|
||
testClient.handleMultipleRequests { (request: CreateWorkDoneProgressRequest) in
|
||
return VoidResponse()
|
||
}
|
||
}
|
||
)
|
||
let begin = try await project.testClient.nextNotification(ofType: WorkDoneProgress.self)
|
||
#expect(begin.value == .begin(WorkDoneProgressBegin(title: "SourceKit-LSP: Reloading Package")))
|
||
didReceiveWorkDoneProgressNotification.signal()
|
||
|
||
let end = try await project.testClient.nextNotification(ofType: WorkDoneProgress.self)
|
||
#expect(end.token == begin.token)
|
||
#expect(end.value == .end(WorkDoneProgressEnd()))
|
||
}
|
||
|
||
@Test
|
||
func testBuildSettingsForVersionSpecificPackageManifest() async throws {
|
||
try await withTestScratchDir { tempDir in
|
||
try FileManager.default.createFiles(
|
||
root: tempDir,
|
||
files: [
|
||
"pkg/Sources/lib/a.swift": "",
|
||
"pkg/Package.swift": """
|
||
// swift-tools-version:4.2
|
||
import PackageDescription
|
||
let package = Package(
|
||
name: "a",
|
||
targets: [.target(name: "lib")]
|
||
)
|
||
""",
|
||
"pkg/Package@swift-5.8.swift": """
|
||
// swift-tools-version:4.2
|
||
import PackageDescription
|
||
let package = Package(
|
||
name: "a",
|
||
targets: [.target(name: "lib")]
|
||
)
|
||
""",
|
||
]
|
||
)
|
||
let packageRoot = try tempDir.appending(component: "pkg").realpath
|
||
let versionSpecificManifestURL = packageRoot.appending(component: "Package@swift-5.8.swift")
|
||
let buildServerManager = await BuildServerManager(
|
||
buildServerSpec: .swiftpmSpec(for: packageRoot),
|
||
toolchainRegistry: .forTesting,
|
||
options: SourceKitLSPOptions(),
|
||
connectionToClient: DummyBuildServerManagerConnectionToClient(),
|
||
buildServerHooks: BuildServerHooks(),
|
||
createMainFilesProvider: { _, _ in nil }
|
||
)
|
||
await buildServerManager.waitForUpToDateBuildGraph()
|
||
let settings = await buildServerManager.buildSettingsInferredFromMainFile(
|
||
for: DocumentURI(versionSpecificManifestURL),
|
||
language: .swift,
|
||
fallbackAfterTimeout: false
|
||
)
|
||
let compilerArgs = try #require(settings?.compilerArguments)
|
||
#expect(compilerArgs.contains("-package-description-version"))
|
||
#expect(compilerArgs.contains(try versionSpecificManifestURL.filePath))
|
||
}
|
||
}
|
||
|
||
@Test
|
||
func testBuildSettingsForInvalidManifest() async throws {
|
||
try await withTestScratchDir { tempDir in
|
||
try FileManager.default.createFiles(
|
||
root: tempDir,
|
||
files: [
|
||
"pkg/Sources/lib/a.swift": "",
|
||
"pkg/Package.swift": """
|
||
// swift-tools-version:5.1
|
||
import PackageDescription
|
||
""",
|
||
]
|
||
)
|
||
let packageRoot = try tempDir.appending(component: "pkg").realpath
|
||
let manifestURL = packageRoot.appending(component: "Package.swift")
|
||
let buildServerManager = await BuildServerManager(
|
||
buildServerSpec: .swiftpmSpec(for: packageRoot),
|
||
toolchainRegistry: .forTesting,
|
||
options: SourceKitLSPOptions(),
|
||
connectionToClient: DummyBuildServerManagerConnectionToClient(),
|
||
buildServerHooks: BuildServerHooks(),
|
||
createMainFilesProvider: { _, _ in nil }
|
||
)
|
||
await buildServerManager.waitForUpToDateBuildGraph()
|
||
let settings = await buildServerManager.buildSettingsInferredFromMainFile(
|
||
for: DocumentURI(manifestURL),
|
||
language: .swift,
|
||
fallbackAfterTimeout: false
|
||
)
|
||
let compilerArgs = try #require(settings?.compilerArguments)
|
||
expectArgumentsContain("-package-description-version", "5.1.0", arguments: compilerArgs)
|
||
#expect(compilerArgs.contains(try manifestURL.filePath))
|
||
}
|
||
}
|
||
|
||
@Test(
|
||
.enabled(if: Platform.current != .windows, "Toolsets are not working on Windows, see swift-package-manager#9438.")
|
||
)
|
||
func testToolsets() async throws {
|
||
let project = try await SwiftPMTestProject(
|
||
files: [
|
||
"Foo/foo.swift": """
|
||
import Bar
|
||
|
||
func foo() {
|
||
bar()
|
||
}
|
||
""",
|
||
"Bar/bar.swift": """
|
||
#if BAR
|
||
public func bar() {}
|
||
#endif
|
||
""",
|
||
"/toolset.json": """
|
||
{
|
||
"schemaVersion": "1.0",
|
||
"swiftCompiler": {
|
||
"extraCLIOptions": [
|
||
"-DBAR"
|
||
]
|
||
}
|
||
}
|
||
""",
|
||
"/.sourcekit-lsp/config.json": """
|
||
{
|
||
"swiftPM": {
|
||
"toolsets": ["toolset.json"]
|
||
}
|
||
}
|
||
""",
|
||
],
|
||
manifest: """
|
||
let package = Package(
|
||
name: "MyLibrary",
|
||
targets: [
|
||
.target(name: "Foo", dependencies: ["Bar"]),
|
||
.target(name: "Bar"),
|
||
]
|
||
)
|
||
""",
|
||
options: .testDefault(experimentalFeatures: [.sourceKitOptionsRequest]),
|
||
enableBackgroundIndexing: true,
|
||
)
|
||
|
||
let (uri, _) = try project.openDocument("foo.swift")
|
||
|
||
let options = try await project.testClient.send(
|
||
SourceKitOptionsRequest(
|
||
textDocument: TextDocumentIdentifier(uri),
|
||
prepareTarget: false,
|
||
allowFallbackSettings: false
|
||
)
|
||
)
|
||
#expect(options.compilerArguments.contains("-DBAR"))
|
||
|
||
let diagnostics = try #require(
|
||
await project.testClient.send(
|
||
DocumentDiagnosticsRequest(textDocument: TextDocumentIdentifier(uri))
|
||
).fullReport?.items
|
||
)
|
||
#expect(diagnostics.isEmpty)
|
||
}
|
||
}
|
||
|
||
private func expectArgumentsDoNotContain(
|
||
_ pattern: String...,
|
||
arguments: [String],
|
||
sourceLocation: SourceLocation = #_sourceLocation
|
||
) {
|
||
if let index = arguments.firstRange(of: pattern)?.startIndex {
|
||
Issue.record(
|
||
"not-pattern \(pattern) unexpectedly found at \(index) in arguments \(arguments)",
|
||
sourceLocation: sourceLocation
|
||
)
|
||
return
|
||
}
|
||
}
|
||
|
||
private func expectArgumentsContain(
|
||
_ pattern: String...,
|
||
arguments: [String],
|
||
allowMultiple: Bool = false,
|
||
sourceLocation: SourceLocation = #_sourceLocation
|
||
) {
|
||
guard let index = arguments.firstRange(of: pattern)?.startIndex else {
|
||
Issue.record("pattern \(pattern) not found in arguments \(arguments)", sourceLocation: sourceLocation)
|
||
return
|
||
}
|
||
|
||
if !allowMultiple, let index2 = arguments[(index + 1)...].firstRange(of: pattern)?.startIndex {
|
||
Issue.record(
|
||
"pattern \(pattern) found twice (\(index), \(index2)) in \(arguments)",
|
||
sourceLocation: sourceLocation
|
||
)
|
||
}
|
||
}
|
||
|
||
private func buildPath(
|
||
root: URL,
|
||
options: SourceKitLSPOptions.SwiftPMOptions = SourceKitLSPOptions.SwiftPMOptions(),
|
||
platform: String
|
||
) -> URL {
|
||
let buildPath =
|
||
if let scratchPath = options.scratchPath {
|
||
URL(fileURLWithPath: scratchPath)
|
||
} else {
|
||
root.appending(components: ".build", "index-build")
|
||
}
|
||
return buildPath.appending(components: platform, "\(options.configuration ?? .debug)")
|
||
}
|
||
|
||
fileprivate extension URL {
|
||
func appending(components: [String]) -> URL {
|
||
var result = self
|
||
for component in components {
|
||
result.appendPathComponent(component)
|
||
}
|
||
return result
|
||
}
|
||
}
|
||
|
||
fileprivate extension BuildServerSpec {
|
||
static func swiftpmSpec(for packageRoot: URL) -> BuildServerSpec {
|
||
return BuildServerSpec(
|
||
kind: .swiftPM,
|
||
projectRoot: packageRoot,
|
||
configPath: packageRoot.appending(component: "Package.swift")
|
||
)
|
||
}
|
||
}
|
||
|
||
#endif
|