Android Advent Calendar 18日目の @zaki50です。既に19日目すら終わろうとしていますが皆様いかがお過ごしでしょうか(すみません...)。
今回は、Android Tools Project で開発が進められている新ビルドシステムについて書きます。ここで言うビルドシステムとは、Android アプリソースコードやリソースファイルを apk に変換するための仕組みのことです。現状では eclipse や ant を使ったビルドシステムが提供されていますが、それが今後どのように改善され発展していくか興味があったので調べたことをまとめます。
まず初めにお断りしておきますが、新ビルドシステムはまだまだ開発中のものです。リリース時期が決まってないどころか現状実装されている機能がそのままリリースされるかもわかりません。もちろん未実装の機能もたくさんあります。ですが、実際に試してみる事が出来る程度には実装されているので、興味が有る方はぜひ試してみてください。
今回書いた内容は、 Android Tools Project の New Build System に書かれている内容を元にしています。一次情報に当たりたいという方はぜひこちらも見てください。
今回のエントリはこれを読んで新ビルドシステムに興味をもった人が実際に試して見ることを目指します。元のサイトでは省略されているが実際に試すには必要となる情報を補いながら進めます。
Build System Concepts のページには、新ビルドシステムのゴールとして以下の3つが掲げられています。
上記の内容を具体的に何ができるかで表現すると以下のようになります。
このようなことが可能になるビルドシステムに興味はあるでしょうか。興味が有る方はさっそく新ビルドシステムを使用するための環境を整えましょう。
新ビルドシステムを使うための方法は Using the new Build System に書かれています。
新ビルドシステムは Gradle というant と Maven のいいとこ取りみたいなシステムの上に構築されています。そこで、まずは Gradle を使える環境を構築します。 Gradle については、 http://www.infoq.com/jp/news/2012/06/gradle-1 など幾つか解説サイトがあるので詳細はそちらを参照してください。
以下、Mac 上での環境構築を前提に記述します。他の環境の人は適宜読み替えをお願いします。
Gradle を http://gradle.org/downloads から落としてきます。注意するポイントは、最新の Gradle ではなく、 Gradle-1.2 をダウンロードすることです。Gradle-1.2 をダウンロードしたら、適当な場所に展開し gradle-1.2/bin/ にパスを通してください。
$ which gradle /Applications/gradle-1.2/bin/gradle $ gradle :help Welcome to Gradle 1.2. To run a build, run gradle <task> ... To see a list of available tasks, run gradle tasks To see a list of command-line options, run gradle --help BUILD SUCCESSFUL Total time: 1.511 secs
こんな感じに出力されればまずは成功です。
MacPorts でも Gradle をインストールすることができますが、そのまま入れると 1.3 が入ってしまうのでがんばって 1.2 をインストールしてください。
環境変数 ANDROID_HOME に、 Android SDK のパスをセットします(もし Android SDK をインストールしていない場合は適当な場所にインストールし、最新の Platform-tools と、android-support-v4 を落としてください)。
$ env |grep ANDROID_HOME ANDROID_HOME=path-to/android-sdks
この環境変数はビルドの際に SDK の場所を特定するために使用されます。
以上で、新ビルドシステムを使用する準備は完了しました。Android アプリケーションのビルドのために必要なプラグイン等はすべて Gradle が必要に応じてダウンロードしてくれます。
では早速 Gradle でアプリをビルドしてみましょう。
まずは、gradle でビルドするためのプロジェクトの作成です。AndroidManifest.xml や各種リソース、ソースコードなどは今までのプロジェクトと同じですが、配置する場所が異なります。また、ビルドに関する設定を行う build.gradle という名前のファイルを作成する必要があります。
まず、プロジェクト用のディレクトリとして空のディレクトリを作成します(今回は MyProject_gradle にしました)。ここに、build.gradle とソースコードなどを順に配置していきます。
Using the new Build System に書かれている最小の build.gradle は以下のものです。
buildscript { repositories { mavenCentral() } dependencies { classpath 'com.android.tools.build:gradle:0.1' } } apply plugin: 'android' android { target = 'android-17' }
このままではいくつか問題があるので、以下の修正を行います。
classpath 'com.android.tools.build:gradle:0.1'
を
classpath 'com.android.tools.build:gradle:0.2'
に変更します。現在 milestone 0.2 がリリースされているので、 0.2 を使用するように変更します。
最近は Java7 を入れている人も多いかと思いますが、Android は Java7 の class ファイルを扱えないので、Java6 互換として処理するように設定を追加します。今は java6 しかインストールしていなくても、将来的に Java7 を入れるかもしれないので今のうちに設定をしておきましょう。
build.gradle の最後に以下の記述を追加します。
sourceCompatibility=1.6 targetCompatibility=1.6
build.gradle の最後に以下の記述を追加します。
repositories { mavenCentral() mavenLocal() }
依存するライブラリを記述します。最近ではほぼ android-support-v4.jar を使用すると思います。Android SDK に含まれる support-v4.jar を依存 jar として追加するには以下の記述を build.gradle の最後に追加します。
dependencies { compile files("${System.getenv('ANDROID_HOME')}/extras/android/support/v4/android-support-v4.jar") }
android-support-v4.jar を自分でローカルの maven リポジトリに入れているのであれば、以下のように記述することもできます。
dependencies { compile 'com.google.android:support-v4:r11' }
ここまでの修正を行うと、 build.gradle は以下の様になっていると思います。
buildscript { repositories { mavenCentral() } dependencies { classpath 'com.android.tools.build:gradle:0.2' } } apply plugin: 'android' android { target = 'android-17' } sourceCompatibility=1.6 targetCompatibility=1.6 repositories { mavenCentral() mavenLocal() } dependencies { compile files("${System.getenv('ANDROID_HOME')}/extras/android/support/v4/android-support-v4.jar") }
まず、各種ファイルを配置するためのディレクトリ群を作成します。
$ mkdir -p src/{main,debug,release}/{java,jni,resources,aidl,rs,res,assets}
こんな感じに作成できていればOKです。
$ find . . ./src ./src/debug ./src/debug/aidl ./src/debug/assets ./src/debug/java ./src/debug/jni ./src/debug/res ./src/debug/resources ./src/debug/rs ./src/main ./src/main/aidl ./src/main/assets ./src/main/java ./src/main/jni ./src/main/res ./src/main/resources ./src/main/rs ./src/release ./src/release/aidl ./src/release/assets ./src/release/java ./src/release/jni ./src/release/res ./src/release/resources ./src/release/rs
現状の Android アプリのプロジェクトからのファイルは、 src/ 以下に配置されます。src/ の直下には、 Sourceset と呼ばれるディレクトリが作成されます。今回は main, debug, release という Sourceset を作成しました。このうち main がすべての基本になるディレクトリです。このプロジェクトで作成するすべての apk に共通するものをここに配置します。 release は リリースビルドの際だけ使用するファイル群を、debug にはデバッグビルドの際に使用するファイル群を配置します。これら以外にも、自由にsourceset ディレクトを作成することができます。
また、全ての Sourceset は直下に以下のファイル/ディレクトリを持つことができます。
次に現状のプロジェクトからファイルをコピーします。元のプロジェクトが ../MyProject にあるとします。
$ cp ../MyProject/AndroidManifest.xml src/main/ $ cp -r ../MyProject/src/* src/main/java/ $ cp -r ../MyProject/res/* src/main/res/ $ cp -r ../MyProject/assets/* src/main/assets/
もし、 元のプロジェクトが AIDL ファイルを含んできる場合は、src/main/java/ から src/main/aidl/ へ移動してください。
ここまでくればapk をビルドする準備は完了です。
さっそくビルド&インストールします。APK を端末にインストールするので、端末をつなげておいてください。
$ gradle installDebug Download http://repo1.maven.org/maven2/com/android/tools/build/gradle/0.2/gradle-0.2.pom Download http://repo1.maven.org/maven2/com/android/tools/build/builder/0.2/builder-0.2.pom Download http://repo1.maven.org/maven2/com/android/tools/build/gradle/0.2/gradle-0.2.jar Download http://repo1.maven.org/maven2/com/android/tools/build/builder/0.2/builder-0.2.jar Deprecated: relying on packaging to define the extension of the main artifact is deprecated, and will not be supported in a future version of Gradle. :prepareOrgZakkyAndroidMyLibrary10Library :prepareDebugDependencies :compileDebugAidl :generateDebugBuildConfig :crunchDebugRes :processDebugManifest :processDebugRes :compileDebug :dexDebug :processDebugJavaRes UP-TO-DATE :packageDebug :installDebug 4184 KB/s (189521 bytes in 0.044s) pkg: /data/local/tmp/MyProject_gradle-debug-unaligned.apk Success BUILD SUCCESSFUL Total time: 16.761 secs
BUILD SUCCESSFUL と出力されたでしょうか。もし BUILD FAILED と出た場合は、エラーメッセージを確認して対処してください。 build.gradle の記述ミスか、 src/main/ の下に必要なファイルが存在しないという場合が多いと思います。
gradle tasks と実行すると、他に実行可能なタスクの一覧が出力されます(--all もつければさらに細かい Task も出力されます)。
今まで作成した main, debug, release は予め定義されているもの(debug, release は BuildType と呼ばれます。BuildType を追加することもできますが今回は省略します)ですが、Flavor を追加することで、様々なバリエーションを持った APK を作成することができます。 Flavor を追加するには、 Flavor に対応する Sourceset を src/ ディレクトの下に作成し、build.gradle に productFlavors として記述を追加します。
たとえば、無料版と有料版で別 APK を作成するためので Flavor を追加する場合は以下のように build.gradle の android の中に以下の記述を追加します。
android{ (略) // ここから追加 defaultConfig { versionCode = 12 versionName = "1.2" } productFlavors { freeapp { packageName = "org.zakky.myproject.free" minSdkVersion = 14 } paidapp { packageName = "org.zakky.myproject.paid" minSdkVersion = 16 } } // ここまで追加 }
すべての Flavor に共通の設定は defaultConfig に記述し、 Flavor 毎の設定は productFlavors に記述します。この例では、全 Flavor 共通で versionCode とversionName を指定し、Flavor の設定で packageName と minSdkVersion を変更しています。各 Flavor にディレクトリに AndroidManifest.xml を置くことができるので、その中で例えな Free 版にだけ広告のために必要な追加 permission を指定することもできます。その場合でも、 versionCode とversionName defaultConfig によって一元管理することでメンテナンスのコストが上がらないように工夫されています。
Flavor を追加すると、gradle に指定するタスクが自動的に増えます。今回は freeapp と paidapp Flavor を追加したので、 installDebug が無くなり、代わりに installFreeappDebug と installPaidappDebug が追加されます(gradle tasks で確認してみてください)。
Flavor を追加した場合は gradle installFreeappDebug の様に、 install + <flavor名> + <build type 名> をタスク名に指定してください。
今回は Flavor の定義を追加しただけですが、 追加した Flavor に対応する Sourceset フォルダの中の java や res フォルダにファイルを配置することで、その Flavor の時のみビルドされるクラスやリソースを追加することができます。たとえば有料版にしか存在しない機能のコードは src/paidapp/java/ に、無料版にしか存在しない広告のレイアウトリソースは src/freeapp/res/layout/ に配置します。
これで一つの プロジェクトから、様々な APK を作成できるようになりました。
各 Flavor や BuildType でどのような設定を行うことができるかは、 Basic Project Setup を参照してください。
次に、ライブラリプロジェクトの扱いについてです。
基本的には上に書いたアプリのプロジェクトとほぼ同じなのですが以下の特徴があります。
アプリのプロジェクトでは Flavor を定義することで様々な APK を生成することができましたが、ライブラリプロジェクトが作成できるのは debug と release の2種類のファイルだけです。また、debug はあくまでUnitTest などのためで、maven リポジトリにdeploy できるのは release 版のみです。
プロジェクトの作成手順はほぼアプリプロジェクトと同じなので、ライブラリプロジェクトについては build.gradle の作成と、maven リポジトリへのdeploy について書きます。
アプリプロジェクトと同じように、空ディレクトリを作成したらまずは build.gradle ファイルを作成します。
以下にライブラリプロジェクト用の build.gradle のサンプルを掲載します。
buildscript { repositories { mavenCentral() } dependencies { classpath 'com.android.tools.build:gradle:0.2' } } apply plugin: 'android-library' sourceCompatibility=1.6 targetCompatibility=1.6 android { target = 'android-14' } dependencies { compile files("${System.getenv('ANDROID_HOME')}/extras/android/support/v4/android-support-v4.jar") } apply plugin: 'maven' uploadArchives { repositories { mavenDeployer { repository(url: "file://localhost/Users/zaki/.m2/repository/") pom.version = '1.0' pom.groupId = 'org.zakky.android' pom.artifactId = 'MyLibrary' } } }
アプりプロジェクト用の build.gradle と違う部分だけ説明します。
このような build.gradle を作成したら、アプリプロジェクトと同じように src/main などを作成し、必要なファイルを配置します。
gradle build でビルド御テストが走ります。
maven リポジトリへは以下のコマンドで deploy することができます。
$ gradle uploadArchives :packageDebugAidl UP-TO-DATE :prepareDebugDependencies :compileDebugAidl :generateDebugBuildConfig :processDebugManifest :processDebugRes :compileDebug :processDebugJavaRes UP-TO-DATE :packageDebugJar :packageDebugRes :packageDebugSymbols :bundleDebug :packageReleaseAidl UP-TO-DATE :prepareReleaseDependencies :compileReleaseAidl :generateReleaseBuildConfig :processReleaseManifest :processReleaseRes :compileRelease :processReleaseJavaRes UP-TO-DATE :packageReleaseJar :packageReleaseRes :packageReleaseSymbols :bundleRelease :uploadArchives Uploading: org/zakky/android/MyLibrary/1.0/MyLibrary-1.0.alb to repository remote at file://localhost/path-to/repository/ Transferring 42K from remote Uploaded 42K Uploading: org/zakky/android/MyLibrary/1.0/MyLibrary-1.0-debug.alb to repository remote at file://localhost/Upath-to/repository/ Transferring 42K from remote Uploaded 42K BUILD SUCCESSFUL Total time: 5.696 secs
ライブラリプロジェクトが maven リポジトリに deploy されていれば、後は dependency に依存を記述するだけです。先ほどの アプリプロジェクトの build.gradle の dependencies にライブラリプロジェクトへの依存を追加します。
dependencies { compile 'org.zakky.android:MyLibrary:1.0' compile files("${System.getenv('ANDROID_HOME')}/extras/android/support/v4/android-support-v4.jar") }
これで MyLibrary プロジェクトに含まれている物を MyProject から利用することができます。
$ gradle installFreeappDebug Deprecated: relying on packaging to define the extension of the main artifact is deprecated, and will not be supported in a future version of Gradle. :prepareOrgZakkyAndroidMyLibrary10Library :prepareFreeappDebugDependencies :compileFreeappDebugAidl :generateFreeappDebugBuildConfig :crunchFreeappDebugRes :processFreeappDebugManifest :processFreeappDebugRes :compileFreeappDebug :dexFreeappDebug :processFreeappDebugJavaRes UP-TO-DATE :packageFreeappDebug :installFreeappDebug 3365 KB/s (189528 bytes in 0.054s) pkg: /data/local/tmp/MyProject_gradle-freeapp-debug-unaligned.apk Success BUILD SUCCESSFUL Total time: 10.026 sec
今回は現在開発が進んでいる Gradle ベースの新ビルドシステムを実際に試して、手順とできることを書いてみました。
Flavor のサポートによって1つのプロジェクトから様々な APK が作成できる点と、maven や Ivy と親和性の高いライブラリプロジェクトが大きな目玉です。正式リリースはもうしばらく先になるとは思いますが、今回これらの機能を実際に使ってみて今後は CI との親和性がより高くなりまたメンテナンスもしやすい開発環境が構築できるのではないかなと思いまいた。
既存のプロジェクトの移行は多少面倒ですが、正式リリースの際にはプロジェクトの移行ツールなども提供されるのではないかなと思っています。 今回はじめて Gradle を使ったのですが強力なビルドシステムだと感じました。Android 以外でも Gradle を使用しつつ将来の 新ビルドシステムに備えようと思います。
また、この新ビルドシステムについては adt-dev Group で議論されています。ADT などについてもここで議論されているのでなにか気づいたことがあれば投稿してみるといいとおもいます(@sys1yagi さんのように!)。