Chapter 21. Gradle Plugins

Gradle at its core intentionally provides little useful functionality for real world automation. All of the useful features, such as the ability to compile Java code for example, are added by plugins. Plugins add new tasks (e.g. JavaCompile), domain objects (e.g. SourceSet), conventions (e.g. main Java source is located at src/main/java) as well as extending core objects and objects from other plugins.

In this chapter we will discuss how to use plugins and the terminology and concepts surrounding plugins.

21.1. Applying plugins

Plugins are said to be applied, which is done via the Project.apply() method.

Example 21.1. Applying a plugin

build.gradle

apply plugin: 'java'

Plugins advertise a short name for themselves. In the above case, we are using the short name ‘java’ to apply the JavaPlugin.

We could also have used the following syntax:

Example 21.2. Applying a plugin by type

build.gradle

apply plugin: org.gradle.api.plugins.JavaPlugin

Thanks to Gradle's default imports (see Appendix E, Existing IDE Support and how to cope without it) you could also write:

Example 21.3. Applying a plugin by type

build.gradle

apply plugin: JavaPlugin

The application of plugins is idempotent. That is, a plugin can be applied multiple times. If the plugin has previously been applied, any further applications will have no effect.

A plugin is simply any class that implements the Plugin interface. Gradle provides the core plugins as part of its distribution so simply applying the plugin as above is all you need to do. For 3rd party plugins however, you need to make the plugins available to the build classpath. For more information on how to do this, see Section 59.5, “External dependencies for the build script”.

For more on writing your own plugins, see Chapter 58, Writing Custom Plugins.

21.2. What plugins do

Applying a plugin to the project allows the plugin to extend the project's capabilities. It can do things such as:

  • Add tasks to the project (e.g. compile, test)
  • Pre-configure added tasks with useful defaults.
  • Add dependency configurations to the project (see Section 8.3, “Dependency configurations”).
  • Add new properties and methods to existing type via extensions.

Let's check this out:

Example 21.4. Tasks added by a plugin

build.gradle

apply plugin: 'java'

task show << {
    println relativePath(compileJava.destinationDir)
    println relativePath(processResources.destinationDir)
}

Output of gradle -q show

> gradle -q show
build/classes/main
build/resources/main

The Java plugin has added a compileJava task and a processResources task to the project and configured the destinationDir property of both of these tasks.

21.3. Conventions

Plugins can pre-configure the project in smart ways to support convention-over-configuration. Gradle provides mechanisms and sophisticated support and it's a key ingredient in powerful-yet-concise build scripts.

We saw in the example above that the Java plugins adds a task named compileJava that has a property named destinationDir (that configures where the compiled Java source should be placed). The Java plugin defaults this property to point to build/classes/main in the project directory. This is an example of convention-over-configuration via a reasonable default.

We can change this property simply by giving it a new value.

Example 21.5. Changing plugin defaults

build.gradle

apply plugin: 'java'

compileJava.destinationDir = file("$buildDir/output/classes")

task show << {
    println relativePath(compileJava.destinationDir)
}

Output of gradle -q show

> gradle -q show
build/output/classes

However, the compileJava task is likely to not be the only task that needs to know where the class files are.

The Java plugin adds the concept of source sets (see SourceSet) to describe the aspects of a set of source, one aspect being where the class files should be written to when it is compiled. The Java plugin maps the destinationDir property of the compileJava task to this aspect of the source set.

We can change where the class files are written via the source set.

Example 21.6. Plugin convention object

build.gradle

apply plugin: 'java'

sourceSets.main.output.classesDir = file("$buildDir/output/classes")

task show << {
    println relativePath(compileJava.destinationDir)
}

Output of gradle -q show

> gradle -q show
build/output/classes

In the above example, we applied the Java plugin which, among other things, did the following:

  • Added a new domain object type: SourceSet
  • Configured a main source set with default (i.e. conventional) values for properties
  • Configured supporting tasks to use these properties to perform work

All of this happened during the apply plugin: "java" step. In the example above we changed the desired location of the class files after this conventional configuration had been performed. Notice by the output with the example that the value for compileJava.destinationDir also changed to reflect the configuration change.

Consider the case where another task is to consume the classes files. If this task is configured to use the value from sourceSets.main.output.classesDir, then changing it in this location will update both the compileJava task and this other consumer task whenever it is changed.

This ability to configure properties of objects to reflect the value of another object's task at all times (i.e. even when it changes) is known as “convention mapping”. It allows Gradle to provide conciseness through convention-over-configuration and sensible defaults yet not require complete reconfiguration if a conventional default needs to be changed. Without this, in the above example we would have had to reconfigure every object that needs to work with the class files.

21.4. More on plugins

This chapter aims to serve as an introduction to plugins and Gradle and the role they play. For more information on the inner workings of plugins, see Chapter 58, Writing Custom Plugins.