Skip to content

Commit db5cb94

Browse files
author
David Ungar
authored
Merge pull request #400 from davidungar/honor-always-rebuild-deps
[Incremental] Honor -driver-always-rebuild-dependents
2 parents e5acce7 + b3c309f commit db5cb94

File tree

8 files changed

+110
-12
lines changed

8 files changed

+110
-12
lines changed

Sources/SwiftDriver/Incremental Compilation/DependencyKey.swift

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import Foundation
33

44
/// A filename from another module
5-
struct ExternalDependency: Hashable, CustomStringConvertible {
5+
struct ExternalDependency: Hashable, Comparable, CustomStringConvertible {
66
let fileName: String
77

88
var file: VirtualPath? {
@@ -14,6 +14,10 @@ struct ExternalDependency: Hashable, CustomStringConvertible {
1414
public var description: String {
1515
fileName.description
1616
}
17+
18+
public static func < (lhs: Self, rhs: Self) -> Bool {
19+
lhs.fileName < rhs.fileName
20+
}
1721
}
1822

1923

@@ -31,7 +35,7 @@ public struct DependencyKey: Hashable, CustomStringConvertible {
3135
/// implementations white. Each node holds an instance variable describing which
3236
/// aspect of the entity it represents.
3337

34-
enum DeclAspect {
38+
enum DeclAspect: Comparable {
3539
case interface, implementation
3640
}
3741

@@ -112,3 +116,15 @@ var correspondingImplementation: Self? {
112116
}
113117
}
114118

119+
// MARK: - Comparing
120+
/// Needed to sort nodes to make tracing deterministic to test against emitted diagnostics
121+
extension DependencyKey: Comparable {
122+
public static func < (lhs: Self, rhs: Self) -> Bool {
123+
lhs.aspect != rhs.aspect ? lhs.aspect < rhs.aspect :
124+
lhs.designator < rhs.designator
125+
}
126+
}
127+
128+
extension DependencyKey.Designator: Comparable {
129+
}
130+

Sources/SwiftDriver/Incremental Compilation/IncrementalCompilationState.swift

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ public class IncrementalCompilationState {
118118
buildRecordInfo: buildRecordInfo,
119119
moduleDependencyGraph: moduleDependencyGraph,
120120
outOfDateBuildRecord: outOfDateBuildRecord,
121+
alwaysRebuildDependents: parsedOptions.contains(.driverAlwaysRebuildDependents),
121122
reportIncrementalDecision: reportIncrementalDecision)
122123

123124
self.moduleDependencyGraph = moduleDependencyGraph
@@ -208,6 +209,7 @@ extension IncrementalCompilationState {
208209
buildRecordInfo: BuildRecordInfo,
209210
moduleDependencyGraph: ModuleDependencyGraph,
210211
outOfDateBuildRecord: BuildRecord,
212+
alwaysRebuildDependents: Bool,
211213
reportIncrementalDecision: ((String, TypedVirtualPath?) -> Void)?
212214
) -> Set<TypedVirtualPath> {
213215

@@ -238,6 +240,7 @@ extension IncrementalCompilationState {
238240
let speculativeInputs = computeSpeculativeInputs(
239241
changedInputs: changedInputs,
240242
moduleDependencyGraph: moduleDependencyGraph,
243+
alwaysRebuildDependents: alwaysRebuildDependents,
241244
reportIncrementalDecision: reportIncrementalDecision)
242245
.subtracting(definitelyRequiredInputs)
243246

@@ -330,26 +333,32 @@ extension IncrementalCompilationState {
330333
private static func computeSpeculativeInputs(
331334
changedInputs: [(TypedVirtualPath, InputInfo.Status)],
332335
moduleDependencyGraph: ModuleDependencyGraph,
336+
alwaysRebuildDependents: Bool,
333337
reportIncrementalDecision: ((String, TypedVirtualPath?) -> Void)?
334338
) -> Set<TypedVirtualPath> {
335339
// Collect the files that will be compiled whose dependents should be schedule
336340
let cascadingFiles: [TypedVirtualPath] = changedInputs.compactMap { input, status in
337341
let basename = input.file.basename
338-
switch status {
339-
case .needsCascadingBuild:
342+
switch (status, alwaysRebuildDependents) {
343+
344+
case (_, true):
345+
reportIncrementalDecision?(
346+
"scheduling dependents of \(basename); -driver-always-rebuild-dependents", nil)
347+
return input
348+
case (.needsCascadingBuild, false):
340349
reportIncrementalDecision?(
341350
"scheduling dependents of \(basename); needed cascading build", nil)
342351
return input
343352

344-
case .upToDate: // Must be building because it changed
353+
case (.upToDate, false): // was up to date, but changed
345354
reportIncrementalDecision?(
346355
"not scheduling dependents of \(basename); unknown changes", nil)
347356
return nil
348-
case .newlyAdded:
357+
case (.newlyAdded, false):
349358
reportIncrementalDecision?(
350359
"not scheduling dependents of \(basename): no entry in build record or dependency graph", nil)
351360
return nil
352-
case .needsNonCascadingBuild:
361+
case (.needsNonCascadingBuild, false):
353362
reportIncrementalDecision?(
354363
"not scheduling dependents of \(basename): does not need cascading build", nil)
355364
return nil

Sources/SwiftDriver/Incremental Compilation/ModuleDependencyGraph Parts/Node.swift

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,22 @@ extension ModuleDependencyGraph.Node: Equatable, Hashable {
8080
}
8181
}
8282

83+
extension ModuleDependencyGraph.Node: Comparable {
84+
static func < (lhs: ModuleDependencyGraph.Node, rhs: ModuleDependencyGraph.Node) -> Bool {
85+
func lt<T: Comparable> (_ a: T?, _ b: T?) -> Bool {
86+
switch (a, b) {
87+
case let (x?, y?): return x < y
88+
case (nil, nil): return false
89+
case (nil, _?): return true
90+
case (_?, nil): return false
91+
}
92+
}
93+
return lhs.dependencyKey != rhs.dependencyKey ? lhs.dependencyKey < rhs.dependencyKey :
94+
lhs.swiftDeps != rhs.swiftDeps ? lt(lhs.swiftDeps, rhs.swiftDeps)
95+
: lt(lhs.fingerprint, rhs.fingerprint)
96+
}
97+
}
98+
8399

84100
extension ModuleDependencyGraph.Node: CustomStringConvertible {
85101
public var description: String {

Sources/SwiftDriver/Incremental Compilation/ModuleDependencyGraph Parts/NodeFinder.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,14 @@ extension ModuleDependencyGraph.NodeFinder {
7171
.map(fnVerifyingSwiftDeps)
7272
}
7373

74+
func forEachUseInOrder(of def: Graph.Node, _ fn: (Graph.Node, Graph.SwiftDeps) -> Void) {
75+
var uses = [(Graph.Node, Graph.SwiftDeps)]()
76+
forEachUse(of: def) {
77+
uses.append(($0, $1))
78+
}
79+
uses.sorted {$0.0 < $1.0} .forEach { fn($0.0, $0.1) }
80+
}
81+
7482
func mappings(of n: Graph.Node) -> [(Graph.SwiftDeps?, DependencyKey)]
7583
{
7684
nodeMap.compactMap {

Sources/SwiftDriver/Incremental Compilation/ModuleDependencyGraph Parts/SwiftDeps.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,3 +46,9 @@ extension ModuleDependencyGraph.SwiftDeps {
4646
file.name
4747
}
4848
}
49+
// MARK: - comparing
50+
extension ModuleDependencyGraph.SwiftDeps: Comparable {
51+
static func < (lhs: Self, rhs: Self) -> Bool {
52+
lhs.file.name < rhs.file.name
53+
}
54+
}

Sources/SwiftDriver/Incremental Compilation/ModuleDependencyGraph Parts/Tracer.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,8 @@ extension ModuleDependencyGraph.Tracer {
5757
where Nodes.Element == ModuleDependencyGraph.Node
5858
{
5959
self.graph = graph
60-
self.startingPoints = Array(defs)
60+
// Sort so "Tracing" diagnostics are deterministically ordered
61+
self.startingPoints = defs.sorted()
6162
self.currentPathIfTracing = graph.reportIncrementalDecision != nil ? [] : nil
6263
self.diagnosticEngine = diagnosticEngine
6364
}
@@ -83,7 +84,7 @@ extension ModuleDependencyGraph.Tracer {
8384
let pathLengthAfterArrival = traceArrival(at: definition);
8485

8586
// If this use also provides something, follow it
86-
graph.nodeFinder.forEachUse(of: definition) { use, _ in
87+
graph.nodeFinder.forEachUseInOrder(of: definition) { use, _ in
8788
findNextPreviouslyUntracedDependent(of: use)
8889
}
8990
traceDeparture(pathLengthAfterArrival);

Sources/SwiftDriver/Incremental Compilation/ModuleDependencyGraph.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ extension ModuleDependencyGraph {
194194
// These nodes will depend on the *interface* of the external Decl.
195195
let key = DependencyKey(interfaceFor: externalSwiftDeps)
196196
let node = Node(key: key, fingerprint: nil, swiftDeps: nil)
197-
nodeFinder.forEachUse(of: node) { use, useSwiftDeps in
197+
nodeFinder.forEachUseInOrder(of: node) { use, useSwiftDeps in
198198
if isUntraced(use) {
199199
fn(useSwiftDeps)
200200
}

Tests/SwiftDriverTests/IncrementalCompilationTests.swift

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,14 @@ final class IncrementalCompilationTests: XCTestCase {
362362
#endif
363363
}
364364

365+
// FIXME: Expect failure in Linux in CI just as testIncrementalDiagnostics
366+
func testAlwaysRebuildDependents() throws {
367+
#if !os(Linux)
368+
tryInitial(true)
369+
tryTouchingMainAlwaysRebuildDependents(true)
370+
#endif
371+
}
372+
365373
func testIncremental() throws {
366374
try testIncremental(checkDiagnostics: false)
367375
}
@@ -500,6 +508,36 @@ final class IncrementalCompilationTests: XCTestCase {
500508
whenAutolinking: autolinkLifecycleExpectations)
501509
}
502510

511+
func tryTouchingMainAlwaysRebuildDependents(_ checkDiagnostics: Bool) {
512+
touch("main")
513+
let extraArgument = "-driver-always-rebuild-dependents"
514+
try! doABuild(
515+
"non-propagating but \(extraArgument)",
516+
checkDiagnostics: checkDiagnostics,
517+
extraArguments: [extraArgument],
518+
expectingRemarks: [
519+
"Incremental compilation: Scheduing changed input {compile: main.o <= main.swift}",
520+
"Incremental compilation: May skip current input: {compile: other.o <= other.swift}",
521+
"Incremental compilation: Queuing (initial): {compile: main.o <= main.swift}",
522+
"Incremental compilation: scheduling dependents of main.swift; -driver-always-rebuild-dependents",
523+
"Incremental compilation: Traced: interface of top-level name foo from: main.swift -> implementation of other.swiftdeps from: other.swift",
524+
"Incremental compilation: Found dependent of main.swift: {compile: other.o <= other.swift}",
525+
"Incremental compilation: Immediately scheduling dependent on main.swift {compile: other.o <= other.swift}",
526+
"Incremental compilation: Queuing (dependent): {compile: other.o <= other.swift}",
527+
"Found 2 batchable jobs",
528+
"Forming into 1 batch",
529+
"Adding {compile: main.swift} to batch 0",
530+
"Adding {compile: other.swift} to batch 0",
531+
"Forming batch job from 2 constituents: main.swift, other.swift",
532+
"Incremental compilation: Queuing Compiling main.swift, other.swift",
533+
"Starting Compiling main.swift, other.swift",
534+
"Finished Compiling main.swift, other.swift",
535+
"Starting Linking theModule",
536+
"Finished Linking theModule",
537+
],
538+
whenAutolinking: autolinkLifecycleExpectations)
539+
}
540+
503541
func touch(_ name: String) {
504542
print("*** touching \(name) ***", to: &stderrStream); stderrStream.flush()
505543
let (path, contents) = try! XCTUnwrap(inputPathsAndContents.filter {$0.0.pathString.contains(name)}.first)
@@ -517,17 +555,20 @@ final class IncrementalCompilationTests: XCTestCase {
517555
}
518556
func doABuild(_ message: String,
519557
checkDiagnostics: Bool,
558+
extraArguments: [String] = [],
520559
expectingRemarks texts: [String],
521560
whenAutolinking: [String]) throws {
522561
try doABuild(
523562
message,
524563
checkDiagnostics: checkDiagnostics,
564+
extraArguments: extraArguments,
525565
expecting: texts.map {.remark($0)},
526566
expectingWhenAutolinking: whenAutolinking.map {.remark($0)})
527567
}
528568

529569
func doABuild(_ message: String,
530570
checkDiagnostics: Bool,
571+
extraArguments: [String] = [],
531572
expecting expectations: [Diagnostic.Message],
532573
expectingWhenAutolinking autolinkExpectations: [Diagnostic.Message]) throws {
533574
print("*** starting build \(message) ***", to: &stderrStream); stderrStream.flush()
@@ -537,8 +578,9 @@ final class IncrementalCompilationTests: XCTestCase {
537578
try? driver.run(jobs: jobs)
538579
}
539580

581+
let allArgs = args + extraArguments
540582
if checkDiagnostics {
541-
try assertDriverDiagnostics(args: args) {driver, verifier in
583+
try assertDriverDiagnostics(args: allArgs) {driver, verifier in
542584
verifier.forbidUnexpected(.error, .warning, .note, .remark, .ignored)
543585
expectations.forEach {verifier.expect($0)}
544586
if driver.isAutolinkExtractJobNeeded {
@@ -551,7 +593,7 @@ final class IncrementalCompilationTests: XCTestCase {
551593
let diagnosticEngine = DiagnosticsEngine(handlers: [
552594
{print($0, to: &stderrStream); stderrStream.flush()}
553595
])
554-
var driver = try Driver(args: args, env: ProcessEnv.vars,
596+
var driver = try Driver(args: allArgs, env: ProcessEnv.vars,
555597
diagnosticsEngine: diagnosticEngine,
556598
fileSystem: localFileSystem)
557599
doIt(&driver)

0 commit comments

Comments
 (0)