从理论上讲,我们似乎可以构建一个包含模拟器、iPhone和iPad的单一静态库。

然而,苹果没有我能找到的关于这方面的文档,Xcode的默认模板也没有配置这样做。

我正在寻找一个简单的,可移植的,可重用的技术,可以在Xcode中完成。

一些历史:

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

引用:

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) 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)


有一个命令行实用程序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的批量构建功能。


选择:

轻松复制/粘贴最新版本(但安装说明可能会改变-见下文!)

Karl的库需要更多的精力来设置,但却是更好的长期解决方案(它将你的库转换为一个框架)。

使用这个,然后调整它,以增加对存档构建的支持- c.f. @Frederik的评论下面的变化,他正在使用使存档模式工作得很好。


最近的变化: 1. 增加了对iOS 10的支持。X(同时保持对旧平台的支持)

Info on how to use this script with a project-embedded-in-another-project (although I highly recommend NOT doing that, ever - Apple has a couple of show-stopper bugs in Xcode if you embed projects inside each other, from Xcode 3.x through to Xcode 4.6.x) Bonus script to let you auto-include Bundles (i.e. include PNG files, PLIST files etc from your library!) - see below (scroll to bottom) now supports iPhone5 (using Apple's workaround to the bugs in lipo). NOTE: the install instructions have changed (I can probably simplify this by changing the script in future, but don't want to risk it now) "copy headers" section now respects the build setting for the location of the public headers (courtesy of Frederik Wallner) Added explicit setting of SYMROOT (maybe need OBJROOT to be set too?), thanks to Doug Dickinson


SCRIPT(这是你需要复制/粘贴的东西)

有关使用/安装说明,请参见下面

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

安装说明

创建一个静态库项目 选择目标 在“Build Settings”选项卡中,将“Build Active Architecture Only”设置为“NO”(适用于所有项目) 在“Build Phases”选项卡中,选择“Add…”新的构建阶段…新的运行脚本构建阶段” 复制/粘贴上面的脚本到方框中

...奖金可选用法:

OPTIONAL: if you have headers in your library, add them to the "Copy Headers" phase OPTIONAL: ...and drag/drop them from the "Project" section to the "Public" section OPTIONAL: ...and they will AUTOMATICALLY be exported every time you build the app, into a sub-directory of the "debug-universal" directory (they will be in usr/local/include) OPTIONAL: NOTE: if you also try to drag/drop your project into another Xcode project, this exposes a bug in Xcode 4, where it cannot create an .IPA file if you have Public Headers in your drag/dropped project. The workaround: dont' embed xcode projects (too many bugs in Apple's code!)

如果你找不到输出文件,这里有一个变通办法:

Add the following code to the very end of the script (courtesy of Frederik Wallner): open "${CREATING_UNIVERSAL_DIR}" Apple deletes all output after 200 lines. Select your Target, and in the Run Script Phase, you MUST untick: "Show environment variables in build log" if you're using a custom "build output" directory for XCode4, then XCode puts all your "unexpected" files in the wrong place. Build the project Click on the last icon on the right, in the top left area of Xcode4. Select the top item (this is your "most recent build". Apple should auto-select it, but they didn't think of that) in the main window, scroll to bottom. The very last line should read: lipo: for current configuration (Debug) creating output file: /Users/blah/Library/Developer/Xcode/DerivedData/AppName-ashwnbutvodmoleijzlncudsekyf/Build/Products/Debug-universal/libTargetName.a ...that is the location of your Universal Build.


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

以上一切都做了,检查一下是否正常 在第一个阶段之后创建一个新的运行脚本阶段(复制/粘贴下面的代码) 在Xcode中创建一个新的目标,类型为bundle 在你的MAIN PROJECT中,在“Build Phases”中,添加新的bundle作为它“依赖”的东西(顶部部分,点击加号按钮,滚动到底部,找到“”)。bundle"文件在你的产品) 在你的NEW BUNDLE TARGET中,在“Build Phases”中,添加一个“Copy BUNDLE Resources”部分,并拖放所有的PNG文件等

脚本自动复制构建的包(s)到相同的文件夹作为你的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}"

伟大的工作!我拼凑了一些类似的东西,但必须单独运行。让它成为构建过程的一部分会使构建过程简单得多。

有一点值得注意。我注意到它不会复制任何你标记为公共的包含文件。我把我剧本里的内容改编成了你的,效果相当不错。将以下内容粘贴到脚本的末尾。

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 4项目模板,它可以让你像创建常规库一样轻松地创建一个通用框架。


我把它做成了一个Xcode 4模板,与Karl的静态框架模板相同。

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


我花了很多时间试图构建一个可以在armv7、armv7s和模拟器上工作的大型静态库。终于找到了解决办法。

要点是分别构建两个库(一个用于设备,另一个用于模拟器),重命名它们以彼此区分,然后将它们创建到一个库中。

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

我试过了,很管用!


实际上,我只是为了这个目的写了自己的脚本。它不使用Xcode。 (它基于Gambit Scheme项目中的一个类似脚本。)

基本上,它运行。/configure和make三次(适用于i386、armv7和armv7s),并将每个生成的库合并到一个胖库中。


我需要一个脂肪静态库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 12更新:

如果你运行xcodebuild而不带-arch参数,XCode 12将默认构建架构为“arm64 x86_64”的模拟器库。

然后运行xrun -sdk iphoneos lipo -create -output会有冲突,因为模拟器中有arm64架构,也有设备库。

我叉子脚本从亚当git和修复它。