From 69af11c2e0fc4b29d55f32822093eee6be22c5a8 Mon Sep 17 00:00:00 2001 From: banjun Date: Thu, 2 Apr 2026 19:25:43 +0900 Subject: [PATCH] fix for Xcode 26.4, estimating build folders without env info that could be retrieved in older Xcodes --- .../BuildHelper.xcodeproj/project.pbxproj | 4 +- .../xcschemes/BuildHelper.xcscheme | 79 +++++++++++++++++++ .../project.pbxproj | 4 +- .../xcschemes/SwiftHotReloadExample.xcscheme | 2 +- .../xcschemes/SwiftHotReload.xcscheme | 68 ++++++++++++++++ Sources/Core/Builder.swift | 2 +- Sources/Core/Env.swift | 35 +++++++- Sources/Core/FileMonitor.swift | 2 +- Sources/Core/TargetSwiftFile.swift | 2 +- Sources/ProxyReloader/Proxy.swift | 2 +- Sources/ProxyReloader/ProxyReloader.swift | 2 +- Sources/ProxyReloader/RuntimePeer.swift | 4 +- 12 files changed, 192 insertions(+), 14 deletions(-) create mode 100644 BuildHelper/BuildHelper.xcodeproj/xcshareddata/xcschemes/BuildHelper.xcscheme create mode 100644 FrameworkTarget/SwiftHotReload.xcodeproj/xcshareddata/xcschemes/SwiftHotReload.xcscheme diff --git a/BuildHelper/BuildHelper.xcodeproj/project.pbxproj b/BuildHelper/BuildHelper.xcodeproj/project.pbxproj index 981bc17..d9fabee 100644 --- a/BuildHelper/BuildHelper.xcodeproj/project.pbxproj +++ b/BuildHelper/BuildHelper.xcodeproj/project.pbxproj @@ -133,7 +133,7 @@ attributes = { BuildIndependentTargetsInParallel = 1; LastSwiftUpdateCheck = 1500; - LastUpgradeCheck = 1600; + LastUpgradeCheck = 2640; TargetAttributes = { 63A746952AFDD742003FA3AC = { CreatedOnToolsVersion = 15.0.1; @@ -244,6 +244,7 @@ MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; + STRING_CATALOG_GENERATE_SYMBOLS = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; }; @@ -301,6 +302,7 @@ MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; SDKROOT = macosx; + STRING_CATALOG_GENERATE_SYMBOLS = YES; SWIFT_COMPILATION_MODE = wholemodule; }; name = Release; diff --git a/BuildHelper/BuildHelper.xcodeproj/xcshareddata/xcschemes/BuildHelper.xcscheme b/BuildHelper/BuildHelper.xcodeproj/xcshareddata/xcschemes/BuildHelper.xcscheme new file mode 100644 index 0000000..f2773df --- /dev/null +++ b/BuildHelper/BuildHelper.xcodeproj/xcshareddata/xcschemes/BuildHelper.xcscheme @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Example/SwiftHotReloadExample.xcodeproj/project.pbxproj b/Example/SwiftHotReloadExample.xcodeproj/project.pbxproj index 334dc1c..0628113 100644 --- a/Example/SwiftHotReloadExample.xcodeproj/project.pbxproj +++ b/Example/SwiftHotReloadExample.xcodeproj/project.pbxproj @@ -123,7 +123,7 @@ attributes = { BuildIndependentTargetsInParallel = 1; LastSwiftUpdateCheck = 1500; - LastUpgradeCheck = 1600; + LastUpgradeCheck = 2640; TargetAttributes = { 63980F422AEA8DA50099B122 = { CreatedOnToolsVersion = 15.0.1; @@ -232,6 +232,7 @@ MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; + STRING_CATALOG_GENERATE_SYMBOLS = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; }; @@ -287,6 +288,7 @@ LOCALIZATION_PREFERS_STRING_CATALOGS = YES; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; + STRING_CATALOG_GENERATE_SYMBOLS = YES; SWIFT_COMPILATION_MODE = wholemodule; }; name = Release; diff --git a/Example/SwiftHotReloadExample.xcodeproj/xcshareddata/xcschemes/SwiftHotReloadExample.xcscheme b/Example/SwiftHotReloadExample.xcodeproj/xcshareddata/xcschemes/SwiftHotReloadExample.xcscheme index c24c1fc..904f437 100644 --- a/Example/SwiftHotReloadExample.xcodeproj/xcshareddata/xcschemes/SwiftHotReloadExample.xcscheme +++ b/Example/SwiftHotReloadExample.xcodeproj/xcshareddata/xcschemes/SwiftHotReloadExample.xcscheme @@ -1,6 +1,6 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Sources/Core/Builder.swift b/Sources/Core/Builder.swift index 419a5fb..30a89a2 100644 --- a/Sources/Core/Builder.swift +++ b/Sources/Core/Builder.swift @@ -59,7 +59,7 @@ public final actor Builder { } self.derivedData = derivedData self.moduleCachePath = derivedData.appendingPathComponent("ModuleCache.noindex") - guard let confBuildDirAppRandomString = p.confBuildDirAppRandomString ?? p.env.estimatedConfigurationBuildRandomString else { + guard let confBuildDirAppRandomString = p.confBuildDirAppRandomString ?? p.env.estimatedConfigurationBuildRandomString ?? Env.host.estimatedConfigurationBuildRandomString else { throw Error.missingRequiredEnvironments("confBuildDirAppRandomString") } guard let mainModule = p.mainModule ?? p.env.estimatedMainModule else { diff --git a/Sources/Core/Env.swift b/Sources/Core/Env.swift index a282496..de76c67 100644 --- a/Sources/Core/Env.swift +++ b/Sources/Core/Env.swift @@ -14,7 +14,7 @@ public struct Env: Codable, Equatable, Sendable { /// /Users/username public var estimatedHomeDir: URL? { - (SIMULATOR_HOST_HOME ?? NSHomeDirectory()).map(URL.init(fileURLWithPath:)) + URL(fileURLWithPath: SIMULATOR_HOST_HOME ?? NSHomeDirectory()) } /// /Users/username/Library/Developer/Xcode/DerivedData/app-abcdefg0123456789/Build/Products/Debug-iphonesimulator var estimatedBuilProductsDir: [URL] { @@ -22,7 +22,17 @@ public struct Env: Codable, Equatable, Sendable { let b = [(__XPC_DYLD_FRAMEWORK_PATH ?? __XPC_DYLD_LIBRARY_PATH ?? __XCODE_BUILT_PRODUCTS_DIR_PATHS ?? __XPC_DYLD_LIBRARY_PATH ?? PWD).map(URL.init(fileURLWithPath:))].compactMap {$0} let c = LC_RPATHs.filter { $0.contains("/DerivedData/") && $0.contains("/Build/Products/") }.map { $0.replacingOccurrences(of: "/PackageFrameworks", with: "") }.map(URL.init(fileURLWithPath:)) - return a + b + c + let r = a + b + c + return r +// // for device, feed by BuildHelper +// func fallback() -> [URL] { +// guard let estimataedDerivedData, let estimatedConfigurationBuildRandomString else {return []} +// return [estimataedDerivedData +// .appendingPathComponent(estimatedConfigurationBuildRandomString) +// .appendingPathComponent("Build/Products")] +// // TODO +// } +// return !r.isEmpty ? r : (self != .host ? Env.host.estimatedBuilProductsDir : []) } /// /Users/username/Library/Developer/Xcode/DerivedData public var estimataedDerivedData: URL? { @@ -31,6 +41,7 @@ public struct Env: Codable, Equatable, Sendable { .reversed().drop {$0 != "DerivedData"}.reversed() .joined(separator: "/")) }.first { $0.path.contains("DerivedData") } + ?? (self != .host ? Env.host.estimataedDerivedData : nil) // for device, feed by BuildHelper } /// app-abcdefg0123456789 public var estimatedConfigurationBuildRandomString: String? { @@ -59,6 +70,7 @@ public struct Env: Codable, Equatable, Sendable { /// Debug public var estimatedConfigurationPlatform: String? { estimatedBuilProductsDir.first?.lastPathComponent + ?? DTPlatformName.map {"Debug-\($0)"} // assuming Debug (non Release, and named Debug), assuming non-macOS (=has suffix) } /// iphonesimulator var estimatedPlatform: String? { @@ -70,7 +82,7 @@ public struct Env: Codable, Equatable, Sendable { $0.components(separatedBy: "/") .reversed().drop {$0 != "Platforms"}.dropFirst().reversed() .joined(separator: "/") - }).map(URL.init(fileURLWithPath:)) + }).flatMap {$0.isEmpty ? nil : URL(fileURLWithPath: $0)} ?? (self != .host ? Env.host.estimatedDeveloperDir : nil) // on iphoneos, developer dir is not available in env. use host env typically on macOS build helper } @@ -120,7 +132,22 @@ public struct Env: Codable, Equatable, Sendable { /// Product app bundle on host public var estimatedProductBundlePath: [URL] { guard let CFBundleName else { return [] } - return estimatedBuilProductsDir.map { $0.appendingPathComponent(CFBundleName).appendingPathExtension("app") } + if !estimatedBuilProductsDir.isEmpty { + return estimatedBuilProductsDir.map { + $0.appendingPathComponent(CFBundleName).appendingPathExtension("app") + } + } else { + guard let estimataedDerivedData, + let configurationBuildRandomString = estimatedConfigurationBuildRandomString ?? Env.host.estimatedConfigurationBuildRandomString, + let configurationPlatform = estimatedConfigurationPlatform else { return [] } + return [estimataedDerivedData + .appendingPathComponent(configurationBuildRandomString) + .appendingPathComponent("Build/Products") + .appendingPathComponent(configurationPlatform) + ].map { + $0.appendingPathComponent(CFBundleName).appendingPathExtension("app") + } + } } // Environment Variables diff --git a/Sources/Core/FileMonitor.swift b/Sources/Core/FileMonitor.swift index 3ae672c..a625d13 100644 --- a/Sources/Core/FileMonitor.swift +++ b/Sources/Core/FileMonitor.swift @@ -50,7 +50,7 @@ final actor FileMonitor { NSLog("%@", "🍓 target file change detected") lastTargetFileContent = content Task { @MainActor in - await fileChangesSubject.send(Date()) + fileChangesSubject.send(Date()) } monitor = nil diff --git a/Sources/Core/TargetSwiftFile.swift b/Sources/Core/TargetSwiftFile.swift index 4db44e7..9922e70 100644 --- a/Sources/Core/TargetSwiftFile.swift +++ b/Sources/Core/TargetSwiftFile.swift @@ -14,7 +14,7 @@ struct TargetSwiftFile { } init(_ file: URL) throws { - content = try String(contentsOf: file) + content = try String(contentsOf: file, encoding: .utf8) } } diff --git a/Sources/ProxyReloader/Proxy.swift b/Sources/ProxyReloader/Proxy.swift index 5906e03..bb5297b 100644 --- a/Sources/ProxyReloader/Proxy.swift +++ b/Sources/ProxyReloader/Proxy.swift @@ -170,7 +170,7 @@ final actor Proxy { try await loader.load(dylibPath: tmpDylibPath) let urls: [URL] = receivedDylibFilesSubject.value + [tmpDylibPath] Task { @MainActor in - await receivedDylibFilesSubject.send(urls) + receivedDylibFilesSubject.send(urls) } } catch { NSLog("%@", "🍓 \(#function) line \(#line) error = \(error)") diff --git a/Sources/ProxyReloader/ProxyReloader.swift b/Sources/ProxyReloader/ProxyReloader.swift index 72305b7..248fb92 100644 --- a/Sources/ProxyReloader/ProxyReloader.swift +++ b/Sources/ProxyReloader/ProxyReloader.swift @@ -18,7 +18,7 @@ public final class ProxyReloader: ObservableObject { } } - public func setShouldConnectToBuilder(_ shouldConnectToBuilder: @escaping (String, String) async -> Bool) { + public func setShouldConnectToBuilder(_ shouldConnectToBuilder: @Sendable @escaping (String, String) async -> Bool) { Task { await proxy.setShouldConnectToBuilder(shouldConnectToBuilder) } } } diff --git a/Sources/ProxyReloader/RuntimePeer.swift b/Sources/ProxyReloader/RuntimePeer.swift index 297f650..d17eb9a 100644 --- a/Sources/ProxyReloader/RuntimePeer.swift +++ b/Sources/ProxyReloader/RuntimePeer.swift @@ -1,8 +1,8 @@ #if DEBUG || os(macOS) import Foundation -import MultipeerConnectivity +@preconcurrency import MultipeerConnectivity -struct RuntimePeer { +struct RuntimePeer: Sendable { /// route for sending dylib var session: MCSession /// the destination peerID that will load the dylib on runtime