How to write a custom Gradle plugin in groovy
by Riley MacDonald, December 28, 2017

While working on an embedded software library for Android I noticed several projects (all in their own repositories) had duplicated code in their build.gradle files. I wanted to encapsulate this duplicated code into a single location that could be applied to any interested project. After some investigation I decided to write my own Gradle plugin which I could apply to each project. This would remove the duplicated code completely. Here’s the complete process I followed to accomplish this.

What’s covered in this post

Initialize a new git repository
Switch to new directory to hold the plugin (see my post on using the -c argument with mkdir):

# Create the directory
$ mkdir -c ~/Documents/myGradlePlugin
 
# Initialize the git repo and add the new remote
~/Documents/myGradlePlugin $ git init
~/Documents/myGradlePlugin $ git remote add origin http://your.git.repo.url

Add the Gradle wrapper
The Gradle wrapper isn’t required but it’s best practice, especially if you’re planning to open source the plugin. You’ll need gradle installed on your machine. If you don’t have it follow these instructions.

~/Documents/myGradlePlugin $ gradle wrapper
Starting a Gradle Daemon (subsequent builds will be faster)
 
BUILD SUCCESSFUL in 3s
1 actionable task: 1 executed

Add the groovy plugin file
The plugin (in this case written in groovy) should live in a directory similar to a Java application src/main/package. Let’s add the plugin file with basic functionality now:

# Create the directory for the plugin file
~/Documents/myGradlePlugin $ mkdir -c src/main/groovy/com/example/pluginName/
 
# Create the groovy plugin file
~/Documents/myGradlePlugin/src/main/groovy/com/example/pluginName $ touch PluginFileName.groovy

Open your preferred IDE and begin writing your plugin. Gradle plugins must implement Plugin and import a few classes. Also be sure the package is declared correctly at the top of the file:

1
2
3
4
5
6
7
8
9
10
11
package com.example.pluginName
 
import org.gradle.api.Plugin
import org.gradle.api.Project
 
class PluginFileName implements Plugin<Project> {
  @Override void apply(Project project) {
    // the meat and potatoes of the plugin
    println "PluginFileName: WORKS!"
  }
}

The apply() method is invoked when the plugin is applied (by the client) using apply plugin:. Within the apply() function you’ll have access to the Project. You can do anything groovy you would normally do within a build.gradle file here (add tasks, extension properties, etc).

Adding the build.gradle file
Next you need to add and configure a build.gradle in the root of the project. This file declares the location of the groovy plugin file, the version of the plugin, how it’s published and a few more things. The file should look something like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
apply plugin: 'groovy'
apply plugin: 'java-gradle-plugin'
apply plugin: 'maven-publish'
 
group = 'com.example.pluginName'
version = '1.0.0'
 
gradlePlugin {
  plugins {
    demoPlugin {
      id = project.group
      implementationClass = 'com.example.pluginName.PluginFileName'
    }
  }
}
 
dependencies {
  compile localGroovy()
  compile gradleApi()
}
 
publishing {
  publications {
    pluginPublication (MavenPublication) {
      from            components.java
      groupId         project.group
      artifactId      "PluginFileName"
      version         project.version
    }
  }
}

Publish the plugin to the local maven repository
Everything should be in place for the plugin to be published to your local machine. You can run ./gradlew tasks to get a list of available tasks. Among those tasks should be publishToMavenLocal. This is the task which will deploy the plugin as a jar to your machines local m2 repository (located at ~/.m2/repository).

# Publish the plugin
~/Documents/myGradlePlugin $ ./gradlew publishToMavenLocal
 
BUILD SUCCESSFUL in 2s
10 actionable tasks: 10 executed
 
# Verify the plugin was deployed appropriately
~/Documents/myGradlePlugin $ cd ~/.m2/repository/com/example/pluginName/PluginFileName
~/.m2/repository/com/example/pluginName/PluginFileName $ ls -la
drwxr-xr-x  4 riley.macdonald  wheel  136 Dec 28 15:53 1.0.0
-rw-r--r--  1 riley.macdonald  wheel  339 Dec 28 15:53 maven-metadata-local.xml

I found any errors I made along the way to be descriptive enough to work through, for example:

Execution failed for task ':publishPluginMavenPublicationToMavenLocal'.
> Failed to publish publication 'pluginMaven' to repository 'mavenLocal'
   > Invalid publication 'pluginMaven': groupId cannot be empty.

If you’ve made it this far the plugin is ready to be consumed by a project! Note: If you want to deploy your plugin to an artifact repository try using the com.gradle.plugin-publish plugin!

Consuming the plugin in another project
Navigate to the root build.gradle in the project you want to apply the plugin to. Only the following is required:

buildscript {
  repositories {
    mavenLocal() // or artifact repository url
  }
  dependencies {
    classpath 'com.example.pluginName:PluginFileName:1.0.0'
  }
}
apply plugin: 'com.example.pluginName'

If your project builds you’re all set!

Summary
Hopefully you’ve found this process as simple as I did. After working through a few errors I was able to apply my plugin to encapsulate my duplicated code. Be sure to rename pluginName, PluginFileName and the packages to your liking. It’s worth noting that a pluginName closure will also be added to your project where you can configure the plugin on a project level see the gradle docs regarding this.

Open the comment form

Leave a comment:

Comments will be reviewed before they are posted.

User Comments:

Be the first to leave a comment on this post!