• Mark Small's picture

    Custom Gradle Plugin

    Mark Small / March 20, 2015
  • This post details my experience building a Custom Gradle Plugin.  While there are many resources on creating Gradle scripts, there are far fewer on custom plugins.  The first question is why the need for a custom plugin?  While build scripts are a good start and they can be re-usable, there is no ability to test script(s) other than trial and error, this flies in the face of quality control.

    The official Gradle docs #gradle-guide are a good place to start, but I used this Java Code Geeks [2] post to scaffold my custom plugin.  The problem with existing tutorials/blogs are they only deal with adding custom tasks.  For a Corporate Plugin, similar to a Maven Parent POM, we really want to set sensible defaults to configure used plugins.  This is not to say we don't want to introduce custom tasks, but this is not the main point of the plugin.

    What do we want from our custom plugin?  At EDINA, we create web applications (among other applications).  We currently use Maven as the build tool for or libraries/applications.  We have a parent POM to set commo
    n properties, configuration, dependencies etc for all our code.  The point of this custom plugin and Gradle in general is to replace Maven, why? There are many resources that explain why Gradle is a good replacement for Maven that I won't repeat here, a couple of these are [3, 4].  The purpose of the custom plugin is to provide sensible defaults for all Gradle projects, libraries and applications.  The plugin provides:

    * Add repositories to retrieve/deploy dependencies from/to.

    * Apply common plugins for code coverage,

    * Configure plugins that will be applied by individual projects e.g. Java/War plugins.

    * Set Java compiler version

    * Set dependencies that all projects should use e.g. JUnit, Logging etc.

    * Set dependencies between tasks e.g. deploy depends on all tests having been run.

    Okay, this is what we want, lets get to the interesting bit how do we do it?  You can write plugins in Java or Groovy, I chose Groovy, I chose this as I wanted to learn more about Groovy and most of the examples scripts you find are written in Groovy.  The first thing you do is to scaffold your plugin project, run

    gradle init

    gradle-init

    build.gradle is the script used to build your custom plugin project.

    the gradle directory is contains at this point, the files for the gradle wrapper.  The gradle wrapper is the version of gradle you want to use for your project.  This ensures that everyone uses the same version, to use it run:

    ./gradlew <task name>

    gradle-wrapper

    These are the default tasks that all projects are provided with.  I will leave the explanation of these tasks to another post, this is about creating a custom plugin, not gradle itself.  Create the source directories:

    mkdir -p src/{main,test}/{groovy,resources}

    As you can see, Gradle re-uses Maven's default source directory structure.  We still need to create the package structure for the plugin, I'm going to omit that from here as this should be obvious enough.  Now we are ready to start writing code, so lets look at the source of our Custom Plugin:

    package edina.shared.gradle
    
    import org.gradle.api.Plugin
    import org.gradle.api.Project
    
    
    class EdinaPlugin implements Plugin<Project> {
    
      static final String EXTENSION_NAME = 'edinaArgs'
    
      @Override
      void apply(Project project) {
        project.extensions.create(EXTENSION_NAME, EdinaPluginExtension)
      }
    
    }
    
    package edina.shared.gradle
    
    class EdinaPluginExtension {
    
      String name = 'Default Name'
      String version = 'Default Version'
    
    }
    

    The above code gives you a base structure to build any custom plugin, but what does it mean?  Well, the plugin itself defines the basic structure of your plugin.  The Extension is the means by which to pass configuration to tasks, I've yet to discover how to provide such configuration to the Plugin itself.  To add configuration options, add a property to the extension, this can then be populated via the edinaArgs configuration block in your build.gradle file e.g.

    edinaArgs {
      name: 'Plugin Name'
      version: 'Plugin Version'
    }

    If there is no configuration specified, the default values are used, otherwise they are overridden by what is specified in the configuration block (See branch scaffold).

    Now, that is the basic scaffolding of a custom plugin but what do we want the plugin to do?  Most of the examples I've found tend to create a nonsense task to display something to the stdout.  While this works, it is such a fake example that it is near useless.  I'm sure there are those who want to create custom tasks, but for my purpose, I really want to configure a basic setup for all Java-based projects, be they JAR libraries or WAR applications.  The  sort of thing I want to do is to apply common plugins and configuration for all projects.  I've detailed above what we want to configure, lets take each in turn

     

    Set Java Compiler

    We set a sensible default for the compiler within our plugin by adding:

    project.sourceCompatibility = JavaVersion.VERSION_1_7
    

    This can be overridden within the body of any sub-project's build.gradle file:

    sourceCompatibility = '1.5'

    If neither is set, then the lates Java version installed will be used (See branch compiler-version).

    Common Plugins

    (See branch apply-plugins).

     

    Common Source Sets

    (See branch sourcesets).

     

    Common Repositories

    (See branch repositories).

     

    Common Dependencies

    (See branch dependencies).

     

    Provided Scope

    (See branch provided-scope).

     

    Task Dependencies

    (See branch task-dependencies).

     

     

    References

    The links below are good reference points for Gradle and writing custom plugins.

    *  https://www.gradle.org/docs/current/userguide/userguide.html

    * [2] http://www.javacodegeeks.com/2012/08/gradle-custom-plugin.html

    * [3] http://www.drdobbs.com/jvm/why-build-your-java-projects-with-gradle/2401...

    * [4] https://www.gradleware.com/resources/java/gradles-advantages-over-maven/

    * [5] http://blog.anorakgirl.co.uk/2014/12/gradle-and-the-parent-pom/