@@ -37,7 +37,7 @@ public final class IncrementalCompilationState {
37
37
@_spi ( Testing) public let moduleDependencyGraph : ModuleDependencyGraph
38
38
39
39
/// If non-null outputs information for `-driver-show-incremental` for input path
40
- private let reporter : Reporter ?
40
+ public let reporter : Reporter ?
41
41
42
42
/// All of the pre-compile or compilation job (groups) known to be required (i.e. in 1st wave).
43
43
/// Already batched, and in order of input files.
@@ -307,10 +307,62 @@ extension IncrementalCompilationState {
307
307
}
308
308
// MARK: - Scheduling post-compile jobs
309
309
extension IncrementalCompilationState {
310
- public func canSkipPostCompile( job: Job ) -> Bool {
311
- job. outputs. allSatisfy { output in
312
- let fileModTime = ( try ? fileSystem. lastModificationTime ( for: output. file) ) ?? . distantFuture
313
- return fileModTime <= buildEndTime}
310
+ /// Only used when no compilations have run; otherwise the caller assumes every post-compile
311
+ /// job is needed, and saves the cost of the filesystem accesses by not calling this function.
312
+ /// (For instance, if a build is cancelled in the merge-module phase, the compilations may be up-to-date
313
+ /// but the postcompile-jobs (e.g. link-edit) may still need to be run.
314
+ /// Since the use-case is rare, this function can afford to be expensive.
315
+ /// Unlike the check in `IncrementalStateComputer.computeChangedInputs`,
316
+ /// this function does not rely on build record information, which makes it more expensive but more robust.
317
+ public func canSkip( postCompileJob: Job ) -> Bool {
318
+ func report( skipping: Bool , _ details: String , _ file: TypedVirtualPath ? = nil ) {
319
+ reporter? . report (
320
+ " \( skipping ? " S " : " Not s " ) kipping job: \( postCompileJob. descriptionForLifecycle) ; \( details) " ,
321
+ file)
322
+ }
323
+
324
+ guard let ( oldestOutput, oldestOutputModTime) =
325
+ findOldestOutputForSkipping ( postCompileJob: postCompileJob)
326
+ else {
327
+ report ( skipping: false , " No outputs " )
328
+ return false
329
+ }
330
+ guard . distantPast < oldestOutputModTime
331
+ else {
332
+ report ( skipping: false , " Missing output " , oldestOutput)
333
+ return false
334
+ }
335
+ if let newerInput = findAnInputOf ( postCompileJob: postCompileJob,
336
+ newerThan: oldestOutputModTime) {
337
+ report ( skipping: false , " Input \( newerInput. file. basename) is newer than output " , oldestOutput)
338
+ return false
339
+ }
340
+ report ( skipping: true , " oldest output is current " , oldestOutput)
341
+ return true
342
+ }
343
+
344
+ private func findOldestOutputForSkipping( postCompileJob: Job ) -> ( TypedVirtualPath , Date ) ? {
345
+ var oldestOutputAndModTime : ( TypedVirtualPath , Date ) ? = nil
346
+ for output in postCompileJob. outputs {
347
+ guard let outputModTime = modTime ( output)
348
+ else {
349
+ oldestOutputAndModTime = ( output, . distantPast)
350
+ break
351
+ }
352
+ oldestOutputAndModTime = oldestOutputAndModTime. map {
353
+ $0. 1 < outputModTime ? $0 : ( output, outputModTime)
354
+ }
355
+ ?? ( output, outputModTime)
356
+ }
357
+ return oldestOutputAndModTime
358
+ }
359
+ private func findAnInputOf( postCompileJob: Job , newerThan outputModTime: Date ) -> TypedVirtualPath ? {
360
+ postCompileJob. inputs. first { input in
361
+ outputModTime < ( modTime ( input) ?? . distantFuture)
362
+ }
363
+ }
364
+ private func modTime( _ path: TypedVirtualPath ) -> Date ? {
365
+ try ? fileSystem. lastModificationTime ( for: path. file)
314
366
}
315
367
}
316
368
@@ -356,7 +408,7 @@ extension IncrementalCompilationState {
356
408
/// - message: The message to emit in the remark.
357
409
/// - path: If non-nil, the path of some file. If the output for an incremental job, will print out the
358
410
/// source and object files.
359
- func report( _ message: String , _ pathIfGiven: TypedVirtualPath ? ) {
411
+ public func report( _ message: String , _ pathIfGiven: TypedVirtualPath ? ) {
360
412
guard let path = pathIfGiven,
361
413
let outputFileMap = outputFileMap,
362
414
let input = path. type == . swift ? path. file : outputFileMap. getInput ( outputFile: path. file)
@@ -370,7 +422,7 @@ extension IncrementalCompilationState {
370
422
}
371
423
372
424
/// Entry point for a simple path, won't print the compile job, path could be anything.
373
- func report( _ message: String , _ path: VirtualPath ? ) {
425
+ public func report( _ message: String , _ path: VirtualPath ? ) {
374
426
guard let path = path
375
427
else {
376
428
report ( message)
@@ -381,7 +433,7 @@ extension IncrementalCompilationState {
381
433
}
382
434
383
435
/// Entry point if no path.
384
- func report( _ message: String ) {
436
+ public func report( _ message: String ) {
385
437
diagnosticEngine. emit ( . remark_incremental_compilation( because: message) )
386
438
}
387
439
0 commit comments