Skip to content

Commit 2776756

Browse files
authored
Merge pull request #48 from spotify/swift_func_improv
Speed up parsing of swift function times and parse swift whole modules
2 parents a5d90de + b4626bc commit 2776756

File tree

9 files changed

+410
-25
lines changed

9 files changed

+410
-25
lines changed

Sources/XCLogParser/commands/Version.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,6 @@ import Foundation
2121

2222
public struct Version {
2323

24-
public static let current = "0.1.9"
24+
public static let current = "0.2.0"
2525

2626
}
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
// Copyright (c) 2019 Spotify AB.
2+
//
3+
// Licensed to the Apache Software Foundation (ASF) under one
4+
// or more contributor license agreements. See the NOTICE file
5+
// distributed with this work for additional information
6+
// regarding copyright ownership. The ASF licenses this file
7+
// to you under the Apache License, Version 2.0 (the
8+
// "License"); you may not use this file except in compliance
9+
// with the License. You may obtain a copy of the License at
10+
//
11+
// http://www.apache.org/licenses/LICENSE-2.0
12+
//
13+
// Unless required by applicable law or agreed to in writing,
14+
// software distributed under the License is distributed on an
15+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16+
// KIND, either express or implied. See the License for the
17+
// specific language governing permissions and limitations
18+
// under the License.
19+
20+
import Foundation
21+
22+
extension BuildStep {
23+
24+
func with(documentURL newDocumentURL: String) -> BuildStep {
25+
return BuildStep(type: type,
26+
machineName: machineName,
27+
buildIdentifier: buildIdentifier,
28+
identifier: identifier,
29+
parentIdentifier: parentIdentifier,
30+
domain: domain,
31+
title: title,
32+
signature: signature,
33+
startDate: startDate,
34+
endDate: endDate,
35+
startTimestamp: startTimestamp,
36+
endTimestamp: endTimestamp,
37+
duration: duration,
38+
detailStepType: detailStepType,
39+
buildStatus: buildStatus,
40+
schema: schema,
41+
subSteps: subSteps,
42+
warningCount: warningCount,
43+
errorCount: errorCount,
44+
architecture: architecture,
45+
documentURL: newDocumentURL,
46+
warnings: warnings,
47+
errors: errors,
48+
notes: notes,
49+
swiftFunctionTimes: swiftFunctionTimes)
50+
}
51+
52+
func with(title newTitle: String) -> BuildStep {
53+
return BuildStep(type: type,
54+
machineName: machineName,
55+
buildIdentifier: buildIdentifier,
56+
identifier: identifier,
57+
parentIdentifier: parentIdentifier,
58+
domain: domain,
59+
title: newTitle,
60+
signature: signature,
61+
startDate: startDate,
62+
endDate: endDate,
63+
startTimestamp: startTimestamp,
64+
endTimestamp: endTimestamp,
65+
duration: duration,
66+
detailStepType: detailStepType,
67+
buildStatus: buildStatus,
68+
schema: schema,
69+
subSteps: subSteps,
70+
warningCount: warningCount,
71+
errorCount: errorCount,
72+
architecture: architecture,
73+
documentURL: documentURL,
74+
warnings: warnings,
75+
errors: errors,
76+
notes: notes,
77+
swiftFunctionTimes: swiftFunctionTimes)
78+
}
79+
80+
func with(signature newSignature: String) -> BuildStep {
81+
return BuildStep(type: type,
82+
machineName: machineName,
83+
buildIdentifier: buildIdentifier,
84+
identifier: identifier,
85+
parentIdentifier: parentIdentifier,
86+
domain: domain,
87+
title: title,
88+
signature: newSignature,
89+
startDate: startDate,
90+
endDate: endDate,
91+
startTimestamp: startTimestamp,
92+
endTimestamp: endTimestamp,
93+
duration: duration,
94+
detailStepType: detailStepType,
95+
buildStatus: buildStatus,
96+
schema: schema,
97+
subSteps: subSteps,
98+
warningCount: warningCount,
99+
errorCount: errorCount,
100+
architecture: architecture,
101+
documentURL: documentURL,
102+
warnings: warnings,
103+
errors: errors,
104+
notes: notes,
105+
swiftFunctionTimes: swiftFunctionTimes)
106+
}
107+
108+
func withFilteredNotices() -> BuildStep {
109+
let filteredNotes = filterNotices(notes)
110+
let filteredWarnings = filterNotices(warnings)
111+
let filtereredErrors = filterNotices(errors)
112+
return BuildStep(type: type,
113+
machineName: machineName,
114+
buildIdentifier: buildIdentifier,
115+
identifier: identifier,
116+
parentIdentifier: parentIdentifier,
117+
domain: domain,
118+
title: title,
119+
signature: signature,
120+
startDate: startDate,
121+
endDate: endDate,
122+
startTimestamp: startTimestamp,
123+
endTimestamp: endTimestamp,
124+
duration: duration,
125+
detailStepType: detailStepType,
126+
buildStatus: buildStatus,
127+
schema: schema,
128+
subSteps: subSteps,
129+
warningCount: filteredWarnings?.count ?? 0,
130+
errorCount: filtereredErrors?.count ?? 0,
131+
architecture: architecture,
132+
documentURL: documentURL,
133+
warnings: filteredWarnings,
134+
errors: filtereredErrors,
135+
notes: filteredNotes,
136+
swiftFunctionTimes: swiftFunctionTimes)
137+
}
138+
139+
func with(subSteps newSubSteps: [BuildStep]) -> BuildStep {
140+
return BuildStep(type: type,
141+
machineName: machineName,
142+
buildIdentifier: buildIdentifier,
143+
identifier: identifier,
144+
parentIdentifier: parentIdentifier,
145+
domain: domain,
146+
title: title,
147+
signature: signature,
148+
startDate: startDate,
149+
endDate: endDate,
150+
startTimestamp: startTimestamp,
151+
endTimestamp: endTimestamp,
152+
duration: duration,
153+
detailStepType: detailStepType,
154+
buildStatus: buildStatus,
155+
schema: schema,
156+
subSteps: newSubSteps,
157+
warningCount: warningCount,
158+
errorCount: errorCount,
159+
architecture: architecture,
160+
documentURL: documentURL,
161+
warnings: warnings,
162+
errors: errors,
163+
notes: notes,
164+
swiftFunctionTimes: swiftFunctionTimes)
165+
}
166+
167+
private func filterNotices(_ notices: [Notice]?) -> [Notice]? {
168+
guard let notices = notices else {
169+
return nil
170+
}
171+
return notices.filter { $0.documentURL.isEmpty || $0.documentURL == documentURL }
172+
}
173+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// Copyright (c) 2019 Spotify AB.
2+
//
3+
// Licensed to the Apache Software Foundation (ASF) under one
4+
// or more contributor license agreements. See the NOTICE file
5+
// distributed with this work for additional information
6+
// regarding copyright ownership. The ASF licenses this file
7+
// to you under the Apache License, Version 2.0 (the
8+
// "License"); you may not use this file except in compliance
9+
// with the License. You may obtain a copy of the License at
10+
//
11+
// http://www.apache.org/licenses/LICENSE-2.0
12+
//
13+
// Unless required by applicable law or agreed to in writing,
14+
// software distributed under the License is distributed on an
15+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16+
// KIND, either express or implied. See the License for the
17+
// specific language governing permissions and limitations
18+
// under the License.
19+
20+
import Foundation
21+
22+
public extension BuildStep {
23+
24+
/// Flattens a group of swift compilations steps.
25+
///
26+
/// When a Swift module is compiled with `whole module` option
27+
/// The paresed log looks like:
28+
/// - CompileSwiftTarget
29+
/// - CompileSwift
30+
/// - CompileSwift file1.swift
31+
/// - CompileSwift file2.swift
32+
/// This tasks removes the intermediate CompileSwift step and moves the substeps
33+
/// to the root:
34+
/// - CompileSwiftTarget
35+
/// - CompileSwift file1.swift
36+
/// - CompileSwift file2.swift
37+
/// - Returns: The build step with its swift substeps at the root level, and intermediate CompileSwift step removed.
38+
func moveSwiftStepsToRoot() -> BuildStep {
39+
var updatedSubSteps = subSteps
40+
for (index, subStep) in subSteps.enumerated() {
41+
if subStep.detailStepType == .swiftCompilation && subStep.subSteps.count > 0 {
42+
updatedSubSteps.remove(at: index)
43+
updatedSubSteps.append(contentsOf: subStep.subSteps)
44+
}
45+
}
46+
return with(subSteps: updatedSubSteps)
47+
}
48+
49+
}

Sources/XCLogParser/parser/IDEActivityLogSection+Parsing.swift

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,22 @@
1+
// Copyright (c) 2019 Spotify AB.
2+
//
3+
// Licensed to the Apache Software Foundation (ASF) under one
4+
// or more contributor license agreements. See the NOTICE file
5+
// distributed with this work for additional information
6+
// regarding copyright ownership. The ASF licenses this file
7+
// to you under the Apache License, Version 2.0 (the
8+
// "License"); you may not use this file except in compliance
9+
// with the License. You may obtain a copy of the License at
10+
//
11+
// http://www.apache.org/licenses/LICENSE-2.0
12+
//
13+
// Unless required by applicable law or agreed to in writing,
14+
// software distributed under the License is distributed on an
15+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16+
// KIND, either express or implied. See the License for the
17+
// specific language governing permissions and limitations
18+
// under the License.
19+
120
import Foundation
221

322
extension IDEActivityLogSection {
@@ -55,6 +74,34 @@ extension IDEActivityLogSection {
5574
}
5675
}
5776

77+
/// Parses the swift files compiled in a module when `whole module` is used
78+
///
79+
/// - Parameter buildStep: the `BuildStep` that has the information about the module
80+
/// - Returns: An array of `BuildStep` with the data of each individual Swift file
81+
/// including the warnings and errors generated by its compilation.
82+
public func getSwiftIndividualSteps(buildStep: BuildStep) -> [BuildStep]? {
83+
let pattern = #"^CompileSwift\s\w+\s\w+\s.+\.swift\s"#
84+
guard commandDetailDesc.range(of: pattern, options: .regularExpression) == nil else {
85+
return nil
86+
}
87+
88+
let swiftFilePattern = #"\s([^\s]+\.swift)"#
89+
guard let regexp = NSRegularExpression.fromPattern(swiftFilePattern) else {
90+
return nil
91+
}
92+
return regexp.matches(in: commandDetailDesc,
93+
options: .reportProgress,
94+
range: NSRange(location: 0, length: commandDetailDesc.count))
95+
.map { match -> BuildStep in
96+
let file = commandDetailDesc.substring(match.range(at: 1))
97+
return buildStep
98+
.with(documentURL: "file://\(file)")
99+
.with(title: "Compile \(file)")
100+
.with(signature: "\(buildStep.signature) \(file)")
101+
.withFilteredNotices()
102+
}
103+
}
104+
58105
private func getOrBuildTarget(_ name: String,
59106
in targets: [String: IDEActivityLogSection],
60107
using section: IDEActivityLogSection)

Sources/XCLogParser/parser/ParserBuildSteps.swift

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ public final class ParserBuildSteps {
9090
}
9191

9292
// swiftlint:disable function_body_length
93-
private func parseLogSection(logSection: IDEActivityLogSection, type: BuildStepType, parentSection: BuildStep?)
93+
public func parseLogSection(logSection: IDEActivityLogSection, type: BuildStepType, parentSection: BuildStep?)
9494
throws -> BuildStep {
9595
currentIndex += 1
9696
let detailType = type == .detail ? DetailStepType.getDetailType(signature: logSection.signature) : .none
@@ -156,8 +156,15 @@ public final class ParserBuildSteps {
156156
step.warningCount = targetWarnings
157157
step.errorCount = targetErrors
158158
}
159+
if type == .detail {
160+
step = step.moveSwiftStepsToRoot()
161+
}
159162
if step.detailStepType == .swiftCompilation {
160-
swiftFunctionTimesParser.parseFromLogSection(logSection)
163+
swiftFunctionTimesParser.addLogSection(logSection)
164+
if let individualSwiftSteps = logSection.getSwiftIndividualSteps(buildStep: step) {
165+
step.subSteps.append(contentsOf: individualSwiftSteps)
166+
step = step.withFilteredNotices()
167+
}
161168
}
162169
return step
163170
}
@@ -254,7 +261,7 @@ public final class ParserBuildSteps {
254261
}
255262

256263
private func decorateWithFunctionSteps(_ mainStep: BuildStep) -> BuildStep {
257-
swiftFunctionTimesParser.parseRawTimes()
264+
swiftFunctionTimesParser.parse()
258265
guard swiftFunctionTimesParser.hasFunctionTimes() else {
259266
return mainStep
260267
}
@@ -274,6 +281,9 @@ public final class ParserBuildSteps {
274281
var mutableSubStep = subStep
275282
mutableSubStep.swiftFunctionTimes = swiftFunctionTimesParser.findFunctionTimesForFilePath(
276283
subStep.documentURL)
284+
if mutableSubStep.subSteps.count > 0 {
285+
mutableSubStep.subSteps = addFunctionSteps(subStep.subSteps)
286+
}
277287
return mutableSubStep
278288
case .swiftAggregatedCompilation:
279289
var mutableSubStep = subStep

Sources/XCLogParser/parser/StringExtension.swift

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,10 @@ import Foundation
2222
extension String {
2323

2424
func substring(_ range: NSRange) -> String {
25-
let start = index(startIndex, offsetBy: range.location)
26-
let end = index(start, offsetBy: range.length)
27-
return String(self[start..<end])
25+
guard let stringRange = Range(range, in: self) else {
26+
return ""
27+
}
28+
return String(self[stringRange])
2829
}
2930

3031
}

0 commit comments

Comments
 (0)