Batch Build and Archive of iOS apps via Terminal - ios

I am trying to simplify the build->archive->submit process for iOS app publishing.
We have over 50 mobile apps that have nearly identical framework, but with different artwork and configuration settings.
I normally would load each project in xcode 4.2 and build->archive->submit the usual way with the xcode GUI, but we are up over 50 apps now and this process is very tiresome any time we push out an update.
That being said, I am trying to speed up this process with using a shell function.
I did plenty of research and found that xcodebuild (See Reid's answer) should work, however the Archive option is not working as I get the following error:
unsupported build action 'archive'
So I wrote the following:
# $1 should be a date like: 2012-07-17
# $2 should be a time like: 10.31AM
# $mybase will be the current directory at the time the function was called
# so make sure to cd into the folder containing the xcode project folders first
function xcodeArchive {
mkdir ~/Library/Developer/Xcode/Archives/$1
mybase=$PWD
for x in `ls`
do
mkdir ~/Library/Developer/Xcode/Archives/$1/$x
mkdir ~/Library/Developer/Xcode/Archives/$1/$x/dSYMs
mkdir ~/Library/Developer/Xcode/Archives/$1/$x/Products
mkdir ~/Library/Developer/Xcode/Archives/$1/$x/Products/Applications
cd $mybase/$x
xcodebuild
#read -p "Press [Enter] to continue"
cd $mybase/$x
cp $x/$x-Info.plist ~/Library/Developer/Xcode/Archives/$1/$x/Info.plist
cp -r build/Release-iphoneos/$x.app.dSYM ~/Library/Developer/Xcode/Archives/$1/$x/dSYMs/$x.app.dSYM
cp -r build/Release-iphoneos/$x.app ~/Library/Developer/Xcode/Archives/$1/$x/Products/Applications/$x.app
cd ~/Library/Developer/Xcode/Archives/$1/
mv $x $x\ $1\ $2.xcarchive
cd $mybase
done
}
export -f xcodeArchive
I put this in my .bash_profile and everything runs correctly as I would expect, except I'm not copying the correct "Info.plist" and I can't figure out where to copy it from or how to generate it. So now I am stuck.
Xcode will recognize the archives, but it lists them under "Unknown Schema" and "Unnamed Archive" in the organizer.
Any help regarding now to get the correct Info.plist is greatly appreciated.
I also welcome recommendations on how to improve the script and/or a more efficient way to batch build+archive these iOS apps.
Note:
I am unable to upgrade beyond Xcode 4.2 as that requires (as I understand it) OS X 10.7+ which I am not able to obtain yet (company computer).
I am still very much a bash/shell novice, so I apologize for any ugly code/practice above.
Also, this is for official app submission, not for ad-hoc or anything like that.
Thanks again for your help.

I had the same issue with the archive command, and found this question via Google. It would fail with this build command:
xcodebuild -verbose -project $ProductName.xcodeproj -target $ProductName -configuration Release -sdk $SDK clean archive CONFIGURATION_BUILD_DIR="$PROJECT_PATH/build" PROVISIONING_PROFILE="${DIST_PROVISONING_PROFILE}"
Yet, it would succeed with this build command:
xcodebuild -verbose -project $ProductName.xcodeproj -scheme $ProductName -configuration Release -sdk $SDK clean archive CONFIGURATION_BUILD_DIR="$PROJECT_PATH/build" PROVISIONING_PROFILE="${DIST_PROVISONING_PROFILE}"
The only difference is specifying the scheme in lieu of the target to build. If there is a sensible reason for this behavior, I'd enjoy hearing it.
I'm running XCode 4.5.1 on Mac OS X 10.7.5

OK I found a solution that will work. After doing a lot more searching and a lot of guess and check, I found I can still use the "archive" option with xcodebuild, I just have to specify a workspace and scheme and apparently I wasn't doing that correctly before as it now works.
So, for anyone looking for a similar solution (to batch archive xcode projects), here is my function:
# $mybase will be the current directory at the time the function was called
# so make sure to cd into the folder containing the xcode project folders first
function xcodeArchive {
mybase=$PWD
for x in `ls`
do
cd $mybase/$x
xcodebuild -workspace $x.xcodeproj/project.xcworkspace -scheme $x archive
cd $mybase
done
}
export -f xcodeArchive

Related

What could cause code coverage enabled builds to fail?

Summary
I'm trying to get a code coverage report from my project. However when I try to build and run tests, there is no CodeCoverage folder created in the derived data folder.
How am I trying to do this?
I am running xcodebuild as follows:
xcodebuild \
-workspace <some_workspace> \
-scheme <some_scheme> \
-destination <some_destination> \
-derivedDataPath <some_path> \
-enableCodeCoverage YES \
build test
What is the problem?
For my workspace/project it fails at the very end with a line:
xcodebuild: error: Failed to build workspace <some_workspace> with scheme <some_scheme>.
Reason: Could not determine bundle identifier for <some_test_target>'s TEST_HOST: "<some_path>/Build/Intermediates/CodeCoverage/Products/Debug-iphonesimulator/<some_product>.app"
At first it seemed directly linked to the TEST_HOST issue, but that's not the case here.
There is simply no CodeCoverage folder:
<some_path>/Build/Intermediates/CodeCoverage
What have I tried?
I tried the same with a clean new project, same running the same xcodebuild command, which succeeds. Within the Build/Intermediates/ folder exists the CodeCoverage folder.
However, in my own project, which is more complex, the Build/Intermediates/ folder contains a bunch of *.build folders (related to the app and the various frameworks, HockeySDK.build for example) and PrecompiledHeaders but no CodeCoverage folder.
When looking through the output of xcodebuild I never see a reference to the CodeCoverage folder for my project. However, for the test project, the first mention is at:
Write auxiliary files
/bin/mkdir -p /<some_path>/Build/Intermediates/CodeCoverage/Intermediates/<some_project>.build/Debug-iphonesimulator/<some_project>.build/Objects-normal/x86_64
For my project I see:
Write auxiliary files
/bin/mkdir -p /<some_path>/Build/Intermediates/<some_project>.build/Debug-iphonesimulator/<some_project>.build/Objects-normal/x86_64
Workaround
Something else that seemed to 'trick' it into working is to set the Host Application to None. After doing this it builds and tests, but tests fail due to the lack of host. However, this causes the CodeCoverage folder to be created, and if the Host Application is correctly set again, running the build and tests works fine, with a code coverage report produced.
You have to assure the package name is equal in all configurations. Xcode modifies it in case you are using unsupported characters. For instance Xcode replaces "-" with "_".
In all configurations, go to:
Project -> Build Settings -> Product Module
and set the exact same name without spaces.

How does the XCode-Run-Command look like?

For an automatic testing purpose, I have to build & run a XCode Project. Right now i prepare my project using scripts, then I open XCode, chose the right scheme and click the run-button. Afterwards I run my test script. All works fine!
I have to eliminate the manual steps (open XCode, run Project) and use scripts as well. Until now I did not yet figure out how to build and run my project exactly the same way as XCode does. I tried with the commands xcodebuild, xcrun simctl, ios-sim and all kind of parameters.
How can I fire the exact same command in the terminal? Thanks!
Edited
I reference to this question https://github.com/calabash/calabash-ios/issues/1076, which is the root of this question.
I tried again using both the "Debug Config" and the "-cal target" approach (https://github.com/calabash/calabash-ios/wiki/Tutorial%3A-How-to-add-Calabash-to-Xcode). Both possibilities work perfect if I do a manual build in Xcode. I studied jmoodys Examples and tried the following (I changed it a bit cause of the nature of my Ionic project):
xcrun xcodebuild \
TARGET_BUILD_DIR=\$BUILT_PRODUCTS_DIR \
DWARF_DSYM_FOLDER_PATH=\$BUILT_PRODUCTS_DIR \
-xcconfig cordova/build-debug.xcconfig \
-project './ABC.xcodeproj' \
-scheme 'ABC-cal' \
-configuration Debug \
-sdk iphonesimulator \
-SYMROOT="build/app" \
ARCHS="i386 x86_64" \
VALID_ARCHS="i386 x86_64" \
ONLY_ACTIVE_ARCH=NO \
build
The build runs without any error, creates the app at the exact same location as the xcode build and cucumber runs and opens the app, BUT the steps are never executed and I dont get any error. I guess my build is still not exact the same?
There are example scripts in these repositories:
Permissions
CalSmokeApp
CalWebApp
iPhoneOnlyApp
The Permissions repo is the easiest to understand. You can probably just grab the bin/make/app.sh and bin/make/ipa.sh and update a couple of environment variables.
I can't be sure, but I think you posted this exact question as an issue on Calabash iOS. If so, please don't cross post. I answered your question there, provided the same examples, and asked you follow up questions that you did not respond to.

Jenkins succeeds without producing IPA file

I have setup an mac mini slave on jenkins. The build process succeeds and my .app file and .dysm files etc are all created successfully.
I have Pack application and build .ipa? checked and defined a ipa filename pattern and an output directory.
When I run the build process, it actually creates the full path of my output directory, but at the end of the path, there is nothing in the final folder. No IPA. Again, the .app and other files are created successfully in the workspace build folder.
Also doesn't work if I leave the output directory blank or change it to other locations.
Jenkins succeeds with this message:
** BUILD SUCCEEDED **
Cleaning up previously generated .ipa files
Cleaning up previously generated .dSYM.zip files
Packaging IPA
Finished: SUCCESS
But alas, no IPA. Any ideas?
I was not able to solve the issue directly, so I am still interested in answers from someone who might have a more direct solution using the Xcode plug in with the pack and build ipa option.
Instead, I removed this option (unchecked it) and added another build phase for execute shell script.
I then added the script from this SO answer (modified for my use) and was able to export the ipa successfully.
/usr/bin/xcrun -sdk iphoneos PackageApplication -v "${RELEASE_BUILDDIR}/${APPLICATION_NAME}.app" -o "${BUILD_HISTORY_DIR}/${APPLICATION_NAME}.ipa" --sign "${DEVELOPER_NAME}" --embed "${PROVISONING_PROFILE}"
RELEASE_BUILDDIR and BUILD_HISTORY_DIR were changed to my own paths, and -sign and -embed were not needed because i'm using the same profile as the one that created the original .app file
I did have to use mkdir -p to make the path or it wouldn't succeed for me
So I was having the same problem and this is how I solved it:
In the Jenkins job, in the configure interface, under Advanced Xcode build options, I specified a Build output directory: ${WORKSPACE}/builds.
I also added a shell script to execute prior to the Xcode build phase:
mkdir -p "${WORKSPACE}/builds"
Now when I click the check box for "Pack application and build .ipa?", specify a .ipa filename pattern and use the output directory: ipa , the job runs, succeeds, and gives me a .ipa I can see in the Workspace at /builds/ipa/{name}.ipa
I hope this helps.
Had exactly the same problem as you.
Besides "Pack application and build .ipa", you also have to check "Unlock Keychain?" which can be found at "Code signing & OS X keychain options".
Furthermore, set the keychain path to:
${HOME}/Library/Keychains/login.keychain
The keychain password for me was the same as my user password. If you leave that field empty, you will see that your build freezes at a certain point. Inside the terminal where you started jenkins, you will see that it waits for you to type the password.
Hope it helped.

Why when generating .app file, xcode needs to be open?

I'm writing a script to build and generate an .app file from an specific target and scheme (to work with simulator). I was using something like:
DEVELOPER_DIR=$xcode_app_path/Contents/Developer xcodebuild -sdk iphonesimulator -scheme "${SCHEME}" -target "${TARGET}" ONLY_ACTIVE_ARCH=NO CONFIGURATION_BUILD_DIR="${build_folder}" clean build
The problem is, that I need to open XCode for the code above to work, otherwise will show me a message telling me that:
xcodebuild: error: The project 'Project-name' does not contain a scheme named "${SCHEME}"
I still yet, don't understand what is XCode doing in background. But if I open the IDE and run the script, works. If I open and later close it, and run the script works. It's not working if the project hasn't been opened first with XCode ¬¬
Any suggestion would be appreaciated
If someone is facing the same experience, this is what I found out. And works.
Well, apparently when you create a new scheme through command line, by default is not "shared". So when you start to link to the folder
xcshareddata --> xcschemes
(trying to build with command line, for example) there's nothing to link with. Xcode do this automaticaly under the hood when you open your project...
The guys from CocoaPods were facing the same issue, so they created a Ruby gem that shares the scheme and place the file in his xcscheme folder:
This is the repo: https://github.com/CocoaPods/Xcodeproj
Also, thanks to this post and this post I found out how to use xcodeproj gem, and change the shared option with a script that I put downhere:
require 'xcodeproj'
xcproj = Xcodeproj::Project.open("MyProject.xcodeproj")
xcproj.recreate_user_schemes
xcproj.save

Xcode 4 - command line target with initial setup

I am implementing an app which has a dependency to a command line tool.
This is because there are some presets to do.
The command line tool is responsible to create a sqlite file with all initial information needed by the app (clearly speaking: its just a tool to add some initial data to the app).
Now the problem is, that during debugging everything works fine but if I'll do a release build, some errors occur.
The first error was
target specifies product type 'com.apple.product-type.tool', but there's no such
product type for the 'iphoneos' platform"
This was because my main app had the command line tool as a target dependency.
I solved this by removing the target dependency and added the build to a run script phase:
#Build the initial setup target
xcodebuild -target InitialCoreDataSetup -sdk macosx -configuration $CONFIGURATION
#Run initializing data target to get current sqlite file
cd "$CONFIGURATION_BUILD_DIR"
cd ..
cd "$CONFIGURATION"
current_dir=$(pwd)
./InitialCoreDataSetup "$current_dir" "$SRCROOT" "$CONFIGURATION"
Now the problem is, that I get the following error:
=== BUILD NATIVE TARGET InitialCoreDataSetup OF PROJECT XY WITH CONFIGURATION AdHoc ===
Check dependencies
SDK Configuration Error: no wrapper for product type #
I have absolutely no idea how to fix this.
Any suggestions?
How do you provide your app with an initial sqlite file?
Thanks in advance for any help!
EDIT
At the moment the build phases of my app target are the following:
BTW: I finally got that working by the following line:
#Build the initial setup target
export DYLD_FRAMEWORK_PATH="$SYMROOT/Debug"
/usr/bin/env -i xcodebuild -target InitialCoreDataSetup -sdk "macosx" -configuration Debug OBJROOT="$SYMROOT/Debug" SYMROOT="$SYMROOT"
echo "$SYMROOT/Debug"
#Run initializing data target to get current sqlite file
cd "$SYMROOT/Debug"
./InitialCoreDataSetup "$SYMROOT/Debug" "$SRCROOT" "$CONFIGURATION"
The trick is to start the initialCoreDataSetup always in DEBUG mode
I would suggest letting Xcode do the build in this situation since you should be able to generate the sqllite file by using a build rule to run a script before the other build rules run.

Resources