@@ -150,9 +150,7 @@ extension ModuleDependencyGraph {
150
150
func collectNodesInvalidatedByChangedOrAddedExternals( ) -> DirectlyInvalidatedNodeSet {
151
151
fingerprintedExternalDependencies. reduce ( into: DirectlyInvalidatedNodeSet ( ) ) {
152
152
invalidatedNodes, fed in
153
- invalidatedNodes. formUnion (
154
- self . collectNodesInvalidatedByProcessing ( fingerprintedExternalDependency: fed,
155
- isPresentInTheGraph: true ) )
153
+ invalidatedNodes. formUnion ( self . integrateExternal ( . known( fed) ) )
156
154
}
157
155
}
158
156
}
@@ -286,9 +284,9 @@ extension ModuleDependencyGraph {
286
284
/// Given an external dependency & its fingerprint, find any nodes directly using that dependency.
287
285
/// As an optimization, only return the nodes that have not been already traced, because the traced nodes
288
286
/// will have already been used to schedule jobs to run.
289
- /*@_spi(Testing)*/ public func collectUntracedNodesUsing (
290
- _ why : ExternalDependency . Why ,
291
- _ fingerprintedExternalDependency : FingerprintedExternalDependency
287
+ /*@_spi(Testing)*/ public func collectUntracedNodes (
288
+ from fingerprintedExternalDependency : FingerprintedExternalDependency ,
289
+ _ why : ExternalDependency . InvalidationReason
292
290
) -> DirectlyInvalidatedNodeSet {
293
291
// These nodes will depend on the *interface* of the external Decl.
294
292
let key = DependencyKey (
@@ -353,45 +351,91 @@ extension ModuleDependencyGraph {
353
351
}
354
352
}
355
353
356
- // MARK: - processing external dependencies
354
+ // MARK: - Integrating External Dependencies
355
+
357
356
extension ModuleDependencyGraph {
357
+ /// The kinds of external dependencies available to integrate.
358
+ enum ExternalIntegrand {
359
+ /// A `known` integrand is known to be present in the graph and requires
360
+ /// only a mod-time check to determine if it is up to date.
361
+ case known( FingerprintedExternalDependency )
362
+ /// An `unknown` integrand is not, up to this point, known to the dependency
363
+ /// graph. This models the addition of an import that is discovered during
364
+ /// the incremental build.
365
+ case unknown( FingerprintedExternalDependency )
366
+
367
+ var externalDependency : FingerprintedExternalDependency {
368
+ switch self {
369
+ case . known( let fed) : return fed
370
+ case . unknown( let fed) : return fed
371
+ }
372
+ }
358
373
359
- /// Process a possibly-fingerprinted external dependency by reading and integrating, if applicable.
360
- /// Return the nodes thus invalidated.
361
- /// But always integrate, in order to detect future changes.
362
- /// This function does not to the transitive closure; that is left to the callers
363
- func collectNodesInvalidatedByProcessing(
364
- fingerprintedExternalDependency fed: FingerprintedExternalDependency ,
365
- isPresentInTheGraph: Bool ? )
366
- -> DirectlyInvalidatedNodeSet {
367
-
368
- /// Compute this up front as an optimization.
369
- let isNewToTheGraph = isPresentInTheGraph != true && fingerprintedExternalDependencies. insert ( fed) . inserted
370
-
371
- let whyIntegrateForClosure = ExternalDependency . Why (
372
- should: fed,
373
- whichIsNewToTheGraph: isNewToTheGraph,
374
- closeOverSwiftModulesIn: self )
375
-
376
- let invalidatedNodesFromIncrementalExternal = whyIntegrateForClosure. flatMap { why in
377
- collectNodesInvalidatedByAttemptingToProcess ( why, fed)
374
+ var isKnown : Bool {
375
+ switch self {
376
+ case . known( _) : return true
377
+ case . unknown( _) : return false
378
+ }
378
379
}
380
+ }
379
381
380
- guard let whyInvalidate = ExternalDependency . Why (
381
- shouldUsesOf: fed,
382
- whichIsNewToTheGraph: isNewToTheGraph,
383
- beInvalidatedIn: self )
384
- else {
382
+ /// Collects the nodes invalidated by a change to the given external
383
+ /// dependency after integrating it into the dependency graph.
384
+ ///
385
+ /// This function does not to the transitive closure; that is left to the
386
+ /// callers.
387
+ ///
388
+ /// - Parameters:
389
+ /// - integrand: The external dependency to integrate.
390
+ /// - isKnown: If `true`, the caller is aware of this node and
391
+ /// integration should assume it is a known external.
392
+ /// If `false`, and the external has not been
393
+ /// integrated before, it is treated as a freshly-
394
+ /// added external dependency.
395
+ /// - Returns: The set of module dependency graph nodes invalidated by integration.
396
+ func integrateExternal(
397
+ _ integrand: ExternalIntegrand
398
+ ) -> DirectlyInvalidatedNodeSet {
399
+ guard let whyInvalidate = self . invalidationReason ( for: integrand) else {
385
400
return DirectlyInvalidatedNodeSet ( )
386
401
}
387
402
388
- // If there was an error integrating the external dependency, or if it was not an incremental one,
389
- // return anything that uses that dependency.
390
- return invalidatedNodesFromIncrementalExternal ?? collectUntracedNodesUsing ( whyInvalidate, fed)
403
+ if self . info. isCrossModuleIncrementalBuildEnabled {
404
+ if let ii = integrateIncrementalImport ( of: integrand. externalDependency, whyInvalidate) {
405
+ return ii
406
+ }
407
+ }
408
+
409
+ // If we're compiling everything anyways, there's no need to trace.
410
+ // FIXME: Seems like
411
+ // 1) We could set this flag a lot earlier in some cases
412
+ // 2) It should apply to incremental imports as well.
413
+ guard !self . phase. isCompilingAllInputsNoMatterWhat else {
414
+ return DirectlyInvalidatedNodeSet ( )
415
+ }
416
+ return collectUntracedNodes ( from: integrand. externalDependency, whyInvalidate)
391
417
}
392
418
393
- func hasFileChanged( of externalDependency: ExternalDependency
394
- ) -> Bool {
419
+ /// Figure out the reason to invalidate or process a dependency.
420
+ ///
421
+ /// Even if invalidation won't be reported to the caller, a new or added
422
+ /// incremental external dependencies may require integration in order to
423
+ /// transitively close them, (e.g. if an imported module imports a module).
424
+ private func invalidationReason(
425
+ for fed: ExternalIntegrand
426
+ ) -> ExternalDependency . InvalidationReason ? {
427
+ let isNewToTheGraph = !fed. isKnown && fingerprintedExternalDependencies. insert ( fed. externalDependency) . inserted
428
+ if self . phase. shouldNewExternalDependenciesTriggerInvalidation && isNewToTheGraph {
429
+ return . added
430
+ }
431
+
432
+ if self . hasFileChanged ( fed. externalDependency. externalDependency) {
433
+ return . changed
434
+ }
435
+ return nil
436
+ }
437
+
438
+ private func hasFileChanged( _ externalDependency: ExternalDependency ) -> Bool {
395
439
if let hasChanged = externalDependencyModTimeCache [ externalDependency] {
396
440
return hasChanged
397
441
}
@@ -406,12 +450,13 @@ extension ModuleDependencyGraph {
406
450
407
451
/// Try to read and integrate an external dependency.
408
452
/// Return nil if it's not incremental, or if an error occurs.
409
- private func collectNodesInvalidatedByAttemptingToProcess (
410
- _ why : ExternalDependency . Why ,
411
- _ fed : FingerprintedExternalDependency
453
+ private func integrateIncrementalImport (
454
+ of fed : FingerprintedExternalDependency ,
455
+ _ why : ExternalDependency . InvalidationReason
412
456
) -> DirectlyInvalidatedNodeSet ? {
413
- guard let source = fed. incrementalDependencySource,
414
- let unserializedDepGraph = source. read ( in: info. fileSystem, reporter: info. reporter)
457
+ guard
458
+ let source = fed. incrementalDependencySource,
459
+ let unserializedDepGraph = source. read ( in: info. fileSystem, reporter: info. reporter)
415
460
else {
416
461
return nil
417
462
}
0 commit comments