Wyodrębnij typowe metody ze skryptu kompilacji Gradle

85

Mam skrypt budowania Gradle ( build.gradle), w którym utworzyłem kilka zadań. Te zadania składają się głównie z wywołań metod. Wywołane metody znajdują się również w skrypcie budowania.

Oto sytuacja:

Tworzę sporo skryptów kompilacji, które zawierają różne zadania, ale używam tych samych metod, co oryginalny skrypt. Dlatego chciałbym w jakiś sposób wyodrębnić te "powszechne metody", aby móc z łatwością ich używać ponownie, zamiast kopiować je dla każdego nowego skryptu, który tworzę.

Gdyby Gradle był PHP, idealne byłoby coś takiego:

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

Ale oczywiście nie jest to możliwe. Albo to jest?

Zresztą, jak mogę osiągnąć taki wynik? Jaka jest najlepsza możliwa metoda, aby to zrobić? Przeczytałem już dokumentację Gradle, ale nie mogę określić, która metoda będzie najłatwiejsza i najlepiej do tego nadaje się.

Z góry dziękuję!


AKTUALIZACJA:

Udało mi się wyodrębnić metody w innym pliku

(używając apply from: 'common-methods.gradle'),

więc struktura jest następująca:

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

Po wykonaniu zadania z poziomu build.gradlenapotkałem nowy problem: najwyraźniej metody nie są rozpoznawane, gdy się pojawią common-methods.gradle.

Jakieś pomysły, jak to naprawić?

Pieter VDE
źródło
Czy na pewno w ogóle musisz pisać metody? Jeśli napiszesz swoje skrypty kompilacji w kategoriach metod, przegapisz niektóre gadżety Gradle, a co najważniejsze, uzyskanie poprawnego działania kompilacji przyrostowej będzie wymagało dodatkowej pracy. Zamierzoną abstrakcją jest użycie i ponowne wykorzystanie zadań . Możesz także tworzyć zadania niestandardowe . Być może powinieneś rozważyć umieszczenie implementacji, które masz teraz w metodach, w zadaniach.
Alpar
@Alpar i inni ; do jakiego celu służy tworzenie czegoś takiego jak a timestamp()lub currentWorkingDirectory()metody jako task-s (na przykład). Funkcje narzędziowe i podobne rzeczy są nominalnie skalarne - nie byłyby zadaniami, z wyjątkiem tego, że istnieją ograniczenia dotyczące ponownego wykorzystania kodu wbudowane w Gradle i większość systemów kompilacji. Lubię świat SUCHEGO, w którym mogę zrobić coś JEDEN raz i użyć tego ponownie. W rzeczywistości, rozszerzając przykład @Pieter VDE, używam również root.gradlewzorca " " dla mojego projektu nadrzędnego - plik build.gradle zwykle definiuje pewne szczegóły projektu, a potem po prostu apply ${ROOT}...
będzie
Jeśli potrzebujesz scentralizowanego sposobu pracy z nieruchomościami, może to pytanie może ci pomóc: stackoverflow.com/questions/60251228/…
GarouDan

Odpowiedzi:

76

Nie można udostępniać metod, ale możesz udostępniać dodatkowe właściwości zawierające zamknięcie, które sprowadza się do tego samego. Na przykład zadeklaruj ext.foo = { ... }w common-methods.gradle, użyj, apply from:aby zastosować skrypt, a następnie wywołaj zamknięcie za pomocą foo().

Peter Niederwieser
źródło
1
To rzeczywiście działa! Ale mam pytanie na ten temat: co z metodami zwracającymi coś? Fe File foo(String f)się stanie ext.foo = { f -> ... }, czy mogę wtedy po prostu zrobić coś takiego File f = foo(...):?
Pieter VDE,
2
Najwyraźniej pytanie w moim poprzednim komentarzu jest możliwe. Dziękuję Peter za odpowiedź na to pytanie!
Pieter VDE,
1
@PeterNiederwieser Dlaczego nie jest to możliwe? Gradle.org myśli inaczej: docs.gradle.org/current/userguide/ ...
IgorGanapolsky
1
@IgorGanapolsky dzięki za link. Zastanawiam się jak mogę wykorzystać wartość wygenerowaną w osobnym pliku build w gradle.build - tak by się bardzo przydało :)
kiedysktos
@IgorGanapolsky W jaki sposób udostępniony przez Ciebie link powinien pomóc w kontekście pytania Petera VDE?
t0r0X
157

Opierając się na odpowiedzi Petera , eksportuję moje metody w następujący sposób:

Treść 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
}

A tak używam tych metod w innym skrypcie:

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

Oto więcej informacji na temat konwersji metod na zamknięcia w Groovy.

Matthias Braun
źródło
czy jest jakiś konkretny powód, aby używać nazwy zamknięcia jako rozszerzenia?
Anoop
1
@AnoopSS Dodajemy dwa zamknięcia do dodatkowych właściwości Gradle . Te dodatkowe właściwości są zawarte w obiekcie o nazwie ext.
Matthias Braun
Czy możemy w jakiś sposób rzutować wartość jako naszą klasę, która jest zdefiniowana w dołączonym pliku?
GarouDan
Prawdopodobnie dobrym pomysłem jest opublikowanie osobnego pytania z przykładowym kodem na ten temat, @GarouDan.
Matthias Braun
7

Używając Kotlin dsl działa to tak:

build.gradle.kts :

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

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

external.gradle.kts :

extra["foo"] = fun() {
  println("Hello world!")
}
Simon L.
źródło
świetnie, czy istnieje sposób na udostępnienie rzeczywistego typu? w zasadzie tracisz pomoc dotyczącą bezpieczeństwa typów i kompilatorów ... gdybyś mógł udostępnić klasę, która zawiera twoje metody, to mógłbyś skorzystać z kompilatora.
vach
0

Innym podejściem do Kotlin DSL może być:

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))
GarouDan
źródło