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, and are provided via plugins. Although the plugins are decoupled, we would like to point out that the Gradle core plugins are NEVER updated or changed for a particular Gradle distribution. If there is a bug in the compile functionality of Gradle, we will release a new version of Gradle. There is no change of behavior for the lifetime of a given distribution of Gradle.
If you want to use the plugin for building a Java project, simply include
usePlugin('java')
in 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 12, The Project and Task API). The Gradle plugins use this API to:
Add tasks to the project (e.g. compile, test)
Create dependencies between those tasks to let them execute in the appropriate order.
Add dependency configurations to the project.
Add a so called convention object to the project.
Let's check this out:
Example 17.1. Using plugin
build.gradle
usePlugin 'java'
task show << {
println relativePath(compileJava.destinationDir)
println relativePath(processResources.destinationDir)
}
Output of gradle -q show
> gradle -q show build/classes/main build/classes/main
The Java Plugin adds a compileJava
task and a processResources
task
to the project object which can be accessed by a build script. It has configured the destinationDir
property of both of these tasks.
The usePlugin()
method either takes a string or a class as an argument. You can write
[14]
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:
Figure 17.1. plugin.properties
# # Copyright 2009 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # base=org.gradle.api.plugins.BasePlugin java=org.gradle.api.plugins.JavaPlugin eclipse=org.gradle.api.plugins.EclipsePlugin groovy=org.gradle.api.plugins.GroovyPlugin war=org.gradle.api.plugins.WarPlugin osgi=org.gradle.api.plugins.osgi.OsgiPlugin jetty=org.gradle.api.plugins.jetty.JettyPlugin maven=org.gradle.api.plugins.MavenPlugin project-reports=org.gradle.api.plugins.ProjectReportsPlugin code-quality=org.gradle.api.plugins.quality.CodeQualityPlugin scala=org.gradle.api.plugins.scala.ScalaPlugin
If you want to use your own plugins, you must make sure that they are accessible via the build script classpath (see Chapter 34, Organizing Build Logic for more information). To learn more about how to write custom plugins, see Chapter 33, Writing Custom Plugins.
If you use the Java Plugin
for example, there are a compileJava
and a processResources
task for
your production code (the same is true for your test
code). The default location for the output of those tasks is the directory build/classes/main
.
What if you want to change this? Let's try:
Example 17.2. Configuring a plugin task
build.gradle
usePlugin 'java' task show << { processResources.destinationDir = new File(buildDir, 'output') println relativePath(processResources.destinationDir) println relativePath(compileJava.destinationDir) }
Output of gradle -q show
> gradle -q show build/output build/classes/main
Setting the destinationDir
of the processResources
task had only an effect on the processResources
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.
Gradle's tasks are usually convention aware. A plugin can add a convention object to your project, and map certain values of this convention object to task properties.
Example 17.3. Plugin convention object
build.gradle
usePlugin 'java' task show << { sourceSets.main.classesDir = new File(buildDir, 'output') println relativePath(processResources.destinationDir) println relativePath(compileJava.destinationDir) }
Output of gradle -q show
> gradle -q show build/output build/output
The Java Plugin has added a convention object with a sourceSets
property, which we use to set the classes directory. Notice that setting this has changed the destinationDir
property of both the processResources
and
compileJava
tasks.
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.
The properties of a convention object can be accessed as project properties. As shown in the following example, you can also access the convention object explicitly.
Example 17.4. Using the plugin convention object
build.gradle
usePlugin 'java' task show << { // Access the convention property as a project property println relativePath(sourceSets.main.classesDir) println relativePath(project.sourceSets.main.classesDir) // Access the convention property via the convention object println relativePath(convention.sourceSets.main.classesDir) println relativePath(convention.plugins.java.sourceSets.main.classesDir) }
Output of gradle -q show
> gradle -q show build/classes/main build/classes/main build/classes/main build/classes/main
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.
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 extend other plugins. For example
usePlugin('groovy')
calls also the Java Plugin. We say the Groovy plugin extends 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.
Plugins provide tasks, which are glued together via dependsOn relations and a convention object.
[14] Thanks to Gradle's default imports (see Appendix C, Existing IDE Support and how to cope without it) you can also write
usePlugin(JavaPlugin)
in this case.