zggame
zggame

Reputation: 999

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.

Upvotes: 17

Views: 21142

Answers (3)

clinton
clinton

Reputation: 132

you can do it the following ways

task TestExt{ Test { systemPropety 'newProperty','fdsjfkd' } }

note: this just adds to the configure method of Test source: https://docs.gradle.org/current/dsl/org.gradle.api.Task.html#N18D18

Upvotes: 1

Emil Lundberg
Emil Lundberg

Reputation: 7379

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.

Upvotes: 1

Rene Groeschke
Rene Groeschke

Reputation: 28653

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'   
}

Upvotes: 21

Related Questions