ALTERNATIVES:
https://gist.github.com/3705459 - easy copy/paste of latest version (but install instructions may change - see below!)
Karl's library (http://stackoverflow.com/a/5721978/153422 ) takes much more effort to setup, but much nicer long-term solution (it converts your library into a Framework).
RECENT CHANGES:
-
Info on how to use this script with a project-embedded-in-another-project (although I highly recommend NOT doing that, ever - Apple has a couple of show-stopper bugs in Xcode if you embed projects inside each other, from Xcode 3.x through to Xcode 4.6.x)
-
Bonus script to let you auto-include Bundles (i.e. include PNG files, PLIST files etc from your library!) - see below (scroll to bottom)
-
now supports iPhone5 (using Apple's workaround to the bugs in lipo). NOTE: the install instructions have changed (I can probably simplify this by changing the script in future, but don't want to risk it now)
-
"copy headers" section now respects the build setting for the location of the public headers (courtesy of Frederik Wallner)
-
Added explicit setting of SYMROOT (maybe need OBJROOT to be set too?), thanks to Doug Dickinson
SCRIPT (this is what you have to copy/paste)
For usage / install instructions, see below
set-o pipefail
DEBUG_THIS_SCRIPT="false"if[ $DEBUG_THIS_SCRIPT ="true"]then
echo "########### TESTS #############"
echo "Use the following variables when debugging this script; note that they may change on recursions"
echo "BUILD_DIR = $BUILD_DIR"
echo "BUILD_ROOT = $BUILD_ROOT"
echo "CONFIGURATION_BUILD_DIR = $CONFIGURATION_BUILD_DIR"
echo "BUILT_PRODUCTS_DIR = $BUILT_PRODUCTS_DIR"
echo "CONFIGURATION_TEMP_DIR = $CONFIGURATION_TEMP_DIR"
echo "TARGET_BUILD_DIR = $TARGET_BUILD_DIR"fi
SDK_VERSION=$(echo ${SDK_NAME}| grep -o '.\{3\}$')
OTHER_SDK_TO_BUILD=iphoneos${SDK_VERSION}else
OTHER_SDK_TO_BUILD=iphonesimulator${SDK_VERSION}fi
echo "XCode has selected SDK: ${PLATFORM_NAME} with version: ${SDK_VERSION} (although back-targetting: ${IPHONEOS_DEPLOYMENT_TARGET})"
echo "...therefore, OTHER_SDK_TO_BUILD = ${OTHER_SDK_TO_BUILD}"
echo "RECURSION: I am NOT the root invocation, so I'm NOT going to recurse"else
echo "RECURSION: I am the root ... recursing all missing build targets NOW..."
echo "RECURSION: ...about to invoke: xcodebuild -configuration \"${CONFIGURATION}\" -project \"${PROJECT_NAME}.xcodeproj\" -target \"${TARGET_NAME}\" -sdk \"${OTHER_SDK_TO_BUILD}\" ${ACTION} RUN_CLANG_STATIC_ANALYZER=NO" BUILD_DIR=\"${BUILD_DIR}\" BUILD_ROOT=\"${BUILD_ROOT}\" SYMROOT=\"${SYMROOT}\"
xcodebuild -configuration "${CONFIGURATION}"-project "${PROJECT_NAME}.xcodeproj"-target "${TARGET_NAME}"-sdk "${OTHER_SDK_TO_BUILD}" ${ACTION} RUN_CLANG_STATIC_ANALYZER=NO BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" SYMROOT="${SYMROOT}"
ACTION="build"
CURRENTCONFIG_DEVICE_DIR=${SYMROOT}/${CONFIGURATION}-iphoneos
CURRENTCONFIG_SIMULATOR_DIR=${SYMROOT}/${CONFIGURATION}-iphonesimulator
echo "Taking device build from: ${CURRENTCONFIG_DEVICE_DIR}"
echo "Taking simulator build from: ${CURRENTCONFIG_SIMULATOR_DIR}"
CREATING_UNIVERSAL_DIR=${SYMROOT}/${CONFIGURATION}-universal
echo "...I will output a universal build to: ${CREATING_UNIVERSAL_DIR}"
rm -rf "${CREATING_UNIVERSAL_DIR}"
mkdir "${CREATING_UNIVERSAL_DIR}"
echo "lipo: for current configuration (${CONFIGURATION}) creating output file: ${CREATING_UNIVERSAL_DIR}/${EXECUTABLE_NAME}"
xcrun -sdk iphoneos lipo -create -output "${CREATING_UNIVERSAL_DIR}/${EXECUTABLE_NAME}""${CURRENTCONFIG_DEVICE_DIR}/${EXECUTABLE_NAME}""${CURRENTCONFIG_SIMULATOR_DIR}/${EXECUTABLE_NAME}"
echo "Fetching headers from ${PUBLIC_HEADERS_FOLDER_PATH}"
echo " (if you embed your library project in another project, you will need to add"
echo " a "UserSearchHeaders" build setting of: (NB INCLUDE THE DOUBLE QUOTES BELOW!)"
echo ' "$(TARGET_BUILD_DIR)/usr/local/include/"'if[-d "${CURRENTCONFIG_DEVICE_DIR}${PUBLIC_HEADERS_FOLDER_PATH}"]then
mkdir -p "${CREATING_UNIVERSAL_DIR}${PUBLIC_HEADERS_FOLDER_PATH}"
cp -r "${CURRENTCONFIG_DEVICE_DIR}${PUBLIC_HEADERS_FOLDER_PATH}"*"${CREATING_UNIVERSAL_DIR}${PUBLIC_HEADERS_FOLDER_PATH}"fifi
INSTALL INSTRUCTIONS
- Create a static lib project
- Select the Target
- In "Build Settings" tab, set "Build Active Architecture Only" to "NO" (for all items)
- In "Build Phases" tab, select "Add ... New Build Phase ... New Run Script Build Phase"
- Copy/paste the script (above) into the box
...BONUS OPTIONAL usage:
- OPTIONAL: if you have headers in your library, add them to the "Copy Headers" phase
- OPTIONAL: ...and drag/drop them from the "Project" section to the "Public" section
- OPTIONAL: ...and they will AUTOMATICALLY be exported every time you build the app, into a sub-directory of the "debug-universal" directory (they will be in usr/local/include)
- OPTIONAL: NOTE: if you also try to drag/drop your project into another Xcode project, this exposes a bug in Xcode 4, where it cannot create an .IPA file if you have Public Headers in your drag/dropped project. The workaround: dont' embed xcode projects (too many bugs in Apple's code!)
If you can't find the output file, here's a workaround:
-
Add the following code to the very end of the script (courtesy of Frederik Wallner): open "${CREATING_UNIVERSAL_DIR}"
-
Apple deletes all output after 200 lines. Select your Target, and in the Run Script Phase, you MUST untick: "Show environment variables in build log"
-
if you're using a custom "build output" directory for XCode4, then XCode puts all your "unexpected" files in the wrong place.
- Build the project
- Click on the last icon on the right, in the top left area of Xcode4.
- Select the top item (this is your "most recent build". Apple should auto-select it, but they didn't think of that)
- in the main window, scroll to bottom. The very last line should read: lipo: for current configuration (Debug) creating output file: /Users/blah/Library/Developer/Xcode/DerivedData/AppName-ashwnbutvodmoleijzlncudsekyf/Build/Products/Debug-universal/libTargetName.a
...that is the location of your Universal Build.
How to include "non sourcecode" files in your project (PNG, PLIST, XML, etc)
- Do everything above, check it works
- Create a new Run Script phase that comes AFTER THE FIRST ONE (copy/paste the code below)
- Create a new Target in Xcode, of type "bundle"
- In your MAIN PROJECT, in "Build Phases", add the new bundle as something it "depends on" (top section, hit the plus button, scroll to bottom, find the ".bundle" file in your Products)
- In your NEW BUNDLE TARGET, in "Build Phases", add a "Copy Bundle Resources" section, and drag/drop all the PNG files etc into it
Script to auto-copy the built bundle(s) into same folder as your FAT static library:
echo "RunScript2:"
echo "Autocopying any bundles into the 'universal' output folder created by RunScript1"
CREATING_UNIVERSAL_DIR=${SYMROOT}/${CONFIGURATION}-universal
cp -r "${BUILT_PRODUCTS_DIR}/"*.bundle "${CREATING_UNIVERSAL_DIR}"