[JAVA] Définitions des tâches Gradle

Diverses notes sur les tâches Gradle, des bases aux applications.

Connaissances préalables

environnement

Gradle

5.0

Java

openjdk 11.0.1

OS

Windows 10

Entité de tâche

Pour définir une tâche dans Gradle, écrivez:

build.gradle


task foo

La définition de cette tâche foo est [task (String)](https: // docs) de Project. Vous appelez la méthode .gradle.org / current / dsl / org.gradle.api.Project.html # org.gradle.api.Project: task (java.lang.String)).

La méthode task () retourne un objet qui représente la tâche créée, vous pouvez donc obtenir l'objet de la tâche foo comme suit:

build.gradle


def foo = task foo
println(foo)
println(foo.class)

Résultat d'exécution


> gradle foo

task ':foo'
class org.gradle.api.DefaultTask_Decorated

La tâche réelle de Gradle est un objet de Task, si elle est simplement créée par task () Un objet pour DefaultTask est créé.

Action La tâche contient une liste de Action en interne. Cette ʻAction a une action spécifique à effectuer sur cette tâche. Ensuite, lorsque la tâche est lancée, ʻAction est exécutée dans l'ordre à partir du haut de la liste.

ʻAction peut être ajoutée au début ou à la fin de la liste avec les méthodes doFirst () , doLast () de Task`.

build.gradle


def foo = task foo

foo.doFirst {
    println "two"
}
foo.doFirst {
    println "one"
}
foo.doLast {
    println "three"
}
foo.doLast {
    println "four"
}

Résultat d'exécution


> gradle foo

one
two
three
four

doFirst () ajoute ʻAction au début de la liste, et doLast () ʻajoute ʻAction` à la fin.

Dans l'exemple ci-dessus, doFirst () est exécuté deux fois. Dans ce cas, ʻAction, qui produit "un" ʻexécuté plus tard, sera inséré en haut de la liste, donc il est sorti dans l'ordre de"un" -> "deux".

Défini dans le bloc de configuration

build.gradle


task foo {
    println(delegate)
    println(delegate.class)

    doFirst {
        println("FOO!!")
    }
}

Résultat d'exécution


> gradle foo

> Configure project :
task ':foo'
class org.gradle.api.DefaultTask_Decorated

> Task :foo
FOO!!

Lors de la définition d'une tâche avec la méthode task (), vous pouvez passer une fermeture comme deuxième argument (https://docs.gradle.org/current/dsl/org.gradle.api.Project.html # org.gradle.api.Project: tâche (java.lang.String,% 20groovy.lang.Closure). Le délégué de cette fermeture est l'objet de tâche créé. Par conséquent, les méthodes et propriétés de la tâche créée peuvent être implicitement accessibles dans la fermeture.

Construire le cycle de vie

L'exécution Gradle est à peu près divisée en trois phases.

Phase Contenu
Phase d'initialisation
(Initialization)
Déterminez si le projet est unique ou multiple, etc.ProjectCréez un objet de.
Phase de configuration
(Configuration)
Exécutez le script de construction etprojectConstruisez l'objet.
Phase d'exécution
(Execution)
Exécutez réellement la tâche spécifiée sur la ligne de commande.

Plus précisément, dans build.gradle, l'intérieur du jeu ʻAction` pour la tâche est exécuté dans la phase d'exécution, et les autres sont exécutés dans la phase de paramétrage.

build.gradle


println("Phase de réglage 1")

task foo {
    println("Phase de réglage 2")
    doFirst {
        println("Phase d'exécution 1")
    }
    
    println("Phase de réglage 3")
    doLast {
        println("Phase d'exécution 2")
    }
}

task bar {
    println("Phase de réglage 4")
}

println("Phase de réglage 5")

Résultat d'exécution


> gradle foo

> Configure project :
Phase de réglage 1
Phase de réglage 2
Phase de réglage 3
Phase de réglage 4
Phase de réglage 5

> Task :foo
Phase d'exécution 1
Phase d'exécution 2

La partie qui est exécutée dans la phase de configuration est toujours exécutée quelle que soit la tâche spécifiée. Par conséquent, si vous écrivez par erreur le processus de la tâche dans la pièce à exécuter dans la phase de paramétrage, le processus sera exécuté même si la tâche n'est pas spécifiée, soyez donc prudent.

Référence de tâche

build.gradle


task foo {
    doFirst {
        println foo
        println project.foo
        println tasks.foo
        println tasks.getByName("foo")
        println tasks["foo"] // getAt()Sucre de syntaxe de méthode
    }
}

Résultat d'exécution


task ':foo'
task ':foo'
task ':foo'
task ':foo'
task ':foo'

Si vous définissez une tâche dans un projet, vous ne pouvez faire référence à la tâche que par le nom de la tâche dans le projet. Ceci est réalisé en ajoutant l'objet de tâche créé aux propriétés de l'objet Project.

L'objet de tâche créé est enregistré dans TaskContainer de Project. Le TaskContainer peut être référencé à partir de la propriété [tâches] du projet (https://docs.gradle.org/current/dsl/org.gradle.api.Project.html#org.gradle.api.Project:tasks). , TaskContainer vous permet de référencer des tâches en utilisant l'accès aux propriétés et les méthodes getByName (), getAt ().

Dans de rares cas, il existe des plugins qui ajoutent un objet d'extension avec le même nom que le nom de la tâche. Par exemple, Eclipse Plugin est nommé ʻeclipseet [Task](https://docs.gradle.org/current/userguide/ eclipse_plugin.html # eclipsetasks) et [Objet d'extension](https://docs.gradle.org/current/userguide/eclipse_plugin.html#sec:eclipse_configuration) sont définis. Dans ce cas, l'objet référencé dansproject.eclipseest l'objet d'extension. Si vous voulez faire référence à une tâche, vous devez passer parTaskContainer comme tasks.eclipse`.

Notez que la méthode spécifiée par une chaîne de caractères telle que getByName () peut également faire référence aux tâches d'autres projets comme : someSubProject: fooTask.

Définir les dépendances des tâches

build.gradle


task foo {
    doFirst {
        println("foo")
    }
}

task bar {
    doFirst {
        println("bar")
    }
}

foo.dependsOn bar

Résultat d'exécution


> gradle foo
bar
foo

[DependsOn ()] de Task (https://docs.gradle.org/current/dsl/org.gradle.api.Task.html#org.gradle.api.Task:dependsOn(java.lang.Object [java.lang.Object] ])) Les dépendances de tâches peuvent être définies à l'aide de la méthode.

Ici, la tâche foo est définie pour dépendre de la tâche bar. Cela garantit que la tâche bar s'exécute en premier chaque fois que vous exécutez la tâche foo.

L'ordre d'exécution des tâches Gradle est essentiellement contrôlé en utilisant cette définition de dépendance (des méthodes de contrôle autres que les dépendances sont également disponibles).

La tâche n'est exécutée qu'une seule fois

Gradle examine les tâches dépendantes avant d'exécuter les tâches spécifiées sur la ligne de commande. Ensuite, les tâches sont exécutées dans l'ordre du côté dépendant.

À ce stade, même s'il existe des tâches qui dépendent d'une pluralité de tâches, toutes les tâches sont contrôlées de sorte qu'elles ne sont toujours exécutées qu'une seule fois.

build.gradle


task foo {
    doFirst { println("foo") }
}

task bar {
    doFirst { println("bar") }
}

task hoge {
    doFirst { println("hoge") }
}

foo.dependsOn bar
hoge.dependsOn foo
hoge.dependsOn bar

Résultat d'exécution


> gradle hoge
bar
foo
hoge

La tâche «hoge» dépend à la fois de «foo» et de «bar». Puisque «foo» dépend également de «bar», un diagramme simple des dépendances de tâches est le suivant.

Les dépendances sont représentées graphiquement


hoge -> foo -> bar
 |              ^
 +--------------+

Si vous exécutez simplement toutes les tâches dépendantes, bar sera exécuté deux fois. Cependant, comme mentionné ci-dessus, Gradle contrôle que même s'il dépend de plusieurs tâches, il n'est exécuté qu'une seule fois.

Où et les notes où vous pouvez écrire dépendent

Puisque dependOn est une méthode de Task, elle peut également être écrite dans le bloc de configuration comme suit.

build.gradle


task foo {
    dependsOn "bar"
    doFirst {
        println("foo")
    }
}

task bar {
    doFirst {
        println("bar")
    }
}

Ici, la tâche bar est spécifiée sous forme de chaîne de caractères. S'il s'agit d'une référence de propriété au lieu d'une chaîne, vous obtiendrez l'erreur suivante:

build.gradle


task foo {
    dependsOn bar //★ Remplacé par la référence de propriété
    doFirst {
        println("foo")
    }
}

task bar {
    doFirst {
        println("bar")
    }
}

Résultat d'exécution


> gradle foo
...
> Could not get unknown property 'bar' for task ':foo' of type org.gradle.api.DefaultTask.
...

BUILD FAILED in 4s

Une erreur se produit car la propriété «bar» est introuvable.

La partie où dependOn bar est écrite est la partie qui est exécutée dans la" phase de configuration "dans le cycle de vie de construction. Dans la phase de configuration, les scripts de construction sont exécutés dans l'ordre du haut. Au moment où "dependOn bar" est exécuté, la "barre des tâches" n'est pas définie car la "barre des tâches" n'a pas encore été exécutée. Par conséquent, la tâche bar n'est pas accessible par référence de propriété dans le bloc de configuration de la tâche foo au-dessus de la définition de la tâche bar.

Ce problème peut être évité par l'une des méthodes suivantes.

--Comme mentionné ci-dessus, définissez en spécifiant une chaîne de caractères --Amenez la définition de tâche bar au-dessus de la définition de tâche foo

Si cela dépend de l'ordre de la description, il devient vulnérable au changement, donc je pense personnellement qu'il vaut mieux spécifier une chaîne de caractères.

Spécifié par l'argument de la méthode task ()

build.gradle


task foo(dependsOn: "bar") {
    doFirst {
        println("foo")
    }
}

task bar {
    doFirst {
        println("bar")
    }
}

Recevoir Map [task ()](https://docs.gradle.org/current/dsl/org.gradle.api.Project.html#org.gradle.api.Project:task (java.util.Map) ,% 20java.lang.String,% 20groovy.lang.Closure))) Vous pouvez également spécifier la tâche dépendante avec l'argument Map.

Appliquer l'ordre d'exécution

build.gradle


apply plugin: "java"

clean.doFirst { println "clean" }
build.doFirst { println "build" }

task cleanBuild(dependsOn: [clean, build])

Résultat d'exécution


> gradle cleanBuild
build
clean

[clean, build] est spécifié comme tâche dépendante de la tâche cleanBuild. Cependant, lorsque la tâche cleanBuild est exécutée, les tâches sont exécutées dans l'ordre de build-> clean.

De cette façon, la simple spécification de «dependOn» ne détermine pas l'ordre d'exécution des tâches (ordre des noms?).

Comment puis-je le contrôler pour qu'il s'exécute dans l'ordre clean-> build? La première idée simple est de définir dependOn pour que build dépende de clean.

build.gradle


apply plugin: "java"

clean.doFirst { println "clean" }
build.doFirst { println "build" }
build.dependsOn clean

task cleanBuild(dependsOn: [clean, build])

Résultat d'exécution


> gradle cleanBuild
clean
build

Maintenant, les tâches sont exécutées dans l'ordre clean-> build.

Cependant, cette méthode rend impossible d'exécuter uniquement «build» (il est toujours «propre»).

Spécifiez uniquement la construction et l'exécution


> gradle build
clean
build

Si possible, je ne veux pas exécuter clean lors de l'exécution de build seul.

Si vous souhaitez limiter l'ordre lorsqu'il est spécifié en même temps, mais que vous voulez pouvoir l'exécuter indépendamment, [mustRunAfter (Object ...)](https://docs.gradle.org/current/ Utilisez dsl / org.gradle.api.Task.html # org.gradle.api.Task: mustRunAfter (java.lang.Object [])).

build.gradle


apply plugin: "java"

clean.doFirst { println "clean" }
build.doFirst { println "build" }
build.mustRunAfter clean

task cleanBuild(dependsOn: [clean, build])

Résultat d'exécution


> gradle cleanBuild
clean
build

> gradle build
build

En utilisant taskA.mustRunAfter (taskB), vous pouvez forcer l'ordre lorsque taskA et taskB sont exécutés en même temps pour être taskB-> taskA.

Spécifier les tâches de post-traitement

build.gradle


task foo {
    doFirst { println "foo" }
    finalizedBy "finalizer"
}

task bar {
    doFirst { 
        println "bar"
        throw new Exception("test")
    }
    finalizedBy "finalizer"
}

task finalizer {
    doFirst { println "finalizer" }
}

Résultat d'exécution


> gradle foo
foo
finalizer

> gradle bar
bar
finalizer

...

Execution failed for task ':bar'.
> java.lang.Exception: test

...

BUILD FAILED in 3s

[finalizedBy ()](https://docs.gradle.org/current/dsl/org.gradle.api.Task.html#org.gradle.api.Task:finalizedBy(java.lang.Object [])) La tâche définie est toujours exécutée, que la tâche se termine normalement ou anormalement.

Spécifiez quand un processus doit être exécuté, comme la libération de ressources.

Spécifiez les conditions à exécuter

build.gradle


task foo {
    doFirst { println "foo" }
    onlyIf { project.hasProperty("hoge") }
}

Résultat d'exécution


> gradle foo
[Aucune sortie]

> gradle foo -Phoge
foo

Utilisation de onlyIf () , Vous pouvez spécifier les conditions pour exécuter l'action de la tâche.

L'action de la tâche n'est exécutée que lorsque la fermeture passée en argument renvoie «true». Si la fermeture renvoie «false», l'action de la tâche est ignorée.

Cependant, seule cette tâche est ignorée et les tâches et tâches dépendantes spécifiées par finalizedBy sont exécutées.

build.gradle


task foo {
    doFirst { println "foo" }
    onlyIf { project.hasProperty("hoge") }
    dependsOn "bar"
    finalizedBy "finalizer"
}

task bar {
    doFirst { println "bar" }
}

task finalizer {
    doFirst { println "finalizer" }
}

Résultat d'exécution


> gradle foo
bar
finalizer

L'exécution de «foo» est ignorée, mais la tâche «barre» dépendante et la tâche «finaliseur» spécifiées dans le finaliseur sont en cours d'exécution.

Sélection de fichiers

Gradle fournit une API flexible pour sélectionner des fichiers et des répertoires dans la classe Project. Lors de l'écriture des paramètres de tâche et des actions, cette API peut être utilisée pour rendre les opérations sur les fichiers concises.

file(Object)

build.gradle


import java.nio.file.Path

task foo {
    doFirst {
        File file1 = file("foo.txt")
        println(file1)

        File file2 = file(new File("foo.txt"))
        println(file2)

        File file3 = file(Path.of("foo.txt"))
        println(file3)
    }
}

Résultat d'exécution


> gradle foo
F:\etc\...\foo.txt
F:\etc\...\foo.txt
F:\etc\...\foo.txt

La méthode file () analyse la valeur reçue en argument et renvoie un objet File.

Il prend en charge une variété d'entrées allant des chemins de chaîne à File et Path (voir la documentation de l'API pour plus de détails).

Si vous voulez que Gradle convertisse correctement n'importe quelle entrée en un objet File, cette méthode file () fonctionne plutôt bien.

files(Object...)

build.gradle


task foo {
    doFirst {
        FileCollection files = files("foo.txt", "bar.txt")
        files.each { println it }
    }
}

Résultat d'exécution


> gradle foo
F:\etc\...\foo.txt
F:\etc\...\bar.txt

La méthode files () remplace FileCollection, qui est une collection de plusieurs fichiers. Produire. L'argument est un argument de longueur variable, et la valeur qui peut être spécifiée avec file () peut être spécifiée de la même manière. En plus de cela, vous pouvez également transmettre ʻIterable, Collection`, etc.

Quoi qu'il en soit, il analyse également la plupart des valeurs dans un FileCollection.

Les méthodes suivantes sont fournies dans FileCollection.

getAsPath()

build.gradle


        FileCollection files = files("foo.txt", "bar.txt")
        println "asPath = ${files.asPath}"

Résultat d'exécution


asPath = F:\etc\...\foo.txt;F:\etc\...\bar.txt

Obtenez une chaîne qui concatène chaque chemin avec un délimiteur pour chaque plate-forme. Le délimiteur est, par exemple, «:» sous Linux et «;» sous Windows.

Il peut être utilisé avec des options telles que le chemin de classe et le chemin du module qui spécifient plusieurs fichiers.

filter(Closure)

build.gradle


        FileCollection files = files("foo.txt", "bar.txt", "fizz.txt", "buzz.txt")
        FileCollection filtered = files.filter { it.name.startsWith("b") }
        filtered.each { println it }

Résultat d'exécution


F:\etc\...\bar.txt
F:\etc\...\buzz.txt

La fermeture passée à filter () retourne un FileCollection avec les éléments réduits uniquement auFichier qui a renvoyé un vrai.

fileTree(Object)

build.gradle


task foo {
    doFirst {
        FileTree tree = fileTree("fromDir")
        tree.each { println it }
    }
}

Résultat d'exécution


> gradle foo
F:\etc\...\fromDir\hoge.txt
F:\etc\...\fromDir\hoge.xml
F:\etc\...\fromDir\sub\foo.txt
F:\etc\...\fromDir\sub\foo.xml

La méthode fileTree () est utilisée pour conserver récursivement des fichiers dans le répertoire spécifié [FileTree](https://docs.gradle.org/current/javadoc/org/gradle/api/file/FileTree. html) peuvent être obtenus. Puisque FileTree hérite de FileCollection, il peut être utilisé de la même manière que FileCollection.

fileTree(Object, Closure)

build.gradle


task foo {
    doFirst {
        FileTree tree = fileTree("fromDir") {
            include "**/*.xml"
        }
        tree.each { println it }
    }
}

Résultat d'exécution


> gradle foo
F:\etc\...\fromDir\hoge.xml
F:\etc\...\fromDir\sub\foo.xml

fileTree () peut passer une fermeture comme deuxième argument.

Le «délégué» de fermeture est un objet ConfigurableFileTree dans le répertoire spécifié par le premier argument. .. Comme son nom l'indique, ConfigurableFileTree est un FileTree configurable qui vous permet de restreindre des fichiers tels que ʻinclude ()et ʻexclude ().

Le modèle de rétrécissement peut être spécifié au format Ant [^ 2].

[^ 2]: Pour le format Ant, recherchez "format de motif fourmi" et vous trouverez diverses informations, alors reportez-vous à cela.

fileTree(Map)

build.gradle


task foo {
    doFirst {
        FileTree tree = fileTree(dir: "fromDir", include: "**/*.xml")
        tree.each { println it }
    }
}

Résultat d'exécution


> gradle foo
F:\etc\...\fromDir\hoge.xml
F:\etc\...\fromDir\sub\foo.xml

Dans fileTree () qui reçoit Map comme argument, la valeur à définir dans la propriété de ConfigurableFileTree peut être spécifiée par Map.

Diagramme de classes lié aux fichiers

Vérifiez à nouveau la relation des classes autour de l'opération de fichier.

gradle.png

FileCollection est une collection de File. FileTree est une collection qui contient récursivement File dans un répertoire spécifique.

Il y a ConfigurableFileCollection et ConfigurableFileTree qui héritent de chacun d'eux, et il est possible de définir pour affiner la cible File.

Entrée / sortie de tâche

build.gradle


task foo {
    doFirst {
        println "foo"
        file("foo.txt").text = project.hoge
    }
}

La tâche foo est une tâche qui renvoie la valeur définie dans la propriété hoge de project à foo.txt. Essayez d'exécuter cette tâche plusieurs fois.

Résultat d'exécution


> gradle foo -Phoge=HOGE
> Task :foo
foo

BUILD SUCCESSFUL in 3s
1 actionable task: 1 executed

> gradle foo -Phoge=HOGE
> Task :foo
foo

BUILD SUCCESSFUL in 3s
1 actionable task: 1 executed

Bien entendu, lorsque la tâche est exécutée, l'action de la tâche est exécutée et le fichier est sorti.

Cependant, tant que l'entrée n'a pas changé, cette tâche ne sortira que le même résultat, quel que soit le nombre de fois qu'elle est exécutée. Il est inutile de l'exécuter à chaque fois, même si vous savez que la sortie ne change pas.

Si cette tâche prend du temps, elle peut avoir un impact significatif sur le temps de construction.

Gradle fournit un mécanisme pour ignorer l'exécution d'une tâche si le résultat de la tâche ne change pas. Pour utiliser ce mécanisme, il est nécessaire de définir l'entrée / sortie de la tâche.

build.gradle


task foo {
    def outputFile = file("foo.txt")

    inputs.property "hoge", project.hoge
    outputs.file outputFile

    doFirst {
        println "foo"
        outputFile.text = hoge
    }
}

Résultat d'exécution


> gradle foo -Phoge=HOGE
> Task :foo
foo

BUILD SUCCESSFUL in 3s
1 actionable task: 1 executed

> gradle foo -Phoge=HOGE
> Task :foo UP-TO-DATE

BUILD SUCCESSFUL in 3s
1 actionable task: 1 up-to-date

> gradle foo -Phoge=HOGEHOGE
> Task :foo
foo

BUILD SUCCESSFUL in 3s
1 actionable task: 1 executed

Lorsque la valeur de hoge est définie sur la même valeur que la première fois et qu'elle est exécutée, ʻUP-TO-DATE (le plus récent) est affiché à droite de l'affichage de la tâche foo, et l'exécution de la tâche est sautée. Après avoir changé la valeur de hoge, la tâche foo` a été exécutée à nouveau.

build.gradle


task foo {
    ...
    inputs.property "hoge", project.hoge
    outputs.file outputFile
    ...
}

Pour définir l'entrée et la sortie d'une tâche, accédez d'abord à l'objet pour définir l'entrée et la sortie à l'aide des deux propriétés suivantes définies dans Tâche.

Les propriétés respectives sont TaskInputs (https://docs.gradle.org/current/javadoc/org/gradle/api/tasks/TaskInputs.html) et TaskOutputs (https://docs.gradle.org/). current / javadoc / org / gradle / api / tasks / TaskOutputs.html) est renvoyé. Utilisez les méthodes fournies par ces classes pour définir des entrées et des sorties spécifiques.

Gradle enregistre le contenu de l'entrée et de la sortie lorsque la tâche est exécutée. Ensuite, la prochaine fois que la même tâche est exécutée, il vérifie si le contenu de l'entrée et de la sortie a changé [^ 1]. Si les entrées et les sorties n'ont pas changé, Gradle ignore l'exécution de la tâche. Si l'une des valeurs spécifiées pour l'entrée et la sortie a changé, la tâche sera exécutée normalement.

[^ 1]: Si le fichier de sortie est supprimé, il doit être réexécuté, il est donc nécessaire de vérifier également l'état de la sortie.

Les entrées et les sorties doivent être définies. Ce mécanisme de saut ne fonctionne pas si un seul (entrée uniquement, sortie uniquement) est spécifié.

Définition d'entrée

Les trois suivants peuvent être largement utilisés pour l'entrée.

--Propriétés - property(String, Object) - properties(Map)

La propriété peut être n'importe quelle clé et valeur, comme indiqué dans l'exemple ci-dessus. Cependant, les valeurs doivent être sérialisables et comparables à ʻequals () `.

Si vous spécifiez un fichier, il est vérifié pour voir si le contenu du fichier a changé.

Si un répertoire est spécifié, l'état sous ce répertoire est vérifié. Si un fichier ou un répertoire est créé dans le répertoire ou si le contenu du fichier est modifié, il est jugé que l'entrée a changé. Ce jugement cible récursivement les sous-répertoires et les inférieurs.

build.gradle


task foo {
    ...
    inputs.file file("input.txt") //Spécifiez le fichier d'entrée
    inputs.dir file("inputDir")   //Spécification du répertoire d'entrée
    ...
}

Définition de sortie

Les deux sorties suivantes peuvent être utilisées.

build.gradle


task foo {
    ...
    outputs.file file("output.txt") //Spécifiez le fichier de sortie
    outputs.dir file("outputDir")   //Spécification du répertoire de sortie
    ...
}

Règles de tâche

build.gradle


tasks.addRule("Pattern: echo<MESSAGE>") { taskName ->
    if (taskName.startsWith("echo")) {
        task(taskName) {
            doFirst { println(taskName - "echo") }
        }
    }
}

Résultat d'exécution


> gradle echoHoge
Hoge

> gradle tasks
...

Rules
-----
Pattern: echo<MESSAGE>

Méthode addRule () de TaskContainer Vous pouvez utiliser pour définir ce que l'on appelle une règle de tâche.

ʻAddRule () `transmet la description de la règle de tâche dans le premier argument et la fermeture dans le deuxième argument. La fermeture reçoit le nom de la tâche à laquelle elle essayait de faire référence (ici, la chaîne «" echoHoge "» spécifiée sur la ligne de commande est passée).

Si vous définissez une tâche avec le nom reçu dans cette fermeture, aucune erreur ne se produira même si la tâche avec ce nom n'est pas prédéfinie et la tâche définie dynamiquement sera adoptée. En d'autres termes, même pour les tâches pour lesquelles le nom de la tâche ne peut pas être défini statiquement à l'avance, la tâche peut être définie dynamiquement au moment de l'exécution en fournissant un modèle (règle) dans le nom de la tâche.

Les règles définies dans cette règle de tâche peuvent également être référencées par dependOn.

build.gradle


tasks.addRule("Pattern: echo<MESSAGE>") { taskName ->
    if (taskName.startsWith("echo")) {
        task(taskName) {
            doFirst { println(taskName - "echo") }
        }
    }
}

task foo(dependsOn: echoBar) {
    doFirst { println "foo" }
}

Résultat d'exécution


> gradle foo
Bar
foo

Ici, ʻechoBarest spécifié dansdependOn de la tâche foo. Encore une fois, les règles de tâche s'appliquent et la tâche ʻechoBar est générée dynamiquement.

Règle propre fournie en standard

build.gradle


apply plugin: "base"

Résultat d'exécution


> gradle tasks
...

Rules
-----
Pattern: clean<TaskName>: Cleans the output files of a task.

Lorsque vous chargez le plug-in base, la règle clean sera appliquée. (Ce plug-in est chargé ensemble si vous appliquez le plug-in Java, etc., donc fondamentalement, il n'est pas chargé explicitement)

Cette règle crée une tâche qui supprime le fichier de sortie de n'importe quelle tâche.

build.gradle


apply plugin: "base"

task foo {
    def outputFile = file("foo.txt")
    outputs.file outputFile

    doFirst {
        outputFile.text = "foo!!"
    }
}

Ici, nous définissons une tâche appelée «foo». Cette tâche définit un fichier appelé foo.txt comme fichier de sortie.

Résultat d'exécution


> type foo.txt
Le fichier spécifié est introuvable.

> gradle foo
...
BUILD SUCCESSFUL in 3s
1 actionable task: 1 executed

> type foo.txt
foo!!

> gradle cleanFoo
...
BUILD SUCCESSFUL in 3s
1 actionable task: 1 executed

> type foo.txt
Le fichier spécifié est introuvable.

La tâche cleanFoo est générée dynamiquement par la règle clean et est définie pour supprimer le fichier de sortie de la tâche foo.

Tâche personnalisée

Les tâches créées par task () ne peuvent être utilisées que dans ce projet. En outre, le même processus ne peut pas être réutilisé dans plusieurs emplacements avec des paramètres différents.

Si vous souhaitez réutiliser la même tâche dans plusieurs projets avec des paramètres différents, créez une tâche personnalisée.

build.gradle


class FooTask extends DefaultTask {
    @TaskAction
    def foo() {
        println("FOO!!")
    }
}

task foo(type: FooTask)

Résultat d'exécution


> gradle foo
FOO!!

Les tâches personnalisées sont créées en héritant de «DefaultTask». Si vous définissez une méthode arbitraire et que vous l'annotez avec @TaskAction, cette méthode sera l'action de la tâche. Sera exécuté.

Pour utiliser la tâche personnalisée que vous avez créée, recevez Map [task ()](https://docs.gradle.org/current/dsl/org.gradle.api.Project.html#org.gradle.api Utilisez .Project: task (java.util.Map,% 20java.lang.String)). Dans cet argument Map, spécifiez le type de tâche que vous souhaitez créer dans type. Ensuite, l'objet de tâche sera créé avec le type spécifié.

Ici, par souci de simplicité, les tâches personnalisées sont définies dans le même script de construction. Cependant, en réalité, il sera défini dans un emplacement externe qui peut être référencé par plusieurs projets (comme «buildSrc» décrit plus tard ou un fichier jar externe).

Paramètres de tâche personnalisés

build.gradle


class FooTask extends DefaultTask {

    String message

    @TaskAction
    def foo() {
        println("FOO!! message=${message}")
    }
}

task foo(type: FooTask) {
    message = "Hello World"
}

Résultat d'exécution


> gradle foo
FOO!! message=Hello World

Une tâche créée en spécifiant un type avec «type» est un objet de ce type. En d'autres termes, si vous définissez des propriétés et des méthodes de paramétrage pour une tâche personnalisée, vous pouvez spécifier les paramètres de l'objet de tâche partout où vous pouvez accéder à l'objet de tâche généré.

Ici, la valeur est définie dans la propriété message dans le bloc de paramètres.

Les paramètres de fichier sont définis dans Object

build.gradle


class FooTask extends DefaultTask {

    List inputFiles = []
    Object output

    @TaskAction
    def foo() {
        def out = project.file(this.output)
        out.text = ""
        project.files(this.inputFiles)
               .each { out.append("${it.name}\n") }
    }
}

task foo (type: FooTask) {
    inputFiles = ["fromDir/hoge.txt", "build.gradle", file(".gitignore")]
    output = file("output/test.txt")
}

Lors de la définition de fichiers et de répertoires pour l'entrée et la sortie des tâches, le type doit être un type qui peut contenir tout ce qui peut être "Objet" ou "Liste". Ensuite, lorsque vous utilisez réellement la valeur de réglage, convertissez en utilisant file () ou files () de Project.

Cela vous permet de spécifier des fichiers dans une variété de formats pris en charge par Project # file (), tels que des chaînes et des objets File, vous donnant une plus grande flexibilité de configuration.

Spécification d'entrée / sortie

build.gradle


class FooTask extends DefaultTask {
    @Input
    String message
    @OutputFile
    Object output

    @TaskAction
    def foo() {
        project.file(output).text = message
    }
}

task foo(type: FooTask) {
    message = "Hello World!!"
    output = "foo.txt"
}

Résultat d'exécution


> gradle foo
...
BUILD SUCCESSFUL in 4s
1 actionable task: 1 executed

> type foo.txt
Hello World!!

> gradle foo
...
BUILD SUCCESSFUL in 4s
1 actionable task: 1 up-to-date

Les tâches personnalisées permettent de définir les entrées et sorties de tâches avec des annotations.

Vous pouvez définir les entrées et sorties de tâche en annotant les champs de tâche personnalisés et la méthode Getter avec les annotations suivantes.

--Contribution - @Input - @InputFile - @InputFiles - @InputDirectory --Production - @OutputFile - @OutputFiles - @OutputDirectory - @OutputDirectories

Tâches fournies en standard

Les classes de tâches sont fournies en standard pour les processus susceptibles d'être utilisés fréquemment. Par exemple, il y a les tâches suivantes.

Copy

build.gradle


task foo(type: Copy) {
    from "fromDir"
    into "toDir"
}

Résultat d'exécution


> tree /f fromDir
...
│  hoge.txt
│
└─sub
        foo.txt

> gradle foo
...

> tree /f toDir
│  hoge.txt
│
└─sub
        foo.txt

Copie La tâche effectue une copie d'un fichier ou d'un répertoire.

CopySpec implémenté par Copy fournit le paramétrage de la méthode de copie. Utilisez l'API existante. CopySpec fournit diverses méthodes pour réduire la cible de la copie et spécifier la destination de la copie, et il permet des paramètres assez flexibles.

from(Object...)

build.gradle


task foo(type: Copy) {
    from "fooDir", "barFile"
    from file("fizzDir"), file("buzzFile")
    ...
}

Spécifiez le fichier ou le répertoire source de la copie. Vous pouvez spécifier plusieurs arguments, ou vous pouvez appeler from () lui-même plusieurs fois.

La valeur passée en argument est finalement les [files (Object ...)] de Project (https://docs.gradle.org/current/dsl/org.gradle.api.Project.html#org.gradle .api.Project: passé à la méthode files (java.lang.Object [])). Par conséquent, vous pouvez transmettre diverses valeurs telles qu'un chemin de chaîne ou un objet File.

from(Object, Closure)

build.gradle


task foo(type: Copy) {
    from("fromDir") {
        include "**/*.txt"
    }
    ...
}

Vous pouvez passer une fermeture comme deuxième argument de from (). Le délégué de cette fermeture est CopySpec, et il est possible de restreindre davantage les conditions de copie uniquement dans le répertoire spécifié parfrom (). Ici, les fichiers à copier par ʻinclude () sont limités à * .txt`.

into(Object)

build.gradle


task foo(type: Copy) {
    into "toDir"
    ...
}

Spécifiez le répertoire de destination de la copie.

include(String...)

build.gradle


task foo(type: Copy) {
    include "*.txt", "**/*.xml"
    ...
}

Vous pouvez spécifier les conditions à inclure dans la cible de copie en spécifiant le modèle au format Ant.

exclude(String...)

build.gradle


task foo(type: Copy) {
    exclude "**/*.class", "**/*.bk"
    ...
}

Ici, vous pouvez également spécifier les conditions à exclure de la copie avec un modèle au format Ant.

rename(Closure)

build.gradle


task foo(type: Copy) {
    from "fromDir"
    into "toDir"
    rename { name ->
        name.toUpperCase()
    }
}

Vous pouvez utiliser rename () pour renommer le fichier lors de la copie. Dans le cas de rename () qui reçoit une fermeture, le nom du fichier avant la copie (String) est passé à l'argument de la fermeture. Ensuite, la valeur renvoyée par la fermeture est utilisée comme nom de fichier après la copie.

Dans le cas de l'implémentation ci-dessus, le nom de fichier dans lequel le nom de fichier d'origine est entièrement en majuscule est utilisé comme nom de fichier de destination de la copie.

rename(String, String)

build.gradle


task foo(type: Copy) {
    from "fromDir"
    into "toDir"
    rename(/(.*)\.([a-z]+)/, '$1_copy.$2')
}

Rename (), qui reçoit deux Strings comme arguments, passe une expression régulière comme premier argument et l'expression renommée comme deuxième argument.

La partie définie comme un groupe dans l'expression régulière du premier argument (la partie entourée par «()») peut être désignée par «$ n» dans l'expression du deuxième argument («n» est un numéro de série de 1).

Dans l'exemple ci-dessus, le premier groupe est (. *), Qui pointe vers le nom de base du fichier avant l'extension. Le deuxième groupe est la partie ([a-z] +), qui pointe vers la partie d'extension.

Ensuite, en spécifiant «$ 1_copy. $ 2» dans l'expression après avoir renommé, le fichier est renommé avec «_copy» à la fin du nom de base et copié.

Lorsqu'il est réellement déplacé, il devient comme suit.

Résultat d'exécution


> tree /f fromDir
...
│  hoge.txt
│  hoge.xml
│
└─sub
        foo.txt
        foo.xml

> gradle foo
...

> tree /f toDir
...
│  hoge_copy.txt
│  hoge_copy.xml
│
└─sub
        foo_copy.txt
        foo_copy.xml

Copie du projet ()

Pour la classe Project, copy (Closure) est fournie.

L'argument de fermeture delegate implémente l'interface CopySpec, qui vous permet de spécifier la cible de copie de la même manière que la tâche Copy.

build.gradle


task foo {
    doFirst {
        copy {
            from "fromDir"
            into "toDir"
            include "**/*.txt"
        }
    }
}

Si vous souhaitez effectuer plusieurs opérations de copie en une seule tâche, vous pouvez également utiliser la méthode copy () de ce Projet.

Delete

build.gradle


task foo(type: Delete) {
    delete "targetDir/aaa.txt", "targetDir/hoge"
}

Résultat d'exécution


> tree /f targetDir
│  aaa.txt
│  bbb.txt
│
└─hoge
    │  ccc.txt
    │
    └─fuga
            ddd.txt

> gradle foo
...
BUILD SUCCESSFUL in 5s

> tree /f targetDir
    bbb.txt

Supprimer Les tâches vous permettent de supprimer des fichiers et des dossiers.

La classe Delete implémente l'interface DeleteSpec. [Supprimer (Objet ...)] défini dans cette interface (https://docs.gradle.org/current/dsl/org.gradle.api.tasks.Delete.html#org.gradle.api.tasks .Delete: delete (java.lang.Object [])) Spécifiez la cible de suppression avec la méthode. La valeur spécifiée dans l'argument est transmise à la méthode Project # files (Object ...), elle peut donc être spécifiée dans différents types tels qu'une chaîne de caractères et File.

«Delete» lui-même ne fournit pas d'API qui vous permet de spécifier «un fichier dans un répertoire spécifique et un modèle spécifique». Cependant, comme mentionné ci-dessus, la valeur passée à delete () est transmise à Project # files (), vous pouvez donc également passer FileCollection. En d'autres termes, il peut être spécifié comme suit.

build.gradle


task foo(type: Delete) {
    delete fileTree(dir: "targetDir", include: "hoge/**/*.txt")
}

Résultat d'exécution


> tree /f targetDir
│  aaa.txt
│  bbb.txt
│
└─hoge
    │  ccc.txt
    │
    └─fuga
            ddd.txt

> gradle foo
...
BUILD SUCCESSFUL in 5s

> tree /f targetDir
│  aaa.txt
│  bbb.txt
│
└─hoge
    └─fuga

Méthode de suppression de projet ()

Comme copy (), delete (Action) est fournie dans Project.

Puisque la fermeture delegate implémente DeleteSpec, vous pouvez spécifier la cible de suppression de la même manière que la tâche Delete.

build.gradle


task foo {
    doFirst {
        project.delete { delete "targetDir" }
    }
}

La mise en garde ici est que chaque fois que vous appelez la méthode delete (), vous devez la préfixer avec project, comme project.delete ().

build.gradle


task foo {
    doFirst {
        project.delete {
            println "project.delete.delegate=${delegate.class}"
        }

        delete {
            println "delete.delegate=${delegate.class}"
        }
    }
}

Résultat d'exécution


> gradle foo
project.delete.delegate=class org.gradle.api.internal.file.delete.DefaultDeleteSpec
delete.delegate=class build_32bm3o8iprxruz9mv43mbtt86$_run_closure1$_closure2

Pour project.delete (), delegate est DeleteSpec, mais pourdelete ()seulement, ce n'est pas DeleteSpec.

Au fait, pour copy (), il n'y a pas de problème car delegate devient CopySpec même si project n'est pas ajouté. Apparemment, si vous avez seulement une méthode qui prend ʻAction comme argument, vous aurez besoin de project (copy ()acopy (Closure)etcopy (Action), mais delete ( ) ʻOnlydelete (Action)).

Pour être honnête, je ne sais pas pourquoi cela se produit.

Sync

build.gradle


task foo(type: Sync) {
    from "fromDir"
    into "toDir"
    include "**/*.txt"
}

Résultat d'exécution


> tree /f fromDir
│  aaa.txt
│  bbb.txt
│  eee.xml
│
└─hoge
    │  ccc.txt
    │  fff.xml
    │
    └─fuga
            ddd.txt
            ggg.xml

> tree /f toDir
    hoge.txt

> gradle foo
...
BUILD SUCCESSFUL in 5s

> tree /f toDir
│  aaa.txt
│  bbb.txt
│
└─hoge
    │  ccc.txt
    │
    └─fuga
            ddd.txt

Sync La tâche est la même que le répertoire spécifié par ʻintoet le répertoire spécifié parfrom`. Mettre à jour à l'état. Puisque «Sync» implémente «CopySpec», vous pouvez contrôler la méthode de copie de la même manière que la tâche «Copier».

Le fichier hoge.txt qui existait dans toDir avant l'exécution de la tâche a été supprimé. La synchronisation avec Sync est effectuée ** par défaut après la suppression de tous les fichiers et dossiers de destination **.

Si vous avez un fichier ou un dossier qui n'existe que dans la destination de synchronisation et que vous ne voulez pas qu'il soit supprimé, [préserve](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.Sync.html Spécifiez # org.gradle.api.tasks.Sync: preserve (org.gradle.api.Action)).

build.gradle


task foo(type: Sync) {
    from "fromDir"
    into "toDir"
    include "**/*.txt"
    preserve {
        include "hoge.txt"
    }
}

Résultat d'exécution


> tree /f toDir
    fuga.txt
    hoge.txt
    piyo.txt

> gradle foo
...
BUILD SUCCESSFUL in 5s

> tree /f toDir
│  aaa.txt
│  bbb.txt
│  hoge.txt
│
└─hoge
    │  ccc.txt
    │
    └─fuga
            ddd.txt

La cible spécifiée par ʻinclude dans preserve n'est plus supprimée (preserve` signifie" save ").

Il est également possible de spécifier ʻexclude, et si ʻexclude "hoge.txt" est spécifié, seul hoge.txt sera supprimé (il ne sera pas enregistré).

Si ʻinclude et ʻexclude sont spécifiés en même temps, le réglage de ʻinclude` semble avoir la priorité.

Méthode Project sync ()

Comme vous pouvez l'imaginer, [sync (Action)](https://docs.gradle.org/current/dsl/org.gradle.api.Project.html#org.gradle.api.Project:sync(org.gradle.api. Action)) La méthode est également fournie dans Projet.

Cependant, le délégué de cette fermeture implémente l'interface CopySpec. En d'autres termes, conserve ne peut pas être spécifié (la référence dit que conserve peut être spécifié, mais si vous l'écrivez réellement, une erreur se produira).

build.gradle


task foo {
    doFirst {
        project.sync {
            from "fromDir"
            into "toDir"
            include "**/*.txt"
        }
    }
}

Puisque sync () n'a que sync (Action), une erreur se produira à moins que le projet ne soit modifié comme delete ().

Exec

build.gradle


task foo(type: Exec) {
    workingDir "workDir"
    environment MESSAGE: "Hello World!!"
    commandLine "cmd", "/c", "echo", "%MESSAGE%", ">", "hello.txt"
}

Résultat d'exécution


> gradle foo
...
BUILD SUCCESSFUL in 5s

> type workDir\hello.txt
Hello World!!

Exec Les tâches vous permettent d'exécuter des commandes arbitraires. ʻExec` implémente l'interface ExecSpec, qui définit les méthodes de configuration. ..

[commandLine (Object ...)](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.Exec.html#org.gradle.api.tasks.Exec:commandLine (java.) Dans lang.Object [])), spécifiez la commande que vous souhaitez exécuter. Pour Linux, vous pouvez commencer avec la commande que vous voulez exécuter directement, mais pour Windows, vous devez continuer avec cmd / c <commande que vous voulez réellement exécuter>.

[workingDir (Object)](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.Exec.html#org.gradle.api.tasks.Exec:workingDir(java.lang.Object) Vous pouvez spécifier le répertoire de travail lors de l'exécution avec)).

environnement (carte)), Vous pouvez définir des variables d'environnement dans le sous-processus qui exécute la commande.

Méthode exec () du projet

build.gradle


task foo {
    doFirst {
        exec {
            commandLine "cmd", "/c", "echo", "Hello World!!"
        }
    }
}

Projet exec (fermeture)) Vous pouvez utiliser la méthode pour exécuter des commandes de la même manière que ʻExec`.

Le délégué de fermeture est ʻExecSpec`.

Zip

build.gradle


task foo(type: Zip) {
    baseName = "foo"
    from "fromDir"
    destinationDir = file("toDir")
    include "**/*.txt"
}

Résultat d'exécution


> tree /f fromDir
│  aaa.txt
│  bbb.txt
│  eee.xml
│
└─hoge
    │  ccc.txt
    │  fff.xml
    │
    └─fuga
            ddd.txt
            ggg.xml

> gradle foo
...
BUILD SUCCESSFUL in 5s

> dir /b toDir
foo.zip

La tâche Zip vous permet de compresser n'importe quel répertoire.

Zip implémente l'interface CopySpec, donc il peut être ciblé de la même manière que la tâche Copy.

Cependant, la destination de sortie est destinationDir Précisez dans la propriété. Notez que cette propriété est File, pas ʻObject. Si vous avez appliqué le plug-in base, destinationDirest par défautbuild / distributions. Si le plug-in base n'est pas appliqué (fondamentalement impossible ...), la spécification de destinationDir` est obligatoire.

Le nom du fichier zip est créé en concaténant les valeurs définies dans certaines propriétés.

build.gradle


task foo(type: Zip) {
    baseName = "baseName"
    appendix = "appendix"
    version = "version"
    classifier = "classifier"
    extension = "extension"

    from "fromDir"
    destinationDir = file("toDir")
    include "**/*.txt"
}

Résultat d'exécution


> gradle foo
...
BUILD SUCCESSFUL in 5s

> dir /b toDir
baseName-appendix-version-classifier.extension

Le nom de fichier entier est créé en connectant chaque propriété avec un trait d'union comme suit. (Les propriétés qui ne sont pas définies sont ignorées (si ʻextension n'est pas définie, elle devient "zip" `))

baseName-appendix-version-classifier.extension

Si vous souhaitez spécifier le nom complet du fichier à la fois, [archiveName](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.bundling.Zip.html#org.gradle.api.tasks .bundling.Zip: archiveName) Spécifiez la propriété.

Le chemin du fichier zip généré par la tâche Zip est [archivePath](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.bundling.Zip.html#org.gradle" Vous pouvez le vérifier dans la propriété .api.tasks.bundling.Zip: archivePath).

Méthode zipTree () du projet

Malheureusement? Il n'y a pas de méthode appelée zip () dans Project.

[ZipTree (Object)](https://docs.gradle.org/current/dsl/org.gradle.api.Project.html#org.gradle.] Pour extraire le contenu de zip, mais pas un substitut. La méthode api.Project: zipTree (java.lang.Object)) est fournie. Si vous spécifiez le chemin du fichier zip cible comme argument, FileTree contenant les informations sur le contenu de ce zip est renvoyé.

build.gradle


task foo {
    doFirst {
        def zip = zipTree("toDir/foo.zip")
        zip.each { println it }
    }
}

Résultat d'exécution


> gradle foo
F:\etc\...\build\tmp\expandedArchives\foo.zip_d658f891babbd7031de320615856ccb1\aaa.txt
F:\etc\...\build\tmp\expandedArchives\foo.zip_d658f891babbd7031de320615856ccb1\bbb.txt
F:\etc\...\build\tmp\expandedArchives\foo.zip_d658f891babbd7031de320615856ccb1\eee.xml
F:\etc\...\build\tmp\expandedArchives\foo.zip_d658f891babbd7031de320615856ccb1\hoge\ccc.txt
F:\etc\...\build\tmp\expandedArchives\foo.zip_d658f891babbd7031de320615856ccb1\hoge\fff.xml
F:\etc\...\build\tmp\expandedArchives\foo.zip_d658f891babbd7031de320615856ccb1\hoge\fuga\ddd.txt
F:\etc\...\build\tmp\expandedArchives\foo.zip_d658f891babbd7031de320615856ccb1\hoge\fuga\ggg.xml

Immédiatement après avoir fait zipTree (), le fichier zip n'a pas encore été décompressé. Lorsque vous essayez d'accéder aux informations du fichier dans ZipTree, le fichier zip est en fait décompressé dans le répertoire temporaire et vous pouvez accéder aux informations du fichier.

En combinant ces «zipTree ()» et «copy ()», vous pouvez reproduire la décompression de zip.

build.gradle


task foo {
    doFirst {
        copy {
            from zipTree("toDir/foo.zip")
            into "build/unzip"
        }
    }
}

Cela décompressera le contenu du zip sous le répertoire build / unzip.

buildSrc Gradle a son propre projet de déploiement de classes utilisées dans les scripts de construction.

Structure des dossiers


|-build.gradle
`-buildSrc/
  `-src/main/groovy/
    `-sample/
      `-HogeTask.groovy

Le répertoire racine buildSrc devient un projet dédié. Vous pouvez placer votre propre tâche ou code de plug-in dans le répertoire src / main / groovy de ce projet.

HogeTask.groovy


package sample

import org.gradle.api.DefaultTask
import org.gradle.api.tasks.TaskAction

class HogeTask extends DefaultTask {

    @TaskAction
    def hoge() {
        println("Hello Hoge.")
    }
}

build.gradle


task foo(type: sample.HogeTask)

Résultat d'exécution


> gradle foo
Hello Hoge.

Si le code source existe dans buildSrc, il sera automatiquement compilé lors de l'exécution de la tâche et rendu disponible dans le script de construction.

Brancher

En utilisant une tâche personnalisée, la logique de la tâche peut être définie à l'avance et seule la valeur définie peut être commutée et réutilisée à plusieurs endroits. Cependant, une tâche personnalisée n'est qu'une tâche unique et toutes les générations de tâches et les paramètres détaillés doivent être spécifiés par l'utilisateur.

Afin de partager et de réutiliser un plus large éventail de processus tels que la création de plusieurs tâches et la définition de dépendances entre les tâches, un mécanisme appelé ** plug-in ** est utilisé.

Structure des dossiers


|-build.gradle
`-buildSrc/
  `-src/main/groovy/
    `-sample/
      `-FooPlugin.groovy

FooPlugin.groovy


package sample

import org.gradle.api.Project
import org.gradle.api.Plugin

class FooPlugin implements Plugin<Project> {

    @Override
    void apply(Project project) {
        project.task("hello") {
            doFirst { println("FOO!!") }
        }
    }
}

build.gradle


apply plugin: sample.FooPlugin

Résultat d'exécution


> gradle hello
FOO!!

Les plugins sont créés en implémentant l'interface Plugin (l'argument de type est Project). ..

La méthode ʻApply (T) est implémentée et le traitement du plug-in y est décrit. Le contenu à décrire est le même que le contenu normalement décrit dans build.gradle, et il n'y a aucun problème. Cependant, dans le cas de build.gradle, la délégation à Project a été implicitement exécutée, donc task () etc. pourrait être appelée directement, mais ici la délégation implicite est effectuée. Puisqu'il n'y a rien de tel, il est nécessaire d'appeler explicitement la méthode de Project`.

Le plug-in créé peut être chargé avec ʻapply` comme les autres plug-ins.

Paramètres du plugin

Si vous souhaitez transmettre la valeur du paramètre au plug-in, utilisez le mécanisme de l'objet d'extension.

FooPlugin.groovy


package sample

import org.gradle.api.Project
import org.gradle.api.Plugin

class FooPlugin implements Plugin<Project> {

    @Override
    void apply(Project project) {
        def foo = project.extensions.create("foo", FooPluginExtension)

        project.task("hello") {
            doFirst { println(foo.message) }
        }
    }
}

class FooPluginExtension {
    String message
}

Pour créer un objet d'extension, utilisez la méthode create () de ExtensionContainer. ʻExtensionContainerest obtenu à partir de la propriété [extensions](https://docs.gradle.org/current/dsl/org.gradle.api.Project.html#org.gradle.api.Project:extensions) deProject` ça peut.

La méthode create () passe le nom de l'objet d'extension dans le premier argument et le type de l'objet d'extension dans le second argument. Ici, «foo» est spécifié pour le nom et «FooPluginExtension» est spécifié pour le type.

La valeur de retour de create () est un objet de la classe spécifiée par le deuxième argument, et la valeur de réglage spécifiée dans le script de construction est accessible via cet objet.

L'objet d'extension généré a été ajouté en tant que propriété à Project afin qu'il puisse être référencé par le nom spécifié dans create ().

build.gradle


apply plugin: sample.FooPlugin

foo.message = "Hello Foo!!"

Résultat d'exécution


> gradle hello
Hello Foo!!

Le bloc de réglage étant disponible pour l'objet d'extension, il peut également être décrit comme suit.

build.gradle


apply plugin: sample.FooPlugin

foo {
    message = "Hello Foo!!"
}

Lorsqu'il existe plusieurs valeurs de réglage, la description peut être simplifiée à l'aide du bloc de réglage.

Argument du constructeur d'objet d'extension

Si vous souhaitez recevoir l'argument dans le constructeur de l'objet d'extension, écrivez comme suit.

FooPlugin.groovy


package sample

import org.gradle.api.Project
import org.gradle.api.Plugin

class FooPlugin implements Plugin<Project> {

    @Override
    void apply(Project project) {
        def foo = project.extensions.create("foo", FooPluginExtension, project)

        project.task("hello") {
            doFirst { println(foo.outputDir) }
        }
    }
}

class FooPluginExtension {
    Object outputDir

    FooPluginExtension(Project project) {
        this.outputDir = project.file("${project.buildDir}/foo")
    }
}

L'objet d'extension FooPluginExtension prend un objet Project et définit la valeur par défaut de ʻoutputDir sur la valeur générée à partir de Project`.

Pour passer une valeur à cet argument de constructeur, implémentez-le de sorte que la valeur correspondante soit passée après le troisième argument de la méthode create (). Le troisième argument de create () est un argument de longueur variable, et la valeur spécifiée ici est passée au constructeur de l'objet d'extension tel quel.

Utilisez la valeur définie dans l'objet d'extension pour la valeur de paramètre de tâche

FooPlugin.groovy


package sample

import org.gradle.api.Project
import org.gradle.api.Plugin

class FooPlugin implements Plugin<Project> {

    @Override
    void apply(Project project) {
        def foo = project.extensions.create("foo", FooPluginExtension)

        project.task("copyFile") {
            inputs.file foo.inputFile
            outputs.dir foo.outputDir

            doFirst {
                project.copy {
                    from foo.inputFile
                    into foo.outputDir
                }
            }
        }
    }
}

class FooPluginExtension {
    Object inputFile
    Object outputDir
}

Une tâche appelée copyFile est créée et implémentée pour copier l'objet d'extension ʻinputFile dans ʻoutputDir. De plus, ʻinputFile et ʻoutputDir sont respectivement définis pour l'entrée et la sortie de la tâche copyFile.

build.gradle


apply plugin: sample.FooPlugin

foo {
    inputFile = "fromDir/aaa.txt"
    outputDir = "build"
}

Lorsque je fais cela, la construction échoue comme suit:

Résultat d'exécution


> gradle copyFile
...
> Task :copyFile FAILED

FAILURE: Build failed with an exception.

* What went wrong:
Some problems were found with the configuration of task ':copyFile'.
> No value has been specified for property '$1'.
...

Avec cela seul, je ne sais pas de quoi vous parlez, mais si vous ajoutez la sortie suivante pour le débogage, vous verrez ce qui se passe.

FooPlugin.groovy


package sample

import org.gradle.api.Project
import org.gradle.api.Plugin

class FooPlugin implements Plugin<Project> {

    @Override
    void apply(Project project) {
        println "FooPlugin.apply()début"
        def foo = project.extensions.create("foo", FooPluginExtension)

        project.task("copyFile") {
            inputs.file foo.inputFile
            outputs.dir foo.outputDir
            
            println "foo.inputFile=${foo.inputFile}"
            println "foo.outputDir=${foo.outputDir}"

            doFirst {
                project.copy {
                    from foo.inputFile
                    into foo.outputDir
                }
            }
        }
        println "FooPlugin.apply()Fin"
    }
}

class FooPluginExtension {
    Object inputFile
    Object outputDir
}

build.gradle


println "Avant d'appliquer FooPlugin"
apply plugin: sample.FooPlugin
println "Après avoir appliqué FooPlugin"

println "toto Spécifiez la valeur de paramètre pour l'objet d'extension"
foo {
    inputFile = "fromDir/aaa.txt"
    outputDir = "build"
}

Résultat d'exécution


> gradle createFile
...
Avant d'appliquer FooPlugin
FooPlugin.apply()début
foo.inputFile=null
foo.outputDir=null
FooPlugin.apply()Fin
Après avoir appliqué FooPlugin
toto Spécifiez la valeur de paramètre pour l'objet d'extension

> Task :copyFile FAILED
...

Au stade de la définition de l'entrée / sortie de la tâche createFile, la valeur n'a pas encore été définie pour l'objet d'extension foo. Par conséquent, la spécification d'entrée / sortie est «nulle».

L'application du plug-in est effectuée dans la phase de paramétrage du cycle de vie de construction, et le processus de paramétrage est essentiellement exécuté dans l'ordre du haut. Par conséquent, dans la méthode ʻapply () du plug-in personnalisé, l'objet d'extension foo` n'a pas encore été défini, donc la valeur ne peut pas être référencée.

En bref, il est dans un état étrange car vous essayez de vous référer à la valeur qui sera définie après l'application du plug-in lors de l'application du plug-in. Si toutes les valeurs définies dans l'objet d'extension sont référencées dans la phase d'exécution, un tel problème ne se produit pas.

Cependant, comme les réglages d'entrée / sortie doivent être spécifiés dans la phase de réglage, il sera impossible de régler l'entrée / sortie telle quelle.

Définir les propriétés à l'aide de Property

Ce problème peut être évité en utilisant Property.

FooPlugin.groovy


package sample

import org.gradle.api.Project
import org.gradle.api.Plugin
import org.gradle.api.provider.Property

class FooPlugin implements Plugin<Project> {

    @Override
    void apply(Project project) {
        def foo = project.extensions.create("foo", FooPluginExtension, project)

        project.task("copyFile") {
            inputs.file foo.inputFile
            outputs.dir foo.outputDir

            doFirst {
                project.copy {
                    from foo.inputFile
                    into foo.outputDir
                }
            }
        }
    }
}

class FooPluginExtension {
    Property<Object> inputFile
    Property<Object> outputDir

    FooPluginExtension(Project project) {
        this.inputFile = project.objects.property(Object)
        this.outputDir = project.objects.property(Object)
    }
}

build.gradle


apply plugin: sample.FooPlugin

foo {
    inputFile = "fromDir/aaa.txt"
    outputDir = "build"
}

Résultat d'exécution


> gradle copyFile
...
BUILD SUCCESSFUL in 6s

ʻInputFile, ʻoutputDir utilise le type Property comme type de propriété.

Une instance de Property peut être créée avec la méthode property (Class) de ObjectFactory. ʻObjectFactory peut être obtenu à partir de la méthode getObjects () de Project`.

File () etc. de Project prend en charge ce type Property, donc il peut être passé tel quel, mais si vous avez besoin de la valeur du contenu, vous pouvez l'obtenir en utilisant la méthodeget (). Vous pouvez également changer la valeur avec la méthode set ().

Par ailleurs, dans l'exemple ci-dessus, une chaîne de caractères (type String) est définie par l'opérateur d'affectation pour une propriété qui doit être de type Property. Normalement, une telle description entraînerait une erreur en raison des différents types.

Cela est possible car Gradle génère automatiquement une méthode de définition pour les propriétés de type «Propriété».

build.gradle


apply plugin: sample.FooPlugin

foo {
    inputFile = "fromDir/aaa.txt"
    outputDir = "build"

    println delegate.class.methods.find { it.name == "setInputFile" }
}

Méthode de définition automatique des propriétés


> gradle copyFile
...
public void sample.FooPluginExtension_Decorated.setInputFile(java.lang.Object)
...

Cet opérateur d'affectation est le sucre de syntaxe pour l'appel de méthode setInpputFile (Object) généré automatiquement.

Ce type Propriété est également disponible pour les propriétés de tâches personnalisées et prend en charge la génération automatique de setter.

CustomCopyTask.groovy


package sample

import org.gradle.api.provider.Property
import org.gradle.api.DefaultTask
import org.gradle.api.tasks.TaskAction
import org.gradle.api.tasks.InputFile
import org.gradle.api.tasks.OutputDirectory

class CustomCopyTask extends DefaultTask {
    @InputFile
    Property<Object> inputFile = project.objects.property(Object)
    @OutputDirectory
    Property<Object> outputDir = project.objects.property(Object)

    @TaskAction
    def copy() {
        project.copy {
            from this.inputFile
            into this.outputDir
        }
    }
}

build.gradle


task foo(type: sample.CustomCopyTask) {
    inputFile = "fromDir/aaa.txt"
    outputDir = "build"
}

Si vous souhaitez déclarer une propriété dans une collection, vous pouvez utiliser ListProperty (https://docs.gradle.org/current/javadoc/org/gradle/api/provider/ListProperty.html) ou SetProperty (https: // docs. Il existe des sous-types tels que gradle.org/current/javadoc/org/gradle/api/provider/SetProperty.html). Les deux ont une méthode de fabrique dans ʻObjectFactory`, vous pouvez donc créer un objet à partir de celle-ci.

Tâche du cycle de vie

Gradle a une ** tâche de cycle de vie **.

Les tâches du cycle de vie ne se traitent pas toutes seules. Au lieu de cela, il joue un rôle en exprimant un concept et en s'appuyant sur d'autres tâches.

Par exemple, dans Base Plugin, [check](https://docs.gradle.org/current/userguide/base_plugin.html# Une tâche appelée sec: base_tasks) est définie. Cette tâche check elle-même ne fait rien, mais exprime le concept de" l'exécution du traitement de vérification de projet ".

Lorsqu'un plug-in ajoute une tâche pour effectuer le traitement de vérification, ajoutez la tâche de vérification à la dépendance de check. Si tous les plugins sont implémentés de cette façon, tout ce que vous avez à faire est d'exécuter check et tous les processus de validation fonctionneront.

Par exemple, le plugin Java (https://docs.gradle.org/current/userguide/java_plugin.html#lifecycle_tasks) ajoute la tâche test à la dépendance check. Cela signifie que si vous exécutez check, vous exécuterez également test.

référence

Recommended Posts

Définitions des tâches Gradle
Gradle
Mémorandum Gradle
Installation de Gradle
Afficher la tâche Gradle dans le projet Spring Boot