finalizedBy tasks runs even though the original task is UP-TO-DATE

gradle runs a task specified in “finalizedBy”, even though the original task is UP-TO-DATE!
That seems contrary to the docs at
https://docs.gradle.org/current/userguide/more_about_tasks.html#sec:finalizer_tasks
where it says

On the other hand, finalizer tasks are not executed if the finalized task didn’t do any work, for example if it is considered up to date or if a dependent task fails.

For testing:
Using gradle 4.10.2:
mkdir testapp
cd testapp
gradlew init --type=application
Then add this to the build.gradle:

check.finalizedBy("finalizeCheck")
task finalizeCheck {
    doLast {
        logger.warn('check has run')
    }
}

Now every execution of “gradlew build” will print “check has run” even if no files have been changed in between.

The build output (when started with “-i”) contains

Task :check UP-TO-DATE
Skipping task ‘:check’ as it has no actions.
:check (Thread[Task worker for ‘:’,5,main]) completed. Took 0.0 secs.
:finalizeCheck (Thread[Task worker for ‘:’ Thread 7,5,main]) started.

Task :finalizeCheck
Task ‘:finalizeCheck’ is not up-to-date because:
Task has not declared any outputs despite executing actions.
check has run
:finalizeCheck (Thread[Task worker for ‘:’ Thread 7,5,main]) completed. Took 0.0 secs.

The important detail is:

Gradle cannot determine whether your finalizeCheck task is up-to-date. You either have to define a task output or a custom up-to-date check (e.g. via onlyIf).

Holger, I understand your answer and yes, if the “finalizeCheck” task would be up-to-date itself, then it would not be executed.

Still that does not make any sense in the context of the “finalizedBy” and makes the feature completely useless.
If I want to have a task that always runs when “check” is executed, I can just make check depend on my task - or in this case, just add the “doLast” to “check” itself.

And it still contradicts the documentation that says

On the other hand, finalizer tasks are not executed if the finalized task didn’t do any work, for example if it is considered up to date or if a dependent task fails.

Note that it does not say that

“finalizer tasks are not executed if they are up-to-date themselves and the finalized task didn’t do any work, for example if it is considered up to date or if a dependent task fails.”

So if the “finalizedBy” cannot be used to execute another task if-and-only-if a task has been executed, how can that be achieved by other means?

Hi Stephen, you are right. The documentation implies another behavior.
However the specific feature of finalizedBy from my point of view is that you can adapt the task chain without adapting one of the involved tasks (the finalized ones as well as depending ones).

Regarding your last point. To make sure the finalizer tasks runs only in case the finalized task run you can connect their upToDate states:

check.finalizedBy("finalizeCheck")
task finalizeCheck {
	outputs.upToDateWhen { check.state.upToDate }
    doLast {
        logger.warn('check has run')
    }
}
1 Like

Thanks a lot - that works nicely.
You can see my actual use case in my next question at Challenge: Seamless execution of both JUnit and TestNG tests?
Unfortunately another problem remains to be solved, feel free to take a look :grin: