Carthage と Bitcode 対応についてのまとめ

iOS 9 や watchOS 2 から新しく Bitcode が導入されました. パッケージ管理ツールの Carthage でも Bitcode を含んだ Framework を適切に出力する仕組みの導入が進んでおり, この対応状況を見ながら Bitcode 対応について考えます.

Carthage とは

Carthage は Cocoa アプリケーションのためのパッケージ管理ツールの1つです. Carthage は Cartfile をもとに GitHub などからソースコードをダウンロードし, Framework を出力します. ユーザーは出力された Framework を動的にリンクして利用します. このため iOS で利用する場合は Embedded Framework に対応した iOS 8 以上でしか利用できません.

Carthage についての詳細は GitHub の README.md を参照してください.

Bitcode とは

Bitcode は以前から存在するもので LLVM の中間言語です. 従来のアプリケーションは Xcode 上で機械語にコンパイルしたものを App Store で配信していましたが, Bitcode を用いる場合は Bitcode を Apple にアップロードし, Apple が Bitcode を機械語に変換して配信するようになります.

Bitcode は App Thinning を構成する技術の一つとして Apple に扱われています. Bitcode を利用するメリットなどは Apple のドキュメントを参照して下さい.

ここで重要な点は watchOS 2 以降と tvOS 用のアプリケーションでは Bitcode 対応が必須であるという点です. Bitcode を利用するメリットやデメリットが何であれ, これらのデバイス向けにアプリを開発する場合は Bitcode に対応することが必須です.

(補足: iOS では現時点で Bitcode 対応は必須ではありません. しかし Bitcode はデフォルトで有効化されているので注意が必要です. watchOS 1 App では WatchKit Extension は iOS デバイス側で動くためか, iOS 同様に Bitcode 対応は必須ではありません.)

Bitcode とライブラリ

Bitcode を iOS App や watchOS App で有効化した場合, アプリケーション本体だけでなくアプリケーションにリンクするすべてのライブラリが Bitcode に対応している必要があります.

このため, コンパイル済みのバイナリでライブラリが配信されているような場合 (e.g. Google) はライブラリ作成者が Bitcode を有効化してコンパイルしたものを配信しない限り, Bitcode 対応を行うことが出来ません.

メジャーなライブラリのうち Crashlytics は速やかに Bitcode に対応したバイナリをリリースしています. この問題については各ベンダーが速やかに Bitcode 対応を進めてくれることを願うしかありません.

Carthage が利用するビルド方法

Carthage はどのようにして Framework を出力するのかを理解することは Bitcode 対応で重要になります.

Carthage に対応しているライブラリは Xcode の Shared Build Scheme を設定しており, この Scheme でビルドすると Framework が出力されるように設定されています.

Carthage は Cartfile に基づき依存関係を解決し GitHub 等からライブラリのソースコードをダウンロードします. そして, 各ライブラリが設定している Shared Build Scheme を用いてライブラリをビルドします. このビルドにはコマンドラインツールの xcodebuild を用います. (動作としては Xcode 上で Build を実行するのと同じのはずです.)

Bitcode が出力される条件

Bitcode を出力するには, ビルドオプションの ENABLE_BITCODEYES にしておきます. しかしビルドオプションを設定するだけでは Bitcode は出力されません.

では Bitcode はどのようなビルドで生成されるのでしょうか? これについて公式ドキュメントに記載はないようなのですが, Apple Developer Forums のスレッドで Apple の社員が回答しています.

簡単にまとめると Xcode でビルドする場合は, Archive ビルドでのみ Bitcode が生成され, Build Configuration が Debug か Release かは関係ないとのことです.

上記の通り Carthage では xcodebuild を用いてライブラリを本体とは別にビルドします. この際 xcodebuild で Archive するのではなく, 通常の Build を用いる実装になっています. このため Carthage で生成された Framework には Bitcode は含まれていません.

補足: clangswiftc で Bitcode を出力するには

C/C++/Objective-C のコードを clang を用いてコンパイルする場合, 特定のオプションをつけることで Bitcode を出力することができます. -fembed-bitcode-marker をオプションに設定すると, 出力されるバイナリに Bitcode 用のセクションが含まれるようになりますが, Bitcode 自体は含まれません. -fembed-bitcode を設定すると Bitcode 用のセクションに Bitcode がきちんと含まれたバイナリが出力されます.

このオプションは Apple が提供している clang の独自のオプションのようです. clang --help | grep bitcode を実行するとオプションの存在を確認できます.

swiftc の場合はそれぞれ -embed-bitcode-marker-embed-bitcode というオプションを指定すれば, Bitcode を出力することができます. これらのオプションも swiftc -h | grep bitcode とすれば確認できます.

実際に生成されるバイナリについては別の記事で説明する予定です.

Build でも Bitcode を生成するには

Carthage における Bitcode 対応については GitHub のこの issue で議論が進みました. ある時 Xcode の Build Settings に BITCODE_GENERATION_MODE というオプションを設定することで, Bitcode を Build を用いたビルドでも出力できるということが発見されました.

BITCODE_GENERATION_MODEmarker とすると空の Bitcode 用のセクションが含まれるようになり, BITCODE_GENERATION_MODEbitcode とするときちんと Bitcode を含む Bitcode 用のセクションが出力されるようになります.

この発見により Archive ではなく Build でも Bitcode を含むバイナリを出力できるようになりました.

BITCODE_GENERATION_MODE への対応

BITCODE_GENERATION_MODE に対応するには, 各ライブラリが Build Settings に BITCODE_GENERATION_MODE を適切に設定する方法と, Carthage 側が Build Settings を上書きしてからビルドする方法が考えられ, 現在はその両方で対応が進んでいます.

ライブラリ側での対応

Bitcode に対応している場合は Build Settings の ENABLE_BITCODEYES にしておきます.
さらに BITCODE_GENERATION_MODE を設定します. BITCODE_GENERATION_MODE のデフォルト設定は marker のようなので, Debug ビルドでは marker に, Release ビルドでは bitcode に設定するのが良いようです.

この対応方法では Carthage のアップデートを待つ必要もなく, 多くの有名ライブラリで対応が進んでいます. ライブラリでの 実装例 も参考になります.

Carthage での対応

Carthage 側でも対応が進んでいます. Carthage での対応は ENABLE_BITCODE=YES となっているライブラリについては BITCODE_GENERATION_MODE を適切に設定してビルドするということになったようです. この Pull Request で対応されすでにマージされていますが, 現時点での最新リリース 0.9.3 にはまだ修正が含まれていません.

なお Carthage は現在 Homebrew で最新版を配信できておらず, 最新版は GitHub の Releases からダウンロードする必要があるようです.

2015/10/28 追記: BITCODE_GENERATION_MODE の自動設定に対応した Carthage 0.9.4 がリリースされました. Homebrew で配信されているものはまだ 0.8 なので, GitHub の Releases から最新版をダウンロードする必要があります.

2015/11/7 追記: Carthage 0.10 の Formula がマージされたため, Homebrew でも最新の Carthage をインストールできるようになりました.

まとめ

Bitcode 対応の詳細については iOS 9 がリリースされてしばらく経っている現在でもうまく浸透しているとは思えない状況です. これには Apple が開示している情報が少ないことや, パッケージ管理ツールの開発者の LLVM や Bitcode についての理解が進んでいないことが原因と考えられます.

現時点で Bitcode 対応をきちんと行うにはパッケージ管理ツールを含め, 最新の情報をきちんと確認しておく必要がありそうです.

更新

  • 2015/10/28 Carthage 0.9.4 のリリースについて追記
  • 2015/11/7 Carthage 0.10 の Homebrew でのインストールについて追記