読者です 読者をやめる 読者になる 読者になる

ndk-buildを使わないでプログラムをビルドする

Android C++

普段は、ndk-buildでビルドをしているのが楽だし、便利なんですが、時に、外部のライブラリがconfigureとか使ってて、自前でAndroid.mkとかを作るのが面倒なことがあります。というか、試しに自前で作ってみようとしたけど、新旧の情報が錯綜しており、また、C++を使った場合のリンク方法がさっぱりとか、とりあえず、root化した端末でコマンドラインベースのコードを動かしてみたいけど・・・な場合にどうやれば良いのか全く分かりませんでした。

結局、当たり前ながら、じゃぁ、ndk-buildは何をやってるんでございましょうということになるんですが、これを見てみるために凄く簡単なサンプルを作ってみます。


sample/
jni/
Application.mk
Android.mk
sample.cpp

というディレクトリ構造で、

# Application.mk
APP_ABI := armeabi-v7a

# RTTI、例外を使うためにstatic GNU STLを利用する
APP_STL := gnustl_static
# Android.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)

LOCAL_MODULE := sample
LOCAL_CFLAGS :=
LOCAL_SRC_FILES += sample.cpp
LOCAL_STATIC_LIBRARIES += gnustl_static

include $(BUILD_EXECUTABLE)
// sample.cpp
#include <iostream>
#include <vector>

int main(int argc, char* argv[])
{
  // 単にSTLが使えているかどうかをチェックしたい
  std::vector<int> v(256);
  std::cout << "Hello, world." << v.size() << std::endl;
}

っていうのを作って、jni/ディレクトリで

ndk-build -n

とやると(ちなみに、/opt/android-ndk-r7にAndroid NDK r7をインストールしている)、

rm -f /somewhere/sample/libs/armeabi/lib*.so /somewhere/sample/libs/armeabi-v7a/lib*.so /somewhere/sample/libs/x86/lib*.so
rm -f /somewhere/sample/libs/armeabi/gdbserver /somewhere/sample/libs/armeabi-v7a/gdbserver /somewhere/sample/libs/x86/gdbserver
rm -f /somewhere/sample/libs/armeabi/gdb.setup /somewhere/sample/libs/armeabi-v7a/gdb.setup /somewhere/sample/libs/x86/gdb.setup
mkdir -p /somewhere/sample/obj/local/armeabi-v7a/objs/helloworld/
echo """Compile++ thumb""  : helloworld <= sample.cpp"
ccache /opt/android-ndk-r7/toolchains/arm-linux-androideabi-4.4.3/prebuilt/darwin-x86/bin/arm-linux-androideabi-g++ -MMD -MP -MF /somewhere/sample/obj/local/armeabi-v7a/objs/helloworld/sample.o.d -fpic -ffunction-sections -funwind-tables -fstack-protector -D__ARM_ARCH_5__ -D__ARM_ARCH_5T__ -D__ARM_ARCH_5E__ -D__ARM_ARCH_5TE__  -Wno-psabi -march=armv7-a -mfloat-abi=softfp -mfpu=vfp -fno-exceptions -fno-rtti -mthumb -Os -fomit-frame-pointer -fno-strict-aliasing -finline-limit=64 -I/opt/android-ndk-r7/sources/cxx-stl/gnu-libstdc++/include -I/opt/android-ndk-r7/sources/cxx-stl/gnu-libstdc++/libs/armeabi-v7a/include -I/opt/android-ndk-r7/sources/cxx-stl/gnu-libstdc++/include -I/opt/android-ndk-r7/sources/cxx-stl/gnu-libstdc++/libs/armeabi-v7a/include -I/somewhere/sample/jni -DANDROID  -Wa,--noexecstack -fexceptions -frtti -fexceptions -frtti  -O2 -DNDEBUG -g -fexceptions -frtti  -I/opt/android-ndk-r7/platforms/android-3/arch-arm/usr/include -c  /somewhere/sample/jni/sample.cpp -o /somewhere/sample/obj/local/armeabi-v7a/objs/helloworld/sample.o 
mkdir -p /somewhere/sample/obj/local/armeabi-v7a/
echo "Prebuilt       : libgnustl_static.a <= <NDK>/sources/cxx-stl/gnu-libstdc++/libs/armeabi-v7a/"
cp -f /opt/android-ndk-r7/sources/cxx-stl/gnu-libstdc++/libs/armeabi-v7a/libgnustl_static.a /somewhere/sample/obj/local/armeabi-v7a/libgnustl_static.a
mkdir -p /somewhere/sample/obj/local/armeabi-v7a/
echo "Executable     : helloworld"
/opt/android-ndk-r7/toolchains/arm-linux-androideabi-4.4.3/prebuilt/darwin-x86/bin/arm-linux-androideabi-g++ -Wl,--gc-sections -Wl,-z,nocopyreloc --sysroot=/opt/android-ndk-r7/platforms/android-3/arch-arm  /somewhere/sample/obj/local/armeabi-v7a/objs/helloworld/sample.o   /somewhere/sample/obj/local/armeabi-v7a/libgnustl_static.a /somewhere/sample/obj/local/armeabi-v7a/libgnustl_static.a /opt/android-ndk-r7/toolchains/arm-linux-androideabi-4.4.3/prebuilt/darwin-x86/bin/../lib/gcc/arm-linux-androideabi/4.4.3/libgcc.a    -Wl,--fix-cortex-a8  -Wl,--no-undefined -Wl,-z,noexecstack  -lc -lm -o /somewhere/sample/obj/local/armeabi-v7a/helloworld
echo "Install        : helloworld => libs/armeabi-v7a/helloworld"
mkdir -p /somewhere/sample/libs/armeabi-v7a
install -p /somewhere/sample/obj/local/armeabi-v7a/helloworld /somewhere/sample/libs/armeabi-v7a/helloworld
/opt/android-ndk-r7/toolchains/arm-linux-androideabi-4.4.3/prebuilt/darwin-x86/bin/arm-linux-androideabi-strip --strip-unneeded  /somewhere/sample/libs/armeabi-v7a/helloworld

という感じのことをやっている事が分かる。

つまり、まとめると、コンパイル、リンク、リンク後処理として、

# コンパイル
/opt/android-ndk-r7/toolchains/arm-linux-androideabi-4.4.3/prebuilt/darwin-x86/bin/arm-linux-androideabi-g++ \
	-MMD -MP -MF \
	/somewhere/sample/obj/local/armeabi-v7a/objs/helloworld/sample.o.d \
	-fpic \
	-ffunction-sections -funwind-tables -fstack-protector \
	-D__ARM_ARCH_5__ -D__ARM_ARCH_5T__ -D__ARM_ARCH_5E__ -D__ARM_ARCH_5TE__  \
	-Wno-psabi \
	-march=armv7-a -mfloat-abi=softfp -mfpu=vfp \
	-fno-exceptions -fno-rtti \
	-mthumb \
	-Os \
	-fomit-frame-pointer \
	-fno-strict-aliasing \
	-finline-limit=64 \
	-I/opt/android-ndk-r7/sources/cxx-stl/gnu-libstdc++/include \
	-I/opt/android-ndk-r7/sources/cxx-stl/gnu-libstdc++/libs/armeabi-v7a/include \
	-I/somewhere/sample/jni \
	-DANDROID \
	-Wa,--noexecstack \
	-O2 -DNDEBUG -g \
	-I/opt/android-ndk-r7/platforms/android-3/arch-arm/usr/include \
	-c  /somewhere/sample/jni/sample.cpp \
	-o /somewhere/sample/obj/local/armeabi-v7a/objs/helloworld/sample.o 
# リンク
/opt/android-ndk-r7/toolchains/arm-linux-androideabi-4.4.3/prebuilt/darwin-x86/bin/arm-linux-androideabi-g++ \
	-Wl,--gc-sections \
	-Wl,-z,nocopyreloc \
	--sysroot=/opt/android-ndk-r7/platforms/android-3/arch-arm \
	/somewhere/sample/obj/local/armeabi-v7a/objs/helloworld/sample.o \
	/somewhere/sample/obj/local/armeabi-v7a/libgnustl_static.a \
	/opt/android-ndk-r7/toolchains/arm-linux-androideabi-4.4.3/prebuilt/darwin-x86/lib/gcc/arm-linux-androideabi/4.4.3/libgcc.a \
	-Wl,--fix-cortex-a8 \
	-Wl,--no-undefined \
	-Wl,-z,noexecstack \
	-lc -lm \
	-o /somewhere/sample/obj/local/armeabi-v7a/helloworld
# デバッグ情報の除去
/opt/android-ndk-r7/toolchains/arm-linux-androideabi-4.4.3/prebuilt/darwin-x86/bin/arm-linux-androideabi-strip \
	--strip-unneeded \
	/somewhere/sample/libs/armeabi-v7a/helloworld

ということが分かる(一部、重複など簡略化済み)。
あとは、この辺を配慮した上でconfigure様に、CC/CXX/LD/CFLAGS/LDFLAGSなどを設定してあげれば良い感じみたいだ。