直感で、一個コマンドだけで、テスト用のC/C++ファイルをAndroid版にコンパイルしたい:
android-gcc-toolchain gcc a.c
NDKは独自のmkファイルは便利ですが、世の中、いろんなオープンソースは伝統的なMakefileやautoconfやCMakeやGYPなどで構成されています。
これらのプロジェクトをAndroid版にコンパイルするには、Standalone Toolchain(むしろ標準toolchainといった方が良い)の登場です。
NDKは、確かに$NDK/build/tools/make_standalone_toolchain.py
(古いバージョンは$NDK/build/tools/make-standalone-toolchain.sh
)を提供していた。
これで標準toolchainを作成できますが、いくつか不便なところがあります:
- toolchainを作成した後、自分でPATHや、CCなど環境変数をいちいち設定しないといけない
- toolchain作成は遅い
- arm,arm64などarchの間での切り替えは不便
- ccache取り込んでいない
- ... つまり、もっと自動化できるのに...
私は我慢できませんので、ツールandroid-gcc-toolchainを作成しました。
何ができるの? 以下、android arm64版を例として簡単に説明します。arm64を省略すれば普通のarm(32bit)になります。
1. Androidのgccなどコマンドを簡単に実行できる.
-
コマンドの前にandroid-gcc-toolchainを入れば良い。arch(arm,arm64,x86,x64,mips,mip64)を指定できます。デフォルトはarm(32bit)。
android-gcc-toolchain arm64 gcc a.c android-gcc-toolchain arm64 g++ -v a.cc android-gcc-toolchain arm64 g++ --help android-gcc-toolchain arm64 clang++ -c a.c android-gcc-toolchain arm64 which gcc android-gcc-toolchain arm64 objdump -h a.out android-gcc-toolchain arm64 sh -c 'git checkout v1.0 && make clean && make'
gccなどコマンドのパラメータは元のコマンドのと変わらない。
実際は、どんなコマンドでも良いが、以下のgcc関連コマンドは全部Android版にリライレクトされます:
- gcc g++ cc c++ clang clang++ ar as ranlib ld strip nm ...
- readelf objdump c++filt elfedit objcopy strings size ...
2. クロスコンパイル用bashに簡単に入れます
-
対話式のbashなどshellに入る、gccなど関連コマンドをご利用いただけます。
$ android-gcc-toolchain arm64 ------------------------------------------------------- android-9-arm toolchain is ready! The bin dir($BIN) is: $NDK/std-toolchains/android-9-arm/bin PATH=$BIN:$PATH. Toolchain commands can be used directly: gcc g++ cc c++ clang clang++ ar as ranlib ld strip nm ... readelf objdump c++filt elfedit objcopy strings size ... [android-9-arm] このプロムプトの後ろにコマンドを入力してください。
上記でガイド表示完了。gccの場所を確認してみたり、コンパイルしたり試してみます:
[android-9-arm] which gcc /Users/q/Library/Android/sdk/ndk-bundle/std-toolchains/android-9-arm/bin/gcc [android-9-arm] gcc a.c
もちろん、後ろにzshなどを追加すれば、bashの代わりにzshにも入れます。
android-gcc-toolchain arm64 zsh
また、PATHでgccなどオーバライドするの代わりに、次のように、$CC系環境変数、$CC_target系環境変数設定済みの環境にも入れます。
-
$CCなどを設定済みの環境にも入れます;
android-gcc-toolchain arm64 -c
これで自動的に以下の変数が設定されます:
export BIN=$NDK/std-toolchains/android-21-arm64/bin export CC=$BIN/gcc export CXX=$BIN/g++ export LINK=$BIN/g++ export AR=$BIN/ar export AS=$BIN/as export RANLIB=$BIN/ranlib export LD=$BIN/ld export STRIP=$BIN/strip export NM=$BIN/nm
$BINは当時のtoolchainのbinフォルダです。
-
$CC...の代わりに、$CC_target...などを自動的に設定する環境にも入れます。
android-gcc-toolchain arm64 -C
CC_target CXX_target LINK_target AR_target AS_target RANLIB_target LD_target STRIP_target NM_targetは設定されます。
3. 普通のMakefileや、AUTOCONF project(e.g. ffmpeg)は簡単にビルドできます.
-
gccリダイレクトされた環境でビルド。例
Makefileあり:
android-gcc-toolchain arm64 make
autoconf系:
OTHER_FFMPEG_OPTIONS="--disable-everything --disable-doc --enable-protocol=pipe --enable-filter=scale --enable-filter=crop --enable-filter=transpose --enable-demuxer=rawvideo --enable-decoder=rawvideo --enable-muxer=image2 --enable-muxer=image2pipe --enable-muxer=mjpeg --enable-encoder=mjpeg --enable-encoder=png" android-gcc-toolchain arm64 <<< "./configure --enable-cross-compile --target-os=linux --arch=arm64 $OTHER_FFMPEG_OPTIONS && make"
-
$CC...設定済の環境でビルドしてもいいです。
android-gcc-toolchain arm64 -c make
4. GYPで構成されたプロジェクト(NodeJSやV8)を簡単にビルドできます
幾つか方法があります:
-
gccリダイレクトされた環境でビルド
android-gcc-toolchain arm64 <<< "./configure --dest-cpu=arm64 --dest-os=android --without-snapshot --without-inspector --without-intl && make"
-
$CC`...設定済の環境でビルド.
android-gcc-toolchain arm64 -c <<< "./configure --dest-cpu=arm64 --dest-os=android --without-snapshot --without-inspector --without-intl && make"
-
$CC_target
...設定済の環境でビルド.android-gcc-toolchain arm64 -C <<< "./configure --dest-cpu=arm64 --dest-os=android && make"
このモード、Host側(ローカルマシン)実行ファイルとtarget(Android)側実行ファイル両方を作成する場合に必須です。ちなみに、上記のコマンドはNodeJSの設定エラーで通らないので、詳しくはbuild-nodejs-for-android-perfectlyを参照ください。
5. 自動的にAndroid API levelのmin/maxを取得する。
-
デフォルトはminですが、
--api max
を指定すれば実際のNDKフォルダ構造からそのarchのmax Android API levelを取得する。例:$ android-gcc-toolchain arm64 --api max ... android-24-arm64 toolchain is ready! ...
6. 初めてはToolchainを自動的に作成します.
- 既存の$NDK/build/tools/make_standalone_toolchain.pyのオプションを継承しています:
--arch
,--api
,--stl
,--force
,--verbose
.
7. Toolchain作成はすごく早い!ハードリンクを利用するようにハッキングした。
- This is done by run a modified py file on-the-fly instead of original $NDK/build/tools/make_standalone_toolchain.py.
- The modified py file replace shutil.copy2 and shutil.copytree with customized copy2 and copytree which use hard link.
- You can specify
--copy
to force use traditional copy mode.
8. コンパイルキャッシュツールCCACHEをサポート
- 繰り返してclean&makeの場合はCCACHEをお勧めです。brew install ccacheあるいはsudo apt-get -y install ccache。そして一回
export USE_CCACHE=1
で済む。 (これでandroid-gcc-toolchainに「CCACHEを利用する」と伝えます)
すごく速くなります!
9. Docker image準備できました(Docker image id: osexp2000/android-gcc-toolchain
)。サイズはお得です!
このDocker imageの中に、NDKは入っていますが、膨大な1.3Gのplatformsフォルダをわずか27Mのplatforms.7zに圧縮した、フォルダを空っぽにした。デフォルト以外のtoolchainを利用しようとする時に、動的にで展開&削除。
これで、Docker imageのダンロードサイズを約1Gから410Mに縮小しました。(7zありがとう!)
このDocker imageを利用する方法はご自由に、例えば:
現在フォルダ($PWD)下にあるファイルa.ccをコンパイルしたい:
-
gccリライレクトされた対話環境で:
$ docker run -it -v $PWD:/work osexp2000/android-gcc-toolchain arm64 [android-21-arm64] cd /work && gcc a.cc -o result_exe
-
one-lineで
docker run -v $PWD:/work osexp2000/android-gcc-toolchain arm64 g++ /work/a.cc -o /work/result_exe
-
複数コンパイルをコマンンド併用
docker run -i -v $PWD:/work osexp2000/android-gcc-toolchain arm64 <<< "cd /work && g++ a.cc -o result_exe"
-
複数コンパイルをコマンンド併用(Here Document)
docker run -i -v $PWD:/work osexp2000/android-gcc-toolchain arm64 <<EOF cd /work && g++ a.cc -o result_exe EOF
Windows上Docker-machineを利用している場合、MINGW(例えばGit Bash)を利用ください。$PWDは必ずC:¥Usersの配下にしてください(例C:¥Users¥q¥Downloads)。また、環境変数MSYS_NO_PATHCONV=1も必要です。
10. すべてのコンパイル関連のコマンドの実際のパラメータを表示する。これは極便利な機能です。
-
環境変数
AGCC_VERBOSE
を1に設定する(export AGCC_VERBOSE=1
)または、-v
or--verbose
オプションをandroid-gcc-toolchain
コマンンドに渡せばいい。コンパイル関連コマンドとは、gcc g++ cc c++ clang clang++ ar as ranlib ld strip nmです。(当然、android-gcc-toolchainから直接か間接的に起動されたもののみ)
出力サンプル。
$___ ccache '....../arm-linux-androideabi-c++' \ $___ '-D_GLIBCXX_USE_C99_MATH' \ $___ '-I../deps/gtest' \ $___ '-I../deps/gtest/include' \ $___ '-Wall' \ $___ '-Wextra' \ $___ '-Wno-unused-parameter' \ $___ '-Wno-missing-field-initializers' \ $___ '-O3' \ $___ '-fno-omit-frame-pointer' \ $___ '-fPIE' \ $___ '-fno-rtti' \ $___ '-fno-exceptions' \ $___ '-std=gnu++0x' \ $___ '-MMD' \ $___ '-MF' \ $___ '....../gtest-filepath.o.d.raw' \ $___ '-c' \ $___ '-o' \ $___ '....../gtest-filepath.o' \ $___ '../deps/gtest/src/gtest-filepath.cc'
$___はgrepしやすいためです。どうせ空ですから、コピーペにも問題ありません。
... 実際は、もっとあります。
詳しくはhttps://github.com/sjitech/android-gcc-toolchainへ。
ちなみに、Windows版でのご利用はMINGW(例えばGIT Bash)が必要です。