Add support for disabling `Mac (designed for iOS)` destination for iOS deployment target (#5095)
* Add logic to remove iPad plist information when not needed * Update tests * Apply suggestions from code review * Fix parameter name * Update hash values in tests * Add documentation
This commit is contained in:
parent
3edcc9801e
commit
c0d1a5be83
|
@ -4,8 +4,8 @@ import Foundation
|
|||
|
||||
/// A supported minimum deployment target.
|
||||
public enum DeploymentTarget: Codable, Hashable {
|
||||
/// The minimum iOS version and the list of devices your product will support.
|
||||
case iOS(targetVersion: String, devices: DeploymentDevice)
|
||||
/// The minimum iOS version, the list of devices your product will support, and whether or not the target should run on mac devices.
|
||||
case iOS(targetVersion: String, devices: DeploymentDevice, supportsMacDesignedForIOS: Bool = true)
|
||||
/// The minimum macOS version your product will support.
|
||||
case macOS(targetVersion: String)
|
||||
/// The minimum watchOS version your product will support.
|
||||
|
@ -23,7 +23,7 @@ public enum DeploymentTarget: Codable, Hashable {
|
|||
/// The target platform version
|
||||
public var targetVersion: String {
|
||||
switch self {
|
||||
case let .iOS(targetVersion, _), let .macOS(targetVersion), let .watchOS(targetVersion), let .tvOS(targetVersion):
|
||||
case let .iOS(targetVersion, _, _), let .macOS(targetVersion), let .watchOS(targetVersion), let .tvOS(targetVersion):
|
||||
return targetVersion
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,8 +22,8 @@ public final class DeploymentTargetContentHasher: DeploymentTargetContentHashing
|
|||
public func hash(deploymentTarget: DeploymentTarget) throws -> String {
|
||||
let stringToHash: String
|
||||
switch deploymentTarget {
|
||||
case let .iOS(version, device):
|
||||
stringToHash = "iOS-\(version)-\(device.rawValue)"
|
||||
case let .iOS(version, device, supportsMacDesignedForIOS):
|
||||
stringToHash = "iOS-\(version)-\(device.rawValue)-\(supportsMacDesignedForIOS)"
|
||||
case let .macOS(version):
|
||||
stringToHash = "macOS-\(version)"
|
||||
case let .watchOS(version):
|
||||
|
|
|
@ -1146,8 +1146,12 @@ extension ProjectDescription.DefaultSettings {
|
|||
extension ProjectDescription.DeploymentTarget {
|
||||
fileprivate static func from(deploymentTarget: TuistGraph.DeploymentTarget) -> Self {
|
||||
switch deploymentTarget {
|
||||
case let .iOS(version, devices):
|
||||
return .iOS(targetVersion: version, devices: .from(devices: devices))
|
||||
case let .iOS(version, devices, supportsMacDesignedForIOS):
|
||||
return .iOS(
|
||||
targetVersion: version,
|
||||
devices: .from(devices: devices),
|
||||
supportsMacDesignedForIOS: supportsMacDesignedForIOS
|
||||
)
|
||||
case let .macOS(version):
|
||||
return .macOS(targetVersion: version)
|
||||
case let .tvOS(version):
|
||||
|
|
|
@ -291,13 +291,14 @@ final class ConfigGenerator: ConfigGenerating {
|
|||
var settings: SettingsDictionary = [:]
|
||||
|
||||
switch deploymentTarget {
|
||||
case let .iOS(version, devices):
|
||||
case let .iOS(version, devices, supportsMacDesignedForIOS):
|
||||
var deviceFamilyValues: [Int] = []
|
||||
if devices.contains(.iphone) { deviceFamilyValues.append(1) }
|
||||
if devices.contains(.ipad) { deviceFamilyValues.append(2) }
|
||||
|
||||
settings["TARGETED_DEVICE_FAMILY"] = .string(deviceFamilyValues.map { "\($0)" }.joined(separator: ","))
|
||||
settings["IPHONEOS_DEPLOYMENT_TARGET"] = .string(version)
|
||||
settings["SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD"] = supportsMacDesignedForIOS ? "YES" : "NO"
|
||||
|
||||
if devices.contains(.ipad), devices.contains(.mac) {
|
||||
settings["SUPPORTS_MACCATALYST"] = "YES"
|
||||
|
|
|
@ -41,7 +41,11 @@ final class InfoPlistContentProvider: InfoPlistContentProviding {
|
|||
|
||||
// iOS app
|
||||
if target.product == .app, target.platform == .iOS {
|
||||
extend(&content, with: iosApp())
|
||||
if case let .iOS(_, devices, _) = target.deploymentTarget, !devices.contains(.ipad) {
|
||||
extend(&content, with: iosApp(iPadSupport: false))
|
||||
} else {
|
||||
extend(&content, with: iosApp(iPadSupport: true))
|
||||
}
|
||||
}
|
||||
|
||||
// macOS app
|
||||
|
@ -145,9 +149,10 @@ final class InfoPlistContentProvider: InfoPlistContentProviding {
|
|||
|
||||
/// Returns the default Info.plist content that iOS apps should have.
|
||||
///
|
||||
/// - Parameter iPadSupport: Wether the `iOS` application supports `iPadOS`.
|
||||
/// - Returns: Info.plist content.
|
||||
func iosApp() -> [String: Any] {
|
||||
[
|
||||
func iosApp(iPadSupport: Bool) -> [String: Any] {
|
||||
var baseInfo: [String: Any] = [
|
||||
"LSRequiresIPhoneOS": true,
|
||||
"UIRequiredDeviceCapabilities": [
|
||||
"armv7",
|
||||
|
@ -157,17 +162,22 @@ final class InfoPlistContentProvider: InfoPlistContentProviding {
|
|||
"UIInterfaceOrientationLandscapeLeft",
|
||||
"UIInterfaceOrientationLandscapeRight",
|
||||
],
|
||||
"UISupportedInterfaceOrientations~ipad": [
|
||||
"UIInterfaceOrientationPortrait",
|
||||
"UIInterfaceOrientationPortraitUpsideDown",
|
||||
"UIInterfaceOrientationLandscapeLeft",
|
||||
"UIInterfaceOrientationLandscapeRight",
|
||||
],
|
||||
"UIApplicationSceneManifest": [
|
||||
"UIApplicationSupportsMultipleScenes": false,
|
||||
"UISceneConfigurations": [:],
|
||||
],
|
||||
]
|
||||
|
||||
if iPadSupport {
|
||||
baseInfo["UISupportedInterfaceOrientations~ipad"] = [
|
||||
"UIInterfaceOrientationPortrait",
|
||||
"UIInterfaceOrientationPortraitUpsideDown",
|
||||
"UIInterfaceOrientationLandscapeLeft",
|
||||
"UIInterfaceOrientationLandscapeRight",
|
||||
]
|
||||
}
|
||||
|
||||
return baseInfo
|
||||
}
|
||||
|
||||
/// Returns the default Info.plist content that macOS apps should have.
|
||||
|
|
|
@ -3,7 +3,7 @@ import Foundation
|
|||
// MARK: - DeploymentTarget
|
||||
|
||||
public enum DeploymentTarget: Hashable, Codable {
|
||||
case iOS(String, DeploymentDevice)
|
||||
case iOS(String, DeploymentDevice, supportsMacDesignedForIOS: Bool)
|
||||
case macOS(String)
|
||||
case watchOS(String)
|
||||
case tvOS(String)
|
||||
|
@ -19,7 +19,7 @@ public enum DeploymentTarget: Hashable, Codable {
|
|||
|
||||
public var version: String {
|
||||
switch self {
|
||||
case let .iOS(version, _): return version
|
||||
case let .iOS(version, _, _): return version
|
||||
case let .macOS(version): return version
|
||||
case let .watchOS(version): return version
|
||||
case let .tvOS(version): return version
|
||||
|
|
|
@ -234,9 +234,9 @@ public struct Target: Equatable, Hashable, Comparable, Codable {
|
|||
/// with Catalyst compatibility.
|
||||
public var targetDependencyBuildFilesPlatformFilter: BuildFilePlatformFilter? {
|
||||
switch deploymentTarget {
|
||||
case let .iOS(_, devices) where devices.contains(.all):
|
||||
case let .iOS(_, devices, _) where devices.contains(.all):
|
||||
return nil
|
||||
case let .iOS(_, devices):
|
||||
case let .iOS(_, devices, _):
|
||||
if devices.contains(.mac) {
|
||||
return .catalyst
|
||||
} else {
|
||||
|
|
|
@ -11,7 +11,7 @@ extension Target {
|
|||
product: Product = .app,
|
||||
productName: String? = nil,
|
||||
bundleId: String? = nil,
|
||||
deploymentTarget: DeploymentTarget? = .iOS("13.1", [.iphone, .ipad]),
|
||||
deploymentTarget: DeploymentTarget? = .iOS("13.1", [.iphone, .ipad], supportsMacDesignedForIOS: true),
|
||||
infoPlist: InfoPlist? = nil,
|
||||
entitlements: AbsolutePath? = nil,
|
||||
settings: Settings? = Settings.test(),
|
||||
|
|
|
@ -10,8 +10,12 @@ extension TuistGraph.DeploymentTarget {
|
|||
/// - generatorPaths: Generator paths.
|
||||
static func from(manifest: ProjectDescription.DeploymentTarget) -> TuistGraph.DeploymentTarget {
|
||||
switch manifest {
|
||||
case let .iOS(version, devices):
|
||||
return .iOS(version, DeploymentDevice(rawValue: devices.rawValue))
|
||||
case let .iOS(version, devices, supportsMacDesignedForIOS):
|
||||
return .iOS(
|
||||
version,
|
||||
DeploymentDevice(rawValue: devices.rawValue),
|
||||
supportsMacDesignedForIOS: supportsMacDesignedForIOS
|
||||
)
|
||||
case let .macOS(version):
|
||||
return .macOS(version)
|
||||
case let .watchOS(version):
|
||||
|
|
|
@ -178,8 +178,8 @@ final class ContentHashingIntegrationTests: TuistUnitTestCase {
|
|||
)
|
||||
|
||||
// Then
|
||||
XCTAssertEqual(contentHash[framework1], "d31f7192381862aa6b74c7bbf2f097ce")
|
||||
XCTAssertEqual(contentHash[framework2], "d0229bc4f00ff0b52bccb45668b9baa9")
|
||||
XCTAssertEqual(contentHash[framework1], "d4631574745e74fc2ab6b7b9cceab088")
|
||||
XCTAssertEqual(contentHash[framework2], "86a8f13a091f073b5395eddb452f14da")
|
||||
}
|
||||
|
||||
func test_contentHashes_hashChangesWithCacheOutputType() throws {
|
||||
|
|
|
@ -27,21 +27,21 @@ final class DeploymentTargetContentHasherTests: TuistUnitTestCase {
|
|||
|
||||
func test_hash_whenIosIphoneV1_callsContentHasherWithExpectedStrings() throws {
|
||||
// When
|
||||
let deploymentTarget = DeploymentTarget.iOS("v1", .iphone)
|
||||
let deploymentTarget = DeploymentTarget.iOS("v1", .iphone, supportsMacDesignedForIOS: false)
|
||||
|
||||
// Then
|
||||
let hash = try subject.hash(deploymentTarget: deploymentTarget)
|
||||
XCTAssertEqual(hash, "iOS-v1-1-hash")
|
||||
XCTAssertEqual(hash, "iOS-v1-1-false-hash")
|
||||
XCTAssertEqual(mockContentHasher.hashStringCallCount, 1)
|
||||
}
|
||||
|
||||
func test_hash_whenIosIpadV2_callsContentHasherWithExpectedStrings() throws {
|
||||
// When
|
||||
let deploymentTarget = DeploymentTarget.iOS("v2", .ipad)
|
||||
let deploymentTarget = DeploymentTarget.iOS("v2", .ipad, supportsMacDesignedForIOS: true)
|
||||
|
||||
// Then
|
||||
let hash = try subject.hash(deploymentTarget: deploymentTarget)
|
||||
XCTAssertEqual(hash, "iOS-v2-2-hash")
|
||||
XCTAssertEqual(hash, "iOS-v2-2-true-hash")
|
||||
XCTAssertEqual(mockContentHasher.hashStringCallCount, 1)
|
||||
}
|
||||
|
||||
|
|
|
@ -230,10 +230,12 @@ final class ConfigGeneratorTests: TuistUnitTestCase {
|
|||
assert(config: releaseConfig, contains: testHostSettings)
|
||||
}
|
||||
|
||||
func test_generateTargetWithDeploymentTarget_whenIOS() throws {
|
||||
func test_generateTargetWithDeploymentTarget_whenIOS_withMacForIPhoneSupport() throws {
|
||||
// Given
|
||||
let project = Project.test()
|
||||
let target = Target.test(deploymentTarget: .iOS("12.0", [.iphone, .ipad]))
|
||||
let target = Target.test(
|
||||
deploymentTarget: .iOS("12.0", [.iphone, .ipad], supportsMacDesignedForIOS: true)
|
||||
)
|
||||
let graph = Graph.test(path: project.path)
|
||||
let graphTraverser = GraphTraverser(graph: graph)
|
||||
|
||||
|
@ -257,6 +259,43 @@ final class ConfigGeneratorTests: TuistUnitTestCase {
|
|||
let expectedSettings = [
|
||||
"TARGETED_DEVICE_FAMILY": "1,2",
|
||||
"IPHONEOS_DEPLOYMENT_TARGET": "12.0",
|
||||
"SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD": "YES",
|
||||
]
|
||||
|
||||
assert(config: debugConfig, contains: expectedSettings)
|
||||
assert(config: releaseConfig, contains: expectedSettings)
|
||||
}
|
||||
|
||||
func test_generateTargetWithDeploymentTarget_whenIOS_withoutMacForIPhoneSupport() throws {
|
||||
// Given
|
||||
let project = Project.test()
|
||||
let target = Target.test(
|
||||
deploymentTarget: .iOS("12.0", [.iphone, .ipad], supportsMacDesignedForIOS: false)
|
||||
)
|
||||
let graph = Graph.test(path: project.path)
|
||||
let graphTraverser = GraphTraverser(graph: graph)
|
||||
|
||||
// When
|
||||
try subject.generateTargetConfig(
|
||||
target,
|
||||
project: project,
|
||||
pbxTarget: pbxTarget,
|
||||
pbxproj: pbxproj,
|
||||
projectSettings: .default,
|
||||
fileElements: ProjectFileElements(),
|
||||
graphTraverser: graphTraverser,
|
||||
sourceRootPath: AbsolutePath("/project")
|
||||
)
|
||||
|
||||
// Then
|
||||
let configurationList = pbxTarget.buildConfigurationList
|
||||
let debugConfig = configurationList?.configuration(name: "Debug")
|
||||
let releaseConfig = configurationList?.configuration(name: "Release")
|
||||
|
||||
let expectedSettings = [
|
||||
"TARGETED_DEVICE_FAMILY": "1,2",
|
||||
"IPHONEOS_DEPLOYMENT_TARGET": "12.0",
|
||||
"SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD": "NO",
|
||||
]
|
||||
|
||||
assert(config: debugConfig, contains: expectedSettings)
|
||||
|
@ -265,7 +304,10 @@ final class ConfigGeneratorTests: TuistUnitTestCase {
|
|||
|
||||
func test_generateTargetWithDeploymentTarget_whenIOS_for_framework() throws {
|
||||
// Given
|
||||
let target = Target.test(product: .framework, deploymentTarget: .iOS("13.0", [.iphone, .ipad]))
|
||||
let target = Target.test(
|
||||
product: .framework,
|
||||
deploymentTarget: .iOS("13.0", [.iphone, .ipad], supportsMacDesignedForIOS: true)
|
||||
)
|
||||
let project = Project.test(targets: [target])
|
||||
let graph = Graph.test(path: project.path)
|
||||
let graphTraverser = GraphTraverser(graph: graph)
|
||||
|
@ -332,7 +374,9 @@ final class ConfigGeneratorTests: TuistUnitTestCase {
|
|||
func test_generateTargetWithDeploymentTarget_whenCatalyst() throws {
|
||||
// Given
|
||||
let project = Project.test()
|
||||
let target = Target.test(deploymentTarget: .iOS("13.1", [.iphone, .ipad, .mac]))
|
||||
let target = Target.test(
|
||||
deploymentTarget: .iOS("13.1", [.iphone, .ipad, .mac], supportsMacDesignedForIOS: false)
|
||||
)
|
||||
let graph = Graph.test(path: project.path)
|
||||
let graphTraverser = GraphTraverser(graph: graph)
|
||||
|
||||
|
@ -358,6 +402,7 @@ final class ConfigGeneratorTests: TuistUnitTestCase {
|
|||
"IPHONEOS_DEPLOYMENT_TARGET": "13.1",
|
||||
"SUPPORTS_MACCATALYST": "YES",
|
||||
"DERIVE_MACCATALYST_PRODUCT_BUNDLE_IDENTIFIER": "YES",
|
||||
"SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD": "NO",
|
||||
]
|
||||
|
||||
assert(config: debugConfig, contains: expectedSettings)
|
||||
|
|
|
@ -19,9 +19,13 @@ final class InfoPlistContentProviderTests: XCTestCase {
|
|||
super.tearDown()
|
||||
}
|
||||
|
||||
func test_content_wheniOSApp() {
|
||||
func test_content_wheniOSApp_withiPadSupport() {
|
||||
// Given
|
||||
let target = Target.test(platform: .iOS, product: .app)
|
||||
let target = Target.test(
|
||||
platform: .iOS,
|
||||
product: .app,
|
||||
deploymentTarget: .iOS("16.0", [.iphone, .ipad], supportsMacDesignedForIOS: true)
|
||||
)
|
||||
|
||||
// When
|
||||
let got = subject.content(
|
||||
|
@ -61,6 +65,46 @@ final class InfoPlistContentProviderTests: XCTestCase {
|
|||
])
|
||||
}
|
||||
|
||||
func test_content_wheniOSApp_withoutiPadSupport() {
|
||||
// Given
|
||||
let target = Target.test(
|
||||
platform: .iOS,
|
||||
product: .app,
|
||||
deploymentTarget: .iOS("16.0", .iphone, supportsMacDesignedForIOS: true)
|
||||
)
|
||||
|
||||
// When
|
||||
let got = subject.content(
|
||||
project: .empty(),
|
||||
target: target,
|
||||
extendedWith: ["ExtraAttribute": "Value"]
|
||||
)
|
||||
|
||||
// Then
|
||||
assertEqual(got, [
|
||||
"CFBundleName": "$(PRODUCT_NAME)",
|
||||
"CFBundleIdentifier": "$(PRODUCT_BUNDLE_IDENTIFIER)",
|
||||
"UIRequiredDeviceCapabilities": ["armv7"],
|
||||
"UISupportedInterfaceOrientations": [
|
||||
"UIInterfaceOrientationPortrait",
|
||||
"UIInterfaceOrientationLandscapeLeft",
|
||||
"UIInterfaceOrientationLandscapeRight",
|
||||
],
|
||||
"CFBundleShortVersionString": "1.0",
|
||||
"LSRequiresIPhoneOS": true,
|
||||
"CFBundleDevelopmentRegion": "$(DEVELOPMENT_LANGUAGE)",
|
||||
"CFBundlePackageType": "APPL",
|
||||
"CFBundleVersion": "1",
|
||||
"ExtraAttribute": "Value",
|
||||
"CFBundleExecutable": "$(EXECUTABLE_NAME)",
|
||||
"CFBundleInfoDictionaryVersion": "6.0",
|
||||
"UIApplicationSceneManifest": [
|
||||
"UIApplicationSupportsMultipleScenes": false,
|
||||
"UISceneConfigurations": [:],
|
||||
],
|
||||
])
|
||||
}
|
||||
|
||||
func test_content_whenMacosApp() {
|
||||
// Given
|
||||
let target = Target.test(platform: .macOS, product: .app)
|
||||
|
|
|
@ -7,7 +7,7 @@ import XCTest
|
|||
final class DeploymentTargetTests: TuistUnitTestCase {
|
||||
func test_codable_iOS() {
|
||||
// Given
|
||||
let subject = DeploymentTarget.iOS("12.1", [.iphone, .mac])
|
||||
let subject = DeploymentTarget.iOS("12.1", [.iphone, .mac], supportsMacDesignedForIOS: true)
|
||||
|
||||
// Then
|
||||
XCTAssertCodable(subject)
|
||||
|
|
|
@ -299,7 +299,7 @@ final class TargetTests: TuistUnitTestCase {
|
|||
|
||||
func test_targetDependencyBuildFilesPlatformFilter_when_iOS_targets_mac() {
|
||||
// Given
|
||||
let target = Target.test(deploymentTarget: .iOS("14.0", [.mac]))
|
||||
let target = Target.test(deploymentTarget: .iOS("14.0", [.mac], supportsMacDesignedForIOS: false))
|
||||
|
||||
// When
|
||||
let got = target.targetDependencyBuildFilesPlatformFilter
|
||||
|
@ -310,7 +310,7 @@ final class TargetTests: TuistUnitTestCase {
|
|||
|
||||
func test_targetDependencyBuildFilesPlatformFilter_when_iOS_and_doesnt_target_mac() {
|
||||
// Given
|
||||
let target = Target.test(deploymentTarget: .iOS("14.0", [.iphone]))
|
||||
let target = Target.test(deploymentTarget: .iOS("14.0", [.iphone], supportsMacDesignedForIOS: false))
|
||||
|
||||
// When
|
||||
let got = target.targetDependencyBuildFilesPlatformFilter
|
||||
|
|
|
@ -19,7 +19,7 @@ final class DeploymentTargetManifestMapperTests: TuistUnitTestCase {
|
|||
let got = TuistGraph.DeploymentTarget.from(manifest: manifest)
|
||||
|
||||
// Then
|
||||
guard case let .iOS(version, devices) = got
|
||||
guard case let .iOS(version, devices, supportsMacDesignedForIOS) = got
|
||||
else {
|
||||
XCTFail("Deployment target should be iOS")
|
||||
return
|
||||
|
@ -28,5 +28,6 @@ final class DeploymentTargetManifestMapperTests: TuistUnitTestCase {
|
|||
XCTAssertEqual(version, "13.1")
|
||||
XCTAssertTrue(devices.contains(.iphone))
|
||||
XCTAssertFalse(devices.contains(.ipad))
|
||||
XCTAssertTrue(supportsMacDesignedForIOS)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -198,24 +198,6 @@
|
|||
"version" : "1.0.4"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-docc-plugin",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-docc-plugin.git",
|
||||
"state" : {
|
||||
"revision" : "10bc670db657d11bdd561e07de30a9041311b2b1",
|
||||
"version" : "1.1.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-docc-symbolkit",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-docc-symbolkit",
|
||||
"state" : {
|
||||
"revision" : "b45d1f2ed151d057b54504d653e0da5552844e34",
|
||||
"version" : "1.0.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-log",
|
||||
"kind" : "remoteSourceControl",
|
||||
|
|
Loading…
Reference in New Issue