Gradle1.6の新機能mustRunAfterでタスク間の実行順序が制御可能になった #gradle
Gradleではタスク間の依存関係の指定ができます。
build.gradle:
task a << { println "A" } task b(dependsOn: "a") << { println "B" } task c(dependsOn:"a") << { println "C" } task d(dependsOn: ["b", "c"]) << { println "D" }
実行結果:
$ gradle d :a A :b B :c C :d D BUILD SUCCESSFUL Total time: 1.612 secs
実はこの例のBとCの間の関係は不定であり、実装に基づいて(この場合はアルファベット順だったりしたはず)実行順序が決められます。 ここではA→B→C→Dになっていますね。
しかし、このようなグラフ的には一見どっちがさきに実行されても良いようにみえるBとCの間に、実は微妙な前後関係があって、Cの方が先に実行されて欲しい!というケースは意外とありますよね。
長い間Gradleユーザから臨まれていたこの機能がついに1.6で実装されました(ということをさっき知った)!
Task#mustRunAfter()
の使い方
以下のように使います。
build.gradle:
task a << { println "A" } task b(dependsOn: "a"/*, mustRunAfter: "c" -- 残念ながら今のところここには書けないらしい*/) { mustRunAfter "c" // メソッド呼出として書く doLast { println "B" } } task c(dependsOn:"a") << { println "C" } task d(dependsOn: ["b", "c"]) << { println "D" }
と書くと、こうなります:
$ gradle d :a A :c C :b B :d D BUILD SUCCESSFUL Total time: 1.64 secs
dependsOn
との違い
実は単にA→C→B→Dとしたいだけであれば、↓と書くだけでもOKです。
task a << { println "A" } task b(dependsOn: ["a", "c"]) << { println "B" } task c(dependsOn:"a") << { println "C" } task d(dependsOn: ["b", "c"]) << { println "D" }
mustRunAfter
との違いは、タスクbを実行してみると見えてきます。
一つ前のサンプルだと:
$ gradle b :a A :c C :b B BUILD SUCCESSFUL Total time: 0.723 secs
となります。 bの依存先にcが入ってるので実行タスクのグラフにcが登場するのですね。
さて、mustRunAfter
を使った先ほどと同じこの設定で実行してみると...
task a << { println "A" } task b(dependsOn: "a") { mustRunAfter "c" doLast { println "B" } } task c(dependsOn:"a") << { println "C" } task d(dependsOn: ["b", "c"]) << { println "D" }
実行結果:
$ gradle b :a A :b B BUILD SUCCESSFUL Total time: 1.745 secs
となります。
mustRunAfter
は依存関係の指定ではなく、実行タスクのグラフ上にそのタスクが存在した場合に単にその順序関係を指定しているだけなので、この場合にはタスクcは登場しないのです!
実際どういう場面で使うの?
ふもさんのサンプルがわかりやすそうです。
task clean << { println 'cleaning.' } task build << { println 'building.' } build.doLast { println 'finish!' } task local(dependsOn: [clean, build]) << { println 'local debug.' }
これを実行するとこうなります:
$ gradle local :build building. finish! :clean cleaning. :local local debug. BUILD SUCCESSFUL Total time: 1.638 secs
build
してからclean
するとかso crazyですね。ちょっと何言ってるか分からないです。
ここで実行順序制御にdependsOn
を使おうとすると、
task clean << { println 'cleaning.' } task build(dependsOn: 'clean') << { println 'building.' } build.doLast { println 'finish!' } task local(dependsOn: [clean, build]) << { println 'local debug.' }
実行結果:
$ gradle local :clean cleaning. :build building. finish! :local local debug. BUILD SUCCESSFUL Total time: 1.605 secs
うまくいった!?ようにみえますが、clean
せずに差分ビルドさせようとbuild
タスクを指定すると...
$ gradle build :clean cleaning. :build building. finish! BUILD SUCCESSFUL Total time: 0.755 secs
clean
まで発動してしまいました!!これでは毎回フルビルドになってしまい、開発速度が上がりません。
そこで、mustRunAfter
の出番です。
build.gradle:
task clean << { println 'cleaning.' } task build { mustRunAfter 'clean' doLast { println 'building.' } } build.doLast { println 'finish!' } task local(dependsOn: [clean, build]) << { println 'local debug.' }
実行結果:
$ gradle local :clean cleaning. :build building. finish! :local local debug. BUILD SUCCESSFUL Total time: 0.722 secs
$ gradle build :build building. finish! BUILD SUCCESSFUL Total time: 1.58 secs
素晴らしい!期待通りの結果です。
というわけで、依存関係のdependsOn
と、実行順序の微調整のmustRunAfter
をうまく使い分けましょう。