The Gradle documentation, chapter "Avoiding traps", has an example "Configuration and execution phase". Link
It is important to keep in mind that Gradle has a distinct configuration and execution phase (see Build Lifecycle).
def classesDir = file('build/classes')
classesDir.mkdirs()
tasks.register('clean', Delete) {
delete 'build'
}
tasks.register('compile') {
dependsOn 'clean'
doLast {
if (!classesDir.isDirectory()) {
println 'The class directory does not exist. I can not operate'
// do something
}
// do something
}
}
There are 2 tasks, compile
and clean
. The build directory is created as part of the project. The clean
task contains a command to delete the build directory. The compile
task contains a check, whether a subdirectory of the build directory exists.
At first this seemed to be an easy-to-understand example: a directory isn't available, because it gets deleted earlier. But then i noticed something that i can't understand.
As the creation of the directory happens during the configuration phase, the clean task removes the directory during the execution phase.
I understand the first part, as the build directory is created during the configuration phase of the project. What i don't understand is the second part. The documentation says this happens during the execution phase. But the removal of the directory isn't wrapped in a doFirst
or doLast
block, so this part of the task should happen during the configuration phase.
Trying to understand, round 1
To get a better overview, i added more println
and removed the comment-only lines.
def classesDir = file('build/classes')
classesDir.mkdirs()
println "configuration phase of project";
tasks.register('clean', Delete) {
println "step 1 in configuration phase of " getName();
delete 'build'
println "step 3 in configuration phase of " getName();
}
tasks.register('compile') {
println "configuration phase of " getName();
dependsOn 'clean'
doLast {
if (!classesDir.isDirectory()) {
println 'The class directory does not exist. I can not operate'
}
}
}
Starting gradle via ./gradlew compile --info
provided the following output.
configuration phase of project
All projects evaluated.
configuration phase of compile
Selected primary task 'compile' from project :
step 1 in configuration phase of clean
step 3 in configuration phase of clean
Tasks to be executed: [task ':clean', task ':compile']
Tasks that were excluded: []
:clean (Thread[Execution worker for ':',5,main]) started.
> Task :clean
Caching disabled for task ':clean' because:
Build cache is disabled
Task ':clean' is not up-to-date because:
Task has not declared any outputs despite executing actions.
:clean (Thread[Execution worker for ':',5,main]) completed. Took 0.002 secs.
:compile (Thread[Execution worker for ':',5,main]) started.
> Task :compile
Caching disabled for task ':compile' because:
Build cache is disabled
Task ':compile' is not up-to-date because:
Task has not declared any outputs despite executing actions.
The class directory does not exist. I can not operate
:compile (Thread[Execution worker for ':',5,main]) completed. Took 0.0 secs.
I didn't get more information from this output, regarding my question. Why is the directory deleted during the execution phase, despite not being wrapped in a doFirst
or doLast
block? Does this have something to do with Delete?
Trying to understand, round 2
I wanted to believe this is a mistake in the documentation, but after modifying the code a little bit more, this doesn't fit.
def classesDir = file('build/classes');
classesDir.mkdirs();
tasks.register('clean', Delete) {
doFirst {
delete 'build';
println "[debug-print] (" getName() ") build directory exists in doFirst: " file("build").exists();
}
doLast {
println "[debug-print] (" getName() ") build directory exists in doLast: " file("build").exists();
}
}
tasks.register('compile') {
dependsOn 'clean'
doFirst {
println "[debug-print] (" getName() ") build directory exists in doFirst: " file("build").exists();
}
doLast {
println "[debug-print] (" getName() ") build directory exists in doLast: " file("build").exists();
}
}
The output of ./gradlew compile
is:
> Task :clean
[debug-print] (clean) build directory exists in doFirst: true
[debug-print] (clean) build directory exists in doLast: false
> Task :compile
[debug-print] (compile) build directory exists in doFirst: false
[debug-print] (compile) build directory exists in doLast: false
This looks like delete
isn't actually deleting the directory, or at least not right away. This matches the javadoc of Delete:
Adds some files to be deleted by this task.
But then again, i don't understand when the actual directory removal happens. During the execution phase, but not in doFirst
and not in doLast
? This doesn't match my understanding of the execution phase.
CodePudding user response:
tasks.register('clean', Delete) {
println "step 1 in configuration phase of " getName();
delete 'build'
println "step 3 in configuration phase of " getName();
}
This one configures the Delete
task via Delete.delete()
, which queues what to delete during configuration, but the actual action of the task does not happen until the execution phase.
tasks.register('clean', Delete) {
doFirst {
delete 'build';
println "[debug-print] (" getName() ") build directory exists in doFirst: " file("build").exists();
}
doLast {
println "[debug-print] (" getName() ") build directory exists in doLast: " file("build").exists();
}
}
This one appears to be queueing the directory to delete via Delete.delete()
during the execution phase (modifying the task configuration on-the-fly), before the task action executes. It seems this way because doFirst { }
runs during execution, and the directory is gone during doLast { }
.