次世代ビルドツールBazelを使ってAndroidアプリをビルドする

会員事業部所属エンジニアの山下(@tomorrowkey)です。
去年はモバイルファースト室でバリバリとAndroidアプリを書いていたのですが、今年に入ってサーバーサイドもやってみたいと思い、最近はRubyを書いている日々です。 Rubyはあまりやったことがなかったのですが、REPLがあってとても助かります。Java 9でREPLが使えるようになるらしいですが、Androidは縁遠い話ですね。
さて、今回は来年ビルドツールとして脚光を浴びそうなBazelをご紹介したいと思います。

Bazelとは何か

Bazel http://bazel.io/

BazelはGoogleが社内で使用していたビルドツールをオープンソース版として開発をしているものです。2015年3月にAlpha版が公開されました。
Alpha版ではクライアントアプリケーションやiOSアプリのビルドなどがサポートされていて、最近Beta版が公開され、Androidアプリのビルドも可能になりました。 Beta版のサポート動作環境はUbuntuとOSXだけですが、将来Windowsもサポートする計画もあります。
実装のロードマップはここで参照することができます。

http://bazel.io/roadmap.html

2016年5月以降のStable版ではAndroid Studioとの統合という計画もあります。現在AndroidアプリではGradleでのビルドが主流ですが、Bazelでのビルドもできるようになるので、いまのうちに味見しておきましょう。

Bazelの特徴

Bazelの特徴は以下のようなものがあります。

  • Bazelは複数のプラットフォームをサポートします。
    • 現在サポートしている動作環境はUbuntuとOSXです。
  • サーバーアプリケーションや、クライアントアプリケーション、iOSやAndroidのアプリなどさまざまなソフトウェアのビルドスクリプトを記述できます。
  • Bazel独自の言語(Pythonに似ています)を使用し、ビルドスクリプトの役割である、WORKSPACEファイルやBUILDファイルを記述します。

Gradleとの違い

ここまで聞くとAndroidアプリ開発者なら、Gradleとどう違うんだ?と思いますが、Bazelではこの問に対して以下のように回答しています。

Gradleより構造的に記述することができ、それによりビルドの並列化と同時に再現性も担保することが可能

また、BraintreeではJavaアプリケーションやライブラリのビルドをGradleからBazelに移行したというエントリを書いており、その中でBazelはGradleよりも高速であると記載しています。

https://www.braintreepayments.com/blog/migrating-from-gradle-to-bazel/

Androidアプリのビルドは非常に時間がかかるものなので、これだけでも興味が湧いてくるのではないでしょうか。

Bazelの構成

BazelでビルドするにはWORKSPACEとBUILDというファイルが必要になります。

WORKSPACEファイル

Bazelを使用したビルドはworkspaceという場所で行われます。 workspaceの場所はどこでも大丈夫ですが、必ずWORKSPACEファイルを配置する必要があります。
WORKSPACEファイルが配置された場所がworkspaceのトップディレクトリとなります。
WORKSPACEファイルはライブラリ外部参照の依存関係を記述します。もし、依存関係がない場合は空ファイルになります。
WORKSPACEはPythonに似た独自の言語を使用して記述します。
workspaceには複数のプロジェクトを含むことができます。

BUILDファイル

BUILDファイルにはソースコードの配置やプロジェクトの依存関係、ライブラリの依存などを記述します。

Androidアプリのビルド

概念の説明ではよくわからないと思うので、具体的なAndroidアプリプロジェクトを例にBazelを使ったビルドをやってみましょう。

Bazelのインストール

ここを読みながらインストールします。 Androidが開発できる環境であればスクリプトを実行するだけで終わると思います。

今回はv0.1.0を使用します。

$ bazel version
Build label: 0.1.0
Build target: bazel-out/local_darwin-fastbuild/bin/src/main/java/bazel-main_deploy.jar
Build time: Tue Sep 8 23:14:50 2015 (1441754090)
Build timestamp: 1441754090
Build timestamp as int: 1441754090

プロジェクト構成

サンプルプロジェクトはここにあります。 https://github.com/tomorrowkey/HelloBazel/tree/v1.0.0

プロジェクトの構成は以下のようになります。

.
├── BUILD
├── HelloBazel
│   ├── app
│   │   ├── build.gradle
│   │   ├── libs
│   │   ├── proguard-rules.pro
│   │   └── src
│   │       └── main
│   │           ├── AndroidManifest.xml
│   │           ├── java
│   │           │   └── jp
│   │           │       └── tomorrowkey
│   │           │           └── android
│   │           │               └── hellobazel
│   │           │                   └── MainActivity.java
│   │           └── res
│   │               ├── drawable
│   │               ├── layout
│   │               │   └── activity_main.xml
│   │               ├── mipmap-hdpi
│   │               │   └── ic_launcher.png
│   │               ├── mipmap-mdpi
│   │               │   └── ic_launcher.png
│   │               ├── mipmap-xhdpi
│   │               │   └── ic_launcher.png
│   │               ├── mipmap-xxhdpi
│   │               │   └── ic_launcher.png
│   │               ├── values
│   │               │   ├── dimens.xml
│   │               │   ├── strings.xml
│   │               │   └── styles.xml
│   │               └── values-w820dp
│   │                   └── dimens.xml
│   ├── build.gradle
│   ├── gradle
│   │   └── wrapper
│   │       ├── gradle-wrapper.jar
│   │       └── gradle-wrapper.properties
│   ├── gradle.properties
│   ├── gradlew
│   ├── gradlew.bat
│   ├── local.properties
│   └── settings.gradle
└── WORKSPACE

このプロジェクトはGradleとBazelを共存させるように作られています。
一般的なAndroidStudioのプロジェクト構成をベースに作っており、親ディレクトリにWORKSPACEファイルとBUILDファイルが存在するのが特徴です。

WORKSPACEの定義

前述の通り、WORKSPACEにはビルドに必要なフレームワークやライブラリの参照を定義します。
今回はAndroid SDKの定義と、Mavenで配布されているcommons-langを使用します。

android_sdk_repository(
    name="androidsdk",
    path="/usr/local/opt/android-sdk",
    api_level = 23,
    build_tools_version="23.0.1"
)

maven_jar(name = "commons-lang", artifact = "org.apache.commons:commons-lang3:3.4")

pathにAndroid SDKの配置パスを指定します。api_levelbuild_tools_versionはビルドするアプリによって適宜変更してください。
Mavenで公開されているライブラリの定義はmaven_jarルールを使用します。

BUILDファイルの作成

BUILDファイルにはソースコードやリソースがどこに配置されているか、どのライブラリを参照するのかを定義します。

android_binary (
    name = "android",
    srcs = glob(["HelloBazel/app/src/main/java/**/*.java"]),
    custom_package = "jp.tomorrowkey.android.hellobazel",
    manifest = "HelloBazel/app/src/main/AndroidManifest.xml",
    resource_files = glob(["HelloBazel/app/src/main/res/**"]),
    deps = ["//external:android/appcompat_v4", "//external:android/appcompat_v7", "commons-lang"],
)

java_library(
  name="commons-lang",
  exports = [
    "@commons-lang//jar",
  ],
)

android_binaryはAndroidアプリをビルドするためのキーワードで、Bazelではこれをルールと呼びます。
Android向けのルールは他にAndroidライブラリを定義するandroid_libraryがあります。

各属性に名前やファイル名を指定します。globはワイルドカードを使ってファイルの配列を取得する関数です。
nameはandroidとしていますが、これはビルドやアプリインストールする際に使われる名前なので、何でも構いません。
他の属性は名前からだいたいの想像がつくのではないでしょうか。

どのようなルールや属性などが使えるかはBuild Encyclopediaに定義されています。

java_libraryにcommons-langを定義し、android_binaryのdepsにnameを指定します。

BUILDファイルの場所について

BUILDファイルはworkspace直下ではなく、プロジェクトに配置するのが一般的だと思われるのですが、モジュール内に配置するとGradleが生成するbuildディレクトリと名前が衝突してしまうためworkspace直下にBUILDファイルを配置しています。
AndroidStudioサポートする際はこの問題をどうするのか気になりますね。

ビルド

WORKSPACEファイルがあるディレクトリで以下のコマンドを実行することでビルドできます。

bazel build :android

androidはさきほどBUILDファイルに指定した名前です。
:の前に何も書かれていませんが、BUILDファイルがカレントディレクトリ以外にある場合に、BUILDファイルが配置してあるディレクトリまでのパスを指定します。
例えば以下のようなディレクトリ構成だった場合

.
├── app
│   ├─── BUILD
│   ├─── hoge
│   └─── fuga
└── WORKSPACE

このようなコマンドでビルドできるようになります。

bazel build app:android

インストール

Bazelにはアプリをインストールするためのコマンドも用意されており、以下のコマンドでインストールすることができます。

bazel mobile-install :android

ただし、環境によってはインストールしたアプリがクラッシュすることがあり、adbコマンドでインストールすることによって問題を回避することができる場合があります。

adb install ./bazel-bin/android_signed.apk

今回解説しなかったこと

例えばQueryを使えば、ビルドコマンド実行時に参照するライブラリを差し替えることができたり、Macro を使えばBUILDファイルで使える関数を増やすことなどができます。

今回説明したもの以外にもいくつかのルールや関数が公開されており、Build Encyclopediaに使用できるルールや関数が一覧できます。一部はv0.1.0には含まれておらず、次バージョンのv0.1.1に含まれるものもあるので注意してください。

おわりに

Android開発ではAndroid Studio移行と同時にGradleが使われてきましたが、Gradleは遅いという意見を多く聞きます。Bazelを使うことによって少しでもビルド時間が短くなればよいですね。Stable版がリリースされるまで半年ほどありますが、すこしずつ触って慣れておきましょう。

クックパッドでは最新のオープンソースプロジェクトやツールに敏感なモバイルアプリエンジニアを募集しています。

/* */ @import "/css/theme/report/report.css"; /* */ /* */ body{ background-image: url('http://cdn-ak.f.st-hatena.com/images/fotolife/c/cookpadtech/20140527/20140527163350.png'); background-repeat: repeat-x; background-color:transparent; background-attachment: scroll; background-position: left top;} /* */ body{ border-top: 3px solid orange; color: #3c3c3c; font-family: 'Helvetica Neue', Helvetica, 'ヒラギノ角ゴ Pro W3', 'Hiragino Kaku Gothic Pro', Meiryo, Osaka, 'MS Pゴシック', sans-serif; line-height: 1.8; font-size: 16px; } a { text-decoration: underline; color: #693e1c; } a:hover { color: #80400e; text-decoration: underline; } .entry-title a{ color: rgb(176, 108, 28); cursor: auto; display: inline; font-family: 'Helvetica Neue', Helvetica, 'ヒラギノ角ゴ Pro W3', 'Hiragino Kaku Gothic Pro', Meiryo, Osaka, 'MS Pゴシック', sans-serif; font-size: 30px; font-weight: bold; height: auto; line-height: 40.5px; text-decoration: underline solid rgb(176, 108, 28); width: auto; line-height: 1.35; } .date a { color: #9b8b6c; font-size: 14px; text-decoration: none; font-weight: normal; } .urllist-title-link { font-size: 14px; } /* Recent Entries */ .recent-entries a{ color: #693e1c; } .recent-entries a:visited { color: #4d2200; text-decoration: none; } .hatena-module-recent-entries li { padding-bottom: 8px; border-bottom-width: 0px; } /*Widget*/ .hatena-module-body li { list-style-type: circle; } .hatena-module-body a{ text-decoration: none; } .hatena-module-body a:hover{ text-decoration: underline; } /* Widget name */ .hatena-module-title, .hatena-module-title a{ color: #b06c1c; margin-top: 20px; margin-bottom: 7px; } /* work frame*/ #container { width: 970px; text-align: center; margin: 0 auto; background: transparent; padding: 0 30px; } #wrapper { float: left; overflow: hidden; width: 660px; } #box2 { width: 240px; float: right; font-size: 14px; word-wrap: break-word; } /*#blog-title-inner{*/ /*margin-top: 3px;*/ /*height: 125px;*/ /*background-position: left 0px;*/ /*}*/ /*.header-image-only #blog-title-inner {*/ /*background-repeat: no-repeat;*/ /*position: relative;*/ /*height: 200px;*/ /*display: none;*/ /*}*/ /*#blog-title {*/ /*margin-top: 3px;*/ /*height: 125px;*/ /*background-image: url('http://cdn-ak.f.st-hatena.com/images/fotolife/c/cookpadtech/20140527/20140527172848.png');*/ /*background-repeat: no-repeat;*/ /*background-position: left 0px;*/ /*}*/