Skip to content

Commit 30a16ec

Browse files
authored
Merge pull request #1326 from ahoppen/index-log
Produce an index log for the client
2 parents 6aecd22 + 3e6319c commit 30a16ec

19 files changed

+304
-91
lines changed

.gitignore

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@
22
default.profraw
33
Package.resolved
44
/.build
5-
/.index-build
6-
/.linux-build
5+
/.*-build
76
/Packages
87
/*.xcodeproj
98
/*.sublime-project

Sources/SKCore/BuildServerBuildSystem.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,10 @@ extension BuildServerBuildSystem: BuildSystem {
289289
return nil
290290
}
291291

292-
public func prepare(targets: [ConfiguredTarget]) async throws {
292+
public func prepare(
293+
targets: [ConfiguredTarget],
294+
indexProcessDidProduceResult: @Sendable (IndexProcessResult) -> Void
295+
) async throws {
293296
throw PrepareNotSupportedError()
294297
}
295298

Sources/SKCore/BuildSystem.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,10 @@ public protocol BuildSystem: AnyObject, Sendable {
158158

159159
/// Prepare the given targets for indexing and semantic functionality. This should build all swift modules of target
160160
/// dependencies.
161-
func prepare(targets: [ConfiguredTarget]) async throws
161+
func prepare(
162+
targets: [ConfiguredTarget],
163+
indexProcessDidProduceResult: @Sendable (IndexProcessResult) -> Void
164+
) async throws
162165

163166
/// If the build system has knowledge about the language that this document should be compiled in, return it.
164167
///

Sources/SKCore/BuildSystemManager.swift

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -231,8 +231,11 @@ extension BuildSystemManager {
231231
return await buildSystem?.targets(dependingOn: targets)
232232
}
233233

234-
public func prepare(targets: [ConfiguredTarget]) async throws {
235-
try await buildSystem?.prepare(targets: targets)
234+
public func prepare(
235+
targets: [ConfiguredTarget],
236+
indexProcessDidProduceResult: @Sendable (IndexProcessResult) -> Void
237+
) async throws {
238+
try await buildSystem?.prepare(targets: targets, indexProcessDidProduceResult: indexProcessDidProduceResult)
236239
}
237240

238241
public func registerForChangeNotifications(for uri: DocumentURI, language: Language) async {

Sources/SKCore/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ add_library(SKCore STATIC
1010
Debouncer.swift
1111
FallbackBuildSystem.swift
1212
FileBuildSettings.swift
13+
IndexProcessResult.swift
1314
MainFilesProvider.swift
1415
PathPrefixMapping.swift
1516
SplitShellCommand.swift

Sources/SKCore/CompilationDatabaseBuildSystem.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,10 @@ extension CompilationDatabaseBuildSystem: BuildSystem {
125125
return [ConfiguredTarget(targetID: "dummy", runDestinationID: "dummy")]
126126
}
127127

128-
public func prepare(targets: [ConfiguredTarget]) async throws {
128+
public func prepare(
129+
targets: [ConfiguredTarget],
130+
indexProcessDidProduceResult: @Sendable (IndexProcessResult) -> Void
131+
) async throws {
129132
throw PrepareNotSupportedError()
130133
}
131134

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
import struct TSCBasic.ProcessResult
14+
15+
/// Result of a process that prepares a target or updates the index store. To be shown in the build log.
16+
///
17+
/// Abstracted over a `ProcessResult` to facilitate build systems that don't spawn a new process to prepare a target but
18+
/// prepare it from a build graph they have loaded in-process.
19+
public struct IndexProcessResult {
20+
/// A human-readable description of what the process was trying to achieve, like `Preparing MyTarget`
21+
public let taskDescription: String
22+
23+
/// The command that was run to produce the result.
24+
public let command: String
25+
26+
/// The output that the process produced.
27+
public let output: String
28+
29+
/// Whether the process failed.
30+
public let failed: Bool
31+
32+
/// The duration it took for the process to execute.
33+
public let duration: Duration
34+
35+
public init(taskDescription: String, command: String, output: String, failed: Bool, duration: Duration) {
36+
self.taskDescription = taskDescription
37+
self.command = command
38+
self.output = output
39+
self.failed = failed
40+
self.duration = duration
41+
}
42+
43+
public init(taskDescription: String, processResult: ProcessResult, start: ContinuousClock.Instant) {
44+
let stdout = (try? String(bytes: processResult.output.get(), encoding: .utf8)) ?? "<failed to decode stdout>"
45+
let stderr = (try? String(bytes: processResult.stderrOutput.get(), encoding: .utf8)) ?? "<failed to decode stderr>"
46+
var outputComponents: [String] = []
47+
if !stdout.isEmpty {
48+
outputComponents.append(
49+
"""
50+
Stdout:
51+
\(stdout)
52+
"""
53+
)
54+
}
55+
if !stderr.isEmpty {
56+
outputComponents.append(
57+
"""
58+
Stderr:
59+
\(stderr)
60+
"""
61+
)
62+
}
63+
self.init(
64+
taskDescription: taskDescription,
65+
command: processResult.arguments.joined(separator: " "),
66+
output: outputComponents.joined(separator: "\n\n"),
67+
failed: processResult.exitStatus != .terminated(code: 0),
68+
duration: start.duration(to: .now)
69+
)
70+
}
71+
}

Sources/SKSwiftPMWorkspace/SwiftPMBuildSystem.swift

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -459,17 +459,23 @@ extension SwiftPMBuildSystem: SKCore.BuildSystem {
459459
}
460460
}
461461

462-
public func prepare(targets: [ConfiguredTarget]) async throws {
462+
public func prepare(
463+
targets: [ConfiguredTarget],
464+
indexProcessDidProduceResult: @Sendable (IndexProcessResult) -> Void
465+
) async throws {
463466
// TODO (indexing): Support preparation of multiple targets at once.
464467
// https://github.com/apple/sourcekit-lsp/issues/1262
465468
for target in targets {
466-
try await prepare(singleTarget: target)
469+
try await prepare(singleTarget: target, indexProcessDidProduceResult: indexProcessDidProduceResult)
467470
}
468471
let filesInPreparedTargets = targets.flatMap { self.targets[$0]?.buildTarget.sources ?? [] }
469472
await fileDependenciesUpdatedDebouncer.scheduleCall(Set(filesInPreparedTargets.map(DocumentURI.init)))
470473
}
471474

472-
private func prepare(singleTarget target: ConfiguredTarget) async throws {
475+
private func prepare(
476+
singleTarget target: ConfiguredTarget,
477+
indexProcessDidProduceResult: @Sendable (IndexProcessResult) -> Void
478+
) async throws {
473479
// TODO (indexing): Add a proper 'prepare' job in SwiftPM instead of building the target.
474480
// https://github.com/apple/sourcekit-lsp/issues/1254
475481
guard let toolchain = await toolchainRegistry.default else {
@@ -492,8 +498,16 @@ extension SwiftPMBuildSystem: SKCore.BuildSystem {
492498
if Task.isCancelled {
493499
return
494500
}
501+
let start = ContinuousClock.now
495502
let process = try Process.launch(arguments: arguments, workingDirectory: nil)
496503
let result = try await process.waitUntilExitSendingSigIntOnTaskCancellation()
504+
indexProcessDidProduceResult(
505+
IndexProcessResult(
506+
taskDescription: "Preparing \(target.targetID) for \(target.runDestinationID)",
507+
processResult: result,
508+
start: start
509+
)
510+
)
497511
switch result.exitStatus.exhaustivelySwitchable {
498512
case .terminated(code: 0):
499513
break

Sources/SKTestSupport/TestSourceKitLSPClient.swift

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -243,25 +243,18 @@ public final class TestSourceKitLSPClient: MessageHandler {
243243
return try await nextNotification(ofType: PublishDiagnosticsNotification.self, timeout: timeout)
244244
}
245245

246-
private struct CastError: Error, CustomStringConvertible {
247-
let expectedType: any NotificationType.Type
248-
let actualType: any NotificationType.Type
249-
250-
var description: String { "Expected a \(expectedType) but got '\(actualType)'" }
251-
}
252-
253-
/// Await the next diagnostic notification sent to the client.
254-
///
255-
/// If the next notification is not of the expected type, this methods throws.
246+
/// Waits for the next notification of the given type to be sent to the client. Ignores any notifications that are of
247+
/// a different type.
256248
public func nextNotification<ExpectedNotificationType: NotificationType>(
257249
ofType: ExpectedNotificationType.Type,
258250
timeout: TimeInterval = defaultTimeout
259251
) async throws -> ExpectedNotificationType {
260-
let nextNotification = try await nextNotification(timeout: timeout)
261-
guard let notification = nextNotification as? ExpectedNotificationType else {
262-
throw CastError(expectedType: ExpectedNotificationType.self, actualType: type(of: nextNotification))
252+
while true {
253+
let nextNotification = try await nextNotification(timeout: timeout)
254+
if let notification = nextNotification as? ExpectedNotificationType {
255+
return notification
256+
}
263257
}
264-
return notification
265258
}
266259

267260
/// Handle the next request that is sent to the client with the given handler.

Sources/SemanticIndex/PreparationTaskDescription.swift

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ public struct PreparationTaskDescription: IndexTaskDescription {
3737

3838
private let preparationUpToDateStatus: IndexUpToDateStatusManager<ConfiguredTarget>
3939

40+
/// See `SemanticIndexManager.indexProcessDidProduceResult`
41+
private let indexProcessDidProduceResult: @Sendable (IndexProcessResult) -> Void
42+
4043
/// Test hooks that should be called when the preparation task finishes.
4144
private let testHooks: IndexTestHooks
4245

@@ -57,11 +60,13 @@ public struct PreparationTaskDescription: IndexTaskDescription {
5760
targetsToPrepare: [ConfiguredTarget],
5861
buildSystemManager: BuildSystemManager,
5962
preparationUpToDateStatus: IndexUpToDateStatusManager<ConfiguredTarget>,
63+
indexProcessDidProduceResult: @escaping @Sendable (IndexProcessResult) -> Void,
6064
testHooks: IndexTestHooks
6165
) {
6266
self.targetsToPrepare = targetsToPrepare
6367
self.buildSystemManager = buildSystemManager
6468
self.preparationUpToDateStatus = preparationUpToDateStatus
69+
self.indexProcessDidProduceResult = indexProcessDidProduceResult
6570
self.testHooks = testHooks
6671
}
6772

@@ -89,7 +94,10 @@ public struct PreparationTaskDescription: IndexTaskDescription {
8994
)
9095
let startDate = Date()
9196
do {
92-
try await buildSystemManager.prepare(targets: targetsToPrepare)
97+
try await buildSystemManager.prepare(
98+
targets: targetsToPrepare,
99+
indexProcessDidProduceResult: indexProcessDidProduceResult
100+
)
93101
} catch {
94102
logger.error(
95103
"Preparation failed: \(error.forLogging)"

0 commit comments

Comments
 (0)