Skip to content

Commit 01a6386

Browse files
author
David Ungar
committed
Incorporate Colton Schlosser's ideas.
1 parent 32ac44e commit 01a6386

File tree

5 files changed

+56
-13
lines changed

5 files changed

+56
-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: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -898,7 +898,7 @@ extension Driver {
898898
allJobs: [Job],
899899
forceResponseFiles: Bool
900900
) throws {
901-
let continueBuildingAfterErrors = parsedOptions.contains(.continueBuildingAfterErrors)
901+
let continueBuildingAfterErrors = computeContinueBuildingAfterErrors()
902902
try executor.execute(
903903
workload: .init(allJobs,
904904
incrementalCompilationState,
@@ -1419,8 +1419,25 @@ extension Driver {
14191419

14201420
return numJobs
14211421
}
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+
}
14221438
}
14231439

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

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: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ public final class MultiJobExecutor {
8888
private(set) var executeAllJobsTaskBuildEngine: LLTaskBuildEngine? = nil
8989

9090
/// If a job fails, the driver needs to stop running jobs.
91-
private(set) var shouldStopRunningJobs = false
91+
private(set) var isBuildCancelled = false
9292

9393
/// The value of the option
9494
let continueBuildingAfterErrors: Bool
@@ -235,13 +235,13 @@ public final class MultiJobExecutor {
235235
}
236236
}
237237

238-
fileprivate func decideIfShouldStopRunningJobs(_ result: ProcessResult) {
238+
fileprivate func cancelBuildIfNeeded(_ result: ProcessResult) {
239239
switch (result.exitStatus, continueBuildingAfterErrors) {
240240
case (.terminated(let code), false) where code != EXIT_SUCCESS:
241-
shouldStopRunningJobs = true
241+
isBuildCancelled = true
242242
#if !os(Windows)
243243
case (.signalled, _):
244-
shouldStopRunningJobs = true
244+
isBuildCancelled = true
245245
#endif
246246
default:
247247
break
@@ -448,9 +448,9 @@ class ExecuteJobRule: LLBuildRule {
448448
rememberIfInputSucceeded(engine, value: value)
449449
}
450450

451+
/// Called when the build engine thinks all inputs are available in order to run the job.
451452
override func inputsAvailable(_ engine: LLTaskBuildEngine) {
452-
// Return early any of the input failed.
453-
guard allInputsSucceeded else {
453+
guard allInputsSucceeded, !context.isBuildCancelled else {
454454
return engine.taskIsComplete(DriverBuildValue.jobExecution(success: false))
455455
}
456456

@@ -486,6 +486,7 @@ class ExecuteJobRule: LLBuildRule {
486486
}
487487

488488
private func executeJob(_ engine: LLTaskBuildEngine) {
489+
precondition(!context.isBuildCancelled)
489490
let context = self.context
490491
let resolver = context.argsResolver
491492
let job = myJob
@@ -494,10 +495,6 @@ class ExecuteJobRule: LLBuildRule {
494495
let value: DriverBuildValue
495496
var pid = 0
496497
do {
497-
if context.shouldStopRunningJobs {
498-
engine.taskIsComplete(DriverBuildValue.jobExecution(success: false))
499-
return
500-
}
501498
let arguments: [String] = try resolver.resolveArgumentList(for: job,
502499
forceResponseFiles: context.forceResponseFiles)
503500

@@ -541,7 +538,7 @@ class ExecuteJobRule: LLBuildRule {
541538
}
542539
value = .jobExecution(success: success)
543540

544-
context.decideIfShouldStopRunningJobs(result)
541+
context.cancelBuildIfNeeded(result)
545542
} catch {
546543
if error is DiagnosticData {
547544
context.diagnosticsEngine.emit(error)

Tests/SwiftDriverTests/SwiftDriverTests.swift

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

1474+
func testBatchModeContinueAfterErrors() throws {
1475+
struct MockExecutor: DriverExecutor {
1476+
let resolver = try! ArgsResolver(fileSystem: localFileSystem)
1477+
1478+
func execute(job: Job, forceResponseFiles: Bool, recordedInputModificationDates: [TypedVirtualPath : Date]) throws -> ProcessResult {
1479+
fatalError()
1480+
}
1481+
func execute(workload: DriverExecutorWorkload,
1482+
delegate: JobExecutionDelegate,
1483+
numParallelJobs: Int,
1484+
forceResponseFiles: Bool,
1485+
recordedInputModificationDates: [TypedVirtualPath : Date]) throws {
1486+
XCTAssert(workload.continueBuildingAfterErrors)
1487+
}
1488+
func checkNonZeroExit(args: String..., environment: [String : String]) throws -> String {
1489+
fatalError()
1490+
}
1491+
func description(of job: Job, forceResponseFiles: Bool) throws -> String {
1492+
fatalError()
1493+
}
1494+
}
1495+
1496+
let driver = try Driver(args: ["swiftc", "foo1.swift", "bar1.swift", "-enable-batch-mode", "-driver-use-frontend-path", "/bin/echo"], executor: MockExecutor())
1497+
}
1498+
14741499
func testSingleThreadedWholeModuleOptimizationCompiles() throws {
14751500
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"])
14761501
let plannedJobs = try driver1.planBuild()

0 commit comments

Comments
 (0)