2014年12月14日日曜日

既にあるAndorid.mkを使ってNDKビルドをGradleで実行する

このエントリーをはてなブックマークに追加 はてなブックマーク - 既にあるAndorid.mkを使ってNDKビルドをGradleで実行する

GradleでNDKサポートも行っているようですが、基本的にAndroid.mkは既存のものは使わず、自動生成するようになったようです。

シンプルな構成ならば指定された場所にファイルを移動したりすれば良いのですが、現在自分が担当しているプロジェクトはAndorid.mkがとても複雑化しており、既存のAndorid.mkを利用したまま、Gradleによってビルドする方法を試してみたのでそのメモを書きます。

検証ではNDKに最初から入っているサンプルプログラム(hello-jni)を利用します。

成果

toshihirock/AndroidNdkGradleSample  

前提

  • NDKインストール済み
  • android-sdkインストール済み
  • 環境変数ANDROID_HOMEを設定済み
  • Gradleがなんとなくは分かっている(細かい説明を書いていません)

環境

  • MacOSX10.10.1
  • ADT 23.0.0.1245622(build.gradle生成のためだけに利用)
  • NDK-r10d

ndk-buildコマンドとAntでビルド

まずはいつも通りantでビルドしてみます。(ここは別に実施しなくても良いですが一応ビルドできることの確認)

$cd ${NDK_HOME}/samples/hello-jni

# targetIDを確認してから、antのビルドで必要なbuild.xmlを生成
$android list target |less
$android update -p . -t 1

# ndk-buildする
$ndk-build

# antでビルド
$ant debug

# アプリをインストール
$adb install bin/HelloJni-debug.apk

ndk-buildをコマンドラインで実施。それ以外はGradleで行う

手っ取り早くGradleでビルドするようにbuild.gradleを作りたいのでEclipse with ADTを使います。

  • Eclipse(ADT)を起動
  • File→Export
  • Android→Generate Gradle build files

作成後、build.gradleの以下の2箇所を変更します。

# andorid-gradle pluginを最新化
- classpath 'com.android.tools.build:gradle:0.12.+'
+ classpath 'com.android.tools.build:gradle:1.0.0-rc4'

# plugin正式名称が変わったので対応
- apply plugin: 'android'
+ apply plugin: 'com.android.application'

デフォルトのgradlewのバージョンが1.12となっているので新しくします。現在の1.0.0-rc4ではgradle2.2.1が必要なので変更します。

$vi gradle/wrapper/gradle-wrapper.properties

- distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-all.zip
+ distributionUrl=http\://services.gradle.org/distributions/gradle-2.2.1-all.zip

とりあえず動くかタスクを見つつ確認。

$./gradlew tasks

ネイティブコードのビルドはCLIからndk-buildを実行します。

$ndk-build

上記状態でlibs/armeobiなどの配下に.soファイルが出来ていることが確認できます。

一度この状態でgradleだけでビルドしてみます。

$./gradlew assembleDebug

ビルドが終わってapkができます。パスはbuild/outputs/apk/hello-jni-debug.apkとなっております。

ただし、これにはNDKでビルドを行なった場合に必要な.soファイルが含まれていません。(unzip hello-jni-debug.apkで中身が確認できます。)起動しようとしても.soファイルがないのでエラーが発生し、起動できません。

上記で.soファイルがapkのパッケージングの際にされないのはAndroidGradleプラグインで想定されている箇所に.soファイルが存在しないためです。なお、想定されているパスはsrc/main/jniLibsとなります。

build.gradleを見ると分かるのですが、実はそもそもjavaファイルなどの場所もAndroidGradleプラグインで想定されているものとは違うため、明示的にbuild.gradleに記載されている事がわかります。

    main {
        manifest.srcFile 'AndroidManifest.xml'
        java.srcDirs = ['src']
        resources.srcDirs = ['src']
        aidl.srcDirs = ['src']
        renderscript.srcDirs = ['src']
        res.srcDirs = ['res']
        assets.srcDirs = ['assets']
    }

AndoridStuidoへの移行を契機にAndoridGradleプラグイン(mavenのプロジェクト)の想定するパスに変えた方が良いと思いますが、とりあえずは既存の状態で対応させていく方法をとります。

.soファイルの存在する場所をbuild.gradleに伝えるためにjniLibsプロパティを追加します。

    main {
        manifest.srcFile 'AndroidManifest.xml'
        java.srcDirs = ['src']
        resources.srcDirs = ['src']
        aidl.srcDirs = ['src']
        renderscript.srcDirs = ['src']
        res.srcDirs = ['res']
        assets.srcDirs = ['assets']
        jniLibs.srcDirs = ['libs']
    }

これで準備ができました。 既に.soファイルは出来ているのでgradleを使って再度ビルドします。

$./gradlew assembleDebug

同じようにapkをunzipすると今度はlibフォルダもパッケージングされていることが確認できます。 当然、アプリとしても動作が可能です。

ndk-buildもGradleで実行する

ndk-buildもgradleで実行したいのでタスクを追加します。

冒頭でも書いているようにAndoridGradleプラグインでもNDKのビルドをしようとするのですが、今回は必要ないのでjniプロパティに何も設定しないようにしておきます。(本構成の場合、規定されたパスにネイティブコードはないのでどうせビルドされないですが。。)

sourceSets {
    main {
        ・・・
        jni.srcDirs = []
    }

また、ndk-buildコマンドを実行するための追加のタスク及びcleanタスクやJavaCompileのタスク実行時にndk-buildが実行されるようにします。

// ndk
ext {
    ndkHome = '/Users/toshihirock/android-ndk-r10d/'
}

tasks.withType(JavaCompile) {
    compileTask -> compileTask.dependsOn ndkBuild
}

clean.dependsOn 'ndkClean'

task ndkBuild(type: Exec) {
    println "exec ndk-build"
    commandLine "${ndkHome}ndk-build"
}

task ndkClean(type: Exec) {
    println "exec ndk-build clean"
    commandLine "${ndkHome}ndk-build", 'clean'
}

自分の環境で試す場合にはndkHomeプロパティを変更するなり、build.propertiesに記載したり、環境変数から取得するなりしてください。

cleanタスクとassembleDebugタスクを実行すると一緒にndk-build cleanとndk-buildも実行されます。

$./gradlew clean assembleDebug

参考

0 件のコメント:

コメントを投稿