Excluded Embedded Binaries

After updating to Xcode 11.5 my release builds started failing because a failure in the “embedded binary validation” build step, which occurs after the last explicit build phase over which we as developers have the ability to affect how products are built.

I was able to trace the problem to an attempt to validate an app extension that is embedded into my app but then removed by a later build phase. The rationale here is that the app extension in question is used internally, and shouldn’t be shipped to customers. But you can’t have dependencies and binary embedding vary based on the build configuration, so I always build the app extension, embed it, and remove it only when building a “Release” build.

This strategy has worked well for years, but in the latest Xcode, it attempts to validate the embedded binary even if it no longer exists. I guess there’s some wisdom to this. After all, a missing binary certainly isn’t valid! But historically Xcode has allowed custom build phases to substantially change the layout and contents of the built product without causing the post-build steps to fail.

I created and submitted to Apple a simple test app that exhibits the problem. In this case, an app named InvalidApp.app embeds an app extension Whatever.appex, and then removes it in a build phase. It results in this errr:

error: Couldn't load Info dictionary for <DVTFilePath:0x7f86ef4be800:'/Users/daniel/Library/Developer/Xcode/DerivedData/InvalidApp-bqvdubwezowsaccymccleomclddp/Build/Products/Debug/InvalidApp.app/Contents/PlugIns/Whatever.appex'> (in target 'InvalidApp' from project 'InvalidApp')

Yes, there is no Info.plist for this app extension because the whole thing is gone! I worried that I would have to rejigger my whole build process and figure out another way to ensure this app extension is avialable only in internal builds, but luckily Apple got back to me with a suggestion to use the EXCLUDED_SOURCE_FILE_NAMES build setting to exclude the app extension.

I am familiar with this build setting, which can be used to impose powerful variations on which source files contribute to a particular built product. For example, it can be used to cause different source files to be compiled for the iOS version of an app than the Mac version. See Dave Delong’s excellent post series on conditional compilation to learn more about this powerful technique.

But I hadn’t considered that the build setting might apply to embedded binaries. After all, Whatever.appex isn’t a “source file.” But lo and behold, adding the app extension name to the excluded source file names for my target, only in the Release build configuration, completely solves the problem. Not only does it avoid the erroneous validation error, it achieves my original goal of excluding the app extension by never copying it to the bundle in the first place. I can remove the custom build phase that selectively removed the app extension, in favor of a build setting that effectively communicates to the Xcode build system exactly what I want it to do.