Gradleのタスクの実行制御についてメモ

またまたGradleネタです。Gradleであるタスクを実行する場合のみに、依存関係にあるタスクの前に処理を割り込ませたい、というのをどう定義するか?というのでちょっと悩んでいたのでメモ。
Antなら、callタスクを使って別のタスクを呼び出すことができるので、例えば以下のように書ける。これをGradleだとどう書くのか?

  <target name="dist">
    <echo message="dist"/>
  </target>

  <target name="release">
    ... (ここで前処理)
    <antcall target="dist" />
    ... (ここで後処理)
  </target>

一応、非公開APIながらGradleではタスクに対してexecute()を呼び出せばタスクを実行できるのだが、これだとそのタスク単体しか呼ばれないようだ。コマンドラインからそのタスクを実行した時のように、そのタスクの依存関係までみて実行はしてくれない? 例えば、以下のように定義できるのだが...

task compile << {
  println "compile"
}

task dist(dependsOn: compile) << {
  println "dist"
}

task release << {
  println "Before process"
  tasks.dist.execute()
  println "After process"
}

これでreleaeタスクを実行しても、distタスクが依存するcompileタスクまで実行してくれない。

# gradle release
:release
Before process
dist
After process

BUILD SUCCESSFUL

というわけでどうするのがいいかなーとGradleのユーザガイドを眺めていると、使えそうな仕組みがあった。

以下のように、gradle.taskGraph.whenReadyのクロージャでGradleのconfiguration phase後に処理をフックすることができるのだ。これを使えば、releaesタスクが実行されるときにだけdistタスクに前処理をdoFirstクロージャで追加することができる。

task compile << {
  println "compile"
}

task dist(dependsOn: compile) << {
  println "dist"
}

task release(dependsOn: dist) << {
  println "After process"
}

gradle.taskGraph.whenReady {taskGraph ->
  if (taskGraph.hasTask(release)) {
    dist.doFirst {
      println "Before process"
    }
  }
}

実行結果は以下の通り。ちゃんと、依存関係のあるcompileも先に実行されつつ、distタスクの前に追加した処理が実行され、distタスク完了後にreleaseタスクが呼ばれるようになった。

gradle release
:compile
compile
:dist
Before process
dist
:release
After process

BUILD SUCCESSFUL

ただ、もうちょっとスマートに書く方法ないのかなぁ。。。