[英]Build fat static library (device + simulator) using Xcode and SDK 4+
從理論上講,我們似乎可以構建一個包含模擬器、iPhone 和 iPad 的靜態庫。
但是,Apple 沒有我可以找到的關於此的文檔,並且 Xcode 的默認模板未配置為執行此操作。
我正在尋找一種可以在 Xcode 中完成的簡單、便攜、可重用的技術。
一些歷史:
參考:
這是一個好主意,它是一個很好的方法,但它不起作用: http : //www.drobnik.com/touch/2010/04/universal-static-libraries/
另一個 SO 提問者詢問如何在沒有 xcode 的情況下做到這一點,並且回答側重於 arm6 與 arm7 部分 - 但忽略了 i386 部分: 如何為 armv6、armv7 和 i386 編譯靜態庫(胖)
備擇方案:
輕松復制/粘貼最新版本(但安裝說明可能會更改 - 見下文!)
Karl 的庫需要更多的努力來設置,但更好的長期解決方案(它將您的庫轉換為框架)。
使用它,然后調整它以添加對存檔構建的支持- 參見下面@Frederik 對他使用的更改的評論,以使其在存檔模式下可以很好地工作。
最近的變化: 1. 添加了對 iOS 10.x 的支持(同時保持對舊平台的支持)
關於如何將這個腳本與嵌入在另一個項目中的項目一起使用的信息(盡管我強烈建議不要這樣做,但如果您將項目從 Xcode 相互嵌入,Apple 在 Xcode 中有幾個顯示停止錯誤3.x 到 Xcode 4.6.x)
獎勵腳本讓您自動包含捆綁包(即包含庫中的 PNG 文件、PLIST 文件等!) - 見下文(滾動到底部)
現在支持 iPhone5(使用 Apple 解決 lipo 中的錯誤的方法)。 注意:安裝說明已更改(我可能可以通過將來更改腳本來簡化此操作,但現在不想冒險)
“復制標題”部分現在尊重公共標題位置的構建設置(由 Frederik Wallner 提供)
添加了 SYMROOT 的顯式設置(也許還需要設置 OBJROOT?),感謝 Doug Dickinson
腳本(這是您必須復制/粘貼的內容)
有關使用/安裝說明,請參見下文
##########################################
#
# c.f. https://stackoverflow.com/questions/3520977/build-fat-static-library-device-simulator-using-xcode-and-sdk-4
#
# Version 2.82
#
# Latest Change:
# - MORE tweaks to get the iOS 10+ and 9- working
# - Support iOS 10+
# - Corrected typo for iOS 1-10+ (thanks @stuikomma)
#
# Purpose:
# Automatically create a Universal static library for iPhone + iPad + iPhone Simulator from within XCode
#
# Author: Adam Martin - http://twitter.com/redglassesapps
# Based on: original script from Eonil (main changes: Eonil's script WILL NOT WORK in Xcode GUI - it WILL CRASH YOUR COMPUTER)
#
set -e
set -o pipefail
#################[ Tests: helps workaround any future bugs in Xcode ]########
#
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
#####################[ part 1 ]##################
# First, work out the BASESDK version number (NB: Apple ought to report this, but they hide it)
# (incidental: searching for substrings in sh is a nightmare! Sob)
SDK_VERSION=$(echo ${SDK_NAME} | grep -o '\d\{1,2\}\.\d\{1,2\}$')
# Next, work out if we're in SIM or DEVICE
if [ ${PLATFORM_NAME} = "iphonesimulator" ]
then
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}"
#
#####################[ end of part 1 ]##################
#####################[ part 2 ]##################
#
# IF this is the original invocation, invoke WHATEVER other builds are required
#
# Xcode is already building ONE target...
#
# ...but this is a LIBRARY, so Apple is wrong to set it to build just one.
# ...we need to build ALL targets
# ...we MUST NOT re-build the target that is ALREADY being built: Xcode WILL CRASH YOUR COMPUTER if you try this (infinite recursion!)
#
#
# So: build ONLY the missing platforms/configurations.
if [ "true" == ${ALREADYINVOKED:-false} ]
then
echo "RECURSION: I am NOT the root invocation, so I'm NOT going to recurse"
else
# CRITICAL:
# Prevent infinite recursion (Xcode sucks)
export ALREADYINVOKED="true"
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"
#Merge all platform binaries as a fat binary for each configurations.
# Calculate where the (multiple) built files are coming from:
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}"
# ... remove the products of previous runs of this script
# NB: this directory is ONLY created by this script - it should be safe to delete!
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}"
#########
#
# Added: StackOverflow suggestion to also copy "include" files
# (untested, but should work OK)
#
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 "User Search Headers" 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}"
# * needs to be outside the double quotes?
cp -r "${CURRENTCONFIG_DEVICE_DIR}${PUBLIC_HEADERS_FOLDER_PATH}"* "${CREATING_UNIVERSAL_DIR}${PUBLIC_HEADERS_FOLDER_PATH}"
fi
fi
安裝說明
...獎金可選用法:
如果找不到輸出文件,這里有一個解決方法:
將以下代碼添加到腳本的最后(由 Frederik Wallner 提供):打開“${CREATING_UNIVERSAL_DIR}”
Apple 刪除 200 行后的所有輸出。 選擇您的目標,在運行腳本階段,您必須取消勾選:“在構建日志中顯示環境變量”
如果您使用 XCode4 的自定義“構建輸出”目錄,則 XCode 會將所有“意外”文件放在錯誤的位置。
...那是您的通用構建的位置。
如何在項目中包含“非源代碼”文件(PNG、PLIST、XML 等)
將構建的包自動復制到與 FAT 靜態庫相同的文件夾中的腳本:
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}"
我花了很多時間試圖構建一個可以在 armv7、armv7s 和模擬器上運行的胖靜態庫。 終於找到了解決辦法。
要點是分別構建兩個庫(一個用於設備,一個用於模擬器),重命名它們以區分彼此,然后 lipo -create 它們到一個庫中。
lipo -create libPhone.a libSimulator.a -output libUniversal.a
我試過了,它有效!
我制作了一個XCode 4 項目模板,讓您可以像制作常規庫一樣輕松制作通用框架。
有一個命令行實用程序xcodebuild
,您可以在 xcode 中運行 shell 命令。 因此,如果您不介意使用自定義腳本,此腳本可能會對您有所幫助。
#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}"
也許看起來效率低下(我不擅長 shell 腳本),但很容易理解。 我配置了一個僅運行此腳本的新目標。 該腳本是為命令行設計的,但未在 :) 中進行測試
核心概念是xcodebuild
和lipo
。
我在 Xcode UI 中嘗試了許多配置,但沒有任何效果。 因為這是一種批處理,所以命令行設計更適合,所以蘋果逐漸從 Xcode 中刪除了批處理構建功能。 所以我不希望他們將來提供基於 UI 的批量構建功能。
我需要一個用於 JsonKit 的靜態庫,所以在 Xcode 中創建了一個靜態庫項目,然后在項目目錄中運行這個 bash 腳本。 只要您在關閉“僅構建活動配置”的情況下配置了 xcode 項目,您就應該在一個庫中獲得所有架構。
#!/bin/bash
xcodebuild -sdk iphoneos
xcodebuild -sdk iphonesimulator
lipo -create -output libJsonKit.a build/Release-iphoneos/libJsonKit.a build/Release-iphonesimulator/libJsonKit.a
IOS 10 更新:
我在用 iphoneos10.0 構建 fatlib 時遇到了問題,因為腳本中的正則表達式只需要 9.x 及更低版本,而對於 ios 10.0 則返回 0.0
要解決這個問題,只需更換
SDK_VERSION=$(echo ${SDK_NAME} | grep -o '.\{3\}$')
和
SDK_VERSION=$(echo ${SDK_NAME} | grep -o '[\\.0-9]\{3,4\}$')
我已經把它變成了一個Xcode 4 模板,與 Karl 的靜態框架模板一樣。
我發現由於明顯的鏈接器錯誤,構建靜態框架(而不是普通靜態庫)會導致 LLVM 隨機崩潰 - 所以,我猜靜態庫仍然有用!
XCode 12 更新:
如果您在沒有-arch
參數的情況下運行xcodebuild
,XCode 12 將構建默認架構為“arm64 x86_64”的模擬器庫。
然后運行xcrun -sdk iphoneos lipo -create -output
會沖突,因為模擬器和設備庫中存在arm64
架構。
我從 Adam git 中 fork 腳本並修復它。
很好! 我將類似的東西拼湊在一起,但不得不單獨運行。 將它作為構建過程的一部分使它變得更加簡單。
注意事項之一。 我注意到它不會復制您標記為公開的任何包含文件。 我已經將我的腳本中的內容改編為您的腳本,並且效果很好。 將以下內容粘貼到腳本的末尾。
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。 (它基於 Gambit Scheme 項目中的類似腳本。)
基本上,它運行 ./configure 並 make 三次(對於 i386、armv7 和 armv7s),並將每個生成的庫組合到一個胖庫中。
從 21 年 10 月 22 日起,我采用了最新 Xcode 的公認答案腳本,並添加了對 .framework 和新 .XCFrameworks 的支持,以及更完整的 Objective-C 標頭復制。
https://github.com/Talking-App-Kit/Useful/blob/main/BuildFatPackage.sh
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.