CMake
Multi-platform Build-tool
Naruto 🍥 TAKAHASHI
● tnaruto@gmail.com
● twitter: @TNaruto
● Primary job: DeNA Co., Ltd.
○ develop SDK for mobile platforms
● Side job: wolfSSL Inc.
○ port embedded SSL/TLS library to RTOS
Today topic
● ネイティブライブラリをマルチプラットフォームへ展
開する
● ネイティブライブラリ
○ C/C++ 実装
○ ターゲット OS & アーキテクチャ用のコンパイラでビルドされたライ
ブラリバイナリ
Multiplatforms means...
Past
● Windows
● MacOS X
● Linux
Now
● Windows
● macOS
● Linux
● iOS
● Android
● many IoT OS...
Agenda
● Multiplatform build
● CMake
● 実践 CMake
Multiplatform build
Platforms & Build-tools
Platforms
● Windows
● macOS
● Linux
● iOS
● Android
Build-tools
● MSVC, NMake
● Xcode
● autotools
● Xcode
● build.mk
👉 各環境でそれぞれメンテナンスが必要
メンテナンス
数が多い・・・
問題点
● Windows と macOS と Linux 3つの起動が必要
● それぞれの Makefiles のメンテナンスが必要
● 問題点
○ 時間が必要(ビルド環境のメンテナンスも必要)
○ バージョン管理ソフトウェアと相性悪い
○ 面倒なので誰もメンテナンスをやりたがらない
○ OSS でそんなプロジェクトがあったら参加しづらい
👉 メンテナンスの面倒くささはプロジェクトの死
モチベーション
● モバイルファースト
○ 皆所有している情報端末
○ サービスはモバイル対応前
提
○ モバイル OS を対応するプ
ライオリティ🆙🆙
👉 同じコードで動作させることで品質担保
● 求められること
○ iOS と Android で共通のラ
イブラリを利用
○ モバイル <-> サーバ(Linux)
で共通の通信ライブラリを
利用
モチベーション(2)
● 開発環境の充実
○ モバイル + デスクトップ環
境での動作確認可能な開発
環境の台頭
○ Unity
○ Xamarin
○ Qt
○ 次に現れる素敵な開発環境
● 求められること
○ デスクトップとモバイルで
同じ動作をすること
○ iOS や Android 以外に
Windows や macOS のライ
ブラリも必要
👉 モバイルとデスクトップで同じコードで動作させることで開発効率担保
最近の開発インフラ
● CI(継続的インテグレーション)サービスが充実
○ Travis CI(Linux, macOS, Android, iOS)
○ appveyor(Windows)
● 自前で構築が可能
○ Jenkins
👉 CI クラウドサービスが充実
全プラットフォームのビルド&ユニットテストが可能
マルチプラットフォーム開発で求めるもの
● 一度書いたら全プラットフォームでビルドしたい
○ Write once, compile anywhere
○ 👉 GUI アプリの Qt は qmake で実現
● ビルド確認、動作確認は CI に任せたい
👉 ライブラリも Qt と同じようにしたいよね
状況と要望まとめ
状況
● ビルドターゲット増加
● メンテコスト増加
● CI 環境の充実
要望
● 一度書けばどこでもビル
ドできる
● ビルドと動作確認は CI
で自動化
👉 CMake という選択肢があります
CMake
What’s CMake?
● https://cmake.org/
● OSS & Cross-Platform tool
● support build, test and packaging
● generate Native Platform makefiles
● support by many companies
OSS & Cross-Platform tool
● Support Platforms
○ Windows
○ macOS
○ Linux
● セットアッププログラムあり
● CLI/GUI どちらもあります。
👉 CMake はインストール&バージョンアップがすごく楽
CMake インストールコマンド
choco install -y cmake --installargs 'ADD_CMAKE_TO_PATH=System'
sudo apt -y install cmake
brew install cmake
Windows
macOS
Linux(Ubuntu)
build, test and packaging
● ビルド
○ ビルド
○ クロスコンパイル
● テスト(ctest)
○ ユニットテスト
● パッケージ
○ リリース物作成
ビルド
● CMakeLists.txt に記述
● CMake は自身ではバイナリをビルドしない
○ 各プラットフォームの makefile やプロジェクトファイルを生成する
● バイナリのビルドは生成したプロジェクトに任せる
👉 プロジェクトを生成するのが CMake のビルド
generate Native Platform makefiles
CMakeLists.txt
● .sln
● .cproj
● Makefile
● .xcodeproj
● Makefile
● Makefile
support Platform makefiles
● Visual Studio
● Xcode
● Makefile (Unix, MSYS, MinGW, NMake...)
● Ninja
● and other…
👉 CMakeLists.txt を一度書けばすべてに出力可能
各プラットフォームのビルド CLI コマンド
make
ndk-build
./gradlew externalNativeBuildCleanDebug
xcodebuild
Unix
macOS & iOS
Android
Windows
"C:¥Program Files (x86)¥MSBuild¥14.0¥Bin¥msbuild.exe"
数が多い・・・(本日二度目)
CMake で生成されたプロジェクトは cmake --build コマンドでビルドが可能
cmake --build .
cmake --build .
cmake --build .
Unix
macOS & iOS
Android
Windows
cmake --build .
CMake toolchain
● toolchain でコンパイラが変更可能に
● toolchain を使うとビルド可能なもの
○ iOS
○ Android
○ その他プラットフォーム
👉 CMake toolchain を使えばクロスビルドも出来ちゃう
● CMake のテストランナー
● テストシナリオは CMakeLists.txt に直接記述
● gtest などのテストフレームワークが利用可能
● enable_testing() でテスト有効化
● add_test() でテストコマンドを登録
● ビルド後, ctest コマンドで実行可能
CTest
全プラットフォームで同コマンド & テストが可能
enable_testing()
add_executable(テスト用コマンド ソース...)
add_test(NAME “テスト1” COMMAND “テストコマンド1”)
add_test(NAME “テスト2” COMMAND “テストコマンド2”)
CMakeLists.txt
mkdir build && cd build # ビルドディレクトリ作成
cmake .. # makefile の生成
cmake --build . # ライブラリとテストプログラムのビルド
ctest . # add_test() で追加されたテストを順次実行
commands
support by many companies
● Qt Company
○ Qt アプリのビルドで CMake が利用可能に
● Microsoft
○ Visual Studio で CMake の対応が進んでいる
● Jetbrains
○ CLion(CMake 用 IDE) を開発
○ (ようやく) Unix ライブラリも IDE で開発できる!
👉 クロスプラットフォームを狙っている企業がサポート
デメリット
● CMake の情報が少ない
○ 欲しい情報はネットにないと思った方が良い
👉 だれか書籍を書いて🙏
CMake チュートリアル
● 数値を倍にして返す timestwo ライブラリプロジェクト
● libtimestwo ライブラリバイナリの作成
● timestwo_test ユニットテスト用プログラム作成
● ビルド&テスト実行
● Android 向け libtimestwo の作成
ライブラリコードとユニットテストコード
int timestwo(int num) { return num * 2; }
timestwo.c
int timestwo(int num);
timestwo.h
#include "timestwo.h"
int main() {
if(timestwo(0) != 0) return 1; // テスト失敗は 0 以外を返す
if(timestwo(1) != 2) return 1; // テスト失敗は 0 以外を返す
return 0; // テスト成功時は 0 を返す
}
timestwo_test.c
CMakeLists.txt
CMakeLists.txt
# ライブラリ作成
add_library(timestwo SHARED timestwo.c)
# ctest 有効化
enable_testing()
# テスト用実行バイナリ作成
add_executable(timestwo_test timestwo_test.c)
# テスト用実行バイナリのライブラリのリンク
target_link_libraries(timestwo_test timestwo)
# テストシナリオの追加
add_test(NAME timestwo_test COMMAND timestwo_test)
ビルド & テストコマンド
# ビルドディレクトリ作成
mkdir build && cd build
# makefile の生成
cmake ..
# ライブラリとテストプログラムのビルド
cmake --build .
# add_test() で追加されたテストを順次実行
ctest .
commands
実行結果(macOS)
Android もビルドしよう
CMakeLists.txt
# ライブラリ作成
add_library(timestwo SHARED timestwo.c)
if(NOT ANDROID) # Android ではユニットテストしない
enable_testing()
add_executable(timestwo_test timestwo_test.c)
target_link_libraries(timestwo_test timestwo)
add_test(NAME timestwo_test COMMAND timestwo_test)
endif()
armeabi-v7a ビルド
mkdir build_armeabi && cd build_armeabi
cmake .. -DCMAKE_SYSTEM_NAME=Android ¥
-DCMAKE_ANDROID_NDK_TOOLCHAIN_VERSION=clang ¥
-DCMAKE_SYSTEM_VERSION=21 ¥
-DCMAKE_ANDROID_ARCH_ABI=armeabi-v7a ¥
-DCMAKE_ANDROID_NDK=${ANDROID_NDK_ROOT}
cmake --build . # ライブラリのビルド
commands
arm64-v8a ビルド
mkdir build_arm64-v8a && cd build_arm64-v8a
cmake .. -DCMAKE_SYSTEM_NAME=Android ¥
-DCMAKE_ANDROID_NDK_TOOLCHAIN_VERSION=clang ¥
-DCMAKE_SYSTEM_VERSION=21 ¥
-DCMAKE_ANDROID_ARCH_ABI=arm64-v8a ¥
-DCMAKE_ANDROID_NDK=${ANDROID_NDK_ROOT}
cmake --build . # ライブラリのビルド
commands
ライブラリのバイナリフォーマット確認
CMake ここまでまとめ
● インストールが簡単
● CMakeLists.txt から Makefiles を生成
● CLI コマンドの共通化可能
● CMake toolchain でクロスビルド可能
● テストランナー搭載
● テストシナリオも CMakeLists.txt で管理可能
● 情報が少ないのがネック(誰か書いて)
実践 CMake
実践 CMake
● モバイルを含めたマルチプラットフォームビルド構築
● ユニットテスト実行
● CI で回す
👉 ようやく本題の CMake バッドノウハウ集です
simon-speck-c
● https://github.com/Naruto/simon-speck-c
● Simon Speck の実装
● Platforms
○ Desktop(Win, macOS, Linux)
○ Mobile(iOS, Android)
● Architectures
○ X86, X86_64
○ ARMv7, ARMv8
Simon Speck
● lightweight block cipher の一つ
● 汎用 IoT 向け
● 複数のブロックサイズ/キーサイズに対応
○ 32/64, 48/72, 48/96, 64/96, 64/128, 96/96, 96/144
○ 128/128, 128/192, 128/256
● simon が HW 向け、speck がCPU 向け
● speck は SIMD を書きやすい
simon-speck-c
● C 言語の Simon Speck 実装(今のところ Speck のみ)
● 対応ブロック・キー: 128/128, 128/192, 128/256
● 暗号モード: ECB, CTR(暗号・復号で並列処理可)
● パディング処理なし(CTR で代替)
● 暗号・復号処理は SIMD 利用
○ C 言語汎用版(ECB, CTR)
○ ARM NEON 版(ECB, CTR)
○ Intel AVX2 版(ECB, CTR)
simon-speck-c ビルド&テスト方法
git clone https://github.com/Naruto/simon-speck-c.git
cd simon-speck-c
mkdir build && cd build
cmake -DENABLE_TESTING=ON ..
cmake --build .
ctest .
commands
simon-speck-c テスト結果(macOS)
要件
● ライブラリビルド
● ブロックサイズ/暗号モードの動作確認
● SIMD ビルドと動作確認
● CI でビルド&動作確認
ライブラリビルド
下記に示すプラットフォーム向けにビ
ルドできること
● Windows
● macOS
● Linux
● iOS
● Android
それぞれ CMakeLists.txt のコーディン
グ方法の注意点とビルド時の注意点を
記載する
Windows 対応
CMakeLists.txt
● DllMain 関数が必要
○ CMake
○ ソース
● オプション指定の違い
○ /D とか -D
ビルド
● win32, win64 の二つビルドする
こと
macOS 対応
CMakeLists.txt
● 気をつけること特に無し
ビルド
● Makefile を出力する
● .framework, .bundle を作ること
○ bundle 作成
● 32bit, 64bit をビルドすること
● libtool で fat library にすること
○ autotools の libtool ではな
い
Linux 対応
CMakeLists.txt
● 気をつけること特に無し
👉 ユニットテストで活躍するので待っててね
ビルド
● 気をつけること特に無し
iOS 対応
CMakeLists.txt
● iOS ビルド用の toolchain ファイ
ルを利用すること
ビルド
● 各アーキテクチャのビルドをす
ること
○ armv7, armv7s, arm64
○ i386, x86_64
● libtool で fat library にすること
(ref1)
Android 対応
CMakeLists.txt
● Android ビルド用の toolchain フ
ァイルを取得
○ CMake v3.4 から標準搭載
ビルド
● 各アーキテクチャのビルドをす
ること
○ armeabi-v7a, arm64-v8a
○ x86(もういいかな・・・), x86_64
● cmake toolchain オプションは
Google を参照
○ https://developer.android.com/ndk/
guides/cmake.html#build-command
ブロックサイズ/暗
号モードの動作確
認
ユニットテストでブロックサイズと暗
号モードの全組み合わせの動作を確認
できること
● 128/128 ECB, 128/128 CTR
● 128/192 ECB, 128/192 CTR
● 128/256 ECB, 128/256 CTR
クロスビルド以外の下記プラットフォ
ームで動作を確認できること
● Windows
● macOS
● Linux
作業内容
● 6種類の動作確認プログラムを作る
○ 128/128 ECB, 128/128 CTR
○ 128/192 ECB, 128/192 CTR
○ 128/256 ECB, 128/256 CTR
● 間違ったら 0 以外を返すプログラムを作る
● ctest を動かす
動作確認プログラム
GitHub リンク
build ディレクトリでのコマンド集
ctest .
ctest .
Linux
macOS
Windows
rem ライブラリのパスを通す
set PATH=%PATH%;%CD%¥Debug
ctest -C Debug .
SIMD ビルドと動
作確認
下記を利用したバイナリを生成できる
こと。また動作確認を出来ること
● Intel AVX2
● ARM NEON
iOS と Android では ctest を用いた動
作確認が出来ないため、Linux 上で
ARM のビルド + 動作確認をすること
アーキテクチャ判別
● Intel
○ AVX2 搭載かを確認する
● ARM
○ ARMv7 か ARMv8 かを判別する
○ NEON 搭載かを確認する
CMake での AVX2 搭載判別方法
● AVX2 以降の命令がコンパイル & 実行可能かで判別
● _mm256_loadu_si256 実行ソース
● CMake でコンパイル&実行で確認(try_run を利用)
○ cpu_avx2.c をビルドし、実行
● ライブラリの動作確認は ctest で可能
CMake での ARM アーキテクチャ判別方法
● コンパイラターゲットで判別
○ ARMv7 の場合は “ARM” 変数が設定される
○ ARMv8 の場合は “AARCH64” 変数が設定される
NEON 搭載判別方法
● NEON 命令がコンパイル可能かで判別
● vld1q_f32 実行ソース
● CMake でコンパイルで確認(try_compile を利用)
● iOS や Android でのライブラリの動作確認は ctest では不可能
ARM バイナリ動作確認方法
● Linux でクロスビルド
○ 別アーキテクチャ向けにコンパイルが可能
■ arm ,aarch64
● QEMU
○ コマンドラインで別アーキテクチャの動作確認が可能
■ ターゲットアーキテクチャのユニットテストが可能
👉 CI 上で ARM のビルド&動作確認が可能になる
ARM クロスコンパイラとQEMU
● aarch64
○ gcc-aarch64-linux-gnu
○ g++-aarch64-linux-gnu
● arm
○ gcc-arm-linux-gnueabi
○ g++-arm-linux-gnueabi
● qemu
○ qemu-user-static
○ qemu-system-arm
ARM Linux クロスビルドオプション
● -DCMAKE_CROSSCOMPILING=ON
● -DCMAKE_SYSTEM_NAME=Linux
● -DCMAKE_SYSTEM_VERSION=1
● -DCMAKE_SYSTEM_PROCESSOR=(arm | aarch64)
● -DCMAKE_C_COMPILER=(arm-linux-gnueabi-gcc | aarch64-linux-gnu-gcc)
● -DCMAKE_CXX_COMPILER=(arm-linux-gnueabi-g++ | aarch64-linux-gnu-
g++)
👉 CMake のオプションでクロスビルド
CTest エミュレータ設定
● -DCMAKE_CROSSCOMPILING_EMULATOR でエミュレータの指定が可能
● エミュレータの引数は一つのみ
● QEMU は “qemu-arm-static -L rootfs” のように rootfs を指定するのでラッ
プしたスクリプトを用意
○ “qemu-arm-static -L /usr/arm-linux-gnueabi $@” (run_qemu_arm.sh)
○ “qemu-aarch64-static -L /usr/aarch64-linux-gnu $@” (run_qemu_aarch64.sh)
arm NEON 有効でクロスビルド & 動作確認
mkdir build_linux_arm && cd build_linux_arm
cmake .. -DCMAKE_CROSSCOMPILING=ON ¥
-DCMAKE_SYSTEM_NAME=Linux ¥
-DCMAKE_SYSTEM_VERSION=1 ¥
-DCMAKE_SYSTEM_PROCESSOR=arm ¥
-
DCMAKE_CROSSCOMPILING_EMULATOR=../scripts/tools/ubuntu/run_qem
u_arm.sh ¥
-DCMAKE_C_COMPILER=arm-linux-gnueabi-gcc ¥
-DCMAKE_CXX_COMPILER=arm-linux-gnueabi-g++ ¥
-DENABLE_NEON=ON ¥
-DENABLE_TESTING=ON
cmake --build .
ctest .
commands
aarch64 NEON 有効でクロスビルド & 動作確認
mkdir build_linux_aarch64 && cd build_linux_aarch64
cmake .. -DCMAKE_CROSSCOMPILING=ON ¥
-DCMAKE_SYSTEM_NAME=Linux ¥
-DCMAKE_SYSTEM_VERSION=1 ¥
-DCMAKE_SYSTEM_PROCESSOR=arm ¥
-
DCMAKE_CROSSCOMPILING_EMULATOR=../scripts/tools/ubuntu/run_qem
u_aarch64.sh ¥
-DCMAKE_C_COMPILER=aarch64-linux-gnueabi-gcc ¥
-DCMAKE_CXX_COMPILER=aarch64-linux-gnueabi-g++ ¥
-DENABLE_NEON=ON ¥
-DENABLE_TESTING=ON
cmake --build .
ctest .
commands
CI で回す
下記を利用したバイナリを生成できる
こと。また動作確認を出来ること
● Intel AVX2
● ARM NEON
iOS と Android では ctest を用いた動
作確認が出来ないため、Linux 上で
ARM のビルド + 動作確認をすること
CI (継続的インテグレーション) とは
継続的インテグレーション、CI(英: continuous
integration)とは、主にプログラマーのアプリケーシ
ョン作成時の品質改善や納期の短縮のための習慣のこ
とである。エクストリーム・プログラミング (XP) のプ
ラクティスの一つで、狭義にはビルドやテスト、イン
スペクションなどを継続的に実行していくことを意味
する( Wikipedia より )
● 課題解決
○ ソフトウェアの修正や変更によりデグレードが発生した
○ リポジトリから最新のソースコードを取得したがビルドが通らない
○ 不具合修正が関連する派生ソフトウェアに適用されていない
○ 結合またはシステムテスト時に多くの問題が発見され手戻りの工数が
かさむ
● マルチプラットフォームではよく起きる
○ すべてのプラットフォームのビルド&動作確認を忘れる・・・
○ すべてのアーキテクチャのビルド&動作確認を忘れる・・・
ネイティブにおける CI ビルド
手動ビルド確認地獄
● Travis CI
○ Linux と macOS でのビルドが確認可能
○ (QEMU で)AVX2, arm, aarch64 の動作確認が可能
● Appveyor
○ Windows でのビルドが確認可能
○ AVX2 の動作確認が可能
CI サービス
● 一度ソースや書けば・・・
○ すべてのプラットフォームのビルドが可能
○ すべてのアーキテクチャの動作確認が可能
● 複数 OS & IDE を立ち上げなくて済む
CI 導入で得たもの
👉CMake 対応は CI でビルド & 動作確認するためのといっても過言ではない
おわりに
CMake との付き合い方
● 情報が少ないので CMake を使いこなしているプロジェクトを参考にする
● OpenCV がすごく参考になります
● Qt や Microsoft がCMake 対応頑張っているので、上質なドキュメントが
出てくることを期待
おわりに
● マルチプラットフォーム対応のコスト
○ コーディング < 開発インフラ構築&運用
● CMake がベストだとは思わない
○ 👉 ただし今のところ、要件を満たせるのが CMake のみ
● CI は積極的に使っていこうね
● 今後は bazel に期待しよう
ご清聴ありがとうございました

CMake multiplatform build-tool