Generating schemes with BuildableProductRunnable for watchOS targets causes errors in Xcode 14 beta
With an example project.yml like this:
name: Foo
targets:
Foo:
type: framework
platform: watchOS
schemes:
Foo:
build:
targets:
Foo: build
And performing the following steps:
xcodegen -s project.yml
xcodebuild -project Foo.xcodeproj -scheme Foo -showBuildSettings
When using Xcode 14 beta 2, the final step errors with:
[MT] DVTAssertions: ASSERTION FAILURE in /System/Volumes/Data/SWE/Apps/DT/BuildRoots/BuildRoot8/ActiveBuildRoot/Library/Caches/com.apple.xbs/Sources/IDEFrameworks/IDEFrameworks-21257.0.0.0.25/IDEFoundation/Execution/RunDestinations/IDERunDestinationCLI.m:565
Details: Unhandled/unexpected case where no run destinations were produced.
Object: <IDERunDestinationCLI>
Method: +resolveRunDestinationsWithWorkspace:scheme:buildAction:schemeCommand:schemeTask:destinationSpecifications:architectures:timeout:runDestinationManager:deviceManager:fallbackPreferredSDK:fallbackPreferredArchitectures:skipUnsupportedDestinations:shouldSkipRunDestinationValidation:didDisambiguate:disambiguatedMatches:disambiguatedMatchesDescription:error:
Thread: <_NSMainThread: 0x60000213c280>{number = 1, name = main}
Hints:
Backtrace:
0 -[DVTAssertionHandler handleFailureInMethod:object:fileName:lineNumber:assertionSignature:messageFormat:arguments:] (in DVTFoundation)
1 _DVTAssertionHandler (in DVTFoundation)
2 _DVTAssertionFailureHandler (in DVTFoundation)
3 _sortDevicesForDisplay (in IDEFoundation)
4 -[Xcode3CommandLineBuildTool _resolveRunDestinationsForBuildAction:] (in Xcode3Core)
5 -[Xcode3CommandLineBuildTool _resolveInputOptionsWithTimingSection:] (in Xcode3Core)
6 -[Xcode3CommandLineBuildTool run] (in Xcode3Core)
7 XcodeBuildMain (in libxcodebuildLoader.dylib)
8 start (in dyld)
The cause appears to be in the profile action of the scheme. If we manually edit the generated scheme in the follow way:
--- a/Foo.xcodeproj/xcshareddata/xcschemes/Foo.xcscheme
+++ b/Foo.xcodeproj/xcshareddata/xcschemes/Foo.xcscheme
@@ -67,8 +67,7 @@
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
- <BuildableProductRunnable
- runnableDebuggingMode = "0">
+ <MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "8F0F80D982E77483A25959A4"
@@ -76,7 +75,7 @@
BlueprintName = "Foo"
ReferencedContainer = "container:Foo.xcodeproj">
</BuildableReference>
- </BuildableProductRunnable>
+ </MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
Then the assertion failure stops and the xcodebuild command completes successfully.
A couple projects appear to have run into versions of this issue: Swinject/Swinject#511, square/Valet#282.
This seems like a bug in Xcode 14 which may be fixed in future betas, but I wanted to let you know in the meantime!
I've tested creating new schemes for watchOS targets in Xcode 14.0 beta 2 and 13.0, and it looks like in both cases Xcode uses MacroExpansion for buildable references for profile actions rather than BuildableProductRunnable. I'm not clear whether that's the case for non-watchOS targets yet.
It looks like XcodeProj does allow specifying macroExpansion for XCScheme.ProfileAction, although it has a default value of nil unlike buildableProductRunnable: https://github.com/tuist/XcodeProj/blob/8.7.1/Sources/XcodeProj/Scheme/XCScheme+ProfileAction.swift#L32
If this is appropriate, it looks like some sort of change could be made here: https://github.com/yonaskolb/XcodeGen/blob/2.29.0/Sources/XcodeGenKit/SchemeGenerator.swift#L323
Sounds like this may be fixed in the beta 3 which came out yesterday: https://github.com/Swinject/Swinject/issues/511#issuecomment-1177109270
I haven't downloaded the new beta yet to verify that.
I don't believe this issue was fully fixed. In beta 4 (and also beta 3, based on a report from my colleague), it's true that the DVTAssertion is no longer thrown, but instead this issues causes xcodebuild to say there are no compatible watchOS simulators.
This can be seen with the original repro case when running a different xcodebuild command:
xcrun simctl list devices watchOS
# Pass the device id of a valid watchOS simulator
xcodebuild -project Foo.xcodeproj -scheme Foo -destination "platform=watchOS Simulator,id=<device id>" build
This results in an error like the following:
error: Unable to find a destination matching the provided destination specifier:
{ platform:watchOS Simulator, id:A695AC2B-FD82-4D94-8918-1FF3D932AEE8 }
Available destinations for the "Foo" scheme:
{ platform:watchOS, id:dvtdevice-DVTiOSDevicePlaceholder-watchos:placeholder, name:Any watchOS Device }
{ platform:watchOS Simulator, id:dvtdevice-DVTiOSDeviceSimulatorPlaceholder-watchsimulator:placeholder, name:Any watchOS Simulator Device }
{ platform:watchOS Simulator, id:A695AC2B-FD82-4D94-8918-1FF3D932AEE8, OS:9.0, name:Apple Watch Series 5 - 40mm }
{ platform:watchOS Simulator, id:055107A2-557B-4C03-A6F4-C043AF29E954, OS:9.0, name:Apple Watch Series 5 - 44mm }
{ platform:watchOS Simulator, id:92CDBC90-3564-4C20-8D36-D160C72355E0, OS:9.0, name:Apple Watch Series 6 - 40mm }
{ platform:watchOS Simulator, id:2436B2A5-2937-402E-976F-34734CB00DB6, OS:9.0, name:Apple Watch Series 6 - 44mm }
{ platform:watchOS Simulator, id:373076C4-BA28-4FCE-84DF-C9548243ED23, OS:9.0, name:Apple Watch Series 7 - 41mm }
{ platform:watchOS Simulator, id:8D09A1F6-99CC-4ED6-BB68-190630062D45, OS:9.0, name:Apple Watch Series 7 - 45mm }
When performed on Xcode 13, this succeeds.
A change like this does seem to fix my issue:
diff --git a/Sources/XcodeGenKit/SchemeGenerator.swift b/Sources/XcodeGenKit/SchemeGenerator.swift
index 60922f2..db4062d 100644
--- a/Sources/XcodeGenKit/SchemeGenerator.swift
+++ b/Sources/XcodeGenKit/SchemeGenerator.swift
@@ -320,10 +320,11 @@ public class SchemeGenerator {
)
let profileAction = XCScheme.ProfileAction(
- buildableProductRunnable: runnables.profile,
+ buildableProductRunnable: nil,
buildConfiguration: scheme.profile?.config ?? defaultReleaseConfig.name,
preActions: scheme.profile?.preActions.map(getExecutionAction) ?? [],
postActions: scheme.profile?.postActions.map(getExecutionAction) ?? [],
+ macroExpansion: runnables.profile.buildableReference,
shouldUseLaunchSchemeArgsEnv: scheme.profile?.shouldUseLaunchSchemeArgsEnv ?? true,
askForAppToLaunch: scheme.profile?.askForAppToLaunch,
commandlineArguments: profileCommandLineArgs,
But I have no idea if it breaks anything else 😅
If I can find more ways to validate this, perhaps I can open a PR.
The snippet I posted is indeed not a good idea for non-framework targets. I found some similar logic for buildable product runnable vs macro expansion added in #328, and it seems to do the trick here! I opened #1245 to fix and did more validation described there.