Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to extend the behavior of a Gradle task for a new task type?

I would like to set a few things for a few test tasks. More specifically, I would like to add a few environment variables and a few system properties, maybe a few other things such as "dependencies" or "workingDir". With the regular Test task I can do this,

task test1(type:Test, dependsOn:[testPrep,testPrep1]){
     workingDir testWorkingPath
     systemProperty 'property','abs'
     environment.find { it.key ==~ /(?i)PATH/ }.value += (System.properties['path.separator'] + myLibPath)
     environment.LD_LIBRARY_PATH = "/usr/lib64:/lib64:${myLibPath}:" + environment.LD_LIBRARY_PATH
 }

task test2(type:Test, dependsOn:[testPrep]){
     workingDir testWorkingPath
     systemProperty 'property','abs'
     environment.find { it.key ==~ /(?i)PATH/ }.value += (System.properties['path.separator'] + myLibPath)
     environment.LD_LIBRARY_PATH = "/usr/lib64:/lib64:${myLibPath}:" + environment.LD_LIBRARY_PATH
     systemPropety 'newProperty','fdsjfkd'
 }

It would be nice to have a new task type MyTestType extending the regular Test task type, where the common definition is defined.

task test1(type:MyTestType){
     dependsOn testPrep1
 }

task test2(type:MyTestType){
     systemPropety 'newProperty','fdsjfkd'
 } 

What would be best way to do this? It seems that the execute() method is final and cannot be extended. I will need to do something like the doFirst to set those properties. Should I add all the extra values in the constructor? Is there any other hook I can use? Thanks.

like image 781
zggame Avatar asked Apr 29 '15 15:04

zggame


2 Answers

In general you can extend the 'Test' task and implement your customizations

task test1(type:MyTestType){
}

task test2(type:MyTestType){
     systemProperty 'newProperty','fdsjfkd'
}

class MyTestType extends Test {
    public MyTestType(){
        systemProperty 'property','abs'
    }
}

Alternatively you can configure all tasks of type Test with less boilerplate:

// will apply to all tasks of type test. 
// regardless the task was created before this snippet or after
tasks.withType(Test) {
   systemProperty 'newProperty','fdsjfkd'   
}
like image 180
Rene Groeschke Avatar answered Sep 22 '22 13:09

Rene Groeschke


It is also possible to specify the behavior of a particular superclass setting. Say for example you want to centralize the environment.find block, but allow setting myLibPath per task like this:

task test1(type: MyTestType) {
}
task test2(type: MyTestType) {
  libPath = '/foo/bar'
}

You could do that by overriding the configure method:

class MyTestType {
  @Input def String libPath

  @Override
  public Task configure(Closure configureClosure) {
    return super.configure(configureClosure >> {
      environment.find { it.key ==~ /(?i)PATH/ }.value += (System.properties['path.separator'] + (libPath ?: myLibPath))
    })
  }
}

Here we use the closure composition operator >> to combine the passed-in closure with our overridden behavior. The user-specified configureClosure will be run first, possibly setting the libPath property, and then we run the environment.find block after that. This can also be combined with soft defaults in the constructor, as in Rene Groeschke's answer

Note that this particular use case might break if you configure the task more than one, since the environment.find statement transforms existing state instead of replacing it.

like image 24
Emil Lundberg Avatar answered Sep 22 '22 13:09

Emil Lundberg