Skip to content

Commit 6e29a42

Browse files
author
David Ungar
authored
Merge pull request #751 from davidungar/5.5-clean-build-fix
[5.5, Incremental]: Fix clean build performance regression
2 parents c7f0a0b + 2de0f00 commit 6e29a42

File tree

7 files changed

+69
-22
lines changed

7 files changed

+69
-22
lines changed

Sources/SwiftDriver/IncrementalCompilation/DependencyKey.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,8 @@ import TSCBasic
5151
return "non-path: '\(fileName)'"
5252
}
5353
switch path.extension {
54-
case FileType.swiftModule.rawValue:
55-
// Swift modules have an extra component at the end that is not descriptive
54+
case FileType.swiftModule.rawValue, FileType.swiftInterface.rawValue:
55+
// These have an extra component at the end that is not descriptive
5656
return path.parentDirectory.basename
5757
default:
5858
return path.basename

Sources/SwiftDriver/IncrementalCompilation/FirstWaveComputer.swift

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -131,13 +131,14 @@ extension IncrementalCompilationState.FirstWaveComputer {
131131
}
132132
}
133133

134-
let inputsHavingMalformedDependencySources =
134+
let inputsMissingFromGraph =
135135
sourceFiles.currentInOrder.filter { sourceFile in
136136
!moduleDependencyGraph.containsNodes(forSourceFile: sourceFile)
137137
}
138138

139-
if let reporter = reporter {
140-
for input in inputsHavingMalformedDependencySources {
139+
if let reporter = reporter,
140+
moduleDependencyGraph.phase == .buildingWithoutAPrior {
141+
for input in inputsMissingFromGraph {
141142
reporter.report("Has malformed dependency source; will queue", input)
142143
}
143144
}
@@ -156,7 +157,7 @@ extension IncrementalCompilationState.FirstWaveComputer {
156157
let definitelyRequiredInputs =
157158
Set(changedInputs.map({ $0.filePath }) +
158159
inputsInvalidatedByExternals +
159-
inputsHavingMalformedDependencySources +
160+
inputsMissingFromGraph +
160161
inputsMissingOutputs)
161162
if let reporter = reporter {
162163
for scheduledInput in sortByCommandLineOrder(definitelyRequiredInputs) {

Sources/SwiftDriver/IncrementalCompilation/IncrementalDependencyAndInputSetup.swift

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ extension IncrementalCompilationState {
124124
// Do not try to reuse a graph from a different compilation, so check
125125
// the build record.
126126
@_spi(Testing) public var readPriorsFromModuleDependencyGraph: Bool {
127-
maybeBuildRecord != nil && options.contains(.readPriorsFromModuleDependencyGraph)
127+
options.contains(.readPriorsFromModuleDependencyGraph)
128128
}
129129
@_spi(Testing) public var alwaysRebuildDependents: Bool {
130130
options.contains(.alwaysRebuildDependents)
@@ -233,10 +233,14 @@ extension IncrementalCompilationState.IncrementalDependencyAndInputSetup {
233233
warning: "Could not read \(dependencyGraphPath), will not do cross-module incremental builds")
234234
graphIfPresent = nil
235235
}
236-
guard let graph = graphIfPresent
237-
else {
238-
return buildInitialGraphFromSwiftDepsAndCollectInputsInvalidatedByChangedExternals()
239-
}
236+
// Do not fall back to `buildInitialGraphFromSwiftDepsAndCollectInputsInvalidatedByChangedExternals`
237+
// because it would be unsound to read a `swiftmodule` file with only a partial set of integrated `swiftdeps`.
238+
// A fingerprint change in such a `swiftmodule` would not be able to propagate and invalidate a use
239+
// in a as-yet-unread swiftdeps file.
240+
//
241+
// Instead, just compile everything. It's OK to be unsound then because every file will be compiled anyway.
242+
// Do this by creating an empty graph here.
243+
let graph = graphIfPresent ?? ModuleDependencyGraph(self, .buildingAfterEachCompilation)
240244
guard graph.populateInputDependencySourceMap() else {
241245
return nil
242246
}

Sources/SwiftDriver/IncrementalCompilation/ModuleDependencyGraph.swift

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,8 @@ extension ModuleDependencyGraph {
168168
// MARK: - Getting a graph read from priors ready to use
169169
extension ModuleDependencyGraph {
170170
func collectNodesInvalidatedByChangedOrAddedExternals() -> DirectlyInvalidatedNodeSet {
171-
fingerprintedExternalDependencies.reduce(into: DirectlyInvalidatedNodeSet()) {
171+
assert(info.isCrossModuleIncrementalBuildEnabled)
172+
return fingerprintedExternalDependencies.reduce(into: DirectlyInvalidatedNodeSet()) {
172173
invalidatedNodes, fed in
173174
invalidatedNodes.formUnion (
174175
self.collectNodesInvalidatedByProcessing(fingerprintedExternalDependency: fed,
@@ -356,22 +357,23 @@ extension ModuleDependencyGraph {
356357
should: fed,
357358
whichIsNewToTheGraph: isNewToTheGraph,
358359
closeOverSwiftModulesIn: self)
359-
360-
let invalidatedNodesFromIncrementalExternal = whyIntegrateForClosure.flatMap { why in
361-
collectNodesInvalidatedByAttemptingToProcess(why, fed)
362-
}
363-
364-
guard let whyInvalidate = ExternalDependency.Why(
360+
361+
// collectNodesInvalidatedByAttemptingToProcess will change the currency cache
362+
// so get this reason now.
363+
let whyInvalidate = ExternalDependency.Why(
365364
shouldUsesOf: fed,
366365
whichIsNewToTheGraph: isNewToTheGraph,
367366
beInvalidatedIn: self)
368-
else {
369-
return DirectlyInvalidatedNodeSet()
367+
368+
let invalidatedNodesFromIncrementalExternal = whyIntegrateForClosure.flatMap { why in
369+
collectNodesInvalidatedByAttemptingToProcess(why, fed)
370370
}
371371

372372
// If there was an error integrating the external dependency, or if it was not an incremental one,
373373
// return anything that uses that dependency.
374-
return invalidatedNodesFromIncrementalExternal ?? collectUntracedNodesUsing(whyInvalidate, fed)
374+
return invalidatedNodesFromIncrementalExternal
375+
?? whyInvalidate.map {collectUntracedNodesUsing($0, fed)}
376+
?? DirectlyInvalidatedNodeSet()
375377
}
376378

377379
func hasFileChanged(of externalDependency: ExternalDependency
@@ -387,6 +389,10 @@ extension ModuleDependencyGraph {
387389
externalDependencyModTimeCache[externalDependency] = hasChanged
388390
return hasChanged
389391
}
392+
393+
func beCurrent(_ externalDependency: ExternalDependency) {
394+
externalDependencyModTimeCache[externalDependency] = false
395+
}
390396

391397
/// Try to read and integrate an external dependency.
392398
/// Return nil if it's not incremental, or if an error occurs.
@@ -399,6 +405,7 @@ extension ModuleDependencyGraph {
399405
else {
400406
return nil
401407
}
408+
beCurrent(fed.externalDependency) // Don't read the same external twice
402409
let invalidatedNodes = Integrator.integrate(from: unserializedDepGraph, into: self)
403410
info.reporter?.reportInvalidated(invalidatedNodes, by: fed.externalDependency, why)
404411
return invalidatedNodes

Sources/SwiftDriver/IncrementalCompilation/ModuleDependencyGraphParts/DependencySource.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ extension DependencySource {
5656
) -> SourceFileDependencyGraph? {
5757
let graphIfPresent: SourceFileDependencyGraph?
5858
do {
59+
reporter?.report("Reading dependencies from \(description)")
5960
graphIfPresent = try SourceFileDependencyGraph.read(
6061
from: self,
6162
on: fileSystem)

Tests/IncrementalTestFramework/CompiledSourceCollector.swift

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ import TestUtilities
2121
/// - seealso: Test
2222
struct CompiledSourceCollector {
2323
private var collectedCompiledBasenames = [String]()
24+
private var collectedReadDependencies = Set<String>()
25+
2426

2527
private func getCompiledBasenames(from d: Diagnostic) -> [String] {
2628
let dd = d.description
@@ -37,10 +39,29 @@ struct CompiledSourceCollector {
3739
}
3840
.compactMap {String($0)}
3941
}
42+
43+
private func getReadDependencies(from d: Diagnostic) -> String? {
44+
let dd = d.description
45+
guard let startOfReading = dd.range(of: "Reading dependencies ")?.upperBound
46+
else {
47+
return nil
48+
}
49+
return String(dd.suffix(from: startOfReading))
50+
}
51+
52+
private mutating func appendReadDependency(_ dep: String) {
53+
let wasNew = collectedReadDependencies.insert(dep).inserted
54+
guard wasNew || dep.hasSuffix(FileType.swiftDeps.rawValue)
55+
else {
56+
XCTFail("Swiftmodule \(dep) read twice")
57+
return
58+
}
59+
}
4060

4161
/// Process a diagnistic
4262
mutating func handle(diagnostic d: Diagnostic) {
4363
collectedCompiledBasenames.append(contentsOf: getCompiledBasenames(from: d))
64+
getReadDependencies(from: d).map {appendReadDependency($0)}
4465
}
4566

4667
/// Returns the basenames of the compiled files, e.g. for `/a/b/foo.swift`, returns `foo.swift`.

Tests/SwiftDriverTests/IncrementalCompilationTests.swift

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ extension IncrementalCompilationTests {
159159
expectNoDotFiles()
160160
try tryInitial(extraArguments: ["-driver-emit-fine-grained-dependency-dot-file-after-every-import"])
161161
expect(dotFilesFor: [
162+
DependencyGraphDotFileWriter.moduleDependencyGraphBasename,
162163
"main.swiftdeps",
163164
DependencyGraphDotFileWriter.moduleDependencyGraphBasename,
164165
"other.swiftdeps",
@@ -322,14 +323,15 @@ extension IncrementalCompilationTests {
322323
"Enabling incremental cross-module building",
323324
"Incremental compilation: Incremental compilation could not read build record at",
324325
"Incremental compilation: Disabling incremental build: could not read build record",
325-
"Incremental compilation: Created dependency graph from swiftdeps files",
326326
"Found 2 batchable jobs",
327327
"Forming into 1 batch",
328328
"Adding {compile: main.swift} to batch 0",
329329
"Adding {compile: other.swift} to batch 0",
330330
"Forming batch job from 2 constituents: main.swift, other.swift",
331331
"Starting Compiling main.swift, other.swift",
332332
"Finished Compiling main.swift, other.swift",
333+
"Incremental compilation: Reading dependencies from main.swiftdeps",
334+
"Incremental compilation: Reading dependencies from other.swiftdeps",
333335
"Incremental compilation: Scheduling all post-compile jobs because something was compiled",
334336
"Starting Linking theModule",
335337
"Finished Linking theModule",
@@ -378,6 +380,7 @@ extension IncrementalCompilationTests {
378380
"Forming batch job from 1 constituents: other.swift",
379381
"Starting Compiling other.swift",
380382
"Finished Compiling other.swift",
383+
"Incremental compilation: Reading dependencies from other.swiftdeps",
381384
"Incremental compilation: Scheduling all post-compile jobs because something was compiled",
382385
"Starting Linking theModule",
383386
"Finished Linking theModule",
@@ -410,6 +413,8 @@ extension IncrementalCompilationTests {
410413
"Forming batch job from 2 constituents: main.swift, other.swift",
411414
"Starting Compiling main.swift, other.swift",
412415
"Finished Compiling main.swift, other.swift",
416+
"Incremental compilation: Reading dependencies from main.swiftdeps",
417+
"Incremental compilation: Reading dependencies from other.swiftdeps",
413418
"Incremental compilation: Scheduling all post-compile jobs because something was compiled",
414419
"Starting Linking theModule",
415420
"Finished Linking theModule",
@@ -439,6 +444,7 @@ extension IncrementalCompilationTests {
439444
"Forming batch job from 1 constituents: main.swift",
440445
"Starting Compiling main.swift",
441446
"Finished Compiling main.swift",
447+
"Incremental compilation: Reading dependencies from main.swiftdeps",
442448
"Incremental compilation: Fingerprint changed for interface of source file main.swiftdeps in main.swiftdeps",
443449
"Incremental compilation: Fingerprint changed for implementation of source file main.swiftdeps in main.swiftdeps",
444450
"Incremental compilation: Traced: interface of source file main.swiftdeps in main.swift -> interface of top-level name 'foo' in main.swift -> implementation of source file other.swiftdeps in other.swift",
@@ -450,6 +456,7 @@ extension IncrementalCompilationTests {
450456
"Forming batch job from 1 constituents: other.swift",
451457
"Starting Compiling other.swift",
452458
"Finished Compiling other.swift",
459+
"Incremental compilation: Reading dependencies from other.swiftdeps",
453460
"Incremental compilation: Scheduling all post-compile jobs because something was compiled",
454461
"Starting Linking theModule",
455462
"Finished Linking theModule",
@@ -485,6 +492,8 @@ extension IncrementalCompilationTests {
485492
"Incremental compilation: Scheduling all post-compile jobs because something was compiled",
486493
"Starting Compiling main.swift, other.swift",
487494
"Finished Compiling main.swift, other.swift",
495+
"Incremental compilation: Reading dependencies from main.swiftdeps",
496+
"Incremental compilation: Reading dependencies from other.swiftdeps",
488497
"Starting Linking theModule",
489498
"Finished Linking theModule",
490499
],
@@ -511,6 +520,7 @@ extension IncrementalCompilationTests {
511520
.remark("Forming batch job from 1 constituents: main.swift"),
512521
.remark("Starting Compiling main.swift"),
513522
.remark("Finished Compiling main.swift"),
523+
.remark("Incremental compilation: Reading dependencies from main.swiftdeps"),
514524
.remark("Incremental compilation: Fingerprint changed for interface of source file main.swiftdeps in main.swiftdeps"),
515525
.remark("Incremental compilation: Fingerprint changed for implementation of source file main.swiftdeps in main.swiftdeps"),
516526
.remark("Incremental compilation: Traced: interface of source file main.swiftdeps in main.swift -> interface of top-level name 'foo' in main.swift -> implementation of source file other.swiftdeps in other.swift"),
@@ -525,6 +535,7 @@ extension IncrementalCompilationTests {
525535
.remark("Forming batch job from 1 constituents: other.swift"),
526536
.remark("Starting Compiling other.swift"),
527537
.remark("Finished Compiling other.swift"),
538+
.remark("Incremental compilation: Reading dependencies from other.swiftdeps"),
528539
.remark("Incremental compilation: Scheduling all post-compile jobs because something was compiled"),
529540
.remark("Starting Linking theModule"),
530541
.remark("Finished Linking theModule"),
@@ -607,6 +618,8 @@ extension IncrementalCompilationTests {
607618
"Forming batch job from 2 constituents: main.swift, other.swift",
608619
"Starting Compiling main.swift, other.swift",
609620
"Finished Compiling main.swift, other.swift",
621+
"Incremental compilation: Reading dependencies from main.swiftdeps",
622+
"Incremental compilation: Reading dependencies from other.swiftdeps",
610623
"Incremental compilation: Scheduling all post-compile jobs because something was compiled",
611624
"Starting Linking theModule",
612625
"Finished Linking theModule",

0 commit comments

Comments
 (0)