O Gradle e o sistema de build padrao para projetos Kotlin, tanto no Android quanto no backend. Com a adocao do Kotlin DSL nos scripts de build (build.gradle.kts em vez de build.gradle), a configuracao ganha autocomplete, type safety e toda a expressividade do Kotlin. Compreender o Gradle profundamente e essencial para qualquer desenvolvedor Kotlin, pois ele controla compilacao, dependencias, testes, empacotamento e publicacao. Neste guia, vamos desde a estrutura basica ate configuracoes avancadas com version catalogs, convention plugins e otimizacao de performance de build.
Gradle com Kotlin DSL: Por Que Migrar
O Kotlin DSL oferece vantagens significativas sobre o Groovy DSL tradicional. Autocomplete no IDE funciona perfeitamente, erros de sintaxe sao detectados em tempo de compilacao e a refatoracao e segura. A configuracao se torna codigo Kotlin real, com tipagem forte e navegacao de codigo.
// build.gradle.kts - Kotlin DSL (recomendado)
plugins {
kotlin("jvm") version "1.9.22"
application
}
group = "com.exemplo"
version = "1.0.0"
application {
mainClass.set("com.exemplo.MainKt")
}
repositories {
mavenCentral()
}
dependencies {
implementation("io.ktor:ktor-server-core:2.3.7")
testImplementation(kotlin("test"))
}
tasks.test {
useJUnitPlatform()
}
Estrutura de um Projeto Multi-Modulo
Projetos maiores se beneficiam de multiplos modulos Gradle:
// settings.gradle.kts
rootProject.name = "meu-projeto"
include(":app")
include(":core:domain")
include(":core:data")
include(":core:network")
include(":feature:produtos")
include(":feature:pedidos")
// Configuracao de resolucao de plugins
pluginManagement {
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
}
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
}
}
Version Catalogs
O Version Catalog centraliza todas as versoes de dependencias em um unico arquivo libs.versions.toml:
// gradle/libs.versions.toml
[versions]
kotlin = "1.9.22"
ktor = "2.3.7"
exposed = "0.46.0"
koin = "3.5.3"
coroutines = "1.7.3"
logback = "1.4.14"
junit = "5.10.1"
mockk = "1.13.9"
[libraries]
ktor-server-core = { module = "io.ktor:ktor-server-core-jvm", version.ref = "ktor" }
ktor-server-netty = { module = "io.ktor:ktor-server-netty-jvm", version.ref = "ktor" }
ktor-server-content-negotiation = { module = "io.ktor:ktor-server-content-negotiation-jvm", version.ref = "ktor" }
ktor-serialization-json = { module = "io.ktor:ktor-serialization-kotlinx-json-jvm", version.ref = "ktor" }
exposed-core = { module = "org.jetbrains.exposed:exposed-core", version.ref = "exposed" }
exposed-dao = { module = "org.jetbrains.exposed:exposed-dao", version.ref = "exposed" }
exposed-jdbc = { module = "org.jetbrains.exposed:exposed-jdbc", version.ref = "exposed" }
koin-core = { module = "io.insert-koin:koin-core", version.ref = "koin" }
koin-ktor = { module = "io.insert-koin:koin-ktor", version.ref = "koin" }
coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "coroutines" }
coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "coroutines" }
junit-jupiter = { module = "org.junit.jupiter:junit-jupiter", version.ref = "junit" }
mockk = { module = "io.mockk:mockk", version.ref = "mockk" }
[bundles]
ktor-server = ["ktor-server-core", "ktor-server-netty", "ktor-server-content-negotiation", "ktor-serialization-json"]
exposed = ["exposed-core", "exposed-dao", "exposed-jdbc"]
testing = ["junit-jupiter", "mockk", "coroutines-test"]
[plugins]
kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
ktor = { id = "io.ktor.plugin", version.ref = "ktor" }
Usando no build.gradle.kts:
plugins {
alias(libs.plugins.kotlin.jvm)
alias(libs.plugins.kotlin.serialization)
alias(libs.plugins.ktor)
}
dependencies {
implementation(libs.bundles.ktor.server)
implementation(libs.bundles.exposed)
implementation(libs.koin.core)
implementation(libs.koin.ktor)
implementation(libs.coroutines.core)
testImplementation(libs.bundles.testing)
}
Convention Plugins
Convention plugins permitem compartilhar configuracao entre modulos sem duplicacao:
// buildSrc/build.gradle.kts
plugins {
`kotlin-dsl`
}
repositories {
mavenCentral()
}
// buildSrc/src/main/kotlin/kotlin-library-conventions.gradle.kts
plugins {
kotlin("jvm")
}
group = "com.exemplo"
kotlin {
jvmToolchain(17)
}
tasks.test {
useJUnitPlatform()
}
dependencies {
testImplementation("org.junit.jupiter:junit-jupiter:5.10.1")
testImplementation("io.mockk:mockk:1.13.9")
}
// Uso em qualquer modulo
// core/domain/build.gradle.kts
plugins {
id("kotlin-library-conventions")
}
dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3")
}
Tasks Customizadas
O Gradle permite criar tasks personalizadas em Kotlin:
// Task simples
tasks.register("limparLogs") {
group = "manutencao"
description = "Remove arquivos de log antigos"
doLast {
val logDir = file("logs")
if (logDir.exists()) {
logDir.listFiles()
?.filter { it.extension == "log" }
?.forEach { it.delete() }
println("Logs removidos com sucesso")
}
}
}
// Task tipada com inputs e outputs
abstract class GerarRelatorioTask : DefaultTask() {
@get:InputDirectory
abstract val sourceDir: DirectoryProperty
@get:OutputFile
abstract val reportFile: RegularFileProperty
@TaskAction
fun gerar() {
val fontes = sourceDir.get().asFile.walkTopDown()
.filter { it.extension == "kt" }
.toList()
val relatorio = buildString {
appendLine("Relatorio do Projeto")
appendLine("Total de arquivos Kotlin: ${fontes.size}")
appendLine("Total de linhas: ${fontes.sumOf { it.readLines().size }}")
}
reportFile.get().asFile.writeText(relatorio)
}
}
tasks.register<GerarRelatorioTask>("gerarRelatorio") {
sourceDir.set(file("src/main/kotlin"))
reportFile.set(file("build/relatorio.txt"))
}
Configuracao para Android
Projetos Android possuem configuracoes especificas:
// build.gradle.kts (app)
plugins {
id("com.android.application")
kotlin("android")
kotlin("plugin.serialization")
id("com.google.dagger.hilt.android")
kotlin("kapt")
}
android {
namespace = "com.exemplo.app"
compileSdk = 34
defaultConfig {
applicationId = "com.exemplo.app"
minSdk = 24
targetSdk = 34
versionCode = 1
versionName = "1.0.0"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
isMinifyEnabled = true
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
debug {
isDebuggable = true
applicationIdSuffix = ".debug"
}
}
buildFeatures {
compose = true
buildConfig = true
}
composeOptions {
kotlinCompilerExtensionVersion = "1.5.8"
}
kotlinOptions {
jvmTarget = "17"
}
}
Otimizacao de Performance do Build
Builds lentos prejudicam a produtividade. Configure o gradle.properties para otimizar:
// gradle.properties
org.gradle.jvmargs=-Xmx4g -XX:+UseParallelGC
org.gradle.parallel=true
org.gradle.caching=true
org.gradle.configuration-cache=true
kotlin.incremental=true
kotlin.caching.enabled=true
// Para projetos Android
android.useAndroidX=true
android.nonTransitiveRClass=true
Boas Praticas com Gradle e Kotlin
- Use Kotlin DSL: migre de Groovy para Kotlin DSL para obter type safety e autocomplete.
- Centralize versoes com Version Catalogs: elimina inconsistencias de versoes entre modulos.
- Crie convention plugins: evita duplicacao de configuracao em projetos multi-modulo.
- Configure cache e builds paralelos: melhora significativamente o tempo de build.
- Minimize dependencias transitivas: use
implementationem vez deapiquando a dependencia nao precisa ser exposta ao consumidor. - Atualize o Gradle regularmente: cada versao traz melhorias de performance e novos recursos.
- Use
buildSrcou composite builds: para logica de build complexa e compartilhada.
Erros Comuns e Armadilhas
- Confundir
implementationeapi:apiexpoe a dependencia aos consumidores do modulo, aumentando o tempo de compilacao. Useimplementationpor padrao. - Nao configurar cache: sem cache, builds incrementais nao aproveitam resultados anteriores.
- Version conflicts: dependencias transitivas podem trazer versoes incompativeis. Use
resolutionStrategyouconstraintspara resolver. - buildSrc invalida todo o cache: qualquer mudanca no
buildSrcrecompila todo o projeto. Para projetos grandes, prefira composite builds. - Ignorar warnings de depreciacao: warnings no Gradle frequentemente antecedem breaking changes em versoes futuras. Resolva-os proativamente.
Conclusao e Proximos Passos
Dominar o Gradle e tao importante quanto dominar a linguagem Kotlin em si. Uma configuracao de build bem estruturada acelera o desenvolvimento, facilita a colaboracao e prepara o projeto para CI/CD. Explore nossos guias sobre CI/CD e Docker para integrar o Gradle em pipelines de deploy automatizado e leve a automacao do seu projeto ao proximo nivel.