Extension inheritance in gradle plugin for tasks

plugins

(Noam Zwebner) #1

I’ve created a Gradle plugin with 2 tasks, I want the tasks to share the same extension except for a few parameters so I’ve implemented a base extension class with the shared parameters and 2 classes that inherit from the base with each it’s own unique parameters.

In order to parse the extension correctly according to the task, in each task I type:

private PluginConfiguration extension = getProject().getExtensions().create("gradleSettings", <TaskExtension.class>);

But now when I try to run any of the tasks I receive:

Cannot add extension with name ‘gradleSettings’, as there is an extension already registered with that name

I would be much obliged if anyone has advice on the correct way of implementing this without creating another extension tag or another plugin.


(Schalk Cronjé) #2

This is relatively similar to a recipes that I am preparing to add to Idiomatic Gradle Vol2.

What you can do is to create one extension on the project (which is thch is your base class) and then add extensions to your tasks (which is of your derived class types). Not only will get the opporutninty to have shared config between the tasks, but also the flexibility of overriding global configuration on a per-task basis.

Here’s an example in dynamic Groovy for simplicity (you should really static compiled Groovy for plugin implementation as far as possible). If you are using Java or Kotlin, you would obvisouly need to adjust the syntax.


class BaseExt {
  static final String EXTENSION_NAME = 'gradleSettings'

  BaseExt(Project project) {
    this.project = project
    this.aSetting = 'I am the base'
  }

  protected BaseExt(Task task) {
    this.task = task
  }

  String getASetting() {
    if(task != null) {
      this.aSetting ?: task.project.getByName(EXTENSION_NAME).getASetting()
    } else {
      this.aSetting
    }
  }

  void setASetting(final String x) {
    this.aSetting = x
  }

  private String aSetting 
  private final Project project
  private final Task task
}

class SubExtA extends BaseExt {
  SubExtA(Task task) {
    super(task)
  }

  void setSpecificSetting(final String x) {
    // whatever needs to be done to set this setting goes here
  }
}

Your plugin code will now contain

void apply(Project project) {
  project.extensions.create(BaseExt.EXTENSION_NAME,BaseExt,project) 
}

The constructor of your task class (assuming it is called MyTask) will now look like

MyTask() {
  extensions.create(BaseExt.EXTENSION_NAME,SubExtA)  
} 

In the build script this will lead to you being able to do

apply plugin : 'your.new.plugin'

gradleSettings {
  aSetting = 'Modified in global extension'
}  

It will also allow you to override the value in the task if you want to

task myTask( type : MyTask ) {
  gradleSettings {
    aSettings = 'Modified in task'
    specificSetting = 'Cannot set this globally'
  }
}

Also thus the following is not legal

gradleSettings {
  setSpecificSetting = 'This will fail to compile'
}

You can just repeat this for your second extension and task type.

HTH