From 94586b2681a6fca14d9b703fd73b16b3479e7b31 Mon Sep 17 00:00:00 2001 From: Kyle Date: Sun, 2 Nov 2025 23:53:29 +0800 Subject: [PATCH 1/5] Add EnvManager support --- Package.swift | 118 ++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 100 insertions(+), 18 deletions(-) diff --git a/Package.swift b/Package.swift index a0a4575..31db5bb 100644 --- a/Package.swift +++ b/Package.swift @@ -4,24 +4,106 @@ import Foundation import PackageDescription -func envEnable(_ key: String, default defaultValue: Bool = false) -> Bool { - guard let value = Context.environment[key] else { - return defaultValue +// MARK: - Env Manager + +@MainActor +final class EnvManager { + static let shared = EnvManager() + + private var domains: [String] = [] + + func register(domain: String) { + domains.append(domain) } - if value == "1" { - return true - } else if value == "0" { - return false - } else { - return defaultValue + + func envEnable(rawKey: String, default defaultValue: Bool, searchInDomain: Bool) -> Bool { + if searchInDomain { + for domain in domains { + let key = "\(domain.uppercased())_\(rawKey)" + guard let (value, result) = _envEnable(key) else { + continue + } + print("[Env] \(key)=\(value) -> \(result)") + return result + } + print("[Env] \(rawKey) not set -> \(defaultValue)(default)") + return defaultValue + } else { + guard let (value, result) = _envEnable(rawKey) else { + print("[Env] \(rawKey) not set -> \(defaultValue)(default)") + return defaultValue + } + print("[Env] \(rawKey)=\(value) -> \(result)") + return result + } + } + + func envValue(rawKey: String, default defaultValue: Int, searchInDomain: Bool) -> Int { + if searchInDomain { + for domain in domains { + let key = "\(domain.uppercased())_\(rawKey)" + guard let (value, result) = _envValue(key) else { + continue + } + print("[Env] \(key)=\(value) -> \(result)") + return result + } + print("[Env] \(rawKey) not set -> \(defaultValue)(default)") + return defaultValue + } else { + guard let (value, result) = _envValue(rawKey) else { + print("[Env] \(rawKey) not set -> \(defaultValue)(default)") + return defaultValue + } + print("[Env] \(rawKey)=\(value) -> \(result)") + return result + } } + + private func _envEnable(_ key: String) -> (String, Bool)? { + guard let value = Context.environment[key] else { + return nil + } + let result: Bool + if value == "1" { + result = true + } else if value == "0" { + result = false + } else { + return nil + } + return (value, result) + } + + private func _envValue(_ key: String) -> (String, Int)? { + guard let value = Context.environment[key] else { + return nil + } + let result: Int + guard let result = Int(value) else { + return nil + } + return (value, result) + } +} +EnvManager.shared.register(domain: "OpenAttributeGraph") +EnvManager.shared.register(domain: "OpenSwiftUI") + +@MainActor +func envEnable(_ key: String, default defaultValue: Bool = false, searchInDomain: Bool = true) -> Bool { + EnvManager.shared.envEnable(rawKey: key, default: defaultValue, searchInDomain: searchInDomain) +} + +@MainActor +func envValue(_ key: String, default defaultValue: Int, searchInDomain: Bool = true) -> Int { + EnvManager.shared.envValue(rawKey: key, default: defaultValue, searchInDomain: searchInDomain) } #if os(macOS) // NOTE: #if os(macOS) check is not accurate if we are cross compiling for Linux platform. So we add an env key to specify it. -let buildForDarwinPlatform = envEnable("OPENSWIFTUI_BUILD_FOR_DARWIN_PLATFORM", default: true) +let buildForDarwinPlatform = envEnable("BUILD_FOR_DARWIN_PLATFORM", default: true) #else -let buildForDarwinPlatform = envEnable("OPENSWIFTUI_BUILD_FOR_DARWIN_PLATFORM") +let buildForDarwinPlatform = envEnable("BUILD_FOR_DARWIN_PLATFORM") #endif @@ -33,7 +115,7 @@ let isSPIBuild = envEnable("SPI_BUILD") // MARK: - Env and Config let isXcodeEnv = Context.environment["__CFBundleIdentifier"] == "com.apple.dt.Xcode" -let development = envEnable("OPENATTRIBUTEGRAPH_DEVELOPMENT", default: false) +let development = envEnable("DEVELOPMENT", default: false) // From Swift toolchain being installed or from Swift SDK. func detectLibSwiftPath() -> String { @@ -107,7 +189,7 @@ if !swiftToolchainVersion.isEmpty { // MARK: - [env] OPENATTRIBUTEGRAPH_SWIFT_TOOLCHAIN_SUPPORTED -let swiftToolchainSupported = envEnable("OPENATTRIBUTEGRAPH_SWIFT_TOOLCHAIN_SUPPORTED", default: !swiftToolchainVersion.isEmpty) +let swiftToolchainSupported = envEnable("SWIFT_TOOLCHAIN_SUPPORTED", default: !swiftToolchainVersion.isEmpty) if swiftToolchainSupported { sharedCSettings.append(.define("OPENATTRIBUTEGRAPH_SWIFT_TOOLCHAIN_SUPPORTED")) sharedSwiftSettings.append(.define("OPENATTRIBUTEGRAPH_SWIFT_TOOLCHAIN_SUPPORTED")) @@ -126,14 +208,14 @@ if releaseVersion >= 2021 { // MARK: - [env] OPENATTRIBUTEGRAPH_WERROR -let warningsAsErrorsCondition = envEnable("OPENATTRIBUTEGRAPH_WERROR", default: isXcodeEnv && development) +let warningsAsErrorsCondition = envEnable("WERROR", default: isXcodeEnv && development) if warningsAsErrorsCondition { sharedSwiftSettings.append(.unsafeFlags(["-warnings-as-errors"])) } // MARK: - [env] OPENATTRIBUTEGRAPH_LIBRARY_EVOLUTION -let libraryEvolutionCondition = envEnable("OPENATTRIBUTEGRAPH_LIBRARY_EVOLUTION", default: buildForDarwinPlatform) +let libraryEvolutionCondition = envEnable("LIBRARY_EVOLUTION", default: buildForDarwinPlatform) if libraryEvolutionCondition { // NOTE: -enable-library-evolution will cause module verify failure for `swift build`. @@ -143,7 +225,7 @@ if libraryEvolutionCondition { // MARK: - [env] OPENATTRIBUTEGRAPH_COMPATIBILITY_TEST -let compatibilityTestCondition = envEnable("OPENATTRIBUTEGRAPH_COMPATIBILITY_TEST", default: false) +let compatibilityTestCondition = envEnable("COMPATIBILITY_TEST", default: false) sharedCSettings.append(.define("OPENATTRIBUTEGRAPH", to: compatibilityTestCondition ? "1" : "0")) if !compatibilityTestCondition { sharedSwiftSettings.append(.define("OPENATTRIBUTEGRAPH")) @@ -258,9 +340,9 @@ if buildForDarwinPlatform { package.targets.append(openAttributeGraphCompatibilityTestsTarget) } -let useLocalDeps = envEnable("OPENATTRIBUTEGRAPH_USE_LOCAL_DEPS") +let useLocalDeps = envEnable("USE_LOCAL_DEPS") -let attributeGraphCondition = envEnable("OPENATTRIBUTEGRAPH_ATTRIBUTEGRAPH", default: buildForDarwinPlatform && !isSPIBuild) +let attributeGraphCondition = envEnable("ATTRIBUTEGRAPH", default: buildForDarwinPlatform && !isSPIBuild) if attributeGraphCondition { let privateFrameworkRepo: Package.Dependency From e9d4bef72f82437b8634031e046809845db4842d Mon Sep 17 00:00:00 2001 From: Kyle Date: Mon, 3 Nov 2025 00:06:03 +0800 Subject: [PATCH 2/5] Update EnvManager --- Package.swift | 111 +++++++++++++++++++++++++++++++------------------- 1 file changed, 68 insertions(+), 43 deletions(-) diff --git a/Package.swift b/Package.swift index 31db5bb..2757fec 100644 --- a/Package.swift +++ b/Package.swift @@ -1,5 +1,4 @@ // swift-tools-version: 6.1 -// The swift-tools-version declares the minimum version of Swift required to build this package. import Foundation import PackageDescription @@ -16,11 +15,25 @@ final class EnvManager { domains.append(domain) } - func envEnable(rawKey: String, default defaultValue: Bool, searchInDomain: Bool) -> Bool { + func envBoolValue(rawKey: String, default defaultValue: Bool, searchInDomain: Bool) -> Bool { + func _envBoolValue(_ key: String) -> (String, Bool)? { + guard let value = Context.environment[key] else { + return nil + } + let result: Bool + if value == "1" { + result = true + } else if value == "0" { + result = false + } else { + return nil + } + return (value, result) + } if searchInDomain { for domain in domains { let key = "\(domain.uppercased())_\(rawKey)" - guard let (value, result) = _envEnable(key) else { + guard let (value, result) = _envBoolValue(key) else { continue } print("[Env] \(key)=\(value) -> \(result)") @@ -29,7 +42,7 @@ final class EnvManager { print("[Env] \(rawKey) not set -> \(defaultValue)(default)") return defaultValue } else { - guard let (value, result) = _envEnable(rawKey) else { + guard let (value, result) = _envBoolValue(rawKey) else { print("[Env] \(rawKey) not set -> \(defaultValue)(default)") return defaultValue } @@ -38,11 +51,21 @@ final class EnvManager { } } - func envValue(rawKey: String, default defaultValue: Int, searchInDomain: Bool) -> Int { + func envIntValue(rawKey: String, default defaultValue: Int, searchInDomain: Bool) -> Int { + func _envIntValue(_ key: String) -> (String, Int)? { + guard let value = Context.environment[key] else { + return nil + } + let result: Int + guard let result = Int(value) else { + return nil + } + return (value, result) + } if searchInDomain { for domain in domains { let key = "\(domain.uppercased())_\(rawKey)" - guard let (value, result) = _envValue(key) else { + guard let (value, result) = _envIntValue(key) else { continue } print("[Env] \(key)=\(value) -> \(result)") @@ -51,7 +74,7 @@ final class EnvManager { print("[Env] \(rawKey) not set -> \(defaultValue)(default)") return defaultValue } else { - guard let (value, result) = _envValue(rawKey) else { + guard let (value, result) = _envIntValue(rawKey) else { print("[Env] \(rawKey) not set -> \(defaultValue)(default)") return defaultValue } @@ -60,30 +83,32 @@ final class EnvManager { } } - private func _envEnable(_ key: String) -> (String, Bool)? { - guard let value = Context.environment[key] else { - return nil + func envStringValue(rawKey: String, default defaultValue: String, searchInDomain: Bool) -> String { + func _envStringValue(_ key: String) -> (String, String)? { + guard let value = Context.environment[key] else { + return nil + } + return (value, value) } - let result: Bool - if value == "1" { - result = true - } else if value == "0" { - result = false + if searchInDomain { + for domain in domains { + let key = "\(domain.uppercased())_\(rawKey)" + guard let (value, result) = _envStringValue(key) else { + continue + } + print("[Env] \(key)=\(value) -> \(result)") + return result + } + print("[Env] \(rawKey) not set -> \(defaultValue)(default)") + return defaultValue } else { - return nil - } - return (value, result) - } - - private func _envValue(_ key: String) -> (String, Int)? { - guard let value = Context.environment[key] else { - return nil - } - let result: Int - guard let result = Int(value) else { - return nil + guard let (value, result) = _envStringValue(rawKey) else { + print("[Env] \(rawKey) not set -> \(defaultValue)(default)") + return defaultValue + } + print("[Env] \(rawKey)=\(value) -> \(result)") + return result } - return (value, result) } } EnvManager.shared.register(domain: "OpenAttributeGraph") @@ -91,34 +116,32 @@ EnvManager.shared.register(domain: "OpenSwiftUI") @MainActor func envEnable(_ key: String, default defaultValue: Bool = false, searchInDomain: Bool = true) -> Bool { - EnvManager.shared.envEnable(rawKey: key, default: defaultValue, searchInDomain: searchInDomain) + EnvManager.shared.envBoolValue(rawKey: key, default: defaultValue, searchInDomain: searchInDomain) } @MainActor func envValue(_ key: String, default defaultValue: Int, searchInDomain: Bool = true) -> Int { - EnvManager.shared.envValue(rawKey: key, default: defaultValue, searchInDomain: searchInDomain) + EnvManager.shared.envIntValue(rawKey: key, default: defaultValue, searchInDomain: searchInDomain) } +// MARK: - Env and config + #if os(macOS) // NOTE: #if os(macOS) check is not accurate if we are cross compiling for Linux platform. So we add an env key to specify it. let buildForDarwinPlatform = envEnable("BUILD_FOR_DARWIN_PLATFORM", default: true) #else let buildForDarwinPlatform = envEnable("BUILD_FOR_DARWIN_PLATFORM") #endif - - // https://github.com/SwiftPackageIndex/SwiftPackageIndex-Server/issues/3061#issuecomment-2118821061 // By-pass https://github.com/swiftlang/swift-package-manager/issues/7580 -let isSPIDocGenerationBuild = envEnable("SPI_GENERATE_DOCS", default: false) -let isSPIBuild = envEnable("SPI_BUILD") - -// MARK: - Env and Config +let isSPIDocGenerationBuild = envEnable("SPI_GENERATE_DOCS", searchInDomain: false) +let isSPIBuild = envEnable("SPI_BUILD", searchInDomain: false) let isXcodeEnv = Context.environment["__CFBundleIdentifier"] == "com.apple.dt.Xcode" let development = envEnable("DEVELOPMENT", default: false) -// From Swift toolchain being installed or from Swift SDK. -func detectLibSwiftPath() -> String { +let libSwiftPath = { + // From Swift toolchain being installed or from Swift SDK. guard let libSwiftPath = Context.environment["OPENATTRIBUTEGRAPH_LIB_SWIFT_PATH"] else { // Fallback when LIB_SWIFT_PATH is not set let swiftBinPath = Context.environment["OPENATTRIBUTEGRAPH_BIN_SWIFT_PATH"] ?? Context.environment["_"] ?? "/usr/bin/swift" @@ -127,8 +150,13 @@ func detectLibSwiftPath() -> String { return SDKPath.appending("/usr/lib/swift") } return libSwiftPath -} -let libSwiftPath = detectLibSwiftPath() +}() + +let swiftToolchainPath = Context.environment["OPENATTRIBUTEGRAPH_SWIFT_TOOLCHAIN_PATH"] ?? (development ? "/Volumes/BuildMachine/swift-project" : "") +let swiftToolchainVersion = Context.environment["OPENATTRIBUTEGRAPH_SWIFT_TOOLCHAIN_VERSION"] ?? (development ? "6.0.2" : "") +let swiftToolchainSupported = envEnable("SWIFT_TOOLCHAIN_SUPPORTED", default: !swiftToolchainVersion.isEmpty) + + var sharedCSettings: [CSetting] = [ .unsafeFlags(["-I", libSwiftPath], .when(platforms: .nonDarwinPlatforms)), @@ -156,7 +184,6 @@ var sharedSwiftSettings: [SwiftSetting] = [ // // where <$OPENATTRIBUTEGRAPH_SWIFT_TOOLCHAIN_PATH> is the parent directory of the swift repository. -let swiftToolchainPath = Context.environment["OPENATTRIBUTEGRAPH_SWIFT_TOOLCHAIN_PATH"] ?? (development ? "/Volumes/BuildMachine/swift-project" : "") if !swiftToolchainPath.isEmpty { sharedCSettings.append( .unsafeFlags( @@ -180,7 +207,6 @@ if !swiftToolchainPath.isEmpty { // MARK: [env] OPENATTRIBUTEGRAPH_SWIFT_TOOLCHAIN_VERSION -let swiftToolchainVersion = Context.environment["OPENATTRIBUTEGRAPH_SWIFT_TOOLCHAIN_VERSION"] ?? (development ? "6.0.2" : "") if !swiftToolchainVersion.isEmpty { sharedCSettings.append( .define("OPENATTRIBUTEGRAPH_SWIFT_TOOLCHAIN_VERSION", to: swiftToolchainVersion) @@ -189,7 +215,6 @@ if !swiftToolchainVersion.isEmpty { // MARK: - [env] OPENATTRIBUTEGRAPH_SWIFT_TOOLCHAIN_SUPPORTED -let swiftToolchainSupported = envEnable("SWIFT_TOOLCHAIN_SUPPORTED", default: !swiftToolchainVersion.isEmpty) if swiftToolchainSupported { sharedCSettings.append(.define("OPENATTRIBUTEGRAPH_SWIFT_TOOLCHAIN_SUPPORTED")) sharedSwiftSettings.append(.define("OPENATTRIBUTEGRAPH_SWIFT_TOOLCHAIN_SUPPORTED")) From 66b80e5f83d60eccbd4de4bdca15b5c50ba2e587 Mon Sep 17 00:00:00 2001 From: Kyle Date: Mon, 3 Nov 2025 00:12:23 +0800 Subject: [PATCH 3/5] Update EnvManager --- Package.swift | 135 +++++++++++++++++--------------------------------- 1 file changed, 46 insertions(+), 89 deletions(-) diff --git a/Package.swift b/Package.swift index 2757fec..ba02d65 100644 --- a/Package.swift +++ b/Package.swift @@ -15,130 +15,87 @@ final class EnvManager { domains.append(domain) } - func envBoolValue(rawKey: String, default defaultValue: Bool, searchInDomain: Bool) -> Bool { - func _envBoolValue(_ key: String) -> (String, Bool)? { + private func envValue( + rawKey: String, + default defaultValue: T, + searchInDomain: Bool, + parser: (String) -> T? + ) -> T { + func parseEnvValue(_ key: String) -> (String, T)? { guard let value = Context.environment[key] else { return nil } - let result: Bool - if value == "1" { - result = true - } else if value == "0" { - result = false - } else { + guard let result = parser(value) else { return nil } return (value, result) } - if searchInDomain { - for domain in domains { - let key = "\(domain.uppercased())_\(rawKey)" - guard let (value, result) = _envBoolValue(key) else { - continue - } + let keys: [String] = searchInDomain + ? domains.map { "\($0.uppercased())_\(rawKey)" } + : [rawKey] + for key in keys { + if let (value, result) = parseEnvValue(key) { print("[Env] \(key)=\(value) -> \(result)") return result } - print("[Env] \(rawKey) not set -> \(defaultValue)(default)") - return defaultValue - } else { - guard let (value, result) = _envBoolValue(rawKey) else { - print("[Env] \(rawKey) not set -> \(defaultValue)(default)") - return defaultValue - } - print("[Env] \(rawKey)=\(value) -> \(result)") - return result } + let primaryKey = keys.first ?? rawKey + print("[Env] \(primaryKey) not set -> \(defaultValue)(default)") + return defaultValue } - func envIntValue(rawKey: String, default defaultValue: Int, searchInDomain: Bool) -> Int { - func _envIntValue(_ key: String) -> (String, Int)? { - guard let value = Context.environment[key] else { - return nil - } - let result: Int - guard let result = Int(value) else { - return nil - } - return (value, result) - } - if searchInDomain { - for domain in domains { - let key = "\(domain.uppercased())_\(rawKey)" - guard let (value, result) = _envIntValue(key) else { - continue - } - print("[Env] \(key)=\(value) -> \(result)") - return result - } - print("[Env] \(rawKey) not set -> \(defaultValue)(default)") - return defaultValue - } else { - guard let (value, result) = _envIntValue(rawKey) else { - print("[Env] \(rawKey) not set -> \(defaultValue)(default)") - return defaultValue + func envBoolValue(rawKey: String, default defaultValue: Bool, searchInDomain: Bool) -> Bool { + envValue(rawKey: rawKey, default: defaultValue, searchInDomain: searchInDomain) { value in + switch value { + case "1": true + case "0": false + default: nil } - print("[Env] \(rawKey)=\(value) -> \(result)") - return result } } + func envIntValue(rawKey: String, default defaultValue: Int, searchInDomain: Bool) -> Int { + envValue(rawKey: rawKey, default: defaultValue, searchInDomain: searchInDomain) { Int($0) } + } + func envStringValue(rawKey: String, default defaultValue: String, searchInDomain: Bool) -> String { - func _envStringValue(_ key: String) -> (String, String)? { - guard let value = Context.environment[key] else { - return nil - } - return (value, value) - } - if searchInDomain { - for domain in domains { - let key = "\(domain.uppercased())_\(rawKey)" - guard let (value, result) = _envStringValue(key) else { - continue - } - print("[Env] \(key)=\(value) -> \(result)") - return result - } - print("[Env] \(rawKey) not set -> \(defaultValue)(default)") - return defaultValue - } else { - guard let (value, result) = _envStringValue(rawKey) else { - print("[Env] \(rawKey) not set -> \(defaultValue)(default)") - return defaultValue - } - print("[Env] \(rawKey)=\(value) -> \(result)") - return result - } + envValue(rawKey: rawKey, default: defaultValue, searchInDomain: searchInDomain) { $0 } } } EnvManager.shared.register(domain: "OpenAttributeGraph") EnvManager.shared.register(domain: "OpenSwiftUI") @MainActor -func envEnable(_ key: String, default defaultValue: Bool = false, searchInDomain: Bool = true) -> Bool { +func envBoolValue(_ key: String, default defaultValue: Bool = false, searchInDomain: Bool = true) -> Bool { EnvManager.shared.envBoolValue(rawKey: key, default: defaultValue, searchInDomain: searchInDomain) } @MainActor -func envValue(_ key: String, default defaultValue: Int, searchInDomain: Bool = true) -> Int { +func envIntValue(_ key: String, default defaultValue: Int, searchInDomain: Bool = true) -> Int { EnvManager.shared.envIntValue(rawKey: key, default: defaultValue, searchInDomain: searchInDomain) } +@MainActor +func envStringValue(_ key: String, default defaultValue: String, searchInDomain: Bool = true) -> String { + EnvManager.shared.envStringValue(rawKey: key, default: defaultValue, searchInDomain: searchInDomain) +} + + // MARK: - Env and config #if os(macOS) // NOTE: #if os(macOS) check is not accurate if we are cross compiling for Linux platform. So we add an env key to specify it. -let buildForDarwinPlatform = envEnable("BUILD_FOR_DARWIN_PLATFORM", default: true) +let buildForDarwinPlatform = envBoolValue("BUILD_FOR_DARWIN_PLATFORM", default: true) #else -let buildForDarwinPlatform = envEnable("BUILD_FOR_DARWIN_PLATFORM") +let buildForDarwinPlatform = envBoolValue("BUILD_FOR_DARWIN_PLATFORM") #endif // https://github.com/SwiftPackageIndex/SwiftPackageIndex-Server/issues/3061#issuecomment-2118821061 // By-pass https://github.com/swiftlang/swift-package-manager/issues/7580 -let isSPIDocGenerationBuild = envEnable("SPI_GENERATE_DOCS", searchInDomain: false) -let isSPIBuild = envEnable("SPI_BUILD", searchInDomain: false) +let isSPIDocGenerationBuild = envBoolValue("SPI_GENERATE_DOCS", searchInDomain: false) +let isSPIBuild = envBoolValue("SPI_BUILD", searchInDomain: false) let isXcodeEnv = Context.environment["__CFBundleIdentifier"] == "com.apple.dt.Xcode" -let development = envEnable("DEVELOPMENT", default: false) +let development = envBoolValue("DEVELOPMENT", default: false) let libSwiftPath = { // From Swift toolchain being installed or from Swift SDK. @@ -154,7 +111,7 @@ let libSwiftPath = { let swiftToolchainPath = Context.environment["OPENATTRIBUTEGRAPH_SWIFT_TOOLCHAIN_PATH"] ?? (development ? "/Volumes/BuildMachine/swift-project" : "") let swiftToolchainVersion = Context.environment["OPENATTRIBUTEGRAPH_SWIFT_TOOLCHAIN_VERSION"] ?? (development ? "6.0.2" : "") -let swiftToolchainSupported = envEnable("SWIFT_TOOLCHAIN_SUPPORTED", default: !swiftToolchainVersion.isEmpty) +let swiftToolchainSupported = envBoolValue("SWIFT_TOOLCHAIN_SUPPORTED", default: !swiftToolchainVersion.isEmpty) @@ -233,14 +190,14 @@ if releaseVersion >= 2021 { // MARK: - [env] OPENATTRIBUTEGRAPH_WERROR -let warningsAsErrorsCondition = envEnable("WERROR", default: isXcodeEnv && development) +let warningsAsErrorsCondition = envBoolValue("WERROR", default: isXcodeEnv && development) if warningsAsErrorsCondition { sharedSwiftSettings.append(.unsafeFlags(["-warnings-as-errors"])) } // MARK: - [env] OPENATTRIBUTEGRAPH_LIBRARY_EVOLUTION -let libraryEvolutionCondition = envEnable("LIBRARY_EVOLUTION", default: buildForDarwinPlatform) +let libraryEvolutionCondition = envBoolValue("LIBRARY_EVOLUTION", default: buildForDarwinPlatform) if libraryEvolutionCondition { // NOTE: -enable-library-evolution will cause module verify failure for `swift build`. @@ -250,7 +207,7 @@ if libraryEvolutionCondition { // MARK: - [env] OPENATTRIBUTEGRAPH_COMPATIBILITY_TEST -let compatibilityTestCondition = envEnable("COMPATIBILITY_TEST", default: false) +let compatibilityTestCondition = envBoolValue("COMPATIBILITY_TEST", default: false) sharedCSettings.append(.define("OPENATTRIBUTEGRAPH", to: compatibilityTestCondition ? "1" : "0")) if !compatibilityTestCondition { sharedSwiftSettings.append(.define("OPENATTRIBUTEGRAPH")) @@ -365,9 +322,9 @@ if buildForDarwinPlatform { package.targets.append(openAttributeGraphCompatibilityTestsTarget) } -let useLocalDeps = envEnable("USE_LOCAL_DEPS") +let useLocalDeps = envBoolValue("USE_LOCAL_DEPS") -let attributeGraphCondition = envEnable("ATTRIBUTEGRAPH", default: buildForDarwinPlatform && !isSPIBuild) +let attributeGraphCondition = envBoolValue("ATTRIBUTEGRAPH", default: buildForDarwinPlatform && !isSPIBuild) if attributeGraphCondition { let privateFrameworkRepo: Package.Dependency From 539fd59ce467cc4b661d7e3f568007c350561282 Mon Sep 17 00:00:00 2001 From: Kyle Date: Mon, 3 Nov 2025 00:34:10 +0800 Subject: [PATCH 4/5] Update Package.swift --- Package.resolved | 2 +- Package.swift | 136 +++++++++++++++++++++++------------------------ 2 files changed, 67 insertions(+), 71 deletions(-) diff --git a/Package.resolved b/Package.resolved index 0d9e88e..d899750 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "24cd5d62b08d90c8d12d94ddb1d84e7d8da76f976a9b8642ccf50d81f29e8bb0", + "originHash" : "5baa2b7b03ddd25a25618a7a83046d7724b256c5e78940b0da177dd2c27e04e8", "pins" : [ { "identity" : "darwinprivateframeworks", diff --git a/Package.swift b/Package.swift index ba02d65..2d2c0ba 100644 --- a/Package.swift +++ b/Package.swift @@ -15,12 +15,18 @@ final class EnvManager { domains.append(domain) } + func withDomain(_ domain: String, perform: () throws -> T) rethrows -> T { + domains.append(domain) + defer { domains.removeAll { $0 == domain } } + return try perform() + } + private func envValue( rawKey: String, - default defaultValue: T, + default defaultValue: T?, searchInDomain: Bool, parser: (String) -> T? - ) -> T { + ) -> T? { func parseEnvValue(_ key: String) -> (String, T)? { guard let value = Context.environment[key] else { return nil @@ -40,11 +46,13 @@ final class EnvManager { } } let primaryKey = keys.first ?? rawKey - print("[Env] \(primaryKey) not set -> \(defaultValue)(default)") + if let defaultValue { + print("[Env] \(primaryKey) not set -> \(defaultValue)(default)") + } return defaultValue } - func envBoolValue(rawKey: String, default defaultValue: Bool, searchInDomain: Bool) -> Bool { + func envBoolValue(rawKey: String, default defaultValue: Bool? = nil, searchInDomain: Bool) -> Bool? { envValue(rawKey: rawKey, default: defaultValue, searchInDomain: searchInDomain) { value in switch value { case "1": true @@ -54,11 +62,11 @@ final class EnvManager { } } - func envIntValue(rawKey: String, default defaultValue: Int, searchInDomain: Bool) -> Int { + func envIntValue(rawKey: String, default defaultValue: Int? = nil, searchInDomain: Bool) -> Int? { envValue(rawKey: rawKey, default: defaultValue, searchInDomain: searchInDomain) { Int($0) } } - func envStringValue(rawKey: String, default defaultValue: String, searchInDomain: Bool) -> String { + func envStringValue(rawKey: String, default defaultValue: String? = nil, searchInDomain: Bool) -> String? { envValue(rawKey: rawKey, default: defaultValue, searchInDomain: searchInDomain) { $0 } } } @@ -67,19 +75,23 @@ EnvManager.shared.register(domain: "OpenSwiftUI") @MainActor func envBoolValue(_ key: String, default defaultValue: Bool = false, searchInDomain: Bool = true) -> Bool { - EnvManager.shared.envBoolValue(rawKey: key, default: defaultValue, searchInDomain: searchInDomain) + EnvManager.shared.envBoolValue(rawKey: key, default: defaultValue, searchInDomain: searchInDomain)! } @MainActor -func envIntValue(_ key: String, default defaultValue: Int, searchInDomain: Bool = true) -> Int { - EnvManager.shared.envIntValue(rawKey: key, default: defaultValue, searchInDomain: searchInDomain) +func envIntValue(_ key: String, default defaultValue: Int = 0, searchInDomain: Bool = true) -> Int { + EnvManager.shared.envIntValue(rawKey: key, default: defaultValue, searchInDomain: searchInDomain)! } @MainActor func envStringValue(_ key: String, default defaultValue: String, searchInDomain: Bool = true) -> String { - EnvManager.shared.envStringValue(rawKey: key, default: defaultValue, searchInDomain: searchInDomain) + EnvManager.shared.envStringValue(rawKey: key, default: defaultValue, searchInDomain: searchInDomain)! } +@MainActor +func envStringValue(_ key: String, searchInDomain: Bool = true) -> String? { + EnvManager.shared.envStringValue(rawKey: key, searchInDomain: searchInDomain) +} // MARK: - Env and config @@ -89,19 +101,21 @@ let buildForDarwinPlatform = envBoolValue("BUILD_FOR_DARWIN_PLATFORM", default: #else let buildForDarwinPlatform = envBoolValue("BUILD_FOR_DARWIN_PLATFORM") #endif + // https://github.com/SwiftPackageIndex/SwiftPackageIndex-Server/issues/3061#issuecomment-2118821061 // By-pass https://github.com/swiftlang/swift-package-manager/issues/7580 let isSPIDocGenerationBuild = envBoolValue("SPI_GENERATE_DOCS", searchInDomain: false) let isSPIBuild = envBoolValue("SPI_BUILD", searchInDomain: false) -let isXcodeEnv = Context.environment["__CFBundleIdentifier"] == "com.apple.dt.Xcode" +let isXcodeEnv = envStringValue("__CFBundleIdentifier", searchInDomain: false) == "com.apple.dt.Xcode" let development = envBoolValue("DEVELOPMENT", default: false) +let warningsAsErrorsCondition = envBoolValue("WERROR", default: isXcodeEnv && development) let libSwiftPath = { // From Swift toolchain being installed or from Swift SDK. - guard let libSwiftPath = Context.environment["OPENATTRIBUTEGRAPH_LIB_SWIFT_PATH"] else { + guard let libSwiftPath = envStringValue("LIB_SWIFT_PATH") else { // Fallback when LIB_SWIFT_PATH is not set - let swiftBinPath = Context.environment["OPENATTRIBUTEGRAPH_BIN_SWIFT_PATH"] ?? Context.environment["_"] ?? "/usr/bin/swift" + let swiftBinPath = envStringValue("BIN_SWIFT_PATH") ?? envStringValue("_", searchInDomain: false) ?? "/usr/bin/swift" let swiftBinURL = URL(fileURLWithPath: swiftBinPath) let SDKPath = swiftBinURL.deletingLastPathComponent().deletingLastPathComponent().deletingLastPathComponent().path return SDKPath.appending("/usr/lib/swift") @@ -109,11 +123,19 @@ let libSwiftPath = { return libSwiftPath }() -let swiftToolchainPath = Context.environment["OPENATTRIBUTEGRAPH_SWIFT_TOOLCHAIN_PATH"] ?? (development ? "/Volumes/BuildMachine/swift-project" : "") -let swiftToolchainVersion = Context.environment["OPENATTRIBUTEGRAPH_SWIFT_TOOLCHAIN_VERSION"] ?? (development ? "6.0.2" : "") +let swiftToolchainPath = envStringValue("SWIFT_TOOLCHAIN_PATH") ?? (development ? "/Volumes/BuildMachine/swift-project" : "") +let swiftToolchainVersion = envStringValue("SWIFT_TOOLCHAIN_VERSION") ?? (development ? "6.0.2" : "") let swiftToolchainSupported = envBoolValue("SWIFT_TOOLCHAIN_SUPPORTED", default: !swiftToolchainVersion.isEmpty) +let releaseVersion = envIntValue("TARGET_RELEASE", default: 2024) + +let libraryEvolutionCondition = envBoolValue("LIBRARY_EVOLUTION", default: buildForDarwinPlatform) +let compatibilityTestCondition = envBoolValue("COMPATIBILITY_TEST", default: false) + +let useLocalDeps = envBoolValue("USE_LOCAL_DEPS") +let attributeGraphCondition = envBoolValue("ATTRIBUTEGRAPH", default: buildForDarwinPlatform && !isSPIBuild) +// MARK: - Shared Settings var sharedCSettings: [CSetting] = [ .unsafeFlags(["-I", libSwiftPath], .when(platforms: .nonDarwinPlatforms)), @@ -124,9 +146,9 @@ var sharedSwiftSettings: [SwiftSetting] = [ .enableUpcomingFeature("InternalImportsByDefault"), .enableExperimentalFeature("Extern"), .swiftLanguageMode(.v5), -] -// MARK: [env] OPENATTRIBUTEGRAPH_SWIFT_TOOLCHAIN_PATH + .define("OPENATTRIBUTEGRAPH_RELEASE_\(releaseVersion)"), +] // Modified from: https://github.com/swiftlang/swift/blob/main/SwiftCompilerSources/Package.swift // @@ -161,58 +183,52 @@ if !swiftToolchainPath.isEmpty { ) ) } - -// MARK: [env] OPENATTRIBUTEGRAPH_SWIFT_TOOLCHAIN_VERSION - if !swiftToolchainVersion.isEmpty { sharedCSettings.append( .define("OPENATTRIBUTEGRAPH_SWIFT_TOOLCHAIN_VERSION", to: swiftToolchainVersion) ) } - -// MARK: - [env] OPENATTRIBUTEGRAPH_SWIFT_TOOLCHAIN_SUPPORTED - if swiftToolchainSupported { sharedCSettings.append(.define("OPENATTRIBUTEGRAPH_SWIFT_TOOLCHAIN_SUPPORTED")) sharedSwiftSettings.append(.define("OPENATTRIBUTEGRAPH_SWIFT_TOOLCHAIN_SUPPORTED")) } - -// MARK: - [env] OPENATTRIBUTEGRAPH_TARGET_RELEASE - -let releaseVersion = Context.environment["OPENATTRIBUTEGRAPH_TARGET_RELEASE"].flatMap { Int($0) } ?? 2024 -//sharedCSettings.append(.define("OPENATTRIBUTEGRAPH_RELEASE", to: "\(releaseVersion)")) -sharedSwiftSettings.append(.define("OPENATTRIBUTEGRAPH_RELEASE_\(releaseVersion)")) if releaseVersion >= 2021 { for year in 2021 ... releaseVersion { sharedSwiftSettings.append(.define("OPENATTRIBUTEGRAPH_SUPPORT_\(year)_API")) } } - -// MARK: - [env] OPENATTRIBUTEGRAPH_WERROR - -let warningsAsErrorsCondition = envBoolValue("WERROR", default: isXcodeEnv && development) if warningsAsErrorsCondition { sharedSwiftSettings.append(.unsafeFlags(["-warnings-as-errors"])) } - -// MARK: - [env] OPENATTRIBUTEGRAPH_LIBRARY_EVOLUTION - -let libraryEvolutionCondition = envBoolValue("LIBRARY_EVOLUTION", default: buildForDarwinPlatform) - if libraryEvolutionCondition { // NOTE: -enable-library-evolution will cause module verify failure for `swift build`. // Either set OPENATTRIBUTEGRAPH_LIBRARY_EVOLUTION=0 or add `-Xswiftc -no-verify-emitted-module-interface` after `swift build` sharedSwiftSettings.append(.unsafeFlags(["-enable-library-evolution", "-no-verify-emitted-module-interface"])) } - -// MARK: - [env] OPENATTRIBUTEGRAPH_COMPATIBILITY_TEST - -let compatibilityTestCondition = envBoolValue("COMPATIBILITY_TEST", default: false) -sharedCSettings.append(.define("OPENATTRIBUTEGRAPH", to: compatibilityTestCondition ? "1" : "0")) if !compatibilityTestCondition { + sharedCSettings.append(.define("OPENATTRIBUTEGRAPH")) sharedSwiftSettings.append(.define("OPENATTRIBUTEGRAPH")) } +// MARK: - Extension + +extension Target { + func addAGSettings() { + dependencies.append( + .product(name: "AttributeGraph", package: "DarwinPrivateFrameworks") + ) + var swiftSettings = swiftSettings ?? [] + swiftSettings.append(.define("OPENATTRIBUTEGRAPH_ATTRIBUTEGRAPH")) + self.swiftSettings = swiftSettings + } +} + +extension [Platform] { + static var nonDarwinPlatforms: [Platform] { + [.linux, .android, .wasi, .openbsd, .windows] + } +} + // MARK: - Targets let openAttributeGraphTarget = Target.target( @@ -297,35 +313,20 @@ let package = Package( cxxLanguageStandard: .cxx20 ) -extension Target { - func addAGSettings() { - dependencies.append( - .product(name: "AttributeGraph", package: "DarwinPrivateFrameworks") - ) - var swiftSettings = swiftSettings ?? [] - swiftSettings.append(.define("OPENATTRIBUTEGRAPH_ATTRIBUTEGRAPH")) - self.swiftSettings = swiftSettings - } -} - -if !compatibilityTestCondition { +if compatibilityTestCondition { + openAttributeGraphCompatibilityTestsTarget.addAGSettings() +} else { package.targets += [ openAttributeGraphTestsTarget, openAttributeGraphCxxTestsTarget, openAttributeGraphShimsTestsTarget, ] -} else { - openAttributeGraphCompatibilityTestsTarget.addAGSettings() } if buildForDarwinPlatform { package.targets.append(openAttributeGraphCompatibilityTestsTarget) } -let useLocalDeps = envBoolValue("USE_LOCAL_DEPS") - -let attributeGraphCondition = envBoolValue("ATTRIBUTEGRAPH", default: buildForDarwinPlatform && !isSPIBuild) - if attributeGraphCondition { let privateFrameworkRepo: Package.Dependency if useLocalDeps { @@ -335,8 +336,10 @@ if attributeGraphCondition { } package.dependencies.append(privateFrameworkRepo) openAttributeGraphShimsTarget.addAGSettings() - - let agVersion = Context.environment["DARWIN_PRIVATE_FRAMEWORKS_TARGET_RELEASE"].flatMap { Int($0) } ?? 2024 + + let agVersion = EnvManager.shared.withDomain("DARWIN_PRIVATE_FRAMEWORKS") { + envIntValue("TARGET_RELEASE", default: 2024) + } package.platforms = switch agVersion { case 2024: [.iOS(.v18), .macOS(.v15), .macCatalyst(.v18), .tvOS(.v18), .watchOS(.v10), .visionOS(.v2)] case 2021: [.iOS(.v15), .macOS(.v12), .macCatalyst(.v15), .tvOS(.v15), .watchOS(.v7)] @@ -346,10 +349,3 @@ if attributeGraphCondition { openAttributeGraphShimsTarget.dependencies.append("OpenAttributeGraph") package.platforms = [.iOS(.v13), .macOS(.v10_15), .macCatalyst(.v13), .tvOS(.v13), .watchOS(.v5)] } - - -extension [Platform] { - static var nonDarwinPlatforms: [Platform] { - [.linux, .android, .wasi, .openbsd, .windows] - } -} From 7a9d9d83f195789436e53c5a81daa5262f38e5ac Mon Sep 17 00:00:00 2001 From: Kyle Date: Mon, 3 Nov 2025 01:43:48 +0800 Subject: [PATCH 5/5] Update to sync source --- Package.swift | 91 ++++++++++++++++++++++++++++++++------------------- 1 file changed, 58 insertions(+), 33 deletions(-) diff --git a/Package.swift b/Package.swift index 2d2c0ba..44ff947 100644 --- a/Package.swift +++ b/Package.swift @@ -3,42 +3,69 @@ import Foundation import PackageDescription +/* GENERATED BY SPMManifestTool BEGIN */ +/* DO NOT EDIT */ + +public protocol EnvironmentProvider { + func value(forKey key: String) -> String? +} + +import PackageDescription +public struct PackageContextEnvironmentProvider: EnvironmentProvider { + public init() {} + + public func value(forKey key: String) -> String? { + Context.environment[key] + } +} + // MARK: - Env Manager -@MainActor -final class EnvManager { - static let shared = EnvManager() +public final class EnvManager { + nonisolated(unsafe) public static let shared = EnvManager() private var domains: [String] = [] + private var environmentProvider: EnvironmentProvider + + /// When true, append raw key as fallback when searching in domains + public var includeFallbackToRawKey: Bool = false - func register(domain: String) { + private init() { + self.environmentProvider = PackageContextEnvironmentProvider() + } + + /// Set a custom environment provider (useful for testing) + public func setEnvironmentProvider(_ provider: EnvironmentProvider) { + self.environmentProvider = provider + } + + /// Reset domains and environment provider (useful for testing) + public func reset() { + domains.removeAll() + includeFallbackToRawKey = false + self.environmentProvider = PackageContextEnvironmentProvider() + } + + public func register(domain: String) { domains.append(domain) } - func withDomain(_ domain: String, perform: () throws -> T) rethrows -> T { + public func withDomain(_ domain: String, perform: () throws -> T) rethrows -> T { domains.append(domain) defer { domains.removeAll { $0 == domain } } return try perform() } - private func envValue( - rawKey: String, - default defaultValue: T?, - searchInDomain: Bool, - parser: (String) -> T? - ) -> T? { + private func envValue(rawKey: String, default defaultValue: T?, searchInDomain: Bool, parser: (String) -> T?) -> T? { func parseEnvValue(_ key: String) -> (String, T)? { - guard let value = Context.environment[key] else { - return nil - } - guard let result = parser(value) else { - return nil - } + guard let value = environmentProvider.value(forKey: key), + let result = parser(value) else { return nil } return (value, result) } - let keys: [String] = searchInDomain - ? domains.map { "\($0.uppercased())_\(rawKey)" } - : [rawKey] + var keys: [String] = searchInDomain ? domains.map { "\($0.uppercased())_\(rawKey)" } : [] + if !searchInDomain || includeFallbackToRawKey { + keys.append(rawKey) + } for key in keys { if let (value, result) = parseEnvValue(key) { print("[Env] \(key)=\(value) -> \(result)") @@ -52,7 +79,7 @@ final class EnvManager { return defaultValue } - func envBoolValue(rawKey: String, default defaultValue: Bool? = nil, searchInDomain: Bool) -> Bool? { + public func envBoolValue(rawKey: String, default defaultValue: Bool? = nil, searchInDomain: Bool) -> Bool? { envValue(rawKey: rawKey, default: defaultValue, searchInDomain: searchInDomain) { value in switch value { case "1": true @@ -62,37 +89,35 @@ final class EnvManager { } } - func envIntValue(rawKey: String, default defaultValue: Int? = nil, searchInDomain: Bool) -> Int? { + public func envIntValue(rawKey: String, default defaultValue: Int? = nil, searchInDomain: Bool) -> Int? { envValue(rawKey: rawKey, default: defaultValue, searchInDomain: searchInDomain) { Int($0) } } - func envStringValue(rawKey: String, default defaultValue: String? = nil, searchInDomain: Bool) -> String? { + public func envStringValue(rawKey: String, default defaultValue: String? = nil, searchInDomain: Bool) -> String? { envValue(rawKey: rawKey, default: defaultValue, searchInDomain: searchInDomain) { $0 } } } -EnvManager.shared.register(domain: "OpenAttributeGraph") -EnvManager.shared.register(domain: "OpenSwiftUI") -@MainActor -func envBoolValue(_ key: String, default defaultValue: Bool = false, searchInDomain: Bool = true) -> Bool { +public func envBoolValue(_ key: String, default defaultValue: Bool = false, searchInDomain: Bool = true) -> Bool { EnvManager.shared.envBoolValue(rawKey: key, default: defaultValue, searchInDomain: searchInDomain)! } -@MainActor -func envIntValue(_ key: String, default defaultValue: Int = 0, searchInDomain: Bool = true) -> Int { +public func envIntValue(_ key: String, default defaultValue: Int = 0, searchInDomain: Bool = true) -> Int { EnvManager.shared.envIntValue(rawKey: key, default: defaultValue, searchInDomain: searchInDomain)! } -@MainActor -func envStringValue(_ key: String, default defaultValue: String, searchInDomain: Bool = true) -> String { +public func envStringValue(_ key: String, default defaultValue: String, searchInDomain: Bool = true) -> String { EnvManager.shared.envStringValue(rawKey: key, default: defaultValue, searchInDomain: searchInDomain)! } -@MainActor -func envStringValue(_ key: String, searchInDomain: Bool = true) -> String? { +public func envStringValue(_ key: String, searchInDomain: Bool = true) -> String? { EnvManager.shared.envStringValue(rawKey: key, searchInDomain: searchInDomain) } +/* GENERATED BY SPMManifestTool END */ +EnvManager.shared.register(domain: "OpenAttributeGraph") +EnvManager.shared.register(domain: "OpenSwiftUI") + // MARK: - Env and config #if os(macOS)