Skip to content

Commit a2a2192

Browse files
committed
BuildSettingCondition should offer a Mac Catalyst condition
rdar://60376383
1 parent 667e7ca commit a2a2192

File tree

13 files changed

+132
-29
lines changed

13 files changed

+132
-29
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: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -24,21 +24,27 @@ public struct MinimumDeploymentTarget {
2424
}
2525
}
2626

27-
static func computeMinimumDeploymentTarget(of binaryPath: AbsolutePath) throws -> PlatformVersion? {
27+
static func computeMinimumDeploymentTarget(of binaryPath: AbsolutePath, platform: String) throws -> PlatformVersion? {
2828
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)
29+
var lines = try runResult.utf8Output().components(separatedBy: "\n")
30+
while !lines.isEmpty {
31+
let first = lines.removeFirst()
32+
if first.contains("platform \(platform)"), let line = lines.first, line.contains("minos") {
33+
return line.components(separatedBy: " ").last.map(PlatformVersion.init(stringLiteral:))
34+
}
35+
}
36+
return nil
3137
}
3238

33-
static func computeXCTestMinimumDeploymentTarget(with runResult: ProcessResult) throws -> PlatformVersion? {
39+
static func computeXCTestMinimumDeploymentTarget(with runResult: ProcessResult, platform: String) throws -> PlatformVersion? {
3440
guard let output = try runResult.utf8Output().spm_chuzzle() else { return nil }
3541
let sdkPath = try AbsolutePath(validating: output)
3642
let xcTestPath = sdkPath.appending(RelativePath("Developer/Library/Frameworks/XCTest.framework/XCTest"))
37-
return try computeMinimumDeploymentTarget(of: xcTestPath)
43+
return try computeMinimumDeploymentTarget(of: xcTestPath, platform: platform)
3844
}
3945

4046
static func computeXCTestMinimumDeploymentTarget(for platform: PackageModel.Platform) -> PlatformVersion {
41-
guard let sdkName = platform.sdkName else {
47+
guard let (sdkName, platformName) = platform.sdkNameAndPlatform else {
4248
return platform.oldestSupportedVersion
4349
}
4450

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

50-
if let version = try computeXCTestMinimumDeploymentTarget(with: runResult) {
56+
if let version = try computeXCTestMinimumDeploymentTarget(with: runResult, platform: platformName) {
5157
return version
5258
}
5359
} catch { } // we do not treat this a fatal and instead use the fallback minimum deployment target
@@ -58,16 +64,18 @@ public struct MinimumDeploymentTarget {
5864
}
5965

6066
private extension PackageModel.Platform {
61-
var sdkName: String? {
67+
var sdkNameAndPlatform: (String, String)? {
6268
switch self {
6369
case .macOS:
64-
return "macosx"
70+
return ("macosx", "MACOS")
71+
case .macCatalyst:
72+
return ("macosx", "MACCATALYST")
6573
case .iOS:
66-
return "iphoneos"
74+
return ("iphoneos", "IOS")
6775
case .tvOS:
68-
return "appletvos"
76+
return ("appletvos", "TVOS")
6977
case .watchOS:
70-
return "watchos"
78+
return ("watchos", "WATCHOS")
7179
case .driverKit:
7280
return nil // DriverKit does not support XCTest.
7381
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
@@ -467,6 +467,8 @@ extension PackageModel.Platform {
467467
switch self {
468468
case .macOS:
469469
return "macOS"
470+
case .macCatalyst:
471+
return "macCatalyst"
470472
case .iOS:
471473
return "iOS"
472474
case .tvOS:
@@ -487,7 +489,7 @@ extension SupportedPlatform {
487489
guard self.version.patch == 0 else {
488490
return false
489491
}
490-
} else if [Platform.macOS, .iOS, .watchOS, .tvOS, .driverKit].contains(platform) {
492+
} else if [Platform.macOS, .macCatalyst, .iOS, .watchOS, .tvOS, .driverKit].contains(platform) {
491493
guard self.version.minor == 0, self.version.patch == 0 else {
492494
return false
493495
}
@@ -500,6 +502,8 @@ extension SupportedPlatform {
500502
return (10...15).contains(version.minor)
501503
case .macOS:
502504
return (11...11).contains(version.major)
505+
case .macCatalyst:
506+
return (13...14).contains(version.major)
503507
case .iOS:
504508
return (8...14).contains(version.major)
505509
case .tvOS:

Sources/XCBuildSupport/PIF.swift

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -957,6 +957,7 @@ public enum PIF {
957957

958958
public enum Platform: String, CaseIterable, Codable {
959959
case macOS = "macos"
960+
case macCatalyst = "maccatalyst"
960961
case iOS = "ios"
961962
case tvOS = "tvos"
962963
case watchOS = "watchos"
@@ -965,7 +966,7 @@ public enum PIF {
965966

966967
public var conditions: [String] {
967968
switch self {
968-
case .macOS: return ["sdk=macosx*"]
969+
case .macOS, .macCatalyst: return ["sdk=macosx*"]
969970
case .iOS: return ["sdk=iphonesimulator*", "sdk=iphoneos*"]
970971
case .tvOS: return ["sdk=appletvsimulator*", "sdk=appletvos*"]
971972
case .watchOS: return ["sdk=watchsimulator*", "sdk=watchos*"]
@@ -975,7 +976,8 @@ public enum PIF {
975976
}
976977
}
977978

978-
public private(set) var platformSpecificSettings = [Platform: [MultipleValueSetting: [String]]]()
979+
public private(set) var platformSpecificSingleValueSettings = [Platform: [SingleValueSetting: String]]()
980+
public private(set) var platformSpecificMultipleValueSettings = [Platform: [MultipleValueSetting: [String]]]()
979981
public private(set) var singleValueSettings: [SingleValueSetting: String] = [:]
980982
public private(set) var multipleValueSettings: [MultipleValueSetting: [String]] = [:]
981983

@@ -984,6 +986,11 @@ public enum PIF {
984986
set { singleValueSettings[setting] = newValue }
985987
}
986988

989+
public subscript(_ setting: SingleValueSetting, for platform: Platform) -> String? {
990+
get { platformSpecificSingleValueSettings[platform]?[setting] }
991+
set { platformSpecificSingleValueSettings[platform, default: [:]][setting] = newValue }
992+
}
993+
987994
public subscript(_ setting: SingleValueSetting, default defaultValue: @autoclosure () -> String) -> String {
988995
get { singleValueSettings[setting, default: defaultValue()] }
989996
set { singleValueSettings[setting] = newValue }
@@ -995,8 +1002,8 @@ public enum PIF {
9951002
}
9961003

9971004
public subscript(_ setting: MultipleValueSetting, for platform: Platform) -> [String]? {
998-
get { platformSpecificSettings[platform]?[setting] }
999-
set { platformSpecificSettings[platform, default: [:]][setting] = newValue }
1005+
get { platformSpecificMultipleValueSettings[platform]?[setting] }
1006+
set { platformSpecificMultipleValueSettings[platform, default: [:]][setting] = newValue }
10001007
}
10011008

10021009
public subscript(
@@ -1012,15 +1019,15 @@ public enum PIF {
10121019
for platform: Platform,
10131020
default defaultValue: @autoclosure () -> [String]
10141021
) -> [String] {
1015-
get { platformSpecificSettings[platform, default: [:]][setting, default: defaultValue()] }
1016-
set { platformSpecificSettings[platform, default: [:]][setting] = newValue }
1022+
get { platformSpecificMultipleValueSettings[platform, default: [:]][setting, default: defaultValue()] }
1023+
set { platformSpecificMultipleValueSettings[platform, default: [:]][setting] = newValue }
10171024
}
10181025

10191026
public init() {
10201027
}
10211028

10221029
private enum CodingKeys: CodingKey {
1023-
case platformSpecificSettings, singleValueSettings, multipleValueSettings
1030+
case platformSpecificSingleValueSettings, platformSpecificMultipleValueSettings, singleValueSettings, multipleValueSettings
10241031
}
10251032

10261033
public func encode(to encoder: Encoder) throws {
@@ -1029,7 +1036,8 @@ public enum PIF {
10291036
}
10301037

10311038
var container = encoder.container(keyedBy: CodingKeys.self)
1032-
try container.encode(platformSpecificSettings, forKey: .platformSpecificSettings)
1039+
try container.encode(platformSpecificSingleValueSettings, forKey: .platformSpecificSingleValueSettings)
1040+
try container.encode(platformSpecificMultipleValueSettings, forKey: .platformSpecificMultipleValueSettings)
10331041
try container.encode(singleValueSettings, forKey: .singleValueSettings)
10341042
try container.encode(multipleValueSettings, forKey: .multipleValueSettings)
10351043
}
@@ -1045,7 +1053,15 @@ public enum PIF {
10451053
try container.encode(value, forKey: StringKey(key.rawValue))
10461054
}
10471055

1048-
for (platform, values) in platformSpecificSettings {
1056+
for (platform, values) in platformSpecificSingleValueSettings {
1057+
for condition in platform.conditions {
1058+
for (key, value) in values {
1059+
try container.encode(value, forKey: "\(key.rawValue)[\(condition)]")
1060+
}
1061+
}
1062+
}
1063+
1064+
for (platform, values) in platformSpecificMultipleValueSettings {
10491065
for condition in platform.conditions {
10501066
for (key, value) in values {
10511067
try container.encode(value, forKey: "\(key.rawValue)[\(condition)]")
@@ -1057,7 +1073,8 @@ public enum PIF {
10571073
public init(from decoder: Decoder) throws {
10581074
let container = try decoder.container(keyedBy: CodingKeys.self)
10591075

1060-
platformSpecificSettings = try container.decodeIfPresent([Platform: [MultipleValueSetting: [String]]].self, forKey: .platformSpecificSettings) ?? .init()
1076+
platformSpecificSingleValueSettings = try container.decodeIfPresent([Platform: [SingleValueSetting: String]].self, forKey: .platformSpecificSingleValueSettings) ?? .init()
1077+
platformSpecificMultipleValueSettings = try container.decodeIfPresent([Platform: [MultipleValueSetting: [String]]].self, forKey: .platformSpecificMultipleValueSettings) ?? .init()
10611078
singleValueSettings = try container.decodeIfPresent([SingleValueSetting: String].self, forKey: .singleValueSettings) ?? [:]
10621079
multipleValueSettings = try container.decodeIfPresent([MultipleValueSetting: [String]] .self, forKey: .multipleValueSettings) ?? [:]
10631080
}

Sources/XCBuildSupport/PIFBuilder.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,7 @@ final class PackagePIFProjectBuilder: PIFProjectBuilder {
256256
let firstTarget = package.targets.first(where: { $0.type != .test })?.underlyingTarget ?? package.targets.first?.underlyingTarget
257257
settings[.MACOSX_DEPLOYMENT_TARGET] = firstTarget?.deploymentTarget(for: .macOS)
258258
settings[.IPHONEOS_DEPLOYMENT_TARGET] = firstTarget?.deploymentTarget(for: .iOS)
259+
settings[.IPHONEOS_DEPLOYMENT_TARGET, for: .macCatalyst] = firstTarget?.deploymentTarget(for: .macCatalyst)
259260
settings[.TVOS_DEPLOYMENT_TARGET] = firstTarget?.deploymentTarget(for: .tvOS)
260261
settings[.WATCHOS_DEPLOYMENT_TARGET] = firstTarget?.deploymentTarget(for: .watchOS)
261262
settings[.DRIVERKIT_DEPLOYMENT_TARGET] = firstTarget?.deploymentTarget(for: .driverKit)
@@ -1470,6 +1471,9 @@ extension Array where Element == PackageConditionProtocol {
14701471
case .macOS:
14711472
result += PIF.PlatformFilter.macOSFilters
14721473

1474+
case .macCatalyst:
1475+
result += PIF.PlatformFilter.macCatalystFilters
1476+
14731477
case .iOS:
14741478
result += PIF.PlatformFilter.iOSFilters
14751479

@@ -1505,6 +1509,11 @@ extension PIF.PlatformFilter {
15051509
/// macOS platform filters.
15061510
public static let macOSFilters: [PIF.PlatformFilter] = [.init(platform: "macos")]
15071511

1512+
/// Mac Catalyst platform filters.
1513+
public static let macCatalystFilters: [PIF.PlatformFilter] = [
1514+
.init(platform: "ios", environment: "maccatalyst")
1515+
]
1516+
15081517
/// iOS platform filters.
15091518
public static let iOSFilters: [PIF.PlatformFilter] = [
15101519
.init(platform: "ios"),
@@ -1553,6 +1562,7 @@ private extension PIF.BuildSettings.Platform {
15531562
switch platform {
15541563
case .iOS: return .iOS
15551564
case .linux: return .linux
1565+
case .macCatalyst: return .macCatalyst
15561566
case .macOS: return .macOS
15571567
case .tvOS: return .tvOS
15581568
case .watchOS: return .watchOS

0 commit comments

Comments
 (0)