Proyek Cabal

Berkas .cabal, dependensi, executable dan library.

Daftar isi

Program yang terdiri atas banyak modul memerlukan tempat yang menyatakan berkas mana saja yang dibangun dan bagaimana pembagiannya, serta pustaka apa yang dibutuhkan. Cabal menampung semua itu dalam satu berkas .cabal yang menjadi pusat proyek, dan menyediakan dua perintah utama, yaitu cabal build untuk membangun dan cabal run untuk menjalankan. Topik ini membahas anatomi berkas .cabal, pembagian pustaka dan executable, keluaran build, serta cara menyatakan dependensi.

Anatomi berkas .cabal#

Berkas .cabal dinamai mengikuti nama proyeknya dan berisi properti umum di bagian atas, lalu satu blok untuk setiap komponen yang dibangun. Proyek contoh pada topik ini bernama penghitung-kata, dan berkasnya terbagi menjadi satu pustaka serta satu executable.

cabal penghitung-kata.cabal
cabal-version:      2.4
name:               penghitung-kata
version:            0.1.0.0
synopsis:           Penghitung kata sederhana
description:        Menghitung jumlah kata pada teks masukan.
build-type:         Simple

library
    exposed-modules:  Penghitung
    hs-source-dirs:   src
    build-depends:    base
    default-language: Haskell2010

executable penghitung-kata
    main-is:          Main.hs
    hs-source-dirs:   app
    build-depends:    base, penghitung-kata
    default-language: Haskell2010

Bagian atas menyatakan versi format cabal-version, nama, dan keterangan proyek. Blok library mengekspor modul Penghitung yang dicari pada direktori src, sedangkan blok executable menyatakan berkas main-is pada direktori app. Baris build-depends pada executable memuat dua nama, yaitu base yang selalu diperlukan dan penghitung-kata yang merujuk pustaka proyek yang sama, sehingga executable berhak mengimpor modul-modul pustakanya. Struktur direktori proyek mengikuti penentuan itu, yaitu src/Penghitung.hs untuk pustaka dan app/Main.hs untuk program utamanya.

Pustaka dan program utama#

Pustaka proyek menampung logika yang ingin diuji dan digunakan ulang, sedangkan executable hanya berisi pengikat main yang memanggil pustakanya. Kedua berkas berikut menunjukkan pembagian itu.

haskell src/Penghitung.hs
-- tidak-dijalankan
-- Modul penghitung kata dari pustaka proyek
module Penghitung (banyakKata, kataPanjang) where

banyakKata :: String -> Int
banyakKata = length . words

kataPanjang :: String -> Int
kataPanjang = length . filter panjang . words
  where
    panjang w = length w > 5

Modul Penghitung tidak berisi main, sehingga modul tersebut bukan program yang dijalankan melainkan satuan pustaka yang diimpor. Eksekusi berada pada executable berikut.

haskell app/Main.hs
-- tidak-dijalankan
-- Program utama yang memakai pustaka proyek
module Main (main) where

import Penghitung (banyakKata, kataPanjang)

main :: IO ()
main = do
  let teks = "Haskell memakai modul untuk memecah program"
  putStrLn (teks ++ ": " ++ show (banyakKata teks) ++ " kata")
  putStrLn ("kata panjang: " ++ show (kataPanjang teks))

Pemisahan ini bukan sekadar kebiasaan, melainkan konsekuensi dari struktur komponen, karena import Penghitung pada executable sah hanya karena build-depends executable memuat nama pustakanya. Program uji, benchmark, dan executable kedua kemudian menambah bloknya sendiri pada berkas .cabal yang sama, semuanya berbagi pustaka yang identik.

Membangun dan menjalankan#

Dua perintah berikut dieksekusi dari direktori proyek, dan keduanya dijalankan berurutan karena cabal run pada eksekusi pertama ikut membangun dahulu apabila belum ada hasil build-nya.

bash
# bangun seluruh komponen proyek dari direktori proyek
cabal build
text Keluaran
Resolving dependencies...
Build profile: -w ghc-9.6.6 -O1
In order, the following will be built (use -v for more details):
 - penghitung-kata-0.1.0.0 (lib) (first run)
 - penghitung-kata-0.1.0.0 (exe:penghitung-kata) (first run)
Configuring library for penghitung-kata-0.1.0.0...
Preprocessing library for penghitung-kata-0.1.0.0...
Building library for penghitung-kata-0.1.0.0...
[1 of 1] Compiling Penghitung       ( src/Penghitung.hs, dist-newstyle/build/x86_64-linux/ghc-9.6.6/penghitung-kata-0.1.0.0/build/Penghitung.o, dist-newstyle/build/x86_64-linux/ghc-9.6.6/penghitung-kata-0.1.0.0/build/Penghitung.dyn_o )
Configuring executable 'penghitung-kata' for penghitung-kata-0.1.0.0...
Preprocessing executable 'penghitung-kata' for penghitung-kata-0.1.0.0...
Building executable 'penghitung-kata' for penghitung-kata-0.1.0.0...
[1 of 1] Compiling Main             ( app/Main.hs, dist-newstyle/build/x86_64-linux/ghc-9.6.6/penghitung-kata-0.1.0.0/x/penghitung-kata/build/penghitung-kata/penghitung-kata-tmp/Main.o )
[2 of 2] Linking dist-newstyle/build/x86_64-linux/ghc-9.6.6/penghitung-kata-0.1.0.0/x/penghitung-kata/build/penghitung-kata/penghitung-kata

Keluaran memperlihatkan urutan kerja Cabal, yaitu memecah dependensi, menyusun rencana berupa satu pustaka dan satu executable, lalu membangun keduanya berurutan. Hasil build disimpan pada direktori dist-newstyle yang seluruh isinya adalah artefak yang dapat dibangun ulang kapan pun, sehingga direktori itu tidak ikut disimpan pada repositori. Baris yang tercetak mengikuti versi GHC dan Cabal yang terpasang, sehingga nomor versi pada keluaran berbeda antarmesin, sedangkan bentuk tahapannya sama.

bash
# jalankan executable proyek
cabal run penghitung-kata
text Keluaran
Haskell memakai modul untuk memecah program: 6 kata
kata panjang: 4

Perintah cabal run mengeksekusi biner hasil build tanpa perlu mencari lokasinya di dalam dist-newstyle, dan apabila ada berkas sumber yang berubah sejak build terakhir, perintah itu membangun ulang bagian yang perlu sebelum program berjalan. Apabila tidak ada perubahan, pemanggilan berikutnya langsung menjalankan program tanpa mencetak tahapan build.

Perintah ketiga yang sering digunakan pada masa pengembangan adalah cabal repl, yaitu perintah untuk memasuki sesi GHCi yang memuat modul pustaka proyek sehingga fungsinya dapat dicoba satu per satu tanpa menjalankan program utuhnya. Perintah cabal test dan cabal benchmark menyusul pola yang sama, yaitu masing-masing membangun komponen pengujian dan tolok ukur yang dideklarasikan pada berkas .cabal sebelum mengeksekusinya. Seluruh perintah itu berbagi hasil build pada dist-newstyle, sehingga perpindahan di antara perintah-perintah tersebut tidak membangun ulang apa pun yang sudah selesai, dan alur kerjanya tetap sama dari proyek kecil sampai proyek yang memiliki banyak komponen.

Menyatakan dependensi#

Pustaka di luar base dinyatakan pada build-depends dengan nama paketnya, misalnya text untuk tipe teks yang efisien atau containers untuk peta dan himpunan, sehingga barisnya berbentuk build-depends: base, text, containers. Versi yang diizinkan dapat diperketat dengan batas rentang seperti base >= 4.17 && < 5, kebiasaan yang mencegah proyek dibangun pada versi yang belum diuji. Sebelum build pertama pada mesin yang baru, indeks paket diperbarui melalui cabal update agar daftar paket yang tersedia terkini, dan setelah itu build yang memerlukan pustaka baru mengunduh serta membangunnya secara otomatis.

Kesalahan umum#

Kesalahan pertama adalah menggunakan modul pustaka proyek dari executable tanpa mendaftarkan pustakanya pada build-depends executable. Compiler melaporkan bahwa modul itu tidak ditemukan meskipun berkasnya ada, karena izin impor mengikuti daftar dependensi, bukan isi direktori. Perbaikannya berupa menambahkan nama pustaka pada baris build-depends blok yang membutuhkannya.

Kesalahan kedua berupa menempatkan berkas sumber di luar hs-source-dirs yang dinyatakan, misalnya menyimpan Main.hs di akar proyek sementara bloknya menunjuk app. Gejala yang muncul tetap berupa modul yang tidak ditemukan, sehingga pencocokan antara lokasi berkas dan deklarasi blok menjadi pemeriksaan pertama ketika build gagal. Kesalahan ketiga berupa mengubah cabal-version ke nilai yang lebih tinggi daripada Cabal yang terpasang, sehingga berkas proyek ditolak sebelum build dimulai, dan perbaikannya berupa menurunkan nilai itu atau memperbarui Cabal.

Ringkasan#

  • Berkas .cabal menyatakan properti proyek dan satu blok untuk setiap komponen, yaitu library dan executable.
  • build-depends mengatur izin impor, termasuk pustaka proyek sendiri yang dirujuk dengan namanya.
  • cabal build menyusun rencana dan membangun ke dist-newstyle, sedangkan cabal run menjalankan hasilnya dan membangun ulang apabila ada perubahan.
  • Dependensi dinyatakan sebagai nama paket beserta batas versinya, dan indeks paket diperbarui melalui cabal update.

Referensi#