A automação das tarefas de construção é crucial para todo projeto de software, e o Gradle, com seu DSL Kotlin, oferece uma flexibilidade excepcional. Durante nossa conversa, exploramos como centralizar e reutilizar a lógica de construção, especialmente para os relatórios de testes, ao aproveitartodos os projetos, buildSrce tarefas personalizadas em Kotlin.

Centralizar as Configurações com allprojects e `subprojects

Os blocos`allprojects` et `subprojects`no seu`build.gradle.kts`A raiz são fundamentais para aplicar configurações comuns através do seu projeto multi-módulo.

*allprojects { …​ }: Aplique a configuração paraprojeto raiz e a todos os seus subprojetos. Ideal para definir um`group`, uma`version`, alguns`repositories`comuns *subprojects { …​ }`Aplica a configuraçãoexclusivamente aos subprojetos, excluindo o projeto raiz. Perfeito para aplicar plugins específicos aos módulos (como`java ou kotlin-jvm) ou dependências comuns às suas bibliotecas.

Aqui está um exemplo ilustrativo :

// build.gradle.kts (projet racine)
plugins {
    base // Appliqué au projet racine
}

allprojects {
    group = "com.example"
    version = "1.0.0"

    repositories {
        mavenCentral()
    }

    tasks.withType<org.gradle.api.tasks.testing.Test> {
        useJUnitPlatform() // Configuration commune des tests pour tous les projets
    }
}

subprojects {
    apply(plugin = "java")
    apply(plugin = "org.jetbrains.kotlin.jvm")

    dependencies {
        implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
    }

}

</think>

</think>`buildSrc`: O Canivete Suíço da Lógica de Build

Quando a lógica das suas tarefas se tornar complexa ou precisar ser reutilizada,buildSrcÉ a solução preferida. É um módulo especial do Gradle que é compilado antes dos scripts de construção principais, tornando suas classes disponíveis no class path de todo o seu build.

Por que usar`buildSrc`para as tarefas?

  • reutilizabilidadeUma tarefa definida em`buildSrc`pode ser aplicada a qualquer projeto do build.

  • Organização: Centraliza o código de compilação, tornando-o mais limpo e manutenível.

  • Type Safety e Autocompletar: O código Kotlin em`buildSrc`é compilado, oferecendo a verificação de erros e o autocomplete da sua IDE, melhorando a experiência de desenvolvimento.

\=== Diagrama de Fluxo`buildSrc`(Código PlantUML)

Para gerar este diagrama, copie o código abaixo e cole-o em uma ferramenta que suporte PlantUML.

[source,plantuml]

@startuml skinparam handwritten true skinparam monochrome true

rectangle "Processo de Construção Gradle" { componente "buildSrc" como BS { arquivo "MyCustomTask.kt" como T arquivo "MyConventionPlugin.kt" como P } componente "Root Project" como RP componente "Subproject A" como SA componente "Subproject B" como SB }

T --\> P : "está definido em P --\> RP : "is applied to P --\> SA : "is applied to P -→ SB : "é aplicado a

RP --|\> SA : "contém RP --|\> SB : "contém

RP -up→ BS : "depende de (para a lógica de construção) SA -up-\> BS : "depende de (para a lógica de construção) SB -up-\> BS : "depende de (para a lógica de construção)

nota à direita de T Classes de tarefas personalizadas (ex: OpenTestReportTask) nota final

nota à direita de P Plugins de convenção que registram as tarefas nota final

@enduml

\== Criação de Tarefas de Relatórios Abstratas

Para gerenciar os relatórios de testes, concebemos uma abordagem modular utilizando uma classe de tarefa abstrata em`buildSrc`. Conforme às suas necessidades, esta classe pode herdar de`DefaultTask` ou de Exec.

\=== Tarefa Abstrata`OpenTestReportTask`(heredando de`DefaultTask`)

Esta abordagem é recomendada se você precisar de uma lógica Kotlin personalizada que interaja com o sistema de arquivos ou outras APIs do Gradle e então execute um comando externo.

[source,kotlin]

package com.yourpackage

import org.gradle.api.DefaultTask import org.gradle.api.tasks.Input import org.gradle.api.tasks.TaskAction import java.io.File

abstract class OpenTestReportTask : DefaultTask() {

----
----
init {
    group = "verification"
    description = "Opens a test report in Firefox."
    dependsOn("check") // Assure que les rapports sont générés
}

@get:Input
abstract var reportPath: String

@TaskAction
fun openReport() {
    val separator = File.separator
    val reportFile = project.layout.projectDirectory.asFile.toPath()
        .resolve(reportPath.replace("/", separator))
        .toAbsolutePath()
        .toFile()

    if (!reportFile.exists()) {
        logger.warn("Report file does not exist: $reportFile. Ensure 'check' ran.")
        return
    }

    project.exec {
        commandLine("firefox", "--new-tab", reportFile.absolutePath)
    }
    logger.lifecycle("Opened test report: ${reportFile.absolutePath}")
}
----

## }

\=== Implementações Concretas

Essas classes herdam da tarefa abstrata e definem o caminho específico do relatório.

## [source,kotlin]

pacote com.yourpackage

classe abstrata ReportUnitTestsTask : OpenTestReportTask() { init { Abre o relatório de teste unitário no Firefox. reportPath = "build/reports/tests/test/index.html } }

package com.yourpackage

abstract class ReportFunctionalTestsTask : OpenTestReportTask() { init { description = "Abre o relatório de teste funcional no Firefox." reportPath = "build/reports/tests/functionalTest/index.html" } }

\=== Registro das Tarefas

No`build.gradle.kts`do seu projeto raiz :

## [source,kotlin]

tasks.register\<com.yourpackage.ReportUnitTestsTask\>("reportTests") {} tasks.register\<com.yourpackage.ReportFunctionalTestsTask\>("reportFunctionalTests") {}

\=== Diagrama UML das Tarefas de Relatório (Código PlantUML)

Copie o código abaixo e cole-o em uma ferramenta que suporte PlantUML.

## [source,plantuml]

@startuml skinparam handwritten true skinparam monocromático verdadeiro

classe abstrata DefaultTask { }

classe abstrata Exec { \+ commandLine(args: String...) \+ exec() }

classe abstrata OpenTestReportTask estende DefaultTask { \+ group: String = "verification \+ descrição: String \+ dependsOn("check") \+ abstract reportPath: String \+ openReport() : void }

classe abstrata AbstractJbakeExecTask extends Exec { \+ group: String = "verification + descrição: String \+ dependsOn("check") \+ abstract reportRelativePath: String \+ exec() : void }

class ReportUnitTestsTask extends OpenTestReportTask { \+ reportPath: String = "build/reports/tests/test/index.html }

class ReportFunctionalTestsTask extends OpenTestReportTask { \+ reportPath: String = "build/reports/tests/functionalTest/index.html }

class ReportJbakeTestsTask extends AbstractJbakeExecTask { \+ reportRelativePath: String = "build/reports/tests/test/index.html }

class ReportJbakeFunctionalTestsTask extends AbstractJbakeExecTask { \+ reportRelativePath: String = "build/reports/tests/functionalTest/index.html }

OpenTestReportTask \<-- ReportUnitTestsTask OpenTestReportTask \<-- ReportFunctionalTestsTask

AbstractJbakeExecTask <-- ReportJbakeTestsTask AbstractJbakeExecTask \<-- ReportJbakeFunctionalTestsTask

DefaultTask \<|-- OpenTestReportTask Executar \<|-- AbstractJbakeExecTask DefaultTask \<|-- Exec

## @enduml

\== Tarefa Abstrata`AbstractJbakeExecTask`(herdando de`Exec`)

Se a sua tarefa consiste principalmente em executar um comando externo com argumentos variáveis, herde diretamente de**Exec**é mais direto.

## [source,kotlin]

package com.yourpackage

import org.gradle.api.tasks.Exec import org.gradle.api.tasks.Input import java.io.File

abstract class AbstractJbakeExecTask : Exec() {

----

init { group = "verification" description = "Opens a Jbake project report in Firefox." dependsOn("check") }

@get:Input abstract var reportRelativePath: String

override fun exec() { val separator = File.separator val reportFile = project.layout.projectDirectory.asFile.toPath() .resolve(reportRelativePath.replace("/", separator)) .toAbsolutePath() .toFile()

if (!reportFile.exists()) { logger.warn("Report file does not exist: $reportFile. Ensure 'check' ran.") return }

commandLine("firefox", "--new-tab", reportFile.absolutePath) logger.lifecycle("Attempting to open report: ${reportFile.absolutePath}") super.exec() // Appelle la méthode exec() de la super-classe Exec }

## }

\=== Implementações Concretas para`Exec`

## [source,kotlin]

pacote com.yourpackage

abstract class ReportJbakeTestsTask : AbstractJbakeExecTask() { init { description = "Abre o relatório de testes unitários do Jbake no Firefox. reportRelativePath = "build/reports/tests/test/index.html } }

package com.yourpackage

abstract class ReportJbakeFunctionalTestsTask : AbstractJbakeExecTask() { init { description = "Abre o relatório de teste funcional no Firefox." reportRelativePath = "build/reports/tests/functionalTest/index.html" } }

Visibilidade das Tarefas`buildSrc`

As tarefas e classes que você define em**buildSrc**são :

***Visíveis e utilizáveis**por todos os projetos do seu build principal (raiz e subprojetos). É por isso que você pode usar`com.yourpackage.ReportUnitTestsTask`em`allprojects { ... }`. ***Não executáveis**diretamente como tarefas de buildSrc (por exemplo`gradle :buildSrc:reportTests`não funcionaria se a tarefa não fosse registrada especificamente em`buildSrc/build.gradle.kts`)`buildSrc`É um módulo de compilação, não é um módulo de aplicativo executável para essas tarefas do build principal.

Executar um relatório para os testes de`buildSrc`ele mesmo

Si `buildSrc`tem os seus próprios testes e gera relatórios, você pode gravar uma tarefa de relatório diretamente em`buildSrc/build.gradle.kts`:

## [source,kotlin]

plugins { `kotlin-jvm` }

repositórios { mavenCentral() }

tasks.withType<Test> { useJUnitPlatform() reports.html.outputLocation.set(layout.buildDirectory.dir("reports/tests")) }

import com.yourpackage.ReportJbakeTestsTask // Importe sua classe de tarefa

## tasks.register\<ReportJbakeTestsTask\>("reportBuildSrcTests") { // O caminho já está definido na classe, ele apontará para os relatórios de buildSrc }

Você poderá então executar:`./gradlew :buildSrc:test`seguido de`./gradlew :buildSrc:reportBuildSrcTests`.

Ao adotar essas práticas, você construirá sistemas de build do Gradle em Kotlin DSL que não são apenas poderosos, mas também incrivelmente modulares, manuteníveis e fáceis de entender.

Articles connexes