Skip to content

Commit 6475635

Browse files
BuildSystem delegate API (#3209)
* Rename public BuildDelegate to internal BuildOperationDelegate * Make BuildOperationDelegate internal class * Setup delegate links * Outline delegate protocol * Call BuildSystemDelegate methods * Add buildSystemDidCancel call * Report progress updates * Set delegate at any point * Public default delegate implementation * Explicit module name * Remove module name from function parameters * Track progress update with raw messages * Call callback on delegate queue * Implement BuildSystemDelegate for XCBuildSystem * Clarify in the comment what build system is referred * Update CMake scripts with new files * Disambiguate type
1 parent 91fac80 commit 6475635

9 files changed

+186
-52
lines changed

Sources/Build/BuildOperation.swift

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ import TSCUtility
1919

2020
public final class BuildOperation: PackageStructureDelegate, SPMBuildCore.BuildSystem, BuildErrorAdviceProvider {
2121

22+
/// The delegate used by the build system.
23+
public weak var delegate: SPMBuildCore.BuildSystemDelegate?
24+
2225
/// The build parameters.
2326
public let buildParameters: BuildParameters
2427

@@ -28,10 +31,10 @@ public final class BuildOperation: PackageStructureDelegate, SPMBuildCore.BuildS
2831
/// The closure for loading the package graph.
2932
let packageGraphLoader: () throws -> PackageGraph
3033

31-
/// The build delegate reference.
32-
private var buildDelegate: BuildDelegate?
34+
/// The llbuild build delegate reference.
35+
private var buildSystemDelegate: BuildOperationBuildSystemDelegateHandler?
3336

34-
/// The build system reference.
37+
/// The llbuild build system reference.
3538
private var buildSystem: SPMLLBuild.BuildSystem?
3639

3740
/// If build manifest caching should be enabled.
@@ -116,7 +119,8 @@ public final class BuildOperation: PackageStructureDelegate, SPMBuildCore.BuildS
116119
let llbuildTarget = try computeLLBuildTargetName(for: subset)
117120
let success = buildSystem.build(target: llbuildTarget)
118121

119-
buildDelegate?.progressAnimation.complete(success: success)
122+
buildSystemDelegate?.progressAnimation.complete(success: success)
123+
delegate?.buildSystem(self, didFinishWithResult: success)
120124
guard success else { throw Diagnostics.fatalError }
121125

122126
// Create backwards-compatibilty symlink to old build path.
@@ -195,24 +199,27 @@ public final class BuildOperation: PackageStructureDelegate, SPMBuildCore.BuildS
195199
)
196200

197201
// Create the build delegate.
198-
let buildDelegate = BuildDelegate(
202+
let buildSystemDelegate = BuildOperationBuildSystemDelegateHandler(
203+
buildSystem: self,
199204
bctx: bctx,
200205
diagnostics: diagnostics,
201206
outputStream: self.stdoutStream,
202-
progressAnimation: progressAnimation
207+
progressAnimation: progressAnimation,
208+
delegate: self.delegate
203209
)
204-
self.buildDelegate = buildDelegate
205-
buildDelegate.isVerbose = isVerbose
210+
self.buildSystemDelegate = buildSystemDelegate
211+
buildSystemDelegate.isVerbose = isVerbose
206212

207213
let databasePath = buildParameters.dataPath.appending(component: "build.db").pathString
208-
let buildSystem = BuildSystem(
214+
let buildSystem = SPMLLBuild.BuildSystem(
209215
buildFile: buildParameters.llbuildManifest.pathString,
210216
databaseFile: databasePath,
211-
delegate: buildDelegate,
217+
delegate: buildSystemDelegate,
212218
schedulerLanes: buildParameters.jobs
213219
)
214-
buildDelegate.onCommmandFailure = {
220+
buildSystemDelegate.onCommmandFailure = {
215221
buildSystem.cancel()
222+
self.delegate?.buildSystemDidCancel(self)
216223
}
217224

218225
return buildSystem

Sources/Build/BuildDelegate.swift renamed to Sources/Build/BuildOperationBuildSystemDelegateHandler.swift

Lines changed: 81 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -355,12 +355,15 @@ final class CopyCommand: CustomLLBuildCommand {
355355
}
356356
}
357357

358-
public final class BuildDelegate: BuildSystemDelegate, SwiftCompilerOutputParserDelegate {
358+
/// Convenient llbuild build system delegate implementation
359+
final class BuildOperationBuildSystemDelegateHandler: llbuildSwift.BuildSystemDelegate, SwiftCompilerOutputParserDelegate {
359360
private let diagnostics: DiagnosticsEngine
360-
public var outputStream: ThreadSafeOutputByteStream
361-
public var progressAnimation: ProgressAnimationProtocol
362-
public var onCommmandFailure: (() -> Void)?
363-
public var isVerbose: Bool = false
361+
var outputStream: ThreadSafeOutputByteStream
362+
var progressAnimation: ProgressAnimationProtocol
363+
var onCommmandFailure: (() -> Void)?
364+
var isVerbose: Bool = false
365+
weak var delegate: SPMBuildCore.BuildSystemDelegate?
366+
private let buildSystem: SPMBuildCore.BuildSystem
364367
private let queue = DispatchQueue(label: "org.swift.swiftpm.build-delegate")
365368
private var taskTracker = CommandTaskTracker()
366369
private var errorMessagesByTarget: [String: [String]] = [:]
@@ -371,30 +374,42 @@ public final class BuildDelegate: BuildSystemDelegate, SwiftCompilerOutputParser
371374
/// The build execution context.
372375
private let buildExecutionContext: BuildExecutionContext
373376

374-
public init(
377+
init(
378+
buildSystem: SPMBuildCore.BuildSystem,
375379
bctx: BuildExecutionContext,
376380
diagnostics: DiagnosticsEngine,
377381
outputStream: OutputByteStream,
378-
progressAnimation: ProgressAnimationProtocol
382+
progressAnimation: ProgressAnimationProtocol,
383+
delegate: SPMBuildCore.BuildSystemDelegate?
379384
) {
380385
self.diagnostics = diagnostics
381386
// FIXME: Implement a class convenience initializer that does this once they are supported
382387
// https://forums.swift.org/t/allow-self-x-in-class-convenience-initializers/15924
383388
self.outputStream = outputStream as? ThreadSafeOutputByteStream ?? ThreadSafeOutputByteStream(outputStream)
384389
self.progressAnimation = progressAnimation
385390
self.buildExecutionContext = bctx
391+
self.delegate = delegate
392+
self.buildSystem = buildSystem
386393

387394
let swiftParsers = bctx.buildDescription?.swiftCommands.mapValues { tool in
388395
SwiftCompilerOutputParser(targetName: tool.moduleName, delegate: self)
389396
} ?? [:]
390397
self.swiftParsers = swiftParsers
398+
399+
self.taskTracker.onTaskProgressUpdateText = { progressText, _ in
400+
self.queue.async {
401+
self.delegate?.buildSystem(self.buildSystem, didUpdateTaskProgress: progressText)
402+
}
403+
}
391404
}
392405

393-
public var fs: SPMLLBuild.FileSystem? {
406+
// MARK: llbuildSwift.BuildSystemDelegate
407+
408+
var fs: SPMLLBuild.FileSystem? {
394409
return nil
395410
}
396411

397-
public func lookupTool(_ name: String) -> Tool? {
412+
func lookupTool(_ name: String) -> Tool? {
398413
switch name {
399414
case TestDiscoveryTool.name:
400415
return InProcessTool(buildExecutionContext, type: TestDiscoveryCommand.self)
@@ -407,11 +422,11 @@ public final class BuildDelegate: BuildSystemDelegate, SwiftCompilerOutputParser
407422
}
408423
}
409424

410-
public func hadCommandFailure() {
425+
func hadCommandFailure() {
411426
onCommmandFailure?()
412427
}
413428

414-
public func handleDiagnostic(_ diagnostic: SPMLLBuild.Diagnostic) {
429+
func handleDiagnostic(_ diagnostic: SPMLLBuild.Diagnostic) {
415430
switch diagnostic.kind {
416431
case .note:
417432
diagnostics.emit(note: diagnostic.message)
@@ -424,7 +439,7 @@ public final class BuildDelegate: BuildSystemDelegate, SwiftCompilerOutputParser
424439
}
425440
}
426441

427-
public func commandStatusChanged(_ command: SPMLLBuild.Command, kind: CommandStatusKind) {
442+
func commandStatusChanged(_ command: SPMLLBuild.Command, kind: CommandStatusKind) {
428443
guard !isVerbose else { return }
429444
guard command.shouldShowStatus else { return }
430445
guard !swiftParsers.keys.contains(command.name) else { return }
@@ -435,69 +450,75 @@ public final class BuildDelegate: BuildSystemDelegate, SwiftCompilerOutputParser
435450
}
436451
}
437452

438-
public func commandPreparing(_ command: SPMLLBuild.Command) {
453+
func commandPreparing(_ command: SPMLLBuild.Command) {
454+
queue.async {
455+
self.delegate?.buildSystem(self.buildSystem, willStartCommand: BuildSystemCommand(command))
456+
}
439457
}
440458

441-
public func commandStarted(_ command: SPMLLBuild.Command) {
459+
func commandStarted(_ command: SPMLLBuild.Command) {
442460
guard command.shouldShowStatus else { return }
443461

444462
queue.async {
463+
self.delegate?.buildSystem(self.buildSystem, didStartCommand: BuildSystemCommand(command))
445464
if self.isVerbose {
446465
self.outputStream <<< command.verboseDescription <<< "\n"
447466
self.outputStream.flush()
448467
}
449468
}
450469
}
451470

452-
public func shouldCommandStart(_ command: SPMLLBuild.Command) -> Bool {
471+
func shouldCommandStart(_ command: SPMLLBuild.Command) -> Bool {
453472
return true
454473
}
455474

456-
public func commandFinished(_ command: SPMLLBuild.Command, result: CommandResult) {
457-
guard !isVerbose else { return }
475+
func commandFinished(_ command: SPMLLBuild.Command, result: CommandResult) {
458476
guard command.shouldShowStatus else { return }
459477
guard !swiftParsers.keys.contains(command.name) else { return }
460478

461479
queue.async {
462-
let targetName = self.swiftParsers[command.name]?.targetName
463-
self.taskTracker.commandFinished(command, result: result, targetName: targetName)
464-
self.updateProgress()
480+
self.delegate?.buildSystem(self.buildSystem, didFinishCommand: BuildSystemCommand(command))
481+
482+
if !self.isVerbose {
483+
let targetName = self.swiftParsers[command.name]?.targetName
484+
self.taskTracker.commandFinished(command, result: result, targetName: targetName)
485+
self.updateProgress()
486+
}
465487
}
466488
}
467489

468-
public func commandHadError(_ command: SPMLLBuild.Command, message: String) {
490+
func commandHadError(_ command: SPMLLBuild.Command, message: String) {
469491
diagnostics.emit(error: message)
470492
}
471493

472-
public func commandHadNote(_ command: SPMLLBuild.Command, message: String) {
473-
// FIXME: This is wrong.
474-
diagnostics.emit(warning: message)
494+
func commandHadNote(_ command: SPMLLBuild.Command, message: String) {
495+
diagnostics.emit(note: message)
475496
}
476497

477-
public func commandHadWarning(_ command: SPMLLBuild.Command, message: String) {
498+
func commandHadWarning(_ command: SPMLLBuild.Command, message: String) {
478499
diagnostics.emit(warning: message)
479500
}
480501

481-
public func commandCannotBuildOutputDueToMissingInputs(
502+
func commandCannotBuildOutputDueToMissingInputs(
482503
_ command: SPMLLBuild.Command,
483504
output: BuildKey,
484505
inputs: [BuildKey]
485506
) {
486507
diagnostics.emit(.missingInputs(output: output, inputs: inputs))
487508
}
488509

489-
public func cannotBuildNodeDueToMultipleProducers(output: BuildKey, commands: [SPMLLBuild.Command]) {
510+
func cannotBuildNodeDueToMultipleProducers(output: BuildKey, commands: [SPMLLBuild.Command]) {
490511
diagnostics.emit(.multipleProducers(output: output, commands: commands))
491512
}
492513

493-
public func commandProcessStarted(_ command: SPMLLBuild.Command, process: ProcessHandle) {
514+
func commandProcessStarted(_ command: SPMLLBuild.Command, process: ProcessHandle) {
494515
}
495516

496-
public func commandProcessHadError(_ command: SPMLLBuild.Command, process: ProcessHandle, message: String) {
517+
func commandProcessHadError(_ command: SPMLLBuild.Command, process: ProcessHandle, message: String) {
497518
diagnostics.emit(.commandError(command: command, message: message))
498519
}
499520

500-
public func commandProcessHadOutput(_ command: SPMLLBuild.Command, process: ProcessHandle, data: [UInt8]) {
521+
func commandProcessHadOutput(_ command: SPMLLBuild.Command, process: ProcessHandle, data: [UInt8]) {
501522
guard command.shouldShowStatus else { return }
502523

503524
if let swiftParser = swiftParsers[command.name] {
@@ -511,7 +532,7 @@ public final class BuildDelegate: BuildSystemDelegate, SwiftCompilerOutputParser
511532
}
512533
}
513534

514-
public func commandProcessFinished(
535+
func commandProcessFinished(
515536
_ command: SPMLLBuild.Command,
516537
process: ProcessHandle,
517538
result: CommandExtendedResult
@@ -532,15 +553,21 @@ public final class BuildDelegate: BuildSystemDelegate, SwiftCompilerOutputParser
532553
}
533554
}
534555

535-
public func cycleDetected(rules: [BuildKey]) {
556+
func cycleDetected(rules: [BuildKey]) {
536557
diagnostics.emit(.cycleError(rules: rules))
558+
559+
queue.async {
560+
self.delegate?.buildSystemDidDetectCycleInRules(self.buildSystem)
561+
}
537562
}
538563

539-
public func shouldResolveCycle(rules: [BuildKey], candidate: BuildKey, action: CycleAction) -> Bool {
564+
func shouldResolveCycle(rules: [BuildKey], candidate: BuildKey, action: CycleAction) -> Bool {
540565
return false
541566
}
542567

543-
public func swiftCompilerOutputParser(_ parser: SwiftCompilerOutputParser, didParse message: SwiftCompilerMessage) {
568+
// MARK: SwiftCompilerOutputParserDelegate
569+
570+
func swiftCompilerOutputParser(_ parser: SwiftCompilerOutputParser, didParse message: SwiftCompilerMessage) {
544571
queue.async {
545572
if self.isVerbose {
546573
if let text = message.verboseProgressText {
@@ -569,12 +596,14 @@ public final class BuildDelegate: BuildSystemDelegate, SwiftCompilerOutputParser
569596
}
570597
}
571598

572-
public func swiftCompilerOutputParser(_ parser: SwiftCompilerOutputParser, didFailWith error: Error) {
599+
func swiftCompilerOutputParser(_ parser: SwiftCompilerOutputParser, didFailWith error: Error) {
573600
let message = (error as? LocalizedError)?.errorDescription ?? error.localizedDescription
574601
diagnostics.emit(.swiftCompilerOutputParsingError(message))
575602
onCommmandFailure?()
576603
}
577604

605+
// MARK: Private
606+
578607
private func updateProgress() {
579608
if let progressText = taskTracker.latestFinishedText {
580609
progressAnimation.update(
@@ -594,14 +623,14 @@ fileprivate struct CommandTaskTracker {
594623
/// The last task text before the task list was emptied.
595624
private(set) var latestFinishedText: String?
596625

626+
var onTaskProgressUpdateText: ((_ text: String, _ targetName: String?) -> Void)?
627+
597628
mutating func commandStatusChanged(_ command: SPMLLBuild.Command, kind: CommandStatusKind) {
598629
switch kind {
599630
case .isScanning:
600631
totalCount += 1
601-
break
602632
case .isUpToDate:
603633
totalCount -= 1
604-
break
605634
case .isComplete:
606635
if (totalCount == finishedCount) {
607636
let latestOutput: String? = latestFinishedText
@@ -610,15 +639,17 @@ fileprivate struct CommandTaskTracker {
610639
* Build Completed!
611640
"""
612641
}
613-
break
614642
@unknown default:
615643
assertionFailure("unhandled command status kind \(kind) for command \(command)")
616644
break
617645
}
618646
}
619647

620648
mutating func commandFinished(_ command: SPMLLBuild.Command, result: CommandResult, targetName: String?) {
621-
latestFinishedText = progressText(of: command, targetName: targetName)
649+
let progressTextValue = progressText(of: command, targetName: targetName)
650+
onTaskProgressUpdateText?(progressTextValue, targetName)
651+
652+
latestFinishedText = progressTextValue
622653

623654
switch result {
624655
case .succeeded, .skipped:
@@ -635,6 +666,7 @@ fileprivate struct CommandTaskTracker {
635666
case .began(let info):
636667
if let text = progressText(of: message, targetName: targetName) {
637668
swiftTaskProgressTexts[info.pid] = text
669+
onTaskProgressUpdateText?(text, targetName)
638670
}
639671

640672
totalCount += 1
@@ -739,3 +771,13 @@ private extension Diagnostic.Message {
739771
.error("failed parsing the Swift compiler output: \(error)")
740772
}
741773
}
774+
775+
private extension BuildSystemCommand {
776+
init(_ command: SPMLLBuild.Command) {
777+
self.init(
778+
name: command.name,
779+
description: command.description,
780+
verboseDescription: command.verboseDescription
781+
)
782+
}
783+
}

Sources/Build/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
# See http://swift.org/CONTRIBUTORS.txt for Swift project authors
88

99
add_library(Build
10-
BuildDelegate.swift
10+
BuildOperationBuildSystemDelegateHandler.swift
1111
BuildOperation.swift
1212
BuildPlan.swift
1313
ManifestBuilder.swift

Sources/SPMBuildCore/BuildSystem.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ public enum BuildSubset {
2929
/// implementation details between SwiftPM's `BuildOperation` and the XCBuild backed `XCBuildSystem`.
3030
public protocol BuildSystem {
3131

32+
/// The delegate used by the build system.
33+
var delegate: BuildSystemDelegate? { get }
34+
3235
/// The test products that this build system will build.
3336
var builtTestProducts: [BuiltTestProduct] { get }
3437

0 commit comments

Comments
 (0)