Cross project task dependencies ordering screws up finalizers

I have a situation where in a multi project build, I try to make sure that similar tasks run after each other. For example, I have integTest, and if project A depends on project B, then I want to make sure I run project B’s integTest before project A (so that I know if project B is broken before even trying project A). I set up these cross project dependencies, but when integTest has a finalizer, the ordering is screwed up.

Below is a trivial example build:


task integTest {
  mustRunAfter ':b:integTest'


task shutdown {}
task integTest {
  finalizedBy shutdown


include 'a', 'b'

Now running gradle integTest -m produces:

:b:integTest SKIPPED
:a:integTest SKIPPED
:b:shutdown SKIPPED

Notice that shutdown for b happens after a’s integ tests have run. This becomes even more problematic when both project’s integ tests have resources to shutdown (ie they both have a shutdown finalizer), and those integration tests rely on share resources (for example, both want to run on the same port, so :a:integTest fails to bind because b’s integ test was not shutdown).

Note that this only happens when the ordering enforced by the mustRunAfter is different than the natural alphabetic ordering gradle uses across projects. ie if you reverse the setup (switch a and b) then the finalizer runs correctly.

Thanks for the reproducible example.

There’s not an ordering relationship between :a:integTest and :b:shutdown, so the task executor doesn’t try to order them in a particular way. :b:integTest is finalized by :b:shutdown, which just means that it will run and run after :b:integTest.

It would be nice if we had a way to say “there’s a resource” (e.g., a port) and “this task, that task and this other task uses it” and “only one can use it at a time” instead of configuring explicit task ordering. We don’t have that yet, but I think you can try something like this:

allprojects {
    task integTest << { println "Run: " + path }
    integTest {
        // tasks with these names should not run at the same time and should run after each other
        def exclusiveTasks = [ 'integTest', 'shutdown' ]
        rootProject.allprojects {
            shouldRunAfter {
                tasks.matching {
           in exclusiveTasks

project(":a") {

project(":b") {
    task shutdown << { println "shutting down" }
    integTest {
        finalizedBy "shutdown"

This says for every integTest in all projects, set it up so that it must run after integTests in other projects and any shutdown tasks in other projects.

Thanks for looking @sterling. I understand what you are saying (and I see how it is implemented), that finalizedBy are kind of the inverse of dependsOn (run a task afterwards). Your suggestion about “shared resources” also makes sense. I would argue though that the entire point of finalizedBy to seems to be cleaning up resources, so to not have finalizedBy run immediately after a task seems counter intuitive.

I solved the problem essentially how you suggested, just with a mustRunAfter on the shutdown task, as the shutdown task will only be present in the task graph when the integTask is there, and shutdown will already be run after integTest.