1. ホーム
  2. iphone

[解決済み】XcodeとSDK 4+を使用してファットスタティックライブラリ(デバイス+シミュレータ)をビルドする。

2022-04-01 22:49:35

質問

理論的には、シミュレータとiPhone/iPadの両方を含む単一の静的ライブラリを構築することができるようです。

しかし、Appleにはこれに関する文書がなく、Xcodeのデフォルトのテンプレートもこれを行うように設定されていません。

Xcodeの中でできる、シンプルでポータブルで再利用可能なテクニックを探しています。

いくつかの歴史

  • 2008年には、SIMとデバイスの両方を含む単一のスタティックリブを作成することができました。Appleはそれを無効にしました。
  • 2009年を通して、私たちはSIM用とデバイス用のスタティック・ライブラリのペアを作りました。Appleは現在これも無効にしています。

参考文献

  1. これは素晴らしいアイデアで、優れたアプローチですが、うまくいきません。 http://www.drobnik.com/touch/2010/04/universal-static-libraries/

    • 彼のスクリプトにはバグがあり、彼のマシンでしか動作しません - BUILT_PRODUCTS_DIR と BUILD_DIR を "guesstimating" の代わりに使用する必要があります)
    • Appleの最新のXcodeでは、彼がしたようなことはできません。Xcodeがターゲットを処理する方法が変更されたため、単純に動作しません。
  2. 別のSOの質問者は、xcodeを使わずにそれを行う方法を尋ね、arm6とarm7の部分に焦点を当てた回答がありましたが、i386の部分は無視されました。 armv6、armv7、i386用のスタティックライブラリ(fat)のコンパイル方法

    • Appleの最新の変更以来、シミュレータの部分はもはやarm6/arm7の違いと同じではありません - それは別の問題です、上記を参照してください)

解決方法は?

ALTERNATIVES(代替案)。

最新版の簡単コピー&ペースト (ただし、インストール方法は変更される可能性があります - 下記参照!)

カールのライブラリ はセットアップに多くの労力を要しますが、長期的な解決策としてはより優れています (ライブラリをフレームワークに変換します)。

これを使用し、アーカイブビルドのサポートを追加するために微調整を行います。 - c.f. @Frederik のコメントで、彼が Archive モードでうまく動作させるために行っている変更について、以下に紹介します。


最近の変更点 1. iOS 10.xのサポートを追加(古いプラットフォームへのサポートは維持)。

  1. このスクリプトを他のプロジェクトに埋め込んで使用する方法についての情報(ただし、絶対にそうしないことを強くお勧めします - Appleは、Xcode 3.xからXcode 4.6.xまで、プロジェクトを互いに埋め込むとXcodeでいくつかのショーストッパーのバグがあります)。

  2. バンドル(PNGファイルやPLISTファイルなどをライブラリから自動インクルードするためのボーナススクリプト) - 下記を参照(一番下までスクロールしてください)

  3. iPhone5をサポートしました(lipoのバグに対するAppleのワークアラウンドを使用)。注:インストール方法が変更されました(将来的にはスクリプトを変更することで簡略化できると思いますが、今は危険を冒したくありません)。

  4. コピーヘッダ"セクションは、パブリックヘッダの場所に対するビルド設定を尊重するようになりました。

  5. SYMROOTの明示的な設定を追加しました(OBJROOTも設定する必要があるかも?


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


インストール方法

  1. 静的 lib プロジェクトを作成する
  2. ターゲットの選択
  3. ビルド設定]タブで、[アクティブアーキテクチャのみビルド]を[NO]に設定します([NO]の場合 すべて 項目)
  4. Build Phases"]タブで、[Add ...]を選択します。新しいビルドフェーズ ... 新しい実行スクリプトビルドフェーズ(New Run Script Build Phase)]を選択します。
  5. スクリプト(上記)をコピーしてボックスに貼り付けます。

...BONUS OPTIONALの使い方。

  1. OPTIONAL: ライブラリにヘッダーがある場合は、"Copy Headers"フェーズに追加してください。
  2. オプション:...そして、quot;Project" セクションから "Public" セクションにドラッグ/ドロップしてください。
  3. オプション: ...そして、それらはアプリをビルドするたびに自動的にエクスポートされ、"debug-universal" ディレクトリのサブディレクトリに入ります(それらは usr/local/include に入ります)。
  4. オプション: 注意: もし また これはXcode 4のバグで、ドラッグ&ドロップしたプロジェクトにPublic Headersがある場合、.IPAファイルを作成することができないのです。回避策:Xcodeプロジェクトを埋め込まないこと(Appleのコードにはバグが多すぎる!)。

出力ファイルが見つからない場合の回避策です。

  1. 次のコードをスクリプトの一番最後に追加してください(Frederik Wallner氏提供): open "${CREATING_UNIVERSAL_DIR}".CREATING_UNIVERSAL_DIR}".CREATING_UNIVERSAL_DIR}"

  2. Appleは200行以降の出力をすべて削除します。ターゲットを選択し、スクリプトの実行フェーズで、"ビルドログに環境変数を表示"のチェックを外す必要があります。

  3. XCode4でカスタムビルド出力ディレクトリを使用している場合、XCodeはすべてのquot;unexpected"ファイルを間違った場所に配置します。

    1. プロジェクトをビルドします。
    2. Xcode4の左上の領域で、右側の最後のアイコンをクリックします。
    3. 一番上の項目を選択します(これはあなたの"最新のビルド"です。Appleはこれを自動選択するはずですが、そのようなことは考えていませんでした)
    4. をクリックし、一番下までスクロールします。一番最後の行は次のように書かれているはずです。/Users/blah/Library/Developer/Xcode/DerivedData/AppName-ashwnbutvodmoleijzlncudsekyf/Build/Products/Debug-universal/libTargetName.a を作成します。

    ...これがあなたのUniversal Buildの場所です。


ソースコード以外のファイル(PNG、PLIST、XMLなど)をプロジェクトにインクルードする方法。

  1. 上記をすべて実行し、動作を確認する
  2. 最初のフェーズの後に来る新しいRun Scriptフェーズを作成します(以下のコードをコピー/ペーストします)
  3. Xcode で新しい Target を作成します。
  4. MAIN PROJECT の "Build Phases" で、新しいバンドルを "depends on" として追加します(上のセクションでプラスボタンを押し、下までスクロールして Products の中の ".bundle" ファイルを探します)。
  5. NEW 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}"