For the purposes of CI, I need to be able to generate an XCARCHIVE and an IPA file in our nightly build. The IPA is for our testers, to be signed with our ad-hoc keys, and the XCARCHIVE is to send to the client so that they can import it into Xcode and submit it to the app store when they're happy with it.
Generating the IPA is simple enough with a bit of googling, however how to generate the .XCARCHIVE file is what eludes me. The closest I've found is:
xcodebuild -scheme myscheme archive
However, this stores the .xcarchive in some hard-to-find folder, eg:
/Users/me/Library/Developer/Xcode/Archives/2011-12-14/MyApp 14-12-11 11.42 AM.xcarchive
Is there some way to control where the archive is put, what its name is, and how to avoid having to re-compile it? I guess the best possible outcome would be to generate the xcarchive from the DSYM and APP that are generated when you do an 'xcodebuild build' - is this possible?
Xcode 5 now supports an
-archivePath
option:You can also now export a signed IPA from the archive you just built:
Starting with Xcode 4 Preview 5 there are three environment variables that are accessible in the scheme archive's post-actions.
You could move/copy the archive in here. I wanted to have a little more control over the process in a CI script, so I saved a temporary file that could easily be sourced in my CI script that contained these values.
Then in my CI script I can run the following:
I have just solved this one - just add the argument
-archivePath
to your xcode build command line, given the initial question that would mean:becomes ...
(Note: paths are relative, I output my build to
$PWD/Build
)This will then place your .app folder in:
If your build target already has your signing certificate and provisioning profile in it you can then create your IPA file without re-signing using the following command:
(Note: xcrun doesn't like relative paths hence the
pwd
)The -v args dump lots of useful information - this command can fail to sign properly and still exit with code 0, sigh!
If you are finding that you can't run the built .ipa it's probably a signing issue that you can do a double check on using:
If it's signed correctly and un-tampered with the output will have this in:
If not you will see something similar to this:
... which generally means you are trying to use an intermediate output directory rather than the proper archive.
On Xcode 4.6 it is possible to specify a post-build action for the scheme to be compiled into an xcarchive:
A build script can be used to check if $ARCHIVE_PATH is defined after running xcodebuild and if this is the case, the output xcarchive can be moved into a designated folder.
This method is not very maintainable if the targets in the project are a large number, as for each one it is necessary to tag the corresponding scheme as 'shared' and add the post-build action.
To address this problem, I have created a build script that generates the archive path programmatically by extracting the last build that matches the target name on the current day. This method works reliably as long as there aren't multiple builds with the same target name running on the machine (this may be a problem in production environments where multiple concurrent builds are run).
Full source code with an example Xcode project can be found here:
https://github.com/bizz84/Xcode-xcarchive-command
Similar to the others, but perhaps a little simpler since I try to record the
.xcarchive
file's location. (I also don't move the archives folder, so this will work better if you're doing multiple builds at the same time.)My caller build script generates a new tempfile and sets its path to an environment variable named
XCARCHIVE_PATH_TMPFILE
. This environment variable is available in my scheme's Archive post-action shell script, which then that writes the .xcarchive's path to that file. The build script that can then read that file after it callsxcodebuild archive
.post-action shell script
My current solution is to rename the user's existing archives folder, run the build, and do a 'find' to copy the archives where i want, then delete the archives folder and rename the old folder back as it was, with code like this in my ruby build script:
Its a bit hacky but i don't see a better solution currently. At least it preserves the user's 'archives' folder and all their pre-existing archives.
--Important note!--
I since found out that the line of code where i find the archive and cp it to the folder i want doesn't copy the symlinks inside the archive correctly, thus breaking the code signing in the app. You'll want to replace that with a 'mv' or something that maintains symlinks. Cheers!