使用Xcode和SDK 4 +构buildfat静态库(设备+模拟器)

编辑:对于那些想用低级英语重新编写我的问题的国防部长来说,不太清楚:走开并写下自己的问题。 离开我一个人 – 谢谢!

看起来,我们可以在理论上构build一个包含模拟器和iPhone和iPad的静态库。

但是,苹果没有这方面的文档,我可以find,而Xcode的默认模板不configuration这样做。

我正在寻找一种简单,便携,可重复使用的技术,可以在Xcode中完成。

一些历史:

  • 在2008年,我们曾经能够制作包含sim和device的单个静态库。 苹果禁用了。
  • 在整个2009年,我们做了一对静态库 – 一个用于模拟,一个用于设备。 苹果现在也禁用了。

参考文献:

  1. 这是一个好主意,这是一个很好的方法,但它不起作用: http : //www.drobnik.com/touch/2010/04/universal-static-libraries/

    • 他的脚本中有一些错误,意味着它只能在他的机器上运行 – 他应该使用BUILT_PRODUCTS_DIR和/或BUILD_DIR而不是“猜测”它们)
    • 苹果公司最新的Xcode可以阻止你完成他所做的事情 – 由于Xcode处理目标的(logging)改变,这种做法根本行不通,
  2. 另一个SO提问者询问如何在没有xcode的情况下做到这一点,以及关注于arm6 vs arm7部分的回应 – 但忽略了i386部分: 我如何为armv6,armv7和i386编译一个静态库(fat)

    • 由于苹果的最新变化,模拟器部分是不一样的arm6 / arm7的差异 – 这是一个不同的问题,见上文)

备择scheme:

轻松复制/粘贴最新版本 (但安装说明可能会更改 – 请参阅下文!)

Karl的库需要更多的努力来设置,但更好的长期解决scheme(它将您的库转换为一个框架)。

使用它,然后调整它以添加对档案构build的支持 – cf @ Frederik下面对他正在使用的更改进行评论,使其与存档模式很好地配合使用。


最近的变化:1.增加了对iOS 10.x的支持(同时保持对老版本的支持)

  1. 关于如何将这个脚本与一个embedded到另一个项目中的脚本一起使用的信息(尽pipe我强烈build议不要那么做 – 如果你在Xcode中embedded项目,苹果在Xcode中会有一些显示停止的bug 3.x到Xcode 4.6.x)

  2. 奖金脚本,让你自动包括捆绑(即包括PNG文件,PLIST文件等从您的图书馆!) – 见下文(滚动到底部)

  3. 现在支持iPhone5(使用苹果公司的解决scheme来解决lipo中的错误)。 注意:安装说明已经改变了(我可以通过改变脚本来简化这个过程,但是现在不想冒险)

  4. “复制标题”部分现在考虑公共标题的位置的构build设置(由Frederik Wallner提供)

  5. 感谢Doug Dickinson,增加了SYMROOT的明确设置(也许还需要设置OBJROOT?)


脚本(这是你必须复制/粘贴)

有关使用/安装说明,请参阅下文

########################################## # # cf 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 

安装说明

  1. 创build一个静态的lib项目
  2. select目标
  3. 在“Build Settings”选项卡中,将“Build Active Architecture Only”设置为“NO”(对于所有项目)
  4. 在“构build阶段”选项卡中,select“添加…新build阶段…新build运行脚本构build阶段”
  5. 复制/粘贴脚本(上面)到框中

…奖金可选用法:

  1. 可选:如果您的资料库中有标题,请将它们添加到“复制标题”阶段
  2. 可选:…并将其从“项目”部分拖放到“公共”部分
  3. 可选:…并且它们将在您每次构build应用程序时自动导出到“debug-universal”目录的子目录中(它们将位于usr / local / include中)
  4. 可选:注意:如果您尝试将您的项目拖放到另一个Xcode项目中,则会在Xcode 4中公开一个错误,如果拖放项目中有公共标题,则无法创build.IPA文件。 解决方法:不要embeddedxcode项目(在苹果的代码中太多的错误!)

如果你找不到输出文件,这是一个解决方法:

  1. 将以下代码添加到脚本的最后(由Frederik Wallner提供):打开“$ {CREATING_UNIVERSAL_DIR}”

  2. 苹果删除200行后的所有输出。 select你的目标,并在运行脚本阶段,你必须解开:“在生成日志中显示环境variables”

  3. 如果您使用XCode4的自定义“构build输出”目录,则XCode会将所有“意外”文件放在错误的位置。

    1. build立这个项目
    2. 点击Xcode4左上angular的右侧最后一个图标。
    3. select最上面的项目(这是你的“最新版本”,苹果应该自动select它,但他们没有想到)
    4. 在主窗口中,滚动到底部。 最后一行应该是:lipo:用于当前configuration(Debug)创build输出文件:/Users/blah/Library/Developer/Xcode/DerivedData/AppName-ashwnbutvodmoleijzlncudsekyf/Build/Products/Debug-universal/libTargetName.a

    …这是您的通用版本的位置。


如何在项目中包含“非源代码”文件(PNG,PLIST,XML等)

  1. 做上面的一切,检查它的工作原理
  2. 创build一个新的运行脚本阶段,在第一个之后(复制/粘贴下面的代码)
  3. 在Xcode中创build一个新的Target,types为“bundle”
  4. 在你的MAIN PROJECT中,在“Build Phases”中,将新的bundle添加为“依赖”(顶部,点击加号button,滚动到底部,在你的Products中find“.bundle”文件)
  5. 在新的BUNDLE TARGET中,在“Build Phases”中,添加一个“Copy Bundle Resources”部分,并将所有的PNG文件等拖放到其中

脚本自动复制到您的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}" 

我制作了一个XCode 4项目模板 ,可以让您像制作常规库一样轻松制作通用框架。

我花了好几个小时试图构build一个可以在armv7,armv7s和模拟器上工作的胖静态库。 终于find了解决办法 。

要点是分别build立两个库(一个用于设备,另一个用于模拟器),重新命名它们以区分对方,然后将它们创build为一个库。

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

我试了一下,它的工作原理!

有一个命令行实用程序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脚本),但容易理解。 我configuration了一个只运行这个脚本的新目标。 该脚本是专为命令行,但没有在testing:)

核心概念是xcodebuildlipo

我在Xcode UI中尝试了许多configuration,但没有任何工作。 由于这是一种批处理,因此命令行devise更为合适,所以苹果逐渐从Xcode中删除了批量生成function。 所以我不指望他们将来会提供基于UI的批量构buildfunction。

我需要一个用于JsonKit的胖静态库,所以在Xcode中创build了一个静态lib项目,然后在项目目录中运行这个bash脚本。 只要您将xcode项目configuration为“Build active configuration only”closures,您应该将所有体系结构放在一个lib中。

 #!/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构buildfatlib时遇到了一个问题,因为脚本中的正则expression式只有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的静态框架模板一样。

我发现构build静态框架(而不是普通的静态库)导致LLVM随机崩溃,由于明显的链接器错误 – 所以,我猜静态库仍然是有用的!

做得好! 我砍了一些类似的东西,但必须分开运行。 让它只是构build过程的一部分使得它变得更简单。

一个注意事项。 我注意到它不会复制任何标记为公共的包含文件。 我已经适应了我的脚本给你的,它运作得很好。 将以下内容粘贴到脚本的末尾。

 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计划项目中类似的脚本。)

基本上,它运行./configure并进行三次(对于i386,armv7和armv7s),并将每个生成的库合并到一个fat lib中。