Small Note on gradle’s afterEvaluate

Posted by & filed under , .

gradle_logoIf you use gradle and you took the path to write your own gradle plugins (try it, it’s fun!) to make your build process more … “enjoyable”, then this might come in handy one day.

I have worked on a few gradle plugins, some of them inside the Netflix Nebula suite, some of them outside Netflix OSS. And in some of those the discussion came up about why occasionally I use afterEvaluate to perform various tasks.

The docco doesn’t do this too much justice to be fair (see https://docs.gradle.org/current/javadoc/org/gradle/api/Project.html#afterEvaluate(org.gradle.api.Action)) as it reads:

void afterEvaluate(Action<? super Project> action)
Adds an action to execute immediately after this project is evaluated.
Parameters:
action – the action to execute.

Go figure :O What does it mean “after this project is evaluated”? To explain this, I’m going to assume you are building a plugin which uses let’s say the project version (project.version in gradle “speak”). Assume the following build.gradle file to start with:

apply plugin: 'java'
apply plugin: 'application'
//... some other configuration
 
version= '1.2.3'
// ... other stuff

At this point let’s say you are developing a plugin 'my.simple.plugin' — so you go ahead and apply your plugin right after applying the application plugin (I’m assuming here the buildscript classpath and dependencies etc are all set):

apply plugin: 'java'
apply plugin: 'application'
apply pugin: 'my.simple.plugin'
//... some other configuration
 
version= '1.2.3'
// ... other stuff

At this point, surprise! Your plugin will “see” the plugin.version not set! In case you haven’t figured it out, it’s because at the time your plugin (which doesn’t use — yet! — afterEvaluate) kicks in, the version has not been set!

OK, you think, I’m going to move my plugin right after the version is set:

apply plugin: 'java'
apply plugin: 'application'
//... some other configuration
 
version= '1.2.3'
apply pugin: 'my.simple.plugin'
// ... other stuff

This works, and now your project.version will reflect the correct value (“1.2.3”). But then it means all of your plugin users now have to be instructed to always apply the plugin after setting the version — which will quite likely make your plugin less popular (we know that any friction for the developers decreases usage).

So this is where afterEvaluate comes in handy : you can simply apply your plugin in that closure right at the top of the file:

apply plugin: 'java'
apply plugin: 'application'
afterEvaluate {
   apply pugin: 'my.simple.plugin'
}
//... some other configuration
 
version= '1.2.3'
// ... other stuff

And now your plugin will show again the correct version, even though it is applied before the version is set. The thing is your plugin is actually not applied right away — since it’s “behind” an afterEvaluate now, gradle will parse the whole file, will evaluate first all the properties of the project and then will finally apply your plugin (at which point the version will have already been evaluated and set).

Of course, you need to take this one step further, as you don’t want to recommend now to your plugin users to wrap up your plugin all the time in an afterEvaluate block, so instead in your plugin apply() method you should probably do this:

void apply(Project project) {
   project.afterEvaluate {
      // call your main plugin method here
   }
}

At this point regardless where in the build.gradle your plugin is applied you will always have access to the correctly evaluated project.plugin.

Hopefully this explanation helps more than the GroovyDoc of the method.