@@ -25,14 +25,16 @@ extension IncrementalCompilationState {
2525 let fileSystem : FileSystem
2626 let showJobLifecycle : Bool
2727 let alwaysRebuildDependents : Bool
28- let rebuildExplicitModuleDependencies : Bool
28+ let interModuleDependencyGraph : InterModuleDependencyGraph ?
29+ let explicitModuleDependenciesGuaranteedUpToDate : Bool
2930 /// If non-null outputs information for `-driver-show-incremental` for input path
3031 private let reporter : Reporter ?
3132
3233 @_spi ( Testing) public init (
3334 initialState: IncrementalCompilationState . InitialStateForPlanning ,
3435 jobsInPhases: JobsInPhases ,
3536 driver: Driver ,
37+ interModuleDependencyGraph: InterModuleDependencyGraph ? ,
3638 reporter: Reporter ?
3739 ) {
3840 self . moduleDependencyGraph = initialState. graph
@@ -44,8 +46,9 @@ extension IncrementalCompilationState {
4446 self . showJobLifecycle = driver. showJobLifecycle
4547 self . alwaysRebuildDependents = initialState. incrementalOptions. contains (
4648 . alwaysRebuildDependents)
47- self . rebuildExplicitModuleDependencies =
48- initialState. maybeUpToDatePriorInterModuleDependencyGraph != nil ? false : true
49+ self . interModuleDependencyGraph = interModuleDependencyGraph
50+ self . explicitModuleDependenciesGuaranteedUpToDate =
51+ initialState. upToDatePriorInterModuleDependencyGraph != nil ? true : false
4952 self . reporter = reporter
5053 }
5154
@@ -118,11 +121,7 @@ extension IncrementalCompilationState.FirstWaveComputer {
118121 : compileGroups [ input]
119122 }
120123
121- // If module dependencies are known to be up-to-date, do not rebuild them
122- let mandatoryBeforeCompilesJobs = self . rebuildExplicitModuleDependencies ?
123- jobsInPhases. beforeCompiles :
124- jobsInPhases. beforeCompiles. filter { $0. kind != . generatePCM && $0. kind != . compileModuleFromInterface }
125-
124+ let mandatoryBeforeCompilesJobs = try computeMandatoryBeforeCompilesJobs ( )
126125 let batchedCompilationJobs = try batchJobFormer. formBatchedJobs (
127126 mandatoryCompileGroupsInOrder. flatMap { $0. allJobs ( ) } ,
128127 showJobLifecycle: showJobLifecycle)
@@ -136,6 +135,121 @@ extension IncrementalCompilationState.FirstWaveComputer {
136135 mandatoryJobsInOrder: mandatoryJobsInOrder)
137136 }
138137
138+ /// We must determine if any of the module dependencies require re-compilation
139+ /// Since we know that a prior dependency graph was not completely up-to-date,
140+ /// there must be at least *some* dependencies that require being re-built.
141+ ///
142+ /// If a dependency is deemed as requiring a re-build, then every module
143+ /// between it and the root (source module being built by this driver
144+ /// instance) must also be re-built.
145+ private func computeInvalidatedModuleDependencies( on moduleDependencyGraph: InterModuleDependencyGraph )
146+ throws -> Set < ModuleDependencyId > {
147+ let mainModuleInfo = moduleDependencyGraph. mainModule
148+ var modulesRequiringRebuild : Set < ModuleDependencyId > = [ ]
149+ var validatedModules : Set < ModuleDependencyId > = [ ]
150+ // Scan from the main module's dependencies to avoid reporting
151+ // the main module itself in the results.
152+ for dependencyId in mainModuleInfo. directDependencies ?? [ ] {
153+ try outOfDateModuleScan ( on: moduleDependencyGraph, from: dependencyId,
154+ pathSoFar: [ ] , visitedValidated: & validatedModules,
155+ modulesRequiringRebuild: & modulesRequiringRebuild)
156+ }
157+
158+ reporter? . reportExplicitDependencyReBuildSet ( Array ( modulesRequiringRebuild) )
159+ return modulesRequiringRebuild
160+ }
161+
162+ /// Perform a postorder DFS to locate modules which are out-of-date with respect
163+ /// to their inputs. Upon encountering such a module, add it to the set of invalidated
164+ /// modules, along with the path from the root to this module.
165+ private func outOfDateModuleScan( on moduleDependencyGraph: InterModuleDependencyGraph ,
166+ from moduleId: ModuleDependencyId ,
167+ pathSoFar: [ ModuleDependencyId ] ,
168+ visitedValidated: inout Set < ModuleDependencyId > ,
169+ modulesRequiringRebuild: inout Set < ModuleDependencyId > ) throws {
170+ let moduleInfo = try moduleDependencyGraph. moduleInfo ( of: moduleId)
171+ let isMainModule = moduleId == . swift( moduleDependencyGraph. mainModuleName)
172+
173+ // Routine to invalidate the path from root to this node
174+ let invalidatePath = { ( modulesRequiringRebuild: inout Set < ModuleDependencyId > ) in
175+ if let reporter {
176+ for pathModuleId in pathSoFar {
177+ if !modulesRequiringRebuild. contains ( pathModuleId) &&
178+ !isMainModule {
179+ let pathModuleInfo = try moduleDependencyGraph. moduleInfo ( of: pathModuleId)
180+ reporter. reportExplicitDependencyWillBeReBuilt ( pathModuleId. moduleNameForDiagnostic,
181+ reason: " Invalidated by downstream dependency " )
182+ }
183+ }
184+ }
185+ modulesRequiringRebuild. formUnion ( pathSoFar)
186+ }
187+
188+ // Routine to invalidate this node and the path that led to it
189+ let invalidateOutOfDate = { ( modulesRequiringRebuild: inout Set < ModuleDependencyId > ) in
190+ reporter? . reportExplicitDependencyWillBeReBuilt ( moduleId. moduleNameForDiagnostic, reason: " Out-of-date " )
191+ modulesRequiringRebuild. insert ( moduleId)
192+ try invalidatePath ( & modulesRequiringRebuild)
193+ }
194+
195+ // Visit the module's dependencies
196+ for dependencyId in moduleInfo. directDependencies ?? [ ] {
197+ if !visitedValidated. contains ( dependencyId) {
198+ let newPath = pathSoFar + [ moduleId]
199+ try outOfDateModuleScan ( on: moduleDependencyGraph, from: dependencyId, pathSoFar: newPath,
200+ visitedValidated: & visitedValidated,
201+ modulesRequiringRebuild: & modulesRequiringRebuild)
202+ }
203+ }
204+
205+ if modulesRequiringRebuild. contains ( moduleId) {
206+ try invalidatePath ( & modulesRequiringRebuild)
207+ } else if try ! IncrementalCompilationState. IncrementalDependencyAndInputSetup. verifyModuleDependencyUpToDate ( moduleID: moduleId, moduleInfo: moduleInfo,
208+ fileSystem: fileSystem, reporter: reporter) {
209+ try invalidateOutOfDate ( & modulesRequiringRebuild)
210+ } else {
211+ // Only if this module is known to be up-to-date with respect to its inputs
212+ // and dependencies do we mark it as visited. We may need to re-visit
213+ // out-of-date modules in order to collect all possible paths to them.
214+ visitedValidated. insert ( moduleId)
215+ }
216+ }
217+
218+ /// In an explicit module build, filter out dependency module pre-compilation tasks
219+ /// for modules up-to-date from a prior compile.
220+ private func computeMandatoryBeforeCompilesJobs( ) throws -> [ Job ] {
221+ // In an implicit module build, we have nothing to filter/compute here
222+ guard let moduleDependencyGraph = interModuleDependencyGraph else {
223+ return jobsInPhases. beforeCompiles
224+ }
225+
226+ // If a prior compile's dependency graph was fully up-to-date, we can skip
227+ // re-building all dependency modules.
228+ guard !self . explicitModuleDependenciesGuaranteedUpToDate else {
229+ return jobsInPhases. beforeCompiles. filter { $0. kind != . generatePCM &&
230+ $0. kind != . compileModuleFromInterface }
231+ }
232+
233+ // Determine which module pre-build jobs must be re-run
234+ let modulesRequiringReBuild =
235+ try computeInvalidatedModuleDependencies ( on: moduleDependencyGraph)
236+
237+ // Filter the `.generatePCM` and `.compileModuleFromInterface` jobs for
238+ // modules which do *not* need re-building.
239+ let mandatoryBeforeCompilesJobs = jobsInPhases. beforeCompiles. filter { job in
240+ switch job. kind {
241+ case . generatePCM:
242+ return modulesRequiringReBuild. contains ( . clang( job. moduleName) )
243+ case . compileModuleFromInterface:
244+ return modulesRequiringReBuild. contains ( . swift( job. moduleName) )
245+ default :
246+ return true
247+ }
248+ }
249+
250+ return mandatoryBeforeCompilesJobs
251+ }
252+
139253 /// Determine if any of the jobs in the `afterCompiles` group depend on outputs produced by jobs in
140254 /// `beforeCompiles` group, which are not also verification jobs.
141255 private func nonVerifyAfterCompileJobsDependOnBeforeCompileJobs( ) -> Bool {
0 commit comments