@@ -148,12 +148,14 @@ extension IncrementalCompilationState.FirstWaveComputer {
148148 private func computeInvalidatedModuleDependencies( on moduleDependencyGraph: InterModuleDependencyGraph )
149149 throws -> Set < ModuleDependencyId > {
150150 let mainModuleInfo = moduleDependencyGraph. mainModule
151+ let reachabilityMap = try moduleDependencyGraph. computeTransitiveClosure ( )
151152 var modulesRequiringRebuild : Set < ModuleDependencyId > = [ ]
152- var visitedModules : Set < ModuleDependencyId > = [ ]
153+ var visited : Set < ModuleDependencyId > = [ ]
153154 // Scan from the main module's dependencies to avoid reporting
154155 // the main module itself in the results.
155156 for dependencyId in mainModuleInfo. directDependencies ?? [ ] {
156- try outOfDateModuleScan ( on: moduleDependencyGraph, from: dependencyId, visited: & visitedModules,
157+ try outOfDateModuleScan ( on: moduleDependencyGraph, from: dependencyId,
158+ reachabilityMap: reachabilityMap, visited: & visited,
157159 modulesRequiringRebuild: & modulesRequiringRebuild)
158160 }
159161
@@ -164,8 +166,12 @@ extension IncrementalCompilationState.FirstWaveComputer {
164166 /// Perform a postorder DFS to locate modules which are out-of-date with respect
165167 /// to their inputs. Upon encountering such a module, add it to the set of invalidated
166168 /// modules, along with the path from the root to this module.
169+ ///
170+ /// Returns the last-modification-time of the newest module dependency output file,
171+ /// direct or transitive.
167172 private func outOfDateModuleScan( on moduleDependencyGraph: InterModuleDependencyGraph ,
168173 from moduleId: ModuleDependencyId ,
174+ reachabilityMap: [ ModuleDependencyId : Set < ModuleDependencyId > ] ,
169175 visited: inout Set < ModuleDependencyId > ,
170176 modulesRequiringRebuild: inout Set < ModuleDependencyId > ) throws {
171177 let moduleInfo = try moduleDependencyGraph. moduleInfo ( of: moduleId)
@@ -175,20 +181,36 @@ extension IncrementalCompilationState.FirstWaveComputer {
175181 // If we have not already visited this module, recurse.
176182 if !visited. contains ( dependencyId) {
177183 try outOfDateModuleScan ( on: moduleDependencyGraph, from: dependencyId,
178- visited: & visited,
184+ reachabilityMap : reachabilityMap , visited: & visited,
179185 modulesRequiringRebuild: & modulesRequiringRebuild)
180186 }
181187 // Even if we're not revisiting a dependency, we must check if it's already known to be out of date.
182188 hasOutOfDateModuleDependency = hasOutOfDateModuleDependency || modulesRequiringRebuild. contains ( dependencyId)
183189 }
184190
185191 if hasOutOfDateModuleDependency {
186- reporter? . reportExplicitDependencyWillBeReBuilt ( moduleId. moduleNameForDiagnostic, reason: " Invalidated by downstream dependency " )
187- modulesRequiringRebuild. insert ( moduleId)
192+ reporter? . reportExplicitDependencyWillBeReBuilt ( moduleId. moduleNameForDiagnostic, reason: " Invalidated by downstream dependency " )
193+ modulesRequiringRebuild. insert ( moduleId)
188194 } else if try ! IncrementalCompilationState. IncrementalDependencyAndInputSetup. verifyModuleDependencyUpToDate ( moduleID: moduleId, moduleInfo: moduleInfo,
189195 fileSystem: fileSystem, reporter: reporter) {
190- reporter? . reportExplicitDependencyWillBeReBuilt ( moduleId. moduleNameForDiagnostic, reason: " Out-of-date " )
191- modulesRequiringRebuild. insert ( moduleId)
196+ reporter? . reportExplicitDependencyWillBeReBuilt ( moduleId. moduleNameForDiagnostic, reason: " Out-of-date " )
197+ modulesRequiringRebuild. insert ( moduleId)
198+ }
199+
200+ // If a prior variant of this module dependnecy exists, and is older than any of its direct or transitive
201+ // module dependency outputs, it must also be re-built
202+ if let outputModTime = try ? fileSystem. lastModificationTime ( for: VirtualPath . lookup ( moduleInfo. modulePath. path) ) {
203+ // FIXME: This needs caching for mod times, otherwise there's too much repeated `stat` traffic
204+ for depId in reachabilityMap [ moduleId] ?? [ ] {
205+ guard let depOutputTimeStamp = try ? fileSystem. lastModificationTime ( for: VirtualPath . lookup ( moduleDependencyGraph. moduleInfo ( of: depId) . modulePath. path) ) else {
206+ continue
207+ }
208+ if depOutputTimeStamp > outputModTime {
209+ reporter? . reportExplicitDependencyWillBeReBuilt ( moduleId. moduleNameForDiagnostic, reason: " Has newer module dependency inputs " )
210+ modulesRequiringRebuild. insert ( moduleId)
211+ break
212+ }
213+ }
192214 }
193215
194216 // Now that we've determined if this module must be rebuilt, mark it as visited.
0 commit comments