@@ -148,12 +148,14 @@ extension IncrementalCompilationState.FirstWaveComputer {
148
148
private func computeInvalidatedModuleDependencies( on moduleDependencyGraph: InterModuleDependencyGraph )
149
149
throws -> Set < ModuleDependencyId > {
150
150
let mainModuleInfo = moduleDependencyGraph. mainModule
151
+ let reachabilityMap = try moduleDependencyGraph. computeTransitiveClosure ( )
151
152
var modulesRequiringRebuild : Set < ModuleDependencyId > = [ ]
152
- var visitedModules : Set < ModuleDependencyId > = [ ]
153
+ var visited : Set < ModuleDependencyId > = [ ]
153
154
// Scan from the main module's dependencies to avoid reporting
154
155
// the main module itself in the results.
155
156
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,
157
159
modulesRequiringRebuild: & modulesRequiringRebuild)
158
160
}
159
161
@@ -164,8 +166,12 @@ extension IncrementalCompilationState.FirstWaveComputer {
164
166
/// Perform a postorder DFS to locate modules which are out-of-date with respect
165
167
/// to their inputs. Upon encountering such a module, add it to the set of invalidated
166
168
/// 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.
167
172
private func outOfDateModuleScan( on moduleDependencyGraph: InterModuleDependencyGraph ,
168
173
from moduleId: ModuleDependencyId ,
174
+ reachabilityMap: [ ModuleDependencyId : Set < ModuleDependencyId > ] ,
169
175
visited: inout Set < ModuleDependencyId > ,
170
176
modulesRequiringRebuild: inout Set < ModuleDependencyId > ) throws {
171
177
let moduleInfo = try moduleDependencyGraph. moduleInfo ( of: moduleId)
@@ -175,20 +181,36 @@ extension IncrementalCompilationState.FirstWaveComputer {
175
181
// If we have not already visited this module, recurse.
176
182
if !visited. contains ( dependencyId) {
177
183
try outOfDateModuleScan ( on: moduleDependencyGraph, from: dependencyId,
178
- visited: & visited,
184
+ reachabilityMap : reachabilityMap , visited: & visited,
179
185
modulesRequiringRebuild: & modulesRequiringRebuild)
180
186
}
181
187
// Even if we're not revisiting a dependency, we must check if it's already known to be out of date.
182
188
hasOutOfDateModuleDependency = hasOutOfDateModuleDependency || modulesRequiringRebuild. contains ( dependencyId)
183
189
}
184
190
185
191
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)
188
194
} else if try ! IncrementalCompilationState. IncrementalDependencyAndInputSetup. verifyModuleDependencyUpToDate ( moduleID: moduleId, moduleInfo: moduleInfo,
189
195
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
+ }
192
214
}
193
215
194
216
// Now that we've determined if this module must be rebuilt, mark it as visited.
0 commit comments