gradle扩展可以处理属性的惰性评估吗?

时间:2013-04-25 15:30:49

标签: gradle

我正在编写一个自定义gradle插件来处理一些模糊的复杂工作,并且在使用属性配置插件适用的一些任务时遇到了令人沮丧的问题。

apply plugin: myPlugin

//Provide properties for the applied plugin
myPluginProps {
    message = "Hello"
}

//Define a task that uses my custom task directly
task thisTaskWorksFine(type: MyTask) {
    input = myPluginProps.message
}

//Define a plugin that will apply a task of my custom type
class MyPlugin implements Plugin<Project> {
    void apply(Project project) {
        project.extensions.create('myPluginProps', MyPluginExtension)

        project.task(type: MyTask, 'thisTaskWorksIncorrectly') {
            input = project.myPluginProps.message
        }
    }
}

//The extension used by my custom plugin to get input
class MyPluginExtension {
    def String message
}

//The task used by both the standard build section and the plugin
class MyTask extends DefaultTask {
    def String input

    @TaskAction
    def action() {
        println "You gave me this: ${input}"
    }
}

使用此文件的结果如下:

$ gradle thisTaskWorksFine thisTaskWorksIncorrectly
:thisTaskWorksFine
You gave me this: Hello
:thisTaskWorksIncorrectly
You gave me this: null

BUILD SUCCESSFUL

我认为这是非常意外的。在我看来,从插件中应用一个任务并直接写一个任务应该在给出相同的输入时产生相同的输出。在这种情况下,两个任务都被赋予myPluginProps.message作为输入,但插件应用的任务是贪婪的,并且在早期评估为null。 (在申请阶段?)

我找到的唯一解决方案是在插件任务的配置块中使用闭包,如下所示:

//Define a plugin that will apply a task of my custom type
class MyPlugin implements Plugin<Project> {
    void apply(Project project) {
        project.extensions.create('myPluginProps', MyPluginExtension)

        project.task(type: MyTask, 'thisTaskWorksIncorrectly') {
            input = { project.myPluginProps.message }
        }
    }
}

这很好地解决了贪婪的评估问题除了现在必须修改自定义任务以期望和处理闭包。这并不是很难做到,但我不认为处理关闭应该是任务的责任,因为插件是“责怪”。

我在这里使用的扩展程序不正确吗?或者他们只是不够?官方立场似乎是we should use extensions,但我还没有找到任何扩展可以做我需要的例子。我可以继续使用闭包并编写一堆可以处理闭包和普通类型的闭包eval和setter的样板getter,但它似乎非常违反groovy的理念,因此也是gradle。如果有一种方法可以使用扩展并自动获得惰性评估,我会非常高兴。

2 个答案:

答案 0 :(得分:13)

彼得在我的问题here中的答案表明,约会马戏团的功能肯定会消失。最好避免它。

使用afterEvaluate解决延迟配置问题使我的代码比conventionMapping方法更清晰。

class MyPlugin implements Plugin<Project> {
    void apply(Project project) {
        project.extensions.create('myPluginProps', MyPluginExtension)

        project.afterEvaluate {
            project.task(type: MyTask, 'thisTaskWorksIncorrectly') {
                input = project.myPluginProps.message
            }
        }
    }
}

答案 1 :(得分:12)

此问题的常见解决方案是使用约定映射:

class MyPlugin implements Plugin<Project> {
    void apply(Project project) {
       project.extensions.create('myPluginProps', MyPluginExtension)

        project.task(type: MyTask, 'thisTaskWorksIncorrectly') {
            conventionMapping.input = { project.myPluginProps.message }
        }
    }
}

然后在任务中:

class MyTask extends DefaultTask {
    def String input

    @TaskAction
    def action() {
        println "You gave me this: ${getInput()}"
    }

}

请注意我明确地使用了input的getter - 如果直接引用该字段,则不会启用约定映射。