@@ -260,6 +260,7 @@ internal extension InterModuleDependencyGraph {
260260 /// between it and the root (source module being built by this driver
261261 /// instance) must also be re-built.
262262 func computeInvalidatedModuleDependencies( fileSystem: FileSystem ,
263+ forRebuild: Bool ,
263264 reporter: IncrementalCompilationState . Reporter ? = nil )
264265 throws -> Set < ModuleDependencyId > {
265266 let mainModuleInfo = mainModule
@@ -270,10 +271,13 @@ internal extension InterModuleDependencyGraph {
270271 for dependencyId in mainModuleInfo. directDependencies ?? [ ] {
271272 try outOfDateModuleScan ( from: dependencyId, visited: & visited,
272273 modulesRequiringRebuild: & modulesRequiringRebuild,
273- fileSystem: fileSystem, reporter: reporter)
274+ fileSystem: fileSystem, forRebuild: forRebuild,
275+ reporter: reporter)
274276 }
275277
276- reporter? . reportExplicitDependencyReBuildSet ( Array ( modulesRequiringRebuild) )
278+ if forRebuild {
279+ reporter? . reportExplicitDependencyReBuildSet ( Array ( modulesRequiringRebuild) )
280+ }
277281 return modulesRequiringRebuild
278282 }
279283
@@ -284,46 +288,124 @@ internal extension InterModuleDependencyGraph {
284288 visited: inout Set < ModuleDependencyId > ,
285289 modulesRequiringRebuild: inout Set < ModuleDependencyId > ,
286290 fileSystem: FileSystem ,
291+ forRebuild: Bool ,
287292 reporter: IncrementalCompilationState . Reporter ? = nil ) throws {
293+ let reportOutOfDate = { ( name: String , reason: String ) in
294+ if forRebuild {
295+ reporter? . reportExplicitDependencyWillBeReBuilt ( sourceModuleId. moduleNameForDiagnostic, reason: reason)
296+ } else {
297+ reporter? . reportPriorExplicitDependencyStale ( sourceModuleId. moduleNameForDiagnostic, reason: reason)
298+ }
299+ }
300+
288301 let sourceModuleInfo = try moduleInfo ( of: sourceModuleId)
289302 // Visit the module's dependencies
290303 var hasOutOfDateModuleDependency = false
291- var mostRecentlyUpdatedDependencyOutput : TimePoint = . zero
292304 for dependencyId in sourceModuleInfo. directDependencies ?? [ ] {
293305 // If we have not already visited this module, recurse.
294306 if !visited. contains ( dependencyId) {
295307 try outOfDateModuleScan ( from: dependencyId, visited: & visited,
296308 modulesRequiringRebuild: & modulesRequiringRebuild,
297- fileSystem: fileSystem, reporter: reporter)
309+ fileSystem: fileSystem, forRebuild: forRebuild,
310+ reporter: reporter)
298311 }
299312 // Even if we're not revisiting a dependency, we must check if it's already known to be out of date.
300313 hasOutOfDateModuleDependency = hasOutOfDateModuleDependency || modulesRequiringRebuild. contains ( dependencyId)
301-
302- // Keep track of dependencies' output file time stamp to determine if it is newer than the current module.
303- if let depOutputTimeStamp = try ? fileSystem. lastModificationTime ( for: VirtualPath . lookup ( moduleInfo ( of: dependencyId) . modulePath. path) ) ,
304- depOutputTimeStamp > mostRecentlyUpdatedDependencyOutput {
305- mostRecentlyUpdatedDependencyOutput = depOutputTimeStamp
306- }
307314 }
308315
309316 if hasOutOfDateModuleDependency {
310- reporter? . reportExplicitDependencyWillBeReBuilt ( sourceModuleId. moduleNameForDiagnostic, reason: " Invalidated by downstream dependency " )
311- modulesRequiringRebuild. insert ( sourceModuleId)
312- } else if try ! IncrementalCompilationState. IncrementalDependencyAndInputSetup. verifyModuleDependencyUpToDate ( moduleID: sourceModuleId, moduleInfo: sourceModuleInfo,
313- fileSystem: fileSystem, reporter: reporter) {
314- reporter? . reportExplicitDependencyWillBeReBuilt ( sourceModuleId. moduleNameForDiagnostic, reason: " Out-of-date " )
317+ reportOutOfDate ( sourceModuleId. moduleNameForDiagnostic, " Invalidated by downstream dependency " )
315318 modulesRequiringRebuild. insert ( sourceModuleId)
316- } else if let outputModTime = try ? fileSystem. lastModificationTime ( for: VirtualPath . lookup ( sourceModuleInfo. modulePath. path) ) ,
317- outputModTime < mostRecentlyUpdatedDependencyOutput {
318- // If a prior variant of this module dependnecy exists, and is older than any of its direct or transitive
319- // module dependency outputs, it must also be re-built.
320- reporter? . reportExplicitDependencyWillBeReBuilt ( sourceModuleId. moduleNameForDiagnostic, reason: " Has newer module dependency inputs " )
319+ } else if try ! verifyModuleDependencyUpToDate( moduleID: sourceModuleId, fileSystem: fileSystem, reporter: reporter) {
320+ reportOutOfDate ( sourceModuleId. moduleNameForDiagnostic, " Out-of-date " )
321321 modulesRequiringRebuild. insert ( sourceModuleId)
322322 }
323323
324324 // Now that we've determined if this module must be rebuilt, mark it as visited.
325325 visited. insert ( sourceModuleId)
326326 }
327+
328+ func verifyModuleDependencyUpToDate( moduleID: ModuleDependencyId ,
329+ fileSystem: FileSystem ,
330+ reporter: IncrementalCompilationState . Reporter ? ) throws -> Bool {
331+ let checkedModuleInfo = try moduleInfo ( of: moduleID)
332+ // Verify that the specified input exists and is older than the specified output
333+ let verifyInputOlderThanOutputModTime : ( String , VirtualPath , TimePoint ) -> Bool =
334+ { moduleName, inputPath, outputModTime in
335+ guard let inputModTime =
336+ try ? fileSystem. lastModificationTime ( for: inputPath) else {
337+ reporter? . report ( " Unable to 'stat' \( inputPath. description) " )
338+ return false
339+ }
340+ if inputModTime > outputModTime {
341+ reporter? . reportExplicitDependencyOutOfDate ( moduleName,
342+ inputPath: inputPath. description)
343+ return false
344+ }
345+ return true
346+ }
347+
348+ // Check if the output file exists
349+ guard let outputModTime = try ? fileSystem. lastModificationTime ( for: VirtualPath . lookup ( checkedModuleInfo. modulePath. path) ) else {
350+ reporter? . report ( " Module output not found: ' \( moduleID. moduleNameForDiagnostic) ' " )
351+ return false
352+ }
353+
354+ // Check if a dependency of this module has a newer output than this module
355+ for dependencyId in checkedModuleInfo. directDependencies ?? [ ] {
356+ let dependencyInfo = try moduleInfo ( of: dependencyId)
357+ if !verifyInputOlderThanOutputModTime( moduleID. moduleName,
358+ VirtualPath . lookup ( dependencyInfo. modulePath. path) ,
359+ outputModTime) {
360+ return false
361+ }
362+ }
363+
364+ // Check if any of the textual sources of this module are newer than this module
365+ switch checkedModuleInfo. details {
366+ case . swift( let swiftDetails) :
367+ if let moduleInterfacePath = swiftDetails. moduleInterfacePath {
368+ if !verifyInputOlderThanOutputModTime( moduleID. moduleName,
369+ VirtualPath . lookup ( moduleInterfacePath. path) ,
370+ outputModTime) {
371+ return false
372+ }
373+ }
374+ if let bridgingHeaderPath = swiftDetails. bridgingHeaderPath {
375+ if !verifyInputOlderThanOutputModTime( moduleID. moduleName,
376+ VirtualPath . lookup ( bridgingHeaderPath. path) ,
377+ outputModTime) {
378+ return false
379+ }
380+ }
381+ for bridgingSourceFile in swiftDetails. bridgingSourceFiles ?? [ ] {
382+ if !verifyInputOlderThanOutputModTime( moduleID. moduleName,
383+ VirtualPath . lookup ( bridgingSourceFile. path) ,
384+ outputModTime) {
385+ return false
386+ }
387+ }
388+ case . clang( _) :
389+ for inputSourceFile in checkedModuleInfo. sourceFiles ?? [ ] {
390+ if !verifyInputOlderThanOutputModTime( moduleID. moduleName,
391+ try VirtualPath ( path: inputSourceFile) ,
392+ outputModTime) {
393+ return false
394+ }
395+ }
396+ case . swiftPrebuiltExternal( _) :
397+ // TODO: We have to give-up here until we have a way to verify the timestamp of the binary module.
398+ // We can do better here by knowing if this module hasn't changed - which would allows us to not
399+ // invalidate any of the dependencies that depend on it.
400+ reporter? . report ( " Unable to verify binary module dependency up-to-date: \( moduleID. moduleNameForDiagnostic) " )
401+ return false ;
402+ case . swiftPlaceholder( _) :
403+ // TODO: This should never ever happen. Hard error?
404+ return false ;
405+ }
406+
407+ return true
408+ }
327409}
328410
329411internal extension InterModuleDependencyGraph {
0 commit comments