Evaluate your afterEvaluate
Posted on 2022-12-09

You’ve probably seen dozens of Stack Overflow answers to Gradle problems to simply adding afterEvaluate {} to your build. Sometimes, this fixes your apparent issue, you commit it and move along. I’m here to tell you that you should only ever use afterEvaluate {} as the last resort as most likely you are just signing yourself up for future headaches.

According to Gradle docs afterEvaluate “adds an action to call immediately after this project is evaluated. […] Actions passed to this method execute in the same order they were passed. […] If you call this method within an afterEvaluate action, the passed action executes after all previously added afterEvaluate actions finish executing.” This works great if you only ever have a single caller to afterEvaluate. Sadly, most project have many plugins and custom build logic. Since afterEvaluate actions depend on when they are scheduled, that means it also depends on the order in which plugins were applied. This puts you in a world of execution order pain.

Let’s look at a specific example of where you might see it used. Let’s say you have a task that wants to output something to project’s build directory. You might start with

myTask.configure {
  outputFile = File(project.buildDir, "myFile.txt")
}

Let’s say you then apply a plugin that does project.buildDir = ..., so now depending on your plugin application order you might get a stale value. Stack Overflow jumps in to help here to put you to

afterEvaluate {
  myTask.configure {
    outputFile = File(project.buildDir, "myFile.txt")
  }
}

Ok, now we are good? What if that plugin then does afterEvaluate { project.buildDir = ... }. and applies after your plugin. 😮‍💨

What is much better is to avoid using afterEvaluate in the first place and only use the value when you need it, which is exactly why Gradle has properties.

myTask.configure {
  outputFile.set(project.layout.buildDirectory.file("myFile.txt"))
}

This is just a tiny example of many. Pretty much every use of afterEvaluate is suspect, so you should evaluate if you should be using some more robust alternative.