Skip to content

Commit c4dd5b5

Browse files
author
David Ungar
committed
Skip post-compile jobs only if no compiles ran, and all the outputs are up-to-date.
1 parent 1430a1f commit c4dd5b5

File tree

9 files changed

+99
-32
lines changed

9 files changed

+99
-32
lines changed

Sources/SwiftDriver/IncrementalCompilation/BuildRecord.swift

Lines changed: 34 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -19,24 +19,30 @@ public struct BuildRecord {
1919
public let swiftVersion: String
2020
/// When testing, the argsHash may be missing from the build record
2121
public let argsHash: String?
22-
public let buildTime: Date
22+
/// Next compile, will compare an input mod time against the start time of the previous build
23+
public let buildStartTime: Date
24+
/// Next compile, will compare an output mod time against the end time of the previous build
25+
public let buildEndTime: Date
2326
/// The date is the modification time of the main input file the last time the driver ran
2427
public let inputInfos: [VirtualPath: InputInfo]
2528

2629
public init(argsHash: String?,
2730
swiftVersion: String,
28-
buildTime: Date,
31+
buildStartTime: Date,
32+
buildEndTime: Date,
2933
inputInfos: [VirtualPath: InputInfo]) {
3034
self.argsHash = argsHash
3135
self.swiftVersion = swiftVersion
32-
self.buildTime = buildTime
36+
self.buildStartTime = buildStartTime
37+
self.buildEndTime = buildEndTime
3338
self.inputInfos = inputInfos
3439
}
3540

3641
private enum SectionName: String, CaseIterable {
3742
case swiftVersion = "version"
3843
case argsHash = "options"
39-
case buildTime = "build_time"
44+
case buildStartTime = "build_start_time"
45+
case buildEndTime = "build_end_time"
4046
case inputInfos = "inputs"
4147

4248
var serializedName: String { rawValue }
@@ -59,7 +65,8 @@ public extension BuildRecord {
5965
var argsHash: String?
6066
var swiftVersion: String?
6167
// Legacy driver does not disable incremental if no buildTime field.
62-
var buildTime: Date = .distantPast
68+
var buildStartTime: Date = .distantPast
69+
var buildEndTime: Date = .distantFuture
6370
var inputInfos: [VirtualPath: InputInfo]?
6471
for (key, value) in sections {
6572
guard let k = key.string else {
@@ -80,15 +87,23 @@ public extension BuildRecord {
8087
return nil
8188
}
8289
argsHash = s
83-
case SectionName.buildTime.serializedName:
90+
case SectionName.buildStartTime.serializedName:
8491
guard let d = Self.decodeDate(value,
8592
forInputInfo: false,
8693
failedToReadOutOfDateMap)
8794
else {
8895
return nil
8996
}
90-
buildTime = d
91-
case SectionName.inputInfos.serializedName:
97+
buildStartTime = d
98+
case SectionName.buildEndTime.serializedName:
99+
guard let d = Self.decodeDate(value,
100+
forInputInfo: false,
101+
failedToReadOutOfDateMap)
102+
else {
103+
return nil
104+
}
105+
buildEndTime = d
106+
case SectionName.inputInfos.serializedName:
92107
guard let ii = Self.decodeInputInfos(value, failedToReadOutOfDateMap) else {
93108
return nil
94109
}
@@ -111,7 +126,8 @@ public extension BuildRecord {
111126
}
112127
self.init(argsHash: argsHash,
113128
swiftVersion: sv,
114-
buildTime: buildTime,
129+
buildStartTime: buildStartTime,
130+
buildEndTime: buildEndTime,
115131
inputInfos: iis)
116132
}
117133

@@ -181,7 +197,8 @@ extension BuildRecord {
181197
compilationInputModificationDates: [TypedVirtualPath: Date],
182198
actualSwiftVersion: String,
183199
argsHash: String!,
184-
timeBeforeFirstJob: Date
200+
timeBeforeFirstJob: Date,
201+
timeAfterLastJob: Date
185202
) {
186203
let jobResultsByInput = Dictionary(uniqueKeysWithValues:
187204
finishedJobResults.flatMap { entry in
@@ -197,7 +214,8 @@ extension BuildRecord {
197214
self.init(
198215
argsHash: argsHash,
199216
swiftVersion: actualSwiftVersion,
200-
buildTime: timeBeforeFirstJob,
217+
buildStartTime: timeBeforeFirstJob,
218+
buildEndTime: timeAfterLastJob,
201219
inputInfos: Dictionary(uniqueKeysWithValues: inputInfosArray)
202220
)
203221
}
@@ -216,10 +234,11 @@ extension BuildRecord {
216234
.map {(Yams.Node($0.0, .implicit, .doubleQuoted), Self.encode($0.1))}
217235
)
218236
let fieldNodes = [
219-
(SectionName.swiftVersion, Yams.Node(swiftVersion, .implicit, .doubleQuoted)),
220-
(SectionName.argsHash, Yams.Node(currentArgsHash, .implicit, .doubleQuoted)),
221-
(SectionName.buildTime, Self.encode(buildTime)),
222-
(SectionName.inputInfos, inputInfosNode )
237+
(SectionName.swiftVersion, Yams.Node(swiftVersion, .implicit, .doubleQuoted)),
238+
(SectionName.argsHash, Yams.Node(currentArgsHash, .implicit, .doubleQuoted)),
239+
(SectionName.buildStartTime, Self.encode(buildStartTime)),
240+
(SectionName.buildEndTime, Self.encode(buildEndTime)),
241+
(SectionName.inputInfos, inputInfosNode )
223242
] .map { (Yams.Node($0.0.serializedName), $0.1) }
224243

225244
let buildRecordNode = Yams.Node(fieldNodes, .implicit, .block)

Sources/SwiftDriver/IncrementalCompilation/BuildRecordInfo.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,8 @@ import SwiftOptions
167167
compilationInputModificationDates: compilationInputModificationDates,
168168
actualSwiftVersion: actualSwiftVersion,
169169
argsHash: currentArgsHash,
170-
timeBeforeFirstJob: timeBeforeFirstJob)
170+
timeBeforeFirstJob: timeBeforeFirstJob,
171+
timeAfterLastJob: Date())
171172
}
172173

173174
guard let contents = buildRecord.encode(currentArgsHash: currentArgsHash,

Sources/SwiftDriver/IncrementalCompilation/IncrementalCompilationState.swift

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,14 @@ public final class IncrementalCompilationState {
4646
/// Jobs to run *after* the last compile, for instance, link-editing.
4747
public let jobsAfterCompiles: [Job]
4848

49+
/// Can compare input mod times against this.
50+
private let buildStartTime: Date
51+
52+
/// In order to decide while postCompile jobs to run, need to compile their outputs against the build time
53+
private let buildEndTime: Date
54+
55+
private let fileSystem: FileSystem
56+
4957
/// A high-priority confinement queue that must be used to protect the incremental compilation state.
5058
private let confinementQueue: DispatchQueue = DispatchQueue(label: "com.apple.swift-driver.incremental-compilation-state", qos: .userInteractive)
5159

@@ -124,6 +132,9 @@ public final class IncrementalCompilationState {
124132
self.mandatoryJobsInOrder = initial.mandatoryJobsInOrder
125133
self.jobsAfterCompiles = jobsInPhases.afterCompiles
126134
self.moduleDependencyGraph = initial.graph
135+
self.buildStartTime = initial.buildStartTime
136+
self.buildEndTime = initial.buildEndTime
137+
self.fileSystem = driver.fileSystem
127138
self.driver = driver
128139
}
129140
}
@@ -150,6 +161,10 @@ extension IncrementalCompilationState {
150161
/// All of the pre-compile or compilation job (groups) known to be required
151162
/// for the first wave to execute.
152163
let mandatoryJobsInOrder: [Job]
164+
/// The last time this compilation was started. Used to compare against e.g. input file mod dates.
165+
let buildStartTime: Date
166+
/// The last time this compilation finished. Used to compare against output file mod dates
167+
let buildEndTime: Date
153168
}
154169
}
155170

@@ -290,6 +305,14 @@ extension IncrementalCompilationState {
290305
return try driver.formBatchedJobs(unbatched, showJobLifecycle: driver.showJobLifecycle)
291306
}
292307
}
308+
// MARK: - Scheduling post-compile jobs
309+
extension IncrementalCompilationState {
310+
public func canSkipPostCompile(job: Job) -> Bool {
311+
job.outputs.allSatisfy {output in
312+
let fileModTime = (try? fileSystem.lastModificationTime(for: output.file)) ?? .distantFuture
313+
return fileModTime < buildEndTime}
314+
}
315+
}
293316

294317
// MARK: - After the build
295318
extension IncrementalCompilationState {

Sources/SwiftDriver/IncrementalCompilation/InitialStateComputer.swift

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@ extension IncrementalCompilationState {
3838
@_spi(Testing) public let dependencyDotFilesIncludeExternals: Bool = true
3939
@_spi(Testing) public let dependencyDotFilesIncludeAPINotes: Bool = false
4040

41-
@_spi(Testing) public let buildTime: Date
41+
@_spi(Testing) public let buildStartTime: Date
42+
@_spi(Testing) public let buildEndTime: Date
4243

4344
@_spi(Testing) public init(
4445
_ options: IncrementalCompilationState.Options,
@@ -72,7 +73,8 @@ extension IncrementalCompilationState {
7273
self.isCrossModuleIncrementalBuildEnabled = options.contains(.enableCrossModuleIncrementalBuild)
7374
self.verifyDependencyGraphAfterEveryImport = options.contains(.verifyDependencyGraphAfterEveryImport)
7475
self.emitDependencyDotFileAfterEveryImport = options.contains(.emitDependencyDotFileAfterEveryImport)
75-
self.buildTime = maybeBuildRecord?.buildTime ?? .distantPast
76+
self.buildStartTime = maybeBuildRecord?.buildStartTime ?? .distantPast
77+
self.buildEndTime = maybeBuildRecord?.buildEndTime ?? .distantFuture
7678
}
7779

7880
func compute(batchJobFormer: inout Driver)
@@ -102,7 +104,9 @@ extension IncrementalCompilationState {
102104

103105
return InitialState(graph: graph,
104106
skippedCompileGroups: skippedCompileGroups,
105-
mandatoryJobsInOrder: mandatoryJobsInOrder)
107+
mandatoryJobsInOrder: mandatoryJobsInOrder,
108+
buildStartTime: buildStartTime,
109+
buildEndTime: buildEndTime)
106110
}
107111
}
108112
}

Sources/SwiftDriver/IncrementalCompilation/ModuleDependencyGraph.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -330,7 +330,7 @@ extension ModuleDependencyGraph {
330330
return true
331331
}
332332
let fileModTime = (try? info.fileSystem.lastModificationTime(for: depFile)) ?? .distantFuture
333-
let hasChanged = fileModTime >= info.buildTime
333+
let hasChanged = fileModTime >= info.buildStartTime
334334
externalDependencyModTimeCache[externalDependency] = hasChanged
335335
return hasChanged
336336
}

Sources/SwiftDriver/IncrementalCompilation/ModuleDependencyGraphParts/Integrator.swift

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -189,8 +189,6 @@ extension ModuleDependencyGraph.Integrator {
189189
}
190190
}
191191

192-
private var buildTime: Date { destination.info.buildTime }
193-
194192
// A `moduleGraphUseNode` is used by an externalDependency key being integrated.
195193
// Remember the dependency for later processing in externalDependencies, and
196194
// also return it in results.

Sources/SwiftDriverExecution/MultiJobExecutor.swift

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -390,17 +390,30 @@ class ExecuteAllJobsRule: LLBuildRule {
390390
let subtaskSuccess = try DriverBuildValue(value).success
391391
// After all compilation jobs are done, we can schedule post-compilation jobs,
392392
// including merge module and linking jobs.
393-
if inputID == allCompilationId && !context.primaryIndices.isEmpty && subtaskSuccess {
394-
context.postCompileIndices.forEach {
395-
engine.taskNeedsInput(ExecuteJobRule.RuleKey(index: $0), inputID: $0)
396-
}
393+
if inputID == allCompilationId && subtaskSuccess {
394+
schedulePostCompileJobs(engine)
397395
}
398396
allInputsSucceeded = allInputsSucceeded && subtaskSuccess
399397
} catch {
400398
allInputsSucceeded = false
401399
}
402400
}
403401

402+
/// After all compilation jobs have run, figure which, for instance link, jobs must run
403+
private func schedulePostCompileJobs(_ engine: LLTaskBuildEngine) {
404+
for postCompileIndex in context.postCompileIndices {
405+
let job = context.jobs[postCompileIndex]
406+
/// If any compile jobs ran, skip the expensive mod-time checks
407+
if context.primaryIndices.isEmpty,
408+
let incrementalCompilationState = context.incrementalCompilationState,
409+
incrementalCompilationState.canSkipPostCompile(job: job) {
410+
continue
411+
}
412+
engine.taskNeedsInput(ExecuteJobRule.RuleKey(index: postCompileIndex),
413+
inputID: postCompileIndex)
414+
}
415+
}
416+
404417
override func inputsAvailable(_ engine: LLTaskBuildEngine) {
405418
engine.taskIsComplete(DriverBuildValue.jobExecution(success: allInputsSucceeded))
406419
}

Tests/SwiftDriverTests/IncrementalCompilationTests.swift

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@ final class NonincrementalCompilationTests: XCTestCase {
2626
"Apple Swift version 5.1 (swiftlang-1100.0.270.13 clang-1100.0.33.7)")
2727
XCTAssertEqual(buildRecord.argsHash, "abbbfbcaf36b93e58efaadd8271ff142")
2828

29-
try XCTAssertEqual(buildRecord.buildTime,
29+
try XCTAssertEqual(buildRecord.buildStartTime,
30+
Date(legacyDriverSecsAndNanos: [1570318779, 32358000]))
31+
try XCTAssertEqual(buildRecord.buildEndTime,
3032
Date(legacyDriverSecsAndNanos: [1570318779, 32358000]))
3133
try XCTAssertEqual(buildRecord.inputInfos,
3234
[
@@ -51,8 +53,10 @@ final class NonincrementalCompilationTests: XCTestCase {
5153
"Apple Swift version 5.1 (swiftlang-1100.0.270.13 clang-1100.0.33.7)")
5254
XCTAssertEqual(buildRecord.argsHash, nil)
5355

54-
try XCTAssertEqual(buildRecord.buildTime,
56+
try XCTAssertEqual(buildRecord.buildStartTime,
5557
Date(legacyDriverSecsAndNanos: [1570318779, 32358000]))
58+
try XCTAssertEqual(buildRecord.buildEndTime,
59+
Date(legacyDriverSecsAndNanos: [1570318779, 32358010]))
5660
try XCTAssertEqual(buildRecord.inputInfos,
5761
[
5862
VirtualPath(path: "/Volumes/AS/repos/swift-driver/sandbox/sandbox/sandbox/file2.swift"):
@@ -211,7 +215,8 @@ final class NonincrementalCompilationTests: XCTestCase {
211215
"""
212216
version: "\(version)"
213217
options: "\(options)"
214-
build_time: [1570318779, 32357931]
218+
build_start_time: [1570318779, 32357931]
219+
build_end_time: [1570318779, 32357941]
215220
inputs:
216221
"\(file2)": !dirty [1570318778, 0]
217222
"\(main)": [1570083660, 0]
@@ -222,8 +227,10 @@ final class NonincrementalCompilationTests: XCTestCase {
222227
XCTAssertEqual(buildRecord.swiftVersion, version)
223228
XCTAssertEqual(buildRecord.argsHash, options)
224229
XCTAssertEqual(buildRecord.inputInfos.count, 3)
225-
XCTAssert(isCloseEnough(buildRecord.buildTime.legacyDriverSecsAndNanos,
230+
XCTAssert(isCloseEnough(buildRecord.buildStartTime.legacyDriverSecsAndNanos,
226231
[1570318779, 32357931]))
232+
XCTAssert(isCloseEnough(buildRecord.buildEndTime.legacyDriverSecsAndNanos,
233+
[1570318779, 32357941]))
227234

228235
XCTAssertEqual(try! buildRecord.inputInfos[VirtualPath(path: file2 )]!.status,
229236
.needsCascadingBuild)

Tests/SwiftDriverTests/Inputs/IncrementalCompilationInputs.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ enum Inputs {
1717
"""
1818
version: "Apple Swift version 5.1 (swiftlang-1100.0.270.13 clang-1100.0.33.7)"
1919
options: "abbbfbcaf36b93e58efaadd8271ff142"
20-
build_time: [1570318779, 32358000]
20+
build_start_time: [1570318779, 32358000]
21+
build_end_time: [1570318779, 32358010]
2122
inputs:
2223
"/Volumes/AS/repos/swift-driver/sandbox/sandbox/sandbox/file2.swift": !dirty [1570318778, 0]
2324
"/Volumes/AS/repos/swift-driver/sandbox/sandbox/sandbox/main.swift": [1570083660, 0]
@@ -27,7 +28,8 @@ enum Inputs {
2728
static var buildRecordWithoutOptions: String {
2829
"""
2930
version: "Apple Swift version 5.1 (swiftlang-1100.0.270.13 clang-1100.0.33.7)"
30-
build_time: [1570318779, 32358000]
31+
build_start_time: [1570318779, 32358000]
32+
build_end_time: [1570318779, 32358010]
3133
inputs:
3234
"/Volumes/AS/repos/swift-driver/sandbox/sandbox/sandbox/file2.swift": !dirty [1570318778, 0]
3335
"/Volumes/AS/repos/swift-driver/sandbox/sandbox/sandbox/main.swift": [1570083660, 0]

0 commit comments

Comments
 (0)