[JAVA] Gradle-Aufgabendefinitionen

Verschiedene Hinweise zu Gradle-Aufgaben von den Grundlagen bis zu den Anwendungen.

Vorausgesetztes Wissen

Umgebung

Gradle

5.0

Java

openjdk 11.0.1

OS

Windows 10

Aufgabenentität

Um eine Aufgabe in Gradle zu definieren, schreiben Sie:

build.gradle


task foo

Die Definition dieser foo Aufgabe ist [Aufgabe (Zeichenfolge)](https: // docs) von Projekt. .gradle.org / current / dsl / org.gradle.api.Project.html # org.gradle.api.Project: task (java.lang.String)) Aufruf der Methode.

Die Methode task () gibt ein Objekt zurück, das die erstellte Aufgabe darstellt, sodass Sie das Objekt für die Aufgabe foo wie folgt abrufen können:

build.gradle


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

Ausführungsergebnis


> gradle foo

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

Die eigentliche Aufgabe von Gradle ist ein Objekt von Task, wenn es einfach von task () erstellt wird Ein Objekt für DefaultTask wird erstellt.

Action Die Aufgabe enthält intern eine Liste von Aktion. Diese "Aktion" hat eine bestimmte Aktion, die von dieser Aufgabe ausgeführt werden soll. Wenn die Aufgabe gestartet wird, wird "Aktion" in der Reihenfolge oben in der Liste ausgeführt.

"Action" kann entweder am Anfang oder am Ende der Liste mit den Methoden "doFirst ()", "doLast ()" von "Task" hinzugefügt werden.

build.gradle


def foo = task foo

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

Ausführungsergebnis


> gradle foo

one
two
three
four

doFirst () fügt am Anfang der Liste Aktion hinzu und doLast () fügt am Ende Aktion hinzu.

Im obigen Beispiel wird "doFirst ()" zweimal ausgeführt. In diesem Fall wird "Aktion", die "eine" ausgibt, die später ausgeführt wird, oben in die Liste eingefügt, sodass sie in der Reihenfolge "eins" -> "zwei" ausgegeben wird.

Im Konfigurationsblock definiert

build.gradle


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

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

Ausführungsergebnis


> gradle foo

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

> Task :foo
FOO!!

Wenn Sie eine Aufgabe mit der Methode task () definieren, können Sie einen Abschluss als zweites Argument übergeben (https://docs.gradle.org/current/dsl/org.gradle.api.Project.html) # org.gradle.api.Project: task (java.lang.String,% 20groovy.lang.Closure). Der "Delegat" dieses Abschlusses ist das erstellte Aufgabenobjekt. Daher kann innerhalb des Abschlusses implizit auf die Methoden und Eigenschaften der erstellten Aufgabe zugegriffen werden.

Lebenszyklus aufbauen

Die Gradle-Ausführung ist grob in drei Phasen unterteilt.

Phase Inhalt
Initialisierungsphase
(Initialization)
Stellen Sie fest, ob das Projekt einfach oder mehrfach usw. ist.ProjectErstellen Sie ein Objekt von.
Konfigurationsphase
(Configuration)
Führen Sie das Build-Skript und ausprojectErstellen Sie das Objekt.
Ausführungsphase
(Execution)
Führen Sie die in der Befehlszeile angegebene Aufgabe tatsächlich aus.

Insbesondere wird in build.gradle das für die Aufgabe festgelegte Innere von Action in der Ausführungsphase ausgeführt, und die anderen werden in der Einstellungsphase ausgeführt.

build.gradle


println("Phase 1 einstellen")

task foo {
    println("Phase 2 einstellen")
    doFirst {
        println("Ausführungsphase 1")
    }
    
    println("Phase 3 einstellen")
    doLast {
        println("Ausführungsphase 2")
    }
}

task bar {
    println("Phase 4 einstellen")
}

println("Phase 5 einstellen")

Ausführungsergebnis


> gradle foo

> Configure project :
Phase 1 einstellen
Phase 2 einstellen
Phase 3 einstellen
Phase 4 einstellen
Phase 5 einstellen

> Task :foo
Ausführungsphase 1
Ausführungsphase 2

Der Teil, der in der Konfigurationsphase ausgeführt wird, wird unabhängig von der angegebenen Aufgabe immer ausgeführt. Wenn Sie den Prozess der Aufgabe fälschlicherweise in das Teil schreiben, das in der Einstellungsphase ausgeführt werden soll, wird der Prozess ausgeführt, obwohl die Aufgabe nicht angegeben ist. Seien Sie also vorsichtig.

Aufgabenreferenz

build.gradle


task foo {
    doFirst {
        println foo
        println project.foo
        println tasks.foo
        println tasks.getByName("foo")
        println tasks["foo"] // getAt()Methodensyntax Zucker
    }
}

Ausführungsergebnis


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

Wenn Sie eine Aufgabe in einem Projekt definieren, können Sie nur über den Aufgabennamen im Projekt auf die Aufgabe verweisen. Dies wird erreicht, indem das erstellte Aufgabenobjekt zu den Eigenschaften des "Projekt" -Objekts hinzugefügt wird.

Das erstellte Aufgabenobjekt ist in TaskContainer von Project registriert. Auf den TaskContainer kann über die Eigenschaft [Aufgaben] des Projekts (https://docs.gradle.org/current/dsl/org.gradle.api.Project.html#org.gradle.api.Project:tasks) verwiesen werden. Mit "TaskContainer" können Sie Aufgaben mithilfe des Eigenschaftszugriffs und der Methoden "getByName ()", "getAt ()" referenzieren.

In seltenen Fällen gibt es Plugins, die ein Erweiterungsobjekt mit demselben Namen wie der Aufgabenname hinzufügen. Beispielsweise heißt Eclipse Plugin "Eclipse" und Task. eclipse_plugin.html # eclipsetasks) und Erweiterungsobjekt sind definiert. In diesem Fall ist das Objekt, auf das in "project.eclipse" verwiesen wird, das Erweiterungsobjekt. Wenn Sie auf eine Aufgabe verweisen möchten, müssen Sie "TaskContainer" wie "task.eclipse" durchlaufen.

Beachten Sie, dass die durch eine Zeichenfolge wie "getByName ()" angegebene Methode auch auf die Aufgaben anderer Projekte als ": someSubProject: fooTask" verweisen kann.

Definieren Sie Aufgabenabhängigkeiten

build.gradle


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

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

foo.dependsOn bar

Ausführungsergebnis


> gradle foo
bar
foo

[DependsOn ()] von Task (https://docs.gradle.org/current/dsl/org.gradle.api.Task.html#org.gradle.api.Task:dependsOn(java.lang.Object [java.lang.Object] ])) Mit der Methode können Sie Aufgabenabhängigkeiten definieren.

Hier wird die Aufgabe "foo" so definiert, dass sie von der Aufgabe "bar" abhängt. Dies stellt sicher, dass bei jeder Ausführung der Aufgabe "foo" die Aufgabe "bar" zuerst ausgeführt wird.

Die Ausführungsreihenfolge von Gradle-Tasks wird im Wesentlichen mithilfe dieser Abhängigkeitsdefinition gesteuert (andere Steuermethoden als Abhängigkeiten sind ebenfalls verfügbar).

Die Aufgabe wird nur einmal ausgeführt

Gradle untersucht die abhängigen Aufgaben, bevor die in der Befehlszeile angegebenen Aufgaben ausgeführt werden. Dann werden die Aufgaben in der Reihenfolge von der abhängigen Seite ausgeführt.

Selbst wenn es Aufgaben gibt, die von mehreren Aufgaben abhängig sind, werden zu diesem Zeitpunkt alle Aufgaben so gesteuert, dass sie immer nur einmal ausgeführt werden.

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

Ausführungsergebnis


> gradle hoge
bar
foo
hoge

Die Aufgabe "hoge" hängt sowohl von "foo" als auch von "bar" ab. Da foo auch von bar abhängt, sieht ein einfaches Diagramm der Aufgabenabhängigkeiten wie folgt aus.

Abhängigkeiten werden grafisch dargestellt


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

Wenn Sie einfach alle abhängigen Aufgaben ausführen, wird bar zweimal ausgeführt. Wie oben erwähnt, steuert Gradle jedoch, dass es nur einmal ausgeführt wird, selbst wenn es von mehreren Aufgaben abhängt.

Wo und wo Sie schreiben können, hängt davon ab

Da abhängigeOn eine Methode von Task ist, kann sie auch wie folgt in den Konfigurationsblock geschrieben werden.

build.gradle


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

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

Hier wird die Task "bar" als Zeichenfolge angegeben. Wenn dies eine Eigenschaftsreferenz anstelle einer Zeichenfolge ist, wird der folgende Fehler angezeigt:

build.gradle


task foo {
    dependsOn bar //★ Ersetzt durch Eigenschaftsreferenz
    doFirst {
        println("foo")
    }
}

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

Ausführungsergebnis


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

BUILD FAILED in 4s

Ein Fehler tritt auf, weil die Eigenschaft bar nicht gefunden werden kann.

Der Teil, in dem "DependantsOn Bar" geschrieben ist, ist der Teil, der in der "Konfigurationsphase" im Build-Lebenszyklus ausgeführt wird. In der Konfigurationsphase werden Build-Skripte in der Reihenfolge von oben ausgeführt. Zum Zeitpunkt der Ausführung von "abhängiger Leiste" ist die Taskleiste undefiniert, da "Taskleiste" noch nicht ausgeführt wurde. Daher kann innerhalb des Taskkonfigurationsblocks "foo" über der Taskdefinition "bar" nicht über die Eigenschaftsreferenz auf die Task "bar" zugegriffen werden.

Dieses Problem kann durch eine der folgenden Methoden vermieden werden.

Wenn es von der Reihenfolge der Beschreibung abhängt, ist es anfällig für Änderungen. Ich persönlich bin der Meinung, dass es besser ist, eine Zeichenfolge anzugeben.

Wird durch das Argument der task () -Methode angegeben

build.gradle


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

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

Erhalten Sie "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))) Sie können die abhängige Aufgabe auch mit dem Argument Map angeben.

Ausführungsreihenfolge erzwingen

build.gradle


apply plugin: "java"

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

task cleanBuild(dependsOn: [clean, build])

Ausführungsergebnis


> gradle cleanBuild
build
clean

"[clean, build]" wird als abhängige Aufgabe der Aufgabe "cleanBuild" angegeben. Wenn jedoch die Task "cleanBuild" ausgeführt wird, werden die Aufgaben in der Reihenfolge "build" -> "clean" ausgeführt.

Auf diese Weise bestimmt die einfache Angabe von "DependantsOn" nicht die Ausführungsreihenfolge von Aufgaben (Namensreihenfolge?).

Wie kann ich steuern, dass es in der Reihenfolge clean-> build ausgeführt wird? Die erste einfache Idee ist, "abhängige" zu definieren, so dass "bauen" von "sauber" abhängt.

build.gradle


apply plugin: "java"

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

task cleanBuild(dependsOn: [clean, build])

Ausführungsergebnis


> gradle cleanBuild
clean
build

Jetzt werden die Aufgaben in der Reihenfolge clean-> build ausgeführt.

Diese Methode macht es jedoch unmöglich, nur "build" auszuführen (es ist immer "clean").

Geben Sie nur Build und Execute an


> gradle build
clean
build

Wenn möglich, möchte ich "clean" nicht ausführen, wenn ich "build" alleine ausführe.

Wenn Sie die Reihenfolge einschränken möchten, wenn sie gleichzeitig angegeben wird, sie aber unabhängig ausführen möchten, mustRunAfter (Object ...) Verwenden Sie 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])

Ausführungsergebnis


> gradle cleanBuild
clean
build

> gradle build
build

Mit "taskA.mustRunAfter (taskB)" können Sie die Reihenfolge erzwingen, wenn "taskA" und "taskB" gleichzeitig ausgeführt werden, "taskB" -> "taskA".

Geben Sie Nachbearbeitungsaufgaben an

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

Ausführungsergebnis


> 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 [])) Die festgelegte Aufgabe wird immer ausgeführt, unabhängig davon, ob die Aufgabe normal oder abnormal endet.

Geben Sie an, wann ein Prozess ausgeführt werden muss, z. B. das Freigeben von Ressourcen.

Geben Sie die auszuführenden Bedingungen an

build.gradle


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

Ausführungsergebnis


> gradle foo
[Keine Leistung]

> gradle foo -Phoge
foo

Verwenden von onlyIf () , Sie können die Bedingungen angeben, unter denen die Aktion der Aufgabe ausgeführt werden soll.

Die Aktion der Aufgabe wird nur ausgeführt, wenn der als Argument übergebene Abschluss "true" zurückgibt. Wenn der Abschluss "false" zurückgibt, wird die Aktion der Aufgabe übersprungen.

Es wird jedoch nur diese Aufgabe übersprungen, und abhängige Aufgaben und Aufgaben, die durch "finalizedBy" angegeben sind, werden ausgeführt.

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

Ausführungsergebnis


> gradle foo
bar
finalizer

Die Ausführung von "foo" wird übersprungen, aber die abhängige "bar" -Aufgabe und die im Finalizer angegebene "finalizer" -Aufgabe werden ausgeführt.

Dateiauswahl

Gradle bietet eine flexible API zum Auswählen von Dateien und Verzeichnissen in der Klasse "Projekt". Beim Schreiben von Aufgabeneinstellungen und Aktionen kann diese API verwendet werden, um Dateivorgänge präzise zu gestalten.

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

Ausführungsergebnis


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

Die Methode "file ()" analysiert den als Argument empfangenen Wert und gibt ein "File" -Objekt zurück.

Es unterstützt eine Vielzahl von Eingaben von Zeichenfolgenpfaden zu "Datei" und "Pfad" (Einzelheiten finden Sie in der API-Dokumentation).

Wenn Sie möchten, dass Gradle Eingaben in ein "Datei" -Objekt konvertiert, funktioniert diese "file ()" - Methode ziemlich gut.

files(Object...)

build.gradle


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

Ausführungsergebnis


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

Die Methode "files ()" ersetzt FileCollection, eine Sammlung mehrerer "File". Generieren. Das Argument ist ein Argument variabler Länge, und der Wert, der mit file () angegeben werden kann, kann auf die gleiche Weise angegeben werden. Außerdem können Sie "Iterable", "Collection" usw. übergeben.

Auf jeden Fall werden die meisten Werte auch gut in eine "FileCollection" analysiert.

Die folgenden Methoden werden in "FileCollection" bereitgestellt.

getAsPath()

build.gradle


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

Ausführungsergebnis


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

Rufen Sie eine Zeichenfolge ab, die jeden Pfad mit einem Trennzeichen für jede Plattform verknüpft. Das Trennzeichen ist beispielsweise ":" unter Linux und ";" unter Windows.

Es kann mit Optionen wie Klassenpfad und Modulpfad verwendet werden, die mehrere Dateien angeben.

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 }

Ausführungsergebnis


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

Der an filter () übergebene Abschluss gibt eine FileCollection zurück, wobei die Elemente auf die File eingegrenzt werden, die ein true zurückgibt.

fileTree(Object)

build.gradle


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

Ausführungsergebnis


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

Die Methode "fileTree ()" wird verwendet, um Dateien rekursiv im angegebenen Verzeichnis FileTree zu speichern. html) erhalten werden. Da "FileTree" von "FileCollection" erbt, kann es auf die gleiche Weise wie "FileCollection" betrieben werden.

fileTree(Object, Closure)

build.gradle


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

Ausführungsergebnis


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

fileTree () kann als zweites Argument einen Abschluss übergeben.

Der Abschlussdelegat ist ein [ConfigurableFileTree] -Objekt (https://docs.gradle.org/current/javadoc/org/gradle/api/file/ConfigurableFileTree.html) in dem durch das erste Argument angegebenen Verzeichnis. .. Wie der Name schon sagt, ist "ConfigurableFileTree" ein konfigurierbarer "FileTree", mit dem Sie Dateien wie "include ()" und "exclude ()" eingrenzen können.

Das Verengungsmuster kann im Ant-Format [^ 2] angegeben werden.

[^ 2]: Suchen Sie für das Ameisenformat nach "Ameisenmusterformat", um verschiedene Informationen zu erhalten.

fileTree(Map)

build.gradle


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

Ausführungsergebnis


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

In fileTree (), das Map als Argument empfängt, kann der in der Eigenschaft von ConfigurableFileTree festzulegende Wert durch Map angegeben werden.

Dateibezogenes Klassendiagramm

Überprüfen Sie erneut die Beziehung der Klassen um den Dateivorgang.

gradle.png

FileCollection ist eine Sammlung von File. FileTree ist eine Sammlung, die rekursiv File in einem bestimmten Verzeichnis enthält.

Es gibt "ConfigurableFileCollection" und "ConfigurableFileTree", die jeweils erben, und es ist möglich, die Zieldatei einzugrenzen.

Aufgabe Eingabe / Ausgabe

build.gradle


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

Die Aufgabe foo ist eine Aufgabe, die den in der Eigenschaft hoge von project festgelegten Wert an foo.txt ausgibt. Versuchen Sie, diese Aufgabe mehrmals auszuführen.

Ausführungsergebnis


> 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

Selbstverständlich wird bei der Ausführung der Aufgabe die Aktion der Aufgabe ausgeführt und die Datei ausgegeben.

Solange sich die Eingabe nicht geändert hat, gibt diese Task nur das gleiche Ergebnis aus, unabhängig davon, wie oft sie ausgeführt wird. Es ist verschwenderisch, es jedes Mal auszuführen, obwohl bekannt ist, dass die Ausgabe unverändert ist.

Wenn diese Aufgabe zeitaufwändig ist, kann sie erhebliche Auswirkungen auf die Erstellungszeit haben.

Gradle bietet einen Mechanismus zum Überspringen der Ausführung einer Aufgabe, wenn sich das Ergebnis der Aufgabe nicht ändert. Um diesen Mechanismus verwenden zu können, muss die Eingabe / Ausgabe der Aufgabe definiert werden.

build.gradle


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

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

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

Ausführungsergebnis


> 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

Wenn der Wert von "hoge" auf den gleichen Wert wie beim ersten Mal gesetzt und ausgeführt wird, wird "UP-TO-DATE" (spätestens) rechts neben der Anzeige der "foo" -Aufgabe ausgegeben und die Ausführung der Aufgabe übersprungen. Nach dem Ändern des Werts von "hoge" wurde die "foo" -Aufgabe erneut ausgeführt.

build.gradle


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

Um die Eingabe und Ausgabe einer Aufgabe zu definieren, greifen Sie zuerst auf das Objekt zum Definieren der Eingabe und Ausgabe zu, indem Sie die folgenden zwei Eigenschaften verwenden, die in "Aufgabe" definiert sind.

Die jeweiligen Eigenschaften sind TaskInputs (https://docs.gradle.org/current/javadoc/org/gradle/api/tasks/TaskInputs.html) und TaskOutputs (https://docs.gradle.org/). current / javadoc / org / gradle / api / task / TaskOutputs.html) wird zurückgegeben. Verwenden Sie die von diesen Klassen bereitgestellten Methoden, um bestimmte Ein- und Ausgänge zu definieren.

Gradle zeichnet den Inhalt der Eingabe und Ausgabe auf, wenn die Aufgabe ausgeführt wird. Bei der nächsten Ausführung derselben Aufgabe wird dann geprüft, ob sich der Inhalt der Eingabe und Ausgabe geändert hat [^ 1]. Wenn sich die Ein- und Ausgänge nicht geändert haben, überspringt Gradle die Aufgabenausführung. Wenn sich einer der für Eingabe und Ausgabe angegebenen Werte geändert hat, wird die Aufgabe normal ausgeführt.

[^ 1]: Wenn die Ausgabedatei gelöscht wird, muss sie erneut ausgeführt werden, sodass auch der Ausgabestatus überprüft werden muss.

Sowohl Ein- als auch Ausgänge müssen definiert werden. Wenn nur eine angegeben ist (nur Eingabe, nur Ausgabe), funktioniert dieser Sprungmechanismus nicht.

Eingabedefinition

Die folgenden drei können häufig für die Eingabe verwendet werden.

--Eigenschaften - property(String, Object) - properties(Map)

Die Eigenschaft kann ein beliebiger Schlüssel und Wert sein, wie im obigen Beispiel gezeigt. Die Werte müssen jedoch serialisierbar sein und können mit "equals ()" verglichen werden.

Wenn Sie eine Datei angeben, wird überprüft, ob sich der Inhalt der Datei geändert hat.

Wenn ein Verzeichnis angegeben ist, wird der Status unter diesem Verzeichnis überprüft. Wenn eine Datei oder ein Verzeichnis im Verzeichnis erstellt wird oder der Inhalt der Datei geändert wird, wird beurteilt, dass sich die Eingabe geändert hat. Dieses Urteil zielt rekursiv auf Unterverzeichnisse und darunter ab.

build.gradle


task foo {
    ...
    inputs.file file("input.txt") //Geben Sie die Eingabedatei an
    inputs.dir file("inputDir")   //Angabe des Eingabeverzeichnisses
    ...
}

Ausgabedefinition

Die folgenden zwei Ausgänge können verwendet werden.

build.gradle


task foo {
    ...
    outputs.file file("output.txt") //Geben Sie die Ausgabedatei an
    outputs.dir file("outputDir")   //Angabe des Ausgabeverzeichnisses
    ...
}

Aufgabenregeln

build.gradle


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

Ausführungsergebnis


> gradle echoHoge
Hoge

> gradle tasks
...

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

Die addRule () Methode von TaskContainer Sie können damit eine sogenannte Aufgabenregel definieren.

addRule () übergibt die Aufgabenregelbeschreibung im ersten Argument und den Abschluss im zweiten Argument. Dem Abschluss wird der Name der Aufgabe übergeben, auf die er verweisen wollte (hier wird die in der Befehlszeile angegebene Zeichenfolge "echoHoge" übergeben).

Wenn Sie eine Aufgabe mit dem Namen definieren, der in diesem Abschluss empfangen wurde, tritt kein Fehler auf, selbst wenn die Aufgabe mit diesem Namen nicht vordefiniert ist und die dynamisch definierte Aufgabe übernommen wird. Mit anderen Worten, selbst für Aufgaben, für die der Aufgabenname nicht im Voraus statisch definiert werden kann, kann die Aufgabe zur Laufzeit dynamisch definiert werden, indem ein Muster (eine Regel) im Aufgabennamen angegeben wird.

Auf die in dieser Aufgabenregel definierten Regeln kann auch mit "abhängige" verwiesen werden.

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

Ausführungsergebnis


> gradle foo
Bar
foo

Hier wird "echoBar" in "abhängig von" der "foo" -Aufgabe angegeben. Auch hier gelten die Aufgabenregeln und die Aufgabe "echoBar" wird dynamisch generiert.

Saubere Regel als Standard

build.gradle


apply plugin: "base"

Ausführungsergebnis


> gradle tasks
...

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

Wenn Sie das "Base" -Plug-In laden, wird die "Clean" -Regel angewendet. (Dieses Plug-In wird zusammen geladen, wenn Sie ein Java-Plug-In usw. anwenden. Grundsätzlich wird es also nicht explizit geladen.)

Diese Regel erstellt eine Aufgabe, die die Ausgabedatei einer Aufgabe löscht.

build.gradle


apply plugin: "base"

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

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

Hier definieren wir eine Aufgabe namens "foo". Diese Aufgabe definiert eine Datei namens "foo.txt" als Ausgabedatei.

Ausführungsergebnis


> type foo.txt
Die angegebene Datei wurde nicht gefunden.

> 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
Die angegebene Datei wurde nicht gefunden.

Die Task cleanFoo wird dynamisch durch die clean -Regel generiert und definiert, um die Ausgabedatei der foo -Task zu löschen.

Benutzerdefinierte Aufgabe

Mit task () erstellte Aufgaben können nur in diesem Projekt verwendet werden. Außerdem kann derselbe Prozess nicht an mehreren Standorten mit unterschiedlichen Parametern wiederverwendet werden.

Wenn Sie dieselbe Aufgabe in mehreren Projekten mit unterschiedlichen Parametern wiederverwenden möchten, erstellen Sie eine benutzerdefinierte Aufgabe.

build.gradle


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

task foo(type: FooTask)

Ausführungsergebnis


> gradle foo
FOO!!

Benutzerdefinierte Aufgaben werden durch Erben von "DefaultTask" erstellt. Wenn Sie eine beliebige Methode definieren und mit @TaskAction mit Anmerkungen versehen, ist diese Methode die Aktion der Aufgabe. Wird durchgeführt.

Um die von Ihnen erstellte benutzerdefinierte Aufgabe zu verwenden, erhalten Sie eine "Map" task (). Verwenden Sie .Project: task (java.util.Map,% 20java.lang.String). Geben Sie in diesem Argument "Map" den Aufgabentyp an, den Sie in "type" erstellen möchten. Anschließend wird das Aufgabenobjekt mit dem angegebenen Typ erstellt.

Hier werden der Einfachheit halber benutzerdefinierte Aufgaben im selben Build-Skript definiert. In der Realität wird es jedoch an einem externen Speicherort definiert, auf den mehrere Projekte verweisen können (z. B. später beschriebenes "buildSrc" oder eine externe JAR-Datei).

Benutzerdefinierte Aufgabeneinstellungen

build.gradle


class FooTask extends DefaultTask {

    String message

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

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

Ausführungsergebnis


> gradle foo
FOO!! message=Hello World

Eine Aufgabe, die durch Angabe eines Typs mit "Typ" erstellt wurde, ist ein Objekt dieses Typs. Mit anderen Worten, wenn Sie Einstellungseigenschaften und -methoden für eine benutzerdefinierte Aufgabe definieren, können Sie die Einstellungen des Aufgabenobjekts überall dort angeben, wo Sie auf das generierte Aufgabenobjekt zugreifen können.

Hier wird der Wert in der Eigenschaft message im Einstellungsblock festgelegt.

Dateieinstellungen werden in Objekt definiert

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

Beim Definieren von Dateien und Verzeichnissen für die Eingabe und Ausgabe von Aufgaben sollte der Typ ein Typ sein, der alles wie "Objekt" oder "Liste" enthalten kann. Wenn Sie dann den Einstellungswert tatsächlich verwenden, konvertieren Sie ihn mit "file ()" oder "files ()" von "Project".

Auf diese Weise können Sie Dateien in einer Vielzahl von Formaten angeben, die von "Project # file ()" unterstützt werden, z. B. Zeichenfolgen und "File" -Objekte, wodurch Sie eine größere Konfigurationsflexibilität erhalten.

Eingabe- / Ausgabespezifikation

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

Ausführungsergebnis


> 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

Benutzerdefinierte Aufgaben bieten eine Möglichkeit, Aufgabenein- und -ausgaben mit Anmerkungen zu definieren.

Sie können Aufgabenein- und -ausgaben definieren, indem Sie benutzerdefinierte Aufgabenfelder und die Getter-Methode mit den folgenden Anmerkungen versehen.

--Eingang - @Input - @InputFile - @InputFiles - @InputDirectory --Ausgabe - @OutputFile - @OutputFiles - @OutputDirectory - @OutputDirectories

Aufgaben werden standardmäßig bereitgestellt

Aufgabenklassen werden standardmäßig für Prozesse bereitgestellt, die wahrscheinlich häufig verwendet werden. Zum Beispiel gibt es die folgenden Aufgaben.

Copy

build.gradle


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

Ausführungsergebnis


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

> gradle foo
...

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

Kopie Die Aufgabe führt eine Kopie einer Datei oder eines Verzeichnisses durch.

Die Einstellung der Kopiermethode wird von CopySpec bereitgestellt, das von Copy implementiert wurde. Verwenden Sie die vorhandene API. CopySpec bietet verschiedene Methoden zum Eingrenzen des Kopierziels und zum Festlegen des Kopierziels und ermöglicht recht flexible Einstellungen.

from(Object...)

build.gradle


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

Geben Sie die Kopie der Quelldatei oder des Verzeichnisses an. Sie können mehrere Argumente angeben oder from () selbst mehrmals aufrufen.

Der als Argument übergebene Wert ist schließlich "Project" files (Object ...) .api.Project: Wird an die Methode files (java.lang.Object []) übergeben. Daher können Sie verschiedene Werte übergeben, z. B. einen Zeichenfolgenpfad oder ein "Datei" -Objekt.

from(Object, Closure)

build.gradle


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

Sie können einen Abschluss als zweites Argument von "from ()" übergeben. Der "Delegat" dieses Abschlusses ist "CopySpec", und es ist möglich, die Kopierbedingungen nur unter dem durch "from ()" angegebenen Verzeichnis weiter einzugrenzen. Hier sind die von "include ()" zu kopierenden Dateien auf "* .txt" beschränkt.

into(Object)

build.gradle


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

Geben Sie das Kopierzielverzeichnis an.

include(String...)

build.gradle


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

Sie können die Bedingungen angeben, die in das Kopierziel aufgenommen werden sollen, indem Sie das Muster im Ant-Format angeben.

exclude(String...)

build.gradle


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

Hier können Sie auch Bedingungen angeben, die vom Kopieren mit einem Ant-Format-Muster ausgeschlossen werden sollen.

rename(Closure)

build.gradle


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

Sie können rename () verwenden, um die Datei beim Kopieren umzubenennen. Im Fall von "rename ()", das einen Abschluss erhält, wird der Dateiname vor dem Kopieren ("String") an das Argument des Abschlusses übergeben. Anschließend wird der vom Abschluss zurückgegebene Wert nach dem Kopieren als Dateiname verwendet.

Im Fall der obigen Implementierung wird der Dateiname, in dem der ursprüngliche Dateiname groß geschrieben ist, als Name der Kopierzieldatei verwendet.

rename(String, String)

build.gradle


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

Rename (), das zwei Strings als Argumente empfängt, übergibt einen regulären Ausdruck als erstes Argument und den umbenannten Ausdruck als zweites Argument.

Der Teil, der im regulären Ausdruck des ersten Arguments als Gruppe definiert ist (der von "()" eingeschlossene Teil), kann im Ausdruck des zweiten Arguments durch "$ n" bezeichnet werden ("n" ist eine Seriennummer von 1).

Im obigen Beispiel ist die erste Gruppe "(. *)", Die auf den Basisnamen der Datei vor der Erweiterung verweist. Die zweite Gruppe ist der Teil "([a-z] +)", der auf den Erweiterungsteil zeigt.

Durch Angabe von "$ 1_copy. $ 2" im Ausdruck nach dem Umbenennen wird die Datei dann in den Namen mit "_copy" am Ende des Basisnamens umbenannt und kopiert.

Wenn es tatsächlich bewegt wird, wird es wie folgt.

Ausführungsergebnis


> 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

Projektkopie ()

Für die Project-Klasse [copy (Closure)](https://docs.gradle.org/current/dsl/org.gradle.api.Project.html#org.gradle.api.Project:copy (groovy). Eine Methode namens lang.Closure)) wird bereitgestellt.

Der Argumentabschluss delegate implementiert die CopySpec -Schnittstelle, mit der Sie das Kopierziel auf dieselbe Weise wie die Copy -Task angeben können.

build.gradle


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

Wenn Sie mehrere Kopiervorgänge in einer einzigen Aufgabe ausführen möchten, können Sie auch die Methode "copy ()" dieses "Projekts" verwenden.

Delete

build.gradle


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

Ausführungsergebnis


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

> gradle foo
...
BUILD SUCCESSFUL in 5s

> tree /f targetDir
    bbb.txt

Löschen Mit Aufgaben können Sie Dateien und Ordner löschen.

Die Klasse "Löschen" implementiert die Schnittstelle DeleteSpec. [Löschen (Objekt ...)] in dieser Schnittstelle definiert (https://docs.gradle.org/current/dsl/org.gradle.api.tasks.Delete.html#org.gradle.api.tasks .Delete: delete (java.lang.Object [])) Geben Sie das Löschziel mit der Methode an. Der im Argument angegebene Wert wird an die Methode "Project # files (Object ...)" übergeben, sodass er in verschiedenen Typen wie einer Zeichenfolge und "File" angegeben werden kann.

Delete selbst bietet keine API, mit der Sie" eine Datei in einem bestimmten Verzeichnis mit einem bestimmten Muster "angeben können. Wie oben erwähnt, wird der an "delete ()" übergebene Wert jedoch an "Project # files ()" übergeben, sodass Sie auch "FileCollection" übergeben können. Mit anderen Worten kann es wie folgt angegeben werden.

build.gradle


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

Ausführungsergebnis


> 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

Projekt delete () Methode

Wie copy (), delete (Action) wird in Project bereitgestellt.

Da der Abschluss-Delegat "DeleteSpec" implementiert, können Sie das Löschziel auf dieselbe Weise wie die Aufgabe "Löschen" angeben.

build.gradle


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

Die Einschränkung hierbei ist, dass Sie jedes Mal, wenn Sie die Methode "delete ()" aufrufen, "project" voranstellen müssen, z. B. "project.delete ()".

build.gradle


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

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

Ausführungsergebnis


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

Für "project.delete ()" ist "delegate" "DeleteSpec", aber für "delete ()" ist es nicht "DeleteSpec".

Übrigens gibt es für "copy ()" kein Problem, da "delegate" zu "CopySpec" wird, auch wenn "project" nicht hinzugefügt wird. Wenn es nur eine Methode gibt, die "Aktion" als Argument akzeptiert, scheint es, dass "Projekt" erforderlich ist ("copy ()" hat "copy (Closure)" und "copy (Action)", aber "delete" ) Nur löschen (Aktion)`).

Um ehrlich zu sein, bin ich mir nicht sicher, warum das passiert.

Sync

build.gradle


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

Ausführungsergebnis


> 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 Die Aufgabe entspricht dem durch into angegebenen Verzeichnis und dem durch from angegebenen Verzeichnis. Auf Status aktualisieren. Da "Sync" "CopySpec" implementiert, können Sie die Kopiermethode auf dieselbe Weise wie die Aufgabe "Copy" steuern.

Die "hoge.txt", die vor der Ausführung der Aufgabe in "toDir" vorhanden war, wurde gelöscht. Die Synchronisierung mit "Synchronisierung" erfolgt standardmäßig ** nach dem Löschen aller Zieldateien und Ordner **.

Wenn Sie eine Datei oder einen Ordner haben, der nur im Synchronisierungsziel vorhanden ist und nicht gelöscht werden soll, bewahren Geben Sie # org.gradle.api.tasks.Sync: generate (org.gradle.api.Action) an.

build.gradle


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

Ausführungsergebnis


> 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

Das durch "include" in "erve "angegebene Ziel wird nicht mehr gelöscht (" bewahren "bedeutet" speichern ").

Sie können auch "ausschließen" angeben. Wenn Sie "ausschließen" für "hoge.txt" angeben, wird nur "hoge.txt" gelöscht (es wird nicht gespeichert).

Wenn Sie gleichzeitig "Einschließen" und "Ausschließen" angeben, scheint die Einstellung "Einschließen" Vorrang zu haben.

Projekt sync () Methode

Wie Sie sich vorstellen können, [sync (Action)](https://docs.gradle.org/current/dsl/org.gradle.api.Project.html#org.gradle.api.Project:sync(org.gradle.api). Aktion)) Die Methode wird auch in "Projekt" bereitgestellt.

Der "Delegat" dieses Abschlusses implementiert jedoch die "CopySpec" -Schnittstelle. Mit anderen Worten, "bewahren" kann nicht angegeben werden (die Referenz besagt, dass "bewahren" angegeben werden kann, aber wenn Sie es tatsächlich schreiben, tritt ein Fehler auf).

build.gradle


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

Da "sync ()" nur "sync (Action)" hat, tritt ein Fehler auf, es sei denn, das "Projekt" wird wie "delete ()" geändert.

Exec

build.gradle


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

Ausführungsergebnis


> gradle foo
...
BUILD SUCCESSFUL in 5s

> type workDir\hello.txt
Hello World!!

Exec Mit Aufgaben können Sie beliebige Befehle ausführen. Exec implementiert die Schnittstelle ExecSpec, die Konfigurationsmethoden definiert. ..

[commandLine (Object ...)](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.Exec.html#org.gradle.api.tasks.Exec:commandLine (java.) Geben Sie in lang.Object [])) den Befehl an, den Sie ausführen möchten. Unter Linux können Sie mit dem Befehl beginnen, den Sie direkt ausführen möchten. Unter Windows müssen Sie jedoch mit cmd / c <dem Befehl, den Sie tatsächlich ausführen möchten> fortfahren.

[workingDir (Object)](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.Exec.html#org.gradle.api.tasks.Exec:workingDir(java.lang.Object) Sie können das Arbeitsverzeichnis zur Laufzeit mit)) angeben.

Umgebung (Karte)) Ermöglicht das Festlegen von Umgebungsvariablen innerhalb des Unterprozesses, der den Befehl ausführt.

Die exec () -Methode des Projekts

build.gradle


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

Project exec (Closure)) Sie können die Methode verwenden, um Befehle auf die gleiche Weise wie "Exec" auszuführen.

Der Abschlussdelegierte ist "ExecSpec".

Zip

build.gradle


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

Ausführungsergebnis


> 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

Mit der Aufgabe Zip können Sie jedes Verzeichnis komprimieren.

Zip implementiert die CopySpec -Schnittstelle, sodass sie auf dieselbe Weise wie die Copy -Aufgabe als Ziel ausgewählt werden kann.

Das Ausgabeziel ist jedoch destinationDir ) Geben Sie in der Eigenschaft an. Beachten Sie, dass diese Eigenschaft "Datei" und nicht "Objekt" ist. Wenn Sie das Plug-In "base" angewendet haben, ist "destinationDir" standardmäßig "build / Distributions". Wenn das "Base" -Plug-In nicht angewendet wird (grundsätzlich unmöglich ...), muss das "destinationDir" angegeben werden.

Der Name der Zip-Datei wird durch Verketten der in einigen Eigenschaften festgelegten Werte erstellt.

build.gradle


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

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

Ausführungsergebnis


> gradle foo
...
BUILD SUCCESSFUL in 5s

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

Der gesamte Dateiname wird erstellt, indem jede Eigenschaft wie folgt mit einem Bindestrich verbunden wird. (Die nicht gesetzten Eigenschaften werden ignoriert (wenn "Erweiterung" nicht festgelegt ist, wird sie zu "" zip "))

baseName-appendix-version-classifier.extension

Wenn Sie den gesamten Dateinamen auf einmal angeben möchten, wählen Sie archiveName .bundling.Zip: archiveName) Geben Sie die Eigenschaft an.

Der Pfad der von der Zip-Task generierten Zip-Datei lautet [archivePath](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.bundling.Zip.html#org.gradle ". Sie können dies mit der Eigenschaft .api.tasks.bundling.Zip: archivePath) überprüfen.

Die zipTree () -Methode des Projekts

Unglücklicherweise? Es gibt keine Methode namens "zip ()" in "Project".

Es ist kein Ersatz, sondern im Gegenteil zipTree (Object). Die Methode api.Project: zipTree (java.lang.Object) wird bereitgestellt. Wenn Sie den Pfad der Zip-Zieldatei als Argument angeben, wird "FileTree" mit den Informationen zum Inhalt dieser Zip-Datei zurückgegeben.

build.gradle


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

Ausführungsergebnis


> 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

Unmittelbar nach dem Ausführen von "zipTree ()" wurde die Zip-Datei noch nicht entpackt. Wenn Sie versuchen, auf die Informationen der Datei in "ZipTree" zuzugreifen, wird die Zip-Datei tatsächlich in das temporäre Verzeichnis entpackt und Sie können auf die Informationen der Datei zugreifen.

Durch Kombinieren von zipTree () und copy () können Sie die Dekomprimierung von zip reproduzieren.

build.gradle


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

Dadurch wird der Inhalt der Zip-Datei im Verzeichnis "build / unzip" entpackt.

buildSrc Gradle hat ein eigenes Projekt zum Bereitstellen von Klassen, die in Build-Skripten verwendet werden.

Ordnerstruktur


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

Das Stammverzeichnis buildSrc wird zu einem dedizierten Projekt. Sie können Ihre eigene Aufgabe oder Ihren eigenen Plug-In-Code im Verzeichnis "src / main / groovy" dieses Projekts ablegen.

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)

Ausführungsergebnis


> gradle foo
Hello Hoge.

Wenn der Quellcode in "buildSrc" vorhanden ist, wird er automatisch kompiliert, wenn die Aufgabe ausgeführt und im Build-Skript verfügbar gemacht wird.

Plugin

Durch die Verwendung einer benutzerdefinierten Aufgabe kann die Logik der Aufgabe im Voraus definiert werden, und nur der eingestellte Wert kann an mehreren Stellen umgeschaltet und wiederverwendet werden. Eine benutzerdefinierte Aufgabe ist jedoch nur eine einzelne Aufgabe, und alle Aufgabengenerierungen und detaillierten Einstellungen müssen vom Benutzer angegeben werden.

Um eine größere Bandbreite von Prozessen gemeinsam zu nutzen und wiederzuverwenden, z. B. das Erstellen mehrerer Aufgaben und das Definieren von Abhängigkeiten zwischen Aufgaben, wird ein Mechanismus namens ** Plug-In ** verwendet.

Ordnerstruktur


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

Ausführungsergebnis


> gradle hello
FOO!!

Plugins werden durch Implementierung der Schnittstelle Plugin erstellt (Typargument ist Project). ..

Implementieren Sie die Methode "apply (T)" und beschreiben Sie die darin enthaltene Plug-In-Verarbeitung. Der zu beschreibende Inhalt ist der gleiche wie der normalerweise in "build.gradle" beschriebene Inhalt, und es gibt kein Problem. Im Fall von "build.gradle" war es jedoch möglich, "task ()" usw. direkt aufzurufen, da die Delegierung an "Project" implizit durchgeführt wurde. Hier erfolgt jedoch die implizite Delegierung. Da es so etwas nicht gibt, muss die Methode "Projekt" explizit aufgerufen werden.

Das erstellte Plug-In kann wie jedes andere Plug-In mit apply geladen werden.

Plugin-Einstellungen

Wenn Sie den Einstellungswert an das Plug-In übergeben möchten, verwenden Sie den Mechanismus des Erweiterungsobjekts.

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
}

Verwenden Sie zum Erstellen eines Erweiterungsobjekts die Methode "create ()" von ExtensionContainer. ExtensionContainer wird von der Eigenschaft extensions von Project abgerufen. es kann.

Die Methode create () übergibt den Namen des Erweiterungsobjekts im ersten Argument und den Typ des Erweiterungsobjekts im zweiten Argument. Hier wird "foo" für den Namen und "FooPluginExtension" für den Typ angegeben.

Der Rückgabewert von "create ()" ist ein Objekt der durch das zweite Argument angegebenen Klasse, und auf den im Erstellungsskript angegebenen Einstellungswert kann über dieses Objekt zugegriffen werden.

Das generierte Erweiterungsobjekt wurde als Eigenschaft zu "Projekt" hinzugefügt, damit auf es mit dem in "create ()" angegebenen Namen verwiesen werden kann.

build.gradle


apply plugin: sample.FooPlugin

foo.message = "Hello Foo!!"

Ausführungsergebnis


> gradle hello
Hello Foo!!

Da der Einstellungsblock für das Erweiterungsobjekt verfügbar ist, kann er auch wie folgt beschrieben werden.

build.gradle


apply plugin: sample.FooPlugin

foo {
    message = "Hello Foo!!"
}

Bei mehreren Einstellwerten kann die Beschreibung mithilfe des Einstellungsblocks vereinfacht werden.

Argument des Erweiterungsobjektkonstruktors

Wenn Sie das Argument im Konstruktor des Erweiterungsobjekts erhalten möchten, schreiben Sie wie folgt.

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

Das Erweiterungsobjekt FooPluginExtension nimmt ein Project-Objekt und setzt den Standardwert von outputDir auf den aus Project generierten Wert.

Um diesem Konstruktorargument einen Wert zu übergeben, implementieren Sie ihn so, dass der entsprechende Wert nach dem dritten Argument der Methode create () übergeben wird. Das dritte Argument von "create ()" ist ein Argument variabler Länge, und der hier angegebene Wert wird unverändert an den Konstruktor des Erweiterungsobjekts übergeben.

Verwenden Sie den im Erweiterungsobjekt festgelegten Wert für den Wert der Aufgabeneinstellung

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
}

Eine Aufgabe namens "copyFile" wird erstellt und implementiert, um das Erweiterungsobjekt "inputFile" nach "outputDir" zu kopieren. Außerdem werden "inputFile" und "outputDir" für die Eingabe und Ausgabe der Aufgabe "copyFile" festgelegt.

build.gradle


apply plugin: sample.FooPlugin

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

Wenn ich das mache, schlägt der Build wie folgt fehl:

Ausführungsergebnis


> 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'.
...

Allein damit weiß ich nicht, wovon Sie sprechen, aber wenn Sie die folgende Ausgabe zum Debuggen hinzufügen, werden Sie sehen, was los ist.

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

class FooPluginExtension {
    Object inputFile
    Object outputDir
}

build.gradle


println "Vor dem Auftragen von FooPlugin"
apply plugin: sample.FooPlugin
println "Nach dem Auftragen von FooPlugin"

println "foo Geben Sie den Einstellungswert für das Erweiterungsobjekt an"
foo {
    inputFile = "fromDir/aaa.txt"
    outputDir = "build"
}

Ausführungsergebnis


> gradle createFile
...
Vor dem Auftragen von FooPlugin
FooPlugin.apply()Start
foo.inputFile=null
foo.outputDir=null
FooPlugin.apply()Ende
Nach dem Auftragen von FooPlugin
foo Geben Sie den Einstellungswert für das Erweiterungsobjekt an

> Task :copyFile FAILED
...

In der Phase des Festlegens der Eingabe / Ausgabe der Task "createFile" wurde der Wert für das Erweiterungsobjekt "foo" noch nicht festgelegt. Daher ist die Eingabe / Ausgabe-Spezifikation "null".

Die Anwendung des Plug-Ins erfolgt in der Einstellungsphase des Build-Lebenszyklus, und der Einstellungsprozess wird grundsätzlich in der Reihenfolge von oben ausgeführt. Daher wurde in der apply () -Methode des benutzerdefinierten Plug-Ins das Erweiterungsobjekt foo noch nicht festgelegt, sodass auf den Wert nicht verwiesen werden kann.

Kurz gesagt, es befindet sich in einem seltsamen Zustand, da Sie versuchen, auf den Wert zu verweisen, der nach dem Anwenden des Plug-Ins beim Anwenden des Plug-Ins festgelegt wird. Wenn in der Ausführungsphase auf alle im Erweiterungsobjekt festgelegten Werte verwiesen wird, tritt ein solches Problem nicht auf.

Da die Eingabe- / Ausgabeeinstellungen jedoch in der Einstellungsphase festgelegt werden müssen, ist es unmöglich, die Eingabe / Ausgabe so einzustellen, wie sie ist.

Definieren Sie Eigenschaften mit Property

Dieses Problem kann durch Verwendung von "Eigenschaft" vermieden werden.

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

Ausführungsergebnis


> gradle copyFile
...
BUILD SUCCESSFUL in 6s

Der Typ der Eigenschaft "inputFile", "outputDir" ist Property.

Eine Instanz von "Property" kann mit der Methode "property (Class)" von ObjectFactory erstellt werden. ObjectFactory kann mit der getObjects () Methode von Project abgerufen werden.

File () etc. von Project unterstützt diesen Property-Typ, sodass er unverändert übergeben werden kann. Wenn Sie jedoch den Wert des Inhalts benötigen, können Sie ihn mit der get () -Methode abrufen. Sie können den Wert auch mit der Methode set () ändern.

Übrigens wird im obigen Beispiel eine Zeichenfolge (Typ "String") vom Zuweisungsoperator für eine Eigenschaft festgelegt, die vom Typ "Property" sein sollte. Normalerweise würde eine solche Beschreibung aufgrund der unterschiedlichen Typen zu einem Fehler führen.

Dies ist möglich, weil Gradle automatisch eine Setter-Methode für Eigenschaften vom Typ "Eigenschaft" generiert.

build.gradle


apply plugin: sample.FooPlugin

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

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

Automatisch generierte Eigenschaftssetzermethode


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

Dieser Zuweisungsoperator ist der Syntaxzucker für den automatisch generierten Methodenaufruf "setInpputFile (Object)".

Dieser Typ "Eigenschaft" ist auch für benutzerdefinierte Aufgabeneigenschaften verfügbar und unterstützt die automatische Setter-Generierung.

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

Wenn Sie eine Eigenschaft in einer Sammlung deklarieren möchten, können Sie ListProperty (https://docs.gradle.org/current/javadoc/org/gradle/api/provider/ListProperty.html) oder SetProperty (https: // docs) verwenden. Es gibt Untertypen wie gradle.org/current/javadoc/org/gradle/api/provider/SetProperty.html). In beiden Fällen verfügt "ObjectFactory" über eine Factory-Methode, mit der Sie ein Objekt erstellen können.

Lebenszyklusaufgabe

Gradle hat eine ** Lebenszyklusaufgabe **.

Lebenszyklusaufgaben werden nicht selbst verarbeitet. Stattdessen spielt es eine Rolle, indem es ein Konzept ausdrückt und sich auf andere Aufgaben stützt.

Beispiel: In Basis-Plugin [check](https://docs.gradle.org/current/userguide/base_plugin.html# Eine Aufgabe namens sec: base_tasks) ist definiert. Diese "Prüf" -Aufgabe selbst bewirkt nichts, sondern drückt das Konzept der "Durchführung der Projektüberprüfungsverarbeitung" aus.

Wenn ein Plug-In eine Aufgabe zur Durchführung der Überprüfungsverarbeitung hinzufügt, fügen Sie die Überprüfungsaufgabe der Abhängigkeit von "check" hinzu. Wenn alle Plugins auf diese Weise implementiert sind, müssen Sie nur "check" ausführen und alle Validierungsprozesse funktionieren.

Beispielsweise fügt das Java-Plugin (https://docs.gradle.org/current/userguide/java_plugin.html#lifecycle_tasks) die Aufgabe "test" zur Abhängigkeit "check" hinzu. Dies bedeutet, dass Sie, wenn Sie "check" ausführen, auch "test" ausführen.

Referenz

Recommended Posts

Gradle-Aufgabendefinitionen
Gradle
Gradle Memorandum
Gradle-Installation
Zeigen Sie die Gradle-Aufgabe im Spring Boot-Projekt an