@@ -99,6 +99,32 @@ extension ModuleDependencyGraph {
9999 }
100100 }
101101
102+ var shouldNewExternalDependenciesTriggerInvalidation : Bool {
103+ switch self {
104+ case . buildingWithoutAPrior:
105+ // Reading graph from a swiftdeps file,
106+ // so every incremental external dependency found will be new to the
107+ // graph. Don't invalidate just 'cause it's new.
108+ return false
109+
110+ case . buildingAfterEachCompilation:
111+ // Will be compiling every file, so no need to invalidate based on
112+ // found external dependencies.
113+ return false
114+
115+ // Reading a swiftdeps file after a compilation.
116+ // A new external dependency represents an addition.
117+ // So must invalidate based on it.
118+ case . updatingAfterCompilation:
119+ return true
120+
121+ case . updatingFromAPrior:
122+ // If the graph was read from priors,
123+ // then any new external dependency must also be an addition.
124+ return true
125+ }
126+ }
127+
102128 var isCompilingAllInputsNoMatterWhat : Bool {
103129 switch self {
104130 case . buildingAfterEachCompilation:
@@ -239,6 +265,7 @@ extension ModuleDependencyGraph {
239265 /// As an optimization, only return the nodes that have not been already traced, because the traced nodes
240266 /// will have already been used to schedule jobs to run.
241267 /*@_spi(Testing)*/ public func collectUntracedNodesUsing(
268+ _ why: ExternalDependency . Why ,
242269 _ fingerprintedExternalDependency: FingerprintedExternalDependency
243270 ) -> DirectlyInvalidatedNodeSet {
244271 // These nodes will depend on the *interface* of the external Decl.
@@ -251,10 +278,12 @@ extension ModuleDependencyGraph {
251278 let node = Node ( key: key,
252279 fingerprint: fingerprintedExternalDependency. fingerprint,
253280 dependencySource: nil )
254- return DirectlyInvalidatedNodeSet (
281+ let untracedUses = DirectlyInvalidatedNodeSet (
255282 nodeFinder
256283 . uses ( of: node)
257284 . filter ( { use in use. isUntraced } ) )
285+ info. reporter? . reportInvalidated ( untracedUses, by: fingerprintedExternalDependency. externalDependency, why)
286+ return untracedUses
258287 }
259288
260289 /// Find all the inputs known to need recompilation as a consequence of reading a swiftdeps or swiftmodule
@@ -307,38 +336,32 @@ extension ModuleDependencyGraph {
307336 isPresentInTheGraph: Bool ? )
308337 -> DirectlyInvalidatedNodeSet {
309338
339+ /// Compute this up front as an optimization.
310340 let isNewToTheGraph = isPresentInTheGraph != true && fingerprintedExternalDependencies. insert ( fed) . inserted
311341
312- // If the graph already includes prior externals, then any new externals are changes
313- // Short-circuit conjunction may avoid the modTime query
314- let shouldTryToProcess = info. isCrossModuleIncrementalBuildEnabled &&
315- ( isNewToTheGraph || hasFileChanged ( of: fed. externalDependency) )
316-
317- // Do this no matter what in order to integrate any incremental external dependencies.
318- let invalidatedNodesFromIncrementalExternal = shouldTryToProcess
319- ? collectNodesInvalidatedByAttemptingToProcess ( fed, info)
320- : nil
342+ let whyIntegrateForClosure = ExternalDependency . Why (
343+ should: fed,
344+ whichIsNewToTheGraph: isNewToTheGraph,
345+ closeOverSwiftModulesIn: self )
321346
322- if phase. isCompilingAllInputsNoMatterWhat {
323- // going to compile every input anyway, less work for callers
324- return DirectlyInvalidatedNodeSet ( )
347+ let invalidatedNodesFromIncrementalExternal = whyIntegrateForClosure. flatMap { why in
348+ collectNodesInvalidatedByAttemptingToProcess ( why, fed)
325349 }
326350
327- /// When building a graph from scratch, an unchanged but new-to-the-graph external dependendcy should be ignored.
328- /// Otherwise, it represents an added Import
329- let callerWantsTheseChanges = ( phase. isUpdating && isNewToTheGraph) ||
330- hasFileChanged ( of: fed. externalDependency)
331-
332- guard callerWantsTheseChanges else {
351+ guard let whyInvalidate = ExternalDependency . Why (
352+ shouldUsesOf: fed,
353+ whichIsNewToTheGraph: isNewToTheGraph,
354+ beInvalidatedIn: self )
355+ else {
333356 return DirectlyInvalidatedNodeSet ( )
334357 }
335358
336359 // If there was an error integrating the external dependency, or if it was not an incremental one,
337360 // return anything that uses that dependency.
338- return invalidatedNodesFromIncrementalExternal ?? collectUntracedNodesUsing ( fed)
361+ return invalidatedNodesFromIncrementalExternal ?? collectUntracedNodesUsing ( whyInvalidate , fed)
339362 }
340363
341- private func hasFileChanged( of externalDependency: ExternalDependency
364+ func hasFileChanged( of externalDependency: ExternalDependency
342365 ) -> Bool {
343366 if let hasChanged = externalDependencyModTimeCache [ externalDependency] {
344367 return hasChanged
@@ -355,14 +378,17 @@ extension ModuleDependencyGraph {
355378 /// Try to read and integrate an external dependency.
356379 /// Return nil if it's not incremental, or if an error occurs.
357380 private func collectNodesInvalidatedByAttemptingToProcess(
358- _ fed: FingerprintedExternalDependency ,
359- _ info: IncrementalCompilationState . IncrementalDependencyAndInputSetup ) -> DirectlyInvalidatedNodeSet ? {
360- fed. incrementalDependencySource?
361- . read ( in: info. fileSystem, reporter: info. reporter)
362- . map { unserializedDepGraph in
363- info. reporter? . report ( " Integrating changes from: \( fed. externalDependency) " )
364- return Integrator . integrate ( from: unserializedDepGraph, into: self )
365- }
381+ _ why: ExternalDependency . Why ,
382+ _ fed: FingerprintedExternalDependency
383+ ) -> DirectlyInvalidatedNodeSet ? {
384+ guard let source = fed. incrementalDependencySource,
385+ let unserializedDepGraph = source. read ( in: info. fileSystem, reporter: info. reporter)
386+ else {
387+ return nil
388+ }
389+ let invalidatedNodes = Integrator . integrate ( from: unserializedDepGraph, into: self )
390+ info. reporter? . reportInvalidated ( invalidatedNodes, by: fed. externalDependency, why)
391+ return invalidatedNodes
366392 }
367393}
368394
0 commit comments