Saat kita menambah satu dependensi di build.gradle.kts, Gradle tidak hanya mengunduh artefak tersebut tetapi juga seluruh pohon dependensi transitifnya. Sebuah library kecil dapat menarik puluhan artefak lain. Konflik versi antara dependensi transitif adalah sumber masalah umum: dua library membutuhkan versi berbeda dari guava atau slf4j-api, dan Gradle harus memilih satu. Pada chapter ini kita akan membahas strategi resolusi bawaan Gradle, cara mengontrol via strict, exclude, dan constraints, serta perintah untuk memeriksa pohon dependensi.
Dependensi Transitif #
Setiap dependensi yang dideklarasikan di build.gradle.kts membawa dependensi transitif dari POM-nya. Misalnya, deklarasi ktor-server-core akan menarik ktor-http, ktor-utils, kotlinx-coroutines-core, dan kotlin-stdlib. Pohon dependensi dapat dilihat dengan:
# Tampilkan pohon dependensi lengkap
./gradlew dependencies
# Filter per configuration
./gradlew dependencies --configuration runtimeClasspath
# Lihat detail satu dependensi (siapa yang membawanya, ke versi apa)
./gradlew dependencyInsight --dependency kotlinx-coroutines-coreStrategi Resolusi Konflik #
Saat dua dependensi memerlukan versi berbeda dari artefak yang sama, Gradle memilih berdasarkan strategi default: versi tertinggi menang (highest version wins). Misalnya bila library-A:1.0 membawa library-B:1.5 dan library-C:1.0 membawa library-B:2.0, Gradle memilih library-B:2.0.
Strategi lain yang dapat dipakai:
| Strategi | Sintaksis | Kapan dipakai |
|---|---|---|
| Strict version | version { strictly("1.5") } | Bila library-B hanya kompatibel dengan satu versi |
| Required version | version { require("1.5") } | Preferensi versi (bisa dinaikkan bila dependensi lain butuh lebih tinggi) |
| Prefer version | version { prefer("1.5") } | Default bila tidak ada konflik |
| Reject version | version { reject("[1.0,1.5[") } | Versi yang diketahui bermasalah |
exclude | exclude(group = "...", module = "...") | Menghapus dependensi transitif tertentu |
constraints | constraints { implementation("group:name:1.5") } | Membatasi range versi transitif |
Contoh Konflik #
dependencies {
implementation("com.library-a:library-a:1.0") // membawa com.example:common:1.5
implementation("com.library-c:library-c:1.0") // membawa com.example:common:2.0
// Konflik: dua versi common. Default: versi 2.0 dipilih.
// Paksa versi 1.5 bila library-a tidak kompatibel dengan 2.0
implementation("com.example:common") {
version { strictly("1.5") }
}
// Atau hapus dependensi transitif tertentu
implementation("com.library-c:library-c:1.0") {
exclude(group = "com.example", module = "common")
}
// Atau batasi range versi yang diperbolehkan
constraints {
implementation("com.example:common:1.5") {
because("library-a hanya kompatibel dengan 1.5")
}
}
}Demonstrasi Resolusi #
Kode berikut mencantumkan skenario konflik dependensi dan strategi penyelesaiannya.
fun main() {
println("=== Resolusi Dependensi ===")
println()
println("Skenario dependensi transitif:")
println(" app -> library-A:1.0")
println(" library-A:1.0 -> library-B:1.5")
println(" app -> library-C:1.0")
println(" library-C:1.0 -> library-B:2.0 (konflik!)")
println()
println("Strategi penyelesaian konflik:")
println(" Default : highest version wins (library-B:2.0 dipilih)")
println(" Strict version : paksa versi spesifik meski konflik")
println(" exclude : hapus dependensi transitif tertentu")
println(" constraints : batasi range versi yang diperbolehkan")
println()
println("Perintah pemeriksaan:")
println(" ./gradlew dependencies -> tree lengkap")
println(" ./gradlew dependencies --configuration runtimeClasspath")
println(" ./gradlew dependencyInsight --dependency library-B")
println()
println("Saat konflik tidak dapat diselesaikan otomatis, Gradle menghentikan build")
println("dengan pesan 'Conflict(s) found'. Bila ini terjadi, gunakan strict version")
println("atau exclude untuk menyelesaikan secara eksplisit.")
}Output yang Dihasilkan:
=== Resolusi Dependensi ===
Skenario dependensi transitif:
app -> library-A:1.0
library-A:1.0 -> library-B:1.5
app -> library-C:1.0
library-C:1.0 -> library-B:2.0 (konflik!)
Strategi penyelesaian konflik:
Default : highest version wins (library-B:2.0 dipilih)
Strict version : paksa versi spesifik meski konflik
exclude : hapus dependensi transitif tertentu
constraints : batasi range versi yang diperbolehkan
Perintah pemeriksaan:
./gradlew dependencies -> tree lengkap
./gradlew dependencies --configuration runtimeClasspath
./gradlew dependencyInsight --dependency library-B
Saat konflik tidak dapat diselesaikan otomatis, Gradle menghentikan build
dengan pesan 'Conflict(s) found'. Bila ini terjadi, gunakan strict version
atau exclude untuk menyelesaikan secara eksplisit.Mari kita telaah kode di atas:
- Skenario konflik: tiga dependensi membentuk diamond —
appmemakai dua library yang sama-sama membawalibrary-Bdengan versi berbeda. Inilah pola konflik paling umum di proyek nyata. - Strategi default:
highest version winsmeminimalkan risiko missing API, namun dapat memecahkan kompatibilitas biner bila library-B breaking change antara 1.5 dan 2.0. exclude: menghapus dependensi tertentu dari grafik resolusi. Berguna bila sebuah library membawa dependensi yang sudah tidak relevan atau bermasalah (mis.commons-loggingsaat proyek sudah pakaijcl-over-slf4j).constraints: membatasi range versi transitif tanpa menambah dependensi langsung. Cocok bila kita ingin memastikan seluruh dependensi transitif memakai versi aman (mis.>=1.5,<2.0).
dependencyInsight #
Perintah dependencyInsight adalah alat investigasi paling kuat:
$ ./gradlew dependencyInsight --dependency kotlinx-coroutines-core
Project :app
------------------------------------------------------------
kotlinx-coroutines-core:1.8.1 (selected)
variant: runtime
selected by:
- app (constraints) require 1.8.1
requested by:
- app:1.0 via libs.coroutines.core
- io.ktor:ktor-server-core:2.3.12 by artifact
- io.ktor:ktor-utils:2.3.12 by artifactOutput menampilkan versi yang dipilih, siapa yang meminta, dan lewat jalur apa. Informasi ini menjawab pertanyaan “mengapa versi X yang dipilih?”
Konflik Tidak Terselesaikan #
Bila dua dependensi memerlukan versi strict yang berbeda, Gradle menghentikan build:
> Conflict(s) found for the following module(s):
- com.example:common between versions 1.5 and 2.0
Run with --scan to get more insight.Saat pesan ini muncul, gunakan dependencyInsight untuk mengetahui siapa yang meminta tiap versi, lalu pilih strategi: upgrade library-a, downgrade library-c, atau ganti library yang bermasalah dengan alternatif.
Referensi Lanjutan #
- Gradle User Guide — buku panduan Gradle untuk dependency resolution, conflict resolution, dan penguncian versi
- Gradle version catalogs — panduan version catalog sebagai mekanis pusat deklarasi versi yang konsisten
Penutup #
Resolusi dependensi transitif adalah mekanisme kuat tetapi berisiko bila tidak dikelola. Pemeriksaan rutin via dependencies dan dependencyInsight, plus pinning versi via catalog, menjaga proyek tetap sehat. Pada chapter berikutnya kita akan beralih ke Maven — alternatif build tool yang lebih lama tetapi masih dominan di banyak organisasi.