Skip to content

Commit 701ea17

Browse files
authored
Optimize environment variable management with EnvManager (#194)
1 parent 21426f2 commit 701ea17

File tree

2 files changed

+168
-83
lines changed

2 files changed

+168
-83
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: 167 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,166 @@
11
// swift-tools-version: 6.1
2-
// The swift-tools-version declares the minimum version of Swift required to build this package.
32

43
import Foundation
54
import PackageDescription
65

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

117+
/* GENERATED BY SPMManifestTool END */
118+
EnvManager.shared.register(domain: "OpenAttributeGraph")
119+
EnvManager.shared.register(domain: "OpenSwiftUI")
120+
121+
// MARK: - Env and config
122+
20123
#if os(macOS)
21124
// 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.
22-
let buildForDarwinPlatform = envEnable("OPENSWIFTUI_BUILD_FOR_DARWIN_PLATFORM", default: true)
125+
let buildForDarwinPlatform = envBoolValue("BUILD_FOR_DARWIN_PLATFORM", default: true)
23126
#else
24-
let buildForDarwinPlatform = envEnable("OPENSWIFTUI_BUILD_FOR_DARWIN_PLATFORM")
127+
let buildForDarwinPlatform = envBoolValue("BUILD_FOR_DARWIN_PLATFORM")
25128
#endif
26129

27-
28130
// https://github.com/SwiftPackageIndex/SwiftPackageIndex-Server/issues/3061#issuecomment-2118821061
29131
// By-pass https://github.com/swiftlang/swift-package-manager/issues/7580
30-
let isSPIDocGenerationBuild = envEnable("SPI_GENERATE_DOCS", default: false)
31-
let isSPIBuild = envEnable("SPI_BUILD")
32-
33-
// MARK: - Env and Config
132+
let isSPIDocGenerationBuild = envBoolValue("SPI_GENERATE_DOCS", searchInDomain: false)
133+
let isSPIBuild = envBoolValue("SPI_BUILD", searchInDomain: false)
34134

35-
let isXcodeEnv = Context.environment["__CFBundleIdentifier"] == "com.apple.dt.Xcode"
36-
let development = envEnable("OPENATTRIBUTEGRAPH_DEVELOPMENT", default: false)
135+
let isXcodeEnv = envStringValue("__CFBundleIdentifier", searchInDomain: false) == "com.apple.dt.Xcode"
136+
let development = envBoolValue("DEVELOPMENT", default: false)
137+
let warningsAsErrorsCondition = envBoolValue("WERROR", default: isXcodeEnv && development)
37138

38-
// From Swift toolchain being installed or from Swift SDK.
39-
func detectLibSwiftPath() -> String {
40-
guard let libSwiftPath = Context.environment["OPENATTRIBUTEGRAPH_LIB_SWIFT_PATH"] else {
139+
let libSwiftPath = {
140+
// From Swift toolchain being installed or from Swift SDK.
141+
guard let libSwiftPath = envStringValue("LIB_SWIFT_PATH") else {
41142
// Fallback when LIB_SWIFT_PATH is not set
42-
let swiftBinPath = Context.environment["OPENATTRIBUTEGRAPH_BIN_SWIFT_PATH"] ?? Context.environment["_"] ?? "/usr/bin/swift"
143+
let swiftBinPath = envStringValue("BIN_SWIFT_PATH") ?? envStringValue("_", searchInDomain: false) ?? "/usr/bin/swift"
43144
let swiftBinURL = URL(fileURLWithPath: swiftBinPath)
44145
let SDKPath = swiftBinURL.deletingLastPathComponent().deletingLastPathComponent().deletingLastPathComponent().path
45146
return SDKPath.appending("/usr/lib/swift")
46147
}
47148
return libSwiftPath
48-
}
49-
let libSwiftPath = detectLibSwiftPath()
149+
}()
150+
151+
let swiftToolchainPath = envStringValue("SWIFT_TOOLCHAIN_PATH") ?? (development ? "/Volumes/BuildMachine/swift-project" : "")
152+
let swiftToolchainVersion = envStringValue("SWIFT_TOOLCHAIN_VERSION") ?? (development ? "6.0.2" : "")
153+
let swiftToolchainSupported = envBoolValue("SWIFT_TOOLCHAIN_SUPPORTED", default: !swiftToolchainVersion.isEmpty)
154+
155+
let releaseVersion = envIntValue("TARGET_RELEASE", default: 2024)
156+
157+
let libraryEvolutionCondition = envBoolValue("LIBRARY_EVOLUTION", default: buildForDarwinPlatform)
158+
let compatibilityTestCondition = envBoolValue("COMPATIBILITY_TEST", default: false)
159+
160+
let useLocalDeps = envBoolValue("USE_LOCAL_DEPS")
161+
let attributeGraphCondition = envBoolValue("ATTRIBUTEGRAPH", default: buildForDarwinPlatform && !isSPIBuild)
162+
163+
// MARK: - Shared Settings
50164

51165
var sharedCSettings: [CSetting] = [
52166
.unsafeFlags(["-I", libSwiftPath], .when(platforms: .nonDarwinPlatforms)),
@@ -57,9 +171,9 @@ var sharedSwiftSettings: [SwiftSetting] = [
57171
.enableUpcomingFeature("InternalImportsByDefault"),
58172
.enableExperimentalFeature("Extern"),
59173
.swiftLanguageMode(.v5),
60-
]
61174

62-
// MARK: [env] OPENATTRIBUTEGRAPH_SWIFT_TOOLCHAIN_PATH
175+
.define("OPENATTRIBUTEGRAPH_RELEASE_\(releaseVersion)"),
176+
]
63177

64178
// Modified from: https://github.com/swiftlang/swift/blob/main/SwiftCompilerSources/Package.swift
65179
//
@@ -74,7 +188,6 @@ var sharedSwiftSettings: [SwiftSetting] = [
74188
//
75189
// where <$OPENATTRIBUTEGRAPH_SWIFT_TOOLCHAIN_PATH> is the parent directory of the swift repository.
76190

77-
let swiftToolchainPath = Context.environment["OPENATTRIBUTEGRAPH_SWIFT_TOOLCHAIN_PATH"] ?? (development ? "/Volumes/BuildMachine/swift-project" : "")
78191
if !swiftToolchainPath.isEmpty {
79192
sharedCSettings.append(
80193
.unsafeFlags(
@@ -95,60 +208,52 @@ if !swiftToolchainPath.isEmpty {
95208
)
96209
)
97210
}
98-
99-
// MARK: [env] OPENATTRIBUTEGRAPH_SWIFT_TOOLCHAIN_VERSION
100-
101-
let swiftToolchainVersion = Context.environment["OPENATTRIBUTEGRAPH_SWIFT_TOOLCHAIN_VERSION"] ?? (development ? "6.0.2" : "")
102211
if !swiftToolchainVersion.isEmpty {
103212
sharedCSettings.append(
104213
.define("OPENATTRIBUTEGRAPH_SWIFT_TOOLCHAIN_VERSION", to: swiftToolchainVersion)
105214
)
106215
}
107-
108-
// MARK: - [env] OPENATTRIBUTEGRAPH_SWIFT_TOOLCHAIN_SUPPORTED
109-
110-
let swiftToolchainSupported = envEnable("OPENATTRIBUTEGRAPH_SWIFT_TOOLCHAIN_SUPPORTED", default: !swiftToolchainVersion.isEmpty)
111216
if swiftToolchainSupported {
112217
sharedCSettings.append(.define("OPENATTRIBUTEGRAPH_SWIFT_TOOLCHAIN_SUPPORTED"))
113218
sharedSwiftSettings.append(.define("OPENATTRIBUTEGRAPH_SWIFT_TOOLCHAIN_SUPPORTED"))
114219
}
115-
116-
// MARK: - [env] OPENATTRIBUTEGRAPH_TARGET_RELEASE
117-
118-
let releaseVersion = Context.environment["OPENATTRIBUTEGRAPH_TARGET_RELEASE"].flatMap { Int($0) } ?? 2024
119-
//sharedCSettings.append(.define("OPENATTRIBUTEGRAPH_RELEASE", to: "\(releaseVersion)"))
120-
sharedSwiftSettings.append(.define("OPENATTRIBUTEGRAPH_RELEASE_\(releaseVersion)"))
121220
if releaseVersion >= 2021 {
122221
for year in 2021 ... releaseVersion {
123222
sharedSwiftSettings.append(.define("OPENATTRIBUTEGRAPH_SUPPORT_\(year)_API"))
124223
}
125224
}
126-
127-
// MARK: - [env] OPENATTRIBUTEGRAPH_WERROR
128-
129-
let warningsAsErrorsCondition = envEnable("OPENATTRIBUTEGRAPH_WERROR", default: isXcodeEnv && development)
130225
if warningsAsErrorsCondition {
131226
sharedSwiftSettings.append(.unsafeFlags(["-warnings-as-errors"]))
132227
}
133-
134-
// MARK: - [env] OPENATTRIBUTEGRAPH_LIBRARY_EVOLUTION
135-
136-
let libraryEvolutionCondition = envEnable("OPENATTRIBUTEGRAPH_LIBRARY_EVOLUTION", default: buildForDarwinPlatform)
137-
138228
if libraryEvolutionCondition {
139229
// NOTE: -enable-library-evolution will cause module verify failure for `swift build`.
140230
// Either set OPENATTRIBUTEGRAPH_LIBRARY_EVOLUTION=0 or add `-Xswiftc -no-verify-emitted-module-interface` after `swift build`
141231
sharedSwiftSettings.append(.unsafeFlags(["-enable-library-evolution", "-no-verify-emitted-module-interface"]))
142232
}
143-
144-
// MARK: - [env] OPENATTRIBUTEGRAPH_COMPATIBILITY_TEST
145-
146-
let compatibilityTestCondition = envEnable("OPENATTRIBUTEGRAPH_COMPATIBILITY_TEST", default: false)
147-
sharedCSettings.append(.define("OPENATTRIBUTEGRAPH", to: compatibilityTestCondition ? "1" : "0"))
148233
if !compatibilityTestCondition {
234+
sharedCSettings.append(.define("OPENATTRIBUTEGRAPH"))
149235
sharedSwiftSettings.append(.define("OPENATTRIBUTEGRAPH"))
150236
}
151237

238+
// MARK: - Extension
239+
240+
extension Target {
241+
func addAGSettings() {
242+
dependencies.append(
243+
.product(name: "AttributeGraph", package: "DarwinPrivateFrameworks")
244+
)
245+
var swiftSettings = swiftSettings ?? []
246+
swiftSettings.append(.define("OPENATTRIBUTEGRAPH_ATTRIBUTEGRAPH"))
247+
self.swiftSettings = swiftSettings
248+
}
249+
}
250+
251+
extension [Platform] {
252+
static var nonDarwinPlatforms: [Platform] {
253+
[.linux, .android, .wasi, .openbsd, .windows]
254+
}
255+
}
256+
152257
// MARK: - Targets
153258

154259
let openAttributeGraphTarget = Target.target(
@@ -233,35 +338,20 @@ let package = Package(
233338
cxxLanguageStandard: .cxx20
234339
)
235340

236-
extension Target {
237-
func addAGSettings() {
238-
dependencies.append(
239-
.product(name: "AttributeGraph", package: "DarwinPrivateFrameworks")
240-
)
241-
var swiftSettings = swiftSettings ?? []
242-
swiftSettings.append(.define("OPENATTRIBUTEGRAPH_ATTRIBUTEGRAPH"))
243-
self.swiftSettings = swiftSettings
244-
}
245-
}
246-
247-
if !compatibilityTestCondition {
341+
if compatibilityTestCondition {
342+
openAttributeGraphCompatibilityTestsTarget.addAGSettings()
343+
} else {
248344
package.targets += [
249345
openAttributeGraphTestsTarget,
250346
openAttributeGraphCxxTestsTarget,
251347
openAttributeGraphShimsTestsTarget,
252348
]
253-
} else {
254-
openAttributeGraphCompatibilityTestsTarget.addAGSettings()
255349
}
256350

257351
if buildForDarwinPlatform {
258352
package.targets.append(openAttributeGraphCompatibilityTestsTarget)
259353
}
260354

261-
let useLocalDeps = envEnable("OPENATTRIBUTEGRAPH_USE_LOCAL_DEPS")
262-
263-
let attributeGraphCondition = envEnable("OPENATTRIBUTEGRAPH_ATTRIBUTEGRAPH", default: buildForDarwinPlatform && !isSPIBuild)
264-
265355
if attributeGraphCondition {
266356
let privateFrameworkRepo: Package.Dependency
267357
if useLocalDeps {
@@ -271,8 +361,10 @@ if attributeGraphCondition {
271361
}
272362
package.dependencies.append(privateFrameworkRepo)
273363
openAttributeGraphShimsTarget.addAGSettings()
274-
275-
let agVersion = Context.environment["DARWIN_PRIVATE_FRAMEWORKS_TARGET_RELEASE"].flatMap { Int($0) } ?? 2024
364+
365+
let agVersion = EnvManager.shared.withDomain("DARWIN_PRIVATE_FRAMEWORKS") {
366+
envIntValue("TARGET_RELEASE", default: 2024)
367+
}
276368
package.platforms = switch agVersion {
277369
case 2024: [.iOS(.v18), .macOS(.v15), .macCatalyst(.v18), .tvOS(.v18), .watchOS(.v10), .visionOS(.v2)]
278370
case 2021: [.iOS(.v15), .macOS(.v12), .macCatalyst(.v15), .tvOS(.v15), .watchOS(.v7)]
@@ -282,10 +374,3 @@ if attributeGraphCondition {
282374
openAttributeGraphShimsTarget.dependencies.append("OpenAttributeGraph")
283375
package.platforms = [.iOS(.v13), .macOS(.v10_15), .macCatalyst(.v13), .tvOS(.v13), .watchOS(.v5)]
284376
}
285-
286-
287-
extension [Platform] {
288-
static var nonDarwinPlatforms: [Platform] {
289-
[.linux, .android, .wasi, .openbsd, .windows]
290-
}
291-
}

0 commit comments

Comments
 (0)