coffeebreaks
coffeebreaks

Reputation: 3817

Unexpected effect of the default build configuration in xcode

xcode one has the ability to set a default build configuration to use for xcodebuild. E.g. Release or Debug

https://developer.apple.com/library/ios/technotes/tn2339/_index.html#//apple_ref/doc/uid/DTS40014588-CH1-MY_APP_HAS_MULTIPLE_BUILD_CONFIGURATIONS__HOW_DO_I_SET_A_DEFAULT_BUILD_CONFIGURATION_FOR_XCODEBUILD_

Yet when I try this on my project, it looks as it is not used as it said it should

$ echo $DEVELOPER_DIR
/Applications/Xcode6.0.1.app/Contents/Developer 
$ xcodebuild  -list
Information about project "CocoaPodsExample":
    Targets:
        CocoaPodsExample
        CocoaPodsExampleTests

    Build Configurations:
        Debug
        Release

    If no build configuration is specified and -scheme is not passed then "Release" is used.

    Schemes:
        CocoaPodsExample

Given a Debug default configuration:

$ git grep defaultConfigurationName
CocoaPodsExample.xcodeproj/project.pbxproj:                     defaultConfigurationName = Debug;
CocoaPodsExample.xcodeproj/project.pbxproj:                     defaultConfigurationName = Debug;
CocoaPodsExample.xcodeproj/project.pbxproj:                     defaultConfigurationName = Debug;
Pods/Pods.xcodeproj/project.pbxproj:                    defaultConfigurationName = Debug;
Pods/Pods.xcodeproj/project.pbxproj:                    defaultConfigurationName = Debug;
Pods/Pods.xcodeproj/project.pbxproj:                    defaultConfigurationName = Debug;

specifying DEFAULT configuration uses Debug (EXPECTED)

$ xcodebuild -scheme CocoaPodsExample -workspace CocoaPodsExample.xcworkspace -configuration DEFAULT build | grep "CONFIGURATION"
=== BUILD TARGET Pods-AFNetworking OF PROJECT Pods WITH THE DEFAULT CONFIGURATION (Debug) ===
=== BUILD TARGET Pods OF PROJECT Pods WITH THE DEFAULT CONFIGURATION (Debug) ===
=== BUILD TARGET CocoaPodsExample OF PROJECT CocoaPodsExample WITH THE DEFAULT CONFIGURATION (Debug) ===
=== BUILD TARGET CocoaPodsExampleTests OF PROJECT CocoaPodsExample WITH THE DEFAULT CONFIGURATION (Debug) ===

and not specifying the configuration uses Debug (EXPECTED)

$ DEVELOPER_DIR=/Applications/Xcode6.0.1.app/Contents/Developer xcodebuild -scheme CocoaPodsExample -workspace CocoaPodsExample.xcworkspace build | grep "CONFIGURATION"
=== BUILD TARGET Pods-AFNetworking OF PROJECT Pods WITH CONFIGURATION Debug ===
=== BUILD TARGET Pods OF PROJECT Pods WITH CONFIGURATION Debug ===
=== BUILD TARGET CocoaPodsExample OF PROJECT CocoaPodsExample WITH CONFIGURATION Debug ===
=== BUILD TARGET CocoaPodsExampleTests OF PROJECT CocoaPodsExample WITH CONFIGURATION Debug ===

Given Release as default configuration

$ git grep defaultConfigurationName
CocoaPodsExample.xcodeproj/project.pbxproj:                     defaultConfigurationName = Release;
CocoaPodsExample.xcodeproj/project.pbxproj:                     defaultConfigurationName = Release;
CocoaPodsExample.xcodeproj/project.pbxproj:                     defaultConfigurationName = Release;
Pods/Pods.xcodeproj/project.pbxproj:                    defaultConfigurationName = Release;
Pods/Pods.xcodeproj/project.pbxproj:                    defaultConfigurationName = Release;
Pods/Pods.xcodeproj/project.pbxproj:                    defaultConfigurationName = Release;

specifying DEFAULT configuration uses Release (EXPECTED)

$ xcodebuild -scheme CocoaPodsExample -workspace CocoaPodsExample.xcworkspace -configuration DEFAULT build | grep "CONFIGURATION"
=== BUILD TARGET Pods-AFNetworking OF PROJECT Pods WITH THE DEFAULT CONFIGURATION (Release) ===
=== BUILD TARGET Pods OF PROJECT Pods WITH THE DEFAULT CONFIGURATION (Release) ===
=== BUILD TARGET CocoaPodsExample OF PROJECT CocoaPodsExample WITH THE DEFAULT CONFIGURATION (Release) ===
=== BUILD TARGET CocoaPodsExampleTests OF PROJECT CocoaPodsExample WITH THE DEFAULT CONFIGURATION (Release) ===

and not specifying the configuration uses Debug (UNEXPECTED)

$ xcodebuild -scheme CocoaPodsExample -workspace CocoaPodsExample.xcworkspace build | grep "CONFIGURATION"
=== BUILD TARGET Pods-AFNetworking OF PROJECT Pods WITH CONFIGURATION Debug ===
=== BUILD TARGET Pods OF PROJECT Pods WITH CONFIGURATION Debug ===
=== BUILD TARGET CocoaPodsExample OF PROJECT CocoaPodsExample WITH CONFIGURATION Debug ===
=== BUILD TARGET CocoaPodsExampleTests OF PROJECT CocoaPodsExample WITH CONFIGURATION Debug ===

It's as if:

I don't even know if this is a bug or expected behavior.

Upvotes: 4

Views: 3099

Answers (1)

Bryan Musial
Bryan Musial

Reputation: 8458

This was something that was tripping up my own Continuous Integration scripts as well and caused me to be very specific in xcodebuild commands when it came to determining which of my configurations to run. It wasn't until several months later I dug into the behavior to determine what was actually going on.

TL;DR version: The behavior you are observing is functioning correctly given the commands you are firing off to xcodebuild.

...now for the 'why'

As it turns out, the defaultConfigurationName value is a setting that was added to pre-Xcode 4 projects as a means to describe which of potentially many build configurations should be leveraged by command-line builds when a configuration hadn't been specified...exactly as its name suggests and entirely consistent with the expectation you've expressed in your question.

There is some nuance in that history -- specifically, why is Xcode 4 important? Xcode 4 was the first version of the developer toolchain that introduced the concept of "Schemes" as a manner of defining different ways in which an application would be built. Per Apple's definition (emphasis added):

An Xcode scheme defines a collection of targets to build, a configuration to use when building, and a collection of tests to execute.

Peering into the .xcscheme file associated with a project we see a bunch of configuration data consistent with the settings we see exposed in the Scheme editor of the modern Xcode interface. There is one section in the root of /Scheme for each of the six items that appear in the left side bar of the scheme editor accessible by selecting "Edit Scheme" from the scheme picker next to the Stop button:

  • BuildAction maps to "Build"
  • TestAction maps to "Test"
  • LaunchAction maps to "Run"
  • ProfileAction maps to "Profile"
  • AnalyzeAction maps to "Analyze"
  • ArchiveAction maps to "Archive"

Within all but the "Build" item in Xcode we find a "Build Configuration" drop down list. The value of this item is reflected in the .xcscheme file under the respective action's setting for buildConfiguration. Take note of the build configuration defined for what the "Run" action (via the Xcode Scheme Editor Interface) or the "LaunchAction" in the .xcscheme file. I'd hazard a guess that the CocoaPodsExample scheme has Debug set as the Build Configuration for the Run / LaunchAction.

In your original question, your xcodebuild commands indicate that you are using a Workspace to organize your project. Per documentation for xcodebuild:

To build an Xcode workspace, you must pass both the -workspace and -scheme options to define the build. The parameters of the scheme will control which targets are built and how they are built, although you may pass other options to xcodebuild to override some param- eters of the scheme.

...because you use a Workspace you are compelled to provide a -scheme parameter and therefore are leveraging the buildConfiguration value that is found under the "Run" item in the Xcode Scheme Editor, equivalently located in the "LaunchAction:buildConfiguration" setting in the Scheme's .xcscheme file. Whatever build configuration is defined in this scheme takes precedence over not setting -configuration in the xcodebuild command itself.

So why does defaultBuildConfiguration still exist?

There are still legacy projects and even some development teams that have not adopted workspaces in their Xcode projects. Were you to create a brand new test project, not incorporate it in a Workspace, and run xcodebuild build | grep "CONFIGURATION" from the folder that contains the .xcodeproj you'd see the defaultBuildConfiguration get picked up.

I created a Single View iOS Application named "Demo" and did nothing else to the blank project. The default project creates a scheme named "Demo" that has the Run Build Configuration set to Debug while the defaultConfigurationName defaults to Release (NOTE: There are user-specific paths in some of these commands -- If you are going to run them yourself, do be sure to update paths accordingly!):

$ xmllint --xpath //Scheme/LaunchAction/@buildConfiguration Demo.xcodeproj/xcuserdata/bmusial.xcuserdatad/xcschemes/Demo.xcscheme
        buildConfiguration="Debug"
$ grep defaultConfigurationName Demo.xcodeproj/project.pbxproj 
        defaultConfigurationName = Release;
        defaultConfigurationName = Release;
        defaultConfigurationName = Release;
$ xcodebuild build | grep "CONFIGURATION"
=== BUILD TARGET Demo OF PROJECT Demo WITH THE DEFAULT CONFIGURATION (Release) ===

The output of the BUILD TARGET has changed slightly: It now reads: DEFAULT CONFIGURATION (Release) instead of CONFIGURATION Release. The inclusion of DEFAULT and the parenthesis around the actual configuration are the only indications that xcodebuild has fallen back to the value from defaultConfigurationName. Incidentally, these same indicators are present in the output from your Workspace command equivalents.

As this is a Scheme-enabled project we can use -scheme to pull the scheme-configured build configuration, or we can explicitly override the configuration to use by setting -configuration:

$ xcodebuild -scheme Demo build | grep "CONFIGURATION"
=== BUILD TARGET Demo OF PROJECT Demo WITH CONFIGURATION Debug ===
=== BUILD TARGET DemoTests OF PROJECT Demo WITH CONFIGURATION Debug ===
$ xcodebuild -configuration Debug build | grep "CONFIGURATION"
=== BUILD TARGET Demo OF PROJECT Demo WITH CONFIGURATION Debug ===

The output of these commands goes back to the normal WITH CONFIGURATION style and the configuration not wrapped by parenthesis.

Hopefully this gives you a lens into what is going on with your own projects and you can adjust your command-line scripts accordingly. Once I sorted this out, I was able to be less specific in my CI scripts so that as the project evolved there was less off a need to update the build scripts in parallel with project configuration changes. Naturally, depending on the nature of your own project and the frequency with which build configuration may or may not change, you may want to defer to being very prescriptive in your xcodebuild commands...that is, compel a specific configuration via -configuration. If you run into any followup questions, I'm all ears!

Upvotes: 3

Related Questions