Gradleのタスクの依存関係・実行順序を定義する

2週間ぶりですが前回のつづきです。 

kdnakt.hatenablog.com

 

docs.gradle.org

 

 

[dependsOn()]

タスクへの依存関係を定義する方法としてdependsOn()がある。

これまでに学んできたところでは、dependsOn()にタスクを渡す方法としては以下のものがあった。

  • タスク名を文字列で指定する
tasks.register("taskY") { println("taskY") }
tasks.register("taskX") {
    dependsOn("taskY")
}
  • 委譲プロパティを利用して定義したタスクを渡す
val taskY by tasks.registering {
    doLast { println("taskY") }
}
tasks.register("taskX") {
    dependsOn(taskY)
}
tasks.register
  • tasksコレクションから型指定で取得する
tasks.register("taskX") {
    dependsOn(tasks.withType<Copy>())
}

 

[プロバイダからタスクを取得する]

これに加えて、プロバイダからタスクを取得して、dependsOn()に渡すこともできる。

val taskX by tasks.registering {
	doLast {
		println("taskX")
	}
}

taskX {
	dependsOn(provider {
	    // 「lib」という名前で始まるタスクのみ取得
		tasks.filter { task -> task.name.startsWith("lib") }
	})
}

tasks.register("lib1") {
	doLast {
		println("lib1")
	}
}

tasks.register("lib2") {
	doLast {
		println("lib2")
	}
}

tasks.register("notALib") {
	doLast {
		println("notALib")
	}
}

 

上記の実行結果は以下のようになる。

$ gradle -q taskX
lib1
lib2
taskX

 

ここで使われているproviderはどうやらこのクラスらしい。

gradle.github.io

何かしらの値を提供するインターフェース、ということらしい。

典型的な使い方は、エクステンションからタスクに値を渡すほか、複雑な計算を遅延させて、タスクが実行され必要になるときに値を渡すこともできるようだ。

 

[タスクの実行順序を指定する]

Gradleのタスクは、gradle taskX taskYのように複数のタスクをまとめて実行できる。

その際、mustRunAfter()またはshouldRunAfter()を利用することで、実行順序を指定できる。この場合、実行順序を指定するだけなので、タスク間に依存関係はなく、従ってそれぞれのタスクを独立して実行することが可能だ。

val taskX by tasks.registering {
    doLast {
        println("taskX")
    }
}
val taskY by tasks.registering {
    doLast {
        println("taskY")
    }
}
taskY {
    mustRunAfter(taskX) // taskYは必ずtaskXの後に実行する
}

 

上記のコードを実行すると以下のようになる。

$ gradle -q taskY taskX
taskX
taskY

 

shouldRunAfter()の場合は、以下の2つの場合に無視されるので、厳密な実行順序が必要ない場合に利用するとよい。

  • 実行順序の循環が発生する場合
  • タスクが並列実行され、全てのタスクの依存関係が満たされている場合

 

循環が発生するのは以下のような場合である。このコードでは、taskXを実行する前にtaskYが実行され、taskYの前にtaskZが実行される必要があり、かつ、taskZはtaksXのあとに実行されるべし、となっておりtaskXが先かtaskZが先かという問題が生じてしまっている。

val taskX by tasks.registering {
    doLast {
        println("taskX")
    }
}
val taskY by tasks.registering {
    doLast {
        println("taskY")
    }
}
val taskZ by tasks.registering {
    doLast {
        println("taskZ")
    }
}
taskX { dependsOn(taskY) }
taskY { dependsOn(taskZ) }
taskZ { shouldRunAfter(taskX) }

これを実行すると以下のようになる。

$ gradle -q taskX
taskZ // shouldRunAfterは無視される
taskY
taskX

 

上記のshouldRunAfter()を、mustRunAfter()に変更してtaskXを実行すると、以下のよう依存関係が循環し、ビルドが失敗となる。

$ gradle -q taskX

FAILURE: Build failed with an exception.

* What went wrong:
Circular dependency between the following tasks:
:taskX
\--- :taskY
     \--- :taskZ
          \--- :taskX (*)

(*) - details omitted (listed previously)


* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.

* Get more help at https://help.gradle.org

BUILD FAILED in 672ms

 

[まとめ]

  • タスクの依存関係を追加する方法として、provider経由で処理をタスク実行時まで遅延させられる
  • タスクの実行順序指定にmustRunAfter()とshouldRunAfter()が利用できる
  • タスクの依存関係と実行順序で循環参照が発生するとビルドが失敗する
  • サンプルコードは以下のリポジトリにまとめてある

github.com