1. Introduction
Gradle is a very popular build tool, which is often appreciated for the highly-customizable build process.
Today we’re going to show how to create custom Gradle plugins, that will allow us to modify the build process beyond what we can achieve with the standard configuration.
2. Plugin Source Location
We can place our code in a few different locations. All of them have some advantages and disadvantages.
2.1. Build Script
We can simply put the source code of our plugin inside the build script itself. This will give us automatic compilation and inclusion of the plugin.
It’s very simple, however, our plugin won’t be visible outside of the build script. Because of that, we can’t reuse it in other build scripts.
2.2. BuildSrc Folder
Another possibility that we can use is placing the source code of our plugin in the buildSrc/src/main/java folder.
When you run Gradle, it’ll check for the existence of the buildSrc folder. If that exists, Gradle will automatically build and include our plugin.
This will give us the possibility to share our plugin between various build scripts, but we still won’t be able to use it in other projects.
2.3. Standalone Project
Finally, we can create our plugin as a separate project which makes the plugin fully reusable in various projects.
However, to use it in an external project, we’ll need to bundle it in a jar file and add to a project.
3. Our First Plugin
Let’s start with the basics – every Gradle Plugin must implement the com.gradle.api.Plugin interface.**
The interface is generic, so we can parametrize it with various parameter types. Usually, the parameter type is org.gradle.api.Project.
However, we can use different type parameters so that the plugin is applied in different lifetime phases:
- using org.gradle.api.Settings will result in applying the plugin to a settings script
- using org.gradle.api.Gradle will result in applying the plugin to an initialization script
The simplest plugin we can create is a hello world application:
public class GreetingPlugin implements Plugin<Project> {
@Override
public void apply(Project project) {
project.task("hello")
.doLast(task -> System.out.println("Hello Gradle!"));
}
}
We can now apply it by adding a line inside our build script:
apply plugin: GreetingPlugin
Now, after calling gradle hello, we’ll see “Hello Gradle” message in logs.
4. Plugin Configuration
Most plugins will need an access to an external configuration from the build script.
We can do that by using extension objects:
public class GreetingPluginExtension {
private String greeter = "Baeldung";
private String message = "Message from the plugin!"
// standard getters and setters
}
Let’s now add the new extension object to our plugin class:
@Override
public void apply(Project project) {
GreetingPluginExtension extension = project.getExtensions()
.create("greeting", GreetingPluginExtension.class);
project.task("hello")
.doLast(task -> {
System.out.println(
"Hello, " + extension.getGreeter());
System.out.println(
"I have a message for You: " + extension.getMessage());
});
}
Now, when we call gradle hello, we’ll see the default message defined in our GreetingPluginExtension.
But since we have created the extension, we can use a closure to do that inside the build script:
greeting {
greeter = "Stranger"
message = "Message from the build script"
}
5. Standalone Plugin Project
For creating a standalone Gradle plugins, we need to do a little more work.
5.1. Setup
Firstly, we need to import the Gradle API dependency – which is quite straightforward:
dependencies {
compile gradleApi()
}
Note that doing the same in Maven requires gradle-tooling-api dependency – from the Gradle repository:
<dependencies>
<dependency>
<groupId>org.gradle</groupId>
<artifactId>gradle-tooling-api</artifactId>
<version>3.0</version>
</dependency>
<dependency>
<groupId>org.gradle</groupId>
<artifactId>gradle-core</artifactId>
<version>3.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
<repositories>
<repository>
<id>repo.gradle.org</id>
<url>https://repo.gradle.org/gradle/libs-releases-local/</url>
</repository>
</repositories>
5.2. Wiring the Plugin
To allow Gradle to find the implementation of our standalone plugin, we need to create the properties file in the src/main/resources/META-INF/gradle-plugins.
The resource file needs to have a name that matches the plugin id. So if our plugin has an id of org.baeldung.greeting, the exact path of the file would be META-INF/gradle-plugins/org.baeldung.greeting.properties.
Next, we can define the implementation class of the plugin:
implementation-class=org.gradle.GreetingPlugin
The implementation-class should be equal to the full package name of our plugin class.
5.3. Creating the Plugin ID
There are some rules and conventions that plugin ID must follow in Gradle. Most of them are similar to package name rules in Java:
- They can contain only alphanumeric characters, “.” and “-“
- The id has to have at least one “.” separating the domain name from the plugin name
- Namespaces org.gradle and com.gradleware are restricted
- An id cannot start or end with “.”
- No two or more consecutive “.” characters are allowed
Finally, there’s a convention that plugin Id should be a lower case name that follows reverse domain name convention.
The main difference between Java package names and Gradle plugin names is that the package name is usually more detailed than the plugin ID.
5.4. Publishing Plugin
When we want to publish our plugin to be able to reuse it in external projects, we have two ways of achieving that.
Firstly, we can publish our plugin JAR to an external repository like Maven or Ivy.
Alternatively, we can use the Gradle Plugin Portal. This will allow our plugin to be accessible by wide Gradle Community. More on publishing projects to Gradle repository can be found in Gradle Plugin Portal Documentation.
5.5. Java Gradle Development Plugin
When we’re writing our plugins in Java, we can benefit from the Java Gradle Development Plugin.
This will automatically compile and add gradleApi() dependencies. It will also perform plugin metadata validation as a part of the gradle jar task.
We can add plugin by adding following block to our build script:
plugins {
id 'java-gradle-plugin'
}
6. Testing Plugins
To test that our plugin works properly and it’s properly applied to the Project, we can use org.gradle.testfixtures.ProjectBuilder to create an instance of the Project.
We can then check if the plugin was applied and proper tasks are present in our Project instance. We can use standard JUnit tests to do that:
@Test
public void greetingTest(){
Project project = ProjectBuilder.builder().build();
project.getPluginManager().apply("com.baeldung.greeting");
assertTrue(project.getPluginManager()
.hasPlugin("com.baeldung.greeting"));
assertNotNull(project.getTasks().getByName("hello"));
}
7. Summary
In this article, we’ve shown the basics of writing custom plugins in Gradle. To go more in-depth into plugin creation, have a look at the Gradle Documentation.
And, as always, all the code samples can be found over on Github.