Setelah kode KMP ditulis dan diuji, langkah akhir adalah membangun artifact dan mendistribusikannya. KMP menghasilkan banyak artifact dari satu source—JAR untuk JVM, AAR untuk Android, Framework untuk iOS, KLIB untuk Native, file JS untuk JavaScript, dan artifact metadata untuk resolusi konsumen. Setiap artifact memiliki pipeline build, distribusi, dan signature yang berbeda. Pada chapter ini kita akan melihat pipeline build untuk delapan target, struktur artifact yang dihasilkan, dan strategi distribusi ke berbagai repository.

Pipeline Build per Target #

Build KMP dimulai dengan langkah umum—lint, compile commonMain menjadi metadata, lalu compile per-target source set. Setelah itu, langkah spesifik per target dijalankan: JVM memaket JAR, Android memaket AAR, iOS membangun XCFramework, JS menjalankan webpack.

kotlin build.gradle.kts: task yang dipanggil per target
// JVM: ./gradlew :lib:assembleJvmJar
// Android: ./gradlew :lib:assembleReleaseAar
// iOS: ./gradlew :lib:assembleIosArm64XCFramework
// JS: ./gradlew :lib:jsBrowserProductionWebpack

Untuk publikasi lengkap, satu perintah ./gradlew publish memicu kompilasi semua target sekaligus.

Artifact per Target #

Setiap target menghasilkan artifact dengan format berbeda. JVM menghasilkan JAR yang berisi bytecode; konsumen JVM menambahkannya ke classpath. Android menghasilkan AAR yang berisi bytecode plus resource Android; konsumen Android menambahkannya ke dependencies. iOS menghasilkan framework yang berisi native binary; konsumen iOS menambahkannya via CocoaPods atau SPM. JS menghasilkan file JS atau NPM package.

TargetArtifactDistribusi Umum
JVMlibrary.jarMaven Central
Androidlibrary.aarMaven Central, Google Maven
iOS (Arm64/X64/Sim)library.frameworkCocoaPods, Maven Central
JSlibrary.js atau npm packagenpm registry
Native (Linux/Mac/Win)library.klibMaven Central
Metadatalibrary-metadata.jarMaven Central

Simulasi Build Pipeline #

Demo berikut mensimulasikan pipeline build lengkap untuk delapan target—JVM, Android, tiga target iOS, JS, Linux x64, dan metadata. Setiap target memiliki langkah build, artifact, dan distribusi sendiri.

kotlin
// multiplatform_deployment.kt
// Distribusi artifact KMP per target: iOS framework, Android AAR, JVM JAR.
// Demo: simulasi build pipeline + ukuran artifact + distribusi.

data class ArtifactOutput(
    val target: String,
    val artifactType: String,
    val filename: String,
    val ukuranKB: Int,
    val distribusi: String,
    val langkahBuild: List<String>
)

data class HasilBuild(
    val versi: String,
    val artifacts: List<ArtifactOutput>
) {
    fun totalUkuranKB(): Int = artifacts.sumOf { it.ukuranKB }
    fun ringkasan(): String {
        val sb = StringBuilder()
        sb.appendLine("Versi       : $versi")
        sb.appendLine("Total target: ${artifacts.size}")
        sb.appendLine("Total ukuran: ${totalUkuranKB()} KB")
        sb.appendLine()
        sb.appendLine("Artifacts:")
        for (a in artifacts) {
            sb.appendLine("  ${a.target.padEnd(20)} ${a.artifactType.padEnd(10)} ${a.filename.padEnd(40)} ${a.ukuranKB.toString().padStart(5)} KB")
        }
        return sb.toString().trimEnd()
    }
}

class BuildPipeline(private val versi: String) {
    private val langkahUmum = listOf(
        "1. Validasi source code (lint)",
        "2. Compile commonMain -> metadata",
        "3. Compile per-target source set",
        "4. Run commonTest di semua target",
        "5. Sign artifact (jika dipublikasi)"
    )

    fun jalankan(targetDefinisi: List<Triple<String, String, Int>>): HasilBuild {
        println("=== Build Pipeline versi $versi ===")
        println()
        println("Langkah umum:")
        langkahUmum.forEach { println("  $it") }
        println()

        val artifacts = mutableListOf<ArtifactOutput>()
        for ((target, tipe, ukuran) in targetDefinisi) {
            println("Build target: $target")
            val langkah = langkahBuild(target, tipe)
            langkah.forEach { println("  - $it") }
            val artifact = ArtifactOutput(
                target = target,
                artifactType = tipe,
                filename = namaFile(target, tipe),
                ukuranKB = ukuran,
                distribusi = distribusi(target),
                langkahBuild = langkah
            )
            artifacts.add(artifact)
            println("  Output: ${artifact.filename} (${artifact.ukuranKB} KB)")
            println("  Distribusi: ${artifact.distribusi}")
            println()
        }

        return HasilBuild(versi, artifacts)
    }

    private fun langkahBuild(target: String, tipe: String): List<String> = when (target) {
        "jvm" -> listOf(
            "compileKotlinJvm",
            "packageJvmJar",
            "shadowJarRuntime (jika diperlukan)"
        )
        "android" -> listOf(
            "compileReleaseKotlinAndroid",
            "bundleReleaseAar",
            "signReleaseAar"
        )
        "iosArm64", "iosX64", "iosSimulatorArm64" -> listOf(
            "compileKotlin${target.replaceFirstChar { it.uppercase() }}",
            "linkDebugFramework${target.replaceFirstChar { it.uppercase() }}",
            "assemble${target.replaceFirstChar { it.uppercase() }}XCFramework"
        )
        "js" -> listOf(
            "compileKotlinJs",
            "browserProductionWebpack (atau esbuild)",
            "bundleJs"
        )
        else -> listOf("compileKotlin${target.replaceFirstChar { it.uppercase() }}")
    }

    private fun namaFile(target: String, tipe: String): String {
        val base = "kmp-util-$versi"
        return when (tipe) {
            "JAR" -> "$base.jar"
            "AAR" -> "$base.aar"
            "Framework" -> "$base-${target}.framework"
            "KLIB" -> "$base-${target}.klib"
            "JS" -> "$base.js"
            "Metadata" -> "$base-metadata.jar"
            else -> "$base-${target}.bin"
        }
    }

    private fun distribusi(target: String): String = when (target) {
        "jvm" -> "Maven Central + GitHub Releases"
        "android" -> "Maven Central + Google Maven"
        "iosArm64", "iosX64", "iosSimulatorArm64" -> "CocoaPods + Maven Central"
        "js" -> "npm registry + GitHub Releases"
        else -> "Maven Central"
    }
}

fun main() {
    val pipeline = BuildPipeline("1.0.0")

    val targetDefinisi = listOf(
        Triple("jvm",               "JAR",       85),
        Triple("android",          "AAR",       120),
        Triple("iosArm64",         "Framework", 95),
        Triple("iosX64",           "Framework", 95),
        Triple("iosSimulatorArm64", "Framework", 95),
        Triple("js",               "JS",        45),
        Triple("linuxX64",         "KLIB",      30),
        Triple("metadata",         "Metadata",  20)
    )

    val hasil = pipeline.jalankan(targetDefinisi)

    println("=== Hasil Build ===")
    println(hasil.ringkasan())
    println()
    println("=== Distribusi per artifact ===")
    for (a in hasil.artifacts) {
        println("${a.target.padEnd(22)} -> ${a.distribusi}")
    }

    println()
    println("=== Verifikasi artifact siap rilis ===")
    val semuanyaSiap = hasil.artifacts.all { it.ukuranKB > 0 }
    println("Semua artifact punya ukuran > 0: $semuanyaSiap")
    println("Total artifact yang dibangun  : ${hasil.artifacts.size}")
    println("Versi rilis                   : ${hasil.versi}")

    println()
    println("=== Ringkasan distribusi ===")
    println("Maven Central : ${hasil.artifacts.count { "Maven" in it.distribusi }} artifact")
    println("CocoaPods     : ${hasil.artifacts.count { "CocoaPods" in it.distribusi }} artifact")
    println("npm registry  : ${hasil.artifacts.count { "npm" in it.distribusi }} artifact")
    println("GitHub Releases: ${hasil.artifacts.count { "GitHub" in it.distribusi }} artifact")
}

Output yang Dihasilkan:

text
=== Build Pipeline versi 1.0.0 ===

Langkah umum:
  1. Validasi source code (lint)
  2. Compile commonMain -> metadata
  3. Compile per-target source set
  4. Run commonTest di semua target
  5. Sign artifact (jika dipublikasi)

Build target: jvm
  - compileKotlinJvm
  - packageJvmJar
  - shadowJarRuntime (jika diperlukan)
  Output: kmp-util-1.0.0.jar (85 KB)
  Distribusi: Maven Central + GitHub Releases

Build target: android
  - compileReleaseKotlinAndroid
  - bundleReleaseAar
  - signReleaseAar
  Output: kmp-util-1.0.0.aar (120 KB)
  Distribusi: Maven Central + Google Maven

Build target: iosArm64
  - compileKotlinIosArm64
  - linkDebugFrameworkIosArm64
  - assembleIosArm64XCFramework
  Output: kmp-util-1.0.0-iosArm64.framework (95 KB)
  Distribusi: CocoaPods + Maven Central

Build target: iosX64
  - compileKotlinIosX64
  - linkDebugFrameworkIosX64
  - assembleIosX64XCFramework
  Output: kmp-util-1.0.0-iosX64.framework (95 KB)
  Distribusi: CocoaPods + Maven Central

Build target: iosSimulatorArm64
  - compileKotlinIosSimulatorArm64
  - linkDebugFrameworkIosSimulatorArm64
  - assembleIosSimulatorArm64XCFramework
  Output: kmp-util-1.0.0-iosSimulatorArm64.framework (95 KB)
  Distribusi: CocoaPods + Maven Central

Build target: js
  - compileKotlinJs
  - browserProductionWebpack (atau esbuild)
  - bundleJs
  Output: kmp-util-1.0.0.js (45 KB)
  Distribusi: npm registry + GitHub Releases

Build target: linuxX64
  - compileKotlinLinuxX64
  Output: kmp-util-1.0.0-linuxX64.klib (30 KB)
  Distribusi: Maven Central

Build target: metadata
  - compileKotlinMetadata
  Output: kmp-util-1.0.0-metadata.jar (20 KB)
  Distribusi: Maven Central

=== Hasil Build ===
Versi       : 1.0.0
Total target: 8
Total ukuran: 585 KB

Artifacts:
  jvm                  JAR        kmp-util-1.0.0.jar                          85 KB
  android              AAR        kmp-util-1.0.0.aar                         120 KB
  iosArm64             Framework  kmp-util-1.0.0-iosArm64.framework           95 KB
  iosX64               Framework  kmp-util-1.0.0-iosX64.framework             95 KB
  iosSimulatorArm64    Framework  kmp-util-1.0.0-iosSimulatorArm64.framework    95 KB
  js                   JS         kmp-util-1.0.0.js                           45 KB
  linuxX64             KLIB       kmp-util-1.0.0-linuxX64.klib                30 KB
  metadata             Metadata   kmp-util-1.0.0-metadata.jar                 20 KB

=== Distribusi per artifact ===
jvm                    -> Maven Central + GitHub Releases
android                -> Maven Central + Google Maven
iosArm64               -> CocoaPods + Maven Central
iosX64                 -> CocoaPods + Maven Central
iosSimulatorArm64      -> CocoaPods + Maven Central
js                     -> npm registry + GitHub Releases
linuxX64               -> Maven Central
metadata               -> Maven Central

=== Verifikasi artifact siap rilis ===
Semua artifact punya ukuran > 0: true
Total artifact yang dibangun  : 8
Versi rilis                   : 1.0.0

=== Ringkasan distribusi ===
Maven Central : 7 artifact
CocoaPods     : 3 artifact
npm registry  : 1 artifact
GitHub Releases: 2 artifact

Mari kita bedah bagian kunci dari kode di atas:

  1. data class ArtifactOutput: model untuk satu artifact hasil build—target, jenis artifact (JAR/AAR/Framework/KLIB/JS/Metadata), nama file, ukuran, distribusi, dan daftar langkah build yang dijalankan.
  2. data class HasilBuild: agregat hasil build untuk satu versi. Menyimpan daftar artifact dan menyediakan ringkasan() untuk laporan. Method totalUkuranKB() menghitung total ukuran semua artifact.
  3. class BuildPipeline: kelas yang mengelola pipeline build. Method jalankan(targetDefinisi) menerima daftar target dengan ukuran, lalu menjalankan langkah umum + langkah spesifik per target.
  4. langkahBuild(target, tipe): mengembalikan daftar Gradle task per target. Untuk JVM: compileKotlinJvm, packageJvmJar, shadowJarRuntime. Untuk iOS: compileKotlinIosArm64, linkDebugFrameworkIosArm64, assembleIosArm64XCFramework. Tiga task iOS inilah yang dipakai Gradle untuk membangun framework.
  5. namaFile(target, tipe): menghasilkan nama file artifact berdasarkan konvensi Maven. JVM menghasilkan kmp-util-1.0.0.jar, iOS menghasilkan kmp-util-1.0.0-iosArm64.framework, metadata menghasilkan kmp-util-1.0.0-metadata.jar.
  6. distribusi(target): menentukan repository distribusi per target. JVM dan Android ke Maven Central; iOS ke CocoaPods + Maven Central; JS ke npm registry; Native ke Maven Central.
  7. Output menunjukkan 8 artifact: total ukuran 585 KB. Maven Central menerima 7 artifact (semua kecuali JS), CocoaPods menerima 3 artifact (iOS), npm menerima 1 (JS).

Strategi Distribusi #

Distribusi artifact KMP mengikuti pola ekosistem target. Maven Central adalah repository umum untuk JVM, Android, Native, dan metadata. CocoaPods atau Swift Package Manager adalah repository native untuk iOS. npm registry adalah repository untuk JavaScript.

bash Distribusi ke Maven Central
./gradlew publishAllPublicationsToSonatypeRepository
# Setelah staging disetujui:
./gradlew closeAndReleaseSonatypeStagingRepository
bash Distribusi ke CocoaPods (iOS)
# Hasilkan podspec dari build.gradle.kts
./gradlew :lib:podPublish
# atau manual publikasi CocoaPods
pod trunk push KmpUtil.podspec
bash Distribusi ke npm registry (JS)
./gradlew :lib:jsBrowserProductionWebpack
npm publish build/js/packages/kmp-util

CI/CD untuk Multi-target #

Pipeline CI/CD untuk KMP biasanya membangun semua target di setiap commit. GitHub Actions contohnya: lint → test commonTest di JVM → build artifact untuk semua target → upload artifact → publish ke Maven Central saat tag. Build iOS framework memerlukan runner macOS—Linux runner tidak dapat membangun iOS framework.

yaml .github/workflows/release.yml (ringkas)
jobs:
  build-jvm:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-java@v4
        with: { java-version: 21 }
      - run: ./gradlew :lib:assembleJvmJar
  build-ios:
    runs-on: macos-latest
    steps:
      - uses: actions/checkout@v4
      - run: ./gradlew :lib:assembleIosArm64XCFramework

Referensi Lanjutan #

  • Publish libraries — dokumentasi publikasi dan distribusi artefak KMP ke target masing-masing

Penutup #

Deployment KMP melibatkan banyak artifact dan repository. Pipeline build mengikuti pola KMP—langkah umum dulu, lalu langkah spesifik per target. Distribusi ke Maven Central, CocoaPods, dan npm registry memungkinkan konsumen di setiap platform menambahkan library dengan satu baris dependency. Dengan ini, kita menutup section KMP dengan pemahaman lengkap dari konsep hingga distribusi.