Gradleのビルドライフサイクルについて

Gradleを使うにあたって知っておいた方が良いと思う。
詳しくはGradleのドキュメントを見てもらえば分かるのですが、結構後ろの方にあるので見つけにくい。個人的にはもっと前半に説明してほしかったかなと。日本語で読みたい場合はこちらです

ちょっと引用すると、

初期化
Gradleはシングルプロジェクト、マルチプロジェクトの双方をサポートします。初期化フェーズでは、どのプロジェクトがビルドに参加しているのか決定し、Projectインスタンスを生成します。


設定
ビルドに参加しているすべてのプロジェクトのビルドスクリプトを実行します。初期化フェーズで生成したProjectインスタンスは、このフェーズでビルドスクリプトの記述通りに設定されるのです。


実行
設定フェーズで初期化と設定が完了したタスクのうち、実行するべきタスクを抽出して実行します。実行するタスクは、gradleコマンドに引き渡されたタスク名と、コマンドの実行ディレクトリから決定されます

47.1. ビルドフェーズ

のように、Gradleを実行すると初期化->設定->実行という順番で各フェーズが動きます。ポイントは、実行フェーズは、指定したタスクと依存関係にあるものが実行されるが、設定フェーズまではビルドスクリプトの内容が全て実行されるという点です。

例として、http://d.hatena.ne.jp/Hirohiro/20120320/1332217853で使用したWARのマージを行うサンプルに少し手を加えた物で説明する。build.gradleの内容は以下の通りとする。

apply plugin: 'war'

war {
    // ここは設定フェーズで実行される。WARファイルの名前を設定する。
    archiveName = 'sample.war'
}

war << {
    // ここは実行フェーズで実行される。WARタスクで生成されたWARに別のWARをマージ。
    ant.zip(destfile: war.archivePath, duplicate: 'preserve', keepcompression: true, update: true) {
        zipfileset (src: project(':common').war.archivePath)
    }
}

実は、タスク定義の書き方によってどのフェーズで実行させるか変わってくる。

  • war { ... } と定義したところは、設定フェーズ(Configuration phase)で実行される。つまり、この箇所はユーザがどのタスクを実行したとしても常に動作する。例では、ここではWARファイルの名前を設定している。
  • war << { ... } と定義したところは、実行フェーズ(Execution phase)で実行される。つまり、関係のないタスク実行時は処理されない。例では、WARファイルのマージ処理を行っている。

なので、設定フェーズに誤ってファイル処理(コピーとかzip作成とか)を入れちゃうと、どのタスクを実行しても常に実行されるという残念なことになるので注意しましょう。

なお、タスクの実行フェーズへの設定方法としては、

  • 前に追加
  • 後ろに追加

の2つ方法があります。上記の例の、「<<」は実は「後ろに追加」という意味。「<<」の代わりに「doLast」と書くこともできる。前に追加はdoFirstを使う。

war << {
    // ここは実行フェーズで実行される
}

war.doLast {
    // ここは実行フェーズで実行される。<<と同じ。
}

war.doFirst {
    // ここは実行フェーズで実行される。上記より先に実行される。
}

要は各タスクの処理はリスト構造になっており、前に追加か後ろに追加ができるようになっているわけです。
上書きではなくて追加という形をとることで、既存のタスクに簡単に処理を追加させることができるわけです。WARのマージ処理のところも、Gradle War Pluginが持っているwarタスクの後処理として、マージする処理を追加しているだけです。

war << {
    ant.zip(destfile: war.archivePath, duplicate: 'preserve', keepcompression: true, update: true) {
        zipfileset (src: project(':common').war.archivePath)
    }
}