Skip to content

Commit c0ab2f9

Browse files
committed
BuildSettingCondition should offer a Mac Catalyst condition
rdar://60376383
1 parent fee268f commit c0ab2f9

File tree

13 files changed

+158
-35
lines changed

13 files changed

+158
-35
lines changed

Sources/PackageCollections/Providers/JSONPackageCollectionProvider.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,8 @@ extension PackageModel.Platform {
426426
switch name.lowercased() {
427427
case let name where name.contains("macos"):
428428
self = PackageModel.Platform.macOS
429+
case let name where name.contains("maccatalyst"):
430+
self = PackageModel.Platform.macCatalyst
429431
case let name where name.contains("ios"):
430432
self = PackageModel.Platform.iOS
431433
case let name where name.contains("tvos"):

Sources/PackageDescription/SupportedPlatforms.swift

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ public struct Platform: Encodable, Equatable {
2222
/// The macOS platform.
2323
public static let macOS: Platform = Platform(name: "macos")
2424

25+
/// The Mac Catalyst platform.
26+
public static let macCatalyst: Platform = Platform(name: "maccatalyst")
27+
2528
/// The iOS platform.
2629
public static let iOS: Platform = Platform(name: "ios")
2730

@@ -102,6 +105,29 @@ public struct SupportedPlatform: Encodable, Equatable {
102105
return SupportedPlatform(platform: .macOS, version: SupportedPlatform.MacOSVersion(string: versionString).version)
103106
}
104107

108+
/// Configures the minimum deployment target version for the Mac Catalyst platform.
109+
///
110+
/// - Since: First available in PackageDescription 5.5
111+
///
112+
/// - Parameter version: The minimum deployment target that the package supports.
113+
@available(_PackageDescription, introduced: 5.5)
114+
public static func macCatalyst(_ version: SupportedPlatform.MacCatalystVersion) -> SupportedPlatform {
115+
return SupportedPlatform(platform: .macCatalyst, version: version.version)
116+
}
117+
118+
/// Configures the minimum deployment target version for the Mac Catalyst platform
119+
/// using a version string.
120+
///
121+
/// The version string must be a series of two or three dot-separated integers, such as `13.0` or `13.0.1`.
122+
///
123+
/// - Since: First available in PackageDescription 5.5
124+
///
125+
/// - Parameter versionString: The minimum deployment target as a string representation of two or three dot-separated integers, such as `13.0.1`.
126+
@available(_PackageDescription, introduced: 5.5)
127+
public static func macCatalyst(_ versionString: String) -> SupportedPlatform {
128+
return SupportedPlatform(platform: .macCatalyst, version: SupportedPlatform.MacCatalystVersion(string: versionString).version)
129+
}
130+
105131
/// Configures the minimum deployment target version for the iOS platform.
106132
///
107133
/// - Since: First available in PackageDescription 5.0
@@ -290,6 +316,31 @@ extension SupportedPlatform {
290316
public static let v14: TVOSVersion = .init(string: "14.0")
291317
}
292318

319+
/// The supported Mac Catalyst version.
320+
public struct MacCatalystVersion: Encodable, AppleOSVersion {
321+
fileprivate static let name = "macCatalyst"
322+
fileprivate static let minimumMajorVersion = 13
323+
324+
/// The underlying version representation.
325+
let version: String
326+
327+
fileprivate init(uncheckedVersion version: String) {
328+
self.version = version
329+
}
330+
331+
/// The value that represents Mac Catalyst 13.0.
332+
///
333+
/// - Since: First available in PackageDescription 5.5
334+
@available(_PackageDescription, introduced: 5.5)
335+
public static let v13: MacCatalystVersion = .init(string: "13.0")
336+
337+
/// The value that represents Mac Catalyst 14.0.
338+
///
339+
/// - Since: First available in PackageDescription 5.5
340+
@available(_PackageDescription, introduced: 5.5)
341+
public static let v14: MacCatalystVersion = .init(string: "14.0")
342+
}
343+
293344
/// The supported iOS version.
294345
public struct IOSVersion: Encodable, AppleOSVersion {
295346
fileprivate static let name = "iOS"

Sources/PackageLoading/ManifestLoader.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -772,7 +772,7 @@ public final class ManifestLoader: ManifestLoaderProtocol {
772772
}
773773

774774
let version = try Self._packageDescriptionMinimumDeploymentTarget.memoize {
775-
(try MinimumDeploymentTarget.computeMinimumDeploymentTarget(of: macOSPackageDescriptionPath))?.versionString ?? "10.15"
775+
(try MinimumDeploymentTarget.computeMinimumDeploymentTarget(of: macOSPackageDescriptionPath, platform: .macOS))?.versionString ?? "10.15"
776776
}
777777
cmd += ["-target", "\(triple.tripleString(forPlatformVersion: version))"]
778778
#endif

Sources/PackageLoading/MinimumDeploymentTarget.swift

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -24,21 +24,31 @@ public struct MinimumDeploymentTarget {
2424
}
2525
}
2626

27-
static func computeMinimumDeploymentTarget(of binaryPath: AbsolutePath) throws -> PlatformVersion? {
27+
static func computeMinimumDeploymentTarget(of binaryPath: AbsolutePath, platform: PackageModel.Platform) throws -> PlatformVersion? {
28+
guard let (_, platformName) = platform.sdkNameAndPlatform else {
29+
return nil
30+
}
31+
2832
let runResult = try Process.popen(arguments: ["/usr/bin/xcrun", "vtool", "-show-build", binaryPath.pathString])
29-
guard let versionString = try runResult.utf8Output().components(separatedBy: "\n").first(where: { $0.contains("minos") })?.components(separatedBy: " ").last else { return nil }
30-
return PlatformVersion(versionString)
33+
var lines = try runResult.utf8Output().components(separatedBy: "\n")
34+
while !lines.isEmpty {
35+
let first = lines.removeFirst()
36+
if first.contains("platform \(platformName)"), let line = lines.first, line.contains("minos") {
37+
return line.components(separatedBy: " ").last.map(PlatformVersion.init(stringLiteral:))
38+
}
39+
}
40+
return nil
3141
}
3242

33-
static func computeXCTestMinimumDeploymentTarget(with runResult: ProcessResult) throws -> PlatformVersion? {
43+
static func computeXCTestMinimumDeploymentTarget(with runResult: ProcessResult, platform: PackageModel.Platform) throws -> PlatformVersion? {
3444
guard let output = try runResult.utf8Output().spm_chuzzle() else { return nil }
3545
let sdkPath = try AbsolutePath(validating: output)
3646
let xcTestPath = sdkPath.appending(RelativePath("Developer/Library/Frameworks/XCTest.framework/XCTest"))
37-
return try computeMinimumDeploymentTarget(of: xcTestPath)
47+
return try computeMinimumDeploymentTarget(of: xcTestPath, platform: platform)
3848
}
3949

4050
static func computeXCTestMinimumDeploymentTarget(for platform: PackageModel.Platform) -> PlatformVersion {
41-
guard let sdkName = platform.sdkName else {
51+
guard let (sdkName, _) = platform.sdkNameAndPlatform else {
4252
return platform.oldestSupportedVersion
4353
}
4454

@@ -47,7 +57,7 @@ public struct MinimumDeploymentTarget {
4757
do {
4858
let runResult = try Process.popen(arguments: ["/usr/bin/xcrun", "--sdk", sdkName, "--show-sdk-platform-path"])
4959

50-
if let version = try computeXCTestMinimumDeploymentTarget(with: runResult) {
60+
if let version = try computeXCTestMinimumDeploymentTarget(with: runResult, platform: platform) {
5161
return version
5262
}
5363
} catch { } // we do not treat this a fatal and instead use the fallback minimum deployment target
@@ -58,16 +68,18 @@ public struct MinimumDeploymentTarget {
5868
}
5969

6070
private extension PackageModel.Platform {
61-
var sdkName: String? {
71+
var sdkNameAndPlatform: (String, String)? {
6272
switch self {
6373
case .macOS:
64-
return "macosx"
74+
return ("macosx", "MACOS")
75+
case .macCatalyst:
76+
return ("macosx", "MACCATALYST")
6577
case .iOS:
66-
return "iphoneos"
78+
return ("iphoneos", "IOS")
6779
case .tvOS:
68-
return "appletvos"
80+
return ("appletvos", "TVOS")
6981
case .watchOS:
70-
return "watchos"
82+
return ("watchos", "WATCHOS")
7183
case .driverKit:
7284
return nil // DriverKit does not support XCTest.
7385
default:

Sources/PackageLoading/PlatformRegistry.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,6 @@ public final class PlatformRegistry {
3131

3232
/// The static list of known platforms.
3333
private static var _knownPlatforms: [Platform] {
34-
return [.macOS, .iOS, .tvOS, .watchOS, .linux, .windows, .android, .wasi, .driverKit]
34+
return [.macOS, .macCatalyst, .iOS, .tvOS, .watchOS, .linux, .windows, .android, .wasi, .driverKit]
3535
}
3636
}

Sources/PackageModel/ManifestSourceGeneration.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,8 @@ fileprivate extension SourceCodeFragment {
104104
switch platform.platformName {
105105
case "macos":
106106
self.init(enum: "macOS", string: platform.version)
107+
case "maccatalyst":
108+
self.init(enum: "macCatalyst", string: platform.version)
107109
case "ios":
108110
self.init(enum: "iOS", string: platform.version)
109111
case "tvos":
@@ -298,6 +300,7 @@ fileprivate extension SourceCodeFragment {
298300
let platformNodes: [SourceCodeFragment] = condition.platformNames.map { platformName in
299301
switch platformName {
300302
case "macos": return SourceCodeFragment(enum: "macOS")
303+
case "maccatalyst": return SourceCodeFragment(enum: "macCatalyst")
301304
case "ios": return SourceCodeFragment(enum: "iOS")
302305
case "tvos": return SourceCodeFragment(enum: "tvOS")
303306
case "watchos": return SourceCodeFragment(enum: "watchOS")

Sources/PackageModel/Platform.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ public struct Platform: Equatable, Hashable, Codable {
2626
}
2727

2828
public static let macOS: Platform = Platform(name: "macos", oldestSupportedVersion: "10.10")
29+
public static let macCatalyst: Platform = Platform(name: "maccatalyst", oldestSupportedVersion: "13.0")
2930
public static let iOS: Platform = Platform(name: "ios", oldestSupportedVersion: "9.0")
3031
public static let tvOS: Platform = Platform(name: "tvos", oldestSupportedVersion: "9.0")
3132
public static let watchOS: Platform = Platform(name: "watchos", oldestSupportedVersion: "2.0")

Sources/SPMTestSupport/PIFTester.swift

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,12 @@ public final class PIFBuildSettingsTester {
265265
Array(buildSettings.multipleValueSettings.keys.map { $0.rawValue })
266266
XCTAssert(uncheckedKeys.isEmpty, "settings are left unchecked: \(uncheckedKeys)", file: file, line: line)
267267

268-
for (platform, settings) in buildSettings.platformSpecificSettings {
268+
for (platform, settings) in buildSettings.platformSpecificSingleValueSettings {
269+
let uncheckedKeys = Array(settings.keys.map { $0.rawValue })
270+
XCTAssert(uncheckedKeys.isEmpty, "\(platform) settings are left unchecked: \(uncheckedKeys)", file: file, line: line)
271+
}
272+
273+
for (platform, settings) in buildSettings.platformSpecificMultipleValueSettings {
269274
let uncheckedKeys = Array(settings.keys.map { $0.rawValue })
270275
XCTAssert(uncheckedKeys.isEmpty, "\(platform) settings are left unchecked: \(uncheckedKeys)", file: file, line: line)
271276
}

Sources/Workspace/InitPackage.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -459,6 +459,8 @@ extension PackageModel.Platform {
459459
switch self {
460460
case .macOS:
461461
return "macOS"
462+
case .macCatalyst:
463+
return "macCatalyst"
462464
case .iOS:
463465
return "iOS"
464466
case .tvOS:
@@ -479,7 +481,7 @@ extension SupportedPlatform {
479481
guard self.version.patch == 0 else {
480482
return false
481483
}
482-
} else if [Platform.macOS, .iOS, .watchOS, .tvOS, .driverKit].contains(platform) {
484+
} else if [Platform.macOS, .macCatalyst, .iOS, .watchOS, .tvOS, .driverKit].contains(platform) {
483485
guard self.version.minor == 0, self.version.patch == 0 else {
484486
return false
485487
}
@@ -492,6 +494,8 @@ extension SupportedPlatform {
492494
return (10...15).contains(version.minor)
493495
case .macOS:
494496
return (11...11).contains(version.major)
497+
case .macCatalyst:
498+
return (13...14).contains(version.major)
495499
case .iOS:
496500
return (8...14).contains(version.major)
497501
case .tvOS:

Sources/XCBuildSupport/PIF.swift

Lines changed: 46 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import Basics
1212
import Foundation
1313
import TSCBasic
14+
import PackageModel
1415

1516
/// The Project Interchange Format (PIF) is a structured representation of the
1617
/// project model created by clients (Xcode/SwiftPM) to send to XCBuild.
@@ -957,25 +958,39 @@ public enum PIF {
957958

958959
public enum Platform: String, CaseIterable, Codable {
959960
case macOS = "macos"
961+
case macCatalyst = "maccatalyst"
960962
case iOS = "ios"
961963
case tvOS = "tvos"
962964
case watchOS = "watchos"
963965
case driverKit = "driverkit"
964966
case linux
965967

966-
public var conditions: [String] {
968+
public var packageModelPlatform: PackageModel.Platform {
967969
switch self {
968-
case .macOS: return ["sdk=macosx*"]
969-
case .iOS: return ["sdk=iphonesimulator*", "sdk=iphoneos*"]
970-
case .tvOS: return ["sdk=appletvsimulator*", "sdk=appletvos*"]
971-
case .watchOS: return ["sdk=watchsimulator*", "sdk=watchos*"]
972-
case .driverKit: return ["sdk=driverkit*"]
973-
case .linux: return ["sdk=linux*"]
970+
case .macOS: return .macOS
971+
case .macCatalyst: return .macCatalyst
972+
case .iOS: return .iOS
973+
case .tvOS: return .tvOS
974+
case .watchOS: return .watchOS
975+
case .driverKit: return .driverKit
976+
case .linux: return .linux
974977
}
975978
}
979+
980+
public var conditions: [String] {
981+
let filters = [PlatformsCondition(platforms: [packageModelPlatform])].toPlatformFilters().map { (filter: PIF.PlatformFilter) -> String in
982+
if filter.environment.isEmpty {
983+
return filter.platform
984+
} else {
985+
return "\(filter.platform)-\(filter.environment)"
986+
}
987+
}.sorted()
988+
return ["__platform_filter=\(filters.joined(separator: ";"))"]
989+
}
976990
}
977991

978-
public private(set) var platformSpecificSettings = [Platform: [MultipleValueSetting: [String]]]()
992+
public private(set) var platformSpecificSingleValueSettings = [Platform: [SingleValueSetting: String]]()
993+
public private(set) var platformSpecificMultipleValueSettings = [Platform: [MultipleValueSetting: [String]]]()
979994
public private(set) var singleValueSettings: [SingleValueSetting: String] = [:]
980995
public private(set) var multipleValueSettings: [MultipleValueSetting: [String]] = [:]
981996

@@ -984,6 +999,11 @@ public enum PIF {
984999
set { singleValueSettings[setting] = newValue }
9851000
}
9861001

1002+
public subscript(_ setting: SingleValueSetting, for platform: Platform) -> String? {
1003+
get { platformSpecificSingleValueSettings[platform]?[setting] }
1004+
set { platformSpecificSingleValueSettings[platform, default: [:]][setting] = newValue }
1005+
}
1006+
9871007
public subscript(_ setting: SingleValueSetting, default defaultValue: @autoclosure () -> String) -> String {
9881008
get { singleValueSettings[setting, default: defaultValue()] }
9891009
set { singleValueSettings[setting] = newValue }
@@ -995,8 +1015,8 @@ public enum PIF {
9951015
}
9961016

9971017
public subscript(_ setting: MultipleValueSetting, for platform: Platform) -> [String]? {
998-
get { platformSpecificSettings[platform]?[setting] }
999-
set { platformSpecificSettings[platform, default: [:]][setting] = newValue }
1018+
get { platformSpecificMultipleValueSettings[platform]?[setting] }
1019+
set { platformSpecificMultipleValueSettings[platform, default: [:]][setting] = newValue }
10001020
}
10011021

10021022
public subscript(
@@ -1012,15 +1032,15 @@ public enum PIF {
10121032
for platform: Platform,
10131033
default defaultValue: @autoclosure () -> [String]
10141034
) -> [String] {
1015-
get { platformSpecificSettings[platform, default: [:]][setting, default: defaultValue()] }
1016-
set { platformSpecificSettings[platform, default: [:]][setting] = newValue }
1035+
get { platformSpecificMultipleValueSettings[platform, default: [:]][setting, default: defaultValue()] }
1036+
set { platformSpecificMultipleValueSettings[platform, default: [:]][setting] = newValue }
10171037
}
10181038

10191039
public init() {
10201040
}
10211041

10221042
private enum CodingKeys: CodingKey {
1023-
case platformSpecificSettings, singleValueSettings, multipleValueSettings
1043+
case platformSpecificSingleValueSettings, platformSpecificMultipleValueSettings, singleValueSettings, multipleValueSettings
10241044
}
10251045

10261046
public func encode(to encoder: Encoder) throws {
@@ -1029,7 +1049,8 @@ public enum PIF {
10291049
}
10301050

10311051
var container = encoder.container(keyedBy: CodingKeys.self)
1032-
try container.encode(platformSpecificSettings, forKey: .platformSpecificSettings)
1052+
try container.encode(platformSpecificSingleValueSettings, forKey: .platformSpecificSingleValueSettings)
1053+
try container.encode(platformSpecificMultipleValueSettings, forKey: .platformSpecificMultipleValueSettings)
10331054
try container.encode(singleValueSettings, forKey: .singleValueSettings)
10341055
try container.encode(multipleValueSettings, forKey: .multipleValueSettings)
10351056
}
@@ -1045,7 +1066,15 @@ public enum PIF {
10451066
try container.encode(value, forKey: StringKey(key.rawValue))
10461067
}
10471068

1048-
for (platform, values) in platformSpecificSettings {
1069+
for (platform, values) in platformSpecificSingleValueSettings {
1070+
for condition in platform.conditions {
1071+
for (key, value) in values {
1072+
try container.encode(value, forKey: "\(key.rawValue)[\(condition)]")
1073+
}
1074+
}
1075+
}
1076+
1077+
for (platform, values) in platformSpecificMultipleValueSettings {
10491078
for condition in platform.conditions {
10501079
for (key, value) in values {
10511080
try container.encode(value, forKey: "\(key.rawValue)[\(condition)]")
@@ -1057,7 +1086,8 @@ public enum PIF {
10571086
public init(from decoder: Decoder) throws {
10581087
let container = try decoder.container(keyedBy: CodingKeys.self)
10591088

1060-
platformSpecificSettings = try container.decodeIfPresent([Platform: [MultipleValueSetting: [String]]].self, forKey: .platformSpecificSettings) ?? .init()
1089+
platformSpecificSingleValueSettings = try container.decodeIfPresent([Platform: [SingleValueSetting: String]].self, forKey: .platformSpecificSingleValueSettings) ?? .init()
1090+
platformSpecificMultipleValueSettings = try container.decodeIfPresent([Platform: [MultipleValueSetting: [String]]].self, forKey: .platformSpecificMultipleValueSettings) ?? .init()
10611091
singleValueSettings = try container.decodeIfPresent([SingleValueSetting: String].self, forKey: .singleValueSettings) ?? [:]
10621092
multipleValueSettings = try container.decodeIfPresent([MultipleValueSetting: [String]] .self, forKey: .multipleValueSettings) ?? [:]
10631093
}

0 commit comments

Comments
 (0)