From c5f547af9028c999bc5aee185d647c2e4d087334 Mon Sep 17 00:00:00 2001 From: Owen Voorhees Date: Tue, 30 Sep 2025 10:34:49 -0700 Subject: [PATCH] Improve project model build settings API for platform-conditional settings --- .../ProjectModel/BuildSettings.swift | 53 ++++++++++++++++++- .../ProjectModel/BuildSettingsTests.swift | 2 + 2 files changed, 53 insertions(+), 2 deletions(-) diff --git a/Sources/SwiftBuild/ProjectModel/BuildSettings.swift b/Sources/SwiftBuild/ProjectModel/BuildSettings.swift index 2762166d..edcd8bac 100644 --- a/Sources/SwiftBuild/ProjectModel/BuildSettings.swift +++ b/Sources/SwiftBuild/ProjectModel/BuildSettings.swift @@ -137,6 +137,7 @@ extension ProjectModel { case SWIFT_WARNINGS_AS_ERRORS_GROUPS } + @available(*, deprecated, message: "Use subscripts to set platform-specific SingleValueSetting/MultipleValueSettings instead") public enum Declaration: String, Hashable, CaseIterable, Sendable { case ARCHS case GCC_PREPROCESSOR_DEFINITIONS @@ -186,8 +187,6 @@ extension ProjectModel { } } - public var platformSpecificSettings = [Platform: [Declaration: [String]]]() - public init() { var settings: [Declaration: [String]] = [:] for declaration in Declaration.allCases { @@ -201,6 +200,13 @@ extension ProjectModel { private(set) var singleValueSettings: OrderedDictionary = [:] private(set) var multipleValueSettings: OrderedDictionary = [:] + private(set) var singleValuePlatformSpecificSettings = [Platform: OrderedDictionary]() + private(set) var multipleValuePlatformSpecificSettings = [Platform: OrderedDictionary]() + + // Kept for API compatibility + @available(*, deprecated, message: "Use subscripts to set platform-specific settings instead") + public var platformSpecificSettings = [Platform: [Declaration: [String]]]() + public subscript(_ setting: SingleValueSetting) -> String? { get { singleValueSettings[setting.rawValue] } @@ -221,6 +227,16 @@ extension ProjectModel { get { multipleValueSettings[setting] } set { multipleValueSettings[setting] = newValue } } + + public subscript(_ setting: SingleValueSetting, platform: Platform) -> String? { + get { singleValuePlatformSpecificSettings[platform]?[setting.rawValue] } + set { singleValuePlatformSpecificSettings[platform, default: .init()][setting.rawValue] = newValue } + } + + public subscript(_ setting: MultipleValueSetting, platform: Platform) -> [String]? { + get { multipleValuePlatformSpecificSettings[platform]?[setting.rawValue] } + set { multipleValuePlatformSpecificSettings[platform, default: .init()][setting.rawValue] = newValue } + } } } @@ -325,6 +341,23 @@ extension ProjectModel.BuildSettings: Codable { self.platformSpecificSettings[platform, default: [:]][declaration] = value } } + let declarationValues = Set(Declaration.allCases.map(\.rawValue)) + for key in SingleValueSetting.allCases { + if declarationValues.contains(key.rawValue) { + continue + } + if let value = try container.decodeIfPresent(String.self, forKey: StringKey("\(key.rawValue)[\(condition)]")) { + self[key, platform] = value + } + } + for key in MultipleValueSetting.allCases { + if declarationValues.contains(key.rawValue) { + continue + } + if let value = try container.decodeIfPresent([String].self, forKey: StringKey("\(key.rawValue)[\(condition)]")) { + self[key, platform] = value + } + } } } } @@ -351,5 +384,21 @@ extension ProjectModel.BuildSettings: Codable { } } } + + for (platform, table) in singleValuePlatformSpecificSettings { + for condition in platform.asConditionStrings { + for (key, value) in table { + try container.encode(value, forKey: StringKey("\(key)[\(condition)]")) + } + } + } + + for (platform, table) in multipleValuePlatformSpecificSettings { + for condition in platform.asConditionStrings { + for (key, value) in table { + try container.encode(value, forKey: StringKey("\(key)[\(condition)]")) + } + } + } } } diff --git a/Tests/SwiftBuildTests/ProjectModel/BuildSettingsTests.swift b/Tests/SwiftBuildTests/ProjectModel/BuildSettingsTests.swift index 51e06a8f..de44edfb 100644 --- a/Tests/SwiftBuildTests/ProjectModel/BuildSettingsTests.swift +++ b/Tests/SwiftBuildTests/ProjectModel/BuildSettingsTests.swift @@ -25,6 +25,8 @@ fileprivate struct BuildSettingsTests { try testCodable(obj) { $0[.BUILT_PRODUCTS_DIR] = "/tmp" } try testCodable(obj) { $0[.HEADER_SEARCH_PATHS] = ["/foo", "/bar"] } try testCodable(obj) { $0.platformSpecificSettings[.macOS, default: [:]][.FRAMEWORK_SEARCH_PATHS] = ["/baz", "/qux"] } + try testCodable(obj) { $0[.CLANG_ENABLE_MODULES, .macOS] = "NO" } + try testCodable(obj) { $0[.SWIFT_MODULE_ALIASES, .macOS] = ["A=B", "C=D"] } } @Test func unknownBuildSettings() throws {