This post has been migrated to http://www.thinkcode.se/blog/2015/03/22/a-gradle-plugin-written-in-java
Gradle is a build automation system. You write your build script in Groovy. This is different compared to other build system such as Ant or Maven. They both use xml. Using Groovy instead of xml gives you a lot of benefits. You have an entire programming language at your disposal. This mean that you can easily customize the build behaviour.
If you, however, want to be able to do the same thing in many projects, it may be a good idea to write a plugin that you can refer to from other projects. I will show you, step by step, how to implement a Hello World Gradle plugin.
Setup the Gradle infrastructure
Create a directory and create a file called
build.gradle in it. Add the content below to this new file.
This build script (almost) defines the project.
I start with defining a
version that should be used when the plugin is referred to later.
This is then followed by applying two plugins, the
maven plugins. The Groovy plugin will allow me to write Groovy code. The Maven plugin will give me access to an install task as well as the Java stack.
I want to tie this project to Java 1.7, so I specify the source code compatibility to be 1.7.
The next section defines the dependencies for this project. I have two dependencies in this example but it is obviously possible to have many more if you need them.
The magic is contained in the
gradleApi() dependency. It will give us the things needed to create a Gradle plugin.
The second dependency is the well known testing framework
junit. I use it to test the plugin.
The last thing I do in this example is to define that I want access to the Maven repository as well as my local Maven repository. I use The Central Repository to get access to JUnit. I use the local Maven repository to make this plugin available on my computer for a test project I will show you below.
The last part I want to have control over is the name of the project. It is called artifact id in Maven. It would have been nice to be able to define it in
build.gradle, but that is not possible. Instead, the name will default to the directory where this project lives. If I want, I can define it to something else by setting the property
settings.gradle as I do below.
This is the infrastructure needed.
Create the Plugin
The plugin is defined in a class called
DemoPlugin. My example looks like this:
A Gradle plugin is an extension to Gradle. The extension is defined in the
apply method. It is defined with a name,
demoSetting, and a class
DemoPluginExtension that will hold default values. The default values will be used if the plugin user doesn’t change any values in the configuration section
demoSetting in a build script.
A task is also defined. It is called
demo and the executing class is defined as
This is all we need to create a new extension and a new task. The magic that makes this a Gradle plugin is the existence of a property file in
META-INF/gradle-plugins. The name of the property file will be the name of the plugin to apply in your build script as I will show later.
Let’s create a file called
se.thinkcode.demo.plugin.properties. It will connect the name
se.thinkcode.demo.plugin to the implementation
se.thinkcode.DemoPlugin. This will allow you to apply the plugin
Now it’s time to take a look at the implementation of the two classes used in the
Defining the default value holder
The extension class holds the default values that will be used, if the user of the plugin doesn’t change any settings. It look like this:
It is a plain POJO and very uninteresting in many senses. It is, however, interesting to know that we have now left Gradle land and we are in Java POJO land.
Implementing an actual Task
What about the task then, is it anything special? Our implementation looks like this:
It inherits a
DefaultTask and implements a method with an annotation that defines that this is the method that will be executed when the user calls the task
demo. This is the method that actually does something. In our case, it looks for an extension and delegates to a
HelloWorld class that will create a message that the user will be greeted with. If the plugin user hasn’t added any extension in
build.gradle, an instance of the default extension will be created and the default values will be used.
HelloWorld class could obviously have been skipped in this small case, but I wanted to show you how you can connect your own implementation into a Gradle plugin.
What about testing?
The plugin testing is done using two unit tests written in Groovy. My custom model, HelloWorld, is done using unit tests written in Java.
Testing the plugin can be done like this:
I don’t do much more than verifying that applying the plugin
se.thinkcode.demo.plugin gives me access to a task called
demo that is implemented in an instance of
DemoTask can be done as below::
I verify that I am able to create a task called
demo using a class
DemoTask and that I get a task of the type
These two tests verify the wiring of the plugin. The last thing to do is to verify the actual behaviour. It is done in a regular Java unit test that verifies HelloWorld.
A unit test that verifies that it is possible to return a value given in the constructor. We might have been able to cope without it, but now you know how to incorporate a unit test in this setup.
The files used to build this Gradle plugin
These are all the files I have used to create this plugin:
plugin |-- build.gradle |-- settings.gradle `-- src |-- main | |-- java | | `-- se | | `-- thinkcode | | |-- DemoPlugin.java | | |-- DemoPluginExtension.java | | |-- DemoTask.java | | `-- HelloWorld.java | `-- resources | `-- META-INF | `-- gradle-plugins | `-- se.thinkcode.demo.plugin.properties `-- test |-- groovy | `-- se | `-- thinkcode | |-- DemoPluginTest.groovy | `-- DemoTaskTest.groovy `-- java `-- se `-- thinkcode `-- HelloWorldTest.java
How do you build it?
With all this implemented, how do you actually go about and build the plugin? The answer is, execute
gradle build install
from a command prompt or similar. This will build the project, execute all the tests and finally install it in the local Maven repository.
Next step is to actually use the plugin.
How do you use it?
The last interesting piece is to use the new plugin. It is done from a Gradle project. An example is this:
There are three important parts here.
Firstly, I apply the plugin. That is making sure that Gradle is aware of something called
Secondly I define my own extension, that is changing the message the user will be greeted with. This will override the default values. If you skip this section, the default values will be used instead.
Thirdly I get hold of the dependency that actually implement the plugin. I define that this build script should use the
mavenLocal() repository. This is where the plugin was installed when I executed
install earlier. I also define the name and version of the dependency that contains the implementation,
We recognise the group, name and version from earlier.
gradle tasks show us that there is now a task defined that is called
gradle demo should result in something like this:
:demo Hi from an extension BUILD SUCCESSFUL
demoSetting extension from the consumer project and re-run
gradle demo. The result is expected to be similar to this:
:demo Default Greeting from Gradle BUILD SUCCESSFUL
That it folks, this is one way to build you own Gradle plugin.