Library yang bagus bukan hanya kode yang bekerja — juga dokumentasi yang lengkap dan prosedur release yang jelas. Pada bagian ini kita menulis README.md yang menjelaskan cara build, API publik, dan kode error; menetapkan strategi versioning dengan konstanta EXAMPLELIB_VERSION_MAJOR/MINOR/PATCH; serta menyusun target install di Makefile untuk mendistribusikan library ke direktori sistem. Tujuan bagian ini adalah memastikan konsumen lain dapat memakai library tanpa membaca source code.
Konsep #
Dokumentasi library minimal terdiri dari empat bagian: (1) cara build (Makefile target dan flag), (2) daftar API publik dengan signature singkat, (3) daftar kode error dan artinya, (4) contoh pemakaian singkat. Format README.md dipilih karena GitHub, GitLab, dan IDE menampilkan markdown secara otomatis.
Versioning mengikuti Semantic Versioning (semver): MAJOR.MINOR.PATCH (mis. 1.0.0). Aturan: naikkan PATCH bila bug fix yang kompatibel, naikkan MINOR bila fitur baru yang kompatibel, naikkan MAJOR bila breaking change. Untuk library C, breaking change meliputi perubahan signature fungsi, hapus fungsi publik, atau ubah makna enum. Konstanta versi di examplelib.h (EXAMPLELIB_VERSION_MAJOR dst.) memungkinkan konsumen cek versi saat preprocessor atau runtime.
Install target meng-copy library ke direktori sistem. Standar UNIX: header ke PREFIX/include/, library ke PREFIX/lib/. Default prefix /usr/local (tidak butuh sudo untuk menulis di sana). Setelah install, konsumen dapat compile dengan -I/usr/local/include dan link dengan -L/usr/local/lib -lexamplelib tanpa menentukan path source proyek.
Implementasi #
README.md untuk examplelib (ringkas):
# examplelib
Contoh library C yang reusable untuk pembelajaran cara membangun,
menguji, dan mendistribusikan library C.
## Build
make # Bangun static + shared + test
make static # Hanya libexamplelib.a
make shared # Hanya libexamplelib.so
make test # Build dan jalankan test
make install # Install ke /usr/local (perlu sudo)
## API Singkat
| Fungsi | Tugas |
|-------------------------|----------------------------------------------|
| `exlib_version()` | Versi library sebagai string |
| `exlib_mean()` | Hitung rata-rata array double |
| `exlib_stddev()` | Hitung standar deviasi populasi |
| `exlib_reverse_bytes()` | Balik urutan byte in-place |
| `exlib_format_size()` | Format ukuran byte ke "1.50 KB" dst. |
| `exlib_contains()` | Cek substring ada di string |
| `exlib_strerror()` | Pesan singkat untuk kode error |
## Kode Error
| Kode | Nilai | Arti |
|------------------------|-------|-------------------------------|
| `EXLIB_OK` | 0 | sukses |
| `EXLIB_ERR_NULL` | -1 | pointer NULL atau n=0 |
| `EXLIB_ERR_RANGE` | -2 | nilai di luar rentang valid |
| `EXLIB_ERR_FORMAT` | -3 | format input tidak valid |
| `EXLIB_ERR_OVERFLOW` | -4 | potensi overflow |
| `EXLIB_ERR_NOT_FOUND` | -5 | tidak ditemukan |Konstanta versi di examplelib.h:
#define EXAMPLELIB_VERSION_MAJOR 1
#define EXAMPLELIB_VERSION_MINOR 0
#define EXAMPLELIB_VERSION_PATCH 0
#define EXAMPLELIB_VERSION_STRING "1.0.0"Target install di Makefile:
PREFIX ?= /usr/local
install: static shared
install -d $(PREFIX)/lib
install -d $(PREFIX)/include
install -m 644 $(LIB_STATIC) $(PREFIX)/lib/
install -m 755 $(LIB_SHARED) $(PREFIX)/lib/
install -m 644 include/examplelib.h $(PREFIX)/include/
Demo program konsumen yang memakai library yang telah di-install:
#include <stdio.h>
#include <examplelib.h>
int main(void) {
printf("Installed lib version: %s\n", exlib_version());
printf("Major: %d, Minor: %d, Patch: %d\n",
EXAMPLELIB_VERSION_MAJOR,
EXAMPLELIB_VERSION_MINOR,
EXAMPLELIB_VERSION_PATCH);
double data[] = {10.0, 20.0, 30.0};
double mean;
exlib_status s = exlib_mean(data, 3, &mean);
if (s == EXLIB_OK) {
printf("Mean [10,20,30] = %.2f\n", mean);
} else {
printf("Error: %s\n", exlib_strerror(s));
}
return 0;
}Pengujian #
Install library ke prefix lokal (tidak butuh sudo):
make install PREFIX=/tmp/examplelib_install
ls -la /tmp/examplelib_install/lib /tmp/examplelib_install/includeOutput yang Dihasilkan:
/tmp/examplelib_install/include/:
total 12
-rw-r--r-- 1 z z 1834 ... examplelib.h
/tmp/examplelib_install/lib/:
total 32
-rw-r--r-- 1 z z 4682 ... libexamplelib.a
-rwxr-xr-x 1 z z 15872 ... libexamplelib.soCompile program konsumen yang memakai library terinstall (static link):
gcc -std=c11 -Wall -Wextra -I/tmp/examplelib_install/include \
-o demo_user demo_user.c \
-L/tmp/examplelib_install/lib -l:libexamplelib.a -lm
./demo_userOutput yang Dihasilkan:
Installed lib version: 1.0.0
Major: 1, Minor: 0, Patch: 0
Mean [10,20,30] = 20.00Verifikasi semua test lulus sebelum release:
make testOutput yang Dihasilkan:
=== Hasil Test examplelib ===
Total : 28
Lulus : 28
Gagal : 0Catatan: macro ASSERT pada test_examplelib.c hanya mencetak baris saat test gagal — saat semua lulus, hanya ringkasan akhir yang muncul. Baris kosong di awal berasal dari \n di printf ringkasan.
Mari kita telaah baris-baris kunci pada README, versioning, dan Makefile install:
- Baris
# examplelib(README H1): judul proyek. GitHub menampilkannya sebagai judul halaman repo. Konvensi Markdown: H1 (#) di README, H2 (##) untuk section utama. - Baris tabel API Singkat: dokumentasi ringkas. Kolom “Fungsi” memakai backtick untuk kode inline (
exlib_version()) — render sebagai monospace di GitHub. - Baris
make install # Install ke /usr/local (perlu sudo): contoh pemakaian dengan komentar. Default prefix/usr/localbutuh sudo karena scoping sistem; overridePREFIX=/tmp/examplelib_installmenghindari sudo. - Baris
#define EXAMPLELIB_VERSION_MAJOR 1(header): konstanta integer. Konsumen dapat memeriksa versi saat preprocessor:#if EXAMPLELIB_VERSION_MAJOR >= 1untuk mengaktifkan fitur yang cuma ada di versi 1+. - Baris
#define EXAMPLELIB_VERSION_STRING "1.0.0"(header): string versi. Fungsiexlib_version()memulangkan string ini, sehingga pemanggil dapat menampilkan versi ke pengguna tanpa preprocessor. - Baris
PREFIX ?= /usr/local(Makefile): variabel dengan default. Sintaks?=hanya set bila belum di-set dari environment atau command line.make install PREFIX=/tmp/...meng-override default. - Baris
install -d $(PREFIX)/lib(Makefile): buat direktori bila belum ada.install -dmembuat direktori dengan permission standar (755). - Baris
install -m 644 $(LIB_STATIC) $(PREFIX)/lib/(Makefile): install static lib. Mode644(rw-r–r–) — file dapat dibaca siapa saja tetapi tidak dieksekusi (static lib tidak butuh execute permission). - Baris
install -m 755 $(LIB_SHARED) $(PREFIX)/lib/(Makefile): install shared lib. Mode755(rwxr-xr-x) — loader perlu execute permission untuk memetakan.soke memori. - Baris
install -m 644 include/examplelib.h $(PREFIX)/include/(Makefile): install header publik. Konsumen perlu header ini untuk compile program mereka; tanpa header, fungsiexlib_versiontidak dikenal.
Proyek 9 selesai. Library examplelib siap dipakai oleh proyek lain sebagai static atau shared library, lengkap dengan dokumentasi dan install target.
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
src/examplelib.c
Unduh berkas/* examplelib.c - Implementasi library examplelib */
#include "examplelib.h"
#include <math.h>
#include <string.h>
#include <stdio.h>
const char *exlib_version(void) {
return EXAMPLELIB_VERSION_STRING;
}
const char *exlib_strerror(exlib_status code) {
switch (code) {
case EXLIB_OK: return "sukses";
case EXLIB_ERR_NULL: return "pointer NULL atau n=0";
case EXLIB_ERR_RANGE: return "nilai di luar rentang valid";
case EXLIB_ERR_FORMAT: return "format input tidak valid";
case EXLIB_ERR_OVERFLOW: return "potensi overflow";
case EXLIB_ERR_NOT_FOUND: return "tidak ditemukan";
default: return "kode tidak dikenal";
}
}
exlib_status exlib_mean(const double *data, size_t n, double *out) {
if (data == NULL || out == NULL || n == 0) {
return EXLIB_ERR_NULL;
}
double sum = 0.0;
for (size_t i = 0; i < n; i++) {
sum += data[i];
}
*out = sum / (double)n;
return EXLIB_OK;
}
exlib_status exlib_stddev(const double *data, size_t n, double *out) {
if (data == NULL || out == NULL || n == 0) {
return EXLIB_ERR_NULL;
}
double mean = 0.0;
exlib_status s = exlib_mean(data, n, &mean);
if (s != EXLIB_OK) {
return s;
}
double sum_sq = 0.0;
for (size_t i = 0; i < n; i++) {
double diff = data[i] - mean;
sum_sq += diff * diff;
}
*out = sqrt(sum_sq / (double)n);
return EXLIB_OK;
}
exlib_status exlib_reverse_bytes(uint8_t *data, size_t n) {
if (data == NULL) {
return EXLIB_ERR_NULL;
}
if (n < 2) {
return EXLIB_OK;
}
size_t i = 0;
size_t j = n - 1;
while (i < j) {
uint8_t tmp = data[i];
data[i] = data[j];
data[j] = tmp;
i++;
j--;
}
return EXLIB_OK;
}
exlib_status exlib_format_size(uint64_t bytes, char *out, size_t out_cap) {
if (out == NULL) {
return EXLIB_ERR_NULL;
}
if (out_cap < 16) {
return EXLIB_ERR_RANGE;
}
/* Daftar unit dari kecil ke besar. */
static const char *units[] = {"B", "KB", "MB", "GB", "TB", "PB"};
static const int n_units = 6;
if (bytes == 0) {
snprintf(out, out_cap, "0.00 B");
return EXLIB_OK;
}
int unit_idx = 0;
double size = (double)bytes;
while (size >= 1024.0 && unit_idx < n_units - 1) {
size /= 1024.0;
unit_idx++;
}
snprintf(out, out_cap, "%.2f %s", size, units[unit_idx]);
return EXLIB_OK;
}
exlib_status exlib_contains(const char *haystack, const char *needle) {
if (haystack == NULL || needle == NULL) {
return EXLIB_ERR_NULL;
}
if (needle[0] == '\0') {
return EXLIB_OK;
}
if (strstr(haystack, needle) != NULL) {
return EXLIB_OK;
}
return EXLIB_ERR_NOT_FOUND;
}
tests/test_examplelib.c
Unduh berkas/* test_examplelib.c - Unit test untuk examplelib */
#include <stdio.h>
#include <string.h>
#include <math.h>
#include "examplelib.h"
static int tests_run = 0;
static int tests_pass = 0;
#define ASSERT(cond, msg) \
do { \
tests_run++; \
if (cond) { \
tests_pass++; \
} else { \
printf("FAIL: %s (line %d)\n", msg, __LINE__); \
} \
} while (0)
static void test_version(void) {
ASSERT(strcmp(exlib_version(), "1.0.0") == 0,
"versi harus 1.0.0");
ASSERT(strcmp(exlib_strerror(EXLIB_OK), "sukses") == 0,
"strerror OK = 'sukses'");
ASSERT(strcmp(exlib_strerror(EXLIB_ERR_NOT_FOUND),
"tidak ditemukan") == 0,
"strerror NOT_FOUND sesuai");
}
static void test_mean(void) {
double data[] = {1.0, 2.0, 3.0, 4.0, 5.0};
double mean = 0.0;
exlib_status s = exlib_mean(data, 5, &mean);
ASSERT(s == EXLIB_OK, "mean harus sukses");
ASSERT(fabs(mean - 3.0) < 1e-9, "mean = 3.0");
s = exlib_mean(NULL, 5, &mean);
ASSERT(s == EXLIB_ERR_NULL, "mean(NULL) = ERR_NULL");
s = exlib_mean(data, 0, &mean);
ASSERT(s == EXLIB_ERR_NULL, "mean n=0 = ERR_NULL");
}
static void test_stddev(void) {
double data[] = {2.0, 4.0, 4.0, 4.0, 5.0, 5.0, 7.0, 9.0};
double sd = 0.0;
exlib_status s = exlib_stddev(data, 8, &sd);
ASSERT(s == EXLIB_OK, "stddev harus sukses");
/* Standar deviasi populasi: sqrt(32/8) = 2.0 */
ASSERT(fabs(sd - 2.0) < 1e-9, "stddev = 2.0");
}
static void test_reverse_bytes(void) {
uint8_t data[] = {1, 2, 3, 4, 5};
exlib_status s = exlib_reverse_bytes(data, 5);
ASSERT(s == EXLIB_OK, "reverse sukses");
ASSERT(data[0] == 5 && data[1] == 4 && data[2] == 3,
"data setelah reverse = 5,4,3,2,1");
ASSERT(data[3] == 2 && data[4] == 1, "data[3..4] = 2,1");
s = exlib_reverse_bytes(NULL, 5);
ASSERT(s == EXLIB_ERR_NULL, "reverse NULL = ERR_NULL");
uint8_t single[] = {42};
s = exlib_reverse_bytes(single, 1);
ASSERT(s == EXLIB_OK, "reverse n=1 = OK");
ASSERT(single[0] == 42, "single byte tidak berubah");
}
static void test_format_size(void) {
char buf[32];
exlib_status s = exlib_format_size(0, buf, sizeof(buf));
ASSERT(s == EXLIB_OK, "format 0 = OK");
ASSERT(strcmp(buf, "0.00 B") == 0, "0 bytes = '0.00 B'");
s = exlib_format_size(1024ULL, buf, sizeof(buf));
ASSERT(s == EXLIB_OK, "format 1024 = OK");
ASSERT(strcmp(buf, "1.00 KB") == 0, "1024 = '1.00 KB'");
s = exlib_format_size(1572864ULL, buf, sizeof(buf));
ASSERT(s == EXLIB_OK, "format 1.5MB = OK");
ASSERT(strcmp(buf, "1.50 MB") == 0, "1.5MB = '1.50 MB'");
s = exlib_format_size(1073741824ULL, buf, sizeof(buf));
ASSERT(s == EXLIB_OK, "format 1GB = OK");
ASSERT(strcmp(buf, "1.00 GB") == 0, "1GB = '1.00 GB'");
s = exlib_format_size(100, buf, 5);
ASSERT(s == EXLIB_ERR_RANGE, "buffer kecil = ERR_RANGE");
}
static void test_contains(void) {
exlib_status s = exlib_contains("hello world", "world");
ASSERT(s == EXLIB_OK, "substring ditemukan");
s = exlib_contains("hello world", "xyz");
ASSERT(s == EXLIB_ERR_NOT_FOUND, "substring tidak ditemukan");
s = exlib_contains("abc", "");
ASSERT(s == EXLIB_OK, "needle kosong = OK");
s = exlib_contains(NULL, "x");
ASSERT(s == EXLIB_ERR_NULL, "NULL haystack = ERR_NULL");
}
int main(void) {
test_version();
test_mean();
test_stddev();
test_reverse_bytes();
test_format_size();
test_contains();
printf("\n=== Hasil Test examplelib ===\n");
printf("Total : %d\n", tests_run);
printf("Lulus : %d\n", tests_pass);
printf("Gagal : %d\n", tests_run - tests_pass);
return (tests_run == tests_pass) ? 0 : 1;
}