Build Tools

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 #

AspekGradle (Kotlin DSL)MavenBazel
Bahasa konfigurasiKotlin (type-safe)XML (verbose)Starlark (Python-like)
Kurva belajarSedangRendah-SedangTinggi
PerformaSangat baik (incremental, cache)SedangSangat baik (distributed cache)
Ekosistem pluginSangat kayaSangat kayaTerbatas
Android support✓ Resmi✗ Tidak✓ Terbatas
Multi-modul✓ Sangat baik✓ Baik✓ Excellent
IDE integration✓ Sangat baik✓ Baik✓ Memadai
Cocok untukSemua proyek KotlinEnterprise / Java migrasiMonorepo 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 (bukan gradle langsung) di dalam proyek. Ini memastikan semua developer dan CI/CD menggunakan versi Gradle yang sama sesuai yang dikonfigurasi di gradle-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, dan gradle/wrapper/ ke repository. Gunakan ./gradlew bukan gradle langsung untuk memastikan konsistensi versi.
  • Aktifkan org.gradle.caching=true dan org.gradle.parallel=true di gradle.properties untuk 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.

← Sebelumnya: Regex   Berikutnya: Multi Threading →

About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact