Skip to content

Commit 9c0e48e

Browse files
authored
[Build] NFC: A few minor refactorings to build operation state tracking (#7660)
### Motivation: Trying to make it possible to share `createBuildOperation` between multiple implementations of `SPMCoreBuild.BuildSystem` in preparation to introduce an operation that would build plugin tools. ### Modifications: - Rename `BuildOperationBuildSystemDelegateHandler` into `LLBuildProgressTracker` which is a more neutral name that could be used by different llbuild operations if necessary. - Integrate `commandFailureHandler` into the progress tracker - Make `BuildOperation.createBuildSystem` stateless and use a single member to set both a new build system and its tracker. ### Result: The change it make it much easier to move `createBuildSystem` out of `BuildOperation` and into `SPMCoreBuild.BuildSystem` itself.
1 parent 067136b commit 9c0e48e

File tree

2 files changed

+48
-42
lines changed

2 files changed

+48
-42
lines changed

Sources/Build/BuildOperation.swift

+37-37
Original file line numberDiff line numberDiff line change
@@ -55,11 +55,9 @@ public final class BuildOperation: PackageStructureDelegate, SPMBuildCore.BuildS
5555
/// The path to scratch space (.build) directory.
5656
let scratchDirectory: AbsolutePath
5757

58-
/// The llbuild build delegate reference.
59-
private var buildSystemDelegate: BuildOperationBuildSystemDelegateHandler?
60-
61-
/// The llbuild build system reference.
62-
private var buildSystem: SPMLLBuild.BuildSystem?
58+
/// The llbuild build system reference previously created
59+
/// via `createBuildSystem` call.
60+
private var current: (buildSystem: SPMLLBuild.BuildSystem, tracker: LLBuildProgressTracker)?
6361

6462
/// If build manifest caching should be enabled.
6563
public let cacheBuildManifest: Bool
@@ -194,7 +192,7 @@ public final class BuildOperation: PackageStructureDelegate, SPMBuildCore.BuildS
194192

195193
/// Cancel the active build operation.
196194
public func cancel(deadline: DispatchTime) throws {
197-
buildSystem?.cancel()
195+
current?.buildSystem.cancel()
198196
}
199197

200198
// Emit a warning if a target imports another target in this build
@@ -355,8 +353,10 @@ public final class BuildOperation: PackageStructureDelegate, SPMBuildCore.BuildS
355353
try verifyTargetImports(in: buildDescription)
356354

357355
// Create the build system.
358-
let buildSystem = try self.createBuildSystem(buildDescription: buildDescription)
359-
self.buildSystem = buildSystem
356+
let (buildSystem, progressTracker) = try self.createBuildSystem(
357+
buildDescription: buildDescription
358+
)
359+
self.current = (buildSystem, progressTracker)
360360

361361
// If any plugins are part of the build set, compile them now to surface
362362
// any errors up-front. Returns true if we should proceed with the build
@@ -366,7 +366,7 @@ public final class BuildOperation: PackageStructureDelegate, SPMBuildCore.BuildS
366366
}
367367

368368
// delegate is only available after createBuildSystem is called
369-
self.buildSystemDelegate?.buildStart(configuration: self.productsBuildParameters.configuration)
369+
progressTracker.buildStart(configuration: self.productsBuildParameters.configuration)
370370

371371
// Perform the build.
372372
let llbuildTarget = try computeLLBuildTargetName(for: subset)
@@ -386,12 +386,11 @@ public final class BuildOperation: PackageStructureDelegate, SPMBuildCore.BuildS
386386
subsetDescriptor = nil
387387
}
388388

389-
self.buildSystemDelegate?.buildComplete(
389+
progressTracker.buildComplete(
390390
success: success,
391391
duration: duration,
392392
subsetDescriptor: subsetDescriptor
393393
)
394-
self.delegate?.buildSystem(self, didFinishWithResult: success)
395394
guard success else { throw Diagnostics.fatalError }
396395

397396
// Create backwards-compatibility symlink to old build path.
@@ -459,45 +458,48 @@ public final class BuildOperation: PackageStructureDelegate, SPMBuildCore.BuildS
459458
throw InternalError("unknown plugin script runner")
460459
}
461460
// Compile the plugin, getting back a PluginCompilationResult.
462-
class Delegate: PluginScriptCompilerDelegate {
461+
final class Delegate: PluginScriptCompilerDelegate {
463462
let preparationStepName: String
464-
let buildSystemDelegate: BuildOperationBuildSystemDelegateHandler?
465-
init(preparationStepName: String, buildSystemDelegate: BuildOperationBuildSystemDelegateHandler?) {
463+
let progressTracker: LLBuildProgressTracker?
464+
init(preparationStepName: String, progressTracker: LLBuildProgressTracker?) {
466465
self.preparationStepName = preparationStepName
467-
self.buildSystemDelegate = buildSystemDelegate
466+
self.progressTracker = progressTracker
468467
}
469468
func willCompilePlugin(commandLine: [String], environment: EnvironmentVariables) {
470-
self.buildSystemDelegate?.preparationStepStarted(preparationStepName)
469+
self.progressTracker?.preparationStepStarted(preparationStepName)
471470
}
472471
func didCompilePlugin(result: PluginCompilationResult) {
473-
self.buildSystemDelegate?.preparationStepHadOutput(
472+
self.progressTracker?.preparationStepHadOutput(
474473
preparationStepName,
475474
output: result.commandLine.joined(separator: " "),
476475
verboseOnly: true
477476
)
478477
if !result.compilerOutput.isEmpty {
479-
self.buildSystemDelegate?.preparationStepHadOutput(
478+
self.progressTracker?.preparationStepHadOutput(
480479
preparationStepName,
481480
output: result.compilerOutput,
482481
verboseOnly: false
483482
)
484483
}
485-
self.buildSystemDelegate?.preparationStepFinished(preparationStepName, result: (result.succeeded ? .succeeded : .failed))
484+
self.progressTracker?.preparationStepFinished(preparationStepName, result: (result.succeeded ? .succeeded : .failed))
486485
}
487486
func skippedCompilingPlugin(cachedResult: PluginCompilationResult) {
488487
// Historically we have emitted log info about cached plugins that are used. We should reconsider whether this is the right thing to do.
489-
self.buildSystemDelegate?.preparationStepStarted(preparationStepName)
488+
self.progressTracker?.preparationStepStarted(preparationStepName)
490489
if !cachedResult.compilerOutput.isEmpty {
491-
self.buildSystemDelegate?.preparationStepHadOutput(
490+
self.progressTracker?.preparationStepHadOutput(
492491
preparationStepName,
493492
output: cachedResult.compilerOutput,
494493
verboseOnly: false
495494
)
496495
}
497-
self.buildSystemDelegate?.preparationStepFinished(preparationStepName, result: (cachedResult.succeeded ? .succeeded : .failed))
496+
self.progressTracker?.preparationStepFinished(preparationStepName, result: (cachedResult.succeeded ? .succeeded : .failed))
498497
}
499498
}
500-
let delegate = Delegate(preparationStepName: "Compiling plugin \(plugin.targetName)", buildSystemDelegate: self.buildSystemDelegate)
499+
let delegate = Delegate(
500+
preparationStepName: "Compiling plugin \(plugin.targetName)",
501+
progressTracker: self.current?.tracker
502+
)
501503
let result = try temp_await {
502504
pluginConfiguration.scriptRunner.compilePluginScript(
503505
sourceFiles: plugin.sources.paths,
@@ -746,8 +748,10 @@ public final class BuildOperation: PackageStructureDelegate, SPMBuildCore.BuildS
746748

747749
/// Build the package structure target.
748750
private func buildPackageStructure() throws -> Bool {
749-
let buildSystem = try self.createBuildSystem(buildDescription: .none)
750-
self.buildSystem = buildSystem
751+
let (buildSystem, tracker) = try self.createBuildSystem(
752+
buildDescription: .none
753+
)
754+
self.current = (buildSystem, tracker)
751755

752756
// Build the package structure target which will re-generate the llbuild manifest, if necessary.
753757
return buildSystem.build(target: "PackageStructure")
@@ -757,7 +761,9 @@ public final class BuildOperation: PackageStructureDelegate, SPMBuildCore.BuildS
757761
///
758762
/// The build description should only be omitted when creating the build system for
759763
/// building the package structure target.
760-
private func createBuildSystem(buildDescription: BuildDescription?) throws -> SPMLLBuild.BuildSystem {
764+
private func createBuildSystem(
765+
buildDescription: BuildDescription?
766+
) throws -> (buildSystem: SPMLLBuild.BuildSystem, tracker: LLBuildProgressTracker) {
761767
// Figure out which progress bar we have to use during the build.
762768
let progressAnimation = ProgressAnimation.ninja(
763769
stream: self.outputStream,
@@ -774,7 +780,7 @@ public final class BuildOperation: PackageStructureDelegate, SPMBuildCore.BuildS
774780
)
775781

776782
// Create the build delegate.
777-
let buildSystemDelegate = BuildOperationBuildSystemDelegateHandler(
783+
let progressTracker = LLBuildProgressTracker(
778784
buildSystem: self,
779785
buildExecutionContext: buildExecutionContext,
780786
outputStream: self.outputStream,
@@ -783,23 +789,17 @@ public final class BuildOperation: PackageStructureDelegate, SPMBuildCore.BuildS
783789
observabilityScope: self.observabilityScope,
784790
delegate: self.delegate
785791
)
786-
self.buildSystemDelegate = buildSystemDelegate
787792

788793
let databasePath = self.scratchDirectory.appending("build.db").pathString
789-
let buildSystem = SPMLLBuild.BuildSystem(
794+
795+
let llbuildSystem = SPMLLBuild.BuildSystem(
790796
buildFile: self.productsBuildParameters.llbuildManifest.pathString,
791797
databaseFile: databasePath,
792-
delegate: buildSystemDelegate,
798+
delegate: progressTracker,
793799
schedulerLanes: self.productsBuildParameters.workers
794800
)
795801

796-
// TODO: this seems fragile, perhaps we replace commandFailureHandler by adding relevant calls in the delegates chain
797-
buildSystemDelegate.commandFailureHandler = {
798-
buildSystem.cancel()
799-
self.delegate?.buildSystemDidCancel(self)
800-
}
801-
802-
return buildSystem
802+
return (buildSystem: llbuildSystem, tracker: progressTracker)
803803
}
804804

805805
/// Runs any prebuild commands associated with the given list of plugin invocation results, in order, and returns the

Sources/Build/BuildOperationBuildSystemDelegateHandler.swift

+11-5
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//
33
// This source file is part of the Swift open source project
44
//
5-
// Copyright (c) 2018-2020 Apple Inc. and the Swift project authors
5+
// Copyright (c) 2018-2024 Apple Inc. and the Swift project authors
66
// Licensed under Apache License v2.0 with Runtime Library Exception
77
//
88
// See http://swift.org/LICENSE.txt for license information
@@ -644,10 +644,9 @@ final class CopyCommand: CustomLLBuildCommand {
644644
}
645645

646646
/// Convenient llbuild build system delegate implementation
647-
final class BuildOperationBuildSystemDelegateHandler: LLBuildBuildSystemDelegate, SwiftCompilerOutputParserDelegate {
647+
final class LLBuildProgressTracker: LLBuildBuildSystemDelegate, SwiftCompilerOutputParserDelegate {
648648
private let outputStream: ThreadSafeOutputByteStream
649649
private let progressAnimation: ProgressAnimationProtocol
650-
var commandFailureHandler: (() -> Void)?
651650
private let logLevel: Basics.Diagnostic.Severity
652651
private weak var delegate: SPMBuildCore.BuildSystemDelegate?
653652
private let buildSystem: SPMBuildCore.BuildSystem
@@ -721,7 +720,12 @@ final class BuildOperationBuildSystemDelegateHandler: LLBuildBuildSystemDelegate
721720
}
722721

723722
func hadCommandFailure() {
724-
self.commandFailureHandler?()
723+
do {
724+
try self.buildSystem.cancel(deadline: .now())
725+
} catch {
726+
self.observabilityScope.emit(error: "failed to cancel the build: \(error)")
727+
}
728+
self.delegate?.buildSystemDidCancel(self.buildSystem)
725729
}
726730

727731
func handleDiagnostic(_ diagnostic: SPMLLBuild.Diagnostic) {
@@ -958,7 +962,7 @@ final class BuildOperationBuildSystemDelegateHandler: LLBuildBuildSystemDelegate
958962
func swiftCompilerOutputParser(_ parser: SwiftCompilerOutputParser, didFailWith error: Error) {
959963
let message = (error as? LocalizedError)?.errorDescription ?? error.localizedDescription
960964
self.observabilityScope.emit(.swiftCompilerOutputParsingError(message))
961-
self.commandFailureHandler?()
965+
self.hadCommandFailure()
962966
}
963967

964968
func buildStart(configuration: BuildConfiguration) {
@@ -979,6 +983,8 @@ final class BuildOperationBuildSystemDelegateHandler: LLBuildBuildSystemDelegate
979983

980984
queue.sync {
981985
self.progressAnimation.complete(success: success)
986+
self.delegate?.buildSystem(self.buildSystem, didFinishWithResult: success)
987+
982988
if success {
983989
let message = cancelled ? "Build \(subsetString)cancelled!" : "Build \(subsetString)complete!"
984990
self.progressAnimation.clear()

0 commit comments

Comments
 (0)