Adding behaviour to existing tasks

I know it is possible to extend tasks by inheritance, but that is not what I am after. I am wondering about whether the following is possible

// Let's assume the jar instance of the Jar task for simplicity
jar {
    // I want to add a 'abc' method with does some extra stuff during the configuration of the task
    abc {
      // Do some special stuff in here as part of configuration
  }
  }

I know it is possible by messing with the metaClass, but I wonder whether there’s a better way.

The idea is that such functionality be added by a plugin.

It depends what you mean when you say “configuration” if you just want to modify the task you can do so in the configuration closure. For example:

jar {

archiveName ‘someOtherNameForJar’

}

If you want to add functionality to the task you could use ‘doFirst’ or ‘doLast’:

jar {

doLast {

// additional work to do

}

}

No, I don’t want to resort to ‘doFirst’ or ‘doLast’. I am interested in seeing whether plugins can add extra methods onto existing tasks, that would be visible during the configuration phase due to closure delegation.

I would think if you want to dynamically add a method to the ‘jar’ task your best option would be using the Groovy metaClass as you’ve said. I’ve used this method for something similar by modifying the ‘DefaultRepositoryHandler’ so as to allow users to do something like:

repositories {

companyNexus()

}

Another thought would be to have your plugin effectively replace the existing ‘jar’ task created by the ‘java’ plugin with your custom task implementation that extends from ‘Jar’.

Tasks are extensible in the same way that Project is. So, you can add extensions (e.g. someTask.extensions.create("foo, FooExtension)) and extra properties (e.g. someTask.myNewMethod = { « method impl })

You could write a plugin that applies a task dependency to your jar task …

jar {
   dependsOn 'myCustomTask'
}

Another option is to create a configuration closure and then apply that …

jar {
   configure customClosure
}

Aha, all great answers. I can do something with these suggestions.

Was looking at extensions for tasks and got me thinking. What if I wanted to do something with the extension when the @TaskAction annotated method got invoked. Obviously since I don’t one task class inheriting from another I cannot override that method. I could not see any way of hooking into that, so I have to assume that is not possible in 2.0 ??

You could add a listener for the task: http://www.gradle.org/docs/current/dsl/org.gradle.api.invocation.Gradle.html#org.gradle.api.invocation.Gradle:addListener(java.lang.Object)

Thanks Luke. Do you mean that I can write something like this

class mySneakyIntervention implements TaskExecutionListener {
  void afterExecute(Task t,TaskState s) {}
  void beforeExecute(Task t) {
    if (t instanceof FooTask ) {
      // Let's make a backup of all the input files
     // (Not really sure why you would want to do _that_,
      //
     but it's an example)
      t.project.copy {
       from(t.inputfiles.files)
        into new File(t.project.buildDir,'backup')
     }
    }
  }
}

and then hook it in, possibly when the plugin gets applied?

project.gradle.addListener( new mySneakyIntervention() )

Also what is the significant differences between ‘TaskExectionListener.beforeExecute’ and a ‘task.doFirst’? Which will be called first?

Yep, that’s about right.

beforeExecute() will be called before any action.

Another question on task extensions. If I annotate properties in my extension class with ‘@Input’, ‘@InputFiles’ etc., will it be treated the same as if the properties were on the Task itself?

The ‘@Input’ and ‘@InputFiles’ annotations can only be applied to tasks. You could get the behavior you want by using a convention mapping to map your extension properties to your task which would in turn extend ‘ConventionTask’. This is an internal API but this method is used pretty ubiquitously throughout Gradle.

Hey Schalk, as Mark already pointed out, annotations in your extension are not considered. But you could use the Task#getOutputs() and Task#getInputs() to programmatically add inputs/outputs to a given task from your plugin.

hope that helps!

cheers, René

Thanks Rene,

I try that as I don’t think convention mapping will work for my specific use-case.