2013年6月29日土曜日

【Android】端末上で実行できる単独バイナリのコンパイル【C/C++】

Androidで実行できるのは、apkのアプリだけでしょうか。待てよ、AndroidってLinuxベースのはず…じゃあネイティブのバイナリが動くはず…!
という鬱陶しい前置きはともかく、CやC++で書いたコードをAndroidのシェルで実行できるバイナリにコンパイルして遊ぶ記事です。

前提は以下です。
・この記事を参考にいろいろやった結果トラブっても自力で復旧できること(つまり自己責任)
・作業PCはWindows 7 64bit

■必要物資
・Android NDK

「android ndk」で検索したら一発で出てくると思います。「sdk」ではないですのでお間違え無く。
「Downloads」の項目の中から、作業環境に合わせたパッケージをダウンロードしましょう。
なお、Android NDKはそもそもJavaと連動して動くモジュールをapkの中に含めるためのツールですが、単独の実行バイナリを生成することも出来ます。

■1.環境構築
まず、ダウンロードしたzipを展開しましょう。あまり深い階層にしない方が無難です。
ここではとりあえずCドライブ直下に展開したとして、「c:\android-ndk」とします。
そしてandroid-ndkフォルダ直下に「ndk-build」がある状態にします。
そこですかさず、パスを通しましょう。ユーザー環境変数の「PATH」に「c:\android-ndk」を追加します。
パスの通し方はここでは詳しく書きませんので、検索等を駆使して調べてください。

次に作業フォルダを作りましょう。ここでは「c:\ndkwork」とします。
この中に、作品ごとのフォルダを作って作業していきます。
今回はベタに「hellondk」にしてみます。
そしてさらに、「hellondk」フォルダの中に「jni」フォルダを作っておきます。
「jni」のフォルダ名は固定です。必ずこの名前にしましょう。

□ndkwork ━ □hellondk ━ □jni

■2.ソースファイル等作成
jniフォルダの中に、ソースファイル等を置いていきます、

【2-1】「hellondk.c」作成
中身は以下の通り。
#include <stdio.h>

int main(int argc, char **argv) {
    printf("Hello, NDK!\n");

    return 0;
}

まあベタです。普通のCのコードですな。
あ、改行コードはLFにしておいた方がいいかもしれません。

【2-2】「Android.mk」作成
これは何か?makefileのようなものです。というか拡張子がmkだからそのまんまか。
中身は以下のような感じです。
LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_CFLAGS += -std=c99
LOCAL_MODULE    := hellondk
LOCAL_SRC_FILES := hellondk.c

include $(BUILD_EXECUTABLE)

お約束の書式が多々ありますが、まあこんなもんだと思っておきましょう。
「LOCAL_CFLAGS」はCコンパイラの追加フラグです。ここではC99規格にしています。
「LOCAL_MODULE」は、生成する実行バイナリの名前です。
「LOCAL_SRC_FILES」はソースファイルの指定です。複数ある場合は半角スペースで区切って並べましょう。
それ以外の部分はこのままでいいと思います。ちなみに最後のincludeの行は、単体の実行バイナリを作るという指定です。
こちらも改行コードはLFで。

■3.ビルド(コンパイル)
では希望に胸を膨らませながら、ビルドしてみましょう。
コマンドプロンプトで「hellondk」フォルダ(jniフォルダではないです)に入り、「ndk-build」を実行します。
C:\ndkwork\hellondk>ndk-build

すると、「hellondk」フォルダ内に新たに「libs」「obj」のフォルダが出来ます。
□ndkwork ━ □hellondk ┳ □jni ┳ hellondk.c
            ┃    ┗ Android.mk
            ┣ □libs ━ □armeabi ━ hellondk
            ┗ □obj ━ (中間ファイル群)

目的の実行ファイルは、「libs」の中の「armeabi」の中に出来ています。
やったー。

■4.実行してみる
では実機で動くか試してみましょう。
C:\ndkwork\hellondk\libs\armeabi>adb push hellondk /data/local/tmp
C:\ndkwork\hellondk\libs\armeabi>adb shell
shell@android:/ $ cd /data/local/tmp
shell@android:/data/local/tmp $ chmod 0755 hellondk
shell@android:/data/local/tmp $ ./hellondk

「Hello, NDK!」と表示されれば成功です。
…それだけです。

■その他
C++であれば、STLとかstd::stringとかも使えます。
ただしその場合は、jniフォルダ内に「Application.mk」を配置します。
中身はテキストで以下のような感じ。くどいようですが改行コードはLFで。
APP_STL := stlport_static

それでは楽しいNDKライフをお送りください。

4 件のコメント:

  1. 同様の方法で作成したのですが・・・
    Compile thumb : hello <= hello-jni.c
    SharedLibrary : libhello.so
    Install : libhello.so => libs/armeabi/libhello.so
    helloという実行ファイルができるはずが,libhello.soと変換されてしまいます.
    対処法をご教授頂けないでしょうか.

    返信削除
    返信
    1. Android.mkの最終行は、「include $(BUILD_EXECUTABLE)」になってますか?

      削除
  2. こちら記事を参考にさせていただいたのですが、以下のエラーとなり実行ファイルが作成されません。対処法など助言いただけませんでしょうか。
    ちなみにeclipseは使っていないのですが必要ですか?
    hellondk.cとAndroid.mkはエディターで文字コードSHIFT-JIS,改行コードLFにて作成しました。

    [armeabi] "Compile thumb ": "hellondk <= hellondk.c"
    process_begin: CreateProcess(NULL, C:/android-ndk/android-ndk-r11b/build//../toolchains/arm-linux-androideabi-4.9/prebuilt/windows-x86/bin/arm-linux-androideabi-gcc -MMD -MP -MF ./obj/local/armeabi/objs/hellondk/hellondk.o.d -fpic -ffunction-sections -funwind-tables -fstack-protector-strong -no-canonical-prefixes -march=armv5te -mtune=xscale -msoft-float -mthumb -Os -g -DNDEBUG -fomit-frame-pointer -fno-strict-aliasing -finline-limit=64 -Ijni -DANDROID -std=c99 -Wa,--noexecstack -Wformat -Werror=format-security -isystem C:/android-ndk/android-ndk-r11b/build//../platforms/android-9/arch-arm/usr/include -c jni/hellondk.c -o ./obj/local/armeabi/objs/hellondk/hellondk.o, ...) failed.
    make (e=2): wウスt@CェゥツゥワケB
    make: *** [obj/local/armeabi/objs/hellondk/hellondk.o] Error 2

    返信削除
  3. NDKのバージョンr11のバグのようでした。
    ndk-buildが参照している
    toolchains\arm-linux-androideabi-4.9\prebuilt\windows-x86
    が存在せず、実際は
    toolchains\arm-linux-androideabi-4.9\prebuilt\windows
    があったのでこのフォルダ名に-86を追加するとビルドが完了しました。ご迷惑おかけして申し訳ありませんでした。

    返信削除