Skip to content

Instantly share code, notes, and snippets.

@cromandini
Forked from cconway25/gist:7ff167c6f98da33c5352
Last active November 9, 2024 08:56
Show Gist options
  • Save cromandini/1a9c4aeab27ca84f5d79 to your computer and use it in GitHub Desktop.
Save cromandini/1a9c4aeab27ca84f5d79 to your computer and use it in GitHub Desktop.
This run script will build the iphoneos and iphonesimulator schemes and then combine them into a single framework using the lipo tool (including all the Swift module architectures).
#!/bin/sh
UNIVERSAL_OUTPUTFOLDER=${BUILD_DIR}/${CONFIGURATION}-universal
# make sure the output directory exists
mkdir -p "${UNIVERSAL_OUTPUTFOLDER}"
# Step 1. Build Device and Simulator versions
xcodebuild -target "${PROJECT_NAME}" ONLY_ACTIVE_ARCH=NO -configuration ${CONFIGURATION} -sdk iphoneos BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" clean build
xcodebuild -target "${PROJECT_NAME}" -configuration ${CONFIGURATION} -sdk iphonesimulator ONLY_ACTIVE_ARCH=NO BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" clean build
# Step 2. Copy the framework structure (from iphoneos build) to the universal folder
cp -R "${BUILD_DIR}/${CONFIGURATION}-iphoneos/${PROJECT_NAME}.framework" "${UNIVERSAL_OUTPUTFOLDER}/"
# Step 3. Copy Swift modules from iphonesimulator build (if it exists) to the copied framework directory
SIMULATOR_SWIFT_MODULES_DIR="${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${PROJECT_NAME}.framework/Modules/${PROJECT_NAME}.swiftmodule/."
if [ -d "${SIMULATOR_SWIFT_MODULES_DIR}" ]; then
cp -R "${SIMULATOR_SWIFT_MODULES_DIR}" "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework/Modules/${PROJECT_NAME}.swiftmodule"
fi
# Step 4. Create universal binary file using lipo and place the combined executable in the copied framework directory
lipo -create -output "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework/${PROJECT_NAME}" "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${PROJECT_NAME}.framework/${PROJECT_NAME}" "${BUILD_DIR}/${CONFIGURATION}-iphoneos/${PROJECT_NAME}.framework/${PROJECT_NAME}"
# Step 5. Convenience step to copy the framework to the project's directory
cp -R "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework" "${PROJECT_DIR}"
# Step 6. Convenience step to open the project's directory in Finder
open "${PROJECT_DIR}"
@marbetschar
Copy link

Very cool!!!

Seems to work beautiful - except for bitcode ...? When I integrate a framework built with your script into another project, Xcode is claiming "You must rebuild it with bitcode enabled (Xcode setting ENABLE_BITCODE), obtain an updated library from the vendor, or disable bitcode for this target. for architecture arm64"

Unfortunately I need bitcode - do you have any idea?

@marbetschar
Copy link

I've just tried adding ENABLE_BITCODE=YES and OTHER_CFLAGS="-fembed-bitcode" to the xcodebuild command, but without any luck :( Any help would be highly appreciated.

Here the full commands with the options added:

xcodebuild -target "${PROJECT_NAME}" ENABLE_BITCODE=YES OTHER_CFLAGS="-fembed-bitcode" ONLY_ACTIVE_ARCH=NO -configuration ${CONFIGURATION} -sdk iphoneos  BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" clean build
xcodebuild -target "${PROJECT_NAME}" -configuration ${CONFIGURATION} -sdk iphonesimulator ENABLE_BITCODE=YES OTHER_CFLAGS="-fembed-bitcode" ONLY_ACTIVE_ARCH=NO BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" clean build

@marbetschar
Copy link

Ok - as it turns out the only reason why Bitcode is not working is due to a bug in Xcode 7.3.
Setting the following on top of the script fixes the bit code issue:

# workaround for bitcode generation problem with Xcode 7.3
unset TOOLCHAINS

See this question on StackOverflow for more information: http://stackoverflow.com/questions/36184930/xcodebuild-7-3-cant-enable-bitcode

@atsepkov
Copy link

I'm having trouble getting this to pick up my Cocoapod libraries. Whereas regular target builds fine, this one throws an error on code that references 3rd party framework (in my case SwiftyJSON). Did anyone else encounter this?

@atsepkov
Copy link

atsepkov commented May 17, 2016

Figured out the initial issue I had, when using cocoapods, -target parameter won't work, you should instead use a combination of -workspace and -scheme. However, now I've encountered another issue. The framework compiled correctly into a fat binary, but when I embed it in the project to run in a simulator, I get no matching architecture in universal wrapper error for each of cocoapod dependencies (although the architecture for the main library is there, and compilation works correctly, this is simply a crash after the simulator loads).

It sounds like each of the cocoapods libs should be lipoed together as well, but the simulator build is lacking the Frameworks folder where I could grab the binaries to lipo. Is there an additional parameter I'm missing to xcodebuild command?

@atsepkov
Copy link

Figured out where the simulator intermediate files are stored. I've modified the script to work with Cocoapods, here is my new version for those using Cocoapods as well: https://gist.github.com/atsepkov/1673c2d899470270e3eb313912aafc6f

@mabbas007
Copy link

Where to place that script in XCode ?

@acegreen
Copy link

acegreen commented Jul 6, 2016

@mabbas007, add a run script in the build phases of the target that contains the framework. Its either a target of your app or a whole other project

@toughrogrammer
Copy link

toughrogrammer commented Aug 23, 2016

Does it works for swift framework project? I tested it but it does not give me luck :(

@squalx
Copy link

squalx commented Sep 22, 2016

To not run into issues with recursion (so using a post-processing code that runs after a build that then calls another build, which will run the script again and run another build, etc...), don't add your script in the build phases of your target, or the post processing on your main scheme. Create a new Aggregate target (it's on the cross platform tab) and add the script on the Build Phases there.

For Cocoapods, here's an example of what the xcodebuild command will look like:
xcodebuild -workspace YourLib.xcworkspace -scheme YourLibScheme ONLY_ACTIVE_ARCH=NO -configuration ${CONFIGURATION} -sdk iphoneos BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" clean build

@SheldonWangRJT
Copy link

Thanks in advance for my question.
What about the dsym file. Both simulator and iphoneos will generate dsym files. Should I use lipo to combine them as well?

@himanshumahajan04
Copy link

Add this run script in build phase of framework target:

   #!/bin/sh

   UNIVERSAL_OUTPUTFOLDER=${BUILD_DIR}/${CONFIGURATION}-universal

   # make sure the output directory exists
   mkdir -p "${UNIVERSAL_OUTPUTFOLDER}"

   # Next, work out if we're in SIM or DEVICE
   if [ "false" == ${ALREADYINVOKED:-false} ]
   then

   export ALREADYINVOKED="true"

   if [ ${PLATFORM_NAME} = "iphonesimulator" ]
   then
   xcodebuild -target "${PROJECT_NAME}" ONLY_ACTIVE_ARCH=NO -configuration ${CONFIGURATION} -sdk iphoneos  BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" clean build
   else
   xcodebuild -target "${PROJECT_NAME}" -configuration ${CONFIGURATION} -sdk iphonesimulator ONLY_ACTIVE_ARCH=NO BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" clean build
   fi

   # Step 2. Copy the framework structure (from iphoneos build) to the universal folder
   cp -R "${BUILD_DIR}/${CONFIGURATION}-iphoneos/${PROJECT_NAME}.framework" "${UNIVERSAL_OUTPUTFOLDER}/"

   # Step 3. Copy Swift modules from iphonesimulator build (if it exists) to the copied framework directory
   SIMULATOR_SWIFT_MODULES_DIR="${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${PROJECT_NAME}.framework/Modules/${PROJECT_NAME}.swiftmodule/."
   if [ -d "${SIMULATOR_SWIFT_MODULES_DIR}" ]; then
   cp -R "${SIMULATOR_SWIFT_MODULES_DIR}" "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework/Modules/${PROJECT_NAME}.swiftmodule"
   fi

   # Step 4. Create universal binary file using lipo and place the combined executable in the copied framework directory
   lipo -create -output "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework/${PROJECT_NAME}" "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${PROJECT_NAME}.framework/${PROJECT_NAME}" "${BUILD_DIR}/${CONFIGURATION}-iphoneos/${PROJECT_NAME}.framework/${PROJECT_NAME}"

    # Step 5. Convenience step to copy the framework to the project's directory
    cp -R "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework" "${PROJECT_DIR}"

    # Step 6. Convenience step to open the project's directory in Finder
    open "${PROJECT_DIR}"

    fi

@Yashrad
Copy link

Yashrad commented Nov 17, 2017

I'm running this build script for a cocoapod project.
Im facing this error and can't figure out the solution:
lipo: can't open input file: /Users/yash/Library/Developer/Xcode/DerivedData/xxxarkglhuoabxutxcoexvaqkvhqarr/Build/Products/Debug-iphonesimulator/xxx.build/xxx (No such file or directory)

Can anybody help please?

@givip
Copy link

givip commented Nov 28, 2017

Add to simulator build line VALID_ARCHS="x86_64 i386", and framework will support all simulators.

@kashiftriffort
Copy link

kashiftriffort commented Mar 29, 2018

@i have created universal framework and it worked sucessfully with Target App. However if I want to add any external framework like Firebase or any other insider universal framework, how can we achieve that. Below is the script which is used for creating universal framework.

#!/bin/sh

UNIVERSAL_OUTPUTFOLDER=${BUILD_DIR}/${CONFIGURATION}-universal

# make sure the output directory exists
mkdir -p "${UNIVERSAL_OUTPUTFOLDER}"

# Step 1. Build Device and Simulator versions
xcodebuild -target "${PROJECT_NAME}" ONLY_ACTIVE_ARCH=NO -configuration ${CONFIGURATION} -sdk iphoneos  BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" clean build
xcodebuild -target "${PROJECT_NAME}" -configuration ${CONFIGURATION} -sdk iphonesimulator ONLY_ACTIVE_ARCH=NO BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" clean build

# Step 2. Copy the framework structure (from iphoneos build) to the universal folder
cp -R "${BUILD_DIR}/${CONFIGURATION}-iphoneos/${PROJECT_NAME}.framework" "${UNIVERSAL_OUTPUTFOLDER}/"

# Step 3. Copy Swift modules from iphonesimulator build (if it exists) to the copied framework directory
SIMULATOR_SWIFT_MODULES_DIR="${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${PROJECT_NAME}.framework/Modules/${PROJECT_NAME}.swiftmodule/."
if [ -d "${SIMULATOR_SWIFT_MODULES_DIR}" ]; then
cp -R "${SIMULATOR_SWIFT_MODULES_DIR}" "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework/Modules/${PROJECT_NAME}.swiftmodule"
fi

# Step 4. Create universal binary file using lipo and place the combined executable in the copied framework directory
lipo -create -output "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework/${PROJECT_NAME}" "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${PROJECT_NAME}.framework/${PROJECT_NAME}" "${BUILD_DIR}/${CONFIGURATION}-iphoneos/${PROJECT_NAME}.framework/${PROJECT_NAME}"

# Step 5. Convenience step to copy the framework to the project's directory
cp -R "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework" "${PROJECT_DIR}"

# Step 6. Convenience step to open the project's directory in Finder
open "${PROJECT_DIR}"

@fabiosoft
Copy link

Tested with Xcode 10?

@SwenVanZanten
Copy link

Yes, just used it 👍
Thanks y'all 🥇

@aboud-masoud
Copy link

/core/build` because it was not created by the build system and it is not a subfolder of derived data

Please can any one help me when I try to run the script I got this error

@Atsumi3
Copy link

Atsumi3 commented Nov 7, 2018

nicework 👍

@shiqp
Copy link

shiqp commented Dec 10, 2018

Thanks in advance for my question.
What about the dsym file. Both simulator and iphoneos will generate dsym files. Should I use lipo to combine them as well?

Any solution for this problem?

@wellbranding
Copy link

not working with cocoa pods dependencies. here is more information:
https://www.reddit.com/r/swift/comments/admgcu/compiled_framework_provides_bitcode_error_when/

@jofvr
Copy link

jofvr commented May 14, 2019

Hello, i have import this script in my framework by follow this article. Archive work fine, it open the folder but when i copy the generated framework into my project, i have an error :
Could not find module '' for architecture 'x86_64'; found: arm64, arm, armv7
It work fine on device... Does i have make somethings wrong ?

@hackiftekhar
Copy link

The PROJECT_NAME should be replace by TARGET_NAME

@jofvr
Copy link

jofvr commented May 21, 2019

Thanks for answer. I have the same error when build my project with framework :

Could not find module '' for architecture 'x86_64'; found: arm64, arm, armv7

@fonixland
Copy link

There is a little more info on how this unsupported (according to Apple) feature broke in Xcode 10. Quinn suggests making a feature request so that Apple will hopefully officially support it in the future. There is also a similar script which purports to work with Xcode 10, but I have not personally tested it yet, so YMMV.

https://forums.developer.apple.com/thread/109583

Copy link

ghost commented Jun 27, 2019

Thanks for answer. I have the same error when build my project with framework :

Could not find module '' for architecture 'x86_64'; found: arm64, arm, armv7

Is any solution for this issue

@Linezek
Copy link

Linezek commented Oct 22, 2019

@i have created universal framework and it worked sucessfully with Target App. However if I want to add any external framework like Firebase or any other insider universal framework, how can we achieve that. Below is the script which is used for creating universal framework.

#!/bin/sh

UNIVERSAL_OUTPUTFOLDER=${BUILD_DIR}/${CONFIGURATION}-universal

# make sure the output directory exists
mkdir -p "${UNIVERSAL_OUTPUTFOLDER}"

# Step 1. Build Device and Simulator versions
xcodebuild -target "${PROJECT_NAME}" ONLY_ACTIVE_ARCH=NO -configuration ${CONFIGURATION} -sdk iphoneos  BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" clean build
xcodebuild -target "${PROJECT_NAME}" -configuration ${CONFIGURATION} -sdk iphonesimulator ONLY_ACTIVE_ARCH=NO BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" clean build

# Step 2. Copy the framework structure (from iphoneos build) to the universal folder
cp -R "${BUILD_DIR}/${CONFIGURATION}-iphoneos/${PROJECT_NAME}.framework" "${UNIVERSAL_OUTPUTFOLDER}/"

# Step 3. Copy Swift modules from iphonesimulator build (if it exists) to the copied framework directory
SIMULATOR_SWIFT_MODULES_DIR="${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${PROJECT_NAME}.framework/Modules/${PROJECT_NAME}.swiftmodule/."
if [ -d "${SIMULATOR_SWIFT_MODULES_DIR}" ]; then
cp -R "${SIMULATOR_SWIFT_MODULES_DIR}" "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework/Modules/${PROJECT_NAME}.swiftmodule"
fi

# Step 4. Create universal binary file using lipo and place the combined executable in the copied framework directory
lipo -create -output "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework/${PROJECT_NAME}" "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${PROJECT_NAME}.framework/${PROJECT_NAME}" "${BUILD_DIR}/${CONFIGURATION}-iphoneos/${PROJECT_NAME}.framework/${PROJECT_NAME}"

# Step 5. Convenience step to copy the framework to the project's directory
cp -R "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework" "${PROJECT_DIR}"

# Step 6. Convenience step to open the project's directory in Finder
open "${PROJECT_DIR}"

Have you found a solution to include Firebase in your universal framework?

@simform-solutions
Copy link

simform-solutions commented Nov 18, 2019

#!/bin/sh
UNIVERSAL_OUTPUTFOLDER=${BUILD_DIR}/${CONFIGURATION}-universal
OUTPUT_FOLDER=${PROJECT_DIR}

make sure the output directory exists
mkdir -p "${UNIVERSAL_OUTPUTFOLDER}"

Step 1. Build Device and Simulator versions. This also includes the full bitcode generation of the framework
xcodebuild BITCODE_GENERATION_MODE=bitcode OTHER_CFLAGS="-fembed-bitcode" -workspace ${PROJECT_NAME}.xcworkspace -scheme ${PROJECT_NAME} ONLY_ACTIVE_ARCH=NO -configuration ${CONFIGURATION} -sdk iphoneos BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" -UseModernBuildSystem=NO clean build

xcodebuild BITCODE_GENERATION_MODE=bitcode OTHER_CFLAGS="-fembed-bitcode" -workspace ${PROJECT_NAME}.xcworkspace -scheme ${PROJECT_NAME} ONLY_ACTIVE_ARCH=NO -configuration ${CONFIGURATION} -sdk iphonesimulator BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" -UseModernBuildSystem=NO clean build

Step 2. Copy the framework structure (from iPhones build) to the universal folder
cp -R "${BUILD_DIR}/${CONFIGURATION}-iphoneos/${PROJECT_NAME}.framework" "${UNIVERSAL_OUTPUTFOLDER}/"

Step 3. Copy Swift modules (from iphonesimulator build) to the copied framework directory
cp -R "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${PROJECT_NAME}.framework/Modules/${PROJECT_NAME}.swiftmodule/." "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework/Modules/${PROJECT_NAME}.swiftmodule"

Step 4. Create a universal binary file using lipo and place the combined executable in the copied framework directory
lipo -create -output "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework/${PROJECT_NAME}" "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${PROJECT_NAME}.framework/${PROJECT_NAME}" "${BUILD_DIR}/${CONFIGURATION}-iphoneos/${PROJECT_NAME}.framework/${PROJECT_NAME}"

Step 5. Convenience step to copy the framework to the project's directory
cp -R "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework" "${OUTPUT_FOLDER}"
Step 6. Convenience step to open the project's directory in Finder
open "${OUTPUT_FOLDER}"

I'm running this script to build a framework with cocoapods earlier in Xcode 10.3 it's working fine, But in new Xcode 11.2.1, this script is not working. can you please find me a workaround.

error ->

error: no such file or directory: '/Users/username/Documents/GitHub/sdkname/GenaratefremeWork/Pods/@/Users/username/Library/Developer/Xcode/DerivedData/SDK-ddyylnfmgscnezeppcqlekfbplzy/Build/Intermediates.noindex/SDK.build/Debug-iphoneos/SDK.build/Objects-normal/arm64/SDK.SwiftFileList'
Command /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swiftc failed with exit code 1

@IkeStarnes
Copy link

This is great, but how do you avoid running steps 2,3,4,5... if step #1 doesn't actually rebuild the framework?
In other words, if no source code changes between builds, how do you avoid the copy and lipo steps?

@KarenShaham
Copy link

Hi! great work! I'm having a problem with resources included in my SDK.
It seems the script creates another exe file inside the bundle.
Do you have any ideas how to handle it?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment