@@ -134,11 +134,23 @@ extension IncrementalCompilationState.FirstWaveComputer {
134
134
jobCreatingPch: jobCreatingPch)
135
135
136
136
// In the case where there are no compilation jobs to run on this build (no source-files were changed),
137
- // we can skip running `beforeCompiles` jobs if we also ensure that none of the `afterCompiles` jobs
138
- // have any dependencies on them.
139
- let skipAllJobs = batchedCompilationJobs. isEmpty ? !nonVerifyAfterCompileJobsDependOnBeforeCompileJobs( ) : false
140
- let beforeCompileJobs = skipAllJobs ? [ ] : jobsInPhases. beforeCompiles
141
- var skippedNonCompileJobs = skipAllJobs ? jobsInPhases. beforeCompiles : [ ]
137
+ // and the emit-module task does not need to be re-run, we can skip running `beforeCompiles` jobs if we
138
+ // also ensure that none of the `afterCompiles` jobs have any dependencies on them.
139
+ let skippingAllCompileJobs = batchedCompilationJobs. isEmpty
140
+ let skipEmitModuleJobs = try skippingAllCompileJobs && computeCanSkipEmitModuleTasks ( buildRecord)
141
+ let skipAllJobs = skippingAllCompileJobs && skipEmitModuleJobs && !nonVerifyAfterCompileJobsDependOnBeforeCompileJobs( )
142
+
143
+ let beforeCompileJobs : [ Job ]
144
+ var skippedNonCompileJobs : [ Job ] = [ ]
145
+ if skipAllJobs {
146
+ beforeCompileJobs = [ ]
147
+ skippedNonCompileJobs = jobsInPhases. beforeCompiles
148
+ } else if skipEmitModuleJobs {
149
+ beforeCompileJobs = jobsInPhases. beforeCompiles. filter { $0. kind != . emitModule }
150
+ skippedNonCompileJobs. append ( contentsOf: jobsInPhases. beforeCompiles. filter { $0. kind == . emitModule } )
151
+ } else {
152
+ beforeCompileJobs = jobsInPhases. beforeCompiles
153
+ }
142
154
143
155
// Schedule emitModule job together with verify module interface job.
144
156
let afterCompileJobs = jobsInPhases. afterCompiles. compactMap { job -> Job ? in
@@ -170,6 +182,27 @@ extension IncrementalCompilationState.FirstWaveComputer {
170
182
}
171
183
}
172
184
185
+ /// Figure out if the emit-module tasks are *not* mandatory. This functionality only runs if there are not actual
186
+ /// compilation tasks to be run in this build, for example on an emit-module-only build.
187
+ private func computeCanSkipEmitModuleTasks( _ buildRecord: BuildRecord ) throws -> Bool {
188
+ guard let emitModuleJob = jobsInPhases. beforeCompiles. first ( where: { $0. kind == . emitModule } ) else {
189
+ return false // Nothing to skip, so no special handling is required
190
+ }
191
+ // If a non-emit-module task exists in 'beforeCompiles', it may be another kind of
192
+ // changed dependency so we should re-run the module task as well
193
+ guard jobsInPhases. beforeCompiles. allSatisfy ( { $0. kind == . emitModule } ) else {
194
+ return false
195
+ }
196
+ // If any of the outputs do not exist, they must be re-computed
197
+ guard try emitModuleJob. outputs. allSatisfy ( { try fileSystem. exists ( $0. file) } ) else {
198
+ return false
199
+ }
200
+
201
+ // Ensure that no output is older than any of the inputs
202
+ let oldestOutputModTime : TimePoint = try emitModuleJob. outputs. map { try fileSystem. lastModificationTime ( for: $0. file) } . min ( ) ?? . distantPast
203
+ return try emitModuleJob. inputs. swiftSourceFiles. allSatisfy ( { try fileSystem. lastModificationTime ( for: $0. typedFile. file) < oldestOutputModTime } )
204
+ }
205
+
173
206
/// Figure out which compilation inputs are *not* mandatory at the start
174
207
private func computeInitiallySkippedCompilationInputs(
175
208
inputsInvalidatedByExternals: TransitivelyInvalidatedSwiftSourceFileSet ,
@@ -178,7 +211,7 @@ extension IncrementalCompilationState.FirstWaveComputer {
178
211
) -> Set < TypedVirtualPath > {
179
212
let allCompileJobs = jobsInPhases. compileJobs
180
213
// Input == source file
181
- let changedInputs = computeChangedInputs ( moduleDependencyGraph , buildRecord)
214
+ let changedInputs = computeChangedInputs ( buildRecord)
182
215
183
216
if let reporter = reporter {
184
217
for input in inputsInvalidatedByExternals {
@@ -274,7 +307,6 @@ extension IncrementalCompilationState.FirstWaveComputer {
274
307
275
308
// Find the inputs that have changed since last compilation, or were marked as needed a build
276
309
private func computeChangedInputs(
277
- _ moduleDependencyGraph: ModuleDependencyGraph ,
278
310
_ outOfDateBuildRecord: BuildRecord
279
311
) -> [ ChangedInput ] {
280
312
jobsInPhases. compileJobs. compactMap { job in
0 commit comments