From a296ffd984faf74101252ec74e8764ae775ad93e Mon Sep 17 00:00:00 2001 From: fortmarek Date: Wed, 10 Dec 2025 14:49:15 +0100 Subject: [PATCH 1/5] fix: unknown integer token location change on Xcode 26.2 Signed-off-by: fortmarek --- .../activityparser/ActivityParser.swift | 78 +++++++++++++------ .../ActivityParserTests.swift | 58 ++++++++++++++ 2 files changed, 114 insertions(+), 22 deletions(-) diff --git a/Sources/XCLogParser/activityparser/ActivityParser.swift b/Sources/XCLogParser/activityparser/ActivityParser.swift index 8aec978..083c084 100644 --- a/Sources/XCLogParser/activityparser/ActivityParser.swift +++ b/Sources/XCLogParser/activityparser/ActivityParser.swift @@ -96,28 +96,62 @@ public class ActivityParser { additionalDescription: try parseAsString(token: iterator.next())) } - public func parseIDEActivityLogSection(iterator: inout IndexingIterator<[Token]>) - throws -> IDEActivityLogSection { - return IDEActivityLogSection(sectionType: Int8(try parseAsInt(token: iterator.next())), - domainType: try parseAsString(token: iterator.next()), - title: try parseAsString(token: iterator.next()), - signature: try parseAsString(token: iterator.next()), - timeStartedRecording: try parseAsDouble(token: iterator.next()), - timeStoppedRecording: try parseAsDouble(token: iterator.next()), - subSections: try parseIDEActivityLogSections(iterator: &iterator), - text: try parseAsString(token: iterator.next()), - messages: try parseMessages(iterator: &iterator), - wasCancelled: try parseBoolean(token: iterator.next()), - isQuiet: try parseBoolean(token: iterator.next()), - wasFetchedFromCache: try parseBoolean(token: iterator.next()), - subtitle: try parseAsString(token: iterator.next()), - location: try parseDocumentLocation(iterator: &iterator), - commandDetailDesc: try parseAsString(token: iterator.next()), - uniqueIdentifier: try parseAsString(token: iterator.next()), - localizedResultString: try parseAsString(token: iterator.next()), - xcbuildSignature: try parseAsString(token: iterator.next()), - attachments: try parseIDEActivityLogSectionAttachments(iterator: &iterator), - unknown: isCommandLineLog ? Int(try parseAsInt(token: iterator.next())) : 0) + public func parseIDEActivityLogSection(iterator: inout IndexingIterator<[Token]>) throws -> IDEActivityLogSection { + let sectionType = Int8(try parseAsInt(token: iterator.next())) + let domainType = try parseAsString(token: iterator.next()) + let title = try parseAsString(token: iterator.next()) + let signature = try parseAsString(token: iterator.next()) + let timeStartedRecording = try parseAsDouble(token: iterator.next()) + let timeStoppedRecording = try parseAsDouble(token: iterator.next()) + let subSections = try parseIDEActivityLogSections(iterator: &iterator) + let text = try parseAsString(token: iterator.next()) + let messages = try parseMessages(iterator: &iterator) + let wasCancelled = try parseBoolean(token: iterator.next()) + let isQuiet = try parseBoolean(token: iterator.next()) + let wasFetchedFromCache = try parseBoolean(token: iterator.next()) + let nextToken = iterator.next() + var unknown: Int? + let subtitle: String + // On Xcode 26.2 and higher, the unknown integer appears before subtitle followed with another unknown list token + switch nextToken { + case let .some(.int(integer)): + unknown = Int(integer) + subtitle = try String(parseAsString(token: iterator.next())) + default: + subtitle = String(try parseAsString(token: nextToken)) + } + let location = try parseDocumentLocation(iterator: &iterator) + let commandDetailDesc = try parseAsString(token: iterator.next()) + let uniqueIdentifier = try parseAsString(token: iterator.next()) + let localizedResultString = try parseAsString(token: iterator.next()) + let xcbuildSignature = try parseAsString(token: iterator.next()) + let attachments = try parseIDEActivityLogSectionAttachments(iterator: &iterator) + if unknown == nil { + unknown = isCommandLineLog ? Int(try parseAsInt(token: iterator.next())) : 0 + } + + return IDEActivityLogSection( + sectionType: sectionType, + domainType: domainType, + title: title, + signature: signature, + timeStartedRecording: timeStartedRecording, + timeStoppedRecording: timeStoppedRecording, + subSections: subSections, + text: text, + messages: messages, + wasCancelled: wasCancelled, + isQuiet: isQuiet, + wasFetchedFromCache: wasFetchedFromCache, + subtitle: subtitle, + location: location, + commandDetailDesc: commandDetailDesc, + uniqueIdentifier: uniqueIdentifier, + localizedResultString: localizedResultString, + xcbuildSignature: xcbuildSignature, + attachments: attachments, + unknown: unknown ?? 0 + ) } public func parseIDEActivityLogUnitTestSection(iterator: inout IndexingIterator<[Token]>) diff --git a/Tests/XCLogParserTests/ActivityParserTests.swift b/Tests/XCLogParserTests/ActivityParserTests.swift index dbb2aa8..0e1f0e7 100644 --- a/Tests/XCLogParserTests/ActivityParserTests.swift +++ b/Tests/XCLogParserTests/ActivityParserTests.swift @@ -148,6 +148,36 @@ class ActivityParserTests: XCTestCase { return startTokens + logMessageTokens + endTokens }() + // Xcode 26.2 format: unknown integer appears before subtitle + lazy var IDEActivityLogSectionTokensXcode26_2: [Token] = { + let startTokens = [Token.int(2), + Token.string("com.apple.dt.IDE.BuildLogSection"), + Token.string("Prepare build"), + Token.string("Prepare build"), + Token.double(575479851.278759), + Token.double(575479851.778325), + Token.null, + Token.string("note: Using legacy build system"), + Token.list(1), + Token.className("IDEActivityLogMessage"), + Token.classNameRef("IDEActivityLogMessage"), + ] + let logMessageTokens = IDEActivityLogMessageTokens + let endTokens = [Token.int(1), + Token.int(0), + Token.int(1), + Token.int(42), // unknown integer before subtitle (Xcode 26.2+) + Token.string("subtitle"), + Token.null, + Token.string("commandDetailDesc"), + Token.string("501796C4-6BE4-4F80-9F9D-3269617ECC17"), + Token.string("localizedResultString"), + Token.string("xcbuildSignature"), + Token.list(0), // attachments + ] + return startTokens + logMessageTokens + endTokens + }() + let IDEConsoleItemTokens: [Token] = [ Token.className("IDEConsoleItem"), Token.classNameRef("IDEConsoleItem"), @@ -387,6 +417,34 @@ class ActivityParserTests: XCTestCase { XCTAssertEqual(0, logSection.unknown) } + func testParseIDEActivityLogSection_xcode26_2() throws { + parser.logVersion = 12 + let tokens = IDEActivityLogSectionTokensXcode26_2 + var iterator = tokens.makeIterator() + let logSection = try parser.parseIDEActivityLogSection(iterator: &iterator) + XCTAssertEqual(2, logSection.sectionType) + XCTAssertEqual("com.apple.dt.IDE.BuildLogSection", logSection.domainType) + XCTAssertEqual("Prepare build", logSection.title) + XCTAssertEqual("Prepare build", logSection.signature) + XCTAssertEqual(575479851.278759, logSection.timeStartedRecording) + XCTAssertEqual(575479851.778325, logSection.timeStoppedRecording) + XCTAssertEqual(0, logSection.subSections.count) + XCTAssertEqual("note: Using legacy build system", logSection.text) + XCTAssertEqual(1, logSection.messages.count) + XCTAssertTrue(logSection.wasCancelled) + XCTAssertFalse(logSection.isQuiet) + XCTAssertTrue(logSection.wasFetchedFromCache) + XCTAssertEqual("subtitle", logSection.subtitle) + XCTAssertEqual("", logSection.location.documentURLString) + XCTAssertEqual(0, logSection.location.timestamp) + XCTAssertEqual("commandDetailDesc", logSection.commandDetailDesc) + XCTAssertEqual("501796C4-6BE4-4F80-9F9D-3269617ECC17", logSection.uniqueIdentifier) + XCTAssertEqual("localizedResultString", logSection.localizedResultString) + XCTAssertEqual("xcbuildSignature", logSection.xcbuildSignature) + XCTAssertEqual(0, logSection.attachments.count) + XCTAssertEqual(42, logSection.unknown) + } + func testParseActivityLog() throws { let activityLog = try parser.parseIDEActiviyLogFromTokens(IDEActivityLogTokens) XCTAssertEqual(10, activityLog.version) From 5935e507ae0b8e2310f93ec97fc31bf73b46d357 Mon Sep 17 00:00:00 2001 From: fortmarek Date: Wed, 10 Dec 2025 15:13:42 +0100 Subject: [PATCH 2/5] Fix lint issues Signed-off-by: fortmarek --- Sources/XCLogParser/activityparser/ActivityParser.swift | 5 +++-- Tests/XCLogParserTests/ActivityParserTests.swift | 6 +++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Sources/XCLogParser/activityparser/ActivityParser.swift b/Sources/XCLogParser/activityparser/ActivityParser.swift index 083c084..155038e 100644 --- a/Sources/XCLogParser/activityparser/ActivityParser.swift +++ b/Sources/XCLogParser/activityparser/ActivityParser.swift @@ -96,6 +96,7 @@ public class ActivityParser { additionalDescription: try parseAsString(token: iterator.next())) } + // swiftlint:disable:next function_body_length public func parseIDEActivityLogSection(iterator: inout IndexingIterator<[Token]>) throws -> IDEActivityLogSection { let sectionType = Int8(try parseAsInt(token: iterator.next())) let domainType = try parseAsString(token: iterator.next()) @@ -112,7 +113,7 @@ public class ActivityParser { let nextToken = iterator.next() var unknown: Int? let subtitle: String - // On Xcode 26.2 and higher, the unknown integer appears before subtitle followed with another unknown list token + // On Xcode 26.2+, the unknown integer appears before subtitle switch nextToken { case let .some(.int(integer)): unknown = Int(integer) @@ -129,7 +130,7 @@ public class ActivityParser { if unknown == nil { unknown = isCommandLineLog ? Int(try parseAsInt(token: iterator.next())) : 0 } - + return IDEActivityLogSection( sectionType: sectionType, domainType: domainType, diff --git a/Tests/XCLogParserTests/ActivityParserTests.swift b/Tests/XCLogParserTests/ActivityParserTests.swift index 0e1f0e7..e2dbbd4 100644 --- a/Tests/XCLogParserTests/ActivityParserTests.swift +++ b/Tests/XCLogParserTests/ActivityParserTests.swift @@ -149,7 +149,7 @@ class ActivityParserTests: XCTestCase { }() // Xcode 26.2 format: unknown integer appears before subtitle - lazy var IDEActivityLogSectionTokensXcode26_2: [Token] = { + lazy var IDEActivityLogSectionTokensXcode262: [Token] = { let startTokens = [Token.int(2), Token.string("com.apple.dt.IDE.BuildLogSection"), Token.string("Prepare build"), @@ -417,9 +417,9 @@ class ActivityParserTests: XCTestCase { XCTAssertEqual(0, logSection.unknown) } - func testParseIDEActivityLogSection_xcode26_2() throws { + func testParseIDEActivityLogSectionXcode262() throws { parser.logVersion = 12 - let tokens = IDEActivityLogSectionTokensXcode26_2 + let tokens = IDEActivityLogSectionTokensXcode262 var iterator = tokens.makeIterator() let logSection = try parser.parseIDEActivityLogSection(iterator: &iterator) XCTAssertEqual(2, logSection.sectionType) From ee2ec8224ee13e56c66e0639a217c40478634b34 Mon Sep 17 00:00:00 2001 From: fortmarek Date: Wed, 10 Dec 2025 15:18:33 +0100 Subject: [PATCH 3/5] Bump macOS images Signed-off-by: fortmarek --- .github/workflows/ci.yml | 2 +- .github/workflows/release.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9ad7250..09c6cbe 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,7 +13,7 @@ jobs: args: --strict macOS: - runs-on: macos-13 + runs-on: macos-14 env: XCODE_VERSION: ${{ '14.1' }} steps: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4c3222d..8972371 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -12,7 +12,7 @@ on: jobs: macOS: name: Add macOS binaries to release - runs-on: macos-13 + runs-on: macos-14 env: XCODE_VERSION: ${{ '14.1' }} steps: From b596f7d744144d6cb8b2997f3924e197dba2835b Mon Sep 17 00:00:00 2001 From: fortmarek Date: Wed, 10 Dec 2025 15:20:32 +0100 Subject: [PATCH 4/5] Bump Xcode version Signed-off-by: fortmarek --- .github/workflows/ci.yml | 2 +- .github/workflows/release.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 09c6cbe..220c5f5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,7 +15,7 @@ jobs: macOS: runs-on: macos-14 env: - XCODE_VERSION: ${{ '14.1' }} + XCODE_VERSION: ${{ '16.4' }} steps: - name: Select Xcode run: "sudo xcode-select -s /Applications/Xcode_$XCODE_VERSION.app" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8972371..bb84035 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -14,7 +14,7 @@ jobs: name: Add macOS binaries to release runs-on: macos-14 env: - XCODE_VERSION: ${{ '14.1' }} + XCODE_VERSION: ${{ '16.4' }} steps: - name: Select Xcode run: "sudo xcode-select -s /Applications/Xcode_$XCODE_VERSION.app" From 042d9a04964d65b09f0333fe866cbf07a8df971c Mon Sep 17 00:00:00 2001 From: fortmarek Date: Wed, 10 Dec 2025 15:22:45 +0100 Subject: [PATCH 5/5] Bump macOS images Signed-off-by: fortmarek --- .github/workflows/ci.yml | 2 +- .github/workflows/release.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 220c5f5..6102052 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,7 +13,7 @@ jobs: args: --strict macOS: - runs-on: macos-14 + runs-on: macos-26 env: XCODE_VERSION: ${{ '16.4' }} steps: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index bb84035..1d5cd94 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -12,7 +12,7 @@ on: jobs: macOS: name: Add macOS binaries to release - runs-on: macos-14 + runs-on: macos-26 env: XCODE_VERSION: ${{ '16.4' }} steps: