2020年3月17日火曜日

Android/iOS向けのC/C++ライブラリをビルドするときにCMakeで引っ掛かった話

iOS/Androidの両方でOSSのライブラリを使いたい.そして,CMakeLists.txtはなるべく共通にしたい,と思って弄っていてハマったことのメモ.

プラットフォームの区別でハマる


プラットフォームでフォルダを区別するために,フォルダ名を設定する処理を次のように書いてました.

if(APPLE)
  set(PLATFORM_NAME "iOS")
elseif(ANDROID)
  set(PLATFORM_NAME "Android")
endif()

project(library)

でも,上手くいかないので何故かと思いきや,projectの後に書かないといけなかったんですね.

project(library)

if(APPLE)
  set(PLATFORM_NAME "iOS")
elseif(ANDROID)
  set(PLATFORM_NAME "Android")
endif()

iOSのビルド設定でハマる


iOS向けのビルドをするために,CMAKE_OSX系の変数をsetする場合,projectより先に書かないといけない.ドキュメントにはちゃんと書いていました.適当に拾ってきたものを利用してちゃんと調べないとダメですね.

iOS向けのCheckIncludeFile


使う場合,CMAKE_TRY_COMPILE_TARGET_TYPEをSTATIC_LIBRARYにしておかないとダメなようです.

オブジェクトライブラリでハマる


ドキュメントでも名指しされていますが,Xcodeはオブジェクトライブラリだけを利用して,そこからstaticライブラリとsharedライブラリを作る,ということができません.

iOS/Androidの両方で使える記述にしようと思うと,ソースコードは変数にまとめるしかなさそうです.ちなみに,自分の場合はstaticライブラリだけで良かったので,add_libraryに直接書きました.

インストールでハマる


特定の名前で特定のフォルダに出力されるようにしようと思ったんです.で,installコマンドで良いか,と思ったんですが,Xcodeで上手くいかない.おそらく使い方の問題だとは思うのですが,色々と面倒になったところで,楽な方法を見つけました.

set_target_properties(
  library
  PROPERTIES
  ARCHIVE_OUTPUT_DIRECTORY_DEBUG lib/${PLATFORM_NAME}/Debug
  ARCHIVE_OUTPUT_DIRECTORY_RELEASE lib/${PLATFORM_NAME}/Release
  ARCHIVE_OUTPUT_NAME_DEBUG library_debug
  ARCHIVE_OUTPUT_NAME_RELEASE library_release
)

出力ディレクトリ(ARCHIVE_OUTPUT_DIRECTORY)と出力名(ARCHIVE_OUTPUT_NAME)をプロパティとして指定できるので,最初から出力先を指定しておけば良かったのです.

ビルドでハマる


Androidの場合,Ninjaを利用してビルドします.Ninjaはシングルコンフィギュレーション,つまりビルド設定を生成する際にDebugやReleaseを指定しておく必要があります.そのため,ビルドの際にはコンフィギュレーションを指定する必要はありません.

次のコマンドは,buildフォルダにビルド設定を生成した場合です.

cmake -G Ninja その他オプション -DCMAKE_BUILD_TYPE=Debug -B build
cmake --build build

一方,Xcodeはマルチコンフィギュレーション,つまり実際のビルド時にコンフィギュレーションを指定するタイプです.そのため,ビルドの手順がAndroidと若干異なります.

cmake -G Xcode その他のオプション -B build
cmake --build build --config Debug

2020年3月14日土曜日

コマンドラインでAndroid NDK向けにライブラリをビルドする

Android NDK向けのライブラリのビルド手順を探すと,基本的にはAndroid Studioでプロジェクトを作って,ビルドする,という情報が多い.

しかし!

Windowsのコマンドラインだけでビルドしたいのである.

というわけで,Android Stuidoがやってくれている作業を何とか再現してみる.

今回ターゲットにしたのは,libogg.Xiph.Org Foundationによって公開されているパテントフリーのマルチメディアコンテナフォーマットを扱うライブラリらしい.ぶっちゃけ,自分が使っているわけではないのでよく知らない.

取り合えず,このライブラリはCMake対応済みなので,最初のとっかかりとして楽だった.

まず,Javaの実行環境を用意する.OpneJDKをダウンロードしておいて,そのbinを含むフォルダへのパスをJAVA_HOMEとして設定しておく.

次に,Android SDK(Android Studioのダウンロードページの下の方にCommand line tools onlyとしてダウンロードリンクがある)をダウンロードし,展開すると,toolsフォルダがあるので,そのtoolsフォルダへのパスをANDROID_SDK_ROOTとする.

ANDROID_SDK_ROOT/binへのパスを環境変数PATHに足しておく.

sdkmanagerが実行できるようになるので,次のように必要なものをインストールする.

> sdkmanager "ndk;21.0.6113669"

すると,ANDROID_SDK_ROOT/ndk/21.0.6113669というフォルダが出来ているので,このフォルダをANDROID_NDK_HOMEとする.

次に,CMakeをインストールする.公式にAndroidに対応したのが3.7以降なので,3.7以降をインストールしておく.できれば最新版が望ましい.インストールの際は,全体で利用できるようにシステム環境変数のPATHにパスを登録しておくと,そのあとのあちこちで問題にならないかもしれない.

また,Ninja(https://ninja-build.org/)をダウンロードしておいて,適当にPATHに登録しておく.

次に,liboggのソースコードをダウンロードし,展開しておく.このフォルダへのパスをOGG_ROOTとする.

さて,ビルド用のフォルダはOGG_ROOTの外に作った方が良い.CMakeが生成する様々なファイルとliboggのファイルの区別がつきやすいからだ.

build/libogg-androidとでもしておこう.このフォルダに移動したら,ようやくCMakeを実行する.

> CMake -G Ninja -DCMAKE_BUILD_TYPE=Debug -DCMAKE_MAKE_PROGRAM=ninja -DCMAKE_TOOLCHAIN_FILE=ANDROID_NDK_HOME/build/cmake/android.toolchain.cmake -DANDROID_PLATFORM=21 -DANDROID_ABI=arm64-v8a -DANDROID_LD=lld OGG_ROOT
> ninja

これで,libogg.aが出来ているので,あとはコレをAndroid NDKを利用しているアプリのプロジェクトに組み込めば良い.

-DANDROID云々の部分については,https://developer.android.com/ndk/guides/cmake を参照する.

LDをlldにしているのは好みの問題なのでご自由に.

最適化されたバージョンが欲しければ,-DCMAKE_BUILD_TYPE=Releaseとすれば良い.