Table of Contents
This chapter looks at some of the details of writing a build script.
Gradle provides a domain specific language, or DSL, for describing builds. This build language is based on Groovy, with some additions to make it easier to describe a build.
A build script can contain any Groovy language element. [6] Gradle assumes that each build script is encoded using UTF-8.
In the tutorial in Chapter 44, Java Quickstart we used, for example, the
apply()
method. Where does this method come from? We said earlier that the build script
defines a project in Gradle. For each project in the build, Gradle creates an object of type
Project
and associates this Project
object with
the build script. As the build script executes, it configures this Project
object:
Don't forget that your build script is simply Groovy code that drives the Gradle API. And the
Project
interface is your starting point for accessing everything
in the Gradle API. So, if you're wondering what 'tags' are available in your build script, you can
start with the documentation for the Project
interface.
Any method you call in your build script which is not defined
in the build script, is delegated to the Project
object.
Any property you access in your build script, which is not defined
in the build script, is delegated to the Project
object.
Let's try this out and try to access the name
property of the
Project
object.
Example 16.1. Accessing property of the Project object
build.gradle
println name println project.name
Output of gradle -q check
> gradle -q check projectApi projectApi
Both println
statements print out the same property. The first uses auto-delegation to
the Project
object, for properties not defined in the build script. The other
statement uses the project
property available to any build script, which returns the
associated Project
object. Only if you define a property or a method which has the
same name as a member of the Project
object, would you need to use the project
property.
The Project
object provides some standard properties, which are available in
your build script. The following table lists a few of the commonly used ones.
Table 16.1. Project Properties
Name | Type | Default Value |
project |
Project |
The Project instance |
name |
String |
The name of the project directory. |
path |
String |
The absolute path of the project. |
description |
String |
A description for the project. |
projectDir |
File |
The directory containing the build script. |
buildDir |
File |
|
group |
Object |
unspecified |
version |
Object |
unspecified |
ant |
AntBuilder |
An AntBuilder instance |
When Gradle executes a script, it compiles the script into a class which implements Script
.
This means that all of the properties and methods declared by the Script
interface
are available in your script.
There are two kinds of variables that can be declared in a build script: local variables and extra properties.
Local variables are declared with the def
keyword. They are only visible in the scope where they have been declared.
Local variables are a feature of the underlying Groovy language.
Example 16.2. Using local variables
build.gradle
def dest = "dest" task copy(type: Copy) { from "source" into dest }
All enhanced objects in Gradle's domain model can hold extra user-defined properties. This includes, but is not limited to, projects, tasks,
and source sets. Extra properties can be added, read and set via the owning object's ext
property. Alternatively, an
ext
block can be used to add multiple properties at once.
Example 16.3. Using extra properties
build.gradle
apply plugin: "java" ext { springVersion = "3.1.0.RELEASE" emailNotification = "build@master.org" } sourceSets.all { ext.purpose = null } sourceSets { main { purpose = "production" } test { purpose = "test" } plugin { purpose = "production" } } task printProperties << { println springVersion println emailNotification sourceSets.matching { it.purpose == "production" }.each { println it.name } }
Output of gradle -q printProperties
> gradle -q printProperties 3.1.0.RELEASE build@master.org main plugin
In this example, an ext
block adds two extra properties to the project
object. Additionally,
a property named purpose
is added to each source set by setting ext.purpose
to
null
(null
is a permissible value). Once the properties have been added, they can be read and set like
predefined properties.
By requiring special syntax for adding a property, Gradle can fail fast when an attempt is made to set a (predefined or extra) property but the property is misspelled or does not exist. Extra properties can be accessed from anywhere their owning object can be accessed, giving them a wider scope than local variables. Extra properties on a project are visible from its subprojects.
For further details on extra properties and their API, see the ExtraPropertiesExtension
class in the API documentation.
You can configure arbitrary objects in the following very readable way.
Example 16.4. Configuring arbitrary objects
build.gradle
task configure << { def pos = configure(new java.text.FieldPosition(10)) { beginIndex = 1 endIndex = 5 } println pos.beginIndex println pos.endIndex }
Output of gradle -q configure
> gradle -q configure 1 5
You can also configure arbitrary objects using an external script.
Example 16.5. Configuring arbitrary objects using a script
build.gradle
task configure << { def pos = new java.text.FieldPosition(10) // Apply the script apply from: 'other.gradle', to: pos println pos.beginIndex println pos.endIndex }
other.gradle
// Set properties. beginIndex = 1 endIndex = 5
Output of gradle -q configure
> gradle -q configure 1 5
The Groovy language provides plenty of features for creating DSLs, and the Gradle build language takes advantage of these. Understanding how the build language works will help you when you write your build script, and in particular, when you start to write custom plugins and tasks.
Groovy adds lots of useful methods to the standard Java classes. For example, Iterable
gets
an each
method, which iterates over the elements of the Iterable
:
Example 16.6. Groovy JDK methods
build.gradle
// Iterable gets an each() method
configurations.runtime.each { File f -> println f }
Have a look at http://groovy-lang.org/gdk.html for more details.
Groovy automatically converts a property reference into a call to the appropriate getter or setter method.
Example 16.7. Property accessors
build.gradle
// Using a getter method println project.buildDir println getProject().getBuildDir() // Using a setter method project.buildDir = 'target' getProject().setBuildDir('target')
Parentheses are optional for method calls.
Example 16.8. Method call without parentheses
build.gradle
test.systemProperty 'some.prop', 'value' test.systemProperty('some.prop', 'value')
Groovy provides some shortcuts for defining List
and Map
instances. Both kinds of literals are straightforward, but map literals have some interesting twists.
For instance, the “apply
” method (where you typically apply plugins) actually
takes a map parameter. However, when you have a line like “apply plugin:'java'
”, you
aren't actually using a map literal, you're actually using “named parameters”, which have almost exactly
the same syntax as a map literal (without the wrapping brackets). That named parameter list gets converted
to a map when the method is called, but it doesn't start out as a map.
Example 16.9. List and map literals
build.gradle
// List literal test.includes = ['org/gradle/api/**', 'org/gradle/internal/**'] List<String> list = new ArrayList<String>() list.add('org/gradle/api/**') list.add('org/gradle/internal/**') test.includes = list // Map literal. Map<String, String> map = [key1:'value1', key2: 'value2'] // Groovy will coerce named arguments // into a single map argument apply plugin: 'java'
The Gradle DSL uses closures in many places. You can find out more about closures here. When the last parameter of a method is a closure, you can place the closure after the method call:
Example 16.10. Closure as method parameter
build.gradle
repositories { println "in a closure" } repositories() { println "in a closure" } repositories({ println "in a closure" })
Each closure has a delegate
object, which Groovy uses to look up variable and method
references which are not local variables or parameters of the closure. Gradle uses this for
configuration closures, where the delegate
object is set to
the object to be configured.
Example 16.11. Closure delegates
build.gradle
dependencies { assert delegate == project.dependencies testCompile('junit:junit:4.12') delegate.testCompile('junit:junit:4.12') }
To make build scripts more concise, Gradle automatically adds a set of import statements to the Gradle scripts.
This means that instead of using throw new org.gradle.api.tasks.StopExecutionException()
you can just type throw new StopExecutionException()
instead.
Listed below are the imports added to each script:
Figure 16.1. gradle-imports
import org.gradle.* import org.gradle.api.* import org.gradle.api.artifacts.* import org.gradle.api.artifacts.cache.* import org.gradle.api.artifacts.component.* import org.gradle.api.artifacts.dsl.* import org.gradle.api.artifacts.ivy.* import org.gradle.api.artifacts.maven.* import org.gradle.api.artifacts.query.* import org.gradle.api.artifacts.repositories.* import org.gradle.api.artifacts.result.* import org.gradle.api.component.* import org.gradle.api.credentials.* import org.gradle.api.distribution.* import org.gradle.api.distribution.plugins.* import org.gradle.api.dsl.* import org.gradle.api.execution.* import org.gradle.api.file.* import org.gradle.api.initialization.* import org.gradle.api.initialization.dsl.* import org.gradle.api.invocation.* import org.gradle.api.java.archives.* import org.gradle.api.logging.* import org.gradle.api.logging.configuration.* import org.gradle.api.plugins.* import org.gradle.api.plugins.announce.* import org.gradle.api.plugins.antlr.* import org.gradle.api.plugins.buildcomparison.gradle.* import org.gradle.api.plugins.jetty.* import org.gradle.api.plugins.osgi.* import org.gradle.api.plugins.quality.* import org.gradle.api.plugins.scala.* import org.gradle.api.plugins.sonar.* import org.gradle.api.plugins.sonar.model.* import org.gradle.api.publish.* import org.gradle.api.publish.ivy.* import org.gradle.api.publish.ivy.plugins.* import org.gradle.api.publish.ivy.tasks.* import org.gradle.api.publish.maven.* import org.gradle.api.publish.maven.plugins.* import org.gradle.api.publish.maven.tasks.* import org.gradle.api.publish.plugins.* import org.gradle.api.reporting.* import org.gradle.api.reporting.components.* import org.gradle.api.reporting.dependencies.* import org.gradle.api.reporting.model.* import org.gradle.api.reporting.plugins.* import org.gradle.api.resources.* import org.gradle.api.specs.* import org.gradle.api.tasks.* import org.gradle.api.tasks.ant.* import org.gradle.api.tasks.application.* import org.gradle.api.tasks.bundling.* import org.gradle.api.tasks.compile.* import org.gradle.api.tasks.diagnostics.* import org.gradle.api.tasks.incremental.* import org.gradle.api.tasks.javadoc.* import org.gradle.api.tasks.scala.* import org.gradle.api.tasks.testing.* import org.gradle.api.tasks.testing.junit.* import org.gradle.api.tasks.testing.testng.* import org.gradle.api.tasks.util.* import org.gradle.api.tasks.wrapper.* import org.gradle.authentication.* import org.gradle.authentication.http.* import org.gradle.buildinit.plugins.* import org.gradle.buildinit.tasks.* import org.gradle.external.javadoc.* import org.gradle.ide.cdt.* import org.gradle.ide.cdt.tasks.* import org.gradle.ide.visualstudio.* import org.gradle.ide.visualstudio.plugins.* import org.gradle.ide.visualstudio.tasks.* import org.gradle.ivy.* import org.gradle.jvm.* import org.gradle.jvm.application.scripts.* import org.gradle.jvm.application.tasks.* import org.gradle.jvm.platform.* import org.gradle.jvm.plugins.* import org.gradle.jvm.tasks.* import org.gradle.jvm.tasks.api.* import org.gradle.jvm.test.* import org.gradle.jvm.toolchain.* import org.gradle.language.assembler.* import org.gradle.language.assembler.plugins.* import org.gradle.language.assembler.tasks.* import org.gradle.language.base.* import org.gradle.language.base.artifact.* import org.gradle.language.base.plugins.* import org.gradle.language.base.sources.* import org.gradle.language.c.* import org.gradle.language.c.plugins.* import org.gradle.language.c.tasks.* import org.gradle.language.coffeescript.* import org.gradle.language.cpp.* import org.gradle.language.cpp.plugins.* import org.gradle.language.cpp.tasks.* import org.gradle.language.java.* import org.gradle.language.java.artifact.* import org.gradle.language.java.plugins.* import org.gradle.language.java.tasks.* import org.gradle.language.javascript.* import org.gradle.language.jvm.* import org.gradle.language.jvm.plugins.* import org.gradle.language.jvm.tasks.* import org.gradle.language.nativeplatform.* import org.gradle.language.nativeplatform.tasks.* import org.gradle.language.objectivec.* import org.gradle.language.objectivec.plugins.* import org.gradle.language.objectivec.tasks.* import org.gradle.language.objectivecpp.* import org.gradle.language.objectivecpp.plugins.* import org.gradle.language.objectivecpp.tasks.* import org.gradle.language.rc.* import org.gradle.language.rc.plugins.* import org.gradle.language.rc.tasks.* import org.gradle.language.routes.* import org.gradle.language.scala.* import org.gradle.language.scala.plugins.* import org.gradle.language.scala.tasks.* import org.gradle.language.scala.toolchain.* import org.gradle.language.twirl.* import org.gradle.maven.* import org.gradle.model.* import org.gradle.nativeplatform.* import org.gradle.nativeplatform.platform.* import org.gradle.nativeplatform.plugins.* import org.gradle.nativeplatform.tasks.* import org.gradle.nativeplatform.test.* import org.gradle.nativeplatform.test.cunit.* import org.gradle.nativeplatform.test.cunit.plugins.* import org.gradle.nativeplatform.test.cunit.tasks.* import org.gradle.nativeplatform.test.googletest.* import org.gradle.nativeplatform.test.googletest.plugins.* import org.gradle.nativeplatform.test.plugins.* import org.gradle.nativeplatform.test.tasks.* import org.gradle.nativeplatform.toolchain.* import org.gradle.nativeplatform.toolchain.plugins.* import org.gradle.platform.base.* import org.gradle.platform.base.binary.* import org.gradle.platform.base.component.* import org.gradle.platform.base.plugins.* import org.gradle.play.* import org.gradle.play.distribution.* import org.gradle.play.platform.* import org.gradle.play.plugins.* import org.gradle.play.plugins.ide.* import org.gradle.play.tasks.* import org.gradle.play.toolchain.* import org.gradle.plugin.devel.* import org.gradle.plugin.repository.* import org.gradle.plugin.use.* import org.gradle.plugins.ear.* import org.gradle.plugins.ear.descriptor.* import org.gradle.plugins.ide.api.* import org.gradle.plugins.ide.eclipse.* import org.gradle.plugins.ide.idea.* import org.gradle.plugins.javascript.base.* import org.gradle.plugins.javascript.coffeescript.* import org.gradle.plugins.javascript.envjs.* import org.gradle.plugins.javascript.envjs.browser.* import org.gradle.plugins.javascript.envjs.http.* import org.gradle.plugins.javascript.envjs.http.simple.* import org.gradle.plugins.javascript.jshint.* import org.gradle.plugins.javascript.rhino.* import org.gradle.plugins.javascript.rhino.worker.* import org.gradle.plugins.signing.* import org.gradle.plugins.signing.signatory.* import org.gradle.plugins.signing.signatory.pgp.* import org.gradle.plugins.signing.type.* import org.gradle.plugins.signing.type.pgp.* import org.gradle.process.* import org.gradle.sonar.runner.* import org.gradle.sonar.runner.plugins.* import org.gradle.sonar.runner.tasks.* import org.gradle.testing.base.* import org.gradle.testing.base.plugins.* import org.gradle.testing.jacoco.plugins.* import org.gradle.testing.jacoco.tasks.* import org.gradle.testkit.runner.* import org.gradle.util.*