Gradleでなんらかのタスクを作って、実行してみると遅い時がありますね。
まあ、大量のファイルをコピーしてzipで固めるなんて場合は、遅いのは仕方ないです。しかし、clean
とかtasks
とかコピーと関係のないタスクまで遅くなるようなことがあれば、設定フェーズでなんらかの重たい処理が必要もないのに走っていることがだいたい原因だったりします。
例えば、次のようなタスクを作ることにします。
- payara-microのjarをコピーしてzipにして
build/payara
ディレクトリーに保存する - jarの中のファイルのリストをテキストファイルに書き出す
- 結構大量のファイルをコピーするタスクなので、2.の結果のテキストファイルを前回の結果と比較して、同じものであれば
UP-TO-DATE
とする
関係ないタスクまで遅くなるビルドスクリプト
何も考えずに上記のタスクを書くと、このようになります。
apply plugin: 'base' ext { payaraVersion = '4.1.153' } repositories { mavenCentral() } configuration { payara } dependencies { payara "fish.payara.extras:payara-micro:${payaraVersion}" } task copyPayara(type: Zip) { // zipファイルの保存先ディレクトリー destinationDir = file("${buildDir}/payara") // zipファイルの中身のリスト def listFile = file("${buildDir}/payara/list.txt") // コピーするファイルの定義 def list = configurations.payara.findAll{ !it.directory }.collect{ zipTree(it) } def contents = list.files.collect{it.absolutePath}.join('\n') // zipタスクの実行内容 from list doLast { listFile.write(contents, 'UTF-8') } // UP-TO-DATEの設定 outputs.upToDateWhen { listFile.exists() && listFile.text == contents } }
これがどれだけ遅いか試してみましょう。clean
タスクを3回連続で実行します。
$ gradle --daemon clean :clean BUILD SUCCESSFUL Total time: 18.971 secs $ gradle --daemon clean :clean BUILD SUCCESSFUL Total time: 18.757 secs $ gradle --daemon clean :clean BUILD SUCCESSFUL Total time: 20.131 secs
たかだかディレクトリーを消すだけのタスクに18秒もかかるのは遅すぎますね。
さらに注目して欲しいのが、clean
タスクが毎回UP-TO-DATE
ではないという点です。
これは、プロジェクトを評価する際に必ずbuild
ディレクトリーが生成されているということが原因です。
具体的にはスクリプトの次の部分が問題です。
// コピーするファイルの定義 def list = configurations.payara.findAll{ !it.directory }.collect{ zipTree(it) } def contents = list.files.collect{it.absolutePath}.join('\n')
設定フェーズでファイルの一覧を構築する際に一度jarをアーカイブから通常のツリーに戻しているために、時間がかかります。
かといって、タスクこのcopyPayara
というタスクを実行するたびに同じことをしてしまうのも時間がかかってしまうのは嫌なので、UP-TO-DATE
の設定は残したい…
できるだけ必要のないときには、この部分は動かしたくないという時に用いるのがTaskExecutionGraphです。
TaskExecutionGraph
を用いた速いビルドスクリプト
TaskExecutionGraph
は実際に実行されるタスクの管理を行うクラスで、このインスタンスに問い合わせることでこれから実行されるタスクが何であるかを取得することができます。。TaskExecutionGraph
はgradle
プロパティのtaskGraph
プロパティから取得できます。そして、TaskExecutionGraph
のwhenReady(Closure)
にて時間のかかる処理を記述します。whenReady
はプロジェクトの初期化・設定フェーズが完了して、実行フェーズに移行する直前に起動されます。whenReady
にはTaskExecutionGraph
自体が引数として渡されます。
// copyPayaraは保存先の設定だけにする task copyPayara(type: Zip) { destinationDir = file("${buildDir}/payara") } gradle.taskGraph.whenReady {graph -> def cpTask = tasks.copyPayara // タスクグラフがcopyPayaraタスクを行う場合のみ追加の設定を行う if(graph.hasTask(cpTask)) { def listFile = file("${buildDir}/payara/list.txt") def list = configurations.payara.findAll{ !it.directory }.collect{ zipTree(it) } def contents = list.files.collect{it.absolutePath}.join('\n') cpTask.from list cpTask.doLast { listFile.write(contents, 'UTF-8') } cpTask.outputs.upToDateWhen { listFile.exists() && listFile.text == contents } } }
では、このビルドスクリプトで、clean
タスクを3回行ってみます。
$ gradle --daemon clean :clean UP-TO-DATE BUILD SUCCESSFUL Total time: 1.09 secs $ gradle --daemon clean :clean UP-TO-DATE BUILD SUCCESSFUL Total time: 0.758 secs $ gradle --daemon clean :clean UP-TO-DATE BUILD SUCCESSFUL Total time: 0.75 secs
この通り、clean
タスクの実行時間が短くなりましたし、clean
自体も常にUP-TO-DATE
になりました。
もしタスク開始までの時間が遅いビルドがあって、かつclean
やtasks
といった普通のタスクまでもが時間がかかっているようでしたら、ビルドスクリプトを見なおしてみるとよいかもしれません。