CMakeでAndroidアプリを作る

Androidは基本的に全てのアプリは何らかのJavaクラスでなければならない。Android2.3以降にはNativeActivityクラスが存在し一応Javaなしで開発できるように配慮はされている。が、結局AndroidシステムなりミドルウェアなりのJavaクラスとやりとりする機会がいつか出てきてしまうので、Javaコードを書く余地を残しておくことが望ましい。
一般的なCMakeのプロジェクトモデルと合わないのは:
そもそもCMake自身のJavaサポートが厳しい。今回はCMakeはCコードのビルドにだけ使用している。Java(を使ったパッケージング)はAndroid公式のGradleを使っている。
マルチアーキテクチャであるAndroidのネイティブアプリケーションは、

  • アプリケーション(Activity)を規定するJavaクラス
  • ネイティブコードを格納した.so - 各サポートアーキテクチャ分(IA32、ARM、...)

に分けられる。
もっとも、実際にはリリースされているIntelベースAndroidのほぼ全てでARMバイナリは実行可能で、かつ、Unity5のようなメジャーゲームエンジンがARM NEON必須だったりといった状況があるので、真剣にマルチアーキテクチャ対応を行う意義は薄い。重要なのはIntel向けのバイナリを用意するとホスト側のIntelAndroidエミュレータで実行できるという点。
今回はVisualStudioでネイティブ側の開発を行いたいのでMSが用意しているCMakeパッチを使用した。

blogで説明されているようにパッチされたCMakeをビルドし、

cmake.exe -G "Visual Studio 14 ARM" -DCMAKE_SYSTEM_NAME=VCMDDAndroid c:\path\to\here

のようにしてCMakeを実行すれば、Androidの.soを生成するソリューションが作成される。
...当然.so単体ではAndroid上で実行できないので、カスタムターゲット apk として、ビルドした.soのコピーとgradleを使ったビルドを行うものを用意した。

    # Android packaging
    set(deploy_dest
        ${CMAKE_CURRENT_SOURCE_DIR}/pkg/android/src/main/libs/armeabi-v7a/libgrpcore0.so)
    add_custom_command(
        OUTPUT ${deploy_dest}
        COMMAND ${CMAKE_COMMAND}
        -DIN=$<TARGET_FILE:grpcore0> -DOUT=${deploy_dest}
        -P ${CMAKE_CURRENT_LIST_DIR}/cmake/deploy_file.cmake
        )
    add_custom_target(grpcore0_deploy
        DEPENDS ${deploy_dest})
    add_dependencies(grpcore0_deploy grpcore0)
    add_gradle_command(apk ${CMAKE_CURRENT_SOURCE_DIR}/pkg/android
        build)
    add_dependencies(apk grpcore0_deploy)
    add_gradle_command(apk_clean
        ${CMAKE_CURRENT_SOURCE_DIR}/pkg/android
        clean)

(add_gradle_commandは適当な独自コマンド: https://github.com/okuoku/grpcore0/blob/b99fd3ba45ba253846b0d3cfa174360e347e2186/cmake/Grpcore0Android.cmake#L4 )
.soを ${CMAKE_CURRENT_SOURCE_DIR}/pkg/android/src/main/libs/armeabi-v7a にコピーしていることから判るように、現状.apkのビルドをソースツリー内で行っている。これは非常にダサいが避ける良い方法が思いあたらなかった。
今回移植層としてはSDL2を使っているので、SDL2のJava側のビルドはGradleで行うようにビルドルールを用意している。https://github.com/okuoku/grpcore0/tree/b99fd3ba45ba253846b0d3cfa174360e347e2186/pkg/android

Android Studio: Unexpected error while executing: am start -n ... で起動しない問題

Android StudioはデフォルトでInstant runを使うように変更されたようだ。というわけで、プロジェクトをロードしたときの指示に従ってビルドツールをアップデートする必要がある。

--- a/pkg/android/build.gradle
+++ b/pkg/android/build.gradle
@@ -3,7 +3,7 @@ buildscript {
         mavenCentral()
     }
     dependencies {
-        classpath 'com.android.tools.build:gradle:2.0.0+'
+        classpath 'com.android.tools.build:gradle:2.1.0'
     }
 }

http://stackoverflow.com/questions/36530648/session-app-error-launching-activity のように、設定からInstant runを無効にするという対策法も一応存在する。

Visual Studio: ネイティブデバッグできない問題

そもそもPermissionにINTERNET入れてなかった。。

--- a/pkg/android/src/main/AndroidManifest.xml
+++ b/pkg/android/src/main/AndroidManifest.xml
@@ -16,6 +16,7 @@

     <!-- Allow writing to external storage -->
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.INTERNET" />

     <!-- Create a Java class extending SDLActivity and place it in a
          directory under src matching the package, e.g.

PermissionにINTERNETを指定する以外に、gdbserverを手動で配備する必要がある。デフォルトのインストールパスでは"C:\ProgramData\Microsoft\AndroidNDK\android-ndk-r10e\prebuilt\android-arm\gdbserver\gdbserver"にあるので、これを.soと同じディレクトリに置く。
Android studioのLLDBデバッガに比べて、スレッド名が出ないなど諸々微妙な点は有る。
Visual StudioAndroid Studioを同時に起動すると互いに自分が起動したadbをkill-serverし合うという難易度の高い状態にハマることがある。VS側には(Xamarinの)adb command promptが有るので、これを使ってadb connectなりなんなりをやりなおす必要がある。