静的ライブラリをiosアプリとリンクする


はじめに

この記事は「完全に理解したTalk Advent Calendar 2020」17日目の投稿記事です。

これまで、静的ライブラリ、動的ライブラリの説明をし、
動的ライブラリの動的ロードについて説明しました。

このままだと動的ライブラリ万々歳な感じの説明になってしまいますので、
静的ライブラリのもう少し具体的な使い方の説明をしようと思います。

静的ライブラリ

静的ライブラリについては、こちらで説明をしています。

ふと思いますが、ビルドしたら結局合体して同じバイナリになるから、静的ライブラリはいらないんじゃない?って思いますよね。

ですが、違います。

静的ライブラリの使い道とは

静的ライブラリは、バイナリですので中身を見てもどんなコードかわかりません。
ですが、リンクすればその機能は使えます。
これがポイントとなります。

ですので、よくある使われ方というのは、
提供されたライブラリを自分で使うとき
ビルドした静的ライブラリを他者に提供するとき
です。

どこかの会社と契約して、「うちのSDKを使っていいよ」と言われて
ようやく提供してもらえるのが静的ライブラリです。(または動的ライブラリの場合もある)

その逆もあり得るわけです。
自分たちが作ったプログラムを他者に使ってもらうために静的ライブラリを提供する。
ソースコードを見せると自分たちのコアな技術も提供することになるため、
ライブラリにして渡すわけです。オープンソースとは違うわけです。

その静的ライブラリというのは、コンパイラに依存します。
x86_64対応のコンパイラでビルドすれば、x86_64でしか使えませんし、
armv7対応のコンパイラでビルドすれば、armv7でしか使えません。

これらのコンパイラを切り替えてビルドすることをクロスコンパイラと言ったりします。

本題

c++で書いたプログラムを静的ライブラリにして、
xcodeのswiftから呼び出したいと思います。

まずはシミュレータから使えるようにし、
実機からも呼び出せるようにしたいと思います。

静的ライブラリを生成

cmakeの準備

ios用のcmakeビルドツールチェインがこちらにあります。
それを使って、ios用の静的ライブラリを作っていきます。

ファイル配置

├── library.cpp
├── library.hpp
└── CMakeLists.txt
CMakeLists.txt
project(swiftCallStaticLib)
set(CMAKE_CXX_STANDARD 14)
add_library(swiftCallStaticLib library.cpp library.h)
$mkdir build && cd build
$cmake -G Xcode .. -DCMAKE_TOOLCHAIN_FILE=ios.toolchain.cmake

これを行うと、Xcodeプロジェクトが生成されます。
名前は、cmake記載されたprojectと同じ名前になります。
ここでは、
swiftCallStaticLib.xcodeproj
という名前になります。

Xcodeからライブラリを生成する

それでは、生成されたxcodeprojを開いてみます。

開くとこんな感じです。

次にアーキテクチャを追加します。

初期値は、
armv7 armv7s arm64が入っています。
自分のパソコンはx86_64ですので、x86_64を追加します。

それぞれビルドしたい環境をスキーム、ターゲットを設定してビルドします。
debugにし、simulatorでビルドすると、Debug-iphonesimulatorが出来ます。
また、
debugにし、Any iOS Deviceでビルドすると、Debug-iphoneosが出来ます。

lipoコマンドでそれぞれのライブラリを合体させることが出来ますが、
今回は無しでそのままそのライブラリを使ってみます。

静的ライブラリをリンクしてみる。

今回は呼び出すところではなく、ビルドが通るところまで確認します。

いつものようにプロジェクトを作ります。
プロジェクトに、先ほど作った
Debug-iphonesimulator内の.aファイルをドラッグドロップします。
これで、リンクされます。

プロジェクトのスキームをシミュレーターにし、ビルドします。
そうすると、ビルドが通るはずです!

では、次に、
Debug-iphoneos内の.aファイルをドラッグドロップします。
このままですと、二つともリンクしていますので、
Debug-iphoneosのライブラリを使うように選択します。

写真では、二つのライブラリの名前が同じなので、わかりやすいように名前を分けています。
リンクしないライブラリを選んで、-ボタンを押します。

ビルドすると、x86_64とそのライブラリが合わないのでリンクエラーになると思います。

なので、次はターゲットをAny iOS Deviceにしてみます。
ビルドすると、通るはずです。

以上がc++で作った静的ライブラリのリンク方法となります。

実際にswiftからライブラリを呼び出すには、
.hppファイルをbridgeすることで呼び出せます。

終わりに

c++で作ったライブラリをiOSアプリとリンクができたでしょうか。
コンパイルされてできたバイナリは、そのコンパイラに寄ります。

arm64で最適化されたプログラムは、x86_64では動きませんし、
その逆もそうです。

罠が一つ

iphonesimulator内の.aファイルは、c++のプログラムとリンクできるかを試します。

CMakeLists.txt
target_link_libraries(testStaticLink swiftCallStaticLib_simulator)

結果は不可です。

cmake error
libswiftCallStaticLib_simulator.a(library.o), building for macOS, but linking 
in object file built for iOS Simulator, for architecture x86_64

訳すと、

macOS用にビルドしていますが、アーキテクチャx86_64用のiOSシミュレータ用にビルドされたオブジェクトファイルにリンクしています

シミュレータ用のビルドになっているので、リンクが不可ということになります。
c++で使いたい場合は、

CMakeLists.txt
add_library()

で作るしかありません。

参考URL