Task operations

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.

Conditional 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.

Build optimization

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.

Task rules

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.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.188.119.81