Loading Gradle plugins in 2019

Martin Bonnin
Nov 26 · 4 min read

Three different ways to load your Gradle plugins and a few ways to apply them.

Elephant by designerpoint

As an Android developer, I use a bunch of Gradle plugins: the Android Gradle plugin and a few other helpful ones too like Kotlin, Fabric, Dokka, SqlDelight, etc..

Most of the times, I copy/paste the instructions from the installation notes. For an example:

The traditional way of adding a Gradle plugin to your build

While this still works, there are nowadays a few other options that give better tooling or enable new features and it’s safe to say the above shouldn’t be recommended anymore.

TLDR;

If you’re in a hurry and you use buildSrc, do this:

If you’re in a hurry and you don’t use buildSrc:

If you have a few minutes, read on 📚!

Anatomy of a Gradle plugin

A plugin is a simple jar file containing JVM class files. It’s like a java library or executable jar except the entry point, instead of being main() is a class that can be applied to a Project :

Another important thing about this jar file is that it needs to contain a special resource file named com.example.greeting.properties in the META-INF.gradle-plugins folder. com.example.greeting is the pluginId and allows to lookup the entry point class (com.example.GreetingPlugin here).

// src/main/resources/META-INF.gradle-plugins/com.example.greeting.propertiesimplementation-class=com.example.GreetingPlugin

In the case of the Android plugin:

  • classpath("com.android.tools.build:gradle:3.5.2") tells Gradle to download the jar from the Google maven repo and load it in the build in the classpath.
  • apply plugin: 'com.android.application' tells gradle to look for a com.android.application.properties resource file, create a new instance of AppPlugin and apply it to the project.

Two pluginIds can point to the same artifact and class.For an example, the below are mostly equivalent:

  • apply plugin:kotlin
  • apply plugin: org.jetbrains.kotlin.jvm

Also, an artifact can contain several pluginIds. For an example, the Android artifact defines plugins for:

  • com.android.application
  • com.android.library
  • com.android.instantapp
  • etc..

Applying plugins: the plugins {} block

The plugin {} block is now the official way to apply plugins to your build. From the documentation:

This allows Gradle to do smart things such as:
* Optimize the loading and reuse of plugin classes
* Allow different plugins to use different versions of dependencies.
* Provide editors detailed information about the potential properties and values in the buildscript for editing assistance.

In particular, this allows the Kotlin DSL to generate typesafe accessors for your build scripts.

Applying plugins with the plugins {} block is as easy as specifying their pluginId:

plugins {
id("org.jetbrains.kotlin.android").version("1.3.60")
}

You can use the plugin block in your module build script, in the root build script but the versions should all match. You cannot load two plugins with different versions. To do this, load all your plugins in your root build.gradle file withtout applying them:

// build.gradle.kts
plugins {
// load the module, do not apply it
id("org.jetbrains.kotlin.android").version("1.3.60").apply(false)
}
// module/build.gradle.kts
plugins {
// this will apply the plugin
id("org.jetbrains.kotlin.android")
}

Latest versions of gradle even allow you to define your plugins in a centralized place in your settings.gradle.kts:

pluginManagement {
plugins {
id("org.jetbrains.kotlin.android").version("1.3.60").apply(false)
}
}

Loading the plugin artifact in the classpath

The plugin {} block works well if the artifact containing your pluginId is already in the classpath. Most of the time, it’s not the case so you need a way to retrieve and load the appropriate maven artifact. Gradle introduced marker artifacts to lookup the implementation artifact for a given pluginId. Just like the com.example.properties resource file points to the plugin implementation, the com.example:com.example.gradle.plugin artifacts points to the actual artifact by depending on it in its pom file.

For an example the Kotlin marker artifact contains this pom:

This tells gradle that org.jetbrains.kotlin.jvmcan be found in the artifact org.jetbrains.kotlin:kotlin-gradle-plugin. If your plugin is not on the Gradle Plugin Portal, you can still use the plugin {} block by adding a repository to the pluginManagement {} block:

And if your plugin does not have a marker artifact, you can also tell Gradle where to look for the artifact:

Using this, you have a centralized way to declare all the plugins your build uses. You can then apply them independently in different modules:

This works well as long as you’re not using buildSrc

Using buildSrc

If your build grows, you can use buildSrc to add some build logic. buildSrc is an included build whose classes are automatically added to the build classpath. That also means that dependencies declared in buildSrc are also added to your build classpath.

The following file will add the android and sqldelight plugins to the build classpath so you can later on apply them from a plugins {} block.

This works well except that buildSrc is loaded first and whatever dependencies you declare in your other build scripts will not be loaded as described in this github issue. That can lead to very weird issues. To prevent that from happening, load all your plugins from buildSrc. This way, you’re guaranteed that you know what version is going to be used.

Conclusion

That’s a lot of different ways to accomplish basically the same thing. Using buildSrc seems to be a trend these days so putting all the plugin dependencies there will definitely avoid some troubles.

If not using buildSrc, the marker artifacts allow for a very concise and elegant syntax. Few projects use them at the moment but it is changing fast!

Happy building !

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade