Task definition to access extension

I’m writing a convention plugin and I can’t get my task type access the extension defined in the same plugin.

Minimum working example:

// task-and-extension.gradle
class SomeTask extends DefaultTask {
  @TaskAction
  void doSomething() {
    println "I know about this extension: ${extensions.someExtension.name}"
  }
}

abstract class SomeExtension {
}

extensions.create('someExtension', SomeExtension)
// Downstream project's build.gradle script
plugins {
  id 'task-and-extension'
}

tasks.register('doSomething', SomeTask)

How do I make my defined task type aware of the extension defined in the same plugin?

You add an extension to Project but you try to retrieve it from SomeTask, that’s why you don’t find it.

But actually, your task should not use the extension directly anyway and especially not at execution time.
This has various problems, including that you need to use project at execution time which is deprecated and with configuration-cache even an error already, and that you most probably miss to properly declare your inputs and outputs properly which is essential for up-to-date checks of task and also for cache-key calculation if you want to make your task cacheable.

Usually your extension has properties of type Property and related,
and your task has properties of type Property and related that accordingly have input and output annotations,
and your plugin adds the extension and wires the extension properties to the task properties.
The task then just uses its own properties.

1 Like

Thanks, it makes sense.

I do have properties on both the extension and the task, I had just omitted them to keep the example minimal.

Actually what I was trying to do is to default a task input to the value of an extension property, but not for a specific task instance, but rather for ALL task instances of the defined task type.

Is that even doable?

e.g. imagine this downstream build script:

tasks.register('uploadAlpha', UploadTask) {
  file = 'alpha'
  url = "myurl/myrepo/path/to/alpha/${someVar}"
  username = 'bob'
  password = 'bob'
}

tasks.register('uploadBeta', UploadTask) {
  file = 'alpha'
  url = "myurl/myrepo/path/to/beta/${someVar}"
  username = 'bob'
  password = 'bob'
}

tasks.register('uploadGamma', UploadTask) {
  file = 'gamma'
  url = "myurl/myrepo/path/to/gamma/${someVar}"
  username = 'bob'
  password = 'bob'
}

...

tasks.register('uploadOmega', UploadTask) {
  file = 'omega'
  url = "myurl/myrepo/path/to/omega/${someVar}"
  username = 'bob'
  password = 'bob'
}

As you can see there’s a lot of code duplication there.

I’d like to declare a bunch of default via an extension

repoCommons {
  baseUrl = 'myurl/myrepo'
  username = 'bob'
  password = 'bob'
}

Or is the below the only way?

tasks.withType(UploadTask).configureEach {
  baseUrl = 'myurl/myrepo'
  username = 'bob'
  password = 'bob'
}

Ideally task classes are as opinionless as possible, as well as extension classes, and the plugins add opinion by configuring conventions or wiring extension properties to task properties.
This increases the likelihood and ease of reuse.

So if have the extension with the properties and really want each and every task of that type to use those extension values, you would do the tasks.withType(UploadTask).configureEach { ... } in the plugin and there wire the extension properties to the task properties.

Then for example a consumer could also decide to just add the plugin to the classpath and use the pure task type which does not require that any extension is created but the consumer can just set the properties he needs on the task directly.

Alternatively, you can of course not use an extension but just use the withType.configureEach in the consumer if this is more a consumer decision that all tasks of that type should have the same values which actually seems to make more sense, because a consumer could also have 3 tasks for server A and 3 tasks for server B and so on.

1 Like