Add support for conditionally linking dependencies based on platform (#1087)

* Add initial support for conditional platform dependencies

* Add tests for conditional platforms

* Update docs and changelog

* Respond to PR feedback

* Change name of field from 'conditionalPlatforms' to 'platforms'

Co-authored-by: Yonas Kolb <yonaskolb@users.noreply.github.com>
This commit is contained in:
Dalton Claybrook
2021-06-20 06:19:02 -04:00
committed by GitHub
parent 4455919be3
commit 81bd52be4b
9 changed files with 80 additions and 27 deletions
+4
View File
@@ -6,6 +6,10 @@
- Added support for DocC Catalogs [#1091](https://github.com/yonaskolb/XcodeGen/pull/1091) @brevansio
- Added support for "driver-extension" and "system-extension" product types [#1092](https://github.com/yonaskolb/XcodeGen/issues/1092) @vgorloff
- Add support for conditionally linking dependencies for specific platforms [#1087](https://github.com/yonaskolb/XcodeGen/pull/1087) @daltonclaybrook
### Changed
- **Breaking**: Rename the `platform` field on `Dependency` to `platformFilter` [#1087](https://github.com/yonaskolb/XcodeGen/pull/1087) @daltonclaybrook
## 2.23.1
+2 -1
View File
@@ -430,7 +430,8 @@ A dependency can be one of a 6 types:
- [ ] **codeSign**: **Bool** - Whether the `codeSignOnCopy` setting is applied when embedding framework. Defaults to true
- [ ] **removeHeaders**: **Bool** - Whether the `removeHeadersOnCopy` setting is applied when embedding the framework. Defaults to true
- [ ] **weak**: **Bool** - Whether the `Weak` setting is applied when linking the framework. Defaults to false
- [ ] **platform**: **String** - Add dependency to selected platforms. Available platforms are: **iOS**, **macOS** and **all**. Defaults is **all**
- [ ] **platformFilter**: **String** - This field is specific to Mac Catalyst. It corresponds to the "Platforms" dropdown in the Frameworks & Libraries section of Target settings in Xcode. Available options are: **iOS**, **macOS** and **all**. Defaults is **all**
- [ ] **platforms**: **[[Platform](#platform)]** - List of platforms this dependency should apply to. Defaults to all applicable platforms.
**Implicit Framework options**:
+16 -8
View File
@@ -5,7 +5,7 @@ public struct Dependency: Equatable {
public static let removeHeadersDefault = true
public static let implicitDefault = false
public static let weakLinkDefault = false
public static let platformDefault: Platform = .all
public static let platformFilterDefault: PlatformFilter = .all
public var type: DependencyType
public var reference: String
@@ -15,7 +15,8 @@ public struct Dependency: Equatable {
public var link: Bool?
public var implicit: Bool = implicitDefault
public var weakLink: Bool = weakLinkDefault
public var platform: Platform = platformDefault
public var platformFilter: PlatformFilter = platformFilterDefault
public var platforms: Set<Platform>?
public init(
type: DependencyType,
@@ -25,7 +26,8 @@ public struct Dependency: Equatable {
link: Bool? = nil,
implicit: Bool = implicitDefault,
weakLink: Bool = weakLinkDefault,
platform: Platform = platformDefault
platformFilter: PlatformFilter = platformFilterDefault,
platforms: Set<Platform>? = nil
) {
self.type = type
self.reference = reference
@@ -34,10 +36,11 @@ public struct Dependency: Equatable {
self.link = link
self.implicit = implicit
self.weakLink = weakLink
self.platform = platform
self.platformFilter = platformFilter
self.platforms = platforms
}
public enum Platform: String, Equatable {
public enum PlatformFilter: String, Equatable {
case all
case iOS
case macOS
@@ -123,10 +126,14 @@ extension Dependency: JSONObjectConvertible {
weakLink = bool
}
if let platformString: String = jsonDictionary.json(atKeyPath: "platform"), let platform = Platform(rawValue: platformString) {
self.platform = platform
if let platformFilterString: String = jsonDictionary.json(atKeyPath: "platformFilter"), let platformFilter = PlatformFilter(rawValue: platformFilterString) {
self.platformFilter = platformFilter
} else {
self.platform = .all
self.platformFilter = .all
}
if let platforms: [ProjectSpec.Platform] = jsonDictionary.json(atKeyPath: "platforms") {
self.platforms = Set(platforms)
}
}
}
@@ -137,6 +144,7 @@ extension Dependency: JSONEncodable {
"embed": embed,
"codeSign": codeSign,
"link": link,
"platforms": platforms?.map(\.rawValue).sorted()
]
if removeHeaders != Dependency.removeHeadersDefault {
+6 -1
View File
@@ -301,7 +301,12 @@ extension Target: NamedJSONDictionaryConvertible {
if jsonDictionary["dependencies"] == nil {
dependencies = []
} else {
dependencies = try jsonDictionary.json(atKeyPath: "dependencies", invalidItemBehaviour: .fail)
let dependencies: [Dependency] = try jsonDictionary.json(atKeyPath: "dependencies", invalidItemBehaviour: .fail)
self.dependencies = dependencies.filter { [platform] dependency -> Bool in
// If unspecified, all platforms are supported
guard let platforms = dependency.platforms else { return true }
return platforms.contains(platform)
}
}
if jsonDictionary["info"] != nil {
+3 -3
View File
@@ -751,7 +751,7 @@ public class PBXProjGenerator {
for dependency in targetDependencies {
let embed = dependency.embed ?? target.shouldEmbedDependencies
let platform = makePlatform(for: dependency.platform)
let platform = makePlatformFilter(for: dependency.platformFilter)
switch dependency.type {
case .target:
@@ -1320,8 +1320,8 @@ public class PBXProjGenerator {
}
}
private func makePlatform(for platform: Dependency.Platform) -> String? {
switch platform {
private func makePlatformFilter(for filter: Dependency.PlatformFilter) -> String? {
switch filter {
case .all:
return nil
case .macOS:
@@ -71,6 +71,7 @@
47D1F439B8E6D287B3F3E8D1 /* MyFramework.h in Headers */ = {isa = PBXBuildFile; fileRef = 6A58A16491CDDF968B0D56DE /* MyFramework.h */; settings = {ATTRIBUTES = (Public, ); }; };
47FC57B04A3AD83359F433EA /* StaticLibrary_ObjC.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 5A2B916A11DCC2565241359F /* StaticLibrary_ObjC.h */; };
49A4B8937BB5520B36EA33F0 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 814D72C2B921F60B759C2D4B /* Main.storyboard */; };
4C1504A05321046B3ED7A839 /* Framework2.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AB055761199DF36DB0C629A6 /* Framework2.framework */; };
4CB673A7C0C11E04F8544BDB /* Contacts.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FDB2B6A77D39CD5602F2125F /* Contacts.framework */; };
4DA7140FF84DBF39961F3409 /* NetworkSystemExtension.systemextension in Embed System Extensions */ = {isa = PBXBuildFile; fileRef = 2049B6DD2AFE85F9DC9F3EB3 /* NetworkSystemExtension.systemextension */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
4F6481557E2BEF8D749C37E3 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 187E665975BB5611AF0F27E1 /* main.m */; };
@@ -125,6 +126,7 @@
A1588BF3BFFE1DF7409CBA10 /* Framework.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8A9274BE42A03DC5DA1FAD04 /* Framework.framework */; };
A1AEAAB53EAEDA1C307871FA /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BB178D03E75929F3F5B10C56 /* Result.framework */; };
A59B3F08914812573AFF6C2D /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = FD4A16C7B8FEB7F97F3CBE3F /* libz.dylib */; };
A7438C77A05D83E7016CF044 /* Framework2.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A0DC40025AB59B688E758829 /* Framework2.framework */; };
A7D1A9942302569A9515696A /* Result.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = D296BB7355994040E197A1EE /* Result.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
A90C4C147AD175DB9F7B5114 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03CD22B8CD2E91BB97CC534E /* main.swift */; };
A949422315536EACDF8DD78A /* NetworkExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5B785B1161553A7DD6DA4255 /* NetworkExtension.framework */; };
@@ -223,6 +225,13 @@
remoteGlobalIDString = 0867B0DACEF28C11442DE8F7;
remoteInfo = App_iOS;
};
45907115465077029040BF29 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 0FBAE303E3CFC2ABAC876A77 /* Project object */;
proxyType = 1;
remoteGlobalIDString = 8B9A14DC280CCE013CC86440;
remoteInfo = Framework2_tvOS;
};
469D922BE967C6D52ED84552 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 0FBAE303E3CFC2ABAC876A77 /* Project object */;
@@ -244,6 +253,13 @@
remoteGlobalIDString = 13E8C5AB873CEE21E18E552F;
remoteInfo = StaticLibrary_ObjC_iOS;
};
59BFAC272F73B46E97B74426 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 0FBAE303E3CFC2ABAC876A77 /* Project object */;
proxyType = 1;
remoteGlobalIDString = 6ED01BC471A8C3642258E178;
remoteInfo = Framework2_watchOS;
};
610412261F48A0A36C32FC5C /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 0FBAE303E3CFC2ABAC876A77 /* Project object */;
@@ -800,6 +816,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
4C1504A05321046B3ED7A839 /* Framework2.framework in Frameworks */,
A1AEAAB53EAEDA1C307871FA /* Result.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -808,6 +825,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
A7438C77A05D83E7016CF044 /* Framework2.framework in Frameworks */,
9DF5931DAD58C35B830A0A75 /* Result.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -1716,6 +1734,7 @@
buildRules = (
);
dependencies = (
D6C733BEB62EAA62CCC68556 /* PBXTargetDependency */,
CE96B0951433713033A03DCD /* PBXTargetDependency */,
);
name = Framework_tvOS;
@@ -1805,6 +1824,7 @@
buildRules = (
);
dependencies = (
0C99705018337CE91AA34CBA /* PBXTargetDependency */,
35DF16CA4A1F88140CF69620 /* PBXTargetDependency */,
);
name = Framework_watchOS;
@@ -2783,6 +2803,11 @@
target = 1C26A6A0BC446191F311D470 /* iMessageExtension */;
targetProxy = C8FD369800D87311EC532712 /* PBXContainerItemProxy */;
};
0C99705018337CE91AA34CBA /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 6ED01BC471A8C3642258E178 /* Framework2_watchOS */;
targetProxy = 59BFAC272F73B46E97B74426 /* PBXContainerItemProxy */;
};
0D33D01C71E8002A07F02122 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 208179651927D1138D19B5AD /* App_watchOS */;
@@ -2909,6 +2934,11 @@
target = 0867B0DACEF28C11442DE8F7 /* App_iOS */;
targetProxy = 3A81C6D6875469889D53A2C5 /* PBXContainerItemProxy */;
};
D6C733BEB62EAA62CCC68556 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 8B9A14DC280CCE013CC86440 /* Framework2_tvOS */;
targetProxy = 45907115465077029040BF29 /* PBXContainerItemProxy */;
};
E84285243DE0BB361A708079 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = AE3F93DB94E7208F2F1D9A78 /* Framework_iOS */;
+6 -4
View File
@@ -119,16 +119,16 @@ targets:
PRODUCT_BUNDLE_IDENTIFIER: com.project.app
dependencies:
- target: Framework_iOS
platform: all
platformFilter: all
- target: StaticLibrary_ObjC_iOS
- carthage: Result
platform: macOS
platformFilter: macOS
- carthage: SwiftyJSON
linkType: static
platform: iOS
platformFilter: iOS
- target: Framework2_iOS
weak: true
platform: iOS
platformFilter: iOS
- target: App_watchOS
- target: iMessageApp
- sdk: Contacts.framework
@@ -284,6 +284,8 @@ targets:
dependencies:
- carthage: Result
- target: StaticLibrary_ObjC_${platform}
- target: Framework2_${platform}
platforms: [tvOS, watchOS]
Framework2:
type: framework
+10 -7
View File
@@ -376,9 +376,9 @@ class SpecLoadingTests: XCTestCase {
$0.it("parses target dependencies") {
var targetDictionary = validTarget
targetDictionary["dependencies"] = [
["target": "name", "embed": false, "platform": "all"],
["target": "project/name", "embed": false, "platform": "macOS"],
["carthage": "name", "findFrameworks": true, "platform": "iOS"],
["target": "name", "embed": false, "platformFilter": "all"],
["target": "project/name", "embed": false, "platformFilter": "macOS"],
["carthage": "name", "findFrameworks": true, "platformFilter": "iOS"],
["carthage": "name", "findFrameworks": true, "linkType": "static"],
["framework": "path", "weak": true],
["sdk": "Contacts.framework"],
@@ -386,16 +386,19 @@ class SpecLoadingTests: XCTestCase {
"sdk": "Platforms/iPhoneOS.platform/Developer/Library/Frameworks/XCTest.framework",
"root": "DEVELOPER_DIR",
],
["target": "conditionalMatch", "platforms": ["iOS"]],
["target": "conditionalMiss", "platforms": ["watchOS"]],
]
let target = try Target(name: "test", jsonDictionary: targetDictionary)
try expect(target.dependencies.count) == 7
try expect(target.dependencies[0]) == Dependency(type: .target, reference: "name", embed: false, platform: .all)
try expect(target.dependencies[1]) == Dependency(type: .target, reference: "project/name", embed: false, platform: .macOS)
try expect(target.dependencies[2]) == Dependency(type: .carthage(findFrameworks: true, linkType: .dynamic), reference: "name", platform: .iOS)
try expect(target.dependencies.count) == 8
try expect(target.dependencies[0]) == Dependency(type: .target, reference: "name", embed: false, platformFilter: .all)
try expect(target.dependencies[1]) == Dependency(type: .target, reference: "project/name", embed: false, platformFilter: .macOS)
try expect(target.dependencies[2]) == Dependency(type: .carthage(findFrameworks: true, linkType: .dynamic), reference: "name", platformFilter: .iOS)
try expect(target.dependencies[3]) == Dependency(type: .carthage(findFrameworks: true, linkType: .static), reference: "name")
try expect(target.dependencies[4]) == Dependency(type: .framework, reference: "path", weakLink: true)
try expect(target.dependencies[5]) == Dependency(type: .sdk(root: nil), reference: "Contacts.framework")
try expect(target.dependencies[6]) == Dependency(type: .sdk(root: "DEVELOPER_DIR"), reference: "Platforms/iPhoneOS.platform/Developer/Library/Frameworks/XCTest.framework")
try expect(target.dependencies[7]) == Dependency(type: .target, reference: "conditionalMatch", platforms: [.iOS])
}
$0.it("parses info plist") {
@@ -354,9 +354,9 @@ class PBXProjGeneratorTests: XCTestCase {
let target1 = Target(name: "TestAll", type: .application, platform: .iOS, sources: ["Sources"])
let target2 = Target(name: "TestiOS", type: .application, platform: .iOS, sources: ["Sources"])
let target3 = Target(name: "TestmacOS", type: .application, platform: .iOS, sources: ["Sources"])
let dependency1 = Dependency(type: .target, reference: "TestAll", platform: .all)
let dependency2 = Dependency(type: .target, reference: "TestiOS", platform: .iOS)
let dependency3 = Dependency(type: .target, reference: "TestmacOS", platform: .macOS)
let dependency1 = Dependency(type: .target, reference: "TestAll", platformFilter: .all)
let dependency2 = Dependency(type: .target, reference: "TestiOS", platformFilter: .iOS)
let dependency3 = Dependency(type: .target, reference: "TestmacOS", platformFilter: .macOS)
let target = Target(name: "Test", type: .application, platform: .iOS, sources: ["Sources"], dependencies: [dependency1, dependency2, dependency3])
let project = Project(basePath: directoryPath, name: "Test", targets: [target, target1, target2, target3])