Skip to content

Commit 81bd52b

Browse files
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 <[email protected]>
1 parent 4455919 commit 81bd52b

File tree

9 files changed

+80
-27
lines changed

9 files changed

+80
-27
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66

77
- Added support for DocC Catalogs [#1091](https://github.com/yonaskolb/XcodeGen/pull/1091) @brevansio
88
- Added support for "driver-extension" and "system-extension" product types [#1092](https://github.com/yonaskolb/XcodeGen/issues/1092) @vgorloff
9+
- Add support for conditionally linking dependencies for specific platforms [#1087](https://github.com/yonaskolb/XcodeGen/pull/1087) @daltonclaybrook
10+
11+
### Changed
12+
- **Breaking**: Rename the `platform` field on `Dependency` to `platformFilter` [#1087](https://github.com/yonaskolb/XcodeGen/pull/1087) @daltonclaybrook
913

1014
## 2.23.1
1115

Docs/ProjectSpec.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -430,7 +430,8 @@ A dependency can be one of a 6 types:
430430
- [ ] **codeSign**: **Bool** - Whether the `codeSignOnCopy` setting is applied when embedding framework. Defaults to true
431431
- [ ] **removeHeaders**: **Bool** - Whether the `removeHeadersOnCopy` setting is applied when embedding the framework. Defaults to true
432432
- [ ] **weak**: **Bool** - Whether the `Weak` setting is applied when linking the framework. Defaults to false
433-
- [ ] **platform**: **String** - Add dependency to selected platforms. Available platforms are: **iOS**, **macOS** and **all**. Defaults is **all**
433+
- [ ] **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**
434+
- [ ] **platforms**: **[[Platform](#platform)]** - List of platforms this dependency should apply to. Defaults to all applicable platforms.
434435

435436
**Implicit Framework options**:
436437

Sources/ProjectSpec/Dependency.swift

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ public struct Dependency: Equatable {
55
public static let removeHeadersDefault = true
66
public static let implicitDefault = false
77
public static let weakLinkDefault = false
8-
public static let platformDefault: Platform = .all
8+
public static let platformFilterDefault: PlatformFilter = .all
99

1010
public var type: DependencyType
1111
public var reference: String
@@ -15,7 +15,8 @@ public struct Dependency: Equatable {
1515
public var link: Bool?
1616
public var implicit: Bool = implicitDefault
1717
public var weakLink: Bool = weakLinkDefault
18-
public var platform: Platform = platformDefault
18+
public var platformFilter: PlatformFilter = platformFilterDefault
19+
public var platforms: Set<Platform>?
1920

2021
public init(
2122
type: DependencyType,
@@ -25,7 +26,8 @@ public struct Dependency: Equatable {
2526
link: Bool? = nil,
2627
implicit: Bool = implicitDefault,
2728
weakLink: Bool = weakLinkDefault,
28-
platform: Platform = platformDefault
29+
platformFilter: PlatformFilter = platformFilterDefault,
30+
platforms: Set<Platform>? = nil
2931
) {
3032
self.type = type
3133
self.reference = reference
@@ -34,10 +36,11 @@ public struct Dependency: Equatable {
3436
self.link = link
3537
self.implicit = implicit
3638
self.weakLink = weakLink
37-
self.platform = platform
39+
self.platformFilter = platformFilter
40+
self.platforms = platforms
3841
}
3942

40-
public enum Platform: String, Equatable {
43+
public enum PlatformFilter: String, Equatable {
4144
case all
4245
case iOS
4346
case macOS
@@ -123,10 +126,14 @@ extension Dependency: JSONObjectConvertible {
123126
weakLink = bool
124127
}
125128

126-
if let platformString: String = jsonDictionary.json(atKeyPath: "platform"), let platform = Platform(rawValue: platformString) {
127-
self.platform = platform
129+
if let platformFilterString: String = jsonDictionary.json(atKeyPath: "platformFilter"), let platformFilter = PlatformFilter(rawValue: platformFilterString) {
130+
self.platformFilter = platformFilter
128131
} else {
129-
self.platform = .all
132+
self.platformFilter = .all
133+
}
134+
135+
if let platforms: [ProjectSpec.Platform] = jsonDictionary.json(atKeyPath: "platforms") {
136+
self.platforms = Set(platforms)
130137
}
131138
}
132139
}
@@ -137,6 +144,7 @@ extension Dependency: JSONEncodable {
137144
"embed": embed,
138145
"codeSign": codeSign,
139146
"link": link,
147+
"platforms": platforms?.map(\.rawValue).sorted()
140148
]
141149

142150
if removeHeaders != Dependency.removeHeadersDefault {

Sources/ProjectSpec/Target.swift

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -301,7 +301,12 @@ extension Target: NamedJSONDictionaryConvertible {
301301
if jsonDictionary["dependencies"] == nil {
302302
dependencies = []
303303
} else {
304-
dependencies = try jsonDictionary.json(atKeyPath: "dependencies", invalidItemBehaviour: .fail)
304+
let dependencies: [Dependency] = try jsonDictionary.json(atKeyPath: "dependencies", invalidItemBehaviour: .fail)
305+
self.dependencies = dependencies.filter { [platform] dependency -> Bool in
306+
// If unspecified, all platforms are supported
307+
guard let platforms = dependency.platforms else { return true }
308+
return platforms.contains(platform)
309+
}
305310
}
306311

307312
if jsonDictionary["info"] != nil {

Sources/XcodeGenKit/PBXProjGenerator.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -751,7 +751,7 @@ public class PBXProjGenerator {
751751
for dependency in targetDependencies {
752752

753753
let embed = dependency.embed ?? target.shouldEmbedDependencies
754-
let platform = makePlatform(for: dependency.platform)
754+
let platform = makePlatformFilter(for: dependency.platformFilter)
755755

756756
switch dependency.type {
757757
case .target:
@@ -1320,8 +1320,8 @@ public class PBXProjGenerator {
13201320
}
13211321
}
13221322

1323-
private func makePlatform(for platform: Dependency.Platform) -> String? {
1324-
switch platform {
1323+
private func makePlatformFilter(for filter: Dependency.PlatformFilter) -> String? {
1324+
switch filter {
13251325
case .all:
13261326
return nil
13271327
case .macOS:

Tests/Fixtures/TestProject/Project.xcodeproj/project.pbxproj

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@
7171
47D1F439B8E6D287B3F3E8D1 /* MyFramework.h in Headers */ = {isa = PBXBuildFile; fileRef = 6A58A16491CDDF968B0D56DE /* MyFramework.h */; settings = {ATTRIBUTES = (Public, ); }; };
7272
47FC57B04A3AD83359F433EA /* StaticLibrary_ObjC.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 5A2B916A11DCC2565241359F /* StaticLibrary_ObjC.h */; };
7373
49A4B8937BB5520B36EA33F0 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 814D72C2B921F60B759C2D4B /* Main.storyboard */; };
74+
4C1504A05321046B3ED7A839 /* Framework2.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AB055761199DF36DB0C629A6 /* Framework2.framework */; };
7475
4CB673A7C0C11E04F8544BDB /* Contacts.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FDB2B6A77D39CD5602F2125F /* Contacts.framework */; };
7576
4DA7140FF84DBF39961F3409 /* NetworkSystemExtension.systemextension in Embed System Extensions */ = {isa = PBXBuildFile; fileRef = 2049B6DD2AFE85F9DC9F3EB3 /* NetworkSystemExtension.systemextension */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
7677
4F6481557E2BEF8D749C37E3 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 187E665975BB5611AF0F27E1 /* main.m */; };
@@ -125,6 +126,7 @@
125126
A1588BF3BFFE1DF7409CBA10 /* Framework.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8A9274BE42A03DC5DA1FAD04 /* Framework.framework */; };
126127
A1AEAAB53EAEDA1C307871FA /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BB178D03E75929F3F5B10C56 /* Result.framework */; };
127128
A59B3F08914812573AFF6C2D /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = FD4A16C7B8FEB7F97F3CBE3F /* libz.dylib */; };
129+
A7438C77A05D83E7016CF044 /* Framework2.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A0DC40025AB59B688E758829 /* Framework2.framework */; };
128130
A7D1A9942302569A9515696A /* Result.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = D296BB7355994040E197A1EE /* Result.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
129131
A90C4C147AD175DB9F7B5114 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03CD22B8CD2E91BB97CC534E /* main.swift */; };
130132
A949422315536EACDF8DD78A /* NetworkExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5B785B1161553A7DD6DA4255 /* NetworkExtension.framework */; };
@@ -223,6 +225,13 @@
223225
remoteGlobalIDString = 0867B0DACEF28C11442DE8F7;
224226
remoteInfo = App_iOS;
225227
};
228+
45907115465077029040BF29 /* PBXContainerItemProxy */ = {
229+
isa = PBXContainerItemProxy;
230+
containerPortal = 0FBAE303E3CFC2ABAC876A77 /* Project object */;
231+
proxyType = 1;
232+
remoteGlobalIDString = 8B9A14DC280CCE013CC86440;
233+
remoteInfo = Framework2_tvOS;
234+
};
226235
469D922BE967C6D52ED84552 /* PBXContainerItemProxy */ = {
227236
isa = PBXContainerItemProxy;
228237
containerPortal = 0FBAE303E3CFC2ABAC876A77 /* Project object */;
@@ -244,6 +253,13 @@
244253
remoteGlobalIDString = 13E8C5AB873CEE21E18E552F;
245254
remoteInfo = StaticLibrary_ObjC_iOS;
246255
};
256+
59BFAC272F73B46E97B74426 /* PBXContainerItemProxy */ = {
257+
isa = PBXContainerItemProxy;
258+
containerPortal = 0FBAE303E3CFC2ABAC876A77 /* Project object */;
259+
proxyType = 1;
260+
remoteGlobalIDString = 6ED01BC471A8C3642258E178;
261+
remoteInfo = Framework2_watchOS;
262+
};
247263
610412261F48A0A36C32FC5C /* PBXContainerItemProxy */ = {
248264
isa = PBXContainerItemProxy;
249265
containerPortal = 0FBAE303E3CFC2ABAC876A77 /* Project object */;
@@ -800,6 +816,7 @@
800816
isa = PBXFrameworksBuildPhase;
801817
buildActionMask = 2147483647;
802818
files = (
819+
4C1504A05321046B3ED7A839 /* Framework2.framework in Frameworks */,
803820
A1AEAAB53EAEDA1C307871FA /* Result.framework in Frameworks */,
804821
);
805822
runOnlyForDeploymentPostprocessing = 0;
@@ -808,6 +825,7 @@
808825
isa = PBXFrameworksBuildPhase;
809826
buildActionMask = 2147483647;
810827
files = (
828+
A7438C77A05D83E7016CF044 /* Framework2.framework in Frameworks */,
811829
9DF5931DAD58C35B830A0A75 /* Result.framework in Frameworks */,
812830
);
813831
runOnlyForDeploymentPostprocessing = 0;
@@ -1716,6 +1734,7 @@
17161734
buildRules = (
17171735
);
17181736
dependencies = (
1737+
D6C733BEB62EAA62CCC68556 /* PBXTargetDependency */,
17191738
CE96B0951433713033A03DCD /* PBXTargetDependency */,
17201739
);
17211740
name = Framework_tvOS;
@@ -1805,6 +1824,7 @@
18051824
buildRules = (
18061825
);
18071826
dependencies = (
1827+
0C99705018337CE91AA34CBA /* PBXTargetDependency */,
18081828
35DF16CA4A1F88140CF69620 /* PBXTargetDependency */,
18091829
);
18101830
name = Framework_watchOS;
@@ -2783,6 +2803,11 @@
27832803
target = 1C26A6A0BC446191F311D470 /* iMessageExtension */;
27842804
targetProxy = C8FD369800D87311EC532712 /* PBXContainerItemProxy */;
27852805
};
2806+
0C99705018337CE91AA34CBA /* PBXTargetDependency */ = {
2807+
isa = PBXTargetDependency;
2808+
target = 6ED01BC471A8C3642258E178 /* Framework2_watchOS */;
2809+
targetProxy = 59BFAC272F73B46E97B74426 /* PBXContainerItemProxy */;
2810+
};
27862811
0D33D01C71E8002A07F02122 /* PBXTargetDependency */ = {
27872812
isa = PBXTargetDependency;
27882813
target = 208179651927D1138D19B5AD /* App_watchOS */;
@@ -2909,6 +2934,11 @@
29092934
target = 0867B0DACEF28C11442DE8F7 /* App_iOS */;
29102935
targetProxy = 3A81C6D6875469889D53A2C5 /* PBXContainerItemProxy */;
29112936
};
2937+
D6C733BEB62EAA62CCC68556 /* PBXTargetDependency */ = {
2938+
isa = PBXTargetDependency;
2939+
target = 8B9A14DC280CCE013CC86440 /* Framework2_tvOS */;
2940+
targetProxy = 45907115465077029040BF29 /* PBXContainerItemProxy */;
2941+
};
29122942
E84285243DE0BB361A708079 /* PBXTargetDependency */ = {
29132943
isa = PBXTargetDependency;
29142944
target = AE3F93DB94E7208F2F1D9A78 /* Framework_iOS */;

Tests/Fixtures/TestProject/project.yml

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -119,16 +119,16 @@ targets:
119119
PRODUCT_BUNDLE_IDENTIFIER: com.project.app
120120
dependencies:
121121
- target: Framework_iOS
122-
platform: all
122+
platformFilter: all
123123
- target: StaticLibrary_ObjC_iOS
124124
- carthage: Result
125-
platform: macOS
125+
platformFilter: macOS
126126
- carthage: SwiftyJSON
127127
linkType: static
128-
platform: iOS
128+
platformFilter: iOS
129129
- target: Framework2_iOS
130130
weak: true
131-
platform: iOS
131+
platformFilter: iOS
132132
- target: App_watchOS
133133
- target: iMessageApp
134134
- sdk: Contacts.framework
@@ -284,6 +284,8 @@ targets:
284284
dependencies:
285285
- carthage: Result
286286
- target: StaticLibrary_ObjC_${platform}
287+
- target: Framework2_${platform}
288+
platforms: [tvOS, watchOS]
287289

288290
Framework2:
289291
type: framework

Tests/ProjectSpecTests/SpecLoadingTests.swift

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -376,26 +376,29 @@ class SpecLoadingTests: XCTestCase {
376376
$0.it("parses target dependencies") {
377377
var targetDictionary = validTarget
378378
targetDictionary["dependencies"] = [
379-
["target": "name", "embed": false, "platform": "all"],
380-
["target": "project/name", "embed": false, "platform": "macOS"],
381-
["carthage": "name", "findFrameworks": true, "platform": "iOS"],
379+
["target": "name", "embed": false, "platformFilter": "all"],
380+
["target": "project/name", "embed": false, "platformFilter": "macOS"],
381+
["carthage": "name", "findFrameworks": true, "platformFilter": "iOS"],
382382
["carthage": "name", "findFrameworks": true, "linkType": "static"],
383383
["framework": "path", "weak": true],
384384
["sdk": "Contacts.framework"],
385385
[
386386
"sdk": "Platforms/iPhoneOS.platform/Developer/Library/Frameworks/XCTest.framework",
387387
"root": "DEVELOPER_DIR",
388388
],
389+
["target": "conditionalMatch", "platforms": ["iOS"]],
390+
["target": "conditionalMiss", "platforms": ["watchOS"]],
389391
]
390392
let target = try Target(name: "test", jsonDictionary: targetDictionary)
391-
try expect(target.dependencies.count) == 7
392-
try expect(target.dependencies[0]) == Dependency(type: .target, reference: "name", embed: false, platform: .all)
393-
try expect(target.dependencies[1]) == Dependency(type: .target, reference: "project/name", embed: false, platform: .macOS)
394-
try expect(target.dependencies[2]) == Dependency(type: .carthage(findFrameworks: true, linkType: .dynamic), reference: "name", platform: .iOS)
393+
try expect(target.dependencies.count) == 8
394+
try expect(target.dependencies[0]) == Dependency(type: .target, reference: "name", embed: false, platformFilter: .all)
395+
try expect(target.dependencies[1]) == Dependency(type: .target, reference: "project/name", embed: false, platformFilter: .macOS)
396+
try expect(target.dependencies[2]) == Dependency(type: .carthage(findFrameworks: true, linkType: .dynamic), reference: "name", platformFilter: .iOS)
395397
try expect(target.dependencies[3]) == Dependency(type: .carthage(findFrameworks: true, linkType: .static), reference: "name")
396398
try expect(target.dependencies[4]) == Dependency(type: .framework, reference: "path", weakLink: true)
397399
try expect(target.dependencies[5]) == Dependency(type: .sdk(root: nil), reference: "Contacts.framework")
398400
try expect(target.dependencies[6]) == Dependency(type: .sdk(root: "DEVELOPER_DIR"), reference: "Platforms/iPhoneOS.platform/Developer/Library/Frameworks/XCTest.framework")
401+
try expect(target.dependencies[7]) == Dependency(type: .target, reference: "conditionalMatch", platforms: [.iOS])
399402
}
400403

401404
$0.it("parses info plist") {

Tests/XcodeGenKitTests/PBXProjGeneratorTests.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -354,9 +354,9 @@ class PBXProjGeneratorTests: XCTestCase {
354354
let target1 = Target(name: "TestAll", type: .application, platform: .iOS, sources: ["Sources"])
355355
let target2 = Target(name: "TestiOS", type: .application, platform: .iOS, sources: ["Sources"])
356356
let target3 = Target(name: "TestmacOS", type: .application, platform: .iOS, sources: ["Sources"])
357-
let dependency1 = Dependency(type: .target, reference: "TestAll", platform: .all)
358-
let dependency2 = Dependency(type: .target, reference: "TestiOS", platform: .iOS)
359-
let dependency3 = Dependency(type: .target, reference: "TestmacOS", platform: .macOS)
357+
let dependency1 = Dependency(type: .target, reference: "TestAll", platformFilter: .all)
358+
let dependency2 = Dependency(type: .target, reference: "TestiOS", platformFilter: .iOS)
359+
let dependency3 = Dependency(type: .target, reference: "TestmacOS", platformFilter: .macOS)
360360
let target = Target(name: "Test", type: .application, platform: .iOS, sources: ["Sources"], dependencies: [dependency1, dependency2, dependency3])
361361
let project = Project(basePath: directoryPath, name: "Test", targets: [target, target1, target2, target3])
362362

0 commit comments

Comments
 (0)