Skip to content

[Build] NFC: A few minor refactorings to build operation state tracking #7660

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 37 additions & 37 deletions Sources/Build/BuildOperation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,9 @@ public final class BuildOperation: PackageStructureDelegate, SPMBuildCore.BuildS
/// The path to scratch space (.build) directory.
let scratchDirectory: AbsolutePath

/// The llbuild build delegate reference.
private var buildSystemDelegate: BuildOperationBuildSystemDelegateHandler?

/// The llbuild build system reference.
private var buildSystem: SPMLLBuild.BuildSystem?
/// The llbuild build system reference previously created
/// via `createBuildSystem` call.
private var current: (buildSystem: SPMLLBuild.BuildSystem, tracker: LLBuildProgressTracker)?

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

/// Cancel the active build operation.
public func cancel(deadline: DispatchTime) throws {
buildSystem?.cancel()
current?.buildSystem.cancel()
}

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

// Create the build system.
let buildSystem = try self.createBuildSystem(buildDescription: buildDescription)
self.buildSystem = buildSystem
let (buildSystem, progressTracker) = try self.createBuildSystem(
buildDescription: buildDescription
)
self.current = (buildSystem, progressTracker)

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

// delegate is only available after createBuildSystem is called
self.buildSystemDelegate?.buildStart(configuration: self.productsBuildParameters.configuration)
progressTracker.buildStart(configuration: self.productsBuildParameters.configuration)

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

self.buildSystemDelegate?.buildComplete(
progressTracker.buildComplete(
success: success,
duration: duration,
subsetDescriptor: subsetDescriptor
)
self.delegate?.buildSystem(self, didFinishWithResult: success)
guard success else { throw Diagnostics.fatalError }

// Create backwards-compatibility symlink to old build path.
Expand Down Expand Up @@ -459,45 +458,48 @@ public final class BuildOperation: PackageStructureDelegate, SPMBuildCore.BuildS
throw InternalError("unknown plugin script runner")
}
// Compile the plugin, getting back a PluginCompilationResult.
class Delegate: PluginScriptCompilerDelegate {
final class Delegate: PluginScriptCompilerDelegate {
let preparationStepName: String
let buildSystemDelegate: BuildOperationBuildSystemDelegateHandler?
init(preparationStepName: String, buildSystemDelegate: BuildOperationBuildSystemDelegateHandler?) {
let progressTracker: LLBuildProgressTracker?
init(preparationStepName: String, progressTracker: LLBuildProgressTracker?) {
self.preparationStepName = preparationStepName
self.buildSystemDelegate = buildSystemDelegate
self.progressTracker = progressTracker
}
func willCompilePlugin(commandLine: [String], environment: EnvironmentVariables) {
self.buildSystemDelegate?.preparationStepStarted(preparationStepName)
self.progressTracker?.preparationStepStarted(preparationStepName)
}
func didCompilePlugin(result: PluginCompilationResult) {
self.buildSystemDelegate?.preparationStepHadOutput(
self.progressTracker?.preparationStepHadOutput(
preparationStepName,
output: result.commandLine.joined(separator: " "),
verboseOnly: true
)
if !result.compilerOutput.isEmpty {
self.buildSystemDelegate?.preparationStepHadOutput(
self.progressTracker?.preparationStepHadOutput(
preparationStepName,
output: result.compilerOutput,
verboseOnly: false
)
}
self.buildSystemDelegate?.preparationStepFinished(preparationStepName, result: (result.succeeded ? .succeeded : .failed))
self.progressTracker?.preparationStepFinished(preparationStepName, result: (result.succeeded ? .succeeded : .failed))
}
func skippedCompilingPlugin(cachedResult: PluginCompilationResult) {
// Historically we have emitted log info about cached plugins that are used. We should reconsider whether this is the right thing to do.
self.buildSystemDelegate?.preparationStepStarted(preparationStepName)
self.progressTracker?.preparationStepStarted(preparationStepName)
if !cachedResult.compilerOutput.isEmpty {
self.buildSystemDelegate?.preparationStepHadOutput(
self.progressTracker?.preparationStepHadOutput(
preparationStepName,
output: cachedResult.compilerOutput,
verboseOnly: false
)
}
self.buildSystemDelegate?.preparationStepFinished(preparationStepName, result: (cachedResult.succeeded ? .succeeded : .failed))
self.progressTracker?.preparationStepFinished(preparationStepName, result: (cachedResult.succeeded ? .succeeded : .failed))
}
}
let delegate = Delegate(preparationStepName: "Compiling plugin \(plugin.targetName)", buildSystemDelegate: self.buildSystemDelegate)
let delegate = Delegate(
preparationStepName: "Compiling plugin \(plugin.targetName)",
progressTracker: self.current?.tracker
)
let result = try temp_await {
pluginConfiguration.scriptRunner.compilePluginScript(
sourceFiles: plugin.sources.paths,
Expand Down Expand Up @@ -746,8 +748,10 @@ public final class BuildOperation: PackageStructureDelegate, SPMBuildCore.BuildS

/// Build the package structure target.
private func buildPackageStructure() throws -> Bool {
let buildSystem = try self.createBuildSystem(buildDescription: .none)
self.buildSystem = buildSystem
let (buildSystem, tracker) = try self.createBuildSystem(
buildDescription: .none
)
self.current = (buildSystem, tracker)

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

// Create the build delegate.
let buildSystemDelegate = BuildOperationBuildSystemDelegateHandler(
let progressTracker = LLBuildProgressTracker(
buildSystem: self,
buildExecutionContext: buildExecutionContext,
outputStream: self.outputStream,
Expand All @@ -783,23 +789,17 @@ public final class BuildOperation: PackageStructureDelegate, SPMBuildCore.BuildS
observabilityScope: self.observabilityScope,
delegate: self.delegate
)
self.buildSystemDelegate = buildSystemDelegate

let databasePath = self.scratchDirectory.appending("build.db").pathString
let buildSystem = SPMLLBuild.BuildSystem(

let llbuildSystem = SPMLLBuild.BuildSystem(
buildFile: self.productsBuildParameters.llbuildManifest.pathString,
databaseFile: databasePath,
delegate: buildSystemDelegate,
delegate: progressTracker,
schedulerLanes: self.productsBuildParameters.workers
)

// TODO: this seems fragile, perhaps we replace commandFailureHandler by adding relevant calls in the delegates chain
buildSystemDelegate.commandFailureHandler = {
buildSystem.cancel()
self.delegate?.buildSystemDidCancel(self)
}

return buildSystem
return (buildSystem: llbuildSystem, tracker: progressTracker)
}

/// Runs any prebuild commands associated with the given list of plugin invocation results, in order, and returns the
Expand Down
16 changes: 11 additions & 5 deletions Sources/Build/BuildOperationBuildSystemDelegateHandler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//
// This source file is part of the Swift open source project
//
// Copyright (c) 2018-2020 Apple Inc. and the Swift project authors
// Copyright (c) 2018-2024 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
Expand Down Expand Up @@ -644,10 +644,9 @@ final class CopyCommand: CustomLLBuildCommand {
}

/// Convenient llbuild build system delegate implementation
final class BuildOperationBuildSystemDelegateHandler: LLBuildBuildSystemDelegate, SwiftCompilerOutputParserDelegate {
final class LLBuildProgressTracker: LLBuildBuildSystemDelegate, SwiftCompilerOutputParserDelegate {
private let outputStream: ThreadSafeOutputByteStream
private let progressAnimation: ProgressAnimationProtocol
var commandFailureHandler: (() -> Void)?
private let logLevel: Basics.Diagnostic.Severity
private weak var delegate: SPMBuildCore.BuildSystemDelegate?
private let buildSystem: SPMBuildCore.BuildSystem
Expand Down Expand Up @@ -721,7 +720,12 @@ final class BuildOperationBuildSystemDelegateHandler: LLBuildBuildSystemDelegate
}

func hadCommandFailure() {
self.commandFailureHandler?()
do {
try self.buildSystem.cancel(deadline: .now())
} catch {
self.observabilityScope.emit(error: "failed to cancel the build: \(error)")
}
self.delegate?.buildSystemDidCancel(self.buildSystem)
}

func handleDiagnostic(_ diagnostic: SPMLLBuild.Diagnostic) {
Expand Down Expand Up @@ -958,7 +962,7 @@ final class BuildOperationBuildSystemDelegateHandler: LLBuildBuildSystemDelegate
func swiftCompilerOutputParser(_ parser: SwiftCompilerOutputParser, didFailWith error: Error) {
let message = (error as? LocalizedError)?.errorDescription ?? error.localizedDescription
self.observabilityScope.emit(.swiftCompilerOutputParsingError(message))
self.commandFailureHandler?()
self.hadCommandFailure()
}

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

queue.sync {
self.progressAnimation.complete(success: success)
self.delegate?.buildSystem(self.buildSystem, didFinishWithResult: success)

if success {
let message = cancelled ? "Build \(subsetString)cancelled!" : "Build \(subsetString)complete!"
self.progressAnimation.clear()
Expand Down