Chapter 7
More about Tasks

In the introductory tutorial (chapter 2) you have learned how to create simple task. You have also learned how to add additional behavior to those tasks later on. And you have learned how to create dependencies between tasks. This was all about simple tasks. But Gradle takes the concept of tasks further. Gradle supports enhanced tasks. Tasks which have there own properties and methods. This is really different to what you are used from Ant targets. Such enhanced tasks are either provided by you or are provided by Gradle.

7.1 Configuring Tasks

As an example, let’s look at the Resources task provided by Gradle. To create such a Resources task for your build, you may declare in your build script1 :

  createTask('resources', type: org.gradle.api.tasks.Resources)

The resources task provides an API to configure it. See Resources Task API. If you create the resources task like this, it has no default behavior.2 . We want now use the resources task to learn about different ways to configure it (the result is always the same for our examples).

  Resources resources = createTask('resources', type: Resources)
  resources.from(file('resources'))
  resources.to(file('target'))
  resources.includes('**/*.txt', '**/*.xml', '**/*.properties')

This is similar to the way we would normally configure objects in Java. You have to repeat the context (resources) in the configuration statement every time. This is a redundancy and not very nice to read.

There is a more convenient way of doing this.

  Resources resources = createTask('resources', type: Resources)
  resources.from(file('resources')).to(file('target')).
   includes('**/*.txt', '**/*.xml', '**/*.properties')

You might know this approach from the Hibernates Criteria Query API or JMock. Of course the API of a task has to support this. The from, to and includes methods all return an instance of the resources object. All of Gradles build-in tasks usually support this configuration style.

But there is yet another way of configuring the resources task. It also preserves the context and it possibly offers the best readability. It is usually our favorite.

1  Resources resources = createTask('resources', type: Resources)
2  resources {
3     from(file('resources'))
4     to(file('target'))
5     includes('**/*.txt', '**/*.xml', '**/*.properties')
6  }

This works for any task. Line 2 of the example is just a shortcut for task("resources"). It is important to note that if you pass a closure to the task method, this closure is applied for configuring the task. There is a slightly different ways of doing this.

  Resources resources = createTask('resources', type: Resources).configure {
     from(file('source'))
     to(file('target'))
     includes('**/*.txt', '**/*.xml', '**/*.properties')
  }

If you pass a closure to the createTask() method, this closure gets added as an action to the task. Every task has a configure method, which you can pass a closure for configuring the task. The above example works, because the createTask method returns the task object. Gradle uses this style for configuring objects in many places, not just for tasks.

7.2 Replacing Tasks

Sometimes you want to replace a task. For example if you want to exchange a task added by the Java Plugin with a custom task of a different type. You can achieve this with:

  createTask('resources', type: Resources)
  
  createTask('resources', overwrite: true) {
      println('I am the new one.')
  }

  >gradle -q resources
  I am the new one.

Here we replace a task of type Resources with a simple task. When creating the simple task, you have to set the overwrite property to true. Otherwise Gradle throws an exception, saying that a task with such a name already exists.

7.3 Summary

If you are coming from Ant, such an enhanced Gradle task as Resources looks like a mixture between an Ant target and an Ant task. And this is actually the case. The separation that Ant does between tasks and targets is not done by Gradle. The simple Gradle tasks are like Ant’s targets and the enhanced Gradle tasks also include the Ant task aspects. All of Gradles tasks share a common API and you can create dependencies between them. Such a task might be nicer to configure than an Ant task. It makes full use of the type system, is more expressive and easier to maintain.