Xcode と SDK 4+ を使用して fat 静的ライブラリ (デバイス + シミュレータ) を構築する 質問する

Xcode と SDK 4+ を使用して fat 静的ライブラリ (デバイス + シミュレータ) を構築する 質問する

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

ただし、私が見つけた限りでは、Apple にはこれに関するドキュメントはなく、Xcode のデフォルトのテンプレートはこれを実行するように構成されていません。

Xcode 内で実行できるシンプルで移植性があり、再利用可能な手法を探しています。

歴史:

  • 2008 年には、SIM とデバイスの両方を含む単一の静的ライブラリを作成できました。Apple はそれを無効にしました。
  • 2009 年を通して、私たちは静的ライブラリのペアを作成しました。1 つは SIM 用、もう 1 つはデバイス用です。Apple は現在、それも無効にしています。

参考文献:

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

    • 彼のスクリプトにはバグがいくつかあり、彼のマシンでしか動作しません。推測するのではなく、BUILT_PRODUCTS_DIR や BUILD_DIR を使用する必要があります。
    • Apple の最新の Xcode では、彼が行ったようなことはできません。Xcode がターゲットを処理する方法が変更されたため (文書化されています)、単純に機能しません。
  2. 別の SO 質問者は、Xcode を使わずにこれを行う方法を尋ねましたが、回答は arm6 と arm7 の部分に焦点を当てていましたが、i386 の部分は無視されていました。armv6、armv7、i386 用の静的ライブラリ (fat) をコンパイルするにはどうすればいいですか?

    • Apple の最新の変更により、シミュレータ部分はもはや arm6/arm7 の違いと同じではなくなりました。これは別の問題です。上記を参照してください)

ベストアンサー1

代替案:

最新バージョンを簡単にコピー/貼り付け(ただし、インストール手順は変更される可能性があります - 以下を参照してください!)

カールの図書館セットアップには多くの労力がかかりますが、長期的にははるかに優れたソリューションです (ライブラリをフレームワークに変換します)。

これを使用して、アーカイブビルドのサポートを追加するように調整します- アーカイブ モードでこれをうまく動作させるために使用している変更については、以下の @Frederik のコメントを参照してください。


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

  1. このスクリプトを別のプロジェクトに埋め込まれたプロジェクトで使用する方法に関する情報 (ただし、絶対にこれを行わないことを強くお勧めします。Xcode 3.x から Xcode 4.6.x まで、プロジェクトを相互に埋め込むと、Apple の Xcode に致命的なバグがいくつかあります)

  2. バンドルを自動的に含める (つまり、ライブラリから PNG ファイル、PLIST ファイルなどを含める) ボーナス スクリプト - 以下を参照してください (一番下までスクロール)

  3. iPhone5 をサポートするようになりました (lipo のバグに対する Apple の回避策を使用)。注: インストール手順が変更されました (将来スクリプトを変更することでこれを簡素化できる可能性がありますが、今はリスクを冒したくありません)

  4. 「ヘッダーをコピー」セクションでは、パブリック ヘッダーの場所のビルド設定が考慮されるようになりました (Frederik Wallner 提供)

  5. 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

インストール手順

  1. 静的ライブラリプロジェクトを作成する
  2. ターゲットを選択
  3. 「ビルド設定」タブで、「アクティブアーキテクチャのみをビルド」を「いいえ」に設定します(すべての項目に対して)
  4. 「ビルドフェーズ」タブで、「追加...新しいビルドフェーズ...新しい実行スクリプトビルドフェーズ」を選択します。
  5. 上記のスクリプトをボックスにコピー/貼り付けます

...ボーナスオプションの使用法:

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

出力ファイルが見つからない場合は、次の回避策を試してください。

  1. スクリプトの最後に次のコードを追加します (Frederik Wallner 提供): open "${CREATING_UNIVERSAL_DIR}"

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

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

    1. プロジェクトを構築する
    2. Xcode4 の左上領域にある右側の最後のアイコンをクリックします。
    3. 一番上の項目を選択します(これは「最新のビルド」です。Apple はこれを自動的に選択するはずですが、その点については考慮していませんでした)
    4. メイン ウィンドウで、一番下までスクロールします。最後の行には、次の内容が表示されます: lipo: 現在の構成 (デバッグ) 出力ファイルを作成しています: /Users/blah/Library/Developer/Xcode/DerivedData/AppName-ashwnbutvodmoleijzlncudsekyf/Build/Products/Debug-universal/libTargetName.a

    ...それがユニバーサル ビルドの場所です。


プロジェクトに「非ソースコード」ファイルを含める方法 (PNG、PLIST、XML など)

  1. 上記のすべてを実行し、動作を確認します
  2. 最初のフェーズの後に来る新しいスクリプト実行フェーズを作成します(以下のコードをコピー/貼り付けます)
  3. Xcodeで「bundle」タイプの新しいターゲットを作成します。
  4. メインプロジェクトの「ビルドフェーズ」で、新しいバンドルを「依存する」ものとして追加します(上部のセクションでプラスボタンを押し、一番下までスクロールして、製品内の「.bundle」ファイルを見つけます)。
  5. 新しいバンドルターゲットの「ビルドフェーズ」に「バンドルリソースのコピー」セクションを追加し、そこにすべての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}"

おすすめ記事