@@ -25,14 +25,16 @@ extension IncrementalCompilationState {
25
25
let fileSystem : FileSystem
26
26
let showJobLifecycle : Bool
27
27
let alwaysRebuildDependents : Bool
28
- let rebuildExplicitModuleDependencies : Bool
28
+ let interModuleDependencyGraph : InterModuleDependencyGraph ?
29
+ let explicitModuleDependenciesGuaranteedUpToDate : Bool
29
30
/// If non-null outputs information for `-driver-show-incremental` for input path
30
31
private let reporter : Reporter ?
31
32
32
33
@_spi ( Testing) public init (
33
34
initialState: IncrementalCompilationState . InitialStateForPlanning ,
34
35
jobsInPhases: JobsInPhases ,
35
36
driver: Driver ,
37
+ interModuleDependencyGraph: InterModuleDependencyGraph ? ,
36
38
reporter: Reporter ?
37
39
) {
38
40
self . moduleDependencyGraph = initialState. graph
@@ -44,8 +46,9 @@ extension IncrementalCompilationState {
44
46
self . showJobLifecycle = driver. showJobLifecycle
45
47
self . alwaysRebuildDependents = initialState. incrementalOptions. contains (
46
48
. alwaysRebuildDependents)
47
- self . rebuildExplicitModuleDependencies =
48
- initialState. maybeUpToDatePriorInterModuleDependencyGraph != nil ? false : true
49
+ self . interModuleDependencyGraph = interModuleDependencyGraph
50
+ self . explicitModuleDependenciesGuaranteedUpToDate =
51
+ initialState. upToDatePriorInterModuleDependencyGraph != nil ? true : false
49
52
self . reporter = reporter
50
53
}
51
54
@@ -118,11 +121,7 @@ extension IncrementalCompilationState.FirstWaveComputer {
118
121
: compileGroups [ input]
119
122
}
120
123
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 ( )
126
125
let batchedCompilationJobs = try batchJobFormer. formBatchedJobs (
127
126
mandatoryCompileGroupsInOrder. flatMap { $0. allJobs ( ) } ,
128
127
showJobLifecycle: showJobLifecycle)
@@ -136,6 +135,121 @@ extension IncrementalCompilationState.FirstWaveComputer {
136
135
mandatoryJobsInOrder: mandatoryJobsInOrder)
137
136
}
138
137
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
+
139
253
/// Determine if any of the jobs in the `afterCompiles` group depend on outputs produced by jobs in
140
254
/// `beforeCompiles` group, which are not also verification jobs.
141
255
private func nonVerifyAfterCompileJobsDependOnBeforeCompileJobs( ) -> Bool {
0 commit comments