Skrip build Gradle

Task, dependensi, dan task kustom dalam DSL Groovy Gradle, dengan catatan Kotlin DSL.

Daftar isi

build.gradle adalah skrip Groovy yang dieksekusi Gradle untuk mengonfigurasi sebuah proyek. Skrip ini bukan berkas konfigurasi pasif yang hanya dibaca, melainkan program Groovy utuh yang berjalan pada setiap build. Topik ini membahas cara membaca dan menulis skrip build dalam DSL Groovy, mulai dari mekanisme delegasi blok konfigurasi, struktur minimal proyek, task dengan konfigurasi lazy, properti tambahan, hingga perbandingan singkat dengan Kotlin DSL. Seluruh contoh memakai build untuk pustaka pengolah statistik ujian, dan sintaksnya mengacu pada Gradle 8.x.

build.gradle sebagai skrip Groovy#

Karena berkasnya berupa skrip Groovy utuh, seluruh fitur bahasanya tersedia, mulai dari pernyataan, penugasan, metode, hingga closure. Gradle menjalankan skrip itu dengan objek Project sebagai target utamanya, sehingga penugasan seperti version = '1.0.0' diterapkan langsung pada properti proyek tanpa perlu menulis nama objeknya. Blok konfigurasi seperti repositories { ... } pada dasarnya adalah panggilan metode yang menerima closure sebagai argumen, dan closure itu didelegasikan ke objek sasarannya melalui mekanisme delegasi closure, yaitu pengalihan pencarian metode dan properti kepada objek lain. Di dalam blok repositories, pemanggilan mavenCentral() dieksekusi pada objek pengelola repositori, bukan pada skrip.

Skrip berikut meniru mekanisme tersebut dengan kelas sederhana. Metode repositories menerima closure, mengisi properti delegate milik closure dengan objek pengelola repositori, mengatur resolveStrategy ke DELEGATE_FIRST, lalu menjalankan closure itu. Hasilnya, pemanggilan mavenCentral() dan mavenLokal di dalam closure ditujukan ke objek pengelola, sehingga daftar repositorinya terisi.

groovy Contoh: delegasi blok konfigurasi
// simulasi delegasi: blok konfigurasi dieksekusi pada objek sasarannya
class HandlerRepositori {
    List<String> daftar = []
    void mavenCentral() { daftar << 'Maven Central' }
    void mavenLokal(String nama) { daftar << "repo lokal $nama" }
}

class Proyek {
    String versi = '1.0.0'
    HandlerRepositori repositories = new HandlerRepositori()
    void repositories(Closure konfigurasi) {
        konfigurasi.delegate = repositories
        konfigurasi.resolveStrategy = Closure.DELEGATE_FIRST
        konfigurasi()
    }
}

def proyek = new Proyek()
proyek.repositories {
    mavenCentral()
    mavenLokal 'lab-sekolah'
}
println "Versi      : ${proyek.versi}"
println "Repositori : ${proyek.repositories.daftar}"
text Keluaran
Versi      : 1.0.0
Repositori : [Maven Central, repo lokal lab-sekolah]

Gradle menerapkan pola yang sama pada seluruh blok konfigurasinya. Blok repositories didelegasikan ke RepositoryHandler, blok dependencies ke DependencyHandler, dan konfigurasi task ke objek task yang bersangkutan. Kebiasaan membaca blok sebagai closure yang didelegasikan membuat build.gradle yang panjang sekalipun tetap dapat dilacak ke API yang menjalankannya.

Struktur minimal build.gradle#

Struktur minimal sebuah proyek Java di Gradle memuat tiga blok, yaitu plugins, repositories, dan dependencies. Blok plugins mendaftarkan plugin, yaitu paket ekstensi yang menambahkan kemampuan build ke proyek, misalnya plugin java yang menyediakan kompilasi, struktur folder sumber, dan task pengujiannya. Blok repositories menyatakan lokasi pemuatan dependensi, dan mavenCentral() menambahkan Maven Central yang menjadi repositori publik paling umum. Blok dependencies mendaftarkan pustaka yang dipakai proyek dengan notasi koordinat group:nama:versi.

groovy Contoh: tiga blok struktur minimal
// tidak-dijalankan
// tiga blok inti pada build.gradle proyek Java
plugins {
    id 'java'
}

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.apache.commons:commons-lang3:3.14.0'
}

Baris implementation 'org.apache.commons:commons-lang3:3.14.0' memuat dua informasi sekaligus, yaitu konfigurasi dependensi dan koordinat pustakanya. Konfigurasi implementation menandakan pustaka yang diperlukan saat kompilasi maupun saat runtime, dan Gradle tidak membocorkannya kepada konsumen pustaka. Koordinat tiga bagian itu dibaca sebagai group org.apache.commons, nama commons-lang3, dan versi 3.14.0. Untuk pustaka pengolah statistik ujian, blok yang sama nantinya juga memuat testImplementation untuk pustaka pengujian yang hanya diperlukan pada fase test.

Task dan konfigurasi lazy#

Task adalah unit kerja yang dijalankan Gradle, misalnya mengompilasi kode, menjalankan test, atau menyalin berkas. Gradle menyediakan dua bentuk pembuatan task pada build.gradle. Bentuk task sapa { doLast { ... } } membuat dan mengonfigurasi task segera saat skrip dibaca, sehingga pekerjaan itu terjadi pada setiap build meskipun task tidak dipanggil. Bentuk tasks.register('sapa') { ... } hanya mendaftarkan task, dan konfigurasinya baru berjalan ketika task benar-benar diperlukan.

groovy Contoh: dua bentuk pembuatan task
// tidak-dijalankan
// bentuk segera: task dibuat dan dikonfigurasi saat skrip dibaca
task sapa {
    doLast {
        println 'Halo dari task sapa'
    }
}

// bentuk lazy: konfigurasi menunggu sampai task dipakai
tasks.register('sapaLain') {
    doLast {
        println 'Halo dari task sapaLain'
    }
}

Isi sebuah task berupa daftar aksi yang dijalankan berurutan. Metode doLast menambahkan aksi ke ujung daftar, sedangkan doFirst menyisipkan aksi di awalnya. Skrip berikut meniru daftar aksi tersebut untuk memperlihatkan urutan eksekusinya.

groovy Contoh: urutan aksi doFirst dan doLast
// simulasi urutan aksi task
class TaskSederhana {
    List<Closure> aksi = []
    void doFirst(Closure c) { aksi.add(0, c) }
    void doLast(Closure c)  { aksi.add(c) }
    void jalankan() { aksi.each { it.call() } }
}

def t = new TaskSederhana()
t.doLast { println 'doLast pertama' }
t.doLast { println 'doLast kedua' }
t.doFirst { println 'doFirst disisipkan' }
t.jalankan()
text Keluaran
doFirst disisipkan
doLast pertama
doLast kedua

Urutan keluaran menegaskan perilaku keduanya. Aksi doFirst yang dipanggil terakhir justru berjalan pertama karena disisipkan di depan, sedangkan dua aksi doLast berjalan sesuai urutan penambahannya. Task juga dapat diberi ketergantungan melalui dependsOn, sehingga pemanggilan gradle ujiStatistik menjalankan task lain lebih dahulu. Untuk kebutuhan penyalinan berkas, Gradle menyediakan tipe task Copy yang cukup dikonfigurasi dengan from dan into tanpa menulis aksi manual.

Contoh build.gradle pustaka statistik ujian#

Berkas berikut merangkum seluruh unsur topik ini menjadi satu build.gradle utuh untuk pustaka pengolah statistik ujian, yaitu pustaka yang menghitung ringkasan seperti rerata dan simpangan baku dari nilai peserta.

groovy build.gradle
// tidak-dijalankan
// build.gradle pustaka pengolah statistik ujian

plugins {
    id 'java'
}

version = '1.0.0'

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.apache.commons:commons-lang3:3.14.0'
    testImplementation 'org.junit.jupiter:junit-jupiter:5.10.2'
}

// properti tambahan milik proyek
ext.pengarang = 'Tim Statistik'

// task ringkas dengan konfigurasi lazy
tasks.register('sapa') {
    group = 'pustaka'
    description = 'Mencetak identitas pustaka'
    doLast {
        println "Pustaka ${project.name} versi ${project.version}"
        println "Pengarang: ${project.ext.pengarang}"
    }
}

// task penyalinan laporan dengan tipe Copy
tasks.register('salinLaporan', Copy) {
    from 'laporan'
    into layout.buildDirectory.dir('laporan-arsip')
}

// task uji modul statistik bergantung pada salinLaporan
tasks.register('ujiStatistik') {
    dependsOn 'salinLaporan'
    doLast {
        println 'Modul statistik siap diuji'
    }
}

Blok plugins, repositories, dan dependencies di bagian atas sudah dikenal, dengan tambahan testImplementation untuk pustaka pengujian JUnit yang hanya dipakai saat test. Penugasan ext.pengarang menambahkan extra property, yaitu properti buatan pengguna yang tersimpan pada objek proyek dan dapat dibaca dari cakupan mana pun. Task sapa diberi group dan description agar tampil rapi pada keluaran gradle tasks, lalu mencetak nama pustaka, versi, dan pengarangnya. Task salinLaporan bertipe Copy sehingga cukup diberi from dan into, sedangkan ujiStatistik memakai dependsOn agar salinLaporan selalu selesai lebih dahulu. Memanggil gradle sapa lebih dahulu adalah cara cepat untuk memastikan skrip terbaca tanpa error, dan memanggil gradle ujiStatistik menjalankan dua task dalam urutan yang benar.

Perbandingan dengan Kotlin DSL#

Gradle kini mendukung dua bahasa skrip build. Selain DSL Groovy yang ditulis dalam build.gradle, Gradle menyediakan Kotlin DSL yang ditulis dalam build.gradle.kts, dan sejak Gradle 8.2 pilihan bawaan perintah gradle init untuk proyek baru adalah Kotlin DSL. Keduanya mengakses API Gradle yang sama, dan dalam satu build multi-proyek keduanya boleh dicampur, misalnya sebagian proyek memakai build.gradle sedangkan proyek lain memakai build.gradle.kts. Groovy DSL tetap dipakai luas pada proyek yang sudah ada, sehingga kemampuan membacanya masih penting.

AspekDSL Groovy (build.gradle)Kotlin DSL (build.gradle.kts)
Pendaftaran pluginplugins { id 'java' }plugins { id("java") }
Dependensiimplementation 'org.apache.commons:commons-lang3:3.14.0'implementation("org.apache.commons:commons-lang3:3.14.0")
Task lazytasks.register('sapa') { doLast { println 'hai' } }tasks.register("sapa") { doLast { println("hai") } }
Kutipan stringkutip tunggal dan ganda sama-sama sahwajib kutip ganda

Perbedaannya terletak pada bahasa hostnya. Groovy membolehkan pemanggilan metode tanpa tanda kurung serta string dengan kutip tunggal, sehingga barisnya lebih pendek, sedangkan Kotlin mewajibkan tanda kurung dan kutip ganda. Sebaliknya, Kotlin DSL memberi pemeriksaan tipe dan pelengkapan yang lebih ketat di IDE karena skripnya dikompilasi seperti kode Kotlin biasa. Bagi pembaca Groovy, tabel di atas cukup untuk menerjemahkan blok inti dari satu bentuk ke bentuk lainnya.

Kesalahan umum#

Kesalahan pertama adalah menyimpan nilai yang perlu dibaca lintas cakupan dengan def. Variabel def hanya terlihat pada cakupan tempat ia dideklarasikan, yaitu skrip itu sendiri, sehingga skrip lain maupun kode plugin tidak dapat membacanya. Properti yang harus melekat pada objek proyek dideklarasikan melalui ext, misalnya ext.pengarang = 'Tim Statistik', dan setelah itu nilainya dapat dibaca dari cakupan mana pun yang melihat objek proyek. Perbaikannya konsisten dengan aturan itu, yaitu def untuk variabel lokal skrip dan ext untuk properti proyek.

Kesalahan kedua adalah menulis teks tanpa tanda kutip pada metode DSL. Baris from laporan di dalam task salinLaporan terlihat wajar, tetapi identifier tanpa kutip dicari sebagai properti, sehingga build berhenti dengan MissingPropertyException karena properti laporan tidak ada. Perbaikannya sederhana, yaitu menulis stringnya dengan tanda kutip menjadi from 'laporan'. Aturan yang sama berlaku pada include dan exclude untuk pola nama berkas, serta dependsOn yang menerima nama task.

Ringkasan#

  • build.gradle adalah skrip Groovy utuh, dan blok konfigurasinya berupa panggilan metode yang menerima closure yang didelegasikan ke objek sasarannya, seperti proyek, pengelola dependensi, atau task.
  • Struktur minimal proyek memuat plugins untuk mendaftarkan plugin, repositories untuk menyatakan lokasi pemuatan dependensi, dan dependencies dengan koordinat group:nama:versi.
  • tasks.register adalah bentuk pembuatan task yang direkomendasikan karena konfigurasinya ditunda sampai task dipakai, aksi diurutkan dengan doFirst di awal dan doLast di akhir, ketergantungan diikat dependsOn, dan properti tambahan disimpan melalui ext.
  • Kotlin DSL pada build.gradle.kts adalah alternatif resmi yang kini didorong Gradle dengan sintaks yang mewajibkan kurung dan kutip ganda, sedangkan Groovy DSL tetap dipakai luas.

Referensi#