Skip to content

Commit 05d08fb

Browse files
committed
Fix unsafeToInterrupt on Amazon Linux 2
This distro is going EoL in July, but fix the test anyways for better future-proofing. What happens is that the task fails to execute because there is no working directory support on Amazon Linux 2, so the build fails. However, in that case the background task which waits for the sentinel file to be written will run forever. Ensure that we cancel the background task in cases where the build has completed.
1 parent 18c11bf commit 05d08fb

File tree

5 files changed

+34
-20
lines changed

5 files changed

+34
-20
lines changed

Sources/SWBBuildService/BuildOperationMessages.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1054,15 +1054,17 @@ final class OperationDelegate: BuildOperationDelegate {
10541054
request.send(BuildOperationReportPathMap(copiedPathMap: copiedPathMap, generatedFilesPathMap: generatedFilesPathMap))
10551055
}
10561056

1057-
func buildComplete(_ operation: any BuildSystemOperation, status: BuildOperationEnded.Status?, delegate: any BuildOutputDelegate, metrics: BuildOperationMetrics?) {
1057+
func buildComplete(_ operation: any BuildSystemOperation, status: BuildOperationEnded.Status?, delegate: any BuildOutputDelegate, metrics: BuildOperationMetrics?) -> BuildOperationEnded.Status {
10581058
if !skipCommandLevelInformation {
10591059
// Kick the target callbacks so that our target-level diagnostics get emitted in the right context in the case of an early build failure due to task construction errors.
10601060
for target in diagnosticsHandler.deferredTargets {
10611061
targetStarted(operation, configuredTarget: target)
10621062
targetComplete(operation, configuredTarget: target)
10631063
}
10641064
}
1065-
activeBuild.completeBuild(status: status ?? taskCompletionBasedStatus, metrics: metrics)
1065+
let realStatus = status ?? taskCompletionBasedStatus
1066+
activeBuild.completeBuild(status: realStatus, metrics: metrics)
1067+
return realStatus
10661068
}
10671069

10681070
private func getActiveTargetInfo(_ operation: any BuildSystemOperation, _ configuredTarget: ConfiguredTarget) -> TargetInfo {

Sources/SWBBuildSystem/BuildOperation.swift

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ package protocol BuildOperationDelegate {
5252
/// - Parameters:
5353
/// - status: Overall build operation status to override the build result with. This can be used when the operation was aborted (the build could not run to completion, e.g. due to discovery of a cycle) or cancelled and wants to propagate that status regardless of the status of the individual build tasks in the underlying (llbuild) build system. `nil` will use the status based on the result status of the tasks in the underlying llbuild build system.
5454
/// - delegate: The task output delegate provided by the \see buildStarted() method.
55-
func buildComplete(_ operation: any BuildSystemOperation, status: BuildOperationEnded.Status?, delegate: any BuildOutputDelegate, metrics: BuildOperationMetrics?)
55+
@discardableResult func buildComplete(_ operation: any BuildSystemOperation, status: BuildOperationEnded.Status?, delegate: any BuildOutputDelegate, metrics: BuildOperationMetrics?) -> BuildOperationEnded.Status
5656

5757
/// Called when an individual task has been started.
5858
///
@@ -137,7 +137,7 @@ package protocol BuildSystemOperation: AnyObject, Sendable {
137137
var subtaskProgressReporter: (any SubtaskProgressReporter)? { get }
138138
var uuid: UUID { get }
139139

140-
func build() async
140+
@discardableResult func build() async -> BuildOperationEnded.Status
141141
func cancel()
142142
func abort()
143143
}
@@ -285,7 +285,7 @@ package final class BuildOperation: BuildSystemOperation {
285285
}
286286

287287
/// Perform the build.
288-
package func build() async {
288+
package func build() async -> BuildOperationEnded.Status {
289289
// Inform the client the build has started.
290290
buildOutputDelegate = delegate.buildStarted(self)
291291

@@ -305,8 +305,9 @@ package final class BuildOperation: BuildSystemOperation {
305305
// Diagnose attempts to use "dry run" mode, which we don't support yet.
306306
if request.useDryRun {
307307
buildOutputDelegate.error("-dry-run is not yet supported in the new build system")
308-
delegate.buildComplete(self, status: .failed, delegate: buildOutputDelegate, metrics: nil)
309-
return
308+
let effectiveStatus = BuildOperationEnded.Status.failed
309+
delegate.buildComplete(self, status: effectiveStatus, delegate: buildOutputDelegate, metrics: nil)
310+
return effectiveStatus
310311
}
311312

312313
// Helper method to emit information to the CAPTURED_BUILD_INFO_DIR. This is mainly to be able to gracefully return after emitting a warning if something goes wrong, because not being able to emit this info is typically not serious enough to want to abort the whole build.
@@ -367,8 +368,9 @@ package final class BuildOperation: BuildSystemOperation {
367368

368369
// If task construction had errors, fail the build immediately, unless `continueBuildingAfterErrors` is set.
369370
if !request.continueBuildingAfterErrors && buildDescription.hadErrors {
370-
delegate.buildComplete(self, status: .failed, delegate: buildOutputDelegate, metrics: nil)
371-
return
371+
let effectiveStatus = BuildOperationEnded.Status.failed
372+
delegate.buildComplete(self, status: effectiveStatus, delegate: buildOutputDelegate, metrics: nil)
373+
return effectiveStatus
372374
}
373375

374376
if userPreferences.enableDebugActivityLogs {
@@ -410,8 +412,9 @@ package final class BuildOperation: BuildSystemOperation {
410412
do {
411413
let isCancelled = await queue.sync{ self.wasCancellationRequested }
412414
if !UserDefaults.skipEarlyBuildOperationCancellation && isCancelled {
413-
delegate.buildComplete(self, status: .cancelled, delegate: buildOutputDelegate, metrics: nil)
414-
return
415+
let effectiveStatus = BuildOperationEnded.Status.cancelled
416+
delegate.buildComplete(self, status: effectiveStatus, delegate: buildOutputDelegate, metrics: nil)
417+
return effectiveStatus
415418
}
416419
}
417420

@@ -421,8 +424,9 @@ package final class BuildOperation: BuildSystemOperation {
421424
for message in warnings { buildOutputDelegate.warning(message) }
422425
for message in errors { buildOutputDelegate.error(message) }
423426
guard errors.isEmpty else {
424-
delegate.buildComplete(self, status: .failed, delegate: buildOutputDelegate, metrics: nil)
425-
return
427+
let effectiveStatus = BuildOperationEnded.Status.failed
428+
delegate.buildComplete(self, status: effectiveStatus, delegate: buildOutputDelegate, metrics: nil)
429+
return effectiveStatus
426430
}
427431
}
428432

@@ -776,7 +780,7 @@ package final class BuildOperation: BuildSystemOperation {
776780
}
777781

778782
// `buildComplete()` should not run within `queue`, otherwise there can be a deadlock during cancelling.
779-
delegate.buildComplete(self, status: effectiveStatus, delegate: buildOutputDelegate, metrics: .init(counters: delegate.aggregatedCounters))
783+
return delegate.buildComplete(self, status: effectiveStatus, delegate: buildOutputDelegate, metrics: .init(counters: delegate.aggregatedCounters))
780784
}
781785

782786
func prepareForBuilding() -> ([String], [String])? {

Sources/SWBBuildSystem/CleanOperation.swift

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
package import SWBCore
1414
import SWBTaskExecution
15+
package import SWBProtocol
1516
package import SWBUtil
1617

1718
package import class Foundation.FileManager
@@ -61,7 +62,7 @@ package final class CleanOperation: BuildSystemOperation, TargetDependencyResolv
6162
return try BuildDescriptionManager.cacheDirectory(buildRequest, buildRequestContext: buildRequestContext, workspaceContext: workspaceContext).join("XCBuildData")
6263
}
6364

64-
package func build() async {
65+
package func build() async -> BuildOperationEnded.Status {
6566
let buildOutputDelegate = delegate.buildStarted(self)
6667

6768
if workspaceContext.userPreferences.enableDebugActivityLogs {
@@ -103,7 +104,7 @@ package final class CleanOperation: BuildSystemOperation, TargetDependencyResolv
103104

104105
cleanBuildFolders(buildFolders: Set(buildFolders), buildOutputDelegate: buildOutputDelegate)
105106

106-
delegate.buildComplete(self, status: nil, delegate: buildOutputDelegate, metrics: nil)
107+
return delegate.buildComplete(self, status: nil, delegate: buildOutputDelegate, metrics: nil)
107108
}
108109

109110
package func cancel() {
@@ -205,7 +206,7 @@ package final class CleanOperation: BuildSystemOperation, TargetDependencyResolv
205206
delegate.targetPreparationStarted(self, configuredTarget: configuredTarget)
206207
delegate.targetStarted(self, configuredTarget: configuredTarget)
207208

208-
let (executable, arguments, workingDirectory, environment) = constructCommandLine(for: configuredTarget.target as! ExternalTarget, action: "clean", settings: settings, workspaceContext: workspaceContext, scope: settings.globalScope)
209+
let (executable, arguments, workingDirectory, environment) = constructCommandLine(for: configuredTarget.target as! SWBCore.ExternalTarget, action: "clean", settings: settings, workspaceContext: workspaceContext, scope: settings.globalScope)
209210
let commandLine = [executable] + arguments
210211

211212
let specLookupContext = SpecLookupCtxt(specRegistry: workspaceContext.core.specRegistry, platform: settings.platform)

Sources/SWBTestSupport/BuildOperationTester.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1995,11 +1995,12 @@ private final class BuildOperationTesterDelegate: BuildOperationDelegate {
19951995
return TesterBuildOutputDelegate(delegate: self)
19961996
}
19971997

1998-
func buildComplete(_ operation: any BuildSystemOperation, status: BuildOperationEnded.Status?, delegate: any BuildOutputDelegate, metrics: BuildOperationMetrics?) {
1998+
func buildComplete(_ operation: any BuildSystemOperation, status: BuildOperationEnded.Status?, delegate: any BuildOutputDelegate, metrics: BuildOperationMetrics?) -> BuildOperationEnded.Status {
19991999
queue.async {
20002000
// There is no "build failed" event, so only explicit cancellation needs to map to `buildCancelled`
20012001
self.events.append(status == .cancelled ? .buildCancelled : .buildCompleted)
20022002
}
2003+
return status ?? .succeeded
20032004
}
20042005

20052006
func targetPreparationStarted(_ operation: any BuildSystemOperation, configuredTarget: ConfiguredTarget) {

Tests/SWBBuildSystemTests/BuildTaskBehaviorTests.swift

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,7 @@ fileprivate struct BuildTaskBehaviorTests: CoreBasedTests {
306306
}
307307

308308
/// Check that we honor specs which are unsafe to interrupt.
309-
@Test(.requireSDKs(.host), .skipHostOS(.windows, "no bash shell"))
309+
@Test(.requireSDKs(.host), .skipHostOS(.windows, "no bash shell"), .requireThreadSafeWorkingDirectory)
310310
func unsafeToInterrupt() async throws {
311311
let fs = localFS
312312
let output = MakePlannedVirtualNode("<WAIT>")
@@ -365,7 +365,13 @@ fileprivate struct BuildTaskBehaviorTests: CoreBasedTests {
365365
}
366366

367367
try await withTaskCancellationHandler {
368-
await operation.build()
368+
switch await operation.build() {
369+
case .cancelled, .failed:
370+
// If the build already failed, cancel the task that waits for the script so the test doesn't hang forever.
371+
task.cancel()
372+
case .succeeded:
373+
break
374+
}
369375
try await task.value
370376
} onCancel: {
371377
task.cancel()

0 commit comments

Comments
 (0)