Correct way to define a Gradle plugin property ext

2019-05-06 14:43发布

问题:

I'm trying to create a Gradle plugin in Java that has property extensions (not conventions, as this is apparently the old, wrong way). For the record, I'm working with Gradle 1.6 on a Linux machine (Ubuntu 12.04).

I've gotten as far as figuring out that the this should be done in the Plugin class definition. Here is A way of adding an extension. Create an extension class that contains your properties:

public class MyPluginExtensions {

    File sourceDir;
    File outputDir;

    public MyPluginExtensions(Project project) {
        this.project = project;
        sourceDir = new File(project.getProjectDir(), "src");
        outputDir = new File(project.getBuildDir(), "out");
    }

}

Now add these extensions to the project in the main plugin class:

public class MyPlugin implements Plugin<Project> {

    @Override
    public void apply(Project project) {

        Map<String,Object> taskInfo = new HashMap<String,Object>();
        taskInfo.put("type", MyPluginTask.class);
        taskInfo.put("description", "Generates blah from blah.");
        taskInfo.put("group", "Blah");
        Task myPluginTask = project.task(taskInfo, "myPluginTask");

        // Define conventions and attach them to tasks
        MyPluginExtensions extensions = new MyPluginExtensions(project);
        myPluginTask.getExtensions().add(
                "sourceDir", 
                extensions.sourceDir);
        myPluginTask.getExtensions().add(
                "outputDir", 
                extensions.outputDir);
    }
}

This approach, however, doesn't seem to be correct. A new project property is shows up in the project.ext namespace. I expect to be able to address the plugin extensions as:

in my build.gradle:
myPluginTask.sourceDir = file('the/main/src')
myPluginTask.outputDir = file('the/output')

However, when I put such things in a gradle script that uses my plugin and try to set this property, gradle tells me I can't set it:

* What went wrong:
A problem occurred evaluating script.
> There's an extension registered with name 'sourceDir'. You should not reassign it via a property setter.

So, what's the right way to add property extensions for a task in a Java-based Gradle plugin?

EDIT:

Based on some other SO posts, I tried just adding my extensions object in one shot:

// first attempt:
//myPluginTask.getExtensions().add("sourceDir", extensions.sourceDir);
//myPluginTask.getExtensions().add("outputDir",extensions.outputDir);

// second attempt
myPluginTask.getExtensions().add("myPluginTask", extensions);

This appears to work. However, Gradle is now complaining that I've added a dynamic property:

Deprecated dynamic property: "sourceDir" on "task ':myPluginTask'", value: "/Users/jfer...".

So, again, what's the right way to add a plugin extension property?

EDIT 2

So, taking yet another shot at this, I'm adding the extension to the project object and using the create method instead:

// first attempt:
//myPluginTask.getExtensions().add("sourceDir", extensions.sourceDir);
//myPluginTask.getExtensions().add("outputDir",extensions.outputDir);

// second attempt
// myPluginTask.getExtensions().add("myPluginTask", extensions);

// third attempt
project.getExtensions().create("myPluginTask", MyPluginExtensions.class, project);

However, this fails for a couple of reasons:

  1. Creating a properties extension with the same name ("myPluginTask") as the task creates a collision between the task name and extension name, causing the task to disappear from gradle's perspective (and throw oblique errors, such as "No such property: dependsOn for class ...MyPluginExtensions").
  2. If I provide a name that does not collide with a task name (e.g., "myPluginPropExt"), the create() method works, but DOES NOT add the extension in its own namespace as expected (e.g., project.myPluginPropExt.propertyName and instead adds it in the project namespace (e.g., project.propertyName) which is not correct and causes Gradle to throw a bunch of "deprecated dynamic property" warnings.

回答1:

So here is a solution to my problem:

public class MyPlugin implements Plugin<Project> {

    @Override
    public void apply(Project project) {

        Map<String,Object> taskInfo = new HashMap<String,Object>();
        taskInfo.put("type", MyPluginTask.class);
        taskInfo.put("description", "Generates blah from blah.");
        taskInfo.put("group", "Blah");
        Task myPluginTask = project.task(taskInfo, "myPluginTask");

        // Define conventions and attach them to tasks
        MyPluginExtensions extensions = new MyPluginExtensions(project);

        // the magic extension code:
        project.getExtensions().add("myPluginName", extensions);
    }
}

Now I can set a value for one of the extension properties in my gradle.build file like so (and I don't get a warning about adding deprecated dynamic properties):

 myPluginName.sourceDir = file('the/main/src')

The final trick is to get this value in my Plugin's task:

public class MyPluginTask extends DefaultTask {
    @TaskAction
    public void action() {
        MyPluginExtensions extensions = (MyPluginExtensions) getProject()
                .getExtensions().findByName("myPluginName");

        System.out.println("sourceDir value: " + extensions.sourceDir);
    }
}

This works, but what annoys me about this solution is that I want to be able to put the extension properties in the same namespace as the task (e.g., myPluginTask.sourceDir) which I have seen in groovy-based plugins, but this apparently is not supported or just doesn't work.

In the meantime, hope this helps someone else.



回答2:

The code is adding an extension to a task (rather than a project), which is rarely useful. After that, it tries to set myPluginTask.sourceDir = file('the/main/src'), which isn't possible because the extension was just registered under that same name.



回答3:

When your task and your extension have the same name, you can do this in your build.gradle:

myPluginTask.sourceDir = file('the/main/src')
project.tasks.myPluginTask.dependsOn clean


标签: java gradle