[iphone] Build fat static library (device + simulator) using Xcode and SDK 4+

It appears that we can - theoretically - build a single static library that includes both simulator and iPhone and iPad.

However, Apple has no documentation on this that I can find, and Xcode's default templates are NOT configured to do this.

I'm looking for a simple, portable, re-usable technique that can be done inside Xcode.

Some history:

  • In 2008, we used to be able to make single static-libs that included both sim and device. Apple disabled that.
  • Throughout 2009, we made pairs of static libs - one for sim, one for device. Apple has now disabled that too.

References:

  1. This is a great idea, it's an excellent approach, but it doesn't work: http://www.drobnik.com/touch/2010/04/universal-static-libraries/

    • There's some bugs in his script that means it only works on his machine - he should be using BUILT_PRODUCTS_DIR and/or BUILD_DIR instead of "guesstimating" them)
    • Apple's latest Xcode prevents you from doing what he's done - it simply will not work, due to the (Documented) change in how Xcode processes targets)
  2. Another SO questioner asked how to do it WITHOUT xcode, and with responses that focussed on the arm6 vs arm7 part - but ignored the i386 part: How do i compile a static library (fat) for armv6, armv7 and i386

    • Since Apple's latest changes, the Simulator part isn't the same as the arm6/arm7 difference any more - it's a different problem, see above)

This question is related to iphone xcode

The answer is


Great job! I hacked together something similar, but had to run it separately. Having it just be part of the build process makes it so much simpler.

One item of note. I noticed that it doesn't copy over any of the include files that you mark as public. I've adapted what I had in my script to yours and it works fairly well. Paste the following to the end of your script.

if [ -d "${CURRENTCONFIG_DEVICE_DIR}/usr/local/include" ]
then
  mkdir -p "${CURRENTCONFIG_UNIVERSAL_DIR}/usr/local/include"
  cp "${CURRENTCONFIG_DEVICE_DIR}"/usr/local/include/* "${CURRENTCONFIG_UNIVERSAL_DIR}/usr/local/include"
fi

XCode 12 update:

If you run xcodebuild without -arch param, XCode 12 will build simulator library with architecture "arm64 x86_64" as default.

Then run xcrun -sdk iphoneos lipo -create -output will conflict, because arm64 architecture exist in simulator and also device library.

I fork script from Adam git and fix it.


I needed a fat static lib for JsonKit so created a static lib project in Xcode and then ran this bash script in the project directory. So long as you've configured the xcode project with "Build active configuration only" turned off, you should get all architectures in one lib.

#!/bin/bash
xcodebuild -sdk iphoneos
xcodebuild -sdk iphonesimulator
lipo -create -output libJsonKit.a build/Release-iphoneos/libJsonKit.a build/Release-iphonesimulator/libJsonKit.a

I've made an XCode 4 project template that lets you make a universal framework as easily as making a regular library.


IOS 10 Update:

I had a problem with building the fatlib with iphoneos10.0 because the regular expression in the script only expects 9.x and lower and returns 0.0 for ios 10.0

to fix this just replace

SDK_VERSION=$(echo ${SDK_NAME} | grep -o '.\{3\}$')

with

SDK_VERSION=$(echo ${SDK_NAME} | grep -o '[\\.0-9]\{3,4\}$')

I actually just wrote my own script for this purpose. It doesn't use Xcode. (It's based off a similar script in the Gambit Scheme project.)

Basically, it runs ./configure and make three times (for i386, armv7, and armv7s), and combines each of the resulting libraries into a fat lib.


I've made this into an Xcode 4 template, in the same vein as Karl's static framework template.

I found that building static frameworks (instead of plain static libraries) was causing random crashes with LLVM, due to an apparent linker bug - so, I guess static libraries are still useful!


I have spent many hours trying to build a fat static library that will work on armv7, armv7s, and the simulator. Finally found a solution.

The gist is to build the two libraries (one for the device and then one for the simulator) separately, rename them to distinguish from each other, and then lipo -create them into one library.

lipo -create libPhone.a libSimulator.a -output libUniversal.a

I tried it and it works!


There is a command-line utility xcodebuild and you can run shell command within xcode. So, if you don't mind using custom script, this script may help you.

#Configurations.
#This script designed for Mac OS X command-line, so does not use Xcode build variables.
#But you can use it freely if you want.

TARGET=sns
ACTION="clean build"
FILE_NAME=libsns.a

DEVICE=iphoneos3.2
SIMULATOR=iphonesimulator3.2






#Build for all platforms/configurations.

xcodebuild -configuration Debug -target ${TARGET} -sdk ${DEVICE} ${ACTION} RUN_CLANG_STATIC_ANALYZER=NO
xcodebuild -configuration Debug -target ${TARGET} -sdk ${SIMULATOR} ${ACTION} RUN_CLANG_STATIC_ANALYZER=NO
xcodebuild -configuration Release -target ${TARGET} -sdk ${DEVICE} ${ACTION} RUN_CLANG_STATIC_ANALYZER=NO
xcodebuild -configuration Release -target ${TARGET} -sdk ${SIMULATOR} ${ACTION} RUN_CLANG_STATIC_ANALYZER=NO







#Merge all platform binaries as a fat binary for each configurations.

DEBUG_DEVICE_DIR=${SYMROOT}/Debug-iphoneos
DEBUG_SIMULATOR_DIR=${SYMROOT}/Debug-iphonesimulator
DEBUG_UNIVERSAL_DIR=${SYMROOT}/Debug-universal

RELEASE_DEVICE_DIR=${SYMROOT}/Release-iphoneos
RELEASE_SIMULATOR_DIR=${SYMROOT}/Release-iphonesimulator
RELEASE_UNIVERSAL_DIR=${SYMROOT}/Release-universal

rm -rf "${DEBUG_UNIVERSAL_DIR}"
rm -rf "${RELEASE_UNIVERSAL_DIR}"
mkdir "${DEBUG_UNIVERSAL_DIR}"
mkdir "${RELEASE_UNIVERSAL_DIR}"

lipo -create -output "${DEBUG_UNIVERSAL_DIR}/${FILE_NAME}" "${DEBUG_DEVICE_DIR}/${FILE_NAME}" "${DEBUG_SIMULATOR_DIR}/${FILE_NAME}"
lipo -create -output "${RELEASE_UNIVERSAL_DIR}/${FILE_NAME}" "${RELEASE_DEVICE_DIR}/${FILE_NAME}" "${RELEASE_SIMULATOR_DIR}/${FILE_NAME}"

Maybe looks inefficient(I'm not good at shell script), but easy to understand. I configured a new target running only this script. The script is designed for command-line but not tested in :)

The core concept is xcodebuild and lipo.

I tried many configurations within Xcode UI, but nothing worked. Because this is a kind of batch processing, so command-line design is more suitable, so Apple removed batch build feature from Xcode gradually. So I don't expect they offer UI based batch build feature in future.


Examples related to iphone

Detect if the device is iPhone X Xcode 8 shows error that provisioning profile doesn't include signing certificate Access files in /var/mobile/Containers/Data/Application without jailbreaking iPhone Certificate has either expired or has been revoked Missing Compliance in Status when I add built for internal testing in Test Flight.How to solve? cordova run with ios error .. Error code 65 for command: xcodebuild with args: "Could not find Developer Disk Image" Reason: no suitable image found iPad Multitasking support requires these orientations How to insert new cell into UITableView in Swift

Examples related to xcode

Undefined Symbols error when integrating Apptentive iOS SDK via Cocoapods Xcode 12, building for iOS Simulator, but linking in object file built for iOS, for architecture arm64 iPhone is not available. Please reconnect the device Make a VStack fill the width of the screen in SwiftUI error Failed to build iOS project. We ran "xcodebuild" command but it exited with error code 65 The iOS Simulator deployment targets is set to 7.0, but the range of supported deployment target version for this platform is 8.0 to 12.1 Xcode 10.2.1 Command PhaseScriptExecution failed with a nonzero exit code Git is not working after macOS Update (xcrun: error: invalid active developer path (/Library/Developer/CommandLineTools) Xcode 10: A valid provisioning profile for this executable was not found Xcode 10, Command CodeSign failed with a nonzero exit code