Skip to content

Commit 87d6a4d

Browse files
author
David Ungar
authored
Merge pull request #387 from davidungar/stop-on-error
[Incremental] stop if no -driver-continue-building-after-errors
2 parents fa3b225 + a8c8979 commit 87d6a4d

File tree

6 files changed

+107
-13
lines changed

6 files changed

+107
-13
lines changed

Sources/SwiftDriver/Driver/CompilerMode.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,10 @@ extension CompilerMode {
8080
}
8181
}
8282

83+
public var isBatchCompile: Bool {
84+
batchModeInfo != nil
85+
}
86+
8387
// Whether this compilation mode supports the use of bridging pre-compiled
8488
// headers.
8589
public var supportsBridgingPCH: Bool {

Sources/SwiftDriver/Driver/Driver.swift

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -898,8 +898,11 @@ extension Driver {
898898
allJobs: [Job],
899899
forceResponseFiles: Bool
900900
) throws {
901+
let continueBuildingAfterErrors = computeContinueBuildingAfterErrors()
901902
try executor.execute(
902-
workload: .init(allJobs, incrementalCompilationState),
903+
workload: .init(allJobs,
904+
incrementalCompilationState,
905+
continueBuildingAfterErrors: continueBuildingAfterErrors),
903906
delegate: createToolExecutionDelegate(),
904907
numParallelJobs: numParallelJobs ?? 1,
905908
forceResponseFiles: forceResponseFiles,
@@ -1416,8 +1419,25 @@ extension Driver {
14161419

14171420
return numJobs
14181421
}
1422+
1423+
private mutating func computeContinueBuildingAfterErrors() -> Bool {
1424+
// Note: Batch mode handling of serialized diagnostics requires that all
1425+
// batches get to run, in order to make sure that all diagnostics emitted
1426+
// during the compilation end up in at least one serialized diagnostic file.
1427+
// Therefore, treat batch mode as implying -continue-building-after-errors.
1428+
// (This behavior could be limited to only when serialized diagnostics are
1429+
// being emitted, but this seems more consistent and less surprising for
1430+
// users.)
1431+
// FIXME: We don't really need (or want) a full ContinueBuildingAfterErrors.
1432+
// If we fail to precompile a bridging header, for example, there's no need
1433+
// to go on to compilation of source files, and if compilation of source files
1434+
// fails, we shouldn't try to link. Instead, we'd want to let all jobs finish
1435+
// but not schedule any new ones.
1436+
return compilerMode.isBatchCompile || parsedOptions.contains(.continueBuildingAfterErrors)
1437+
}
14191438
}
14201439

1440+
14211441
extension Diagnostic.Message {
14221442
static func remark_max_determinism_overriding(_ option: Option) -> Diagnostic.Message {
14231443
.remark("SWIFTC_MAXIMUM_DETERMINISM overriding \(option.spelling)")

Sources/SwiftDriver/Execution/DriverExecutor.swift

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,26 @@ public protocol DriverExecutor {
4747
func description(of job: Job, forceResponseFiles: Bool) throws -> String
4848
}
4949

50-
public enum DriverExecutorWorkload {
51-
case all([Job])
52-
case incremental(IncrementalCompilationState)
50+
public struct DriverExecutorWorkload {
51+
public let continueBuildingAfterErrors: Bool
52+
public enum Kind {
53+
case all([Job])
54+
case incremental(IncrementalCompilationState)
55+
}
56+
public let kind: Kind
57+
58+
public init(_ allJobs: [Job],
59+
_ incrementalCompilationState: IncrementalCompilationState?,
60+
continueBuildingAfterErrors: Bool
61+
) {
62+
self.continueBuildingAfterErrors = continueBuildingAfterErrors
63+
self.kind = incrementalCompilationState
64+
.map {.incremental($0)}
65+
?? .all(allJobs)
66+
}
5367

54-
init(_ allJobs: [Job], _ incrementalCompilationState: IncrementalCompilationState?) {
55-
self = incrementalCompilationState.map {.incremental($0)} ?? .all(allJobs)
68+
static public func all(_ jobs: [Job]) -> Self {
69+
.init(jobs, nil, continueBuildingAfterErrors: false)
5670
}
5771
}
5872

Sources/SwiftDriver/Jobs/Planning.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -545,7 +545,7 @@ extension Driver {
545545
/// So, in order to avoid making jobs and rebatching, the code would have to just get outputs for each
546546
/// compilation. But `compileJob` intermixes the output computation with other stuff.
547547
mutating func formBatchedJobs(_ jobs: [Job], forIncremental: Bool) throws -> [Job] {
548-
guard let _ = compilerMode.batchModeInfo else {
548+
guard compilerMode.isBatchCompile else {
549549
// Don't even go through the logic so as to not print out confusing
550550
// "batched foobar" messages.
551551
return jobs

Sources/SwiftDriverExecution/MultiJobExecutor.swift

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,12 @@ public final class MultiJobExecutor {
8787
/// any newly-required job. Set only once.
8888
private(set) var executeAllJobsTaskBuildEngine: LLTaskBuildEngine? = nil
8989

90+
/// If a job fails, the driver needs to stop running jobs.
91+
private(set) var isBuildCancelled = false
92+
93+
/// The value of the option
94+
let continueBuildingAfterErrors: Bool
95+
9096

9197
init(
9298
argsResolver: ArgsResolver,
@@ -106,7 +112,8 @@ public final class MultiJobExecutor {
106112
producerMap: self.producerMap,
107113
primaryIndices: self.primaryIndices,
108114
postCompileIndices: self.postCompileIndices,
109-
incrementalCompilationState: self.incrementalCompilationState
115+
incrementalCompilationState: self.incrementalCompilationState,
116+
continueBuildingAfterErrors: self.continueBuildingAfterErrors
110117
) = Self.fillInJobsAndProducers(workload)
111118

112119
self.argsResolver = argsResolver
@@ -131,20 +138,21 @@ public final class MultiJobExecutor {
131138
producerMap: [VirtualPath: Int],
132139
primaryIndices: Range<Int>,
133140
postCompileIndices: Range<Int>,
134-
incrementalCompilationState: IncrementalCompilationState?)
141+
incrementalCompilationState: IncrementalCompilationState?,
142+
continueBuildingAfterErrors: Bool)
135143
{
136144
var jobs = [Job]()
137145
var producerMap = [VirtualPath: Int]()
138146
let primaryIndices, postCompileIndices: Range<Int>
139147
let incrementalCompilationState: IncrementalCompilationState?
140-
switch workload {
148+
switch workload.kind {
141149
case let .incremental(ics):
142150
incrementalCompilationState = ics
143151
primaryIndices = Self.addJobs(
144152
ics.mandatoryPreOrCompileJobsInOrder,
145153
to: &jobs,
146154
producing: &producerMap
147-
)
155+
)
148156
postCompileIndices = Self.addJobs(
149157
ics.postCompileJobs,
150158
to: &jobs,
@@ -161,7 +169,8 @@ public final class MultiJobExecutor {
161169
producerMap: producerMap,
162170
primaryIndices: primaryIndices,
163171
postCompileIndices: postCompileIndices,
164-
incrementalCompilationState: incrementalCompilationState)
172+
incrementalCompilationState: incrementalCompilationState,
173+
continueBuildingAfterErrors: workload.continueBuildingAfterErrors)
165174
}
166175

167176
/// Allow for dynamically adding jobs, since some compile jobs are added dynamically.
@@ -225,6 +234,19 @@ public final class MultiJobExecutor {
225234
executeAllJobsTaskBuildEngine!.taskNeedsInput(key, inputID: index)
226235
}
227236
}
237+
238+
fileprivate func cancelBuildIfNeeded(_ result: ProcessResult) {
239+
switch (result.exitStatus, continueBuildingAfterErrors) {
240+
case (.terminated(let code), false) where code != EXIT_SUCCESS:
241+
isBuildCancelled = true
242+
#if !os(Windows)
243+
case (.signalled, _):
244+
isBuildCancelled = true
245+
#endif
246+
default:
247+
break
248+
}
249+
}
228250
}
229251

230252
/// The work to be done.
@@ -426,8 +448,8 @@ class ExecuteJobRule: LLBuildRule {
426448
rememberIfInputSucceeded(engine, value: value)
427449
}
428450

451+
/// Called when the build engine thinks all inputs are available in order to run the job.
429452
override func inputsAvailable(_ engine: LLTaskBuildEngine) {
430-
// Return early any of the input failed.
431453
guard allInputsSucceeded else {
432454
return engine.taskIsComplete(DriverBuildValue.jobExecution(success: false))
433455
}
@@ -464,6 +486,10 @@ class ExecuteJobRule: LLBuildRule {
464486
}
465487

466488
private func executeJob(_ engine: LLTaskBuildEngine) {
489+
if context.isBuildCancelled {
490+
engine.taskIsComplete(DriverBuildValue.jobExecution(success: false))
491+
return
492+
}
467493
let context = self.context
468494
let resolver = context.argsResolver
469495
let job = myJob
@@ -514,6 +540,8 @@ class ExecuteJobRule: LLBuildRule {
514540
context.executorDelegate.jobFinished(job: job, result: result, pid: pid)
515541
}
516542
value = .jobExecution(success: success)
543+
544+
context.cancelBuildIfNeeded(result)
517545
} catch {
518546
if error is DiagnosticData {
519547
context.diagnosticsEngine.emit(error)

Tests/SwiftDriverTests/SwiftDriverTests.swift

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1471,6 +1471,34 @@ final class SwiftDriverTests: XCTestCase {
14711471
}
14721472
}
14731473

1474+
func testBatchModeContinueAfterErrors() throws {
1475+
throw XCTSkip("This test requires the fix to honoring -driver-use-frontend-path")
1476+
struct MockExecutor: DriverExecutor {
1477+
let resolver = try! ArgsResolver(fileSystem: localFileSystem)
1478+
1479+
struct ShouldNeverGetHere: LocalizedError {}
1480+
1481+
func execute(job: Job, forceResponseFiles: Bool, recordedInputModificationDates: [TypedVirtualPath : Date]) throws -> ProcessResult {
1482+
throw ShouldNeverGetHere()
1483+
}
1484+
func execute(workload: DriverExecutorWorkload,
1485+
delegate: JobExecutionDelegate,
1486+
numParallelJobs: Int,
1487+
forceResponseFiles: Bool,
1488+
recordedInputModificationDates: [TypedVirtualPath : Date]) throws {
1489+
XCTAssert(workload.continueBuildingAfterErrors)
1490+
}
1491+
func checkNonZeroExit(args: String..., environment: [String : String]) throws -> String {
1492+
throw ShouldNeverGetHere()
1493+
}
1494+
func description(of job: Job, forceResponseFiles: Bool) throws -> String {
1495+
throw ShouldNeverGetHere()
1496+
}
1497+
}
1498+
1499+
let driver = try Driver(args: ["swiftc", "foo1.swift", "bar1.swift", "-enable-batch-mode", "-driver-use-frontend-path", "/bin/echo"], executor: MockExecutor())
1500+
}
1501+
14741502
func testSingleThreadedWholeModuleOptimizationCompiles() throws {
14751503
var driver1 = try Driver(args: ["swiftc", "-whole-module-optimization", "foo.swift", "bar.swift", "-module-name", "Test", "-target", "x86_64-apple-macosx10.15", "-emit-module-interface", "-emit-objc-header-path", "Test-Swift.h", "-emit-private-module-interface-path", "Test.private.swiftinterface"])
14761504
let plannedJobs = try driver1.planBuild()

0 commit comments

Comments
 (0)