How to add reusable methods to a custom task?


(thirsch81) #1

Hello,

maybe this should be a no-brainer, but I haven’t found any clear example in the docs. Basically I want to implement a custom task (i.e. extend Default Task), and have more methods in it besides the one I declare with @TaskAction. I want to be able to reuse those methods in the task-action itself and also in the to-be-written buildscripts that use my custom task.

I want to do something like:

class ConfigureProperties extends DefaultTask {

@Input File source

@Input ConfigObject additionalConfig

@TaskAction void configurePropertiesFile() {

def sourceConfig

if(isJavaPropertiesFile(source)) {

sourceConfig = slurpProperties(source) // slurpProperties is a method I want to reuse inside instances of my task, see below

} else {

sourceConfig = slurpConfig(source)

}

// do something with sourceConfig and additionalConfig }

ConfigObject slurpProperties(File file) {

Properties props = new Properties()

file.withReader { props.load(it) }

return new ConfigSlurper().parse(props) }

}

Thanks for your help and regards,

Thomas


(Peter Niederwieser) #2

Although it’s technically possible to do this (and I don’t see why your code wouldn’t work), I don’t recommend it. Instead, I’d declare the method in a separate class that can be used directly from the build script, or in an extension object added by a plugin. You can find more on this in the Gradle User Guide.

PS: Please use HTML code tags for all code snippets.


(thirsch81) #3

Hi Peter,

again, thanks for the quick response. Heres a more complete example:

import java.util.Properties
  import groovy.util.ConfigObject
  import org.gradle.api.DefaultTask
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.OutputFile
import org.gradle.api.tasks.TaskAction
  task configureProps(type: ConfigureProperties) {
    source = file("java.properties")
    //configuration = slurpConfig("test { some { key = 'value' } }")
    target = file("out.properties")
}
  class ConfigureProperties extends DefaultTask {
   @Input
 File source
   @Input
 ConfigObject configuration
   @OutputFile
 File target
   @TaskAction
 void configurePropertiesFile() {
  def sourceConfig
  if(isJavaPropertiesFile(source)) {
   sourceConfig = slurpProperties(source)
  } else {
   sourceConfig = slurpConfig(source)
   }
  println sourceConfig
 }
       def slurpConfig(File file) {
  new ConfigSlurper().parse(file.text)
 }
   def isJavaPropertiesFile(File file) {
  file.name.endsWith(".properties")
 }
   def slurpProperties(File file) {
  Properties props = new Properties()
  file.withReader { props.load(it) }
  return new ConfigSlurper().parse(props)
 }
}

The Problem arises when I uncomment this line:

//configuration = slurpConfig("test { some { key = 'value' } }")

I think this means the method is only visible inside the actuall class, but not on the project itself (which must be the case, if I wanted to use it this way), correct?

I already thought an extension object, I will try that next. Thanks for your help!


(Peter Niederwieser) #4

The problem with this code is that you are passing an incompatible argument to the method.


(thirsch81) #5

Thank you, pardon the dumb mistake. I have one more question regarding your first answer.

I’m currently dealing with a convention class that is bloated with utility methods that I need for various tasks in a plugin, but not anywhere else, so that’s why I came up with the idea of putting them inside custom task classes which I’m now refatoring stuff into.

So do I understand your recommendation correctly:

  1. Use a convention class for methods I actually do want to reuse in build scripts that use my plugin

If i wanted to use a method in a task definition, do it like this:

project.convention.getPlugin(myPlugin).myMethod()
  1. Use separate POGO utility classes/jars for utilties that are only used inside Custom Task actions

Also, what would be the differences between using an extension or a convention class to add reusable functionality?

Thank you for your time!


(Peter Niederwieser) #6

My recommendation was to either provide a utility class, or an extension. Conventions are the old way to extend the build model; extensions are the new one. Another solution might be to make the methods an integral part of the task classes, rather than pure utility methods. After all, the methods are used to configure tasks, aren’t they?


(thirsch81) #7

Another solution might be to make the methods an integral part of the task classes, rather than pure > utility methods.

In which way would that differ from the way I did it in the code snibbet?

Conventions are the old way to extend the build model;

Referring to this excellent plugin here:

https://github.com/int128/gradle-ssh-plugin/blob/master/src/main/groovy/org/hidetake/gradle/ssh/SshPlugin.groovy https://github.com/int128/gradle-ssh-plugin/blob/master/src/main/groovy/org/hidetake/gradle/ssh/SshPluginConvention.groovy Are you saying the way it is done there, i.e.

void apply(Project project) {
        attachRemoteContainer(project)
        project.convention.plugins.put('ssh', new SshPluginConvention(project))
    }

is or will become deprecated and you would refactor it to

project.extensions.create("ssh", SshPluginConvention)

(even though that might not compile in this case)

Also, in the Javadoc for Project it says The convention properties added to the project by each Plugin applied to the project. A Plugin can add properties and methods to a project through the project’s Convention object. The properties of this s cope may be readable or writable, depending on the convention objects.

So is that something that shouldn’t be used in this way anmore or am I missing something?

Sorry for being repetetive, but I’m still a bit confused about this particular issue, when it comes to developing plugins.


(Peter Niederwieser) #8

In which way would that differ from the way I did it in the code snibbet?

It would actually configure the task, rather than just providing some utility method.

So is that something that shouldn’t be used in this way anmore or am I missing something?

Extensions are the newer and better mechanism. Conventions may become deprecated in a future release. I wouldn’t use conventions anymore.