Build sbt

Struktur build.sbt, modul, dan resolusi dependensi pada alat build utama ekosistem Scala.

Daftar isi

sbt adalah alat build utama ekosistem Scala yang mengompilasi kode, mengelola dependensi, menjalankan pengujian, dan mengemas hasil dalam satu proses build. Topik ini membahas anatomi build.sbt, struktur direktori proyek, notasi dependensi, pembagian proyek menjadi beberapa modul, serta tugas utama seperti compile, test, dan run. Bagian akhir menempatkan sbt berdampingan dengan Scala CLI agar pemilihan alatnya tepat. Setelah topik ini diikuti, berkas build Scala yang lazim dijumpai pada proyek nyata dapat dibaca dan disusun.

Struktur direktori proyek sbt#

sbt mengenali sebuah folder sebagai proyek apabila folder itu memuat berkas build.sbt pada akarnya. Kode produksi ditempatkan pada src/main/scala, kode pengujian pada src/test/scala, dan konfigurasi pendukung build pada folder project. Susunan ini membuat pemisahan kode produksi, kode pengujian, dan konfigurasi terjadi otomatis tanpa daftar berkas manual.

text Struktur direktori proyek sbt
kalkulator-suhu/
├── build.sbt
├── project/
│   └── build.properties
└── src/
    ├── main/
    │   └── scala/
    │       └── KonversiSuhu.scala
    └── test/
        └── scala/
            └── UjiKonversiSuhu.scala

Definisi build paling sederhana memuat identitas proyek, versi Scala, dan daftar dependensi. Berkas berikut adalah build.sbt utuh untuk proyek pada struktur di atas.

scala build.sbt
lazy val root = (project in file("."))
  .settings(
    name := "kalkulator-suhu",
    version := "0.1.0",
    scalaVersion := "3.9.0",
    libraryDependencies ++= Seq(
      "io.circe" %% "circe-generic" % "0.14.13",
      "org.scalameta" %% "munit" % "1.1.1" % Test
    )
  )

Setiap baris di dalam settings berbentuk kunci := nilai. Kunci name menyatakan nama proyek, version menyatakan versi artefak hasil build, dan scalaVersion menentukan kompiler Scala yang digunakan. Kunci libraryDependencies menampung daftar dependensi yang dijelaskan pada bagian berikutnya. Versi sbt sendiri tidak diatur pada build.sbt, melainkan pada berkas project/build.properties.

properties project/build.properties
sbt.version=1.13.1

Notasi dependensi dan resolusinya#

Dependensi dinyatakan pada kunci libraryDependencies dalam bentuk grup, artefak, dan versi. Operator %% di antara grup dan artefak berlaku untuk pustaka Scala yang diterbitkan silang, yaitu diterbitkan dalam beberapa varian per versi Scala. Operator itu menambahkan akhiran versi Scala secara otomatis, sehingga "io.circe" %% "circe-generic" % "0.14.13" diselesaikan menjadi artefak circe-generic_3 karena scalaVersion proyek adalah 3.9.0. Artefak Java murni tidak memiliki varian semacam itu dan ditulis dengan satu % saja.

scala Contoh: ragam entri dependensi
libraryDependencies ++= Seq(
  "io.circe" %% "circe-generic" % "0.14.13",
  "org.slf4j" % "slf4j-nop" % "2.0.17",
  "org.scalameta" %% "munit" % "1.1.1" % Test
)

Konfigurasi % Test pada baris terakhir membuat munit hanya tersedia saat kompilasi dan eksekusi kode pengujian, tidak disertakan dalam artefak produksi. Operator ++= menambahkan seluruh isi daftar sekaligus. Sejak sbt 1.3, resolusi dependensi dikerjakan oleh Coursier, yaitu pustaka penyelesai dependensi yang mengunduh artefak dari Maven Central secara paralel dan menyimpannya pada cache bersama antarproyek. sbt juga menyelesaikan dependensi transitif secara otomatis, sehingga satu entri dapat menarik beberapa artefak pendampingnya.

Proyek multi-modul#

Build yang besar biasanya dipecah menjadi beberapa modul, yaitu subproyek dengan kode dan konfigurasi sendiri di dalam satu build. Pada build.sbt, setiap modul dinyatakan sebagai nilai project beserta pengaturannya, dan folder modulnya dinamai sesuai nilai tersebut.

scala Contoh: build dengan dua modul
lazy val inti = project
  .settings(
    name := "kalkulator-inti"
  )

lazy val aplikasi = project
  .dependsOn(inti)
  .settings(
    name := "kalkulator-aplikasi"
  )

lazy val root = (project in file("."))
  .aggregate(inti, aplikasi)

Definisi di atas membuat modul inti dan aplikasi pada folder inti/ dan aplikasi/. Panggilan dependsOn menyatakan bahwa aplikasi menggunakan kelas dari inti, sehingga mengompilasi aplikasi otomatis mengompilasi inti lebih dahulu. Panggilan aggregate membuat tugas yang dijalankan pada root berlaku juga untuk kedua modulnya, misalnya satu perintah test memeriksa keduanya. Pola ini memisahkan kode yang digunakan ulang dari kode yang mengonsumsinya tanpa memisahkan repositori.

Tugas compile, test, dan run#

sbt dijalankan sebagai shell build, yaitu sesi interaktif yang menerima nama tugas. Mengetik sbt pada akar proyek membuka shell tersebut, kemudian tugas dipanggil dengan namanya.

bash
# tidak-dijalankan
sbt compile

Tugas compile mengompilasi kode produksi, test menjalankan pengujian, dan run mengompilasi lalu mengeksekusi kelas utama. Tanda ~ di depan nama tugas mengaktifkan eksekusi ulang otomatis setiap berkas sumber berubah, misalnya ~compile. Perintah console membuka REPL Scala yang classpath-nya sudah memuat dependensi proyek. Semua tugas juga dapat dipanggil dalam satu proses tanpa shell, misalnya sbt clean compile, dan bentuk inilah yang lazim digunakan pada skrip build otomatis.

Posisi sbt dibanding Scala CLI#

Scala CLI mencakup kebutuhan belajar dan proyek kecil karena konfigurasinya melekat pada berkas sumber dan setiap perintah berjalan langsung dari terminal. sbt menjadi standar proyek besar karena mendukung build multi-modul, ekosistem plugin, publikasi artefak, dan integrasi dengan alat build otomatis. Keduanya mengunduh artefak melalui Coursier dan menargetkan kompiler Scala yang sama, sehingga pemahaman dependensi pada satu alat berlaku juga pada alat lainnya.

AspekScala CLIsbt
KonfigurasiDirektif pada berkas sumberbuild.sbt dan folder project
Proyek multi-modulSatu direktori proyek tunggalDukungan penuh dengan dependsOn dan aggregate
Cara menjalankanPerintah tunggal dari terminalShell build interaktif atau mode satu proses
PerluasanOpsi dan direktif bawaanEkosistem plugin yang luas

Proyek belajar dan contoh berbentuk program kecil cukup dikelola dengan Scala CLI. Pindah ke sbt saat proyek membutuhkan pembagian modul, pengujian terjadwal pada build otomatis, atau konvensi yang dipahami seluruh ekosistem Scala.

Kesalahan umum#

Menggunakan %% untuk artefak Java murni. Operator itu menambahkan akhiran versi Scala pada nama artefak, sehingga pencarian artefak gagal dengan error dependensi tidak ditemukan. Perbaikannya adalah menulis satu % untuk artefak Java.

Tidak mengunci versi sbt pada project/build.properties. Peluncur kemudian menggunakan versi bawaan yang mungkin berbeda dari versi yang digunakan saat menulis build, sehingga perubahan perilaku antarversi dapat memicu error build yang membingungkan. Cantumkan sbt.version sejak proyek pertama.

Meletakkan kode sumber di luar src/main/scala. sbt hanya mengompilasi folder sumber yang dideklarasikannya, sehingga berkas yang berserakan di akar proyek tidak ikut terkompilasi dan kelas utamanya tidak ditemukan saat tugas run dipanggil. Ikuti struktur src/main/scala dan src/test/scala sejak awal.

Ringkasan#

  • Proyek sbt diakui dari build.sbt pada akar folder, dengan kode produksi pada src/main/scala dan pengujian pada src/test/scala.
  • Kunci name, version, dan scalaVersion menyusun identitas proyek, sedangkan libraryDependencies beserta operator %% dan % menyatakan dependensinya.
  • Versi sbt dikunci pada project/build.properties, dan build besar dipecah menjadi modul melalui dependsOn dan aggregate.
  • Tugas compile, test, dan run dipanggil dari shell sbt atau dari mode satu proses.

Referensi#