mirror of
https://github.com/apple/sourcekit-lsp.git
synced 2026-03-02 18:23:24 +01:00
Use fallback build settings if build system doesn’t provide build settings within a timeout
When we receive build settings after hitting the timeout, we call `fileBuildSettingsChanged` on the delegate, which should cause the document to get re-opened in sourcekitd and diagnostics to get refreshed. rdar://136332685 Fixes #1693
This commit is contained in:
@@ -31,6 +31,7 @@ The structure of the file is currently not guaranteed to be stable. Options may
|
||||
- `cxxCompilerFlags: string[]`: Extra arguments passed to the compiler for C++ files
|
||||
- `swiftCompilerFlags: string[]`: Extra arguments passed to the compiler for Swift files
|
||||
- `sdk: string`: The SDK to use for fallback arguments. Default is to infer the SDK using `xcrun`.
|
||||
- `buildSettingsTimeout: int`: Number of milliseconds to wait for build settings from the build system before using fallback build settings.
|
||||
- `clangdOptions: string[]`: Extra command line arguments passed to `clangd` when launching it
|
||||
- `index`: Dictionary with the following keys, defining options related to indexing
|
||||
- `indexStorePath: string`: Directory in which a separate compilation stores the index store. By default, inferred from the build system.
|
||||
|
||||
@@ -139,6 +139,7 @@ private extension BuildSystemKind {
|
||||
private static func createBuiltInBuildSystemAdapter(
|
||||
projectRoot: AbsolutePath,
|
||||
messagesToSourceKitLSPHandler: any MessageHandler,
|
||||
buildSystemTestHooks: BuildSystemTestHooks,
|
||||
_ createBuildSystem: @Sendable (_ connectionToSourceKitLSP: any Connection) async throws -> BuiltInBuildSystem?
|
||||
) async -> BuildSystemAdapter? {
|
||||
let connectionToSourceKitLSP = LocalConnection(
|
||||
@@ -156,7 +157,8 @@ private extension BuildSystemKind {
|
||||
logger.log("Created \(type(of: buildSystem), privacy: .public) at \(projectRoot.pathString)")
|
||||
let buildSystemAdapter = BuiltInBuildSystemAdapter(
|
||||
underlyingBuildSystem: buildSystem,
|
||||
connectionToSourceKitLSP: connectionToSourceKitLSP
|
||||
connectionToSourceKitLSP: connectionToSourceKitLSP,
|
||||
buildSystemTestHooks: buildSystemTestHooks
|
||||
)
|
||||
let connectionToBuildSystem = LocalConnection(
|
||||
receiverName: "\(type(of: buildSystem)) for \(projectRoot.asURL.lastPathComponent)"
|
||||
@@ -190,7 +192,8 @@ private extension BuildSystemKind {
|
||||
case .compilationDatabase(projectRoot: let projectRoot):
|
||||
return await Self.createBuiltInBuildSystemAdapter(
|
||||
projectRoot: projectRoot,
|
||||
messagesToSourceKitLSPHandler: messagesToSourceKitLSPHandler
|
||||
messagesToSourceKitLSPHandler: messagesToSourceKitLSPHandler,
|
||||
buildSystemTestHooks: testHooks
|
||||
) { connectionToSourceKitLSP in
|
||||
CompilationDatabaseBuildSystem(
|
||||
projectRoot: projectRoot,
|
||||
@@ -203,7 +206,8 @@ private extension BuildSystemKind {
|
||||
case .swiftPM(projectRoot: let projectRoot):
|
||||
return await Self.createBuiltInBuildSystemAdapter(
|
||||
projectRoot: projectRoot,
|
||||
messagesToSourceKitLSPHandler: messagesToSourceKitLSPHandler
|
||||
messagesToSourceKitLSPHandler: messagesToSourceKitLSPHandler,
|
||||
buildSystemTestHooks: testHooks
|
||||
) { connectionToSourceKitLSP in
|
||||
try await SwiftPMBuildSystem(
|
||||
projectRoot: projectRoot,
|
||||
@@ -216,7 +220,8 @@ private extension BuildSystemKind {
|
||||
case .testBuildSystem(projectRoot: let projectRoot):
|
||||
return await Self.createBuiltInBuildSystemAdapter(
|
||||
projectRoot: projectRoot,
|
||||
messagesToSourceKitLSPHandler: messagesToSourceKitLSPHandler
|
||||
messagesToSourceKitLSPHandler: messagesToSourceKitLSPHandler,
|
||||
buildSystemTestHooks: testHooks
|
||||
) { connectionToSourceKitLSP in
|
||||
TestBuildSystem(projectRoot: projectRoot, connectionToSourceKitLSP: connectionToSourceKitLSP)
|
||||
}
|
||||
@@ -411,7 +416,8 @@ package actor BuildSystemManager: QueueBasedMessageHandler {
|
||||
)
|
||||
let adapter = BuiltInBuildSystemAdapter(
|
||||
underlyingBuildSystem: legacyBuildServer,
|
||||
connectionToSourceKitLSP: legacyBuildServer.connectionToSourceKitLSP
|
||||
connectionToSourceKitLSP: legacyBuildServer.connectionToSourceKitLSP,
|
||||
buildSystemTestHooks: buildSystemTestHooks
|
||||
)
|
||||
let connectionToBuildSystem = LocalConnection(receiverName: "Legacy BSP server")
|
||||
connectionToBuildSystem.start(handler: adapter)
|
||||
@@ -672,7 +678,12 @@ package actor BuildSystemManager: QueueBasedMessageHandler {
|
||||
/// Returns the target's module name as parsed from the `BuildTargetIdentifier`'s compiler arguments.
|
||||
package func moduleName(for document: DocumentURI, in target: BuildTargetIdentifier) async -> String? {
|
||||
guard let language = await self.defaultLanguage(for: document, in: target),
|
||||
let buildSettings = await buildSettings(for: document, in: target, language: language)
|
||||
let buildSettings = await buildSettings(
|
||||
for: document,
|
||||
in: target,
|
||||
language: language,
|
||||
fallbackAfterTimeout: false
|
||||
)
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
@@ -715,11 +726,6 @@ package actor BuildSystemManager: QueueBasedMessageHandler {
|
||||
language: language
|
||||
)
|
||||
|
||||
// TODO: We should only wait `fallbackSettingsTimeout` for build settings
|
||||
// and return fallback afterwards.
|
||||
// For now, this should be fine because all build systems return
|
||||
// very quickly from `settings(for:language:)`.
|
||||
// https://github.com/apple/sourcekit-lsp/issues/1181
|
||||
let response = try await cachedSourceKitOptions.get(request, isolation: self) { request in
|
||||
try await buildSystemAdapter.send(request)
|
||||
}
|
||||
@@ -740,19 +746,30 @@ package actor BuildSystemManager: QueueBasedMessageHandler {
|
||||
/// Only call this method if it is known that `document` is a main file. Prefer `buildSettingsInferredFromMainFile`
|
||||
/// otherwise. If `document` is a header file, this will most likely return fallback settings because header files
|
||||
/// don't have build settings by themselves.
|
||||
///
|
||||
/// If `fallbackAfterTimeout` is true fallback build settings will be returned if no build settings can be found in
|
||||
/// `SourceKitLSPOptions.buildSettingsTimeoutOrDefault`.
|
||||
package func buildSettings(
|
||||
for document: DocumentURI,
|
||||
in target: BuildTargetIdentifier?,
|
||||
language: Language
|
||||
language: Language,
|
||||
fallbackAfterTimeout: Bool
|
||||
) async -> FileBuildSettings? {
|
||||
do {
|
||||
if let target,
|
||||
let buildSettings = try await buildSettingsFromBuildSystem(for: document, in: target, language: language)
|
||||
{
|
||||
return buildSettings
|
||||
if let target {
|
||||
let buildSettingsFromBuildSystem = await orLog("Getting build settings") {
|
||||
if fallbackAfterTimeout {
|
||||
try await withTimeout(options.buildSettingsTimeoutOrDefault) {
|
||||
return try await self.buildSettingsFromBuildSystem(for: document, in: target, language: language)
|
||||
} resultReceivedAfterTimeout: {
|
||||
await self.delegate?.fileBuildSettingsChanged([document])
|
||||
}
|
||||
} else {
|
||||
try await self.buildSettingsFromBuildSystem(for: document, in: target, language: language)
|
||||
}
|
||||
}
|
||||
if let buildSettingsFromBuildSystem {
|
||||
return buildSettingsFromBuildSystem
|
||||
}
|
||||
} catch {
|
||||
logger.error("Getting build settings failed: \(error.forLogging)")
|
||||
}
|
||||
|
||||
guard
|
||||
@@ -780,14 +797,27 @@ package actor BuildSystemManager: QueueBasedMessageHandler {
|
||||
/// by the header file.
|
||||
package func buildSettingsInferredFromMainFile(
|
||||
for document: DocumentURI,
|
||||
language: Language
|
||||
language: Language,
|
||||
fallbackAfterTimeout: Bool
|
||||
) async -> FileBuildSettings? {
|
||||
func mainFileAndSettings(
|
||||
basedOn document: DocumentURI
|
||||
) async -> (mainFile: DocumentURI, settings: FileBuildSettings)? {
|
||||
let mainFile = await self.mainFile(for: document, language: language)
|
||||
let target = await canonicalTarget(for: mainFile)
|
||||
guard let settings = await buildSettings(for: mainFile, in: target, language: language) else {
|
||||
let settings = await orLog("Getting build settings") {
|
||||
let target = try await withTimeout(options.buildSettingsTimeoutOrDefault) {
|
||||
await self.canonicalTarget(for: mainFile)
|
||||
} resultReceivedAfterTimeout: {
|
||||
await self.delegate?.fileBuildSettingsChanged([document])
|
||||
}
|
||||
return await self.buildSettings(
|
||||
for: mainFile,
|
||||
in: target,
|
||||
language: language,
|
||||
fallbackAfterTimeout: fallbackAfterTimeout
|
||||
)
|
||||
}
|
||||
guard let settings else {
|
||||
return nil
|
||||
}
|
||||
return (mainFile, settings)
|
||||
|
||||
@@ -10,10 +10,25 @@
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#if compiler(>=6)
|
||||
package import LanguageServerProtocol
|
||||
#else
|
||||
import LanguageServerProtocol
|
||||
#endif
|
||||
|
||||
package struct BuildSystemTestHooks: Sendable {
|
||||
package var swiftPMTestHooks: SwiftPMTestHooks
|
||||
|
||||
package init(swiftPMTestHooks: 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 init(
|
||||
swiftPMTestHooks: SwiftPMTestHooks = SwiftPMTestHooks(),
|
||||
handleRequest: (@Sendable (any RequestType) async -> Void)? = nil
|
||||
) {
|
||||
self.swiftPMTestHooks = swiftPMTestHooks
|
||||
self.handleRequest = handleRequest
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,6 +59,8 @@ actor BuiltInBuildSystemAdapter: QueueBasedMessageHandler {
|
||||
/// The connection with which messages are sent to `BuildSystemManager`.
|
||||
private let connectionToSourceKitLSP: LocalConnection
|
||||
|
||||
private let buildSystemTestHooks: BuildSystemTestHooks
|
||||
|
||||
/// If the underlying build system is a `TestBuildSystem`, return it. Otherwise, `nil`
|
||||
///
|
||||
/// - Important: For testing purposes only.
|
||||
@@ -70,10 +72,12 @@ actor BuiltInBuildSystemAdapter: QueueBasedMessageHandler {
|
||||
/// from the build system to SourceKit-LSP.
|
||||
init(
|
||||
underlyingBuildSystem: BuiltInBuildSystem,
|
||||
connectionToSourceKitLSP: LocalConnection
|
||||
connectionToSourceKitLSP: LocalConnection,
|
||||
buildSystemTestHooks: BuildSystemTestHooks
|
||||
) {
|
||||
self.underlyingBuildSystem = underlyingBuildSystem
|
||||
self.connectionToSourceKitLSP = connectionToSourceKitLSP
|
||||
self.buildSystemTestHooks = buildSystemTestHooks
|
||||
}
|
||||
|
||||
deinit {
|
||||
@@ -116,6 +120,7 @@ actor BuiltInBuildSystemAdapter: QueueBasedMessageHandler {
|
||||
reply: @Sendable @escaping (LSPResult<Request.Response>) -> Void
|
||||
) async {
|
||||
let request = RequestAndReply(request, reply: reply)
|
||||
await buildSystemTestHooks.handleRequest?(request.params)
|
||||
switch request {
|
||||
case let request as RequestAndReply<BuildShutdownRequest>:
|
||||
await request.reply { VoidResponse() }
|
||||
|
||||
@@ -14,7 +14,6 @@ add_library(BuildSystemIntegration STATIC
|
||||
ExternalBuildSystemAdapter.swift
|
||||
FallbackBuildSettings.swift
|
||||
FileBuildSettings.swift
|
||||
Language+InferredFromFileExtension.swift
|
||||
LegacyBuildServerBuildSystem.swift
|
||||
MainFilesProvider.swift
|
||||
PathPrefixMapping.swift
|
||||
|
||||
@@ -250,6 +250,13 @@ public struct SourceKitLSPOptions: Sendable, Codable, Equatable {
|
||||
set { fallbackBuildSystem = newValue }
|
||||
}
|
||||
|
||||
/// Number of milliseconds to wait for build settings from the build system before using fallback build settings.
|
||||
public var buildSettingsTimeout: Int?
|
||||
public var buildSettingsTimeoutOrDefault: Duration {
|
||||
// The default timeout of 500ms was chosen arbitrarily without any measurements.
|
||||
get { .milliseconds(buildSettingsTimeout ?? 500) }
|
||||
}
|
||||
|
||||
public var clangdOptions: [String]?
|
||||
|
||||
private var index: IndexOptions?
|
||||
|
||||
@@ -8,6 +8,7 @@ add_library(SKSupport STATIC
|
||||
DocumentURI+CustomLogStringConvertible.swift
|
||||
DocumentURI+symlinkTarget.swift
|
||||
FileSystem.swift
|
||||
Language+InferredFromFileExtension.swift
|
||||
LineTable.swift
|
||||
LocalConnection.swift
|
||||
Process+Run.swift
|
||||
|
||||
@@ -10,11 +10,16 @@
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#if compiler(>=6)
|
||||
import Foundation
|
||||
package import LanguageServerProtocol
|
||||
#else
|
||||
import Foundation
|
||||
import LanguageServerProtocol
|
||||
#endif
|
||||
|
||||
extension Language {
|
||||
init?(inferredFromFileExtension uri: DocumentURI) {
|
||||
package init?(inferredFromFileExtension uri: DocumentURI) {
|
||||
// URL.pathExtension is only set for file URLs but we want to also infer a file extension for non-file URLs like
|
||||
// untitled:file.cpp
|
||||
let pathExtension = uri.fileURL?.pathExtension ?? (uri.pseudoPath as NSString).pathExtension
|
||||
@@ -56,12 +56,16 @@ package struct WrappedSemaphore: Sendable {
|
||||
}
|
||||
|
||||
/// Wait for a signal and emit an XCTFail if the semaphore is not signaled within `timeout`.
|
||||
package func waitOrXCTFail(timeout: DispatchTime = DispatchTime.now() + .seconds(Int(defaultTimeout))) {
|
||||
package func waitOrXCTFail(
|
||||
timeout: DispatchTime = DispatchTime.now() + .seconds(Int(defaultTimeout)),
|
||||
file: StaticString = #filePath,
|
||||
line: UInt = #line
|
||||
) {
|
||||
switch self.wait(timeout: timeout) {
|
||||
case .success:
|
||||
break
|
||||
case .timedOut:
|
||||
XCTFail("\(name) timed out")
|
||||
XCTFail("\(name) timed out", file: file, line: line)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -254,7 +254,12 @@ package struct UpdateIndexStoreTaskDescription: IndexTaskDescription {
|
||||
logger.error("Not indexing \(file.forLogging) because its language could not be determined")
|
||||
return
|
||||
}
|
||||
let buildSettings = await buildSystemManager.buildSettings(for: file.mainFile, in: target, language: language)
|
||||
let buildSettings = await buildSystemManager.buildSettings(
|
||||
for: file.mainFile,
|
||||
in: target,
|
||||
language: language,
|
||||
fallbackAfterTimeout: false
|
||||
)
|
||||
guard let buildSettings else {
|
||||
logger.error("Not indexing \(file.forLogging) because it has no compiler arguments")
|
||||
return
|
||||
|
||||
@@ -129,14 +129,15 @@ actor ClangLanguageService: LanguageService, MessageHandler {
|
||||
try startClangdProcess()
|
||||
}
|
||||
|
||||
private func buildSettings(for document: DocumentURI) async -> ClangBuildSettings? {
|
||||
private func buildSettings(for document: DocumentURI, fallbackAfterTimeout: Bool) async -> ClangBuildSettings? {
|
||||
guard let workspace = workspace.value, let language = openDocuments[document] else {
|
||||
return nil
|
||||
}
|
||||
guard
|
||||
let settings = await workspace.buildSystemManager.buildSettingsInferredFromMainFile(
|
||||
for: document,
|
||||
language: language
|
||||
language: language,
|
||||
fallbackAfterTimeout: fallbackAfterTimeout
|
||||
)
|
||||
else {
|
||||
return nil
|
||||
@@ -340,7 +341,7 @@ actor ClangLanguageService: LanguageService, MessageHandler {
|
||||
|
||||
extension ClangLanguageService {
|
||||
|
||||
/// Intercept clangd's `PublishDiagnosticsNotification` to withold it if we're using fallback
|
||||
/// Intercept clangd's `PublishDiagnosticsNotification` to withhold it if we're using fallback
|
||||
/// build settings.
|
||||
func publishDiagnostics(_ notification: PublishDiagnosticsNotification) async {
|
||||
// Technically, the publish diagnostics notification could still originate
|
||||
@@ -354,7 +355,9 @@ extension ClangLanguageService {
|
||||
// short and we expect clangd to send us new diagnostics with the updated
|
||||
// non-fallback settings very shortly after, which will override the
|
||||
// incorrect result, making it very temporary.
|
||||
let buildSettings = await self.buildSettings(for: notification.uri)
|
||||
// TODO: We want to know the build settings that are currently transmitted to clangd, not whichever ones we would
|
||||
// get next. (https://github.com/swiftlang/sourcekit-lsp/issues/1761)
|
||||
let buildSettings = await self.buildSettings(for: notification.uri, fallbackAfterTimeout: true)
|
||||
guard let sourceKitLSPServer else {
|
||||
logger.fault("Cannot publish diagnostics because SourceKitLSPServer has been destroyed")
|
||||
return
|
||||
@@ -452,7 +455,7 @@ extension ClangLanguageService {
|
||||
logger.error("Received updated build settings for non-file URI '\(uri.forLogging)'. Ignoring the update.")
|
||||
return
|
||||
}
|
||||
let clangBuildSettings = await self.buildSettings(for: uri)
|
||||
let clangBuildSettings = await self.buildSettings(for: uri, fallbackAfterTimeout: false)
|
||||
|
||||
// The compile command changed, send over the new one.
|
||||
if let compileCommand = clangBuildSettings?.compileCommand,
|
||||
|
||||
@@ -364,7 +364,8 @@ extension SwiftLanguageService {
|
||||
let req = sourcekitd.dictionary([
|
||||
keys.request: sourcekitd.requests.nameTranslation,
|
||||
keys.sourceFile: snapshot.uri.pseudoPath,
|
||||
keys.compilerArgs: await self.buildSettings(for: snapshot.uri)?.compilerArgs as [SKDRequestValue]?,
|
||||
keys.compilerArgs: await self.buildSettings(for: snapshot.uri, fallbackAfterTimeout: false)?.compilerArgs
|
||||
as [SKDRequestValue]?,
|
||||
keys.offset: snapshot.utf8Offset(of: snapshot.position(of: symbolLocation)),
|
||||
keys.nameKind: sourcekitd.values.nameSwift,
|
||||
keys.baseName: name.baseName,
|
||||
@@ -415,7 +416,8 @@ extension SwiftLanguageService {
|
||||
let req = sourcekitd.dictionary([
|
||||
keys.request: sourcekitd.requests.nameTranslation,
|
||||
keys.sourceFile: snapshot.uri.pseudoPath,
|
||||
keys.compilerArgs: await self.buildSettings(for: snapshot.uri)?.compilerArgs as [SKDRequestValue]?,
|
||||
keys.compilerArgs: await self.buildSettings(for: snapshot.uri, fallbackAfterTimeout: false)?.compilerArgs
|
||||
as [SKDRequestValue]?,
|
||||
keys.offset: snapshot.utf8Offset(of: snapshot.position(of: symbolLocation)),
|
||||
keys.nameKind: sourcekitd.values.nameObjc,
|
||||
])
|
||||
|
||||
@@ -34,7 +34,7 @@ extension SwiftLanguageService {
|
||||
|
||||
let clientSupportsSnippets =
|
||||
capabilityRegistry.clientCapabilities.textDocument?.completion?.completionItem?.snippetSupport ?? false
|
||||
let buildSettings = await buildSettings(for: snapshot.uri)
|
||||
let buildSettings = await buildSettings(for: snapshot.uri, fallbackAfterTimeout: false)
|
||||
|
||||
let inferredIndentationWidth = BasicFormat.inferIndentation(of: await syntaxTreeManager.syntaxTree(for: snapshot))
|
||||
|
||||
|
||||
@@ -141,10 +141,12 @@ extension SwiftLanguageService {
|
||||
/// - Parameters:
|
||||
/// - url: Document URI in which to perform the request. Must be an open document.
|
||||
/// - range: The position range within the document to lookup the symbol at.
|
||||
/// - completion: Completion block to asynchronously receive the CursorInfo, or error.
|
||||
/// - fallbackSettingsAfterTimeout: Whether fallback build settings should be used for the cursor info request if no
|
||||
/// build settings can be retrieved within a timeout.
|
||||
func cursorInfo(
|
||||
_ uri: DocumentURI,
|
||||
_ range: Range<Position>,
|
||||
fallbackSettingsAfterTimeout: Bool,
|
||||
additionalParameters appendAdditionalParameters: ((SKDRequestDictionary) -> Void)? = nil
|
||||
) async throws -> (cursorInfo: [CursorInfo], refactorActions: [SemanticRefactorCommand]) {
|
||||
let documentManager = try self.documentManager
|
||||
@@ -161,7 +163,8 @@ extension SwiftLanguageService {
|
||||
keys.length: offsetRange.upperBound != offsetRange.lowerBound ? offsetRange.count : nil,
|
||||
keys.sourceFile: snapshot.uri.sourcekitdSourceFile,
|
||||
keys.primaryFile: snapshot.uri.primaryFile?.pseudoPath,
|
||||
keys.compilerArgs: await self.buildSettings(for: uri)?.compilerArgs as [SKDRequestValue]?,
|
||||
keys.compilerArgs: await self.buildSettings(for: uri, fallbackAfterTimeout: fallbackSettingsAfterTimeout)?
|
||||
.compilerArgs as [SKDRequestValue]?,
|
||||
])
|
||||
|
||||
appendAdditionalParameters?(skreq)
|
||||
|
||||
@@ -86,7 +86,7 @@ actor MacroExpansionManager {
|
||||
}
|
||||
|
||||
let snapshot = try await swiftLanguageService.latestSnapshot(for: uri)
|
||||
let buildSettings = await swiftLanguageService.buildSettings(for: uri)
|
||||
let buildSettings = await swiftLanguageService.buildSettings(for: uri, fallbackAfterTimeout: false)
|
||||
|
||||
if let cacheEntry = cache.first(where: {
|
||||
$0.snapshotID == snapshot.id && $0.range == range && $0.buildSettings == buildSettings
|
||||
|
||||
@@ -100,7 +100,8 @@ extension SwiftLanguageService {
|
||||
keys.groupName: groupName,
|
||||
keys.name: interfaceURI.pseudoPath,
|
||||
keys.synthesizedExtension: 1,
|
||||
keys.compilerArgs: await self.buildSettings(for: document)?.compilerArgs as [SKDRequestValue]?,
|
||||
keys.compilerArgs: await self.buildSettings(for: document, fallbackAfterTimeout: false)?.compilerArgs
|
||||
as [SKDRequestValue]?,
|
||||
])
|
||||
|
||||
let dict = try await sendSourcekitdRequest(skreq, fileContents: nil)
|
||||
|
||||
@@ -126,7 +126,8 @@ extension SwiftLanguageService {
|
||||
keys.column: utf8Column + 1,
|
||||
keys.length: snapshot.utf8OffsetRange(of: refactorCommand.positionRange).count,
|
||||
keys.actionUID: self.sourcekitd.api.uid_get_from_cstr(refactorCommand.actionString)!,
|
||||
keys.compilerArgs: await self.buildSettings(for: snapshot.uri)?.compilerArgs as [SKDRequestValue]?,
|
||||
keys.compilerArgs: await self.buildSettings(for: snapshot.uri, fallbackAfterTimeout: true)?.compilerArgs
|
||||
as [SKDRequestValue]?,
|
||||
])
|
||||
|
||||
let dict = try await sendSourcekitdRequest(skreq, fileContents: snapshot.text)
|
||||
|
||||
@@ -73,7 +73,8 @@ extension SwiftLanguageService {
|
||||
keys.sourceFile: snapshot.uri.sourcekitdSourceFile,
|
||||
keys.primaryFile: snapshot.uri.primaryFile?.pseudoPath,
|
||||
keys.includeNonEditableBaseNames: includeNonEditableBaseNames ? 1 : 0,
|
||||
keys.compilerArgs: await self.buildSettings(for: snapshot.uri)?.compilerArgs as [SKDRequestValue]?,
|
||||
keys.compilerArgs: await self.buildSettings(for: snapshot.uri, fallbackAfterTimeout: true)?.compilerArgs
|
||||
as [SKDRequestValue]?,
|
||||
])
|
||||
|
||||
let dict = try await sendSourcekitdRequest(skreq, fileContents: snapshot.text)
|
||||
|
||||
@@ -29,7 +29,9 @@ import SwiftSyntax
|
||||
extension SwiftLanguageService {
|
||||
/// Requests the semantic highlighting tokens for the given snapshot from sourcekitd.
|
||||
private func semanticHighlightingTokens(for snapshot: DocumentSnapshot) async throws -> SyntaxHighlightingTokens? {
|
||||
guard let buildSettings = await self.buildSettings(for: snapshot.uri), !buildSettings.isFallback else {
|
||||
guard let buildSettings = await self.buildSettings(for: snapshot.uri, fallbackAfterTimeout: false),
|
||||
!buildSettings.isFallback
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -259,7 +259,7 @@ package actor SwiftLanguageService: LanguageService, Sendable {
|
||||
}
|
||||
}
|
||||
|
||||
func buildSettings(for document: DocumentURI) async -> SwiftCompileCommand? {
|
||||
func buildSettings(for document: DocumentURI, fallbackAfterTimeout: Bool) async -> SwiftCompileCommand? {
|
||||
let primaryDocument = document.primaryFile ?? document
|
||||
|
||||
guard let sourceKitLSPServer else {
|
||||
@@ -271,7 +271,8 @@ package actor SwiftLanguageService: LanguageService, Sendable {
|
||||
}
|
||||
if let settings = await workspace.buildSystemManager.buildSettingsInferredFromMainFile(
|
||||
for: primaryDocument,
|
||||
language: .swift
|
||||
language: .swift,
|
||||
fallbackAfterTimeout: fallbackAfterTimeout
|
||||
) {
|
||||
return SwiftCompileCommand(settings)
|
||||
} else {
|
||||
@@ -428,7 +429,7 @@ extension SwiftLanguageService {
|
||||
try await self.sendSourcekitdRequest(closeReq, fileContents: nil)
|
||||
}
|
||||
|
||||
let buildSettings = await buildSettings(for: snapshot.uri)
|
||||
let buildSettings = await buildSettings(for: snapshot.uri, fallbackAfterTimeout: true)
|
||||
let openReq = openDocumentSourcekitdRequest(
|
||||
snapshot: snapshot,
|
||||
compileCommand: buildSettings
|
||||
@@ -450,7 +451,7 @@ extension SwiftLanguageService {
|
||||
guard (try? documentManager.openDocuments.contains(uri)) ?? false else {
|
||||
return
|
||||
}
|
||||
let newBuildSettings = await self.buildSettings(for: uri)
|
||||
let newBuildSettings = await self.buildSettings(for: uri, fallbackAfterTimeout: false)
|
||||
if newBuildSettings != buildSettingsForOpenFiles[uri] {
|
||||
// Close and re-open the document internally to inform sourcekitd to update the compile command. At the moment
|
||||
// there's no better way to do this.
|
||||
@@ -516,7 +517,7 @@ extension SwiftLanguageService {
|
||||
cancelInFlightPublishDiagnosticsTask(for: notification.textDocument.uri)
|
||||
await diagnosticReportManager.removeItemsFromCache(with: notification.textDocument.uri)
|
||||
|
||||
let buildSettings = await self.buildSettings(for: snapshot.uri)
|
||||
let buildSettings = await self.buildSettings(for: snapshot.uri, fallbackAfterTimeout: true)
|
||||
buildSettingsForOpenFiles[snapshot.uri] = buildSettings
|
||||
|
||||
let req = openDocumentSourcekitdRequest(snapshot: snapshot, compileCommand: buildSettings)
|
||||
@@ -578,7 +579,7 @@ extension SwiftLanguageService {
|
||||
}
|
||||
do {
|
||||
let snapshot = try await self.latestSnapshot(for: document)
|
||||
let buildSettings = await self.buildSettings(for: document)
|
||||
let buildSettings = await self.buildSettings(for: document, fallbackAfterTimeout: false)
|
||||
let diagnosticReport = try await self.diagnosticReportManager.diagnosticReport(
|
||||
for: snapshot,
|
||||
buildSettings: buildSettings
|
||||
@@ -688,7 +689,8 @@ extension SwiftLanguageService {
|
||||
package func hover(_ req: HoverRequest) async throws -> HoverResponse? {
|
||||
let uri = req.textDocument.uri
|
||||
let position = req.position
|
||||
let cursorInfoResults = try await cursorInfo(uri, position..<position).cursorInfo
|
||||
let cursorInfoResults = try await cursorInfo(uri, position..<position, fallbackSettingsAfterTimeout: false)
|
||||
.cursorInfo
|
||||
|
||||
let symbolDocumentations = cursorInfoResults.compactMap { (cursorInfo) -> String? in
|
||||
if let documentation = cursorInfo.documentation {
|
||||
@@ -891,6 +893,7 @@ extension SwiftLanguageService {
|
||||
let cursorInfoResponse = try await cursorInfo(
|
||||
params.textDocument.uri,
|
||||
params.range,
|
||||
fallbackSettingsAfterTimeout: true,
|
||||
additionalParameters: additionalCursorInfoParameters
|
||||
)
|
||||
|
||||
@@ -917,7 +920,7 @@ extension SwiftLanguageService {
|
||||
|
||||
func retrieveQuickFixCodeActions(_ params: CodeActionRequest) async throws -> [CodeAction] {
|
||||
let snapshot = try await self.latestSnapshot(for: params.textDocument.uri)
|
||||
let buildSettings = await self.buildSettings(for: params.textDocument.uri)
|
||||
let buildSettings = await self.buildSettings(for: params.textDocument.uri, fallbackAfterTimeout: true)
|
||||
let diagnosticReport = try await self.diagnosticReportManager.diagnosticReport(
|
||||
for: snapshot,
|
||||
buildSettings: buildSettings
|
||||
@@ -1010,7 +1013,8 @@ extension SwiftLanguageService {
|
||||
req.textDocument.uri.primaryFile ?? req.textDocument.uri
|
||||
)
|
||||
let snapshot = try await self.latestSnapshot(for: req.textDocument.uri)
|
||||
let buildSettings = await self.buildSettings(for: req.textDocument.uri)
|
||||
let buildSettings = await self.buildSettings(for: req.textDocument.uri, fallbackAfterTimeout: false)
|
||||
try Task.checkCancellation()
|
||||
let diagnosticReport = try await self.diagnosticReportManager.diagnosticReport(
|
||||
for: snapshot,
|
||||
buildSettings: buildSettings
|
||||
|
||||
@@ -21,6 +21,7 @@ extension SwiftLanguageService {
|
||||
let uri = req.textDocument.uri
|
||||
let snapshot = try documentManager.latestSnapshot(uri)
|
||||
let position = await self.adjustPositionToStartOfIdentifier(req.position, in: snapshot)
|
||||
return try await cursorInfo(uri, position..<position).cursorInfo.map { $0.symbolInfo }
|
||||
return try await cursorInfo(uri, position..<position, fallbackSettingsAfterTimeout: false)
|
||||
.cursorInfo.map { $0.symbolInfo }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,7 +89,8 @@ extension SwiftLanguageService {
|
||||
keys.request: requests.collectVariableType,
|
||||
keys.sourceFile: snapshot.uri.sourcekitdSourceFile,
|
||||
keys.primaryFile: snapshot.uri.primaryFile?.pseudoPath,
|
||||
keys.compilerArgs: await self.buildSettings(for: uri)?.compilerArgs as [SKDRequestValue]?,
|
||||
keys.compilerArgs: await self.buildSettings(for: uri, fallbackAfterTimeout: false)?.compilerArgs
|
||||
as [SKDRequestValue]?,
|
||||
])
|
||||
|
||||
if let range = range {
|
||||
|
||||
@@ -169,6 +169,8 @@ extension Collection where Element: Sendable {
|
||||
|
||||
package struct TimeoutError: Error, CustomStringConvertible {
|
||||
package var description: String { "Timed out" }
|
||||
|
||||
package init() {}
|
||||
}
|
||||
|
||||
/// Executes `body`. If it doesn't finish after `duration`, throws a `TimeoutError`.
|
||||
@@ -219,3 +221,47 @@ package func withTimeout<T: Sendable>(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Executes `body`. If it doesn't finish after `duration`, return `nil` and continue running body. When `body` returns
|
||||
/// a value after the timeout, `resultReceivedAfterTimeout` is called.
|
||||
///
|
||||
/// - Important: `body` will not be cancelled when the timeout is received. Use the other overload of `withTimeout` if
|
||||
/// `body` should be cancelled after `timeout`.
|
||||
package func withTimeout<T: Sendable>(
|
||||
_ timeout: Duration,
|
||||
body: @escaping @Sendable () async throws -> T?,
|
||||
resultReceivedAfterTimeout: @escaping @Sendable () async -> Void
|
||||
) async throws -> T? {
|
||||
let didHitTimeout = AtomicBool(initialValue: false)
|
||||
|
||||
let stream = AsyncThrowingStream<T?, Error> { continuation in
|
||||
Task {
|
||||
try await Task.sleep(for: timeout)
|
||||
didHitTimeout.value = true
|
||||
continuation.yield(nil)
|
||||
}
|
||||
|
||||
Task {
|
||||
do {
|
||||
let result = try await body()
|
||||
if didHitTimeout.value {
|
||||
await resultReceivedAfterTimeout()
|
||||
}
|
||||
continuation.yield(result)
|
||||
} catch {
|
||||
continuation.yield(with: .failure(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for try await value in stream {
|
||||
return value
|
||||
}
|
||||
// The only reason for the loop above to terminate is if the Task got cancelled or if the continuation finishes
|
||||
// (which it never does).
|
||||
if Task.isCancelled {
|
||||
throw CancellationError()
|
||||
} else {
|
||||
preconditionFailure("Continuation never finishes")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,7 +118,11 @@ final class BuildSystemManagerTests: XCTestCase {
|
||||
// Wait for the new build settings to settle before registering for change notifications
|
||||
await bsm.waitForUpToDateBuildGraph()
|
||||
await bsm.registerForChangeNotifications(for: a, language: .swift)
|
||||
assertEqual(await bsm.buildSettingsInferredFromMainFile(for: a, language: .swift)?.compilerArguments, ["x"])
|
||||
assertEqual(
|
||||
await bsm.buildSettingsInferredFromMainFile(for: a, language: .swift, fallbackAfterTimeout: false)?
|
||||
.compilerArguments,
|
||||
["x"]
|
||||
)
|
||||
|
||||
let changed = expectation(description: "changed settings")
|
||||
await del.setExpected([
|
||||
@@ -166,7 +170,10 @@ final class BuildSystemManagerTests: XCTestCase {
|
||||
let del = await BSMDelegate(bsm)
|
||||
let fallbackSettings = fallbackBuildSettings(for: a, language: .swift, options: .init())
|
||||
await bsm.registerForChangeNotifications(for: a, language: .swift)
|
||||
assertEqual(await bsm.buildSettingsInferredFromMainFile(for: a, language: .swift), fallbackSettings)
|
||||
assertEqual(
|
||||
await bsm.buildSettingsInferredFromMainFile(for: a, language: .swift, fallbackAfterTimeout: false),
|
||||
fallbackSettings
|
||||
)
|
||||
|
||||
let changed = expectation(description: "changed settings")
|
||||
await del.setExpected([(a, .swift, FileBuildSettings(compilerArguments: ["non-fallback", "args"]), changed)])
|
||||
@@ -212,7 +219,10 @@ final class BuildSystemManagerTests: XCTestCase {
|
||||
// Wait for the new build settings to settle before registering for change notifications
|
||||
await bsm.waitForUpToDateBuildGraph()
|
||||
await bsm.registerForChangeNotifications(for: h, language: .c)
|
||||
assertEqual(await bsm.buildSettingsInferredFromMainFile(for: h, language: .c)?.compilerArguments, ["C++ 1"])
|
||||
assertEqual(
|
||||
await bsm.buildSettingsInferredFromMainFile(for: h, language: .c, fallbackAfterTimeout: false)?.compilerArguments,
|
||||
["C++ 1"]
|
||||
)
|
||||
|
||||
await mainFiles.updateMainFiles(for: h, to: [cpp2])
|
||||
|
||||
@@ -281,8 +291,14 @@ final class BuildSystemManagerTests: XCTestCase {
|
||||
|
||||
let expectedArgsH1 = FileBuildSettings(compilerArguments: ["-xc++", cppArg, h1.pseudoPath])
|
||||
let expectedArgsH2 = FileBuildSettings(compilerArguments: ["-xc++", cppArg, h2.pseudoPath])
|
||||
assertEqual(await bsm.buildSettingsInferredFromMainFile(for: h1, language: .c), expectedArgsH1)
|
||||
assertEqual(await bsm.buildSettingsInferredFromMainFile(for: h2, language: .c), expectedArgsH2)
|
||||
assertEqual(
|
||||
await bsm.buildSettingsInferredFromMainFile(for: h1, language: .c, fallbackAfterTimeout: false),
|
||||
expectedArgsH1
|
||||
)
|
||||
assertEqual(
|
||||
await bsm.buildSettingsInferredFromMainFile(for: h2, language: .c, fallbackAfterTimeout: false),
|
||||
expectedArgsH2
|
||||
)
|
||||
|
||||
let newCppArg = "New C++ Main File"
|
||||
let changed1 = expectation(description: "initial settings h1 via cpp")
|
||||
@@ -362,7 +378,11 @@ private actor BSMDelegate: BuildSystemManagerDelegate {
|
||||
self.expected.remove(at: expectedIndex)
|
||||
|
||||
XCTAssertEqual(uri, expected.uri, file: expected.file, line: expected.line)
|
||||
let settings = await bsm.buildSettingsInferredFromMainFile(for: uri, language: expected.language)
|
||||
let settings = await bsm.buildSettingsInferredFromMainFile(
|
||||
for: uri,
|
||||
language: expected.language,
|
||||
fallbackAfterTimeout: false
|
||||
)
|
||||
XCTAssertEqual(settings, expected.settings, file: expected.file, line: expected.line)
|
||||
expected.expectation.fulfill()
|
||||
}
|
||||
|
||||
@@ -10,10 +10,12 @@
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
import BuildServerProtocol
|
||||
@_spi(Testing) import BuildSystemIntegration
|
||||
import LanguageServerProtocol
|
||||
import SKOptions
|
||||
import SKTestSupport
|
||||
import SourceKitLSP
|
||||
import TSCBasic
|
||||
import XCTest
|
||||
|
||||
@@ -160,4 +162,42 @@ final class FallbackBuildSystemTests: XCTestCase {
|
||||
|
||||
XCTAssertNil(fallbackBuildSettings(for: source, language: Language(rawValue: "unknown"), options: .init()))
|
||||
}
|
||||
|
||||
func testFallbackBuildSettingsWhileBuildSystemIsComputingBuildSettings() async throws {
|
||||
let fallbackResultsReceived = WrappedSemaphore(name: "Fallback results received")
|
||||
let project = try await SwiftPMTestProject(
|
||||
files: [
|
||||
"Test.swift": """
|
||||
let x: 1️⃣String2️⃣ = 1
|
||||
"""
|
||||
],
|
||||
testHooks: TestHooks(
|
||||
buildSystemTestHooks: BuildSystemTestHooks(
|
||||
handleRequest: { request in
|
||||
if request is TextDocumentSourceKitOptionsRequest {
|
||||
fallbackResultsReceived.waitOrXCTFail()
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
let (uri, positions) = try project.openDocument("Test.swift")
|
||||
|
||||
let documentHighlight = try await project.testClient.send(
|
||||
DocumentHighlightRequest(textDocument: TextDocumentIdentifier(uri), position: positions["1️⃣"])
|
||||
)
|
||||
XCTAssertEqual(documentHighlight, [DocumentHighlight(range: positions["1️⃣"]..<positions["2️⃣"], kind: .read)])
|
||||
|
||||
fallbackResultsReceived.signal()
|
||||
|
||||
try await repeatUntilExpectedResult {
|
||||
let diagsAfterBuildSettings = try await project.testClient.send(
|
||||
DocumentDiagnosticsRequest(textDocument: TextDocumentIdentifier(uri))
|
||||
)
|
||||
return diagsAfterBuildSettings.fullReport?.items.map(\.message) == [
|
||||
"Cannot convert value of type 'Int' to specified type 'String'"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,7 +121,11 @@ final class SwiftPMBuildSystemTests: XCTestCase {
|
||||
assertNotNil(await buildSystemManager.initializationData?.indexDatabasePath)
|
||||
assertNotNil(await buildSystemManager.initializationData?.indexStorePath)
|
||||
let arguments = try await unwrap(
|
||||
buildSystemManager.buildSettingsInferredFromMainFile(for: aswift.asURI, language: .swift)
|
||||
buildSystemManager.buildSettingsInferredFromMainFile(
|
||||
for: aswift.asURI,
|
||||
language: .swift,
|
||||
fallbackAfterTimeout: false
|
||||
)
|
||||
).compilerArguments
|
||||
|
||||
assertArgumentsContain("-module-name", "lib", arguments: arguments)
|
||||
@@ -191,7 +195,8 @@ final class SwiftPMBuildSystemTests: XCTestCase {
|
||||
let arguments = try await unwrap(
|
||||
buildSystemManager.buildSettingsInferredFromMainFile(
|
||||
for: DocumentURI(urlWithPlusEscaped),
|
||||
language: .swift
|
||||
language: .swift,
|
||||
fallbackAfterTimeout: false
|
||||
)
|
||||
)
|
||||
.compilerArguments
|
||||
@@ -246,7 +251,11 @@ final class SwiftPMBuildSystemTests: XCTestCase {
|
||||
let aswift = packageRoot.appending(components: "Sources", "lib", "a.swift")
|
||||
|
||||
let arguments = try await unwrap(
|
||||
buildSystemManager.buildSettingsInferredFromMainFile(for: aswift.asURI, language: .swift)
|
||||
buildSystemManager.buildSettingsInferredFromMainFile(
|
||||
for: aswift.asURI,
|
||||
language: .swift,
|
||||
fallbackAfterTimeout: false
|
||||
)
|
||||
).compilerArguments
|
||||
|
||||
assertArgumentsContain("-typecheck", arguments: arguments)
|
||||
@@ -322,7 +331,11 @@ final class SwiftPMBuildSystemTests: XCTestCase {
|
||||
|
||||
let source = try resolveSymlinks(packageRoot.appending(component: "Package.swift"))
|
||||
let arguments = try await unwrap(
|
||||
buildSystemManager.buildSettingsInferredFromMainFile(for: source.asURI, language: .swift)
|
||||
buildSystemManager.buildSettingsInferredFromMainFile(
|
||||
for: source.asURI,
|
||||
language: .swift,
|
||||
fallbackAfterTimeout: false
|
||||
)
|
||||
).compilerArguments
|
||||
|
||||
assertArgumentsContain("-swift-version", "4.2", arguments: arguments)
|
||||
@@ -360,12 +373,20 @@ final class SwiftPMBuildSystemTests: XCTestCase {
|
||||
let bswift = packageRoot.appending(components: "Sources", "lib", "b.swift")
|
||||
|
||||
let argumentsA = try await unwrap(
|
||||
buildSystemManager.buildSettingsInferredFromMainFile(for: aswift.asURI, language: .swift)
|
||||
buildSystemManager.buildSettingsInferredFromMainFile(
|
||||
for: aswift.asURI,
|
||||
language: .swift,
|
||||
fallbackAfterTimeout: false
|
||||
)
|
||||
).compilerArguments
|
||||
assertArgumentsContain(aswift.pathString, arguments: argumentsA)
|
||||
assertArgumentsContain(bswift.pathString, arguments: argumentsA)
|
||||
let argumentsB = try await unwrap(
|
||||
buildSystemManager.buildSettingsInferredFromMainFile(for: aswift.asURI, language: .swift)
|
||||
buildSystemManager.buildSettingsInferredFromMainFile(
|
||||
for: aswift.asURI,
|
||||
language: .swift,
|
||||
fallbackAfterTimeout: false
|
||||
)
|
||||
).compilerArguments
|
||||
assertArgumentsContain(aswift.pathString, arguments: argumentsB)
|
||||
assertArgumentsContain(bswift.pathString, arguments: argumentsB)
|
||||
@@ -408,7 +429,11 @@ final class SwiftPMBuildSystemTests: XCTestCase {
|
||||
let aswift = packageRoot.appending(components: "Sources", "libA", "a.swift")
|
||||
let bswift = packageRoot.appending(components: "Sources", "libB", "b.swift")
|
||||
let arguments = try await unwrap(
|
||||
buildSystemManager.buildSettingsInferredFromMainFile(for: aswift.asURI, language: .swift)
|
||||
buildSystemManager.buildSettingsInferredFromMainFile(
|
||||
for: aswift.asURI,
|
||||
language: .swift,
|
||||
fallbackAfterTimeout: false
|
||||
)
|
||||
).compilerArguments
|
||||
assertArgumentsContain(aswift.pathString, arguments: arguments)
|
||||
assertArgumentsDoNotContain(bswift.pathString, arguments: arguments)
|
||||
@@ -421,7 +446,11 @@ final class SwiftPMBuildSystemTests: XCTestCase {
|
||||
)
|
||||
|
||||
let argumentsB = try await unwrap(
|
||||
buildSystemManager.buildSettingsInferredFromMainFile(for: bswift.asURI, language: .swift)
|
||||
buildSystemManager.buildSettingsInferredFromMainFile(
|
||||
for: bswift.asURI,
|
||||
language: .swift,
|
||||
fallbackAfterTimeout: false
|
||||
)
|
||||
).compilerArguments
|
||||
assertArgumentsContain(bswift.pathString, arguments: argumentsB)
|
||||
assertArgumentsDoNotContain(aswift.pathString, arguments: argumentsB)
|
||||
@@ -462,15 +491,26 @@ final class SwiftPMBuildSystemTests: XCTestCase {
|
||||
|
||||
let aswift = packageRoot.appending(components: "Sources", "libA", "a.swift")
|
||||
let bswift = packageRoot.appending(components: "Sources", "libB", "b.swift")
|
||||
assertNotNil(await buildSystemManager.buildSettingsInferredFromMainFile(for: aswift.asURI, language: .swift))
|
||||
assertNotNil(
|
||||
await buildSystemManager.buildSettingsInferredFromMainFile(
|
||||
for: aswift.asURI,
|
||||
language: .swift,
|
||||
fallbackAfterTimeout: false
|
||||
)
|
||||
)
|
||||
assertEqual(
|
||||
await buildSystemManager.buildSettingsInferredFromMainFile(for: bswift.asURI, language: .swift)?.isFallback,
|
||||
await buildSystemManager.buildSettingsInferredFromMainFile(
|
||||
for: bswift.asURI,
|
||||
language: .swift,
|
||||
fallbackAfterTimeout: false
|
||||
)?.isFallback,
|
||||
true
|
||||
)
|
||||
assertEqual(
|
||||
await buildSystemManager.buildSettingsInferredFromMainFile(
|
||||
for: DocumentURI(URL(string: "https://www.apple.com")!),
|
||||
language: .swift
|
||||
language: .swift,
|
||||
fallbackAfterTimeout: false
|
||||
)?.isFallback,
|
||||
true
|
||||
)
|
||||
@@ -515,7 +555,11 @@ final class SwiftPMBuildSystemTests: XCTestCase {
|
||||
|
||||
for file in [acxx, header] {
|
||||
let args = try await unwrap(
|
||||
buildSystemManager.buildSettingsInferredFromMainFile(for: file.asURI, language: .cpp)
|
||||
buildSystemManager.buildSettingsInferredFromMainFile(
|
||||
for: file.asURI,
|
||||
language: .cpp,
|
||||
fallbackAfterTimeout: false
|
||||
)
|
||||
).compilerArguments
|
||||
|
||||
assertArgumentsContain("-std=c++14", arguments: args)
|
||||
@@ -588,7 +632,11 @@ final class SwiftPMBuildSystemTests: XCTestCase {
|
||||
|
||||
let aswift = packageRoot.appending(components: "Sources", "lib", "a.swift")
|
||||
let arguments = try await unwrap(
|
||||
buildSystemManager.buildSettingsInferredFromMainFile(for: aswift.asURI, language: .swift)
|
||||
buildSystemManager.buildSettingsInferredFromMainFile(
|
||||
for: aswift.asURI,
|
||||
language: .swift,
|
||||
fallbackAfterTimeout: false
|
||||
)
|
||||
).compilerArguments
|
||||
assertArgumentsContain("-target", arguments: arguments) // Only one!
|
||||
|
||||
@@ -642,10 +690,18 @@ final class SwiftPMBuildSystemTests: XCTestCase {
|
||||
let manifest = packageRoot.appending(components: "Package.swift")
|
||||
|
||||
let argumentsFromSymlink = try await unwrap(
|
||||
buildSystemManager.buildSettingsInferredFromMainFile(for: aswiftSymlink.asURI, language: .swift)
|
||||
buildSystemManager.buildSettingsInferredFromMainFile(
|
||||
for: aswiftSymlink.asURI,
|
||||
language: .swift,
|
||||
fallbackAfterTimeout: false
|
||||
)
|
||||
).compilerArguments
|
||||
let argumentsFromReal = try await unwrap(
|
||||
buildSystemManager.buildSettingsInferredFromMainFile(for: aswiftReal.asURI, language: .swift)
|
||||
buildSystemManager.buildSettingsInferredFromMainFile(
|
||||
for: aswiftReal.asURI,
|
||||
language: .swift,
|
||||
fallbackAfterTimeout: false
|
||||
)
|
||||
).compilerArguments
|
||||
|
||||
// The arguments retrieved from the symlink and the real document should be the same, except that both should
|
||||
@@ -663,7 +719,11 @@ final class SwiftPMBuildSystemTests: XCTestCase {
|
||||
assertArgumentsDoNotContain(aswiftSymlink.pathString, arguments: argumentsFromReal)
|
||||
|
||||
let argsManifest = try await unwrap(
|
||||
buildSystemManager.buildSettingsInferredFromMainFile(for: manifest.asURI, language: .swift)
|
||||
buildSystemManager.buildSettingsInferredFromMainFile(
|
||||
for: manifest.asURI,
|
||||
language: .swift,
|
||||
fallbackAfterTimeout: false
|
||||
)
|
||||
).compilerArguments
|
||||
XCTAssertNotNil(argsManifest)
|
||||
|
||||
@@ -717,7 +777,8 @@ final class SwiftPMBuildSystemTests: XCTestCase {
|
||||
let args = try unwrap(
|
||||
await buildSystemManager.buildSettingsInferredFromMainFile(
|
||||
for: symlinkRoot.appending(components: file).asURI,
|
||||
language: .cpp
|
||||
language: .cpp,
|
||||
fallbackAfterTimeout: false
|
||||
)?
|
||||
.compilerArguments
|
||||
)
|
||||
@@ -756,7 +817,11 @@ final class SwiftPMBuildSystemTests: XCTestCase {
|
||||
|
||||
let aswift = packageRoot.appending(components: "Sources", "lib", "a.swift")
|
||||
let arguments = try await unwrap(
|
||||
buildSystemManager.buildSettingsInferredFromMainFile(for: aswift.asURI, language: .swift)
|
||||
buildSystemManager.buildSettingsInferredFromMainFile(
|
||||
for: aswift.asURI,
|
||||
language: .swift,
|
||||
fallbackAfterTimeout: false
|
||||
)
|
||||
)
|
||||
.compilerArguments
|
||||
assertArgumentsContain(aswift.pathString, arguments: arguments)
|
||||
@@ -830,7 +895,11 @@ final class SwiftPMBuildSystemTests: XCTestCase {
|
||||
|
||||
assertNotNil(await buildSystemManager.initializationData?.indexStorePath)
|
||||
let arguments = try await unwrap(
|
||||
buildSystemManager.buildSettingsInferredFromMainFile(for: aswift.asURI, language: .swift)
|
||||
buildSystemManager.buildSettingsInferredFromMainFile(
|
||||
for: aswift.asURI,
|
||||
language: .swift,
|
||||
fallbackAfterTimeout: false
|
||||
)
|
||||
).compilerArguments
|
||||
|
||||
// Plugins get compiled with the same compiler arguments as the package manifest
|
||||
|
||||
Reference in New Issue
Block a user