Extrahieren Sie gängige Methoden aus dem Gradle-Build-Skript


83

Ich habe ein Gradle-Build-Skript ( build.gradle), in dem ich einige Aufgaben erstellt habe. Diese Aufgaben bestehen hauptsächlich aus Methodenaufrufen. Die aufgerufenen Methoden befinden sich auch im Build-Skript.

Hier ist die Situation:

Ich erstelle eine ganze Reihe von Build-Skripten, die unterschiedliche Aufgaben enthalten, aber dieselben Methoden wie das ursprüngliche Skript verwenden. Daher möchte ich diese "allgemeinen Methoden" auf irgendeine Weise extrahieren, damit ich sie problemlos wiederverwenden kann, anstatt sie für jedes neue Skript zu kopieren, das ich erstelle.

Wenn Gradle PHP wäre, wäre Folgendes ideal:

//script content
...
require("common-methods.gradle");
...
//more script content

Aber das ist natürlich nicht möglich. Oder ist es?

Wie kann ich dieses Ergebnis erzielen? Was ist die bestmögliche Methode, um dies zu tun? Ich habe die Gradle-Dokumentation bereits gelesen, kann aber anscheinend nicht feststellen, welche Methode dafür am einfachsten und am besten geeignet ist.

Danke im Voraus!


AKTUALISIEREN:

Ich habe es geschafft, die Methoden in eine andere Datei zu extrahieren

(mit apply from: 'common-methods.gradle'),

Die Struktur ist also wie folgt:

parent/
      /build.gradle              // The original build script
      /common-methods.gradle     // The extracted methods
      /gradle.properties         // Properties used by the build script

Nachdem build.gradleich eine Aufgabe von ausgeführt habe , bin ich auf ein neues Problem gestoßen: Anscheinend werden Methoden nicht erkannt, wenn sie sich in befinden common-methods.gradle.

Irgendwelche Ideen, wie man das behebt?


Sind Sie sicher, dass Sie die Methoden überhaupt schreiben müssen? Sie würden einige der Gradle-Extras verpassen, wenn Sie Ihre Build-Skripte in Bezug auf Methoden schreiben. Vor allem ist zusätzliche Arbeit erforderlich, damit der inkrementelle Build ordnungsgemäß funktioniert. Die beabsichtigte Abstraktion besteht darin, Aufgaben zu verwenden und wiederzuverwenden . Sie können auch benutzerdefinierte Aufgaben erstellen . Vielleicht sollten Sie erwägen, die Implementierungen, die Sie jetzt in Methoden haben, in Aufgaben umzusetzen.
Alpar

@Alpar und andere ; Welchem ​​Zweck dient es, so etwas wie a timestamp()oder currentWorkingDirectory()Methoden als task-s zu erstellen (zum Beispiel). Dienstprogrammfunktionen und ähnliche Dinge sind nominell skalar - Sie wären keine Aufgaben, außer dass die Wiederverwendung von Code, die in Gradle und den meisten Build-Systemen integriert ist, Einschränkungen unterliegt. Ich mag die TROCKENE Welt, in der ich EINMAL etwas machen und es wiederverwenden kann. Wenn ich das Beispiel von @Pieter VDE erweitere, verwende ich auch ein " root.gradle" Muster für mein übergeordnetes Projekt - Die Datei build.gradle definiert normalerweise einige Projektspezifikationen und wird dann nur apply ${ROOT}...
am

Wenn Sie eine zentralisierte Methode zum Arbeiten mit Eigenschaften benötigen, kann Ihnen diese Frage möglicherweise helfen: stackoverflow.com/questions/60251228/…
GarouDan

Antworten:


74

Es ist nicht möglich, Methoden freizugeben, aber Sie können zusätzliche Eigenschaften freigeben, die einen Abschluss enthalten, der sich auf dasselbe beschränkt. Deklarieren Sie beispielsweise ext.foo = { ... }in common-methods.gradle, verwenden Sie apply from:, um das Skript anzuwenden, und rufen Sie dann den Abschluss mit auf foo().


1
Es macht den Trick in der Tat! Aber ich habe eine Frage dazu: Wie wäre es mit Methoden, die etwas zurückgeben? Fe File foo(String f)wird werden ext.foo = { f -> ... }, kann ich dann einfach so etwas machen wie : File f = foo(...)?
Pieter VDE

2
Anscheinend ist die Frage in meinem vorherigen Kommentar möglich. Also danke Peter, dass du diese Frage beantwortet hast!
Pieter VDE

1
@PeterNiederwieser Warum ist das nicht möglich? Gradle.org denkt anders: docs.gradle.org/current/userguide/…
IgorGanapolsky

1
@IgorGanapolsky danke für den Link. Ich frage mich, wie ich einen Wert verwenden kann, der in einer separaten Build-Datei in gradle.build generiert wurde - auf diese Weise wäre es sehr nützlich :)
kiedysktos

@IgorGanapolsky Wie soll der Link, den Sie geteilt haben, im Zusammenhang mit der Frage von Peter VDEs helfen?
t0r0X

154

Aufbauend auf Peters Antwort exportiere ich meine Methoden folgendermaßen:

Inhalt von helpers/common-methods.gradle:

// Define methods as usual
def commonMethod1(param) {
    return true
}
def commonMethod2(param) {
    return true
}

// Export methods by turning them into closures
ext {
    commonMethod1 = this.&commonMethod1
    otherNameForMethod2 = this.&commonMethod2
}

Und so verwende ich diese Methoden in einem anderen Skript:

// Use double-quotes, otherwise $ won't work
apply from: "$rootDir/helpers/common-methods.gradle"

// You can also use URLs
//apply from: "https://bitbucket.org/mb/build_scripts/raw/master/common-methods.gradle"

task myBuildTask {
    def myVar = commonMethod1("parameter1")
    otherNameForMethod2(myVar)
}

Hier erfahren Sie mehr über das Konvertieren von Methoden in Closures in Groovy.


Gibt es einen bestimmten Grund, den Verschlussnamen als ext zu verwenden?
Anoop

1
@AnoopSS Wir fügen die beiden Verschlüsse zu Gradles zusätzlichen Eigenschaften hinzu . Diese zusätzlichen Eigenschaften werden in einem Objekt namens gebündelt ext.
Matthias Braun

Können wir den Wert irgendwie als eine unserer Klassen umwandeln, die in der enthaltenen Datei definiert ist?
GarouDan

Es ist wahrscheinlich eine gute Idee, eine separate Frage mit Beispielcode dazu zu stellen, @GarouDan.
Matthias Braun

7

Mit dem Kotlin dsl funktioniert es folgendermaßen:

build.gradle.kts :

apply {
  from("external.gradle.kts")
}

val foo = extra["foo"] as () -> Unit
foo()

external.gradle.kts :

extra["foo"] = fun() {
  println("Hello world!")
}

Großartig, gibt es eine Möglichkeit, den tatsächlichen Typ zu teilen? Sie verlieren im Grunde die Typensicherheit und die Hilfe der Compiler. Wenn Sie die Klasse, die Ihre Methoden enthält, gemeinsam nutzen könnten, könnten Sie den Compiler verwenden.
Vach

0

Ein anderer Ansatz für Kotlin DSL könnte sein:

my-plugin.gradle.kts

extra["sum"] = { x: Int, y: Int -> x + y }

settings.gradle.kts

@Suppress("unchecked_cast", "nothing_to_inline")
inline fun <T> uncheckedCast(target: Any?): T = target as T

apply("my-plugin.gradle.kts")

val sum = uncheckedCast<(Int, Int) -> Int>(extra["sum"])

println(sum(1, 2))
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.