Pada proyek multi-modul, deklarasi dependensi yang tersebar di banyak build.gradle.kts menjadi sumber masalah: versi tidak konsisten antar modul, duplikasi koordinat, dan kesulitan upgrade. Version catalog adalah fitur Gradle modern (stabil sejak Gradle 7.4) yang memusatkan deklarasi versi di satu file TOML. File ini menjadi sumber kebenaran tunggal untuk seluruh modul. Pada chapter ini kita akan membahas struktur file libs.versions.toml, cara mereferensikannya di build script, serta pola penggunaan [bundles] untuk mengelompokkan dependensi yang sering dipakai bersama.
Struktur File libs.versions.toml #
File gradle/libs.versions.toml terdiri atas empat section utama:
[versions]
kotlin = "2.0.21"
coroutines = "1.8.1"
ktor = "2.3.12"
logback = "1.5.6"
[libraries]
kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "coroutines" }
kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version = "1.6.3" }
ktor-server-core = { module = "io.ktor:ktor-server-core", version.ref = "ktor" }
ktor-server-netty = { module = "io.ktor:ktor-server-netty", version.ref = "ktor" }
logback-classic = { module = "ch.qos.logback:logback-classic", version.ref = "logback" }
[bundles]
ktor = ["ktor-server-core", "ktor-server-netty"]
[plugins]
kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }Mari kita bedah struktur di atas:
[versions]: deklarasi versi tunggal. Variabel dapat dirujuk berulang viaversion.ref. Hal ini memastikan seluruh artefak dari grup yang sama (mis.kotlinx-coroutines-*) menggunakan versi yang konsisten.[libraries]: deklarasi artefak. Fieldmoduleberisi koordinatgroup:name. Fieldversiondapat berupa string literal ("1.6.3") atauversion.ref = "alias"untuk merujuk versi di section[versions].[bundles]: grup artefak yang sering dipakai bersamaan. Bundlektordi atas akan menambahktor-server-coredanktor-server-nettysekaligus saat direferensikan sebagailibs.bundles.ktor.[plugins]: deklarasi plugin Gradle. Plugin ini dapat diterapkan diplugins { alias(libs.plugins.kotlin.jvm) }.
Referensi di build.gradle.kts #
Setelah file libs.versions.toml dibuat, Gradle otomatis menyediakan typed accessor libs.* di semua build.gradle.kts proyek. Alias di TOML (kotlinx-coroutines-core) diubah menjadi dot notation (libs.kotlinx.coroutines.core).
plugins {
alias(libs.plugins.kotlin.jvm)
alias(libs.plugins.kotlin.serialization)
}
dependencies {
implementation(libs.kotlinx.coroutines.core)
implementation(libs.kotlinx.serialization.json)
// Bundle: menambah dua artefak sekaligus
implementation(libs.bundles.ktor)
implementation(libs.logback.classic)
}Demonstrasi Version Catalog #
Kode berikut mencantumkan struktur catalog dan contoh akses dari build script.
fun main() {
println("=== Version Catalog ===")
println("File: gradle/libs.versions.toml")
println()
println("Struktur file .toml:")
println(" [versions] - deklarasi versi tunggal")
println(" [libraries] - deklarasi artefak dengan alias")
println(" [bundles] - grup artefak yang sering dipakai bersama")
println(" [plugins] - deklarasi plugin Gradle")
println()
println("Contoh akses dari build.gradle.kts:")
println(" libs.coroutines.core -> kotlinx-coroutines-core")
println(" libs.kotlinx.serialization -> kotlinx-serialization-json")
println(" libs.bundles.ktor -> ktor-server-core + ktor-server-netty")
println(" libs.jetbrains.kotlin.jvm -> plugin kotlin(\"jvm\")")
println()
println("Keuntungan:")
println(" - Versi terpusat di satu file")
println(" - IDE autocomplete penuh (typed accessor)")
println(" - Berbagi antar modul tanpa duplikasi")
println(" - Update versi cukup di satu tempat")
}Output yang Dihasilkan:
=== Version Catalog ===
File: gradle/libs.versions.toml
Struktur file .toml:
[versions] - deklarasi versi tunggal
[libraries] - deklarasi artefak dengan alias
[bundles] - grup artefak yang sering dipakai bersama
[plugins] - deklarasi plugin Gradle
Contoh akses dari build.gradle.kts:
libs.coroutines.core -> kotlinx-coroutines-core
libs.kotlinx.serialization -> kotlinx-serialization-json
libs.bundles.ktor -> ktor-server-core + ktor-server-netty
libs.jetbrains.kotlin.jvm -> plugin kotlin("jvm")
Keuntungan:
- Versi terpusat di satu file
- IDE autocomplete penuh (typed accessor)
- Berbagi antar modul tanpa duplikasi
- Update versi cukup di satu tempatMari kita telaah kode di atas:
- Lokasi file:
gradle/libs.versions.tomladalah path default yang dikenali Gradle. Path alternatif dapat ditentukan disettings.gradle.ktsviadependencyResolutionManagement.versionCatalogs. - Konversi alias ke accessor: Gradle mengonversi tanda hubung (
-) dan titik (.) di alias TOML menjadi titik di accessor Kotlin. Mis.ktor-server-core→libs.ktor.server.core. Kita dapat memilih format yang lebih ringkas:ktorCore→libs.ktor.core. - Keuntungan typed accessor: IntelliJ memberi autocomplete penuh. Bila alias dihapus dari TOML, build script langsung galat kompilasi (bukan runtime).
Custom Catalog Location #
Bila catalog berada di luar gradle/, definisikan secara eksplisit:
dependencyResolutionManagement {
versionCatalogs {
create("libs") {
from(files("../gradle/libs.versions.toml"))
}
}
}Bundle dan Plugin #
Bundle sangat berguna saat beberapa artefak selalu dipakai bersama. Mis. untuk testing dengan MockK + JUnit:
[libraries]
junit-jupiter = { module = "org.junit.jupiter:junit-jupiter", version = "5.10.0" }
mockk = { module = "io.mockk:mockk", version = "1.13.10" }
kotlinx-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "coroutines" }
[bundles]
testing = ["junit-jupiter", "mockk", "kotlinx-coroutines-test"]Referensi: testImplementation(libs.bundles.testing).
Plugin dan Version #
Plugin juga dapat dirujuk via catalog. Sintaksis alias(libs.plugins.kotlin.jvm) lebih ringkas dan type-safe dibanding id("org.jetbrains.kotlin.jvm") version "2.0.21":
[plugins]
kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }plugins {
alias(libs.plugins.kotlin.jvm)
alias(libs.plugins.kotlin.serialization)
}Pola Versi Rich #
Catalog mendukung rich version untuk kontrol lebih:
[libraries]
guava = {
module = "com.google.guava:guava",
version = {
require = "33.0.0-jre"
prefer = "33.1.0-jre"
}
}
slf4j-api = {
module = "org.slf4j:slf4j-api",
version = { strictly = "2.0.13" }
}Rich version sangat berguna bila kita membatasi range versi yang dapat ditarik secara transitif. Mis. strictly = "2.0.13" memaksa Gradle memilih tepat versi tersebut meski dependensi lain meminta versi berbeda.
Membandingkan dengan Pendekatan Lama #
Sebelum version catalog stabil, dua pendekatan umum dipakai di proyek Kotlin: extra properties (ext.kotlinVersion = "2.0.21") dan buildSrc (file Kotlin yang dikompilasi menjadi classpath Gradle). Version catalog menggantikan keduanya dengan keunggulan: tidak memerlukan kompilasi tambahan, mendukung autocomplete, dan tersedia lintas modul tanpa konfigurasi eksplisit.
Pendekatan buildSrc tetap berguna untuk logika build kompleks (custom task, plugin lokal), tetapi untuk deklarasi versi sederhana, version catalog adalah pilihan default.
Referensi Lanjutan #
- Gradle version catalogs
— panduan resmi Gradle untuk version catalogs di file
libs.versions.toml - Gradle — rujukan Kotlin + Gradle untuk integrasi version catalog pada proyek Kotlin
Penutup #
Version catalog adalah praktik standar untuk proyek Gradle modern. Pemusatan versi mempermudah upgrade dan menjaga konsistensi antar modul. Pada chapter berikutnya kita akan membahas bagaimana Gradle menyelesaikan dependensi transitif dan menangani konflik versi.