mirror of
https://github.com/apple/sourcekit-lsp.git
synced 2026-03-02 18:23:24 +01:00
There were a few places that options only took place *after* determining
a build system, even though we have multiple that impact the search (eg.
`defaultBuildSystem` and `searchPaths`).
Additionally track project root and configuration paths separately, so
that when searching for implicit workspaces we can make sure to skip
creating duplicates.
(cherry picked from commit 0c896696c9)
431 lines
12 KiB
Swift
431 lines
12 KiB
Swift
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2018 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
import BuildServerProtocol
|
|
import BuildSystemIntegration
|
|
import LanguageServerProtocol
|
|
import LanguageServerProtocolExtensions
|
|
import SKTestSupport
|
|
import SwiftExtensions
|
|
import TSCExtensions
|
|
import XCTest
|
|
|
|
import struct TSCBasic.RelativePath
|
|
|
|
final class CompilationDatabaseTests: XCTestCase {
|
|
func testEncodeCompDBCommand() throws {
|
|
// Requires JSONEncoder.OutputFormatting.sortedKeys
|
|
func check(
|
|
_ cmd: CompilationDatabase.Command,
|
|
_ expected: String,
|
|
file: StaticString = #filePath,
|
|
line: UInt = #line
|
|
) throws {
|
|
let encoder = JSONEncoder()
|
|
encoder.outputFormatting.insert(.sortedKeys)
|
|
let encodedString = try String(data: encoder.encode(cmd), encoding: .utf8)
|
|
XCTAssertEqual(encodedString, expected, file: file, line: line)
|
|
}
|
|
|
|
try check(
|
|
.init(directory: "a", filename: "b", commandLine: [], output: "c"),
|
|
"""
|
|
{"arguments":[],"directory":"a","file":"b","output":"c"}
|
|
"""
|
|
)
|
|
try check(
|
|
.init(directory: "a", filename: "b", commandLine: ["c", "d"], output: nil),
|
|
"""
|
|
{"arguments":["c","d"],"directory":"a","file":"b"}
|
|
"""
|
|
)
|
|
}
|
|
|
|
func testDecodeCompDBCommand() throws {
|
|
func check(
|
|
_ str: String,
|
|
_ expected: CompilationDatabase.Command,
|
|
file: StaticString = #filePath,
|
|
line: UInt = #line
|
|
) throws {
|
|
let cmd = try JSONDecoder().decode(CompilationDatabase.Command.self, from: str.data(using: .utf8)!)
|
|
XCTAssertEqual(cmd, expected, file: file, line: line)
|
|
}
|
|
|
|
try check(
|
|
"""
|
|
{
|
|
"arguments" : [
|
|
|
|
],
|
|
"directory" : "a",
|
|
"file" : "b",
|
|
"output" : "c"
|
|
}
|
|
""",
|
|
.init(directory: "a", filename: "b", commandLine: [], output: "c")
|
|
)
|
|
try check(
|
|
"""
|
|
{
|
|
"arguments" : [
|
|
"c",
|
|
"d"
|
|
],
|
|
"directory" : "a",
|
|
"file" : "b"
|
|
}
|
|
""",
|
|
.init(directory: "a", filename: "b", commandLine: ["c", "d"], output: nil)
|
|
)
|
|
|
|
try check(
|
|
"""
|
|
{
|
|
"directory":"a",
|
|
"file":"b.cpp",
|
|
"command": "/usr/bin/clang++ -std=c++11 -DFOO b.cpp"
|
|
}
|
|
""",
|
|
.init(
|
|
directory: "a",
|
|
filename: "b.cpp",
|
|
commandLine: [
|
|
"/usr/bin/clang++",
|
|
"-std=c++11",
|
|
"-DFOO",
|
|
"b.cpp",
|
|
],
|
|
output: nil
|
|
)
|
|
)
|
|
|
|
XCTAssertThrowsError(
|
|
try JSONDecoder().decode(
|
|
CompilationDatabase.Command.self,
|
|
from: """
|
|
{"directory":"a","file":"b"}
|
|
""".data(using: .utf8)!
|
|
)
|
|
)
|
|
}
|
|
|
|
func testJSONCompilationDatabaseCoding() {
|
|
checkCoding(
|
|
JSONCompilationDatabase([]),
|
|
json: """
|
|
[
|
|
|
|
]
|
|
"""
|
|
)
|
|
let db = JSONCompilationDatabase([
|
|
.init(directory: "a", filename: "b", commandLine: [], output: nil),
|
|
.init(directory: "c", filename: "b", commandLine: [], output: nil),
|
|
])
|
|
checkCoding(
|
|
db,
|
|
json: """
|
|
[
|
|
{
|
|
"arguments" : [
|
|
|
|
],
|
|
"directory" : "a",
|
|
"file" : "b"
|
|
},
|
|
{
|
|
"arguments" : [
|
|
|
|
],
|
|
"directory" : "c",
|
|
"file" : "b"
|
|
}
|
|
]
|
|
"""
|
|
)
|
|
}
|
|
|
|
func testJSONCompilationDatabaseLookup() throws {
|
|
#if os(Windows)
|
|
let fileSystemRoot = "c:/"
|
|
#else
|
|
let fileSystemRoot = "/"
|
|
#endif
|
|
|
|
let cmd1 = CompilationDatabase.Command(directory: "a", filename: "b", commandLine: [], output: nil)
|
|
let cmd2 = CompilationDatabase.Command(directory: "\(fileSystemRoot)c", filename: "b", commandLine: [], output: nil)
|
|
let cmd3 = CompilationDatabase.Command(
|
|
directory: "\(fileSystemRoot)c",
|
|
filename: "\(fileSystemRoot)b",
|
|
commandLine: [],
|
|
output: nil
|
|
)
|
|
|
|
let db = JSONCompilationDatabase([cmd1, cmd2, cmd3])
|
|
|
|
XCTAssertEqual(db[DocumentURI(filePath: "b", isDirectory: false)], [cmd1])
|
|
XCTAssertEqual(db[DocumentURI(filePath: "\(fileSystemRoot)c/b", isDirectory: false)], [cmd2])
|
|
XCTAssertEqual(db[DocumentURI(filePath: "\(fileSystemRoot)b", isDirectory: false)], [cmd3])
|
|
}
|
|
|
|
func testJSONCompilationDatabaseFromDirectory() async throws {
|
|
try await withTestScratchDir { tempDir in
|
|
let dbFile = tempDir.appendingPathComponent(JSONCompilationDatabase.dbName)
|
|
|
|
XCTAssertNil(tryLoadCompilationDatabase(file: dbFile))
|
|
|
|
try """
|
|
[
|
|
{
|
|
"file": "/a/a.swift",
|
|
"directory": "/a",
|
|
"arguments": ["swiftc", "/a/a.swift"]
|
|
}
|
|
]
|
|
""".write(to: dbFile, atomically: true, encoding: .utf8)
|
|
|
|
XCTAssertNotNil(tryLoadCompilationDatabase(file: dbFile))
|
|
}
|
|
}
|
|
|
|
func testFixedCompilationDatabase() async throws {
|
|
try await withTestScratchDir { tempDir in
|
|
let dbFile = tempDir.appendingPathComponent(FixedCompilationDatabase.dbName)
|
|
|
|
XCTAssertNil(tryLoadCompilationDatabase(file: dbFile))
|
|
|
|
try """
|
|
-xc++
|
|
-I
|
|
libwidget/include/
|
|
""".write(to: dbFile, atomically: true, encoding: .utf8)
|
|
|
|
let db = try XCTUnwrap(tryLoadCompilationDatabase(file: dbFile))
|
|
|
|
let filePath = try tempDir.appendingPathComponent("a.c").filePath
|
|
XCTAssertEqual(
|
|
db[DocumentURI(filePath: filePath, isDirectory: false)],
|
|
[
|
|
CompilationDatabase.Command(
|
|
directory: try tempDir.filePath,
|
|
filename: filePath,
|
|
commandLine: [
|
|
"clang", "-xc++", "-I", "libwidget/include/", filePath,
|
|
],
|
|
output: nil
|
|
)
|
|
]
|
|
)
|
|
}
|
|
}
|
|
|
|
func testInvalidCompilationDatabase() async throws {
|
|
try await withTestScratchDir { tempDir in
|
|
let dbFile = tempDir.appendingPathComponent(JSONCompilationDatabase.dbName)
|
|
|
|
try "".write(to: dbFile, atomically: true, encoding: .utf8)
|
|
XCTAssertNil(tryLoadCompilationDatabase(file: dbFile))
|
|
}
|
|
}
|
|
|
|
func testCompilationDatabaseBuildSystem() async throws {
|
|
try await checkCompilationDatabaseBuildSystem(
|
|
"""
|
|
[
|
|
{
|
|
"file": "/a/a.swift",
|
|
"directory": "/a",
|
|
"arguments": ["swiftc", "-swift-version", "4", "/a/a.swift"]
|
|
}
|
|
]
|
|
"""
|
|
) { buildSystem in
|
|
let settings = try await buildSystem.sourceKitOptions(
|
|
request: TextDocumentSourceKitOptionsRequest(
|
|
textDocument: TextDocumentIdentifier(DocumentURI(URL(fileURLWithPath: "/a/a.swift"))),
|
|
target: BuildTargetIdentifier.dummy,
|
|
language: .swift
|
|
)
|
|
)
|
|
|
|
XCTAssertNotNil(settings)
|
|
XCTAssertEqual(settings?.workingDirectory, "/a")
|
|
XCTAssertEqual(settings?.compilerArguments, ["-swift-version", "4", "/a/a.swift"])
|
|
assertNil(await buildSystem.indexStorePath)
|
|
assertNil(await buildSystem.indexDatabasePath)
|
|
}
|
|
}
|
|
|
|
func testCompilationDatabaseBuildSystemIndexStoreSwift0() async throws {
|
|
try await checkCompilationDatabaseBuildSystem("[]") { buildSystem in
|
|
assertNil(await buildSystem.indexStorePath)
|
|
}
|
|
}
|
|
|
|
func testCompilationDatabaseBuildSystemIndexStoreSwift1() async throws {
|
|
try await checkCompilationDatabaseBuildSystem(
|
|
"""
|
|
[
|
|
{
|
|
"file": "/a/a.swift",
|
|
"directory": "/a",
|
|
"arguments": ["swiftc", "-swift-version", "4", "/a/a.swift", "-index-store-path", "/b"]
|
|
}
|
|
]
|
|
"""
|
|
) { buildSystem in
|
|
assertEqual(
|
|
try await buildSystem.indexStorePath?.filePath,
|
|
"\(pathSeparator)b"
|
|
)
|
|
assertEqual(
|
|
try await buildSystem.indexDatabasePath?.filePath,
|
|
"\(pathSeparator)IndexDatabase"
|
|
)
|
|
}
|
|
}
|
|
|
|
func testCompilationDatabaseBuildSystemIndexStoreSwift2() async throws {
|
|
try await checkCompilationDatabaseBuildSystem(
|
|
"""
|
|
[
|
|
{
|
|
"file": "/a/a.swift",
|
|
"directory": "/a",
|
|
"arguments": ["swiftc", "-swift-version", "4", "/a/a.swift"]
|
|
},
|
|
{
|
|
"file": "/a/b.swift",
|
|
"directory": "/a",
|
|
"arguments": ["swiftc", "-swift-version", "4", "/a/b.swift"]
|
|
},
|
|
{
|
|
"file": "/a/c.swift",
|
|
"directory": "/a",
|
|
"arguments": ["swiftc", "-swift-version", "4", "/a/c.swift", "-index-store-path", "/b"]
|
|
}
|
|
]
|
|
"""
|
|
) { buildSystem in
|
|
await assertEqual(buildSystem.indexStorePath, URL(fileURLWithPath: "/b"))
|
|
}
|
|
}
|
|
|
|
func testCompilationDatabaseBuildSystemIndexStoreSwift3() async throws {
|
|
try await checkCompilationDatabaseBuildSystem(
|
|
"""
|
|
[
|
|
{
|
|
"file": "/a/a.swift",
|
|
"directory": "/a",
|
|
"arguments": ["swiftc", "-index-store-path", "/b", "-swift-version", "4", "/a/a.swift"]
|
|
}
|
|
]
|
|
"""
|
|
) { buildSystem in
|
|
assertEqual(await buildSystem.indexStorePath, URL(fileURLWithPath: "/b"))
|
|
}
|
|
}
|
|
|
|
func testCompilationDatabaseBuildSystemIndexStoreSwift4() async throws {
|
|
try await checkCompilationDatabaseBuildSystem(
|
|
"""
|
|
[
|
|
{
|
|
"file": "/a/a.swift",
|
|
"directory": "/a",
|
|
"arguments": ["swiftc", "-swift-version", "4", "/a/c.swift", "-index-store-path"]
|
|
}
|
|
]
|
|
"""
|
|
) { buildSystem in
|
|
assertNil(await buildSystem.indexStorePath)
|
|
}
|
|
}
|
|
|
|
func testCompilationDatabaseBuildSystemIndexStoreClang() async throws {
|
|
try await checkCompilationDatabaseBuildSystem(
|
|
"""
|
|
[
|
|
{
|
|
"file": "/a/a.cpp",
|
|
"directory": "/a",
|
|
"arguments": ["clang", "/a/a.cpp"]
|
|
},
|
|
{
|
|
"file": "/a/b.cpp",
|
|
"directory": "/a",
|
|
"arguments": ["clang", "/a/b.cpp"]
|
|
},
|
|
{
|
|
"file": "/a/c.cpp",
|
|
"directory": "/a",
|
|
"arguments": ["clang", "/a/c.cpp", "-index-store-path", "/b"]
|
|
}
|
|
]
|
|
"""
|
|
) { buildSystem in
|
|
assertEqual(
|
|
try await buildSystem.indexStorePath?.filePath,
|
|
"\(pathSeparator)b"
|
|
)
|
|
assertEqual(
|
|
try await buildSystem.indexDatabasePath?.filePath,
|
|
"\(pathSeparator)IndexDatabase"
|
|
)
|
|
}
|
|
}
|
|
|
|
func testIndexStorePathRelativeToWorkingDirectory() async throws {
|
|
try await checkCompilationDatabaseBuildSystem(
|
|
"""
|
|
[
|
|
{
|
|
"file": "a.swift",
|
|
"directory": "/a",
|
|
"arguments": ["swift", "a.swift", "-index-store-path", "index-store"]
|
|
}
|
|
]
|
|
"""
|
|
) { buildSystem in
|
|
assertEqual(
|
|
try await buildSystem.indexStorePath?.filePath,
|
|
"\(pathSeparator)a\(pathSeparator)index-store"
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
fileprivate var pathSeparator: String {
|
|
#if os(Windows)
|
|
return #"\"#
|
|
#else
|
|
return "/"
|
|
#endif
|
|
}
|
|
|
|
private func checkCompilationDatabaseBuildSystem(
|
|
_ compdb: String,
|
|
block: @Sendable (CompilationDatabaseBuildSystem) async throws -> ()
|
|
) async throws {
|
|
try await withTestScratchDir { tempDir in
|
|
let configPath = tempDir.appendingPathComponent(JSONCompilationDatabase.dbName)
|
|
try compdb.write(to: configPath, atomically: true, encoding: .utf8)
|
|
let buildSystem = try CompilationDatabaseBuildSystem(
|
|
configPath: configPath,
|
|
connectionToSourceKitLSP: LocalConnection(receiverName: "Dummy SourceKit-LSP")
|
|
)
|
|
try await block(XCTUnwrap(buildSystem))
|
|
}
|
|
}
|