Skip to content

Commit b7ab843

Browse files
authored
Merge pull request #638 from davidungar/more-external-inval-info
[5.5][Incremental Imports] Print more info about externals when setting -driver-show-incremental
2 parents 28ca31b + c38cf48 commit b7ab843

File tree

4 files changed

+131
-30
lines changed

4 files changed

+131
-30
lines changed

Sources/SwiftDriver/IncrementalCompilation/DependencyKey.swift

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -367,3 +367,64 @@ extension DependencyKey: Comparable {
367367

368368
extension DependencyKey.Designator: Comparable {
369369
}
370+
371+
// MARK: - InvalidationReason
372+
extension ExternalDependency {
373+
/// When explaining incremental decisions, it helps to know why a particular external dependency
374+
/// was investigated.
375+
public enum Why: String, CustomStringConvertible {
376+
/// An `Import` of this file was added to the source code.
377+
case added
378+
379+
/// The imported file has changed.
380+
case changed
381+
382+
/// Used when testing invalidation
383+
case testing
384+
385+
/// Figure out the reason to invalidate or process a dependency.
386+
/// Even if invalidation won't be reported to the caller, a new or added
387+
/// incremental external dependency may require integration in order to
388+
/// transitively close them, (e.g. if an imported module imports a module).
389+
init?(should fed: FingerprintedExternalDependency,
390+
whichIsNewToTheGraph isNewToTheGraph: Bool,
391+
closeOverSwiftModulesIn graph: ModuleDependencyGraph) {
392+
guard graph.info.isCrossModuleIncrementalBuildEnabled
393+
else { return nil }
394+
self.init(should: fed,
395+
whichIsSignificantlyNew: isNewToTheGraph,
396+
beInvestigatedIn: graph)
397+
}
398+
399+
400+
init?(shouldUsesOf fed: FingerprintedExternalDependency,
401+
whichIsNewToTheGraph isNewToTheGraph: Bool,
402+
beInvalidatedIn graph: ModuleDependencyGraph) {
403+
if graph.phase.isCompilingAllInputsNoMatterWhat {
404+
// going to compile every input anyway, less work for callers
405+
return nil
406+
}
407+
let isSignificantlyNew = graph.phase.shouldNewExternalDependenciesTriggerInvalidation && isNewToTheGraph
408+
self.init(should: fed,
409+
whichIsSignificantlyNew: isSignificantlyNew,
410+
beInvestigatedIn: graph)
411+
}
412+
413+
/// Figure out the reason to invalidate or process a dependency
414+
private init?(should fed: FingerprintedExternalDependency,
415+
whichIsSignificantlyNew isSignificantlyNew: Bool,
416+
beInvestigatedIn graph: ModuleDependencyGraph) {
417+
if isSignificantlyNew {
418+
self = .added
419+
return
420+
}
421+
if graph.hasFileChanged(of: fed.externalDependency) {
422+
self = .changed
423+
return
424+
}
425+
return nil
426+
}
427+
428+
public var description: String { rawValue }
429+
}
430+
}

Sources/SwiftDriver/IncrementalCompilation/IncrementalCompilationState.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -428,6 +428,20 @@ extension IncrementalCompilationState {
428428
func reportIncrementalCompilationHasBeenDisabled(_ why: String) {
429429
report("Incremental compilation has been disabled, \(why)")
430430
}
431+
432+
func reportInvalidated<Nodes: Sequence>(
433+
_ nodes: Nodes,
434+
by externalDependency: ExternalDependency,
435+
_ why: ExternalDependency.Why
436+
)
437+
where Nodes.Element == ModuleDependencyGraph.Node
438+
{
439+
let whyString = why.description.capitalized
440+
let depString = externalDependency.shortDescription
441+
for node in nodes {
442+
report("\(whyString): \(depString) -> \(node)")
443+
}
444+
}
431445
}
432446
}
433447

Sources/SwiftDriver/IncrementalCompilation/ModuleDependencyGraph.swift

Lines changed: 55 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,32 @@ extension ModuleDependencyGraph {
100100
}
101101
}
102102

103+
var shouldNewExternalDependenciesTriggerInvalidation: Bool {
104+
switch self {
105+
case .buildingWithoutAPrior:
106+
// Reading graph from a swiftdeps file,
107+
// so every incremental external dependency found will be new to the
108+
// graph. Don't invalidate just 'cause it's new.
109+
return false
110+
111+
case .buildingAfterEachCompilation:
112+
// Will be compiling every file, so no need to invalidate based on
113+
// found external dependencies.
114+
return false
115+
116+
// Reading a swiftdeps file after a compilation.
117+
// A new external dependency represents an addition.
118+
// So must invalidate based on it.
119+
case .updatingAfterCompilation:
120+
return true
121+
122+
case .updatingFromAPrior:
123+
// If the graph was read from priors,
124+
// then any new external dependency must also be an addition.
125+
return true
126+
}
127+
}
128+
103129
var isCompilingAllInputsNoMatterWhat: Bool {
104130
switch self {
105131
case .buildingAfterEachCompilation:
@@ -240,6 +266,7 @@ extension ModuleDependencyGraph {
240266
/// As an optimization, only return the nodes that have not been already traced, because the traced nodes
241267
/// will have already been used to schedule jobs to run.
242268
/*@_spi(Testing)*/ public func collectUntracedNodesUsing(
269+
_ why: ExternalDependency.Why,
243270
_ fingerprintedExternalDependency: FingerprintedExternalDependency
244271
) -> DirectlyInvalidatedNodeSet {
245272
// These nodes will depend on the *interface* of the external Decl.
@@ -252,10 +279,12 @@ extension ModuleDependencyGraph {
252279
let node = Node(key: key,
253280
fingerprint: fingerprintedExternalDependency.fingerprint,
254281
dependencySource: nil)
255-
return DirectlyInvalidatedNodeSet(
282+
let untracedUses = DirectlyInvalidatedNodeSet(
256283
nodeFinder
257284
.uses(of: node)
258285
.filter({ use in use.isUntraced }))
286+
info.reporter?.reportInvalidated(untracedUses, by: fingerprintedExternalDependency.externalDependency, why)
287+
return untracedUses
259288
}
260289

261290
/// Find all the inputs known to need recompilation as a consequence of reading a swiftdeps or swiftmodule
@@ -308,38 +337,32 @@ extension ModuleDependencyGraph {
308337
isPresentInTheGraph: Bool?)
309338
-> DirectlyInvalidatedNodeSet {
310339

340+
/// Compute this up front as an optimization.
311341
let isNewToTheGraph = isPresentInTheGraph != true && fingerprintedExternalDependencies.insert(fed).inserted
312342

313-
// If the graph already includes prior externals, then any new externals are changes
314-
// Short-circuit conjunction may avoid the modTime query
315-
let shouldTryToProcess = info.isCrossModuleIncrementalBuildEnabled &&
316-
(isNewToTheGraph || hasFileChanged(of: fed.externalDependency))
317-
318-
// Do this no matter what in order to integrate any incremental external dependencies.
319-
let invalidatedNodesFromIncrementalExternal = shouldTryToProcess
320-
? collectNodesInvalidatedByAttemptingToProcess(fed, info)
321-
: nil
343+
let whyIntegrateForClosure = ExternalDependency.Why(
344+
should: fed,
345+
whichIsNewToTheGraph: isNewToTheGraph,
346+
closeOverSwiftModulesIn: self)
322347

323-
if phase.isCompilingAllInputsNoMatterWhat {
324-
// going to compile every input anyway, less work for callers
325-
return DirectlyInvalidatedNodeSet()
348+
let invalidatedNodesFromIncrementalExternal = whyIntegrateForClosure.flatMap { why in
349+
collectNodesInvalidatedByAttemptingToProcess(why, fed)
326350
}
327351

328-
/// When building a graph from scratch, an unchanged but new-to-the-graph external dependendcy should be ignored.
329-
/// Otherwise, it represents an added Import
330-
let callerWantsTheseChanges = (phase.isUpdating && isNewToTheGraph) ||
331-
hasFileChanged(of: fed.externalDependency)
332-
333-
guard callerWantsTheseChanges else {
352+
guard let whyInvalidate = ExternalDependency.Why(
353+
shouldUsesOf: fed,
354+
whichIsNewToTheGraph: isNewToTheGraph,
355+
beInvalidatedIn: self)
356+
else {
334357
return DirectlyInvalidatedNodeSet()
335358
}
336359

337360
// If there was an error integrating the external dependency, or if it was not an incremental one,
338361
// return anything that uses that dependency.
339-
return invalidatedNodesFromIncrementalExternal ?? collectUntracedNodesUsing(fed)
362+
return invalidatedNodesFromIncrementalExternal ?? collectUntracedNodesUsing(whyInvalidate, fed)
340363
}
341364

342-
private func hasFileChanged(of externalDependency: ExternalDependency
365+
func hasFileChanged(of externalDependency: ExternalDependency
343366
) -> Bool {
344367
if let hasChanged = externalDependencyModTimeCache[externalDependency] {
345368
return hasChanged
@@ -356,14 +379,17 @@ extension ModuleDependencyGraph {
356379
/// Try to read and integrate an external dependency.
357380
/// Return nil if it's not incremental, or if an error occurs.
358381
private func collectNodesInvalidatedByAttemptingToProcess(
359-
_ fed: FingerprintedExternalDependency,
360-
_ info: IncrementalCompilationState.IncrementalDependencyAndInputSetup) -> DirectlyInvalidatedNodeSet? {
361-
fed.incrementalDependencySource?
362-
.read(in: info.fileSystem, reporter: info.reporter)
363-
.map { unserializedDepGraph in
364-
info.reporter?.report("Integrating changes from: \(fed.externalDependency)")
365-
return Integrator.integrate(from: unserializedDepGraph, into: self)
366-
}
382+
_ why: ExternalDependency.Why,
383+
_ fed: FingerprintedExternalDependency
384+
) -> DirectlyInvalidatedNodeSet? {
385+
guard let source = fed.incrementalDependencySource,
386+
let unserializedDepGraph = source.read(in: info.fileSystem, reporter: info.reporter)
387+
else {
388+
return nil
389+
}
390+
let invalidatedNodes = Integrator.integrate(from: unserializedDepGraph, into: self)
391+
info.reporter?.reportInvalidated(invalidatedNodes, by: fed.externalDependency, why)
392+
return invalidatedNodes
367393
}
368394
}
369395

Tests/SwiftDriverTests/ModuleDependencyGraphTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1014,7 +1014,7 @@ extension ModuleDependencyGraph {
10141014
on fingerprintedExternalDependency: FingerprintedExternalDependency
10151015
) -> [DependencySource] {
10161016
var foundSources = [DependencySource]()
1017-
for dependent in collectUntracedNodesUsing(fingerprintedExternalDependency) {
1017+
for dependent in collectUntracedNodesUsing(.testing, fingerprintedExternalDependency) {
10181018
let dependencySource = dependent.dependencySource!
10191019
foundSources.append(dependencySource)
10201020
// findSwiftDepsToRecompileWhenWholeSwiftDepChanges is reflexive

0 commit comments

Comments
 (0)