Skip to content

Commit a014323

Browse files
fortmarekclaude
andauthored
fix: ignore stale substep timestamps when computing target compilation duration (#249)
* fix: ignore stale substep timestamps when computing target compilation duration For incremental builds, Xcode reuses substep entries from previous build sessions for files that don't need recompilation. Those substeps keep their original (older) timestamps and are not flagged `wasFetchedFromCache`. The previous logic in `addCompilationTimesToTarget` took the latest substep `compilationEndTimestamp` regardless of whether it actually ran in this build session, which produced a negative `compilationDuration` whenever the chosen substep predated the target's `startTimestamp`. Filter out such stale substeps by requiring `compilationEndTimestamp >= parent.startTimestamp` in both `addCompilationTimesToTarget` and `addCompilationTimesToApp`. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Signed-off-by: fortmarek <marekfort@me.com> * fix: keep ParserBuildSteps.swift under the 400-line SwiftLint limit Inline the new filter clause across two lines instead of expanding it into a four-line block, so the file stays at 400 lines (the SwiftLint file_length limit). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Signed-off-by: fortmarek <marekfort@me.com> --------- Signed-off-by: fortmarek <marekfort@me.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 3285807 commit a014323

2 files changed

Lines changed: 29 additions & 2 deletions

File tree

Sources/XCLogParser/parser/ParserBuildSteps.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -374,7 +374,8 @@ public final class ParserBuildSteps {
374374
private func addCompilationTimesToTarget(_ target: BuildStep) -> BuildStep {
375375

376376
let lastCompilationStep = target.subSteps
377-
.filter { $0.isCompilationStep() && $0.fetchedFromCache == false }
377+
.filter { $0.isCompilationStep() && $0.fetchedFromCache == false &&
378+
$0.compilationEndTimestamp >= target.startTimestamp }
378379
.max { $0.compilationEndTimestamp < $1.compilationEndTimestamp }
379380
guard let lastStep = lastCompilationStep else {
380381
return target.with(newCompilationEndTimestamp: target.startTimestamp, andCompilationDuration: 0.0)
@@ -385,7 +386,8 @@ public final class ParserBuildSteps {
385386

386387
private func addCompilationTimesToApp(_ app: BuildStep) -> BuildStep {
387388
let lastCompilationStep = app.subSteps
388-
.filter { $0.compilationDuration > 0 && $0.fetchedFromCache == false }
389+
.filter { $0.compilationDuration > 0 && $0.fetchedFromCache == false &&
390+
$0.compilationEndTimestamp >= app.startTimestamp }
389391
.max { $0.compilationEndTimestamp < $1.compilationEndTimestamp }
390392
guard let lastStep = lastCompilationStep else {
391393
return app.with(newCompilationEndTimestamp: app.startTimestamp,

Tests/XCLogParserTests/ParserTests.swift

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,31 @@ note: use 'updatedDoSomething' instead\r doSomething()\r ^~~~~~~~~~~\r
346346
XCTAssertEqual(expectedCompilationDuration, targetStep.compilationDuration)
347347
}
348348

349+
func testParseTargetCompilationTimesIgnoresStaleSubsteps() {
350+
// For incremental builds, Xcode reuses substep entries from previous build sessions for
351+
// files that didn't need recompilation. Those substeps keep their original (older)
352+
// timestamps and are not flagged `wasFetchedFromCache`. The target's compilationDuration
353+
// must remain non-negative; substeps that ended before the target started belong to a
354+
// prior session and should be ignored.
355+
let now = Date().timeIntervalSince1970
356+
let staleCompileEnd = now - 1000
357+
let staleStep = makeFakeBuildStep(title: "Stale Compile",
358+
type: .detail,
359+
detailStepType: .cCompilation,
360+
startTimestamp: now - 1010,
361+
fetchedFromCache: false)
362+
.with(newCompilationEndTimestamp: staleCompileEnd, andCompilationDuration: 10)
363+
var targetStep = makeFakeBuildStep(title: "Build Target",
364+
type: .target,
365+
detailStepType: .none,
366+
startTimestamp: now,
367+
fetchedFromCache: false).with(subSteps: [staleStep])
368+
369+
targetStep = parser.addCompilationTimes(step: targetStep)
370+
XCTAssertEqual(targetStep.startTimestamp, targetStep.compilationEndTimestamp)
371+
XCTAssertEqual(0.0, targetStep.compilationDuration)
372+
}
373+
349374
func testParseAppCompilationTimes() {
350375
let expectedCompilationDuration = 50.0
351376
let now = Date().timeIntervalSince1970

0 commit comments

Comments
 (0)