2013-04-25 9 views
16

Sto scrivendo un plugin gradle personalizzato per gestire un lavoro vagamente complicato e mi sono imbattuto in un problema frustrante mentre utilizzavo le proprietà per configurare alcune delle attività che il plugin applica.Le estensioni gradle possono gestire la valutazione lenta di una proprietà?

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

I risultati di utilizzare questo file sono i seguenti:

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

BUILD SUCCESSFUL 

Ritengo che questo sia molto inaspettato. A mio avviso, l'applicazione di un task dal plug-in e la scrittura diretta di uno dovrebbe portare allo stesso output quando viene fornito lo stesso input. In questo caso, entrambe le attività vengono fornite come input, myPluginProps.message, ma l'attività applicata dal plug-in è ingorda e viene valutata nulla all'inizio. (Durante la fase di applicazione?)

L'unica soluzione che ho trovato è quello di utilizzare le chiusure nel blocco di configurazione del task plug-in in questo modo:

//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 } 
     } 
    } 
} 

che risolve il problema di valutazione avido abbastanza bene tranne che ora l'attività personalizzata deve essere modificata per prevedere e gestire una chiusura. Non è terribilmente difficile da fare, ma non penso che dovrebbe essere la responsabilità del compito di occuparsi della chiusura, dal momento che il plugin è "da incolpare".

Sto utilizzando le estensioni in modo errato qui? O semplicemente non sono adeguati? La posizione ufficiale sembra essere quella we should use extensions ma non ho ancora trovato alcun esempio in cui le estensioni potrebbero fare ciò di cui ho bisogno. Posso andare avanti con il mio uso di chiusure e scrivere un gruppo di getter di piastre di riscaldamento che fanno la chiusura di eval e setter in grado di gestire chiusure e tipi normali, ma sembra molto contrario alla filosofia di groovy e quindi gradle. Sarei molto felice se c'è un modo in cui posso usare le estensioni e ottenere automaticamente la valutazione pigra.

risposta

12

La soluzione più comune per questo problema è quello di utilizzare la mappatura convenzione:

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

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

e poi nel compito:

class MyTask extends DefaultTask { 
    def String input 

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

}

Si prega di notare che ho usato in modo esplicito getter per input - la mappatura della convenzione non entrerà in azione se si fa riferimento direttamente al campo.

+0

C'è differenza tra la convenzione e le convenzioni? La tua risposta sembra piuttosto chiara, quindi quasi sicuramente la userò, ma uno degli sviluppatori principali ha detto "In breve, usa solo le estensioni, non usare le convenzioni". Forse l'ho preso troppo alla lettera. –

+2

Le [convenzioni] (http://www.gradle.org/docs/current/dsl/org.gradle.api.Project.html#org.gradle.api.Project:convention) che Peter sta citando sono il vecchio meccanismo per le estensioni. La differenza è che ottieni il dsl ('myPluginProps {message =" Hello "}') gratuitamente con le estensioni e non le hai con le convenzioni.Le convenzioni e le mappature delle convenzioni sono due cose diverse e le mappature delle convenzioni sono utilizzate internamente in [gradle code] (https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/groovy/org/gradle/ api/plugins/JavaPlugin.java # L131) molto. – erdi

+0

Eccellente. Nella mia ricerca stavo schivando tutto ciò che menzionava la parola convenzione, ma era sbrigativo. Grazie per il consiglio! –

13

La risposta di Peter nella mia domanda here indica che la funzione conventionMapping andrà sicuramente via. È meglio evitarlo.

L'utilizzo di afterEvaluate per risolvere il problema di configurazione posticipata ha reso il mio codice molto più pulito rispetto all'approccio 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 
      } 
     } 
    } 
} 
Problemi correlati