Build Tools #
Build tool adalah fondasi dari setiap proyek Kotlin yang serius. Ia mengatur bagaimana kode dikompilasi, dependensi diunduh dan dikelola, artifact dipaket, dan test dijalankan. Memilih build tool yang tepat dan memahami cara mengonfigurasinya adalah keterampilan yang membedakan developer yang bisa sekadar “menulis kode” dari yang benar-benar bisa mengelola proyek secara profesional. Di ekosistem Kotlin, Gradle adalah pilihan utama yang sangat direkomendasikan — ia adalah build tool resmi untuk Android dan memiliki integrasi terdalam dengan toolchain Kotlin. Artikel ini membahas Gradle secara mendalam dengan Kotlin DSL, Maven sebagai alternatif di lingkungan enterprise, perbandingan build tool yang tersedia, dan praktik terbaik struktur proyek.
Peta Build Tool di Ekosistem Kotlin #
flowchart TD
A[Proyek Kotlin Baru] --> B{Jenis Proyek?}
B -- Android --> C[Gradle + Kotlin DSL\nSatu-satunya pilihan praktis]
B -- Backend / Library / CLI --> D{Lingkungan Tim?}
D -- Sudah pakai Maven --> E[Maven + kotlin-maven-plugin]
D -- Proyek baru --> F[Gradle + Kotlin DSL\nDirekomendasikan]
B -- Monorepo Besar --> G[Bazel\nUntuk skala sangat besar]Gradle — Build Tool Utama untuk Kotlin #
Gradle adalah build tool berbasis Groovy/Kotlin DSL yang menggunakan konsep incremental build dan build cache — ia hanya mengompilasi ulang bagian yang berubah, membuat proses build jauh lebih cepat pada proyek besar. Gradle memiliki dua bahasa DSL: Groovy (lama, file build.gradle) dan Kotlin DSL (modern, file build.gradle.kts). Untuk proyek Kotlin, gunakan Kotlin DSL — lebih type-safe, auto-complete lebih baik di IDE, dan konsisten dengan bahasa yang kamu tulis.
Struktur Proyek Gradle Standar #
proyek-kotlin/
├── build.gradle.kts ← konfigurasi build utama
├── settings.gradle.kts ← nama proyek dan daftar submodule
├── gradle.properties ← properti global
├── gradlew ← Gradle wrapper script (Linux/macOS)
├── gradlew.bat ← Gradle wrapper script (Windows)
├── gradle/
│ ├── wrapper/
│ │ └── gradle-wrapper.properties ← versi Gradle yang digunakan
│ └── libs.versions.toml ← Version Catalog (opsional tapi direkomendasikan)
└── src/
├── main/
│ └── kotlin/ ← kode sumber utama
└── test/
└── kotlin/ ← kode test
settings.gradle.kts — Titik Awal Proyek
#
// settings.gradle.kts
rootProject.name = "aplikasi-kotlin"
// Daftar submodule untuk proyek multi-modul
include(":core")
include(":api")
include(":service")
include(":repository")
build.gradle.kts — Konfigurasi Build Utama
#
// build.gradle.kts
plugins {
kotlin("jvm") version "2.0.0"
application // untuk membuat aplikasi yang bisa dijalankan
}
group = "com.myapp"
version = "1.0.0"
repositories {
mavenCentral() // repository utama untuk library Java/Kotlin
maven("https://jitpack.io") // repository tambahan
}
dependencies {
// Dependensi utama
implementation("org.jetbrains.kotlin:kotlin-stdlib")
// Coroutines
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.0")
// Logging
implementation("ch.qos.logback:logback-classic:1.5.3")
// Test
testImplementation("org.jetbrains.kotlin:kotlin-test")
testImplementation("io.mockk:mockk:1.13.10")
}
application {
mainClass.set("com.myapp.MainKt") // titik masuk program
}
kotlin {
jvmToolchain(17) // targetkan JDK 17
}
tasks.test {
useJUnitPlatform()
}
Version Catalog — Manajemen Versi Terpusat #
Version Catalog adalah fitur Gradle modern (sejak versi 7.4) yang memungkinkan mendefinisikan semua versi library di satu tempat. Ini menghilangkan duplikasi versi di proyek multi-modul dan memudahkan pembaruan.
gradle/libs.versions.toml
#
[versions]
kotlin = "2.0.0"
coroutines = "1.8.0"
ktor = "2.3.9"
exposed = "0.49.0"
logback = "1.5.3"
mockk = "1.13.10"
junit = "5.10.2"
[libraries]
# Kotlin
kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib", version.ref = "kotlin" }
kotlinx-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "coroutines" }
# Ktor
ktor-server-core = { module = "io.ktor:ktor-server-core", version.ref = "ktor" }
ktor-server-netty = { module = "io.ktor:ktor-server-netty", version.ref = "ktor" }
ktor-server-content-negotiation = { module = "io.ktor:ktor-server-content-negotiation", version.ref = "ktor" }
ktor-serialization-kotlinx-json = { module = "io.ktor:ktor-serialization-kotlinx-json", version.ref = "ktor" }
# Database
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" }
# Logging
logback-classic = { module = "ch.qos.logback:logback-classic", version.ref = "logback" }
# Test
mockk = { module = "io.mockk:mockk", version.ref = "mockk" }
junit-jupiter = { module = "org.junit.jupiter:junit-jupiter", version.ref = "junit" }
[bundles]
# Grup library yang selalu dipakai bersama
ktor-server = ["ktor-server-core", "ktor-server-netty", "ktor-server-content-negotiation"]
exposed = ["exposed-core", "exposed-dao", "exposed-jdbc"]
testing = ["mockk", "junit-jupiter"]
[plugins]
kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
Menggunakan Version Catalog di build.gradle.kts
#
plugins {
alias(libs.plugins.kotlin.jvm)
alias(libs.plugins.kotlin.serialization)
}
dependencies {
// Library individual
implementation(libs.kotlinx.coroutines)
implementation(libs.logback.classic)
// Bundle — tambahkan sekelompok library sekaligus
implementation(libs.bundles.ktor.server)
implementation(libs.bundles.exposed)
// Test
testImplementation(libs.bundles.testing)
}
Proyek Multi-Modul dengan Gradle #
Untuk proyek besar, memecah kode menjadi modul-modul terpisah memberi manfaat: kompilasi inkremental lebih cepat, separation of concerns yang jelas, dan modul bisa digunakan di proyek lain.
Struktur Multi-Modul #
aplikasi-backend/
├── settings.gradle.kts
├── build.gradle.kts ← konfigurasi root (shared config)
├── gradle/
│ └── libs.versions.toml
├── core/ ← domain model, shared utilities
│ ├── build.gradle.kts
│ └── src/main/kotlin/
├── repository/ ← akses database
│ ├── build.gradle.kts
│ └── src/main/kotlin/
├── service/ ← logika bisnis
│ ├── build.gradle.kts
│ └── src/main/kotlin/
└── api/ ← HTTP handler, entry point
├── build.gradle.kts
└── src/main/kotlin/
Root build.gradle.kts — Konfigurasi Bersama
#
// build.gradle.kts (root)
plugins {
alias(libs.plugins.kotlin.jvm) apply false // apply false = jangan apply ke root
}
// Konfigurasi yang berlaku untuk semua submodule
subprojects {
apply(plugin = "org.jetbrains.kotlin.jvm")
repositories {
mavenCentral()
}
kotlin {
jvmToolchain(17)
}
tasks.test {
useJUnitPlatform()
}
}
core/build.gradle.kts — Modul Tanpa Dependensi Internal
#
// core/build.gradle.kts
dependencies {
implementation(libs.kotlinx.coroutines)
testImplementation(libs.bundles.testing)
}
service/build.gradle.kts — Modul yang Bergantung ke Modul Lain
#
// service/build.gradle.kts
dependencies {
// Dependensi ke modul internal
implementation(project(":core"))
implementation(project(":repository"))
// Library eksternal
implementation(libs.kotlinx.coroutines)
testImplementation(libs.bundles.testing)
}
Task Kustom di Gradle #
Gradle memungkinkan kamu mendefinisikan task kustom untuk otomasi — generate kode, packaging, deploy, dan lainnya.
// Task sederhana
tasks.register("salam") {
group = "custom"
description = "Tampilkan pesan sambutan"
doLast {
println("Halo dari Gradle task!")
}
}
// Task dengan input dan output (incremental)
tasks.register<Copy>("salinKonfigurasi") {
group = "custom"
description = "Salin file konfigurasi ke build dir"
from("src/main/resources/config")
into("${buildDir}/config")
include("*.yaml", "*.properties")
}
// Task yang bergantung ke task lain
tasks.register("buildDanCek") {
group = "custom"
dependsOn("build", "test")
doLast {
println("Build dan test berhasil!")
}
}
// Modifikasi task yang sudah ada
tasks.named<Jar>("jar") {
manifest {
attributes(
"Main-Class" to "com.myapp.MainKt",
"Implementation-Version" to project.version
)
}
// Fat JAR — sertakan semua dependensi
from(configurations.runtimeClasspath.get().map {
if (it.isDirectory) it else zipTree(it)
})
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
}
Perintah Gradle yang Paling Sering Digunakan #
# Build proyek
./gradlew build
# Jalankan test saja
./gradlew test
# Jalankan aplikasi
./gradlew run
# Bersihkan hasil build sebelumnya
./gradlew clean
# Build tanpa test
./gradlew build -x test
# Lihat semua task yang tersedia
./gradlew tasks
# Jalankan task di submodule tertentu
./gradlew :service:test
# Tampilkan dependensi
./gradlew dependencies
./gradlew :api:dependencies --configuration runtimeClasspath
# Build dengan profiling (untuk analisis performa build)
./gradlew build --profile
# Build dengan info lengkap
./gradlew build --info
# Paksa download ulang semua dependensi
./gradlew build --refresh-dependencies
gradle.properties — Konfigurasi Global
#
# gradle.properties
# Tingkatkan memori JVM untuk build besar
org.gradle.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=512m
# Aktifkan build cache
org.gradle.caching=true
# Aktifkan parallel build (untuk multi-modul)
org.gradle.parallel=true
# Aktifkan configuration cache (Gradle 8+)
org.gradle.configuration-cache=true
# Versi Kotlin untuk digunakan di seluruh proyek
kotlin.code.style=official
# Android-specific (jika proyek Android)
android.useAndroidX=true
android.enableJetifier=true
Maven — Untuk Lingkungan Enterprise #
Maven menggunakan XML (pom.xml) untuk konfigurasi dan mengandalkan convention over configuration — struktur direktori standar tidak perlu dikonfigurasi. Maven masih banyak digunakan di perusahaan besar yang sudah memiliki infrastruktur Maven (Nexus, Artifactory).
Struktur Proyek Maven #
proyek-maven/
├── pom.xml ← konfigurasi build utama
└── src/
├── main/
│ └── kotlin/ ← kode sumber
└── test/
└── kotlin/ ← kode test
pom.xml Lengkap untuk Kotlin
#
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.myapp</groupId>
<artifactId>aplikasi-kotlin</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<properties>
<kotlin.version>2.0.0</kotlin.version>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- Kotlin stdlib -->
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib</artifactId>
<version>${kotlin.version}</version>
</dependency>
<!-- Coroutines -->
<dependency>
<groupId>org.jetbrains.kotlinx</groupId>
<artifactId>kotlinx-coroutines-core</artifactId>
<version>1.8.0</version>
</dependency>
<!-- Test -->
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-test-junit5</artifactId>
<version>${kotlin.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<sourceDirectory>${project.basedir}/src/main/kotlin</sourceDirectory>
<testSourceDirectory>${project.basedir}/src/test/kotlin</testSourceDirectory>
<plugins>
<!-- Plugin kompilasi Kotlin -->
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<version>${kotlin.version}</version>
<executions>
<execution>
<id>compile</id>
<phase>compile</phase>
<goals><goal>compile</goal></goals>
</execution>
<execution>
<id>test-compile</id>
<phase>test-compile</phase>
<goals><goal>test-compile</goal></goals>
</execution>
</executions>
<configuration>
<jvmTarget>17</jvmTarget>
</configuration>
</plugin>
</plugins>
</build>
</project>
Perintah Maven yang Sering Digunakan #
# Build dan install ke local repository
mvn install
# Build tanpa test
mvn install -DskipTests
# Jalankan test saja
mvn test
# Bersihkan
mvn clean
# Full clean + build
mvn clean install
# Tampilkan dependensi
mvn dependency:tree
# Update versi dependensi
mvn versions:display-dependency-updates
Perbandingan Build Tool #
| Aspek | Gradle (Kotlin DSL) | Maven | Bazel |
|---|---|---|---|
| Bahasa konfigurasi | Kotlin (type-safe) | XML (verbose) | Starlark (Python-like) |
| Kurva belajar | Sedang | Rendah-Sedang | Tinggi |
| Performa | Sangat baik (incremental, cache) | Sedang | Sangat baik (distributed cache) |
| Ekosistem plugin | Sangat kaya | Sangat kaya | Terbatas |
| Android support | ✓ Resmi | ✗ Tidak | ✓ Terbatas |
| Multi-modul | ✓ Sangat baik | ✓ Baik | ✓ Excellent |
| IDE integration | ✓ Sangat baik | ✓ Baik | ✓ Memadai |
| Cocok untuk | Semua proyek Kotlin | Enterprise / Java migrasi | Monorepo sangat besar |
Gradle Wrapper — Mengunci Versi Gradle #
Gradle Wrapper (gradlew) memastikan semua anggota tim menggunakan versi Gradle yang sama, tanpa perlu menginstal Gradle secara manual. Selalu commit file wrapper ke repository.
# gradle/wrapper/gradle-wrapper.properties
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
# Update versi Gradle wrapper
./gradlew wrapper --gradle-version 8.7
# Verifikasi versi yang digunakan
./gradlew --version
Selalu gunakan./gradlew(bukangradlelangsung) di dalam proyek. Ini memastikan semua developer dan CI/CD menggunakan versi Gradle yang sama sesuai yang dikonfigurasi digradle-wrapper.properties, bukan versi yang terinstal di sistem masing-masing.
Tips dan Praktik Terbaik #
Optimalkan Performa Build #
// build.gradle.kts
// Aktifkan incremental annotation processing
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> {
compilerOptions {
freeCompilerArgs.addAll(
"-opt-in=kotlin.RequiresOptIn",
"-Xjsr305=strict" // strict null-safety untuk interop Java
)
}
}
// Konfigurasi test paralel
tasks.withType<Test> {
maxParallelForks = (Runtime.getRuntime().availableProcessors() / 2).coerceAtLeast(1)
forkEvery = 100 // restart JVM setiap 100 test untuk cegah memory leak
}
Pisahkan Source Set untuk Test Integrasi #
// Tambah source set baru untuk integration test
sourceSets {
create("integrationTest") {
kotlin.srcDir("src/integrationTest/kotlin")
resources.srcDir("src/integrationTest/resources")
compileClasspath += sourceSets["main"].output + configurations["testRuntimeClasspath"]
runtimeClasspath += output + compileClasspath
}
}
tasks.register<Test>("integrationTest") {
description = "Jalankan integration tests"
group = "verification"
testClassesDirs = sourceSets["integrationTest"].output.classesDirs
classpath = sourceSets["integrationTest"].runtimeClasspath
shouldRunAfter("test")
}
Ringkasan #
- Gradle + Kotlin DSL adalah standar — untuk proyek Kotlin baru, selalu pilih Gradle dengan
build.gradle.kts. Type-safe, auto-complete di IDE, dan konsisten dengan bahasa yang kamu tulis.- Gunakan Version Catalog — definisikan semua versi di
gradle/libs.versions.toml. Ini menghilangkan duplikasi versi di proyek multi-modul dan mempermudah pembaruan library secara terpusat.- Gradle Wrapper adalah wajib — commit
gradlew,gradlew.bat, dangradle/wrapper/ke repository. Gunakan./gradlewbukangradlelangsung untuk memastikan konsistensi versi.- Aktifkan
org.gradle.caching=truedanorg.gradle.parallel=truedigradle.propertiesuntuk mempercepat build secara signifikan, terutama di proyek multi-modul.- Proyek multi-modul untuk proyek besar — pecah menjadi modul
core,repository,service,api. Ini mempercepat kompilasi inkremental dan membuat dependency graph lebih jelas.- Maven untuk lingkungan enterprise — jika tim sudah memiliki infrastruktur Maven (Nexus, CI yang dikonfigurasi untuk Maven), gunakan
kotlin-maven-plugin. Tidak perlu migrasi ke Gradle jika sudah ada investasi Maven.- Bazel untuk monorepo sangat besar — hanya pertimbangkan Bazel jika proyek benar-benar besar (ribuan modul), membutuhkan distributed build cache, atau memiliki tim DevOps khusus untuk mengelola build infrastructure.
- Optimalkan performa build — aktifkan incremental compilation, parallel test execution, dan configuration cache. Untuk proyek besar, perbedaan waktu build bisa signifikan.