Specifying the Build Configuration for Xcode Project Dependencies

When working with multiple Xcode project dependencies, such as during the development of a Cocoa Touch static Library, it can be convenient to add the dependencies’ project files as subprojects of a “host” or “parent” project, or to use Xcode workspaces to work with multiple projects in parallel. However, when dealing with these types of project configurations, one quickly becomes aware of the limitations of the way Xcode handles building dependencies.

Xcode’s implicit building of subproject dependencies will always look for a build configuration in the subproject with the same name as the one in the parent project. This means that building the parent project in the Debug configuration will result in each dependency being implicitly built using its Debug configuration. In the case where a dependency doesn’t have a configuration with the same name, Xcode will attempt to fall back to building the dependency using its Release configuration.

This can be annoying when developing a static library if there is a reason to specify one of a number of configurations for the library’s build settings, such as switching between server environments. The reasons for this behavior not being easily overridden completely elude me. I don’t find it intuitive, nor can I think of any security-related reason for limiting this feature.

Furthermore, Xcode will automatically build static library products from subproject dependencies if the .a file is included in the “Link Binary With Libraries” section of the parent project’s Build Phases by default. This occurs even if the subproject target isn’t included in “Target Dependencies,” which I personally find more confusing than intuitive or convenient.

When setting up a project with a dependency that has multiple configurations, there are two basic approaches: one which relies on the limited, implicit behavior of Xcode’s build system, and the other which uses build scripts to bypass it.

The Implicit Approach

The easiest way to ensure that a subproject is built with the correct configuration is to create an identically-named configuration in the parent project. That’s all well and good if your build configuration strategy is dead simple, or if the names happen to match between the projects by mere coincidence.

But, for those of us who dare to venture beyond the confines Debug and Release, things tend to get complicated.

If the parent project also has its own set of configurations, all of which must be matched up with a subproject configuration, this can quickly spiral out of control. Which leads us to…

The Scripting Approach

The second method is more difficult to configure as it requires a custom build script and build settings, but it’s much more flexible.

In order to specify an arbitrary configuration for a subproject dependency, the first step is to disable Xcode’s implicit detection of dependencies from linked library files. This can be accomplished in the build scheme settings for the parent project. Under the Build action of the scheme settings, there is an option labeled “Find Implicit Dependencies.” Uncheck this for any scheme that is building the subproject using a custom configuration.

Implicit Dependencies

Next, ensure that the “Link Binary With Libraries” section of the Build Phases for the parent project contains the subproject’s target, but that the “Target Dependencies” section does not contain the subproject’s target. This will prevent Xcode from automatically building the subproject target, but still attempt to link to the subproject’s resulting product in the link phase.

NOTE: Alternatively, a linker flag can be added to the “Additional Linker Flags” build setting to link to the resulting library instead of adding it to the “Link Binary With Libraries” section (for example -lMyLib). When using a manual linker flag, it’s not necessary to disable the “Find Implicit Dependencies” checkbox, since Xcode won’t be aware of the linkage, but be sure to remove the product from the “Link Binary With Libraries” section since linking is being handled manually.

Finally, add a “Run Script” to the parent app’s Build Phases. In its simplest form, this script will simply invoke xcodebuild for the dependency, which allows any build configuration of the subproject to be specified. The only caveat is that the build products directory for the subproject must be set to the parent project’s build products directory, so Xcode’s implicit linking settings will know where to find the resulting .a file.

Here is an example script to build a subproject named “MyLib” with a configuration “Debug-Staging.”

The build environment variables for the subproject must be set to the same value as the parent project in order to ensure that all build artifacts end up in the parent project’s derived data directory, and Xcode knows where to find the resulting .a file to link.

The one really ugly thing about this script is that the VALID_ARCHS variable must be set to all architectures for both the device and simulator, otherwise the subproject will not consistently build correctly when switching between device and simulator. It’s possible that there is a better solution for this, but at the time of publishing this article, it escapes me.

Switching Configurations

It is also possible to specify one of any number of build configurations for the subproject, depending on the parent project’s configuration, effectively creating a “mapping” between configurations for the parent and subproject.

This is accomplished by creating a custom Build Setting in the parent project that will cause the script to provide a different configuration when building the subproject.

For example, by creating a custom build setting called SUBPROJECT_CONFIG, the value of which is the name of the build configuration of the subproject, the script can reference the build setting when invoking xcodebuild.

Custom Build Setting

 

Thus, for each configuration in the parent project, the subproject has a mapping to one of its own configurations.

Drawbacks

There are a few disadvantages with using the xcodebuild technique which must be weighed against the importance of building a subproject with a different configuration.

1. Warnings in the subproject will not show up consistently.

This is a big one. For a clean build of the subproject, all warnings will show up in the Xcode warnings tab. In certain scenarios, building again will make these warnings disappear, and only warnings from the files that changed since the last build will show up. For this reason, I would only recommend using this technique when testing particular configurations, and not during regular debugging, as it can be easy to lose track of outstanding warnings in the subproject.

2. Cleaning the parent project will not clean the subproject.

For this reason, I would recommend creating a new aggregate target in the parent project’s build settings that runs the same script as above, but with the clean action as its first argument (xcodebuild clean ...). You can build this target when you need to clean the subproject. This is certainly inconvenient, but I don’t know of a better way to do it.

3. It can potentially be confusing when working with a team.

Since this is not a “standard” Xcode workflow, anyone else working on the project may have questions (and/or stern words) for you as to what the @$%! is going on in the build settings.

4. Not CI-friendly.

Since cleaning the parent project does not clean the subproject, this technique is not very CI-friendly. Practically anything can be done with careful scripting, but depending on how important it is that you build the subproject with multiple configurations, it may or may not be worth the extra effort.

Summary

Despite the regrettable omission of a native Xcode option to specify subprojects’ build configurations, it’s still possible to do so with a bit of scripting. This technique can be useful when doing iterative development on a static library when there is a reason to provide different build configurations. However, it is not without drawbacks, and these should be carefully weighed when choosing whether or not to bypass Xcode’s implicit dependency system.


Do you have a project in mind? We’d love to work with you. If you’d like an opportunity to work on projects with us, check out our Careers page. We’re hiring!

4 thoughts on “Specifying the Build Configuration for Xcode Project Dependencies”

  1. I have another solution which seems to address all of the drawbacks you mention, see http://stackoverflow.com/a/30884779/21698

  2. The Find Implicit Dependencies setting was the missing piece of my puzzle. Thanks for pointing it out.

  3. There is another drawback: as the build is executed in a build phase of the parent project, the xcodebuild command will pick up all build setting from the parent project too. This does not produce desired results.

  4. I liked this article outbox. Can we have way where we can change dependant targets macros based on parent projects?

    Lets I have ProjectX having 2 different target1 and target2. This projects have dependancy project called XLIb.a so i want to change Preprocessor macros of XLIb based on target1 and target2. SO if its building for target 1 then XLIb will have different macro and also different while building for target2.

    Thanks,
    Nitin

Leave a Comment