Question

I'm writing a script to archive the iOS portion of a phonegap project. The script wipes the directory that the project is in and then repopulates it using the latest code from source control. I then run$ phonegap local build ios in order to build the project. However in order to archive the project I need its schemes to be defined. I have tried building the project from the command line but I get the message ** BUILD FAILED **. As of right now I have the code open the xcode project (the only way that I've found to get the schemes defined) and then sleep for 30 second while I wait for xcode to work its magic. My question is how can I either simulate opening xcode or otherwise define the scheme from the command line.

Thanks in advance for any help.

Was it helpful?

Solution

This is a completely fair question given that Xcode schemes are somewhat less than thoroughly documented and schemes have this feeling of being somewhat magical until you see how they hook into the build process as a whole.

Based on the workarounds you are seeking, it sounds as though you need to promote a scheme to being "Shared" so that automated tools (or other developers) do not have to first open your project and wait for Xcode to auto-generate the default scheme. This is an entirely normal 'ask' from developers trying to make their Xcode projects work with Continuous Integration systems or with other command line tools acting on an Xcode 4 or Xcode 5 project. The great news is that there are Xcode-native ways to configure your project without having to resort to messy or error prone workarounds.

TL;DR Version:

The default Xcode behavior for schemes is to treat them as a developer-specific setting and not share with other developers or tools. We need to promote your project's scheme to being 'Shared' and commit those changes to your version control system:

  1. Start with a clean checkout of your project.
  2. Navigate Xcode's Menus: Product > Scheme > Manage Schemes... menu option
  3. Uncheck 'Autocreate Schemes' in the upper left corner of the scheme sheet,
  4. Check the 'Shared' checkbox next to the scheme that should be made available to all developer users and build systems.
  5. Finally commit all project changes back to your version control system.

This will make a single Scheme shared across all developer using this project, regardless of OS X username and make it such that unattended builds via xcodebuild or the build tool of choice will have a scheme to work with.

...And now, on to the the longer answer for the curious

First a bit of background before we dive into your direct questions:

Target: The app, static library, bundle, or more generally the 'product' constructed from the source code, assets, plists, build settings, and other files contained within the project. This 'product' is generated when a build operation is invoked either via Xcode's "Run" button or via the command line tool xcodebuild

Build Configuration: A named set of build settings that can be identified by a human-readable label. By default, all Xcode projects start with a "Debug" configuration that generates build targets with the greatest amount of transparency aiding developers in debugging their applications and a "Release" configuration that strips the resulting build of this diagnostic information and optimizes the build to reduce its size. Some developers elect to create additional configurations based on their team's needs: "Ad-Hoc" might be created so that the Signing Identity and Provisioning Profile settings can be changed for code signing the app for installation via an Ad-Hoc provisioning profile. "AppStore" or "Distribution" are other common custom Build Configurations one might see in other projects.

Action: A set of related activities supporting different phases involved in the development, diagnosis, and testing of a product. As of the time of writing there are six actions: "Build", "Run", "Test", "Profile", "Analyze", and "Archive". As a developer the two you will most frequently use are "Build" and "Run".

Build Scheme: An Xcode 4 invention for managing project build target dependencies, build parallelization options, for a specified Build Target. Each Scheme allows a developer to select exactly one Build Configuration (ex. "Debug" or "Release") for each Action ("Build", "Run", etc.) of a project's lifecycle as well as define other behaviors or options associated with that specific Action. For example, the "Profile" action in a scheme allows the developer to select which diagnostic instrument will be loaded by default when Profiling code in Instruments.app.

With these definitions in mind, lets get back to your questions:

How can I either simulate opening xcode or otherwise define the scheme from the command line?

Very simply: You don't need to do either, there is an Xcode-native mechanism for making schemes available and we just need to do some minor scheme reconfiguration to get you up and running then commit those changes to version control (I'm going to refer to this as 'SCM' for the rest of this answer).

The behavior you are facing is Xcode's default project behavior when it comes to persisting project settings. By default, many things are considered developer-specific settings and reside in a set of files mapping to the specific username of the account that opened the Xcode project itself (more on this in a moment). The policy governing these settings could be distilled down to the rule that Xcode settings were considered 'developer private until explicitly promoted to shared'. Although this was present in versions of Xcode prior to Xcode 4, it wasn't until the introduction of Schemes as the primary vehicle for invoking builds that this approach caused development teams and their Continuous Integration systems problems.

Schemes came along and consolidated a great number of settings screens from early versions of Xcode into a single editor window where a developer could take a look at the highest-level settings for each of the different Action phases of the app:

  • When running the "Build" action, one could define which targets need to get constructed, or if Xcode should try and identify build dependencies on its own.
  • For a "Run" action, select which Build Configuration should be used as well as which Debugger to use.
  • For a "Test" action, select which Build Configuration should be used as well as which Test Classes and Test Data Bundles should be used to test application behavior.
  • ...etc...There are lots of other high-level settings but I'm going to leave exploring them as an exercise for the reader...or an opportunity to ask another SO question!

In each case, these settings cause something of a cascade effect -- Selecting a "Debug" configuration keeps as much diagnostic data in the app as possible to aid developers in tracing the source of problems, this in turn would invoke the "Debug" specific Build Settings as configured in the Build Target itself that may also run "Debug" specific scripts or enable "Debug" specific settings.

Naturally, these selections needed to live somewhere so that they could be persisted between Development sessions or on the rare occasion that Xcode decides to crash. The behavior of "Developer private until promoted" reigned supreme and these Scheme settings were persisted in the "xcuserdata" folder within the .xcodeproj file itself -- This still holds true for those projects that reside as a part of an .xcworkspace.

You can see this for yourself in your own project. First, ensure you are working with a clean version of your code, then open the Xcode project or workspace to ensure that your personal version of the default scheme is available when we walk through your project file:

  1. Switch from Xcode to Finder, then navigate to your project's checkout directory.
  2. Right-click on the .xcodeproj file for your project and select 'Show Package Contents'. If you use a workspace, still select the .xcodeproj that contains your project files, and not the .xcworkspace itself
  3. Navigate into "xcuserdata".

Depending on the number of developers that have been involved with this project or the number of different machines with different usernames that have committed against this project, it is distinctly possible to have more than one .xcuserdatad folder.

  1. Select the folder that matches your OS X username. For me, my OS X username is 'bmusial' so I would select the 'bmusial.xcuserdatad' folder.
  2. Navigate into 'xcschemes' folder.
  3. Observe that you have two files: "[TARGET NAME].xcscheme" and "xcschemenamagement.plist" that contains information about the order of schemes and if schemes should be auto-generated or not.

Ah ha! Schemes are treated as developer-private data and are auto-generated on the first launch of the project!

This realization starts to get at the core of what we need to do -- migrate this scheme out of the developer-specific xcuserdata folder into something shared among all developers, disable auto-scheme-generation to prevent others from falling into the trap in the future, and commit those changes back to your SCM. Switch back to Xcode, let's reconfigure a few things:

  1. Navigate Xcode's Menus: Product > Scheme > Manage Schemes... menu option
  2. Uncheck 'Autocreate Schemes' in the upper left corner of the scheme sheet,
  3. Check the 'Shared' checkbox next to the scheme that should be made available to all developer users and build systems.

Switch back to your Finder window and go up a two levels to get back to the contents of the .xcodeproj folder (the one that contains a 'xcuserdata' folder). Notice that you now have a 'xcshareddata' folder. This folder contains a 'xcschemes' folder that contains the scheme we just shared and the .xcscheme in our own xcuserdata folder is now gone. We have just promoted your private Scheme as a shared, public scheme that will be available to all developers and tools, even those that have never launched the Xcode project directly.

Commit all of the changes we've made (there will be some new folders and files!) back to your SCM so that everyone receives the same configuration changes when the next time they update their source code!

The next time you run phonegap it will reset your checkout as your indicated but because you have a scheme committed it will have build actions it can work with.

Give this a shot and let us know how things go and if you run into any followup questions or problems along the way.

OTHER TIPS

You may also find the ruby gem xcodeproj useful. It can create schemes without having to open xcode.

You can read more about it here.

For phonegap/cordova, save the share_schemes.rb script in a scripts directory in the cordova project.

#!/usr/bin/env ruby
# share_schemes.rb

require 'xcodeproj'
xcproj = Xcodeproj::Project.open("platforms/ios/MyProject.xcodeproj")
xcproj.recreate_user_schemes
xcproj.save

Then add a hook to run it in your config.xml.

<platform name="ios">
    <hook type="after_platform_add" src="scripts/share_schemes.rb" />
</platform>

Now you don't have to open xcode to make changes, or check in any changes in your platforms folder. Every time you add the ios platform, your scheme will be created by this script.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top