Skip to content

Commit 107a513

Browse files
authored
Optimize environment variable management with EnvManager (#11)
1 parent 7c8778e commit 107a513

File tree

2 files changed

+171
-53
lines changed

2 files changed

+171
-53
lines changed

Package.resolved

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Package.swift

Lines changed: 170 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -3,67 +3,200 @@
33
import Foundation
44
import PackageDescription
55

6-
func envEnable(_ key: String, default defaultValue: Bool = false) -> Bool {
7-
guard let value = Context.environment[key] else {
8-
return defaultValue
6+
// MARK: - Env Manager
7+
8+
/* GENERATED BY SPMManifestTool BEGIN */
9+
/* DO NOT EDIT */
10+
11+
public protocol EnvironmentProvider {
12+
func value(forKey key: String) -> String?
13+
}
14+
15+
import PackageDescription
16+
public struct PackageContextEnvironmentProvider: EnvironmentProvider {
17+
public init() {}
18+
19+
public func value(forKey key: String) -> String? {
20+
Context.environment[key]
921
}
10-
if value == "1" {
11-
return true
12-
} else if value == "0" {
13-
return false
14-
} else {
22+
}
23+
24+
// MARK: - Env Manager
25+
26+
public final class EnvManager {
27+
nonisolated(unsafe) public static let shared = EnvManager()
28+
29+
private var domains: [String] = []
30+
private var environmentProvider: EnvironmentProvider
31+
32+
/// When true, append raw key as fallback when searching in domains
33+
public var includeFallbackToRawKey: Bool = false
34+
35+
private init() {
36+
self.environmentProvider = PackageContextEnvironmentProvider()
37+
}
38+
39+
/// Set a custom environment provider (useful for testing)
40+
public func setEnvironmentProvider(_ provider: EnvironmentProvider) {
41+
self.environmentProvider = provider
42+
}
43+
44+
/// Reset domains and environment provider (useful for testing)
45+
public func reset() {
46+
domains.removeAll()
47+
includeFallbackToRawKey = false
48+
self.environmentProvider = PackageContextEnvironmentProvider()
49+
}
50+
51+
public func register(domain: String) {
52+
domains.append(domain)
53+
}
54+
55+
public func withDomain<T>(_ domain: String, perform: () throws -> T) rethrows -> T {
56+
domains.append(domain)
57+
defer { domains.removeAll { $0 == domain } }
58+
return try perform()
59+
}
60+
61+
private func envValue<T>(rawKey: String, default defaultValue: T?, searchInDomain: Bool, parser: (String) -> T?) -> T? {
62+
func parseEnvValue(_ key: String) -> (String, T)? {
63+
guard let value = environmentProvider.value(forKey: key),
64+
let result = parser(value) else { return nil }
65+
return (value, result)
66+
}
67+
var keys: [String] = searchInDomain ? domains.map { "\($0.uppercased())_\(rawKey)" } : []
68+
if !searchInDomain || includeFallbackToRawKey {
69+
keys.append(rawKey)
70+
}
71+
for key in keys {
72+
if let (value, result) = parseEnvValue(key) {
73+
print("[Env] \(key)=\(value) -> \(result)")
74+
return result
75+
}
76+
}
77+
let primaryKey = keys.first ?? rawKey
78+
if let defaultValue {
79+
print("[Env] \(primaryKey) not set -> \(defaultValue)(default)")
80+
}
1581
return defaultValue
1682
}
83+
84+
public func envBoolValue(rawKey: String, default defaultValue: Bool? = nil, searchInDomain: Bool) -> Bool? {
85+
envValue(rawKey: rawKey, default: defaultValue, searchInDomain: searchInDomain) { value in
86+
switch value {
87+
case "1": true
88+
case "0": false
89+
default: nil
90+
}
91+
}
92+
}
93+
94+
public func envIntValue(rawKey: String, default defaultValue: Int? = nil, searchInDomain: Bool) -> Int? {
95+
envValue(rawKey: rawKey, default: defaultValue, searchInDomain: searchInDomain) { Int($0) }
96+
}
97+
98+
public func envStringValue(rawKey: String, default defaultValue: String? = nil, searchInDomain: Bool) -> String? {
99+
envValue(rawKey: rawKey, default: defaultValue, searchInDomain: searchInDomain) { $0 }
100+
}
101+
}
102+
103+
public func envBoolValue(_ key: String, default defaultValue: Bool = false, searchInDomain: Bool = true) -> Bool {
104+
EnvManager.shared.envBoolValue(rawKey: key, default: defaultValue, searchInDomain: searchInDomain)!
105+
}
106+
107+
public func envIntValue(_ key: String, default defaultValue: Int = 0, searchInDomain: Bool = true) -> Int {
108+
EnvManager.shared.envIntValue(rawKey: key, default: defaultValue, searchInDomain: searchInDomain)!
109+
}
110+
111+
public func envStringValue(_ key: String, default defaultValue: String, searchInDomain: Bool = true) -> String {
112+
EnvManager.shared.envStringValue(rawKey: key, default: defaultValue, searchInDomain: searchInDomain)!
113+
}
114+
115+
public func envStringValue(_ key: String, searchInDomain: Bool = true) -> String? {
116+
EnvManager.shared.envStringValue(rawKey: key, searchInDomain: searchInDomain)
17117
}
18118

119+
/* GENERATED BY SPMManifestTool END */
120+
EnvManager.shared.register(domain: "OpenRenderBox")
121+
EnvManager.shared.register(domain: "OpenSwiftUI")
122+
123+
// MARK: - Env and config
124+
19125
#if os(macOS)
20126
// 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.
21-
let buildForDarwinPlatform = envEnable("OPENSWIFTUI_BUILD_FOR_DARWIN_PLATFORM", default: true)
127+
let buildForDarwinPlatform = envBoolValue("BUILD_FOR_DARWIN_PLATFORM", default: true)
22128
#else
23-
let buildForDarwinPlatform = envEnable("OPENSWIFTUI_BUILD_FOR_DARWIN_PLATFORM")
129+
let buildForDarwinPlatform = envBoolValue("BUILD_FOR_DARWIN_PLATFORM")
24130
#endif
25131

26132
// https://github.com/SwiftPackageIndex/SwiftPackageIndex-Server/issues/3061#issuecomment-2118821061
27133
// By-pass https://github.com/swiftlang/swift-package-manager/issues/7580
28-
let isSPIDocGenerationBuild = envEnable("SPI_GENERATE_DOCS", default: false)
29-
let isSPIBuild = envEnable("SPI_BUILD")
134+
let isSPIDocGenerationBuild = envBoolValue("SPI_GENERATE_DOCS", searchInDomain: false)
135+
let isSPIBuild = envBoolValue("SPI_BUILD", searchInDomain: false)
30136

31-
// MARK: - Env and Config
137+
let isXcodeEnv = envStringValue("__CFBundleIdentifier", searchInDomain: false) == "com.apple.dt.Xcode"
138+
let development = envBoolValue("DEVELOPMENT", default: false)
32139

33-
let isXcodeEnv = Context.environment["__CFBundleIdentifier"] == "com.apple.dt.Xcode"
34-
let development = envEnable("OPENRENDERBOX_DEVELOPMENT")
140+
let releaseVersion = envIntValue("TARGET_RELEASE", default: 2024)
141+
142+
let libSwiftPath = {
143+
// From Swift toolchain being installed or from Swift SDK.
144+
guard let libSwiftPath = envStringValue("LIB_SWIFT_PATH") else {
145+
// Fallback when LIB_SWIFT_PATH is not set
146+
let swiftBinPath = envStringValue("BIN_SWIFT_PATH") ?? envStringValue("_", searchInDomain: false) ?? "/usr/bin/swift"
147+
let swiftBinURL = URL(fileURLWithPath: swiftBinPath)
148+
let SDKPath = swiftBinURL.deletingLastPathComponent().deletingLastPathComponent().deletingLastPathComponent().path
149+
return SDKPath.appending("/usr/lib/swift")
150+
}
151+
return libSwiftPath
152+
}()
35153

36-
let releaseVersion = Context.environment["OPENRENDERBOX_TARGET_RELEASE"].flatMap { Int($0) } ?? 2024
154+
let libraryEvolutionCondition = envBoolValue("LIBRARY_EVOLUTION", default: buildForDarwinPlatform)
155+
let compatibilityTestCondition = envBoolValue("COMPATIBILITY_TEST", default: false)
37156

38-
let swiftBinPath = Context.environment["_"] ?? "/usr/bin/swift"
39-
let swiftBinURL = URL(fileURLWithPath: swiftBinPath)
40-
let SDKPath = swiftBinURL.deletingLastPathComponent().deletingLastPathComponent().deletingLastPathComponent().path
41-
let includePath = SDKPath.appending("/usr/lib/swift")
157+
let useLocalDeps = envBoolValue("USE_LOCAL_DEPS")
158+
let renderBoxCondtion = envBoolValue("RENDERBOX", default: buildForDarwinPlatform && !isSPIBuild)
42159

43-
let sharedCSettings: [CSetting] = [
44-
.unsafeFlags(["-I", includePath], .when(platforms: .nonDarwinPlatforms)),
160+
// MARK: - Shared Settings
161+
162+
var sharedCSettings: [CSetting] = [
163+
.unsafeFlags(["-I", libSwiftPath], .when(platforms: .nonDarwinPlatforms)),
45164
.unsafeFlags(["-fmodules"]),
46165
.define("__COREFOUNDATION_FORSWIFTFOUNDATIONONLY__", to: "1", .when(platforms: .nonDarwinPlatforms)),
47166
]
48-
let sharedCxxSettings: [CXXSetting] = [
49-
.unsafeFlags(["-I", includePath], .when(platforms: .nonDarwinPlatforms)),
167+
var sharedCxxSettings: [CXXSetting] = [
168+
.unsafeFlags(["-I", libSwiftPath], .when(platforms: .nonDarwinPlatforms)),
50169
.unsafeFlags(["-fcxx-modules"]),
51170
.define("__COREFOUNDATION_FORSWIFTFOUNDATIONONLY__", to: "1", .when(platforms: .nonDarwinPlatforms)),
52171
]
53172
var sharedSwiftSettings: [SwiftSetting] = [
54173
.swiftLanguageMode(.v5),
55174
]
56-
57-
// MARK: - [env] OPENRENDERBOX_LIBRARY_EVOLUTION
58-
59-
let libraryEvolutionCondition = envEnable("OPENRENDERBOX_LIBRARY_EVOLUTION", default: buildForDarwinPlatform)
60-
61175
if libraryEvolutionCondition {
62176
// NOTE: -enable-library-evolution will cause module verify failure for `swift build`.
63-
// Either set OPENRENDERBOX_LIBRARY_EVOLUTION=0 or add `-Xswiftc -no-verify-emitted-module-interface` after `swift build`
177+
// Either set LIBRARY_EVOLUTION=0 or add `-Xswiftc -no-verify-emitted-module-interface` after `swift build`
64178
sharedSwiftSettings.append(.unsafeFlags(["-enable-library-evolution", "-no-verify-emitted-module-interface"]))
65179
}
66180

181+
// MARK: - Extension
182+
183+
extension Target {
184+
func addRBSettings() {
185+
dependencies.append(
186+
.product(name: "RenderBox", package: "DarwinPrivateFrameworks")
187+
)
188+
var swiftSettings = swiftSettings ?? []
189+
swiftSettings.append(.define("OPENRENDERBOX_RENDERBOX"))
190+
self.swiftSettings = swiftSettings
191+
}
192+
}
193+
194+
extension [Platform] {
195+
static var nonDarwinPlatforms: [Platform] {
196+
[.linux, .android, .wasi, .openbsd, .windows]
197+
}
198+
}
199+
67200
// MARK: - Targets
68201

69202
let openRenderBoxTarget = Target.target(
@@ -96,7 +229,7 @@ let openRenderBoxCompatibilityTestTarget = Target.testTarget(
96229
// MARK: - Package
97230

98231
let libraryType: Product.Library.LibraryType?
99-
switch Context.environment["OPENRENDERBOX_LIBRARY_TYPE"] {
232+
switch envStringValue("LIBRARY_TYPE") {
100233
case "dynamic":
101234
libraryType = .dynamic
102235
case "static":
@@ -123,10 +256,6 @@ let package = Package(
123256
cxxLanguageStandard: .cxx20
124257
)
125258

126-
let useLocalDeps = envEnable("OPENRENDERBOX_USE_LOCAL_DEPS")
127-
128-
let renderBoxCondtion = envEnable("OPENRENDERBOX_RENDERBOX", default: buildForDarwinPlatform && !isSPIBuild )
129-
130259
if renderBoxCondtion {
131260
let privateFrameworkRepo: Package.Dependency
132261
if useLocalDeps {
@@ -135,13 +264,11 @@ if renderBoxCondtion {
135264
privateFrameworkRepo = Package.Dependency.package(url: "https://github.com/OpenSwiftUIProject/DarwinPrivateFrameworks.git", branch: "main")
136265
}
137266
package.dependencies.append(privateFrameworkRepo)
138-
var swiftSettings: [SwiftSetting] = (openRenderBoxShimsTarget.swiftSettings ?? [])
139-
swiftSettings.append(.define("OPENRENDERBOX_RENDERBOX"))
140-
openRenderBoxShimsTarget.swiftSettings = swiftSettings
141-
openRenderBoxShimsTarget.dependencies.append(
142-
.product(name: "RenderBox", package: "DarwinPrivateFrameworks")
143-
)
144-
let rbVersion = Context.environment["DARWIN_PRIVATE_FRAMEWORKS_TARGET_RELEASE"].flatMap { Int($0) } ?? 2024
267+
openRenderBoxShimsTarget.addRBSettings()
268+
269+
let rbVersion = EnvManager.shared.withDomain("DARWIN_PRIVATE_FRAMEWORKS") {
270+
envIntValue("TARGET_RELEASE", default: 2024)
271+
}
145272
package.platforms = switch rbVersion {
146273
case 2024: [.iOS(.v18), .macOS(.v15), .macCatalyst(.v18), .tvOS(.v18), .watchOS(.v10), .visionOS(.v2)]
147274
case 2021: [.iOS(.v15), .macOS(.v12), .macCatalyst(.v15), .tvOS(.v15), .watchOS(.v7)]
@@ -151,20 +278,11 @@ if renderBoxCondtion {
151278
openRenderBoxShimsTarget.dependencies.append("OpenRenderBox")
152279
}
153280

154-
let compatibilityTestCondition = envEnable("OPENRENDERBOX_COMPATIBILITY_TEST")
155281
if compatibilityTestCondition && renderBoxCondtion {
156-
openRenderBoxCompatibilityTestTarget.dependencies.append(
157-
.product(name: "RenderBox", package: "DarwinPrivateFrameworks")
158-
)
282+
openRenderBoxCompatibilityTestTarget.addRBSettings()
159283
var swiftSettings: [SwiftSetting] = (openRenderBoxCompatibilityTestTarget.swiftSettings ?? [])
160284
swiftSettings.append(.define("OPENRENDERBOX_COMPATIBILITY_TEST"))
161285
openRenderBoxCompatibilityTestTarget.swiftSettings = swiftSettings
162286
} else {
163287
openRenderBoxCompatibilityTestTarget.dependencies.append("OpenRenderBox")
164288
}
165-
166-
extension [Platform] {
167-
static var nonDarwinPlatforms: [Platform] {
168-
[.linux, .android, .wasi, .openbsd, .windows]
169-
}
170-
}

0 commit comments

Comments
 (0)