2013-04-25 61 views
16

我正在寫一個自定義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作爲輸入,但插件應用的任務是貪婪的,並且在早期評估爲空。 (在申請階段?)

我發現的唯一的解決方案是使用封閉在插件任務的配置塊,像這樣:

//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,但我還沒有找到任何擴展可以做我需要的例子。我可以繼續使用閉包和編寫一堆模板getter,它可以處理閉包和正常類型的封閉eval和setter,但它似乎非常違背groovy和gradle的哲學。如果有一種方法可以使用擴展並自動獲得懶惰評估,我將非常高興。

回答

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()}" 
    } 

}

請注意,我明確使用getter方法input - 如果直接引用該字段,則約定映射不會啓動。

+0

約定映射和約定之間有區別嗎?你的答案看起來很漂亮,所以我幾乎肯定會使用它,但其中一位核心開發人員說:「總之,只使用擴展,不要使用約定。」也許我從字面上理解了這一點。 – 2013-04-25 17:24:51

+2

Peter提到的[約定](http://www.gradle.org/docs/current/dsl/org.gradle.api.Project.html#org.gradle.api.Project:convention)是舊機制用於擴展。不同之處在於,你可以通過擴展免費獲得dsl('myPluginProps {message =「Hello」}''),並且你沒有按照慣例得到它們。約定和約定映射是兩種不同的事情,約定映射在[gradle code]中內部使用(https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/groovy/org/gradle/ api/plugins/JavaPlugin.java#L131)很多。 – erdi 2013-04-25 17:36:02

+0

非常好。在我的研究中,我隱瞞了提到約定這個詞的所有內容,但這很倉促。感謝您的建議! – 2013-04-25 18:43:29

13

Peter在我的問題here的答案表明conventionMapping功能一定會消失。最好避免它。

使用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 
      } 
     } 
    } 
}