@@ -266,6 +266,7 @@ internal extension InterModuleDependencyGraph {
266266 /// between it and the root (source module being built by this driver
267267 /// instance) must also be re-built.
268268 func computeInvalidatedModuleDependencies( fileSystem: FileSystem ,
269+ forRebuild: Bool ,
269270 reporter: IncrementalCompilationState . Reporter ? = nil )
270271 throws -> Set < ModuleDependencyId > {
271272 let mainModuleInfo = mainModule
@@ -276,10 +277,13 @@ internal extension InterModuleDependencyGraph {
276277 for dependencyId in mainModuleInfo. directDependencies ?? [ ] {
277278 try outOfDateModuleScan ( from: dependencyId, visited: & visited,
278279 modulesRequiringRebuild: & modulesRequiringRebuild,
279- fileSystem: fileSystem, reporter: reporter)
280+ fileSystem: fileSystem, forRebuild: forRebuild,
281+ reporter: reporter)
280282 }
281283
282- reporter? . reportExplicitDependencyReBuildSet ( Array ( modulesRequiringRebuild) )
284+ if forRebuild {
285+ reporter? . reportExplicitDependencyReBuildSet ( Array ( modulesRequiringRebuild) )
286+ }
283287 return modulesRequiringRebuild
284288 }
285289
@@ -290,46 +294,124 @@ internal extension InterModuleDependencyGraph {
290294 visited: inout Set < ModuleDependencyId > ,
291295 modulesRequiringRebuild: inout Set < ModuleDependencyId > ,
292296 fileSystem: FileSystem ,
297+ forRebuild: Bool ,
293298 reporter: IncrementalCompilationState . Reporter ? = nil ) throws {
299+ let reportOutOfDate = { ( name: String , reason: String ) in
300+ if forRebuild {
301+ reporter? . reportExplicitDependencyWillBeReBuilt ( sourceModuleId. moduleNameForDiagnostic, reason: reason)
302+ } else {
303+ reporter? . reportPriorExplicitDependencyStale ( sourceModuleId. moduleNameForDiagnostic, reason: reason)
304+ }
305+ }
306+
294307 let sourceModuleInfo = try moduleInfo ( of: sourceModuleId)
295308 // Visit the module's dependencies
296309 var hasOutOfDateModuleDependency = false
297- var mostRecentlyUpdatedDependencyOutput : TimePoint = . zero
298310 for dependencyId in sourceModuleInfo. directDependencies ?? [ ] {
299311 // If we have not already visited this module, recurse.
300312 if !visited. contains ( dependencyId) {
301313 try outOfDateModuleScan ( from: dependencyId, visited: & visited,
302314 modulesRequiringRebuild: & modulesRequiringRebuild,
303- fileSystem: fileSystem, reporter: reporter)
315+ fileSystem: fileSystem, forRebuild: forRebuild,
316+ reporter: reporter)
304317 }
305318 // Even if we're not revisiting a dependency, we must check if it's already known to be out of date.
306319 hasOutOfDateModuleDependency = hasOutOfDateModuleDependency || modulesRequiringRebuild. contains ( dependencyId)
307-
308- // Keep track of dependencies' output file time stamp to determine if it is newer than the current module.
309- if let depOutputTimeStamp = try ? fileSystem. lastModificationTime ( for: VirtualPath . lookup ( moduleInfo ( of: dependencyId) . modulePath. path) ) ,
310- depOutputTimeStamp > mostRecentlyUpdatedDependencyOutput {
311- mostRecentlyUpdatedDependencyOutput = depOutputTimeStamp
312- }
313320 }
314321
315322 if hasOutOfDateModuleDependency {
316- reporter? . reportExplicitDependencyWillBeReBuilt ( sourceModuleId. moduleNameForDiagnostic, reason: " Invalidated by downstream dependency " )
317- modulesRequiringRebuild. insert ( sourceModuleId)
318- } else if try ! IncrementalCompilationState. IncrementalDependencyAndInputSetup. verifyModuleDependencyUpToDate ( moduleID: sourceModuleId, moduleInfo: sourceModuleInfo,
319- fileSystem: fileSystem, reporter: reporter) {
320- reporter? . reportExplicitDependencyWillBeReBuilt ( sourceModuleId. moduleNameForDiagnostic, reason: " Out-of-date " )
323+ reportOutOfDate ( sourceModuleId. moduleNameForDiagnostic, " Invalidated by downstream dependency " )
321324 modulesRequiringRebuild. insert ( sourceModuleId)
322- } else if let outputModTime = try ? fileSystem. lastModificationTime ( for: VirtualPath . lookup ( sourceModuleInfo. modulePath. path) ) ,
323- outputModTime < mostRecentlyUpdatedDependencyOutput {
324- // If a prior variant of this module dependnecy exists, and is older than any of its direct or transitive
325- // module dependency outputs, it must also be re-built.
326- reporter? . reportExplicitDependencyWillBeReBuilt ( sourceModuleId. moduleNameForDiagnostic, reason: " Has newer module dependency inputs " )
325+ } else if try ! verifyModuleDependencyUpToDate( moduleID: sourceModuleId, fileSystem: fileSystem, reporter: reporter) {
326+ reportOutOfDate ( sourceModuleId. moduleNameForDiagnostic, " Out-of-date " )
327327 modulesRequiringRebuild. insert ( sourceModuleId)
328328 }
329329
330330 // Now that we've determined if this module must be rebuilt, mark it as visited.
331331 visited. insert ( sourceModuleId)
332332 }
333+
334+ func verifyModuleDependencyUpToDate( moduleID: ModuleDependencyId ,
335+ fileSystem: FileSystem ,
336+ reporter: IncrementalCompilationState . Reporter ? ) throws -> Bool {
337+ let checkedModuleInfo = try moduleInfo ( of: moduleID)
338+ // Verify that the specified input exists and is older than the specified output
339+ let verifyInputOlderThanOutputModTime : ( String , VirtualPath , TimePoint ) -> Bool =
340+ { moduleName, inputPath, outputModTime in
341+ guard let inputModTime =
342+ try ? fileSystem. lastModificationTime ( for: inputPath) else {
343+ reporter? . report ( " Unable to 'stat' \( inputPath. description) " )
344+ return false
345+ }
346+ if inputModTime > outputModTime {
347+ reporter? . reportExplicitDependencyOutOfDate ( moduleName,
348+ inputPath: inputPath. description)
349+ return false
350+ }
351+ return true
352+ }
353+
354+ // Check if the output file exists
355+ guard let outputModTime = try ? fileSystem. lastModificationTime ( for: VirtualPath . lookup ( checkedModuleInfo. modulePath. path) ) else {
356+ reporter? . report ( " Module output not found: ' \( moduleID. moduleNameForDiagnostic) ' " )
357+ return false
358+ }
359+
360+ // Check if a dependency of this module has a newer output than this module
361+ for dependencyId in checkedModuleInfo. directDependencies ?? [ ] {
362+ let dependencyInfo = try moduleInfo ( of: dependencyId)
363+ if !verifyInputOlderThanOutputModTime( moduleID. moduleName,
364+ VirtualPath . lookup ( dependencyInfo. modulePath. path) ,
365+ outputModTime) {
366+ return false
367+ }
368+ }
369+
370+ // Check if any of the textual sources of this module are newer than this module
371+ switch checkedModuleInfo. details {
372+ case . swift( let swiftDetails) :
373+ if let moduleInterfacePath = swiftDetails. moduleInterfacePath {
374+ if !verifyInputOlderThanOutputModTime( moduleID. moduleName,
375+ VirtualPath . lookup ( moduleInterfacePath. path) ,
376+ outputModTime) {
377+ return false
378+ }
379+ }
380+ if let bridgingHeaderPath = swiftDetails. bridgingHeaderPath {
381+ if !verifyInputOlderThanOutputModTime( moduleID. moduleName,
382+ VirtualPath . lookup ( bridgingHeaderPath. path) ,
383+ outputModTime) {
384+ return false
385+ }
386+ }
387+ for bridgingSourceFile in swiftDetails. bridgingSourceFiles ?? [ ] {
388+ if !verifyInputOlderThanOutputModTime( moduleID. moduleName,
389+ VirtualPath . lookup ( bridgingSourceFile. path) ,
390+ outputModTime) {
391+ return false
392+ }
393+ }
394+ case . clang( _) :
395+ for inputSourceFile in checkedModuleInfo. sourceFiles ?? [ ] {
396+ if !verifyInputOlderThanOutputModTime( moduleID. moduleName,
397+ try VirtualPath ( path: inputSourceFile) ,
398+ outputModTime) {
399+ return false
400+ }
401+ }
402+ case . swiftPrebuiltExternal( _) :
403+ // TODO: We have to give-up here until we have a way to verify the timestamp of the binary module.
404+ // We can do better here by knowing if this module hasn't changed - which would allows us to not
405+ // invalidate any of the dependencies that depend on it.
406+ reporter? . report ( " Unable to verify binary module dependency up-to-date: \( moduleID. moduleNameForDiagnostic) " )
407+ return false ;
408+ case . swiftPlaceholder( _) :
409+ // TODO: This should never ever happen. Hard error?
410+ return false ;
411+ }
412+
413+ return true
414+ }
333415}
334416
335417internal extension InterModuleDependencyGraph {
0 commit comments