ほとんどのUNIX系OSにおけるCライブラリはAutotoolsを使って開発されています(./configure && make installの手順でビルドする)。
普通に./configure && make installすると実行している環境(MacやLinux)用のバイナリが生成されますので、そのままではiOSで利用できません。
iOSで使用できるようにするにはiOS環境用にビルド、いわゆるクロスコンパイルする必要があります。
Autotools(./configure)ではクロスコンパイルのことが初めから考慮されていてそのための機能がサポートされているので、./configureの設定を正しくできれば、意外と簡単にさまざまなCのライブラリをiOS上で動くようにビルドできます。
設定について
iOSではデバイスとシミュレータでCPUアーキテクチャが異なるのでそれぞれのアーキテクチャのバイナリをビルドします。
デバイス用にビルドするときの./configureの例
./configure \ --host=x86_64-apple-darwin \ CC=`xcrun -find clang` \ CFLAGS="-O3 -arch armv7 -arch armv7s -arch arm64 -isysroot `xcrun -sdk iphoneos --show-sdk-path` -fembed-bitcode -mios-version-min=8.0" \ CXX=`xcrun -find clang++` \ CXXFLAGS="-O3 -arch armv7 -arch armv7s -arch arm64 -isysroot `xcrun -sdk iphonesimulator --show-sdk-path` -fembed-bitcode -mios-version-min=8.0" \ --prefix="$TARGETDIR_IPHONEOS" \ --disable-shared --enable-static
シミュレータ用にビルドするときの./configureの例
./configure \ --host=x86_64-apple-darwin \ CC=`xcrun -find clang` \ CFLAGS="-O3 -arch i386 -arch x86_64 -isysroot `xcrun -sdk iphonesimulator --show-sdk-path` -fembed-bitcode-marker -mios-simulator-version-min=8.0" \ CXX=`xcrun -find clang++` \ CXXFLAGS="-O3 -arch i386 -arch x86_64 -isysroot `xcrun -sdk iphoneos --show-sdk-path` -fembed-bitcode-marker -mios-simulator-version-min=8.0" \ --prefix="$TARGETDIR_IPHONESIMULATOR" \ --disable-shared --enable-static
--hostはクロスコンパイル(ビルド環境と実行環境が異なる)を有効にするために必要なオプションで、ビルドしたバイナリが実行される環境を指定します。
CCはCコンパイラの指定、CFLAGSはCコンパイラのオプションです。CXX、CXXFLAGSはC++コンパイラとコンパイラオプションの指定です。
デバイス用のビルドとシミュレータ用のビルドではアーキテクチャ(-archオプション)とシステムルート(-isysrootオプション)、ビットコードの埋め込み方(-fembed-bitcodeまたは-fembed-bitcode-marker)、Deployment Target(-mios-version-minまたは-mios-simulator-version-min)が異なります。
--prefixはmake installしたときのバイナリのインストール先を指定します。iOS用にライブラリをビルドする際は、デバイス用とシミュレータ用の2種類のバイナリを生成する必要があるので、続けてビルドした際に上書きされてしまわないようにそれぞれ別のディレクトリを指定しておくと便利です。
--disable-sharedはダイナミック(共有)ライブラリ(*.dylib)を生成しないオプション、--enable-staticは静的ライブラリを生成するオプションです。静的ライブラリの方が使い勝手がいいので私はこちらを指定することが多いですが、必要に応じて使い分けてください。
もし、watchOSやtvOS用のビルド(制限が強いのでこちらはそのままビルドできることは少ないですが)をしたい場合は、この要領でアーキテクチャやシステムルートを、そのデバイス向けに変更します。
実践(secp256k1をiOS用にビルド)
それでは試しにCで書かれたオープンソースのライブラリをiOS向けにビルドしてみましょう。
例として、Bitcoinで使われている楕円曲線暗号 secp256k1 のライブラリをiOSで使えるようにビルドしてみましょう。
Autotoolsがインストールされていない場合は先にautoconfとautomakeをインストールします。
$ brew install autoconf automake
リポジトリをクローンします。
$ git clone https://github.com/bitcoin-core/secp256k1 src
クローンしたリポジトリに移動します。
$ cd src
autoreconfコマンドでconfigureスクリプトを生成します。
$ autoreconf -if
成果物を保存するためのディレクトリを作っておきます。このディレクトリを--prefixオプションで指定します。
$ mkdir -p build/iphoneos
iOSデバイス向けの設定で./configureを実行します。
$ ./configure \ --host=arm-apple-darwin \ CC=`xcrun -find clang` CFLAGS="-O3 -arch armv7 -arch armv7s -arch arm64 -isysroot `xcrun -sdk iphoneos --show-sdk-path` -fembed-bitcode -mios-version-min=8.0" \ CXX=`xcrun -find clang++` \ CXXFLAGS="-O3 -arch armv7 -arch armv7s -arch arm64 -isysroot `xcrun -sdk iphonesimulator --show-sdk-path` -fembed-bitcode -mios-version-min=8.0" \ --prefix="$PWD/build/iphoneos" \ --disable-shared --enable-static
特にエラーがなければ続けて、make installを実行します。
$ make install
成功したらbuild/iphoneos/lib/以下のlibディレクトリにlibsecp256k1.aが生成されています。
正しくiOSデバイス向けにビルドされているか、アーキテクチャを確認します。
$ xcrun lipo -info build/iphoneos/lib/libsecp256k1.a
このコマンドの出力は下記のようになります。iOSデバイス用のビルドなのでARMアーキテクチャ用のバイナリが出力されています。
Architectures in the fat file: build/iphoneos/lib/libsecp256k1.a are: armv7 armv7s arm64
続けてシミュレータ用のビルドを作成します。
成果物が上書きされないように、出力先のディレクトリを別名で作成します。
$ mkdir -p build/iphonesimulator
シミュレータ用のビルド設定にして./configureをやり直します。
$ ./configure \ --host=x86_64-apple-darwin \ CC=`xcrun -find clang` \ CFLAGS="-O3 -arch i386 -arch x86_64 -isysroot `xcrun -sdk iphonesimulator --show-sdk-path` -fembed-bitcode-marker -mios-simulator-version-min=8.0" \ CXX=`xcrun -find clang++` \ CXXFLAGS="-O3 -arch i386 -arch x86_64 -isysroot `xcrun -sdk iphoneos --show-sdk-path` -fembed-bitcode-marker -mios-simulator-version-min=8.0" \ --prefix="$PWD/build/iphonesimulator" \ --disable-shared --enable-static
$ make install
ビルドが成功したら、build/iphonesimulator/libにバイナリが生成されています。同様にアーキテクチャを確認してみましょう。
xcrun lipo -info build/iphonesimulator/lib/libsecp256k1.a
このコマンドの出力は以下のようになります。
Architectures in the fat file: build/iphonesimulator/lib/libsecp256k1.a are: i386 x86_64
デバイス用のバイナリとシミュレータ用のバイナリをそれぞれ生成しました。 このままでも工夫すれば使えますが、不便なので2つのバイナリを結合した1つのファイル(Fatバイナリ)を作ります。
Fatバイナリの生成は、lipoコマンドを使います。
$ xcrun lipo -create "build/iphoneos/lib/libsecp256k1.a" \ "build/iphonesimulator/lib/libsecp256k1.a" \ -o "build/libsecp256k1.a"
xcrun lipo -info build/libsecp256k1.a
このコマンドの出力は次のようになります。ARMアーキテクチャとIntelアーキテクチャの両方が1つのバイナリに含まれています。
Architectures in the fat file: build/libsecp256k1.a are: armv7 armv7s i386 x86_64 arm64
後は、ライブラリをリンクし、ヘッダファイル(includeディレクトリにコピーされています)を適切に参照すればSwift/Objective-Cからこのライブラリを使用できます。
Swiftで使う場合にはアプリで使う場合(ブリッジヘッダ)とライブラリで使う場合(Module Map File)で多少利用方法が異なるので、実際にアプリ・ライブラリに組み込む方法は別の記事で解説します。
まとめ
このように、多くのCのライブラリは、iOS向けに書かれていないライブラリでも、簡単にiOS向けにビルドして使用できます。
私は上記で説明したような手順をスクリプトにまとめて、それぞれのライブラリごとの微修正して使用しています。
- BitcoinKit/build_secp256k1.sh at master · yenom/BitcoinKit · GitHub
- swift-magic/build_deps.sh at master · kishikawakatsumi/swift-magic · GitHub
OpenSSLなどAutotoolsを使用していないライブラリも存在しますが、基本的な考え方は同様です。そのビルドシステム(多くは単なるShellスクリプト)にアーキテクチャなどiOS用にビルドするためのコンパイラオプションを渡します。
下記の例は、OpenSSL(のlibcrypto)をiOS用にビルドするためのスクリプトの例です。OpenSSL のビルドスクリプトに環境変数を通じてコンパイラオプションを渡しています。
BitcoinKit/build_crypto_impl.sh at master · yenom/BitcoinKit · GitHub