jarinosuke blog

about software engineering, mostly about iOS

Xcode 5 で作る Universal Static Library

なぜ Static Library なのか

全てのソースコードGitHub にアップロードし、Cocoapods に podspec をマージしてもらって、

他のデベロッパー達とのコラボレーションを楽しむのが現在の iOS 界隈の Social Coding の主流になっているのかなと思います。

しかし、以下のような要因などによっては上記のようなアクションを取り辛い場合があると思います。

  • コード自体は自由に使ってもらいたいが、実装ファイルは見られたくない

  • ファイルが山ほどあるので、それらを使いやすくパッケージ化したうえで共有したい

そんなときに有効な手段の一つとなるのが Static Library です。

Static Library は指定した header file とコンパイル済みの archive file (.a 拡張子)がインターフェースとなります。

そのため、実装部分が利用者には見えなくなり隠蔽できるうえ、利用者にも扱いやすくなります。

プロジェクトの作成

では実際に Xcode 5 を用いて Static Library を作成してみましょう。

まずは Static Library 作成用のプロジェクトを新規に作成します。

Xcode を起動し、メニューから File -> New… -> Project を選択します。

左ペインの iOS セクションから Framework & Library を選択し、以下の Cocoa Touch Static Library を選択します。

f:id:jarinosuke0808:20140208184830p:plain

適当な Product Name を付けたら完成です、早速ビルドしてみましょう。

Product フォルダに libProject.a という archive file ができていると思います。

これを選択状態にし、 Show in Finder を選択して実際にどんなファイルが生成されているかを確認してみましょう。

Product フォルダ内にある lib.a という archive file が生成されているので、

それを選択して右クリックメニューから Show in Finder です。

上記でも説明した、header file と archive file の2種類が生成されています。

手順としてはこれだけです。

あとは生成された Static Library を、利用したいプロジェクトに追加し適切にリンクするだけです。

Universal Library

実は上記の手順だけだと、一つ問題があります。

このままだとビルドした時に選択している Architecture 用にコンパイルされた Static Library しか生成されないからです。

例えば iOS Simulator を選択してビルドした Static Library は iPhone など実機のArchitecture とは異なるため使用できません。

Xcode が Universal Library 用のテンプレートなりを用意してくれても良い気がするのですが、そんなものはありません。

なので lipo コマンドを使い、各 Architecture 毎に生成した Static Library を結合する処理を追加します。

手順をみていきましょう。

まず先ほど作成した Static Library 作成用プロジェクトに Aggregate ターゲットを追加します。

f:id:jarinosuke0808:20140208184910p:plain

Aggregate ターゲットは、複数のターゲットを一度にビルドしてくれ、コマンドラインスクリプトを記述できるターゲットです。

ソースコードコンパイルしたり、ライブラリをリンクするなど普通のターゲットとは違うので、ターゲット同士を結合するための非常にシンプルかつ透明性のあるターゲットという位置付けになっています。

ターゲットが作成できたら、下記のようにメニューを辿ってスクリプトを追加するための項目を以下のようにして追加します。

f:id:jarinosuke0808:20140208184947p:plain

スクリプトは以下のようなものを追加しましょう。

# Universal Libraryを配置する場所
UNIVERSAL_OUTPUTFOLDER=${BUILD_DIR}/${CONFIGURATION}-universal
 
# 実機用ライブラリ生成
xcodebuild -target TestTarget ONLY_ACTIVE_ARCH=NO -configuration ${CONFIGURATION} -sdk iphoneos  BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}

# iOS Simulator用ライブラリ生成
xcodebuild -target TestTarget -configuration ${CONFIGURATION} -sdk iphonesimulator -arch i386 BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}"
 
# Universal Library用フォルダ生成
mkdir -p "${UNIVERSAL_OUTPUTFOLDER}"
 
# lipo コマンドで実機・iOS Simulatorライブラリを結合
lipo -create -output "${UNIVERSAL_OUTPUTFOLDER}/lib${PROJECT_NAME}.a" "${BUILD_DIR}/${CONFIGURATION}-iphoneos/lib${PROJECT_NAME}.a" "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/lib${PROJECT_NAME}.a"
 
# lipoコマンドにより生成されたUniversal Libraryをコピー
cp -R "${BUILD_DIR}/${CONFIGURATION}-iphoneos/include" "${UNIVERSAL_OUTPUTFOLDER}/" 

簡単に言うと、iOS Simulator 向けの i386, iOS Device の向けの arm の Architecture を生成し、それらを lipo で結合してアウトプットを作っています。

これでビルドするターゲットを Aggregate に設定すれば Universal ビルドを作成出来るようになります。

終わりに

ここまで読んで頂いた方は気付いたかもしれませんが、私たちが SDK 普段使っているなどで使用している Static Library は凄い簡単に作れます。

次回はもう一つ先を行った Framework の作り方を書こうと思います。

参考

Xcode Build Settings Reference

xcodebuild man page

Creating a Static Library in iOS Tutorial