Sering kali kita ingin menghasilkan beberapa versi build dari satu basis kode: versi debug dengan logging ekstra, versi release yang dioptimasi, atau versi free dan pro dengan fitur berbeda. Gradle menyediakan mekanisme build variants untuk mengelola hal ini. Konsep ini paling matang di Android Gradle Plugin, tetapi prinsipnya berlaku juga untuk proyek Kotlin/JVM murni. Pada chapter ini kita akan membahas konsep buildType, flavor, variant, serta bagaimana source set per variant diorganisir.

Konsep Build Variants #

Tiga konsep yang saling membentuk:

  • Build type: tipe build berdasarkan tujuan pengembangan. Default: debug dan release. Build type menentukan optimisasi compiler, debuggable flag, proguard, dsb.
  • Product flavor: dimensi varian produk. Mis. tier dengan nilai free/pro, atau env dengan nilai dev/staging/prod.
  • Variant: kombinasi build type × flavor. Mis. freeDebug, proRelease.

Untuk build type dan flavor tunggal, jumlah variant = buildType × flavor1 × flavor2 × .... Mis. dengan 2 build type dan 2 dimensi flavor (masing-masing 2 nilai), kita memperoleh 8 variant.

Source Set per Variant #

Setiap variant memiliki source set sendiri. Gradle menggabungkan source set saat kompilasi dengan prioritas: variant > buildType > flavor > main.

text
src/
├── main/kotlin/                 # kode bersama
├── debug/kotlin/                # kode khusus buildType debug
├── release/kotlin/              # kode khusus buildType release
├── free/kotlin/                  # kode khusus flavor free
├── pro/kotlin/                   # kode khusus flavor pro
└── freeDebug/kotlin/             # kode khusus variant freeDebug

Kode di src/free/kotlin akan dikompilasi untuk freeDebug dan freeRelease. Kode di src/freeDebug/kotlin hanya dikompilasi untuk variant freeDebug.

Android Gradle Plugin #

Pada Android Gradle Plugin, build variants dideklarasikan di build.gradle.kts:

kotlin build.gradle.kts (Android) — build variants
android {
    buildTypes {
        getByName("debug") {
            applicationIdSuffix = ".debug"
            versionNameSuffix = "-debug"
            isDebuggable = true
        }
        getByName("release") {
            isMinifyEnabled = true
            isShrinkResources = true
            proguardFiles(
                getDefaultProguardFile("proguard-android-optimize.txt"),
                "proguard-rules.pro"
            )
        }
    }

    flavorDimensions += listOf("tier")
    productFlavors {
        create("free") { dimension = "tier" }
        create("pro") { dimension = "tier" }
    }
}

Mari kita bedah contoh di atas:

  1. applicationIdSuffix = ".debug": ID aplikasi menjadi com.example.app.debug untuk debug. Memungkinkan dua build (debug + release) terpasang bersamaan di satu ponsel.
  2. isMinifyEnabled = true pada release: mengaktifkan R8/ProGuard untuk mengoptimasi dan mengobfuscate kode.
  3. flavorDimensions: dimensi yang dipakai. Bila ada beberapa dimensi (mis. tier + env), seluruh dimensi harus dideklarasikan di sini.
  4. productFlavors: deklarasi nilai per dimensi. Bila ada dimensi yang tidak diisi, Gradle memunculkan galat.

Demonstrasi Build Variants #

Kode berikut mencantumkan konsep dan source set per variant.

kotlin
fun main() {
    println("=== Build Variants ===")
    println()
    println("Build variants memungkinkan satu proyek menghasilkan multiple build")
    println("dengan konfigurasi berbeda (debug vs release, free vs pro).")
    println()
    println("Konsep kunci:")
    println("  buildType   : debug / release (umumnya 2)")
    println("  flavor      : dimensinya (mis. tier = free/pro, env = dev/staging/prod)")
    println("  variant     = buildType x flavor (mis. freeDebug, proRelease)")
    println()
    println("Source set per variant:")
    println("  src/main/kotlin               - kode bersama")
    println("  src/debug/kotlin              - kode khusus debug")
    println("  src/release/kotlin            - kode khusus release")
    println("  src/free/kotlin               - kode khusus flavor free")
    println("  src/freeDebug/kotlin          - kode khusus variant freeDebug")
    println()
    println("Pengaturan Android (Android Gradle Plugin):")
    println("  buildTypes { debug { applicationIdSuffix = \".debug\" } }")
    println("  productFlavors { free {} pro {} }")
    println()
    println("Pengaturan Kotlin/JVM (tanpa AGP): pakai sourceSets + custom configurations")
}

Output yang Dihasilkan:

text
=== Build Variants ===

Build variants memungkinkan satu proyek menghasilkan multiple build
dengan konfigurasi berbeda (debug vs release, free vs pro).

Konsep kunci:
  buildType   : debug / release (umumnya 2)
  flavor      : dimensinya (mis. tier = free/pro, env = dev/staging/prod)
  variant     = buildType x flavor (mis. freeDebug, proRelease)

Source set per variant:
  src/main/kotlin               - kode bersama
  src/debug/kotlin              - kode khusus debug
  src/release/kotlin            - kode khusus release
  src/free/kotlin               - kode khusus flavor free
  src/freeDebug/kotlin          - kode khusus variant freeDebug

Pengaturan Android (Android Gradle Plugin):
  buildTypes { debug { applicationIdSuffix = ".debug" } }
  productFlavors { free {} pro {} }

Pengaturan Kotlin/JVM (tanpa AGP): pakai sourceSets + custom configurations

Mari kita telaah kode di atas:

  1. Tiga konsep kunci: buildType, flavor, dan variant. Variant adalah produk akhir yang sebenarnya dibangun. Tanpa flavor, hanya ada debug dan release.
  2. Hierarki source set: kode di src/freeDebug/kotlin meng-override kode di src/free/kotlin yang meng-override src/main/kotlin. Gradle menggabungkan source set dengan urutan prioritas ini.
  3. Konfigurasi AGP: blok android { buildTypes { ... }; productFlavors { ... } } adalah sintaksis Android Gradle Plugin. Untuk proyek Kotlin/JVM murni tanpa AGP, pendekatan berbeda dipakai.

Build Variants untuk Kotlin/JVM #

Tanpa AGP, proyek Kotlin/JVM murni tidak punya blok buildTypes dan productFlavors bawaan. Namun, kita dapat mensimulasikan via custom source sets dan configurations:

kotlin build.gradle.kts — JVM custom variants
sourceSets {
    create("dev") {
        kotlin.srcDir("src/dev/kotlin")
    }
    create("prod") {
        kotlin.srcDir("src/prod/kotlin")
    }
}

val devImplementation: Configuration by configurations.creating
val prodImplementation: Configuration by configurations.creating

tasks.register<Jar>("devJar") {
    from(sourceSets["dev"].output)
    archiveClassifier.set("dev")
}

tasks.register<Jar>("prodJar") {
    from(sourceSets["prod"].output)
    archiveClassifier.set("prod")
}

Pendekatan ini lebih manual tetapi memberi kontrol penuh. Untuk sebagian besar proyek server Kotlin/JVM, variant terbatas pada environment (dev/prod) yang dikelola via file konfigurasi eksternal daripada build variants.

Pengaturan umum per Variant #

Beberapa hal yang biasanya berbeda per variant:

AspekDebugRelease
isDebuggabletruefalse
isMinifyEnabled (R8)falsetrue
SigningDebug key (auto)Release key (rahasia)
Version name suffix-debug(tidak ada)
Application IDcom.example.app.debugcom.example.app
Logging levelDEBUG/TRACEWARN/ERROR

Referensi Lanjutan #

  • Gradle User Guide — buku panduan Gradle untuk build variants, source set, dan build type
  • Gradle — rujukan Kotlin + Gradle untuk konfigurasi varian build pada proyek Kotlin/JVM

Penutup #

Build variants adalah mekanisme yang matang di ekosistem Gradle, terutama untuk Android. Pemahaman source set per variant dan applicationIdSuffix menjadi bekal untuk mengelola aplikasi dengan multiple build. Pada chapter berikutnya kita akan membahas reproducible builds, konsep bahwa input yang sama harus menghasilkan output identik dari build ke build.