If you are tired of typing complete task names in the command-line; here is a good option for you. If you have defined task names in camel case (camelCase) format, you can just execute the task by mentioning the first letter of each word. For example, you can execute the sampleTask1
task with shorthand sT1
:
$ gradle -q –b build_ordering.gradle sT1 sT2
This will execute sampleTask1
and sampleTask2
.
If the shorthand of camel case matches more than one task, it will result in ambiguity:
$ gradle -q -b build_ordering.gradle sT FAILURE: Build failed with an exception. * What went wrong: Task 'sT' is ambiguous in root project 'Chapter3'. Candidates are: 'sampleTask1', 'sampleTask2', 'sampleTask3', 'sampleTask4', 'sampleTask5', 'sampleTask6'. * Try: Run gradle tasks to get a list of available tasks. Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.
Now, we will explore some other task operations such as conditional execution, build optimization, and force execution.
There are different scenarios when you want to execute some tasks based on certain properties. For example, you have a property named environment
in the build
file. If the value of the property is set to prod
, you want to execute production specific tasks and if it is qa
, you want to execute test-specific tasks. Create a build
file build_condition.gradle
with the following code snippet:
ext { environment='prod' // can set this value from property file or command line using -Pname=value option } task prodTask << { println 'Executing prod tasks '+ environment } prodTask.onlyIf {project.hasProperty('environment') && project.environment=='prod' } task qaTask << { println 'Executing qa tasks '+ environment } qaTask.onlyIf { project.hasProperty('environment') && project.environment== 'qa '}
Execute the preceding build
file with both the tasks:
$ gradle -b build_condition.gradle prodTask qaTask :prodTask Executing prod tasks prod :qaTask SKIPPED BUILD SUCCESSFUL
Here, Gradle skipped qaTask
and executed only prodTask
based on the environment property set in the build
file. You can also remove the environment value in the preceding ext
closure and directly set the property from the command-line option and try to execute the following commands:
$ gradle -b build_condition.gradle -Penvironment=qa qaTask prodTask :qaTask Executing qatasks qa :prodTask SKIPPED BUILD SUCCESSFUL
There might be another scenario when a task is outdated and you do not want to execute it, even if some other task depends on this task. This feature is supported by the enabled
option in the task configuration phase:
task sampleTask12 << { println " This task is disabled" } task sampleTask13 (dependsOn: sampleTask12) << { println "This task depends on sampleTask12" } sampleTask12.enabled = false
$ gradle -b build_enabled.gradle sT12 sT13 :sampleTask12 SKIPPED :sampleTask13 This task depends on task12 BUILD SUCCESSFUL
Note that you can set
enabled
in the configuration phase itself. It should not be part of the doFirst
or doLast
closure:
task sampleTask12 { //enabled = false // valid statement doLast { enabled = false // Invalid statement println 'Task execution' } }
In the preceding example, if we try to set enabled = false
in the doLast
closure, the task will not execute. Build will fail with the Cannot call Task.setEnabled(boolean) on task ':sampleTask12' after task has started execution
error.
Consider a scenario where your build
file consists of 10 tasks, which execute as per the task dependencies order. Out of 10 tasks, five tasks are modifying five different files on the filesystem. Let's say these five files are some property files and these build tasks are setting some values to the property:
envproperty.txt env=prod sysproperty.txt memory=1024 ……
After first execution, the property files are modified with the respective values. When you run the build
script again, although the files are already modified, the build script modifies those files again.
Gradle provides a mechanism of skipping the execution of these kinds of tasks based on the input and output parameters of the task, which is also known as incremental build. It helps in reducing the build time. You might have observed when you apply Java plugin and build your project couple of times, some tasks are marked with UP-TO-DATE keyword (execute without -q
option). This means there is no change in the input and output compared to the last execution of these tasks and those tasks are ignored.
By default, Gradle provides this feature to its in-built tasks. You can also enhance your tasks with this capability, with the help of inputs and outputs of the task. Task inputs and outputs are of type TaskInputs
and TaskOuputs
. We will explain this behavior with help of one example:
Consider the PropDetails.xml
file:
<properties> <property> <filedetail> <name>envproperty.txt</name> <key>env</key> <value>prod</value> </filedetail> </property> <property> <filedetail> <name>sysproperty.txt</name> <key>memory</key> <value>1024</value> </filedetail> </property> </properties>
Consider the build_optimization.gradle
file:
task updateExample { ext { propXml = file('PropDetails.xml') } File envFile = file('envproperty.txt') File sysFile = file('sysproperty.txt') inputs.file propXml outputs.files (envFile, sysFile) doLast { println "Generating Properties files" def properties = new XmlParser().parse(propXml) properties.property.each { property -> def fileName = property.filedetail[0].name[0].text() def key = property.filedetail[0].key[0].text() def value = property.filedetail[0].value[0].text() def destFile = new File("${fileName}") destFile.text = "$key = ${value} " } } } $ gradle –b build_optimization.gradle updateExample
If you run this task for the first time, it will read the PropDetail.xml
file and will create two files envproperty.txt
and sysproperty.txt
with key=value
pair mentioned in the property
file. Now, if you run this command again, you will see the following output:
:updateExample UP-TO-DATE BUILD SUCCESSFUL
This implies that there is no change in the input and output of this task; thus, there is no need to execute the task again.
Try to change either the XML file or the generated property files or delete the output files. If you run the Gradle
command again, this time, the task will execute and it will recreate the files. Gradle internally generates snapshots of input parameters and output parameters (Gradle generates a hash code to avoid duplicates) and stores it. Next time onwards, Gradle generates the snapshots of input and output parameters, and if both are the same, it avoids the execution of tasks.
Also, an important point to remember, if no output is defined for tasks, then it will not be considered for optimization (UP-TO-DATE
). The task will always execute. There can be a scenario where the output of a task is not a file or a directory, it could be some other logical build steps or system-related check. In this situation, you can use the TaskOutputs.upToDateWhen()
method or the outputs.upToDateWhen
closure to check the specific scenario and mark tasks UP-TO-DATE
.
To skip the optimization technique and force full execution of the task, the --rerun-tasks
command line option can be used. It will execute the task forcefully, even if it is UP-TO-DATE
.
$ gradle –b build_optimization.gradle updateExample --rerun-tasks
The --rerun-tasks
option will always execute the task without checking the input and output parameters.
We discussed the methodMissing
concept in Groovy. You can define some method patterns in Groovy, which can respond to method calls at runtime with the predefined patterns. Task rules provide the same flexibility with tasks. It allows executing a task, which does not exist. Gradle checks the task rule and creates the task if the rules have been defined. We will see the usage with the help of a simple example. For example, you have different assets, which are synced from different repository servers. Rather than creating different tasks for each sync, you can create the task rule as follows:
tasks.addRule("Pattern: sync<repoServer>") { String taskName -> if (taskName.startsWith("sync")) { task(taskName) << { println "Syncing from repository: " + (taskName - 'sync') } } }
Here you can call different tasks for each repository servers as gradle sync<repoServer>
and it will sync the assets from that repository.
A very common example of task rules can be found in the Java plugin. Add apply plugin: 'java'
as the first line in the build
file and run the following command:
$ gradle -b build_rule.gradle tasks ……………. Rules ----- Pattern: clean<TaskName>: Cleans the output files of a task. Pattern: build<ConfigurationName>: Assembles the artifacts of a configuration. Pattern: upload<ConfigurationName>: Assembles and uploads the artifacts belonging to a configuration. Pattern: sync<repoServer> To see all tasks and more detail, run with --all. BUILD SUCCESSFUL Total time: 4.021 secs
As of now, do not worry much about the plugin. We will discuss plugins in detail in Chapter 4, Plugin Management.
In the above output, you can find the rules defined in the Java plugin. Gradle provides three in-built rules clean<TaskName>
, build<sConfigurationName>
, and upload<ConfigurationName>
and the newly created sync<repoServer>
rule. For all the tasks that are available in your build
file (Java plugin tasks and user-defined tasks), you can execute one additional task using clean<TaskName>
. For example, you have assemble, classes, and jar tasks available in the Java plugin. Apart from executing normal clean task, which deletes the build directory, you can also execute tasks such as cleanClasses, cleanJar
, and so on, which cleans only the result of one particular task.
18.188.119.81