I have a gradle Copy task that copies assets from the project directory into the build directory:
tasks.register("copyAssets", Copy) {
def fromDir = "${project.projectDir.toString()}/../assets"
def toDir = "${project.buildDir.toString()}/assets"
println "Copying assets"
println " from $fromDir"
println " into $toDir"
from fromDir
into toDir
}
build.dependsOn copyAssets
run.dependsOn copyAssets
This works, but somehow it not only runs on build and run, but also on clean.
If I remove both lines with dependsOn
, it doesn't run on build, run, or clean. But as soon as I put the line with build.dependsOn
in, the task runs on build, run, and clean. If, on the other hand, I remove build.dependsOn
and put in run.dependsOn
instead, the outcome is the same: The task runs on build, run, and clean.
How does dependsOn
work? How can I make it to run on build and run, but not on clean?
I use gradle wrapper, and it's a multi-module project, i.e.
./gradlew main:clean
./gradlew main:build
./gradlew main:run
The task is in the main module only, not inside the top-level build.gradle
.
CodePudding user response:
Interesting question, as it mixes two of the most common mistakes or confusion made when using Gradle:
- confusion between task configuration and execution phases
- misuse of task configuration avoidance feature.
Task configuration vs execution phase
Lots of SO questions are dealing with this topic , just search "configuration vs execution phase" for answers explaining this in details. One example : Gradle always does println from any task
In your example: you say that copyAssets
task is also executed when running clean
task but in fact it's not executed, it's just configured.
Executing gradle build
:
> Configure project :sample
Copying assets <==== the println are executed, because they are part of the Configuration phase
from src
into /path/sample/build/
> Task :sample:assemble UP-TO-DATE
> Task :sample:check UP-TO-DATE
> Task :sample:copyAssets <<<===== task copyAssets is executed as expected
> Task :sample:build
Executing gradle clean
:
> Configure project :sample
Copying assets <==== the println are still executed, because they are part of the Configuration phase
from src
into /path/sample/build/
> Task :sample:clean <<<===== task copyAssets is NOT executed, only clean task is.
You can check that when calling clean
, you assets will not be copied, you will just see the println in console.
Note that you will also see these println when executing any other task ( e.g. gradle help
, gradle tasks
...)
Task configuration avoidance issue
You are declaring your task using the register
API,
which enables Task configuraiton avoidance feature.
So, in theory, the copyAssets
should be configured only if it must be executed (=> only when you invoke build
or run
tasks in your example)
So why is it configured (but not executed) when executing clean
in your example ?
This is due to the way you are declaring the tasks dependencies , as explained in the Task configuration avoidance pitfalls section/
build.dependsOn copyAssets
=> this will eagerly create and configure the build
task, and in cascade will also create and configure the dependent copyAssets
task.
There are several ways to fix this, given in the link above. One example:
// replace
build.dependsOn copyAssets
// with:
build.dependsOn tasks.named("copyAssets")
CodePudding user response:
Ah! you have put in the default task closure - the steps happen at task initialisation.
tasks.register("copyAssets", Copy) {
doLast() {
def fromDir = "${project.projectDir.toString()}/../assets"
def toDir = "${project.buildDir.toString()}/assets"
println "Copying assets"
println " from $fromDir"
println " into $toDir"
from fromDir
into toDir
}
}
read - https://docs.gradle.org/current/userguide/more_about_tasks.html#header