Extraia métodos comuns do script de compilação do Gradle

86

Eu tenho um script de compilação do Gradle ( build.gradle), no qual criei algumas tarefas. Essas tarefas consistem principalmente em chamadas de método. Os métodos chamados também estão no script de construção.

Agora, esta é a situação:

Estou criando uma boa quantidade de scripts de construção, que contêm tarefas diferentes, mas utilizam os mesmos métodos do script original. Portanto, gostaria de extrair esses "métodos comuns" de alguma forma, para que possa reutilizá-los facilmente em vez de copiá-los para cada novo script que criar.

Se o Gradle fosse PHP, algo como o seguinte seria o ideal:

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

Mas é claro, isso não é possível. Ou é?

Enfim, como posso alcançar esse resultado? Qual é o melhor método possível para fazer isso? Já li a documentação do Gradle, mas não consigo determinar qual método será o mais fácil e mais adequado para isso.

Desde já, obrigado!


ATUALIZAR:

Consegui extrair os métodos em outro arquivo

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

então a estrutura é a seguinte:

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

Depois de executar uma tarefa de build.gradle, me deparei com um novo problema: aparentemente, os métodos não são reconhecidos quando estão emcommon-methods.gradle .

Alguma ideia de como consertar isso?

Pieter VDE
fonte
Tem certeza de que precisa escrever os métodos? Você perderia algumas das vantagens do Gradle se escrever seus scripts de construção em termos de métodos; o mais importante, será necessário um trabalho extra para que a construção incremental funcione corretamente. A abstração pretendida é usar e reutilizar Task s. Você também pode criar tarefas personalizadas . Talvez você deva considerar colocar as implementações que você tem agora nos métodos em tarefas.
Alpar
@Alpar e outros ; que propósito é servido fazer algo como um timestamp()ou currentWorkingDirectory()métodos como task-s (por exemplo). Funções utilitárias e coisas semelhantes são nominalmente escalares - elas não seriam tarefas, exceto pelo fato de que há limitações na reutilização de código embutida no Gradle e na maioria dos sistemas de construção. Gosto do mundo DRY, onde posso fazer uma coisa UMA vez e reutilizá-la. Na verdade, estendendo o exemplo de @Pieter VDE, eu também uso um root.gradlepadrão " " para meu projeto pai - O arquivo build.gradle geralmente define alguns detalhes do projeto e então apply ${ROOT}...
será
Se você precisa de uma maneira centralizada de trabalhar com propriedades, talvez esta pergunta possa ajudá-lo: stackoverflow.com/questions/60251228/…
GarouDan

Respostas:

76

Não é possível compartilhar métodos, mas você pode compartilhar propriedades extras contendo um closure, que se resume na mesma coisa. Por exemplo, declare ext.foo = { ... }in common-methods.gradle, use apply from:para aplicar o script e chame o encerramento com foo().

Peter Niederwieser
fonte
1
Realmente funciona! Mas eu tenho uma pergunta sobre isso: e os métodos que retornam algo? Fe File foo(String f)se tornará ext.foo = { f -> ... }, posso então fazer algo como File f = foo(...):?
Pieter VDE
2
Aparentemente, a pergunta no meu comentário anterior é possível. Então, obrigado Peter, por responder a esta pergunta!
Pieter VDE
1
@PeterNiederwieser Por que não é possível? Gradle.org pensa o contrário: docs.gradle.org/current/userguide/…
IgorGanapolsky
1
@IgorGanapolsky obrigado pelo link. Gostaria de saber como posso usar um valor gerado em um arquivo de compilação separado no gradle.build - dessa forma seria muito útil :)
kiedysktos
@IgorGanapolsky Como o link que você compartilhou deve ajudar no contexto da pergunta de Peter VDEs?
t0r0X
161

Com base na resposta de Peter , é assim que exporto meus métodos:

Conteúdo de 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
}

E é assim que uso esses métodos em outro script:

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

Aqui está mais informações sobre como converter métodos em fechamentos no Groovy.

Matthias Braun
fonte
há algum motivo específico para usar o nome de encerramento como ext?
Anoop
1
@AnoopSS Adicionamos os dois encerramentos às propriedades extras do Gradle . Essas propriedades extras são agrupadas em um objeto chamado ext.
Matthias Braun
Podemos, de alguma forma, converter o valor como uma classe nossa, que é definida no arquivo incluído?
GarouDan de
É provavelmente uma boa ideia postar uma pergunta separada com código de exemplo sobre isso, @GarouDan.
Matthias Braun
7

Usando o dsl Kotlin , funciona assim:

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
fonte
1
ótimo, há uma maneira de compartilhar o tipo real? você está basicamente perdendo a segurança de tipo e a ajuda dos compiladores ... se você pudesse compartilhar a classe que contém seus métodos, então você poderia fazer uso do compilador.
vach
0

Outra abordagem para Kotlin DSL poderia ser:

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
fonte