Skip to content

Commit e44832f

Browse files
committed
fix: group untagged build sections under one target instead of many
In flattened xcodebuild logs, sections without an (in target ...) tag were appended to the root and reported as targets. Xcode 26 and later emit tens of thousands of such sections (explicit module scans), so the HTML report grew to 1.4GB and the target list and Slowest targets chart were wrong. Group them under a single Build system target.
1 parent d723980 commit e44832f

3 files changed

Lines changed: 78 additions & 13 deletions

File tree

‎Sources/XCLogParser/parser/IDEActivityLogSection+Parsing.swift‎

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -44,27 +44,18 @@ extension IDEActivityLogSection {
4444
// for the `(in target 'ABC' from project Project)` string
4545
let isFlatten = subSections.contains { $0.getTargetFromCommand() != nil }
4646
if isFlatten {
47-
let mainTarget = "$MainTarget"
4847
let targetsDictionary = subSections.reduce(
4948
[String: IDEActivityLogSection]()) { targets, subSection -> [String: IDEActivityLogSection] in
50-
// some substeps belong to the root project, we use a fixed name for them
51-
let targetName = subSection.getTargetFromCommand() ?? mainTarget
49+
// sections without a target (build description, module scans) are grouped as one
50+
let targetName = subSection.getTargetFromCommand() ?? Self.buildSystemTarget
5251
let target = getOrBuildTarget(targetName, in: targets, using: subSection)
5352
target.subSections.append(subSection)
5453
var updatedTargets = targets
5554
updatedTargets[targetName] = target
5655
return updatedTargets
5756
}
58-
let nonMainTargets = targetsDictionary.filter { (key: String, _) -> Bool in
59-
return key != mainTarget
60-
}
61-
var subSections = [IDEActivityLogSection]()
62-
if let mainTarget = targetsDictionary[mainTarget] {
63-
subSections.append(contentsOf: mainTarget.subSections)
64-
}
65-
subSections.append(contentsOf: nonMainTargets.values)
6657
let withTargets = self
67-
withTargets.subSections = subSections.sorted { lhs, rhs -> Bool in
58+
withTargets.subSections = targetsDictionary.values.sorted { lhs, rhs -> Bool in
6859
lhs.timeStartedRecording < rhs.timeStartedRecording
6960
}
7061
return withTargets
@@ -136,10 +127,12 @@ extension IDEActivityLogSection {
136127
return buildTargetSection(name, with: section)
137128
}
138129

130+
static let buildSystemTarget = "Build system"
131+
139132
private func buildTargetSection(_ name: String, with section: IDEActivityLogSection) -> IDEActivityLogSection {
140133
return IDEActivityLogSection(sectionType: 2,
141134
domainType: section.domainType,
142-
title: "Build target \(name)",
135+
title: name == Self.buildSystemTarget ? name : "Build target \(name)",
143136
signature: name,
144137
timeStartedRecording: section.timeStartedRecording,
145138
timeStoppedRecording: section.timeStoppedRecording,
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import XCTest
2+
@testable import XCLogParser
3+
4+
class GroupedByTargetTests: XCTestCase {
5+
6+
func testUntaggedSectionsAreGroupedUnderOneTarget() {
7+
let main = makeSection(title: "Build", subSections: [
8+
makeSection(title: "Create build description", start: 0),
9+
makeSection(title: "Compiling Clang module Foundation", start: 1),
10+
makeSection(title: "CompileC a.o", command: "CompileC a.o (in target 'Foo' from project 'P')", start: 2),
11+
makeSection(title: "Compiling Clang module UIKit", start: 3),
12+
makeSection(title: "CompileC b.o", command: "CompileC b.o (in target 'Bar' from project 'P')", start: 4),
13+
])
14+
15+
let grouped = main.groupedByTarget()
16+
17+
XCTAssertEqual(["Build system", "Build target Foo", "Build target Bar"],
18+
grouped.subSections.map { $0.title })
19+
let buildSystem = grouped.subSections[0]
20+
XCTAssertEqual(["Create build description",
21+
"Compiling Clang module Foundation",
22+
"Compiling Clang module UIKit"],
23+
buildSystem.subSections.map { $0.title })
24+
}
25+
26+
func testTargetsKeepTheirOrder() {
27+
let main = makeSection(title: "Build", subSections: [
28+
makeSection(title: "Ld Bar", command: "Ld Bar (in target 'Bar' from project 'P')", start: 2),
29+
makeSection(title: "CompileC a.o", command: "CompileC a.o (in target 'Foo' from project 'P')", start: 1),
30+
])
31+
32+
XCTAssertEqual(["Build target Foo", "Build target Bar"],
33+
main.groupedByTarget().subSections.map { $0.title })
34+
}
35+
36+
private func makeSection(title: String,
37+
command: String = "",
38+
start: Double = 0,
39+
subSections: [IDEActivityLogSection] = []) -> IDEActivityLogSection {
40+
return IDEActivityLogSection(sectionType: 1,
41+
domainType: "",
42+
title: title,
43+
signature: "",
44+
timeStartedRecording: start,
45+
timeStoppedRecording: start,
46+
subSections: subSections,
47+
text: "",
48+
messages: [],
49+
wasCancelled: false,
50+
isQuiet: false,
51+
wasFetchedFromCache: false,
52+
subtitle: "",
53+
location: DVTDocumentLocation(documentURLString: "", timestamp: 0.0),
54+
commandDetailDesc: command,
55+
uniqueIdentifier: "",
56+
localizedResultString: "",
57+
xcbuildSignature: "",
58+
attachments: [],
59+
unknown: 0)
60+
}
61+
}

‎Tests/XCLogParserTests/XCTestManifests.swift‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,16 @@ extension ChromeTracerOutputTests {
2828
]
2929
}
3030

31+
extension GroupedByTargetTests {
32+
// DO NOT MODIFY: This is autogenerated, use:
33+
// `swift test --generate-linuxmain`
34+
// to regenerate.
35+
static let __allTests__GroupedByTargetTests = [
36+
("testTargetsKeepTheirOrder", testTargetsKeepTheirOrder),
37+
("testUntaggedSectionsAreGroupedUnderOneTarget", testUntaggedSectionsAreGroupedUnderOneTarget),
38+
]
39+
}
40+
3141
extension IssuesReporterTests {
3242
// DO NOT MODIFY: This is autogenerated, use:
3343
// `swift test --generate-linuxmain`
@@ -146,6 +156,7 @@ public func __allTests() -> [XCTestCaseEntry] {
146156
return [
147157
testCase(ActivityParserTests.__allTests__ActivityParserTests),
148158
testCase(ChromeTracerOutputTests.__allTests__ChromeTracerOutputTests),
159+
testCase(GroupedByTargetTests.__allTests__GroupedByTargetTests),
149160
testCase(IssuesReporterTests.__allTests__IssuesReporterTests),
150161
testCase(LexRedactorTests.__allTests__LexRedactorTests),
151162
testCase(LexerTests.__allTests__LexerTests),

0 commit comments

Comments
 (0)