Make adalah alat build otomatis yang mengelola dependensi antar file—ketika header util.h berubah, hanya file .c yang meng-include header tersebut yang perlu di-compile ulang. Make membaca file Makefile (atau makefile) berisi rule yang mendeskripsikan cara menghasilkan target dari prerequisite. Bab ini menyusun sintaks Make yang paling sering kita pakai, lengkap dengan automatic variables, built-in functions, dan contoh Makefile yang berfungsi.
Anatomi Rule #
Rule dasar Make terdiri dari target, prerequisite, dan recipe:
target: prerequisite1 prerequisite2
<TAB>recipe commandPenting: Baris recipe wajib diawali dengan karakter TAB, bukan spasi. Banyak editor secara default mengonversi TAB menjadi spasi—ini adalah penyebab error missing separator paling umum.
Variabel #
Variabel di Make didefinisikan dengan =, :=, ?=, atau +=. Perbedaannya terletak pada waktu ekspansi: = dievaluasi saat dipakai (recursive), := dievaluasi saat didefinisikan (simple), ?= hanya set jika belum terdefinisi, += menambah ke nilai yang ada.
| Operator | Nama | Waktu evaluasi |
|---|---|---|
= | Recursive | Saat variabel dipakai |
:= / ::= | Simple | Saat didefinisikan |
?= | Conditional | Saat didefinisikan, jika belum ada |
+= | Append | Sesuai operator asal |
CC = gcc # recursive
CFLAGS := -std=c11 -Wall # simple
LDFLAGS ?= -lm # hanya jika belum diset
SOURCES += main.c # append
Automatic Variables #
Saat recipe dieksekusi, Make menyediakan variabel otomatis yang merujuk ke target dan prerequisite rule. Tabel berikut mencantumkan yang paling sering dipakai.
| Variabel | Arti |
|---|---|
$@ | Nama target rule |
$< | Prerequisite pertama |
$^ | Semua prerequisite (dengan duplikat dihilangkan) |
$+ | Semua prerequisite (termasuk duplikat) |
$? | Prerequisite yang lebih baru dari target |
$* | Suffix matching stem (untuk pattern rule) |
$(@D) | Direktori dari $@ |
$(@F) | Nama file dari $@ |
Built-in Functions #
Make menyediakan banyak function built-in untuk manipulasi teks dan daftar. Sintaks pemanggilan: $(function arg1,arg2,...).
| Function | Efek |
|---|---|
$(wildcard pattern) | Daftar file yang cocok glob pattern |
$(patsubst pattern,replacement,text) | Substitusi pattern |
$(subst from,to,text) | Substitusi teks sederhana |
$(filter pattern...,text) | Filter elemen yang cocok |
$(filter-out pattern...,text) | Kebalikan filter |
$(strip text) | Hapus whitespace leading/trailing |
$(shell command) | Eksekusi command shell, kembalikan output |
$(foreach var,list,text) | Iterasi list |
$(if cond,then,else) | Kondisional |
$(notdir name) | Nama file tanpa path |
$(dir name) | Direktori saja |
$(basename name) | Nama tanpa extension |
$(addsuffix suffix,names) | Tambah suffix |
$(addprefix prefix,names) | Tambah prefix |
$(sort list) | Urutkan dan hapus duplikat |
$(words text) | Jumlah kata |
$(word n,text) | Kata ke-n |
$(words text) | Hitung jumlah kata |
Program Demo #
Kita akan membuat program tiga file—main.c, util.c, dan util.h—lalu menyusun dua versi Makefile: versi eksplisit dan versi dengan pattern rule. Kode programnya adalah sebagai berikut.
/* File: main.c - Demo Makefile */
#include <stdio.h>
#include "util.h"
int main(void) {
cetak_salam("Budi");
printf("2 + 3 = %d\n", tambah(2, 3));
return 0;
}/* File: util.h */
#ifndef UTIL_H
#define UTIL_H
void cetak_salam(const char *nama);
int tambah(int a, int b);
#endif/* File: util.c */
#include <stdio.h>
#include "util.h"
void cetak_salam(const char *nama) {
printf("Halo, %s!\n", nama);
}
int tambah(int a, int b) { return a + b; }Makefile Versi Eksplisit #
Versi pertama menuliskan setiap rule secara eksplisit. Mudah dipahami tetapi repetitif untuk proyek besar.
# Makefile demo untuk mkdemo
CC = gcc
CFLAGS = -std=c11 -Wall -Wextra -g
LDFLAGS =
OBJS = main.o util.o
TARGET = mkdemo
.PHONY: all clean run
all: $(TARGET)
$(TARGET): $(OBJS)
$(CC) $(LDFLAGS) -o $@ $^
main.o: main.c util.h
$(CC) $(CFLAGS) -c -o $@ $<
util.o: util.c util.h
$(CC) $(CFLAGS) -c -o $@ $<
run: $(TARGET)
./$(TARGET)
clean:
rm -f $(OBJS) $(TARGET)
Build dan jalankan:
make
./mkdemoOutput yang Dihasilkan (perintah make mencetak setiap perintah yang dijalankan, lalu program menghasilkan outputnya sendiri):
gcc -std=c11 -Wall -Wextra -g -c -o main.o main.c
gcc -std=c11 -Wall -Wextra -g -c -o util.o util.c
gcc -o mkdemo main.o util.o
Halo, Budi!
2 + 3 = 5Mari kita telaah baris-baris kode di atas:
- Baris 2-4 (
CC,CFLAGS,LDFLAGS): Variabel konfigurasi.CCmemilih compiler,CFLAGSberisi flag saat kompilasi,LDFLAGSuntuk linker. Memisahkan variabel membuat ganti compiler atau flag hanya perlu satu baris. - Baris 11 (
$(TARGET): $(OBJS)): Rule untuk executable. Targetmkdemobergantung pada dua object file. Recipe$(CC) $(LDFLAGS) -o $@ $^memakai automatic variables$@(nama targetmkdemo) dan$^(semua prerequisitemain.o util.o). - Baris 14-15 (
main.o: main.c util.h): Make melacak dependensi pada file header. Saatutil.hdiubah, hanyamain.odanutil.oyang di-compile ulang, bukan keseluruhan proyek. - Baris 23 (
clean): Rule phony (bukan file) yang membersihkan artefak build. Ditandai.PHONYagar Make tidak bingung jika ada file bernamacleandi direktori.
Makefile Versi Pattern Rule #
Untuk proyek dengan banyak file .c, menulis rule satu per satu menjadi melelahkan. Pattern rule memakai % sebagai wildcard dan function wildcard untuk mendeteksi source file otomatis.
# Makefile dengan automatic variables dan functions
CC = gcc
CFLAGS = -std=c11 -Wall -Wextra -g
SRCS = $(wildcard *.c)
OBJS = $(SRCS:.c=.o)
TARGET = mkdemo
.PHONY: all clean run
all: $(TARGET)
$(TARGET): $(OBJS)
$(CC) $(LDFLAGS) -o $@ $^
%.o: %.c
$(CC) $(CFLAGS) -c -o $@ $<
run: $(TARGET)
./$(TARGET)
clean:
$(RM) $(OBJS) $(TARGET)
Output yang Dihasilkan (make setelah make clean):
gcc -std=c11 -Wall -Wextra -g -c -o main.o main.c
gcc -std=c11 -Wall -Wextra -g -c -o util.o util.c
gcc -o mkdemo main.o util.oTracking Dependensi Header #
Pattern rule %.o: %.c hanya melacak dependensi .c → .o, bukan header. Untuk header, gunakan compiler option -MMD -MP yang menghasilkan file .d berisi dependensi:
CFLAGS = -std=c11 -Wall -Wextra -g -MMD -MP
SRCS = $(wildcard *.c)
OBJS = $(SRCS:.c=.o)
DEPS = $(SRCS:.c=.d)
-include $(DEPS)
clean:
$(RM) $(OBJS) $(TARGET) $(DEPS)