Sampai di sini kita sudah punya biner rilis yang bersih (Chapter Release Build) dan mampu membangunnya untuk arsitektur mana pun (Chapter Cross-compilation). Pertanyaan tinggal satu: bagaimana biner itu sampai ke pengguna Linux dengan cara yang rapi — terpasang di jalur yang benar, menyatakan dependensinya, dan bisa dicopot bersih? Ekosistem Linux menjawabnya dengan tiga jalur yang saling melengkapi: paket distro (.deb/.rpm) untuk instalasi terintegrasi, tarball untuk distribusi universal, dan bundle mandiri seperti AppImage untuk aplikasi sekali-klik. Di chapter ini jalur pertama dan kedua kita kerjakan sungguhan: kita merakit satu paket .deb yang sah dari nol, memeriksa isinya, lalu mengetarkan tarball yang reproducible.

Struktur Instalasi mengikuti FHS #

Sebelum menaruh berkas, kita perlu tahu aturan tata letaknya. Distribusi Linux mengikuti FHS (Filesystem Hierarchy Standard) yang menetapkan jalur mana untuk apa:

JalurIsi
/usr/binExecutable yang dipakai semua pengguna
/usr/lib/<nama>/Library milik satu paket, tidak dipakai paket lain
/usr/share/doc/<nama>/README, changelog, berkas lisensi
/usr/local/binSoftware yang admin pasang secara manual, di luar manajer paket

Recap dari Chapter Dynamic Library Deployment menjadi relevan di sini: library yang dipasang lewat paket berada di jalur sistem sehingga cukup ditemukan lewat cache ldconfig, bukan lewat rpath $ORIGIN — rpath adalah teknik untuk bundle mandiri, bukan untuk paket. Dengan struktur ini pula alat-alat distro (manajer paket, man, sistem menu) bisa menemukan setiap bagian aplikasi kita tanpa kejutan.

Program yang Kita Paketkan #

Bahan uji kita aplikasi CLI yang sudah mengikuti semua aturan rilis dari chapter lama — kalkulator angsuran dengan formula anuitas:

cpp
#include <cmath>
#include <iomanip>
#include <iostream>

// Kalkulator angsuran (formula anuitas): pokok, tenor, dan bunga tahunan
// diambil dari argumen baris perintah. Aplikasi ini yang kita paketkan
// sebagai .deb di chapter ini.
int main(int argc, char* argv[]) {
    if (argc < 4) {
        std::cerr << "Pemakaian: kalkulator-cicilan <pokok> <tenor-bulan> <bunga-persen>\n";
        return 1;
    }

    double pokok = std::stod(argv[1]);
    int tenor    = std::stoi(argv[2]);
    double bunga = std::stod(argv[3]) / 100.0 / 12.0;   // bunga per bulan

    if (tenor <= 0 || pokok <= 0 || bunga < 0) {
        std::cerr << "Parameter tidak valid\n";
        return 1;
    }

    double angsuran;
    if (bunga == 0.0) {
        angsuran = pokok / tenor;
    } else {
        angsuran = pokok * bunga / (1.0 - std::pow(1.0 + bunga, -tenor));
    }

    std::cout << std::fixed << std::setprecision(0);
    std::cout << "Angsuran per bulan: Rp" << angsuran << '\n';
    std::cout << "Total dibayar     : Rp" << angsuran * tenor << '\n';
    return 0;
}

Output yang Dihasilkan:

text
Angsuran per bulan: Rp2027187
Total dibayar     : Rp24326240

Baris 11-15 menagih tiga argumen dan menolak input janggal — pokok negatif, tenor nol — dengan pesan ke std::cerr dan exit code 1. Baris 19-26 menerapkan formula anuitas dengan std::pow; cabang bunga == 0.0 menjaga pembagian nol saat pinjaman tanpa bunga. Baris 28-30 mencetak rupiah tanpa desimal agar angkanya terbaca wajar. Aplikasi inilah yang kita ubah menjadi paket.

Merakit Paket .deb #

Paket .deb pada dasarnya adalah arsip berisi dua bagian: direktori DEBIAN/ dengan metainformasi, dan pohon berkas yang akan ditempatkan ke sistem. Metainformasi terpenting ada di DEBIAN/control:

text
Package: kalkulator-cicilan
Version: 1.0.0
Section: utils
Priority: optional
Architecture: amd64
Maintainer: Budi Santoso <[email protected]>
Depends: libc6 (>= 2.34), libstdc++6 (>= 12)
Description: Kalkulator angsuran dari kursus C++
 Kalkulator cicilan sederhana dengan formula anuitas:
 angsuran tetap per bulan dari pokok, tenor, dan bunga tahunan.

Setiap baris punya peran: Package adalah nama unik untuk dicari dan dicopot; Architecture: amd64 menyatakan biner ini untuk x86-64 (di Debian, nama amd64 — sesuai target triple yang kita bedah di chapter cross-compilation); Depends mendaftar paket library yang wajib ada, dengan versi minimum yang bisa Anda turunkan langsung dari hasil ldd program; dan Description berbaris lanjutan yang diawali satu spasi. Kini kita rakit: staging sesuai FHS, bangun biner rilis, lalu panggil dpkg-deb — semuanya nyata di praktikum:

bash
mkdir -p cicilan-pkg/DEBIAN cicilan-pkg/usr/bin
g++ -std=c++23 -Wall -Wextra -O2 -DNDEBUG \
    kalkulator-cicilan.cpp -o cicilan-pkg/usr/bin/kalkulator-cicilan
strip cicilan-pkg/usr/bin/kalkulator-cicilan
dpkg-deb --build --root-owner-group cicilan-pkg kalkulator-cicilan_1.0.0_amd64.deb
ls -l kalkulator-cicilan_1.0.0_amd64.deb | awk '{print $5, $9}'

Output yang Dihasilkan:

text
dpkg-deb: building package 'kalkulator-cicilan' in 'kalkulator-cicilan_1.0.0_amd64.deb'.
4140 kalkulator-cicilan_1.0.0_amd64.deb

Struktur staging menentukan instalasi: berkas di cicilan-pkg/usr/bin/ akan berakhir di /usr/bin/ di mesin pengguna. Konvensi penamaan berkas nama_versi_arsitektur.deb membantu manajer paket, dan flag --root-owner-group membuat kepemilikan berkas di dalam paket tertulis sebagai root bukan user Anda — penting karena sistem memasangnya sebagai root. Dua belas baris perintah, dan kita punya paket resmi yang bisa dipasang di Debian/Ubuntu mana pun dengan dependensinya terpenuhi.

Memeriksa dan Memasang Paket #

Sebelum dikirim, periksa isi paket — dua subperintah dpkg-deb menjawab dua pertanyaan berbeda:

bash
dpkg-deb --info kalkulator-cicilan_1.0.0_amd64.deb

Output yang Dihasilkan:

text
 new Debian package, version 2.0.
 size 4140 bytes: control archive=460 bytes.
     353 bytes,    10 lines      control
 Package: kalkulator-cicilan
 Version: 1.0.0
 Section: utils
 Priority: optional
 Architecture: amd64
 Maintainer: Budi Santoso <[email protected]>
 Depends: libc6 (>= 2.34), libstdc++6 (>= 12)
 Description: Kalkulator angsuran dari kursus C++
  Kalkulator cicilan sederhana dengan formula anuitas:
  angsuran tetap per bulan dari pokok, tenor, dan bunga tahunan.

Sedangkan --contents menampilkan daftar berkas; kita saring kolom ukuran dan tanggalnya dengan awk supaya tampilan tidak berubah-ubah antar build:

bash
dpkg-deb --contents kalkulator-cicilan_1.0.0_amd64.deb | awk '{print $1, $2, $6}'

Output yang Dihasilkan:

text
drwxrwxr-x root/root ./
drwxrwxr-x root/root ./usr/
drwxrwxr-x root/root ./usr/bin/
-rwxrwxr-x root/root ./usr/bin/kalkulator-cicilan

Pemasangan di mesin pengguna — yang tidak kita jalankan di praktikum karena mengubah sistem — tinggal:

bash Contoh — di mesin pengguna Debian/Ubuntu
sudo dpkg -i kalkulator-cicilan_1.0.0_amd64.deb
sudo apt install ./kalkulator-cicilan_1.0.0_amd64.deb
sudo dpkg -r kalkulator-cicilan

Bentuk apt install ./berkas.deb lebih pintar daripada dpkg -i karena sekaligus memasang paket Depends yang belum ada. Variasi dpkg -r mencopot paket berdasarkan nama Package di control — daftar paket yang terpasang bisa Anda lihat dengan dpkg -l.

Tarball: Jalur Paling Universal #

Tidak semua pengguna memakai distro berbasis Debian, dan tidak semua rilis layak dirakit sebagai paket resmi. Tarball — arsip .tar.gz berisi folder siap pakai — adalah jalur paling universal, dan sekaligus tempat pertama kita menerapkan konsep reproducible builds dari Chapter Release Build:

bash
mkdir -p dist/kalkulator-cicilan_1.0.0-amd64/bin
cp cicilan-pkg/usr/bin/kalkulator-cicilan dist/kalkulator-cicilan_1.0.0-amd64/bin/
tar --sort=name --mtime='2026-01-01' --owner=0 --group=0 \
    -czf kalkulator-cicilan_1.0.0-amd64.tar.gz -C dist kalkulator-cicilan_1.0.0-amd64
tar -tzf kalkulator-cicilan_1.0.0-amd64.tar.gz

Output yang Dihasilkan:

text
kalkulator-cicilan_1.0.0-amd64/
kalkulator-cicilan_1.0.0-amd64/README.txt
kalkulator-cicilan_1.0.0-amd64/bin/
kalkulator-cicilan_1.0.0-amd64/bin/kalkulator-cicilan

Tiga flag tar itu bukan hiasan: --sort=name mengurutkan entri secara deterministik, --mtime menetapkan stempel waktu tetap, dan --owner=0 --group=0 menghapus ketergantungan pada nama user yang mengetarkan arsip. Tanpa ketiganya, tarball yang dibangun hari ini dan besok berbeda byte meski isinya sama — checksum rilis Anda jadi tidak bermakna. Tarball seperti ini biasanya disertai skrip pemasangan kecil yang juga diuji:

bash Contoh isi install.sh
install -Dm755 bin/kalkulator-cicilan /usr/local/bin/kalkulator-cicilan
kalkulator-cicilan 24000000 12 2.5

Perintah install -Dm755 sekaligus membuat direktori tujuan (-D) dan mengatur izin eksekusi (-m755); jalur /usr/local/bin dipilih karena FHS menunjuknya sebagai rumah software yang admin pasang manual — tepat untuk tarball.

AppImage dan Bundle Mandiri #

Jalur ketiga menyelesaikan persoalan yang di Chapter Dynamic Library Deployment kita sebut dependency hell: aplikasi butuh versi library tertentu, sistem punya versi lain, dan paket resmi menuntut Depends yang tidak tersedia. Solusi bundle mandiri sudah kita rakit fondasinya — biner dengan RUNPATH $ORIGIN/lib yang membawa library miliknya sendiri dalam satu folder. AppImage membungkus pola yang sama menjadi satu berkas eksekusi: folder aplikasi (dengan struktur usr/, berkas .desktop, dan skrip AppRun) dikemas sebagai image squashfs lalu disatukan dengan runtime kecil yang me-mount image itu saat dijalankan:

bash Contoh — merakit AppImage (perlu alat appimagetool)
appimagetool AppDir kalkulator-cicilan-1.0.0-x86_64.AppImage
chmod +x kalkulator-cicilan-1.0.0-x86_64.AppImage
./kalkulator-cicilan-1.0.0-x86_64.AppImage 24000000 12 2.5

Hasilnya satu berkas yang bisa diunduh, ditandai executable, dan dijalankan tanpa instalasi sama sekali — pola yang meniru kenyamanan aplikasi Windows atau macOS. Harga yang dibayar adalah ukuran (semua library ikut dibawa) dan tanggung jawab pembaruan yang kembali ke aplikasi, bukan ke manajer paket.

Kita kini bisa menyalurkan aplikasi ke pengguna Linux dengan tiga gaya berbeda, masing-masing dengan janji dan harganya yang jelas. Chapter berikutnya melintasi batas sistem operasi: distribusi di Windows, dunia dengan aturan runtime, instalasi, dan sertifikasi digital yang sepenuhnya berbeda.

Referensi: Link Options — GCC Online Docs .