From 4bc468573a2d4769d8d9bba45b22bf4cbf184598 Mon Sep 17 00:00:00 2001 From: Gabriela Coelho Date: Wed, 10 Jun 2026 10:42:53 -0300 Subject: [PATCH 1/2] Adding Xcode 26 Compability --- Package.resolved | 16 ++++++------- Package.swift | 4 ++-- .../BuildSettingsIntegrateAppender.swift | 11 ++++----- .../Integrate/XcodeProjIntegrate.swift | 2 +- .../Commands/Prepare/Prepare.swift | 2 +- .../Dependencies/CacheModeController.swift | 9 +++++++- ...jBuildSettingsIntegrateAppenderTests.swift | 23 ++++++++++--------- .../PhaseCacheModeControllerTests.swift | 4 ++-- 8 files changed, 39 insertions(+), 32 deletions(-) diff --git a/Package.resolved b/Package.resolved index f2b131ac..19f383d4 100644 --- a/Package.resolved +++ b/Package.resolved @@ -6,8 +6,8 @@ "repositoryURL": "https://github.com/tadija/AEXML.git", "state": { "branch": null, - "revision": "38f7d00b23ecd891e1ee656fa6aeebd6ba04ecc3", - "version": "4.6.1" + "revision": "db806756c989760b35108146381535aec231092b", + "version": "4.7.0" } }, { @@ -15,8 +15,8 @@ "repositoryURL": "https://github.com/kylef/PathKit.git", "state": { "branch": null, - "revision": "73f8e9dca9b7a3078cb79128217dc8f2e585a511", - "version": "1.0.0" + "revision": "3bfd2737b700b9a36565a8c94f4ad2b050a5e574", + "version": "1.0.1" } }, { @@ -24,8 +24,8 @@ "repositoryURL": "https://github.com/kylef/Spectre.git", "state": { "branch": null, - "revision": "f79d4ecbf8bc4e1579fbd86c3e1d652fb6876c53", - "version": "0.9.2" + "revision": "26cc5e9ae0947092c7139ef7ba612e34646086c7", + "version": "0.10.1" } }, { @@ -51,8 +51,8 @@ "repositoryURL": "https://github.com/tuist/XcodeProj.git", "state": { "branch": null, - "revision": "fae27b48bc14ff3fd9b02902e48c4665ce5a0793", - "version": "8.9.0" + "revision": "621eca8d091cc110a99adc23bb0d6618a65b4544", + "version": "9.13.0" } }, { diff --git a/Package.swift b/Package.swift index 00c88e43..14df8f46 100644 --- a/Package.swift +++ b/Package.swift @@ -7,7 +7,7 @@ import PackageDescription let package = Package( name: "XCRemoteCache", platforms: [ - .macOS(.v10_14), + .macOS(.v11), ], products: [ .executable(name: "xcprebuild", targets: ["xcprebuild"]), @@ -16,7 +16,7 @@ let package = Package( .package(url: "https://github.com/marmelroy/Zip.git", from: "2.1.2"), .package(url: "https://github.com/jpsim/Yams.git", from: "5.0.0"), .package(url: "https://github.com/apple/swift-argument-parser", from: "0.0.1"), - .package(url: "https://github.com/tuist/XcodeProj.git", from: "8.9.0"), + .package(url: "https://github.com/tuist/XcodeProj.git", from: "9.12.0"), .package(url: "https://github.com/apple/swift-docc-plugin", from: "1.0.0"), ], targets: [ diff --git a/Sources/XCRemoteCache/Commands/Prepare/Integrate/BuildSettingsIntegrateAppender.swift b/Sources/XCRemoteCache/Commands/Prepare/Integrate/BuildSettingsIntegrateAppender.swift index ec4f4b1c..51328399 100644 --- a/Sources/XCRemoteCache/Commands/Prepare/Integrate/BuildSettingsIntegrateAppender.swift +++ b/Sources/XCRemoteCache/Commands/Prepare/Integrate/BuildSettingsIntegrateAppender.swift @@ -18,8 +18,7 @@ // under the License. import Foundation - -typealias BuildSettings = [String: Any] +import XcodeProj struct BuildSettingsIntegrateAppenderOption: OptionSet { let rawValue: Int @@ -78,8 +77,8 @@ class XcodeProjBuildSettingsIntegrateAppender: BuildSettingsIntegrateAppender { setBuildSetting(buildSettings: &result, key: "LDPLUSPLUS", value: wrappers.ldplusplus.path ) } - let existingSwiftFlags = result["OTHER_SWIFT_FLAGS"] as? String - let existingCFlags = result["OTHER_CFLAGS"] as? String + let existingSwiftFlags = result["OTHER_SWIFT_FLAGS"]?.stringValue + let existingCFlags = result["OTHER_CFLAGS"]?.stringValue var swiftFlags = XcodeSettingsSwiftFlags(settingValue: existingSwiftFlags) var clangFlags = XcodeSettingsCFlags(settingValue: existingCFlags) @@ -102,14 +101,14 @@ class XcodeProjBuildSettingsIntegrateAppender: BuildSettingsIntegrateAppender { } private func setBuildSetting(buildSettings: inout BuildSettings, key: String, value: String?, excludedValue: String = "") { - buildSettings[key] = value + buildSettings[key] = value.map { .string($0) } guard value != nil else { // no need to exclude as the value will return } // Erase all overrides for a given sdk so a default toolchain is used for skippedSDK in sdksExclude { - buildSettings["\(key)[sdk=\(skippedSDK)]"] = excludedValue + buildSettings["\(key)[sdk=\(skippedSDK)]"] = .string(excludedValue) } } diff --git a/Sources/XCRemoteCache/Commands/Prepare/Integrate/XcodeProjIntegrate.swift b/Sources/XCRemoteCache/Commands/Prepare/Integrate/XcodeProjIntegrate.swift index 9713d1ff..e1a2e8f0 100644 --- a/Sources/XCRemoteCache/Commands/Prepare/Integrate/XcodeProjIntegrate.swift +++ b/Sources/XCRemoteCache/Commands/Prepare/Integrate/XcodeProjIntegrate.swift @@ -216,7 +216,7 @@ struct XcodeProjIntegrate: Integrate { } } - try xcodeproj.write(path: outputPath) + try xcodeproj.writePBXProj(path: outputPath, override: true, outputSettings: PBXOutputSetting()) try lldbPatcher.enable() } diff --git a/Sources/XCRemoteCache/Commands/Prepare/Prepare.swift b/Sources/XCRemoteCache/Commands/Prepare/Prepare.swift index 78814c91..d34b359d 100644 --- a/Sources/XCRemoteCache/Commands/Prepare/Prepare.swift +++ b/Sources/XCRemoteCache/Commands/Prepare/Prepare.swift @@ -74,7 +74,7 @@ class Prepare: PrepareLogic { /// Finds the best commit with generated artifacts to use func prepare() throws -> PrepareResult { do { - guard fileAccessor.fileExists(atPath: PhaseCacheModeController.xcodeSelectLink.path) else { + guard (try? FileManager.default.destinationOfSymbolicLink(atPath: PhaseCacheModeController.xcodeSelectLink.path)) != nil else { throw PrepareError.missingXcodeSelectDirectory } diff --git a/Sources/XCRemoteCache/Dependencies/CacheModeController.swift b/Sources/XCRemoteCache/Dependencies/CacheModeController.swift index 8768917d..7303bba9 100644 --- a/Sources/XCRemoteCache/Dependencies/CacheModeController.swift +++ b/Sources/XCRemoteCache/Dependencies/CacheModeController.swift @@ -41,7 +41,14 @@ protocol CacheModeController { class PhaseCacheModeController: CacheModeController { /// Path to the symbolic link that changes if other xcode is selected with `xcode-select -s` - static let xcodeSelectLink: URL = URL(fileURLWithPath: "/var/db/xcode_select_link") + static var xcodeSelectLink: URL { + let legacyPath = URL(fileURLWithPath: "/var/db/xcode_select_link") + let modernPath = URL(fileURLWithPath: "/var/select/developer_dir") + if (try? FileManager.default.destinationOfSymbolicLink(atPath: modernPath.path)) != nil { + return modernPath + } + return legacyPath + } private let mergeCommitFile: URL private let modeMarker: URL private let forceCached: Bool diff --git a/Tests/XCRemoteCacheTests/Commands/Prepare/Integrate/XcodeProjBuildSettingsIntegrateAppenderTests.swift b/Tests/XCRemoteCacheTests/Commands/Prepare/Integrate/XcodeProjBuildSettingsIntegrateAppenderTests.swift index 819323c7..d4c98808 100644 --- a/Tests/XCRemoteCacheTests/Commands/Prepare/Integrate/XcodeProjBuildSettingsIntegrateAppenderTests.swift +++ b/Tests/XCRemoteCacheTests/Commands/Prepare/Integrate/XcodeProjBuildSettingsIntegrateAppenderTests.swift @@ -18,6 +18,7 @@ // under the License. @testable import XCRemoteCache +import XcodeProj import XCTest class XcodeProjBuildSettingsIntegrateAppenderTests: XCTestCase { @@ -53,7 +54,7 @@ class XcodeProjBuildSettingsIntegrateAppenderTests: XCTestCase { options: [] ) let result = appender.appendToBuildSettings(buildSettings: buildSettings, wrappers: binaries) - let resultURL = try XCTUnwrap(result["XCRC_FAKE_SRCROOT"] as? String) + let resultURL = try XCTUnwrap(result["XCRC_FAKE_SRCROOT"]?.stringValue) XCTAssertEqual(resultURL, fakeRootURL.path) } @@ -69,7 +70,7 @@ class XcodeProjBuildSettingsIntegrateAppenderTests: XCTestCase { options: [] ) let result = appender.appendToBuildSettings(buildSettings: buildSettings, wrappers: binaries) - let resultURL: String = try XCTUnwrap(result["XCRC_FAKE_SRCROOT"] as? String) + let resultURL: String = try XCTUnwrap(result["XCRC_FAKE_SRCROOT"]?.stringValue) XCTAssertEqual(resultURL, fakeRootURL.path) } @@ -85,7 +86,7 @@ class XcodeProjBuildSettingsIntegrateAppenderTests: XCTestCase { options: [] ) let result = appender.appendToBuildSettings(buildSettings: buildSettings, wrappers: binaries) - let ldPlusPlus: String = try XCTUnwrap(result["LDPLUSPLUS"] as? String) + let ldPlusPlus: String = try XCTUnwrap(result["LDPLUSPLUS"]?.stringValue) XCTAssertEqual(ldPlusPlus, binaries.ldplusplus.path) } @@ -100,7 +101,7 @@ class XcodeProjBuildSettingsIntegrateAppenderTests: XCTestCase { options: [] ) let result = appender.appendToBuildSettings(buildSettings: buildSettings, wrappers: binaries) - let ldPlusPlusWatchOS: String = try XCTUnwrap(result["LDPLUSPLUS[sdk=watchOS*]"] as? String) + let ldPlusPlusWatchOS: String = try XCTUnwrap(result["LDPLUSPLUS[sdk=watchOS*]"]?.stringValue) XCTAssertEqual(ldPlusPlusWatchOS, "") } @@ -115,7 +116,7 @@ class XcodeProjBuildSettingsIntegrateAppenderTests: XCTestCase { options: [] ) let result = appender.appendToBuildSettings(buildSettings: buildSettings, wrappers: binaries) - let libtoolWatchOS: String = try XCTUnwrap(result["LIBTOOL[sdk=watchOS*]"] as? String) + let libtoolWatchOS: String = try XCTUnwrap(result["LIBTOOL[sdk=watchOS*]"]?.stringValue) XCTAssertEqual(libtoolWatchOS, "libtool") } @@ -130,8 +131,8 @@ class XcodeProjBuildSettingsIntegrateAppenderTests: XCTestCase { options: [] ) let result = appender.appendToBuildSettings(buildSettings: buildSettings, wrappers: binaries) - let ldPlusPlusWatchOS: String = try XCTUnwrap(result["LDPLUSPLUS[sdk=watchOS*]"] as? String) - let ldPlusPlusWatchSimulator: String = try XCTUnwrap(result["LDPLUSPLUS[sdk=watchsimulator*]"] as? String) + let ldPlusPlusWatchOS: String = try XCTUnwrap(result["LDPLUSPLUS[sdk=watchOS*]"]?.stringValue) + let ldPlusPlusWatchSimulator: String = try XCTUnwrap(result["LDPLUSPLUS[sdk=watchsimulator*]"]?.stringValue) XCTAssertEqual(ldPlusPlusWatchOS, "") XCTAssertEqual(ldPlusPlusWatchSimulator, "") @@ -147,8 +148,8 @@ class XcodeProjBuildSettingsIntegrateAppenderTests: XCTestCase { options: [] ) let result = appender.appendToBuildSettings(buildSettings: buildSettings, wrappers: binaries) - let disabledWatchOS: String = try XCTUnwrap(result["XCRC_DISABLED[sdk=watchOS*]"] as? String) - let disabledWatchSimulator: String = try XCTUnwrap(result["XCRC_DISABLED[sdk=watchsimulator*]"] as? String) + let disabledWatchOS: String = try XCTUnwrap(result["XCRC_DISABLED[sdk=watchOS*]"]?.stringValue) + let disabledWatchSimulator: String = try XCTUnwrap(result["XCRC_DISABLED[sdk=watchsimulator*]"]?.stringValue) XCTAssertEqual(disabledWatchOS, "YES") XCTAssertEqual(disabledWatchSimulator, "YES") @@ -164,7 +165,7 @@ class XcodeProjBuildSettingsIntegrateAppenderTests: XCTestCase { options: [.disableSwiftDriverIntegration] ) let result = appender.appendToBuildSettings(buildSettings: buildSettings, wrappers: binaries) - let useSwiftIntegrationDriver: String = try XCTUnwrap(result["SWIFT_USE_INTEGRATED_DRIVER"] as? String) + let useSwiftIntegrationDriver: String = try XCTUnwrap(result["SWIFT_USE_INTEGRATED_DRIVER"]?.stringValue) XCTAssertEqual(useSwiftIntegrationDriver, "NO") } @@ -179,7 +180,7 @@ class XcodeProjBuildSettingsIntegrateAppenderTests: XCTestCase { options: [] ) let result = appender.appendToBuildSettings(buildSettings: buildSettings, wrappers: binaries) - let useSwiftIntegrationDriver: String? = result["SWIFT_USE_INTEGRATED_DRIVER"] as? String + let useSwiftIntegrationDriver: String? = result["SWIFT_USE_INTEGRATED_DRIVER"]?.stringValue XCTAssertNil(useSwiftIntegrationDriver) } diff --git a/Tests/XCRemoteCacheTests/Dependencies/PhaseCacheModeControllerTests.swift b/Tests/XCRemoteCacheTests/Dependencies/PhaseCacheModeControllerTests.swift index 2e08cee3..3b138221 100644 --- a/Tests/XCRemoteCacheTests/Dependencies/PhaseCacheModeControllerTests.swift +++ b/Tests/XCRemoteCacheTests/Dependencies/PhaseCacheModeControllerTests.swift @@ -101,7 +101,7 @@ class PhaseCacheModeControllerTests: FileXCTestCase { try modeController.enable(allowedInputFiles: [], dependencies: []) let allDeps = try dependenciesWriter.wroteDependencies.unwrap().values.flatMap { $0 } - XCTAssertTrue(allDeps.contains("/var/db/xcode_select_link")) + XCTAssertTrue(allDeps.contains(PhaseCacheModeController.xcodeSelectLink.path)) } func testDependsOnXcodeSelectLinkWhenDisabled() throws { @@ -122,7 +122,7 @@ class PhaseCacheModeControllerTests: FileXCTestCase { try modeController.disable() let allDeps = try dependenciesWriter.wroteDependencies.unwrap().values.flatMap { $0 } - XCTAssertTrue(allDeps.contains("/var/db/xcode_select_link")) + XCTAssertTrue(allDeps.contains(PhaseCacheModeController.xcodeSelectLink.path)) } func testForcedCachedPhaseFailToDisable() throws { From 4a1fbad907a631a501d36c7319e44bc7733669f2 Mon Sep 17 00:00:00 2001 From: Gabriela Coelho Date: Wed, 10 Jun 2026 18:28:24 -0300 Subject: [PATCH 2/2] Fix to typo --- .../Commands/Prepare/Integrate/XcodeProjIntegrate.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/XCRemoteCache/Commands/Prepare/Integrate/XcodeProjIntegrate.swift b/Sources/XCRemoteCache/Commands/Prepare/Integrate/XcodeProjIntegrate.swift index e1a2e8f0..a1f2661f 100644 --- a/Sources/XCRemoteCache/Commands/Prepare/Integrate/XcodeProjIntegrate.swift +++ b/Sources/XCRemoteCache/Commands/Prepare/Integrate/XcodeProjIntegrate.swift @@ -216,7 +216,7 @@ struct XcodeProjIntegrate: Integrate { } } - try xcodeproj.writePBXProj(path: outputPath, override: true, outputSettings: PBXOutputSetting()) + try xcodeproj.writePBXProj(path: outputPath, override: true, outputSettings: PBXOutputSettings()) try lldbPatcher.enable() }