Skip to content

Commit d35d22f

Browse files
JakubBednarJakub Bednářyonaskolb
authored
Added support for dependency destination specification. (Resolves #1038) (#1039)
* Added support for dependency destination specification. (Resolves #1038) * More generic way covering more different dependency types. (#1038) * Added unit-test for each possible dependency combination. First test current embeding then the new one with custom copy spec. (#1038) * Review fixes. (#1038) * Minimized unit-test boiler-plate (#1038) * Update CHANGELOG.md Co-authored-by: Jakub Bednář <jakub.bednar@avast.com> Co-authored-by: Yonas Kolb <yonaskolb@users.noreply.github.com>
1 parent e2f062b commit d35d22f

File tree

8 files changed

+1522
-11
lines changed

8 files changed

+1522
-11
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
## Next Version
44

5+
### Added
6+
- Allow specifying a `copy` setting for each dependency. [#1038](https://github.com/yonaskolb/XcodeGen/pull/1039) @JakubBednar
7+
58
## 2.24.0
69

710
### Added

Docs/ProjectSpec.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -432,6 +432,19 @@ A dependency can be one of a 6 types:
432432
- [ ] **weak**: **Bool** - Whether the `Weak` setting is applied when linking the framework. Defaults to false
433433
- [ ] **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**
434434
- [ ] **platforms**: **[[Platform](#platform)]** - List of platforms this dependency should apply to. Defaults to all applicable platforms.
435+
- **copy** - Copy Files Phase for this dependency. This only applies when `embed` is true. Must be specified as an object with the following fields:
436+
- [x] **destination**: **String** - Destination of the Copy Files phase. This can be one of the following values:
437+
- `absolutePath`
438+
- `productsDirectory`
439+
- `wrapper`
440+
- `executables`
441+
- `resources`
442+
- `javaResources`
443+
- `frameworks`
444+
- `sharedFrameworks`
445+
- `sharedSupport`
446+
- `plugins`
447+
- [ ] **subpath**: **String** - The path inside of the destination to copy the files.
435448

436449
**Implicit Framework options**:
437450

Sources/ProjectSpec/Dependency.swift

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ public struct Dependency: Equatable {
1717
public var weakLink: Bool = weakLinkDefault
1818
public var platformFilter: PlatformFilter = platformFilterDefault
1919
public var platforms: Set<Platform>?
20+
public var copyPhase: BuildPhaseSpec.CopyFilesSettings?
2021

2122
public init(
2223
type: DependencyType,
@@ -27,7 +28,8 @@ public struct Dependency: Equatable {
2728
implicit: Bool = implicitDefault,
2829
weakLink: Bool = weakLinkDefault,
2930
platformFilter: PlatformFilter = platformFilterDefault,
30-
platforms: Set<Platform>? = nil
31+
platforms: Set<Platform>? = nil,
32+
copyPhase: BuildPhaseSpec.CopyFilesSettings? = nil
3133
) {
3234
self.type = type
3335
self.reference = reference
@@ -38,6 +40,7 @@ public struct Dependency: Equatable {
3840
self.weakLink = weakLink
3941
self.platformFilter = platformFilter
4042
self.platforms = platforms
43+
self.copyPhase = copyPhase
4144
}
4245

4346
public enum PlatformFilter: String, Equatable {
@@ -135,6 +138,10 @@ extension Dependency: JSONObjectConvertible {
135138
if let platforms: [ProjectSpec.Platform] = jsonDictionary.json(atKeyPath: "platforms") {
136139
self.platforms = Set(platforms)
137140
}
141+
142+
if let object: JSONDictionary = jsonDictionary.json(atKeyPath: "copy") {
143+
copyPhase = try BuildPhaseSpec.CopyFilesSettings(jsonDictionary: object)
144+
}
138145
}
139146
}
140147

@@ -144,7 +151,8 @@ extension Dependency: JSONEncodable {
144151
"embed": embed,
145152
"codeSign": codeSign,
146153
"link": link,
147-
"platforms": platforms?.map(\.rawValue).sorted()
154+
"platforms": platforms?.map(\.rawValue).sorted(),
155+
"copy": copyPhase?.toJSONValue(),
148156
]
149157

150158
if removeHeaders != Dependency.removeHeadersDefault {

Sources/XcodeGenKit/PBXProjGenerator.swift

Lines changed: 61 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -662,6 +662,7 @@ public class PBXProjGenerator {
662662
var dependencies: [PBXTargetDependency] = []
663663
var targetFrameworkBuildFiles: [PBXBuildFile] = []
664664
var frameworkBuildPaths = Set<String>()
665+
var customCopyDependenciesReferences: [PBXBuildFile] = []
665666
var copyFilesBuildPhasesFiles: [BuildPhaseSpec.CopyFilesSettings: [PBXBuildFile]] = [:]
666667
var copyFrameworksReferences: [PBXBuildFile] = []
667668
var copyResourcesReferences: [PBXBuildFile] = []
@@ -689,7 +690,11 @@ public class PBXProjGenerator {
689690
if dependency.removeHeaders {
690691
embedAttributes.append("RemoveHeadersOnCopy")
691692
}
692-
return ["ATTRIBUTES": embedAttributes]
693+
var retval: [String:Any] = ["ATTRIBUTES": embedAttributes]
694+
if let copyPhase = dependency.copyPhase {
695+
retval["COPY_PHASE"] = copyPhase
696+
}
697+
return retval
693698
}
694699

695700
func getDependencyFrameworkSettings(dependency: Dependency) -> [String: Any]? {
@@ -727,7 +732,10 @@ public class PBXProjGenerator {
727732
pbxBuildFile.platformFilter = platform
728733
let embedFile = addObject(pbxBuildFile)
729734

730-
if dependencyTarget.type.isExtension {
735+
if dependency.copyPhase != nil {
736+
// custom copy takes precedence
737+
customCopyDependenciesReferences.append(embedFile)
738+
} else if dependencyTarget.type.isExtension {
731739
// embed app extension
732740
extensions.append(embedFile)
733741
} else if dependencyTarget.type.isSystemExtension {
@@ -807,7 +815,12 @@ public class PBXProjGenerator {
807815
let pbxBuildFile = PBXBuildFile(file: fileReference, settings: getEmbedSettings(dependency: dependency, codeSign: dependency.codeSign ?? true))
808816
pbxBuildFile.platformFilter = platform
809817
let embedFile = addObject(pbxBuildFile)
810-
copyFrameworksReferences.append(embedFile)
818+
819+
if dependency.copyPhase != nil {
820+
customCopyDependenciesReferences.append(embedFile)
821+
} else {
822+
copyFrameworksReferences.append(embedFile)
823+
}
811824
}
812825
case .sdk(let root):
813826

@@ -858,7 +871,12 @@ public class PBXProjGenerator {
858871
let pbxBuildFile = PBXBuildFile(file: fileReference, settings: getEmbedSettings(dependency: dependency, codeSign: dependency.codeSign ?? true))
859872
pbxBuildFile.platformFilter = platform
860873
let embedFile = addObject(pbxBuildFile)
861-
copyFrameworksReferences.append(embedFile)
874+
875+
if dependency.copyPhase != nil {
876+
customCopyDependenciesReferences.append(embedFile)
877+
} else {
878+
copyFrameworksReferences.append(embedFile)
879+
}
862880
}
863881

864882
case .carthage(let findFrameworks, let linkType):
@@ -923,7 +941,12 @@ public class PBXProjGenerator {
923941
settings: getEmbedSettings(dependency: dependency, codeSign: dependency.codeSign ?? true))
924942
pbxBuildFile.platformFilter = platform
925943
let embedFile = addObject(pbxBuildFile)
926-
copyFrameworksReferences.append(embedFile)
944+
945+
if dependency.copyPhase != nil {
946+
customCopyDependenciesReferences.append(embedFile)
947+
} else {
948+
copyFrameworksReferences.append(embedFile)
949+
}
927950
}
928951
case .bundle:
929952
// Static and dynamic libraries can't copy resources
@@ -969,7 +992,11 @@ public class PBXProjGenerator {
969992
let embedFile = addObject(
970993
PBXBuildFile(file: fileReference, settings: getEmbedSettings(dependency: dependency, codeSign: dependency.codeSign ?? true))
971994
)
972-
copyFrameworksReferences.append(embedFile)
995+
if dependency.copyPhase != nil {
996+
customCopyDependenciesReferences.append(embedFile)
997+
} else {
998+
copyFrameworksReferences.append(embedFile)
999+
}
9731000
} else {
9741001
carthageFrameworksToEmbed.append(dependency.reference)
9751002
}
@@ -1016,6 +1043,19 @@ public class PBXProjGenerator {
10161043
)
10171044
}
10181045

1046+
func splitCopyDepsByDestination(_ references: [PBXBuildFile]) -> [BuildPhaseSpec.CopyFilesSettings : [PBXBuildFile]] {
1047+
1048+
var retval = [BuildPhaseSpec.CopyFilesSettings : [PBXBuildFile]]()
1049+
for reference in references {
1050+
1051+
guard let key = reference.settings?["COPY_PHASE"] as? BuildPhaseSpec.CopyFilesSettings else { continue }
1052+
var filesWithSameDestination = retval[key] ?? [PBXBuildFile]()
1053+
filesWithSameDestination.append(reference)
1054+
retval[key] = filesWithSameDestination
1055+
}
1056+
return retval
1057+
}
1058+
10191059
copyFilesBuildPhasesFiles.merge(getBuildFilesForCopyFilesPhases()) { $0 + $1 }
10201060

10211061
buildPhases += try target.preBuildScripts.map { try generateBuildScript(targetName: target.name, buildScript: $0) }
@@ -1154,6 +1194,21 @@ public class PBXProjGenerator {
11541194
buildPhases.append(copyFilesPhase)
11551195
}
11561196

1197+
if !customCopyDependenciesReferences.isEmpty {
1198+
1199+
let splitted = splitCopyDepsByDestination(customCopyDependenciesReferences)
1200+
for (phase, references) in splitted {
1201+
1202+
guard let destination = phase.destination.destination else { continue }
1203+
1204+
let copyFilesPhase = addObject(
1205+
getPBXCopyFilesBuildPhase(dstSubfolderSpec: destination, dstPath:phase.subpath, name: "Embed Dependencies", files: references)
1206+
)
1207+
1208+
buildPhases.append(copyFilesPhase)
1209+
}
1210+
}
1211+
11571212
if !copyWatchReferences.isEmpty {
11581213

11591214
let copyFilesPhase = addObject(

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

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@
111111
7F658343A505B824321E086B /* Headers in Headers */ = {isa = PBXBuildFile; fileRef = 2E1E747C7BC434ADB80CC269 /* Headers */; settings = {ATTRIBUTES = (Public, ); }; };
112112
803B7CE086CFBA409F9D1ED7 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 108BB29172D27BE3BD1E7F35 /* Assets.xcassets */; };
113113
818D448D4DDD6649B5B26098 /* example.mp4 in Resources */ = {isa = PBXBuildFile; fileRef = 28360ECA4D727FAA58557A81 /* example.mp4 */; settings = {ASSET_TAGS = (tag1, tag2, ); }; };
114+
81DFAB3A7633CE97929B9B2A /* Framework.framework in Embed Dependencies */ = {isa = PBXBuildFile; fileRef = 41FC82ED1C4C3B7B3D7B2FB7 /* Framework.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
114115
8267B75289E9D6C7B38FC426 /* DriverKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C0A428E67153BB40184F37BE /* DriverKit.framework */; };
115116
87927928A8A3460166ACB819 /* SwiftFileInDotPath.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F430AABE04B7499B458D9DB /* SwiftFileInDotPath.swift */; settings = {COMPILER_FLAGS = "-Werror"; }; };
116117
8C941A6EF08069CB3CB88FC1 /* Result.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 0C5AC2545AE4D4F7F44E2E9B /* Result.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
@@ -142,7 +143,6 @@
142143
BAA1C1E3828F5D43546AF997 /* libc++.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 0BB1B49A91B892152D68ED76 /* libc++.tbd */; };
143144
BB06A57E259D0D2A001EA21F /* Result.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 0C5AC2545AE4D4F7F44E2E9B /* Result.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
144145
BD1419893577E6CEDF8CBA83 /* Result.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 0C5AC2545AE4D4F7F44E2E9B /* Result.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
145-
BD95416F2005199F6B3572CF /* Framework.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 41FC82ED1C4C3B7B3D7B2FB7 /* Framework.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
146146
BFCCC56337A5D9D513C1C791 /* module.modulemap in CopyFiles */ = {isa = PBXBuildFile; fileRef = F2950763C4C568CC85021D18 /* module.modulemap */; };
147147
C093BF20B99FE892D0F06B2D /* libEndpointSecurity.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 0BC75409252FF15F540FBB7B /* libEndpointSecurity.tbd */; };
148148
C3672B561F456794151C047C /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4C3FE6B986506724DAB5D0F /* ViewController.swift */; };
@@ -578,6 +578,17 @@
578578
name = "Embed App Extensions";
579579
runOnlyForDeploymentPostprocessing = 0;
580580
};
581+
CF6B94E7B2D2312582A526F5 /* Embed Dependencies */ = {
582+
isa = PBXCopyFilesBuildPhase;
583+
buildActionMask = 2147483647;
584+
dstPath = test;
585+
dstSubfolderSpec = 13;
586+
files = (
587+
81DFAB3A7633CE97929B9B2A /* Framework.framework in Embed Dependencies */,
588+
);
589+
name = "Embed Dependencies";
590+
runOnlyForDeploymentPostprocessing = 0;
591+
};
581592
DE875E9A37F7CB9C347AEFA0 /* Embed System Extensions */ = {
582593
isa = PBXCopyFilesBuildPhase;
583594
buildActionMask = 2147483647;
@@ -607,7 +618,6 @@
607618
dstPath = "";
608619
dstSubfolderSpec = 10;
609620
files = (
610-
BD95416F2005199F6B3572CF /* Framework.framework in Embed Frameworks */,
611621
A7D1A9942302569A9515696A /* Result.framework in Embed Frameworks */,
612622
);
613623
name = "Embed Frameworks";
@@ -1529,6 +1539,7 @@
15291539
A6E1C88C073F8CC6B5B072B6 /* Frameworks */,
15301540
DE875E9A37F7CB9C347AEFA0 /* Embed System Extensions */,
15311541
F8CDEFED6ED131A09041F995 /* Embed Frameworks */,
1542+
CF6B94E7B2D2312582A526F5 /* Embed Dependencies */,
15321543
);
15331544
buildRules = (
15341545
);

Tests/Fixtures/TestProject/project.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@ targets:
5757
optional: true
5858
dependencies:
5959
- target: Framework_macOS
60+
copy:
61+
destination: plugins
62+
subpath: "test"
6063
- target: XPC Service
6164
- target: NetworkSystemExtension
6265
- target: EndpointSecuritySystemExtension

Tests/ProjectSpecTests/ProjectSpecTests.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -398,7 +398,8 @@ class ProjectSpecTests: XCTestCase {
398398
codeSign: true,
399399
link: true,
400400
implicit: true,
401-
weakLink: true)],
401+
weakLink: true,
402+
copyPhase: BuildPhaseSpec.CopyFilesSettings(destination: .frameworks, subpath: "example", phaseOrder: .postCompile))],
402403
info: Plist(path: "info.plist", attributes: ["foo": "bar"]),
403404
entitlements: Plist(path: "entitlements.plist", attributes: ["foo": "bar"]),
404405
transitivelyLinkDependencies: true,

0 commit comments

Comments
 (0)