-
-
Save eladnava/0824d08da8f99419ef2c7b7fb6d4cc78 to your computer and use it in GitHub Desktop.
exec > /tmp/${PROJECT_NAME}_archive.log 2>&1 | |
UNIVERSAL_OUTPUTFOLDER=${BUILD_DIR}/${CONFIGURATION}-universal | |
if [ "true" == ${ALREADYINVOKED:-false} ] | |
then | |
echo "RECURSION: Detected, stopping" | |
else | |
export ALREADYINVOKED="true" | |
# make sure the output directory exists | |
mkdir -p "${UNIVERSAL_OUTPUTFOLDER}" | |
echo "Building for iPhoneSimulator" | |
xcodebuild -workspace "${WORKSPACE_PATH}" -scheme "${TARGET_NAME}" -configuration ${CONFIGURATION} -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 6' ONLY_ACTIVE_ARCH=NO ARCHS='i386 x86_64' BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" ENABLE_BITCODE=YES OTHER_CFLAGS="-fembed-bitcode" BITCODE_GENERATION_MODE=bitcode clean build | |
# Step 1. Copy the framework structure (from iphoneos build) to the universal folder | |
echo "Copying to output folder" | |
cp -R "${ARCHIVE_PRODUCTS_PATH}${INSTALL_PATH}/${FULL_PRODUCT_NAME}" "${UNIVERSAL_OUTPUTFOLDER}/" | |
# Step 2. Copy Swift modules from iphonesimulator build (if it exists) to the copied framework directory | |
SIMULATOR_SWIFT_MODULES_DIR="${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${TARGET_NAME}.framework/Modules/${TARGET_NAME}.swiftmodule/." | |
if [ -d "${SIMULATOR_SWIFT_MODULES_DIR}" ]; then | |
cp -R "${SIMULATOR_SWIFT_MODULES_DIR}" "${UNIVERSAL_OUTPUTFOLDER}/${TARGET_NAME}.framework/Modules/${TARGET_NAME}.swiftmodule" | |
fi | |
# Step 3. Create universal binary file using lipo and place the combined executable in the copied framework directory | |
echo "Combining executables" | |
lipo -create -output "${UNIVERSAL_OUTPUTFOLDER}/${EXECUTABLE_PATH}" "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${EXECUTABLE_PATH}" "${ARCHIVE_PRODUCTS_PATH}${INSTALL_PATH}/${EXECUTABLE_PATH}" | |
# Step 4. Create universal binaries for embedded frameworks | |
#for SUB_FRAMEWORK in $( ls "${UNIVERSAL_OUTPUTFOLDER}/${TARGET_NAME}.framework/Frameworks" ); do | |
#BINARY_NAME="${SUB_FRAMEWORK%.*}" | |
#lipo -create -output "${UNIVERSAL_OUTPUTFOLDER}/${TARGET_NAME}.framework/Frameworks/${SUB_FRAMEWORK}/${BINARY_NAME}" "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${SUB_FRAMEWORK}/${BINARY_NAME}" "${ARCHIVE_PRODUCTS_PATH}${INSTALL_PATH}/${TARGET_NAME}.framework/Frameworks/${SUB_FRAMEWORK}/${BINARY_NAME}" | |
#done | |
# Step 5. Convenience step to copy the framework to the project's directory | |
echo "Copying to project dir" | |
yes | cp -Rf "${UNIVERSAL_OUTPUTFOLDER}/${FULL_PRODUCT_NAME}" "${PROJECT_DIR}" | |
open "${PROJECT_DIR}" | |
fi |
I followed the steps in the guide. When I run 'file' command, �it prints only the following message
- Mach-O 64-bit dynamically linked shared library arm64
Where have I gone wrong ?
Here's something I found helpful:
When following the blog post, on Xcode 9.3, I had to do the hint outlined earlier:
change ARCHS='i386 x86_64' to ARCHS='x86_64'
This is because iOS 11 doesn't support i386.
However, changing the Deployment Target back to an older version of iOS, and this worked fine. Since many users writing frameworks want to support older versions of iOS, I would suggest moving the deployment target back to an earlier version (iOS 8 & 9 are tested and working for me). Then, no changes are needed to the script.
@MithunMadhav1112 I had the same issue until changing the deployment target. See above.
Upon further review, neither of these options appears to create a build that allows me to submit to the App Store. Using the option above works great for simulator and device, but when submitting to the App Store, I get several errors:
ITMS-90087: Unsupported Architectures. The framework contains unsupported architectures [x86_64, i386]'.
ITMS-90635: Invalid Mach-O format. The format in X.framework is not consistent with the Mach-O in the main bundle. The main bundle Mach-O contains armv7(bitcode) and arm64(bitcode), while the nested bundle Mach-O contains X86_64ALL(machine code). Verify that all of the targets for a platform have a consistent value for the ENABLE_BITCODE build setting.
ITMS-90209: Invalid Segment Alignment. The app binary at DIR/X.framework/X does not have proper segment alignment. Try rebuilding the app with the latest Xcode version.
@mknippen You'd have to strip the x86_64 and/or i386 from the frameworks you're using in order to submit to the App Store. Often you'll see people add a build phase script item to check if the current build configuration is RELEASE and then if so iterate through frameworks that are linked in and remove the x86 slices from them. You may find it easier if you do this with an app-store-specific build scheme.
Add below code If want to zip sequence into the script.
# Step 6. Create zip
cd ${PROJECT_DIR}
mkdir Build
cp LICENSE Build
cp -rf ${FULL_PRODUCT_NAME} Build
rm -rf "${FULL_PRODUCT_NAME}.zip"
zip -r "${FULL_PRODUCT_NAME}.zip" Build
rm -rf ${FULL_PRODUCT_NAME} Build
Didn't find the framework generated under universal framework but was found in both simulator and iPhone individually
@eladnava the script seems to not be working in XCode 10... It seems that the .framework does not exist under Release-universal folder. Is there any suggestion?
A few things:
- Thanks for posting this! It was really helpful
- This script can also be done completely using the command line's
archive
option onxcodebuild
; doing it as a post-archive in Xcode's schemes is pretty opaque because you don't get a sense of build progress like you do in the command line - You aren't
lipo
'ing the dSYM files into a universal dSYM but it's easy to add. Personally, I'd want to ensure all frameworks are distributed with dSYM files so crashes can be symbolicated
Here's an example script from where we're creating a universal iOS .framework
and .framework.dSYM
and bundling that with a macOS build into one zip file that can be used in a .podspec
Custom script (with dSYM lipo step as well): https://github.com/KeepSafe/ObjectiveRocks/blob/frameworks/build_universal_framework.sh
Podspec example: https://github.com/KeepSafe/ObjectiveRocksFramework/blob/master/ObjectiveRocksFramework.podspec
Another custom example: https://github.com/AudioKit/AudioKit/blob/master/Frameworks/build_frameworks.sh
@rob-keepsafe we ran this script from the project's folder? Also, does this produce a universal framework for both simulators and devices? Thank you!
@nick3389 seeing the same result with xcode 10.
The Script is not working in Xcode 10. Its opens OUTPUTFOLDER, But the Universal Framework is not generated in that folder. Anything need to change in the Script. @eladnava.
@eladnava @fer662 @BalaKarunakaran
It seems that in XCode 10, the folder under Release-universal path does not contain the {YOUR_FRAMEWORK}.framework file but its contents. I solved it by replacing this line under echo "Copying to output folder"
:
cp -R "${ARCHIVE_PRODUCTS_PATH}${INSTALL_PATH}/${FULL_PRODUCT_NAME}" "${UNIVERSAL_OUTPUTFOLDER}/"
with this one:
cp -R "${ARCHIVE_PRODUCTS_PATH}${INSTALL_PATH}" "${UNIVERSAL_OUTPUTFOLDER}/"
Please, confirm that it works for you too.
@nick3389 Great, Its working fine. 👏
This used to work in Xcode 8.3.3
It is not working in Xcode 9.4.1. The generated framework does not have the i386 & x86_64 files. Any update needed in script?
@nick3389 👍 thanks!
Hi all of you ,
Please can you help me .
Regarding third party framework add in podspec file .
@eladnava @fer662 @BalaKarunakaran
It seems that in XCode 10, the folder under Release-universal path does not contain the {YOUR_FRAMEWORK}.framework file but its contents. I solved it by replacing this line under
echo "Copying to output folder"
:
cp -R "${ARCHIVE_PRODUCTS_PATH}${INSTALL_PATH}/${FULL_PRODUCT_NAME}" "${UNIVERSAL_OUTPUTFOLDER}/"
with this one:
cp -R "${ARCHIVE_PRODUCTS_PATH}${INSTALL_PATH}" "${UNIVERSAL_OUTPUTFOLDER}/"
Please, confirm that it works for you too.
It's work for xcode 10
@eladnava @fer662 @BalaKarunakaran
It seems that in XCode 10, the folder under Release-universal path does not contain the {YOUR_FRAMEWORK}.framework file but its contents. I solved it by replacing this line under
echo "Copying to output folder"
:
cp -R "${ARCHIVE_PRODUCTS_PATH}${INSTALL_PATH}/${FULL_PRODUCT_NAME}" "${UNIVERSAL_OUTPUTFOLDER}/"
with this one:
cp -R "${ARCHIVE_PRODUCTS_PATH}${INSTALL_PATH}" "${UNIVERSAL_OUTPUTFOLDER}/"
Please, confirm that it works for you too.
I actually had to change a different line for this to work on Xcode 10 (Version 10.0 (10A255)). I added a missing target name. But kept the original line you mentioned changing. I just changed:
lipo -create -output "${UNIVERSAL_OUTPUTFOLDER}/${EXECUTABLE_PATH}" "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${EXECUTABLE_PATH}" "${ARCHIVE_PRODUCTS_PATH}${INSTALL_PATH}/${EXECUTABLE_PATH}"
for:
lipo -create -output "${UNIVERSAL_OUTPUTFOLDER}/${EXECUTABLE_PATH}" "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${TARGET_NAME}/${EXECUTABLE_PATH}" "${ARCHIVE_PRODUCTS_PATH}${INSTALL_PATH}/${EXECUTABLE_PATH}"
and it worked correctly.
If still not working check AlexM answer https://stackoverflow.com/a/52873099/4311935
Just want to notice that script will not work as is if you have different target and product names (e.g., target MyLib_iOS and product MyLib.framework). This can be fixed by using $PRODUCT_NAME
instead of $TARGET_NAME
(except of -scheme
parameter)
@eladnava thanks so much for creating this! This and your article at https://eladnava.com/publish-a-universal-binary-ios-framework-in-swift-using-cocoapods/ was amazing.
--
Here is the change I made to get it to working:
Make ${ARCHIVE_PRODUCTS_PATH}${INSTALL_PATH}/${FULL_PRODUCT_NAME}
to ${ARCHIVE_PRODUCTS_PATH}/${INSTALL_PATH}/
Why?
- products_path and install_path needed a file slash otherwise it doesn’t reference the correct file.
- Including the full_product_name causes ONLY the framework contents to be copied.
——
Also, as you’ll probably have to modify this Xcode script to fit your particular framework, here's three debugging tips:
-
The file at
/tmp/${PROJECT_NAME}_archive.log
is a lifesaver for trouble-shooting efforts. Every archive, you should be referencing this file. -
I highly suggest adding this echo statement to the file as this path is were 99% of your problems will occur:
echo "Framework location: ${ARCHIVE_PRODUCTS_PATH}/${INSTALL_PATH}/“
-
If the error log tries a cp command and "file can't be found" then it is most likely a path error.
For Xcode 11 change 'platform=iOS Simulator,name=iPhone 6' to 'platform=iOS Simulator,name=iPhone 8'
I had some issues in copy files. I resolved with this code:
exec > /tmp/${PROJECT_NAME}_archive.log 2>&1
UNIVERSAL_OUTPUTFOLDER=${BUILD_DIR}/${CONFIGURATION}-universal
if [ "true" == ${ALREADYINVOKED:-false} ]
then
echo "RECURSION: Detected, stopping"
else
export ALREADYINVOKED="true"
echo "Building for iPhoneSimulator"
xcodebuild -workspace "${WORKSPACE_PATH}" -scheme "${TARGET_NAME}" -configuration ${CONFIGURATION} -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 6' ONLY_ACTIVE_ARCH=NO ARCHS='i386 x86_64' BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" ENABLE_BITCODE=YES OTHER_CFLAGS="-fembed-bitcode" BITCODE_GENERATION_MODE=bitcode clean build
# make sure the output directory exists
mkdir -p "${UNIVERSAL_OUTPUTFOLDER}"
mkdir -p "${UNIVERSAL_OUTPUTFOLDER}/${TARGET_NAME}.framework"
# Step 1. Copy the framework structure (from iphoneos build) to the universal folder
echo "Copying to output folder"
cp -R "${ARCHIVE_PRODUCTS_PATH}${INSTALL_PATH}/${FULL_PRODUCT_NAME}" "${UNIVERSAL_OUTPUTFOLDER}/"
# Step 2. Copy Swift modules from iphonesimulator build (if it exists) to the copied framework directory
SIMULATOR_SWIFT_MODULES_DIR="${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${TARGET_NAME}.framework/Modules/${TARGET_NAME}.swiftmodule/."
if [ -d "${SIMULATOR_SWIFT_MODULES_DIR}" ]; then
cp -R "${SIMULATOR_SWIFT_MODULES_DIR}" "${UNIVERSAL_OUTPUTFOLDER}/${TARGET_NAME}.framework/Modules/${TARGET_NAME}.swiftmodule"
fi
# Step 3. Create universal binary file using lipo and place the combined executable in the copied framework directory
echo "Combining executables"
lipo -create -output "${UNIVERSAL_OUTPUTFOLDER}/${EXECUTABLE_PATH}" "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${EXECUTABLE_PATH}" "${ARCHIVE_PRODUCTS_PATH}${INSTALL_PATH}/${EXECUTABLE_PATH}"
# Step 4. Create universal binaries for embedded frameworks
#for SUB_FRAMEWORK in $( ls "${UNIVERSAL_OUTPUTFOLDER}/${TARGET_NAME}.framework/Frameworks" ); do
#BINARY_NAME="${SUB_FRAMEWORK%.*}"
#lipo -create -output "${UNIVERSAL_OUTPUTFOLDER}/${TARGET_NAME}.framework/Frameworks/${SUB_FRAMEWORK}/${BINARY_NAME}" "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${SUB_FRAMEWORK}/${BINARY_NAME}" "${ARCHIVE_PRODUCTS_PATH}${INSTALL_PATH}/${TARGET_NAME}.framework/Frameworks/${SUB_FRAMEWORK}/${BINARY_NAME}"
#done
# Step 5. Convenience step to copy the framework to the project's directory
echo "Copying to project dir"
echo ${FULL_PRODUCT_NAME}
cp -Rf "${UNIVERSAL_OUTPUTFOLDER}/${FULL_PRODUCT_NAME}" "${PROJECT_DIR}"
open "${PROJECT_DIR}"
fi
@caiovncius environment variables are not working since XCode 11.4. I just try to echo "${TARGET_NAME}" but the result is blank. Any thought about this? It only works if I embed it as a post archive script.
in xCode12 , the result is blank
in xCode12 , the result is blank - SAME HERE : )) what the hell : ))
Hello i am currently learning about framework, and faced the issue with blank result.
I am using Xcode 12
Comment out the first line if your results are blank. It redirects the output to a file.
Hey everyone,
When I was using this script for the first time few months ago it was working. But right now it doesn't. I finally found the issue. This script tries to find iPhone 6 as simulator. If your XCode doesn't have iPhone simulator, the framework appears after few minutes and binary doesn't have simulator architectures. In that case change change simulator name to available one. in my case I change it to iPhone 8
xcodebuild -workspace "${WORKSPACE_PATH}" -scheme "${TARGET_NAME}" -configuration ${CONFIGURATION} -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 8' ONLY_ACTIVE_ARCH=NO ARCHS='i386 x86_64' BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" ENABLE_BITCODE=YES OTHER_CFLAGS="-fembed-bitcode" BITCODE_GENERATION_MODE=bitcode clean build