Chapter 8
Plugins

Now we look at how Gradle provides build-by-convention and out of the box functionality. These features are decoupled from the core of Gradle. Gradle provides them via plugins. To make this clear at the very beginning. The plugins provided by Gradle belong to the core distribution of Gradle and they are NEVER updated or changed in any way for a particular Gradle distribution. If there is a bug in the compile functionality of Gradle, we gonna release a new version of Gradle. There is no change of behavior for the lifetime of a given distribution of Gradle. We mention this as there is another build tools with a plugin system which behaves differently.

8.1 Declaring Plugins

If you want to use the plugin for building a Java project, simply type

  usePlugin('java')

at the beginning of your script. That’s all. From a technological point of view plugins use just the same operations as you can use from your build scripts. That is they use the Project and Task API (see chapter 6). The Gradle plugins use this API for:

Let’s check this out:

  usePlugin('java')
  
  createTask('check') {
      println(task('compile').destinationDir.name) // We could also write println(compile)
  }

  >gradle -q check
  classes

The JavaPlugin adds a compile task to the project object which can be accessed by a build script.

The usePlugin method either takes a string or a class as an argument. You can write1

  usePlugin(org.gradle.api.plugins.JavaPlugin)

Any class, which implements the Plugin interface, can be used as a plugin. Just pass the class as an argument. You don’t need to configure anything else for this. If you want to access a custom plugin via a string identifier, you must inform Gradle about the mapping. You can do this in the file plugin.properties in the top level directory of Gradle. It looks like this for the current release:

  java=org.gradle.api.plugins.JavaPlugin
  groovy=org.gradle.api.plugins.GroovyPlugin
  war=org.gradle.api.plugins.WarPlugin

If you want to use your own plugins, you must make sure that they are accessible via the build script classpath. See chapter 15 for more information.

8.2 Configuration

If you use the JavaPlugin there is for example a compile and resources task for your production code (the same is true for your test code). The default location for the output of those task is the directory build/classes. What if you want to change this? Let’s try:

  usePlugin('java')
  
  createTask('check') {
      resources.destinationDir = new File(buildDir, 'output')
      println(resources.destinationDir.name)
      println(compile.destinationDir.name)
  }

  >gradle -q check
  output
  classes

Setting the destinationDir of the resources task had only an effect on the resources task. Maybe this was what you wanted. But what if you want to change the output directory for all tasks? It would be unfortunate if you had to do this for each task separately.

Gradles tasks are usually convention aware. A plugin can add a convention object to your build. It can also map certain values of this convention object to task properties.

  usePlugin('java')
  
  createTask('check') {
      classesDirName = 'output'
      println(resources.destinationDir.name)
      println(compile.destinationDir.name)
      println(convention.classesDirName)
  }

  >gradle -q check
  output
  output
  output

The JavaPlugin has added a convention object with a classesDirName property. The properties of a convention object can be accessed like project properties. As shown in the example, you can also access the convention object explicitly.

By setting a task attribute explicitly (as we have done in the first example) you overwrite the convention value for this particular task.

Not all of the tasks attributes are mapped to convention object values. It is the decision of the plugin to decide what are the shared properties and then bundle them in a convention object and map them to the tasks.

8.2.1 More about convention objects

Every project object has a convention object which is a container for convention objects contributed by the plugins declared for your project. If you simply access or set a property or access a method in your build script, the project object first looks if this is a property of itself. If not, it delegates the request to its convention object. The convention object checks if any of the plugin convention objects can fulfill the request (first wins and the order is not defined). The plugin convention objects also introduce a namespace.

  usePlugin('java')
  println classesDir
  println convention.classesDir
  println convention.plugins.java.classesDir

All three statements print out the same property. The more specific statements are useful if there are ambiguities.

8.2.2 Declaring Plugins Multiple Times

A plugin is only called once for a given project, even if you have multiple usePlugin() statements. An additional call after the first call has no effect but doesn’t hurt either. This can be important if you use plugins which extends other plugins. For example usePlugin(’groovy’) calls also the Java Plugin. We say the Groovy plugin extend the Java plugin. But you might as well write:

  usePlugin('java')
  usePlugin('groovy')

If you use cross-project configuration in multi-project builds this is a useful feature.

8.3 Summary

Plugins provide tasks, which are glued together via dependsOn relations and a convention object.