Build / Gradle

Gradle Build – Part 9

In the previous article we defined a simple build task, but it was of limited use because it could only be invoked as a standalone task. In this article we’ll see how we can attach it to the build lifecycle.

gradle_logoGradle allows us to attach our task to the build lifecycle by allowing us to define task dependencies. This enables us to define a dependency on task A from task B, which will ensure that task A gets run before task B.

The assemble task is defined within the Android plugin, and we can add a dependency to ensure that our custom task gets executed before the assemble task:

If we now run a build, we can see our task being called:

It’s important to note that there are other tasks upon which the assemble task also depends which are executed before our custom task. We’ll need to change how we define our dependency a little more if we want to execute our task before these. More on this in a moment.

Adding a dependency in this way has a rather strange side effect. If we run ./gradlew -q tasks again, we’ll see that our task is no longer listed. That is because ./gradlew -q tasks only displays the top level tasks and our custom task is no longer than because of the dependency. However, we can get a more detailed task list by adding the -all switch to the command:

Our custom task is now listed, but only as a dependency of the assemble task. That may be what we need, but what if we need our custom task to be run before the dynamic assemble tasks which get created for each of the build variants? If we attempt to iterate the build variants at the point where the task is created, it will simply return us an empty list because they don’t yet exist. However we can defer the task creation until later on by using a rule. A rule allows us to defer the definition of the task until it is first accessed:

[groovy] .
.
.
assemble.dependsOn(‘customAssemble’)

tasks.addRule(“Pattern: customAssemble“) { taskName ->
println “Creating task ${taskName}”
if (taskName.equals(“customAssemble”)) {
android.applicationVariants.each { variant ->
println “Adding dependency to assemble${variant.name}”
def targetTask = project.tasks.findByName(“assemble${variant.name}”)
if(targetTask != null) {
targetTask.dependsOn(“customAssemble${variant.name}”)
}
}
}
task(taskName) << { task ->
println “Running custom task ${task.name}”
}
}
[/groovy]

What happens here is that we define a dependency on the assemble task to a task named customAssemble. The customAssemble task has not yet been defined, but when it is required our rule definition is invoked because the name of the task matches the pattern defined for the rule (line 6). When the rule definition is invoked, we check whether the current task is named customAssemble to ensure that we only add the new dependencies once (line 8). We then iterate through the application variants (line 9) and, if an assemble task exists for the variant (lines 11-12), set a dependency on to a new task (line 13). This new task will then be dynamically created using the same rule as it is required. Each time the rule is invoked we create the task (lines 17-19) which will, once again, print a status message when it is invoked.

The println lines within the rule have been left in so that we can see precisely what is happening (normally, you would remove these):

Initially we see the creation of the customAssemble task which caused the dependencies on four new tasks to be created, and then we see the creation of these tasks. The during the execution of the build we see these tasks executed before the tasks which depend on them.

Of course, the actual implementation of your tasks will vary depending on your requirements. All we’re looking at here is how you can attach your tasks to the build lifecycle to ensure that you task gets invoked at the correct point of the build lifecycle.

That concludes our tour of the Android Gradle Build system. We’ve covered a fair bit of material over the last 9 articles, and much of it will see rather alien because it is a real departure from the existing build mechanisms. While it may seem daunting, it is well worth learning to use the new system because it certainly makes life much easier while, if you require it, providing you with the opportunity to inject new tasks in to the build process to enable you to customise the build to your requirements.

The source code for this article is available here.

© 2013 – 2014, Mark Allison. All rights reserved.

CC BY-NC-SA 4.0 Gradle Build – Part 9 by Styling Android is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. Permissions beyond the scope of this license may be available at http://blog.stylingandroid.com/license-information.

3 Comments

  1. Great blog !! One of the best I have seen in Android world 🙂 Makes me come back here time and again!! Keep up the good work.

  2. Hello Mark,

    Thank you the Gradle build system articles, I enjoy reading your blog.

    I found a small typo mistake in this article,

    “$ ./gradlew -q tasks -all” – Mistake
    “$ ./gradlew -q tasks –all” – Correct, all key require — instead of single –

    My gradle version detail

    ————————————————————
    Gradle 1.7
    ————————————————————

    Build time: 2013-08-06 11:19:56 UTC
    Build number: none
    Revision: 9a7199efaf72c620b33f9767874f0ebced135d83

    Groovy: 1.8.6
    Ant: Apache Ant(TM) version 1.8.4 compiled on May 22 2012
    Ivy: 2.2.0
    JVM: 1.6.0_51 (Apple Inc. 20.51-b01-457)
    OS: Mac OS X 10.8.4 x86_64

    Thank You
    KPBird

Leave a Reply

Your email address will not be published. Required fields are marked *