From a461bec373713cdfdcd19749bd2886a368d4ded2 Mon Sep 17 00:00:00 2001 From: Dalton Claybrook Date: Tue, 3 Mar 2020 21:23:51 -0500 Subject: [PATCH 1/5] Add support for using remote runnables with an Apple Watch scheme --- Sources/XcodeGenKit/SchemeGenerator.swift | 62 +++++++++++++++++++---- 1 file changed, 52 insertions(+), 10 deletions(-) diff --git a/Sources/XcodeGenKit/SchemeGenerator.swift b/Sources/XcodeGenKit/SchemeGenerator.swift index afd240be8..2f0f3bc40 100644 --- a/Sources/XcodeGenKit/SchemeGenerator.swift +++ b/Sources/XcodeGenKit/SchemeGenerator.swift @@ -57,14 +57,15 @@ public class SchemeGenerator { let debugConfig = suitableConfig(for: .debug, in: project) let releaseConfig = suitableConfig(for: .release, in: project) - let scheme = Scheme.init( + let scheme = Scheme( name: schemeName, target: target, targetScheme: targetScheme, + project: project, debugConfig: debugConfig.name, releaseConfig: releaseConfig.name ) - let xcscheme = try generateScheme(scheme) + let xcscheme = try generateScheme(scheme, for: target) xcschemes.append(xcscheme) } else { for configVariant in targetScheme.configVariants { @@ -80,10 +81,11 @@ public class SchemeGenerator { name: schemeName, target: target, targetScheme: targetScheme, + project: project, debugConfig: debugConfig.name, releaseConfig: releaseConfig.name ) - let xcscheme = try generateScheme(scheme) + let xcscheme = try generateScheme(scheme, for: target) xcschemes.append(xcscheme) } } @@ -93,7 +95,7 @@ public class SchemeGenerator { return xcschemes } - public func generateScheme(_ scheme: Scheme) throws -> XCScheme { + public func generateScheme(_ scheme: Scheme, for target: Target? = nil) throws -> XCScheme { func getBuildableReference(_ target: TargetReference) throws -> XCScheme.BuildableReference { let pbxProj: PBXProj @@ -159,11 +161,11 @@ public class SchemeGenerator { return XCScheme.ExecutionAction(scriptText: action.script, title: action.name, environmentBuildable: environmentBuildable) } - let target = project.getTarget(scheme.build.targets.first!.target.name) - let shouldExecuteOnLaunch = target?.type.isExecutable == true + let schemeTarget = target ?? project.getTarget(scheme.build.targets.first!.target.name) + let shouldExecuteOnLaunch = schemeTarget?.type.isExecutable == true let buildableReference = buildActionEntries.first!.buildableReference - let productRunable = XCScheme.BuildableProductRunnable(buildableReference: buildableReference) + let runnables = makeProductRunnables(for: schemeTarget, buildableReference: buildableReference) let buildAction = XCScheme.BuildAction( buildActionEntries: buildActionEntries, @@ -225,7 +227,7 @@ public class SchemeGenerator { locationScenarioReference = XCScheme.LocationScenarioReference(identifier: identifier, referenceType: referenceType.rawValue) } let launchAction = XCScheme.LaunchAction( - runnable: shouldExecuteOnLaunch ? productRunable : nil, + runnable: shouldExecuteOnLaunch ? runnables.launch : nil, buildConfiguration: scheme.run?.config ?? defaultDebugConfig.name, preActions: scheme.run?.preActions.map(getExecutionAction) ?? [], postActions: scheme.run?.postActions.map(getExecutionAction) ?? [], @@ -243,7 +245,7 @@ public class SchemeGenerator { ) let profileAction = XCScheme.ProfileAction( - buildableProductRunnable: productRunable, + buildableProductRunnable: runnables.profile, buildConfiguration: scheme.profile?.config ?? defaultReleaseConfig.name, preActions: scheme.profile?.preActions.map(getExecutionAction) ?? [], postActions: scheme.profile?.postActions.map(getExecutionAction) ?? [], @@ -274,6 +276,20 @@ public class SchemeGenerator { archiveAction: archiveAction ) } + + private func makeProductRunnables(for target: Target?, buildableReference: XCScheme.BuildableReference) -> (launch: XCScheme.Runnable, profile: XCScheme.BuildableProductRunnable) { + let buildable = XCScheme.BuildableProductRunnable(buildableReference: buildableReference) + if target?.type.isWatchApp == true { + let remote = XCScheme.RemoteRunnable( + buildableReference: buildableReference, + bundleIdentifier: "com.apple.Carousel", + runnableDebuggingMode: "2" + ) + return (remote, buildable) + } else { + return (buildable, buildable) + } + } } enum SchemeGenerationError: Error, CustomStringConvertible { @@ -292,7 +308,7 @@ enum SchemeGenerationError: Error, CustomStringConvertible { } extension Scheme { - public init(name: String, target: Target, targetScheme: TargetScheme, debugConfig: String, releaseConfig: String) { + public init(name: String, target: Target, targetScheme: TargetScheme, project: Project, debugConfig: String, releaseConfig: String) { self.init( name: name, build: .init( @@ -339,4 +355,30 @@ extension Scheme { ) ) } + + private static func buildTargets(for target: Target, project: Project) -> [BuildTarget] { + let buildTarget = Scheme.BuildTarget(target: TargetReference.local(target.name)) + switch target.type { + case .watchApp, .watch2App: + let hostTarget = project.targets + .first { projectTarget in + projectTarget.dependencies.contains { $0.reference == target.name } + } + .map { BuildTarget(target: TargetReference.local($0.name)) } + return hostTarget.map { [buildTarget, $0] } ?? [buildTarget] + default: + return [buildTarget] + } + } +} + +extension PBXProductType { + var isWatchApp: Bool { + switch self { + case .watchApp, .watch2App: + return true + default: + return false + } + } } From 43fb3004914e9c074f388818a2833f5c75b71bf8 Mon Sep 17 00:00:00 2001 From: Dalton Claybrook Date: Tue, 3 Mar 2020 21:55:02 -0500 Subject: [PATCH 2/5] Add tests for watch scheme --- .../xcschemes/App_watchOS.xcscheme | 21 ++++++++-- .../SchemeGeneratorTests.swift | 42 +++++++++++++++++++ 2 files changed, 60 insertions(+), 3 deletions(-) diff --git a/Tests/Fixtures/TestProject/Project.xcodeproj/xcshareddata/xcschemes/App_watchOS.xcscheme b/Tests/Fixtures/TestProject/Project.xcodeproj/xcshareddata/xcschemes/App_watchOS.xcscheme index 5daa2ee99..c4928b9f8 100644 --- a/Tests/Fixtures/TestProject/Project.xcodeproj/xcshareddata/xcschemes/App_watchOS.xcscheme +++ b/Tests/Fixtures/TestProject/Project.xcodeproj/xcshareddata/xcschemes/App_watchOS.xcscheme @@ -20,6 +20,20 @@ ReferencedContainer = "container:Project.xcodeproj"> + + + + - + - + diff --git a/Tests/XcodeGenKitTests/SchemeGeneratorTests.swift b/Tests/XcodeGenKitTests/SchemeGeneratorTests.swift index b3459ff3c..81d493cea 100644 --- a/Tests/XcodeGenKitTests/SchemeGeneratorTests.swift +++ b/Tests/XcodeGenKitTests/SchemeGeneratorTests.swift @@ -336,6 +336,48 @@ class SchemeGeneratorTests: XCTestCase { try expect(buildableReference?.blueprintName) == "ExternalTarget" try expect(buildableReference?.referencedContainer) == "container:\(externalProject.string)" } + + $0.it("generates scheme with remote runnable for watch") { + let xcscheme = try self.makeWatchScheme(appType: .watch2App, extensionType: .watch2Extension) + try expect(xcscheme.launchAction?.runnable).beOfType(XCScheme.RemoteRunnable.self) + } + + $0.it("generates scheme with host target build action for watch") { + let xcscheme = try self.makeWatchScheme(appType: .watch2App, extensionType: .watch2Extension) + let buildEntries = xcscheme.buildAction?.buildActionEntries ?? [] + try expect(buildEntries.count) == 2 + try expect(buildEntries.first?.buildableReference.blueprintName) == "WatchApp" + try expect(buildEntries.last?.buildableReference.blueprintName) == "HostApp" + } } } + + // MARK: - Helpers + + private func makeWatchScheme(appType: PBXProductType, extensionType: PBXProductType) throws -> XCScheme { + let watchExtension = Target( + name: "WatchExtension", + type: extensionType, + platform: .watchOS + ) + let watchApp = Target( + name: "WatchApp", + type: appType, + platform: .watchOS, + dependencies: [Dependency(type: .target, reference: watchExtension.name)], + scheme: TargetScheme() + ) + let hostApp = Target( + name: "HostApp", + type: .application, + platform: .iOS, + dependencies: [Dependency(type: .target, reference: watchApp.name)] + ) + let project = Project( + name: "watch_test", + targets: [hostApp, watchApp, watchExtension] + ) + let xcodeProject = try project.generateXcodeProject() + return try unwrap(xcodeProject.sharedData?.schemes.first) + } } From c0aa7a2b6acaf70252dc993cb9a2a6f9c6b204a6 Mon Sep 17 00:00:00 2001 From: Dalton Claybrook Date: Tue, 3 Mar 2020 22:19:26 -0500 Subject: [PATCH 3/5] Update changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ccbe589aa..48ef673de 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ ## Next Version +#### Fixed +- Fixed issue which caused watch app schemes to be generated incorrectly, preventing these apps from launching. [#798](https://github.com/yonaskolb/XcodeGen/pull/798) @daltonclaybrook + ## 2.15.0 #### Added From 276112057294f39dfa576c8e626f7af0909757c7 Mon Sep 17 00:00:00 2001 From: Dalton Claybrook Date: Thu, 26 Mar 2020 08:08:21 -0400 Subject: [PATCH 4/5] Fix error after rebase --- Sources/XcodeGenKit/SchemeGenerator.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/XcodeGenKit/SchemeGenerator.swift b/Sources/XcodeGenKit/SchemeGenerator.swift index 2f0f3bc40..4df57ff39 100644 --- a/Sources/XcodeGenKit/SchemeGenerator.swift +++ b/Sources/XcodeGenKit/SchemeGenerator.swift @@ -312,7 +312,7 @@ extension Scheme { self.init( name: name, build: .init( - targets: [Scheme.BuildTarget(target: TargetReference.local(target.name))], + targets: Scheme.buildTargets(for: target, project: project), buildImplicitDependencies: targetScheme.buildImplicitDependencies ), run: .init( From b529bbe479cfe23ae0ce9d820e2ba87072db7705 Mon Sep 17 00:00:00 2001 From: Dalton Claybrook Date: Thu, 26 Mar 2020 09:07:01 -0400 Subject: [PATCH 5/5] Add test for BuildableProductRunnable action on iOS target schemes --- Tests/XcodeGenKitTests/SchemeGeneratorTests.swift | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/Tests/XcodeGenKitTests/SchemeGeneratorTests.swift b/Tests/XcodeGenKitTests/SchemeGeneratorTests.swift index 81d493cea..81ddb5e9a 100644 --- a/Tests/XcodeGenKitTests/SchemeGeneratorTests.swift +++ b/Tests/XcodeGenKitTests/SchemeGeneratorTests.swift @@ -337,7 +337,20 @@ class SchemeGeneratorTests: XCTestCase { try expect(buildableReference?.referencedContainer) == "container:\(externalProject.string)" } - $0.it("generates scheme with remote runnable for watch") { + $0.it("generates scheme with buildable product runnable for ios app target") { + let app = Target( + name: "MyApp", + type: .application, + platform: .iOS, + scheme: TargetScheme() + ) + let project = Project(name: "ios_test", targets: [app]) + let xcodeProject = try project.generateXcodeProject() + let xcscheme = try unwrap(xcodeProject.sharedData?.schemes.first) + try expect(xcscheme.launchAction?.runnable).beOfType(XCScheme.BuildableProductRunnable.self) + } + + $0.it("generates scheme with remote runnable for watch app target") { let xcscheme = try self.makeWatchScheme(appType: .watch2App, extensionType: .watch2Extension) try expect(xcscheme.launchAction?.runnable).beOfType(XCScheme.RemoteRunnable.self) }