diff --git a/CHANGELOG.md b/CHANGELOG.md index 286cf2b4..0d4227a2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ - Allow SDK dependencies to be embedded. [#922](https://github.com/yonaskolb/XcodeGen/pull/922) @k-thorat - Allow creating intermediary groups outside of the project directory. [#892](https://github.com/yonaskolb/XcodeGen/pull/892) @segiddins - Fix appex's Runpath Search Paths under macOS target. [#952](https://github.com/yonaskolb/XcodeGen/pull/952) @rinsuki +- `onlyCopyFilesOnInstall` is extended for the Embed App Extensions build phase. [#948](https://github.com/yonaskolb/XcodeGen/pull/948) @RomanPodymov ## 2.17.0 diff --git a/Docs/ProjectSpec.md b/Docs/ProjectSpec.md index 31ef26b0..3d4fb2f3 100644 --- a/Docs/ProjectSpec.md +++ b/Docs/ProjectSpec.md @@ -250,7 +250,7 @@ Settings are merged in the following order: groups, base, configs. - [ ] **transitivelyLinkDependencies**: **Bool** - If this is not specified the value from the project set in [Options](#options)`.transitivelyLinkDependencies` will be used. - [ ] **directlyEmbedCarthageDependencies**: **Bool** - If this is `true` Carthage dependencies will be embedded using an `Embed Frameworks` build phase instead of the `copy-frameworks` script. Defaults to `true` for all targets except iOS/tvOS/watchOS Applications. - [ ] **requiresObjCLinking**: **Bool** - If this is `true` any targets that link to this target will have `-ObjC` added to their `OTHER_LDFLAGS`. This is required if a static library has any catagories or extensions on Objective-C code. See [this guide](https://pewpewthespells.com/blog/objc_linker_flags.html#objc) for more details. Defaults to `true` if `type` is `library.static`. If you are 100% sure you don't have catagories or extensions on Objective-C code (pure Swift with no use of Foundation/UIKit) you can set this to `false`, otherwise it's best to leave it alone. -- [ ] **onlyCopyFilesOnInstall**: **Bool** – If this is `true`, the `Embed Frameworks` build phase will have the "Copy only when installing" chekbox checked. Defaults to `false`. +- [ ] **onlyCopyFilesOnInstall**: **Bool** – If this is `true`, the `Embed Frameworks` and `Embed App Extensions` (if available) build phases will have the "Copy only when installing" chekbox checked. Defaults to `false`. - [ ] **preBuildScripts**: **[[Build Script](#build-script)]** - Build scripts that run *before* any other build phases - [ ] **postCompileScripts**: **[[Build Script](#build-script)]** - Build scripts that run after the Compile Sources phase - [ ] **postBuildScripts**: **[[Build Script](#build-script)]** - Build scripts that run *after* any other build phases diff --git a/Sources/XcodeGenKit/PBXProjGenerator.swift b/Sources/XcodeGenKit/PBXProjGenerator.swift index 886cd70c..8a89814b 100644 --- a/Sources/XcodeGenKit/PBXProjGenerator.swift +++ b/Sources/XcodeGenKit/PBXProjGenerator.swift @@ -13,6 +13,8 @@ public class PBXProjGenerator { let projectDirectory: Path? let carthageResolver: CarthageDependencyResolver + public static let copyFilesActionMask: UInt = 8 + var sourceGenerator: SourceGenerator! var targetObjects: [String: PBXTarget] = [:] @@ -979,6 +981,17 @@ public class PBXProjGenerator { return sourceFilesByCopyFiles.mapValues { getBuildFilesForSourceFiles($0) } } + func getPBXCopyFilesBuildPhase(dstSubfolderSpec: PBXCopyFilesBuildPhase.SubFolder, name: String, files: [PBXBuildFile]) -> PBXCopyFilesBuildPhase { + return PBXCopyFilesBuildPhase( + dstPath: "", + dstSubfolderSpec: dstSubfolderSpec, + name: name, + buildActionMask: target.onlyCopyFilesOnInstall ? PBXProjGenerator.copyFilesActionMask : PBXBuildPhase.defaultBuildActionMask, + files: files, + runOnlyForDeploymentPostprocessing: target.onlyCopyFilesOnInstall ? true : false + ) + } + copyFilesBuildPhasesFiles.merge(getBuildFilesForCopyFilesPhases()) { $0 + $1 } buildPhases += try target.preBuildScripts.map { try generateBuildScript(targetName: target.name, buildScript: $0) } @@ -1076,13 +1089,8 @@ public class PBXProjGenerator { if !extensions.isEmpty { - let copyFilesPhase = addObject( - PBXCopyFilesBuildPhase( - dstPath: "", - dstSubfolderSpec: .plugins, - name: "Embed App Extensions", - files: extensions - ) + let copyFilesPhase = addObject( + getPBXCopyFilesBuildPhase(dstSubfolderSpec: .plugins, name: "Embed App Extensions", files: extensions) ) buildPhases.append(copyFilesPhase) @@ -1105,16 +1113,8 @@ public class PBXProjGenerator { copyFrameworksReferences += getBuildFilesForPhase(.frameworks) if !copyFrameworksReferences.isEmpty { - let copyFilesActionMask: UInt = 8 let copyFilesPhase = addObject( - PBXCopyFilesBuildPhase( - dstPath: "", - dstSubfolderSpec: .frameworks, - name: "Embed Frameworks", - buildActionMask: target.onlyCopyFilesOnInstall ? copyFilesActionMask : PBXBuildPhase.defaultBuildActionMask, - files: copyFrameworksReferences, - runOnlyForDeploymentPostprocessing: target.onlyCopyFilesOnInstall ? true : false - ) + getPBXCopyFilesBuildPhase(dstSubfolderSpec: .frameworks, name: "Embed Frameworks", files: copyFrameworksReferences) ) buildPhases.append(copyFilesPhase) diff --git a/Tests/XcodeGenKitTests/ProjectGeneratorTests.swift b/Tests/XcodeGenKitTests/ProjectGeneratorTests.swift index 1f2e329e..81c12d34 100644 --- a/Tests/XcodeGenKitTests/ProjectGeneratorTests.swift +++ b/Tests/XcodeGenKitTests/ProjectGeneratorTests.swift @@ -805,16 +805,91 @@ class ProjectGeneratorTests: XCTestCase { let nativeTarget = try unwrap(pbxProject.nativeTargets.first(where: { $0.name == app.name })) let buildPhases = nativeTarget.buildPhases - let embedFrameworkPhase = pbxProject + let embedFrameworksPhase = pbxProject .copyFilesBuildPhases .filter { buildPhases.contains($0) } .first { $0.dstSubfolderSpec == .frameworks } - let phase = try unwrap(embedFrameworkPhase) - try expect(phase.buildActionMask) == 8 + let phase = try unwrap(embedFrameworksPhase) + try expect(phase.buildActionMask) == PBXProjGenerator.copyFilesActionMask try expect(phase.runOnlyForDeploymentPostprocessing) == true } + $0.it("copies files only on install in the Embed App Extensions step") { + let appExtension = Target( + name: "AppExtension", + type: .appExtension, + platform: .tvOS + ) + + let app = Target( + name: "App", + type: .application, + platform: .tvOS, + dependencies: [ + Dependency(type: .target, reference: "AppExtension") + ], + onlyCopyFilesOnInstall: true + ) + + let project = Project(name: "test", targets: [app, appExtension]) + let pbxProject = try project.generatePbxProj() + let nativeTarget = try unwrap(pbxProject.nativeTargets.first(where: { $0.name == app.name })) + let buildPhases = nativeTarget.buildPhases + + let embedAppExtensionsPhase = pbxProject + .copyFilesBuildPhases + .filter { buildPhases.contains($0) } + .first { $0.dstSubfolderSpec == .plugins } + + let phase = try unwrap(embedAppExtensionsPhase) + try expect(phase.buildActionMask) == PBXProjGenerator.copyFilesActionMask + try expect(phase.runOnlyForDeploymentPostprocessing) == true + } + + $0.it("copies files only on install in the Embed Frameworks and Embed App Extensions steps") { + let appExtension = Target( + name: "AppExtension", + type: .appExtension, + platform: .tvOS + ) + + let app = Target( + name: "App", + type: .application, + platform: .tvOS, + dependencies: [ + Dependency(type: .target, reference: "AppExtension"), + Dependency(type: .framework, reference: "FrameworkA.framework"), + Dependency(type: .framework, reference: "FrameworkB.framework", embed: false), + ], + onlyCopyFilesOnInstall: true + ) + + let project = Project(name: "test", targets: [app, appExtension]) + let pbxProject = try project.generatePbxProj() + let nativeTarget = try unwrap(pbxProject.nativeTargets.first(where: { $0.name == app.name })) + let buildPhases = nativeTarget.buildPhases + + let embedFrameworksPhase = pbxProject + .copyFilesBuildPhases + .filter { buildPhases.contains($0) } + .first { $0.dstSubfolderSpec == .frameworks } + + let embedFrameworksPhaseValue = try unwrap(embedFrameworksPhase) + try expect(embedFrameworksPhaseValue.buildActionMask) == PBXProjGenerator.copyFilesActionMask + try expect(embedFrameworksPhaseValue.runOnlyForDeploymentPostprocessing) == true + + let embedAppExtensionsPhase = pbxProject + .copyFilesBuildPhases + .filter { buildPhases.contains($0) } + .first { $0.dstSubfolderSpec == .plugins } + + let embedAppExtensionsPhaseValue = try unwrap(embedAppExtensionsPhase) + try expect(embedAppExtensionsPhaseValue.buildActionMask) == PBXProjGenerator.copyFilesActionMask + try expect(embedAppExtensionsPhaseValue.runOnlyForDeploymentPostprocessing) == true + } + $0.it("sets -ObjC for targets that depend on requiresObjCLinking targets") { let requiresObjCLinking = Target( name: "requiresObjCLinking",