Belajar Makefile dengan Contoh Terbaik
(makefiletutorial.com)- Makefile adalah alat untuk menyederhanakan otomatisasi build C/C++ dan pengelolaan dependensi
- Menggunakan deteksi file yang berubah berbasis timestamp, sehingga kompilasi hanya dijalankan saat diperlukan
- Menjelaskan struktur inti seperti rule, command, dan prerequisite dengan contoh
- Juga membahas secara praktis fitur lanjutan seperti automatic variables, pattern rules, dan variable expansion
- Memperkenalkan pentingnya skalabilitas dan kemudahan pengelolaan melalui template Makefile praktis untuk proyek skala menengah
Pengantar panduan tutorial Makefile
- Makefile adalah alat inti untuk otomatisasi build proyek dan pengelolaan dependensi
- Karena banyak aturan dan simbol tersembunyi, Makefile bisa terasa rumit saat pertama kali dipelajari, tetapi panduan ini merangkum poin-poin utama secara ringkas dengan contoh yang bisa langsung dijalankan
- Setiap bagian dapat dipahami melalui contoh berbasis praktik
Memulai
Tujuan adanya Makefile
- Makefile digunakan pada program besar untuk mengompilasi ulang hanya bagian yang berubah
- Selain C/C++, ada berbagai alat build khusus untuk bahasa lain, tetapi Make digunakan secara luas untuk skenario build umum
- Intinya adalah logika mendeteksi file yang berubah dan hanya menjalankan pekerjaan yang diperlukan
Sistem build alternatif untuk Make
- Keluarga C/C++: ada banyak pilihan seperti SCons, CMake, Bazel, Ninja, dan lainnya
- Keluarga Java: Ant, Maven, Gradle, dan sebagainya
- Go, Rust, TypeScript, dan lain-lain juga menyediakan alat build mereka sendiri
- Bahasa interpreter seperti Python, Ruby, JavaScript tidak memerlukan kompilasi, sehingga kebutuhan pengelolaan terpisah seperti Makefile lebih rendah
Versi dan jenis Make
- Ada berbagai implementasi Make, tetapi panduan ini dioptimalkan untuk GNU Make (umumnya digunakan di Linux dan MacOS)
- Contohnya kompatibel dengan GNU Make versi 3 dan 4
Cara menjalankan contoh
- Setelah memasang make di terminal, simpan tiap contoh ke file
Makefilelalu jalankan perintahmake - Baris perintah di dalam Makefile harus diindentasi menggunakan karakter tab
Sintaks dasar Makefile
Struktur Rule
-
target: prerequisite(s)- command
- command
-
Target: nama file hasil build (biasanya satu)
-
Command: skrip shell yang benar-benar dijalankan (diawali tab)
-
Prerequisite: daftar file yang harus siap sebelum target dibangun
Hakikat Make
Contoh Hello World
hello:
echo "Hello, World"
echo "This line will print if the file hello does not exist."
- Target
hellotidak memiliki dependensi dan menjalankan 2 command - Saat
make hellodijalankan, jika filehellobelum ada maka command akan dijalankan. Jika file itu sudah ada, command tidak dijalankan - Umumnya target ditulis agar sesuai dengan nama file
Contoh dasar kompilasi file C
- Buat file
blah.c(berisiint main() { return 0; }) - Tulis Makefile berikut
blah:
cc blah.c -o blah
- Saat
makedijalankan, jika targetblahbelum ada maka kompilasi dilakukan dan fileblahdibuat - Walau
blah.cdiubah, kompilasi ulang tidak terjadi otomatis → perlu menambahkan dependensi
Cara menambahkan dependensi
blah: blah.c
cc blah.c -o blah
- Sekarang jika
blah.cbaru saja berubah, targetblahakan dibangun ulang - Timestamp file digunakan sebagai dasar deteksi perubahan
- Jika timestamp dimanipulasi secara manual, perilakunya bisa menjadi tidak sesuai harapan
Contoh tambahan
Contoh target dan dependensi berantai
blah: blah.o
cc blah.o -o blah
blah.o: blah.c
cc -c blah.c -o blah.o
blah.c:
echo "int main() { return 0; }" > blah.c
- Dependensi diikuti dalam struktur pohon sehingga proses pembuatan tiap tahap diotomatisasi
Contoh target yang selalu dijalankan
some_file: other_file
echo "This will always run, and runs second"
touch some_file
other_file:
echo "This will always run, and runs first"
- Karena
other_filetidak benar-benar dibuat sebagai file, command untuksome_fileakan dijalankan setiap kali
Make clean
- Target
cleansering digunakan untuk menghapus hasil build - Ini bukan kata khusus yang dipesan oleh Make, jadi perlu didefinisikan sendiri sebagai command
- Jika ada file bernama
clean, hal itu bisa membingungkan, jadi disarankan memakai.PHONY
Contoh:
some_file:
touch some_file
clean:
rm -f some_file
Penanganan variabel
- Variabel selalu berupa string.
- Umumnya
:=direkomendasikan, tetapi ada juga berbagai bentuk assignment seperti=,?=,+= - Contoh penggunaan:
files := file1 file2
some_file: $(files)
echo "Look at this variable: " $(files)
touch some_file
file1:
touch file1
file2:
touch file2
clean:
rm -f file1 file2 some_file
- Cara mereferensikan variabel:
$(variable)atau${variable} - Tanda kutip di dalam Makefile tidak punya arti bagi Make itu sendiri (namun diperlukan dalam command shell)
Pengelolaan target
Target all
- Untuk menjalankan beberapa target sekaligus, berikan peran itu pada target pertama (default)
all: one two three
one:
touch one
two:
touch two
three:
touch three
clean:
rm -f one two three
Multi-target dan automatic variables
- Untuk banyak target, command individual dapat dijalankan masing-masing.
$@berisi nama target saat ini
all: f1.o f2.o
f1.o f2.o:
echo $@
Automatic variables dan wildcard
Wildcard *
*mencari nama langsung di file system- Disarankan selalu menggunakannya dengan membungkusnya dalam fungsi
wildcard
print: $(wildcard *.c)
ls -la $?
- Jangan gunakan
*langsung dalam definisi variabel
thing_wrong := *.o
thing_right := $(wildcard *.o)
Wildcard %
- Umumnya digunakan pada pattern rules, sehingga pola tertentu bisa diekstrak dan diperluas
Fancy Rules
Aturan implisit (Implicit rules)
- Make memiliki berbagai aturan default tersembunyi untuk build C/C++
- Variabel yang umum:
CC,CXX,CFLAGS,CPPFLAGS,LDFLAGS, dan lainnya - Contoh C:
CC = gcc
CFLAGS = -g
blah: blah.o
blah.c:
echo "int main() { return 0; }" > blah.c
clean:
rm -f blah*
Static Pattern Rules
- Dapat menulis banyak rule yang mengikuti pola yang sama secara lebih ringkas
objects = foo.o bar.o all.o
all: $(objects)
$(CC) $^ -o all
$(objects): %.o: %.c
$(CC) -c $^ -o $@
all.c:
echo "int main() { return 0; }" > all.c
%.c:
touch $@
clean:
rm -f *.c *.o all
Static Pattern Rules + fungsi filter
- Dengan filter, hanya target yang cocok dengan pola ekstensi tertentu yang bisa dipilih
obj_files = foo.result bar.o lose.o
src_files = foo.raw bar.c lose.c
all: $(obj_files)
.PHONY: all
$(filter %.o,$(obj_files)): %.o: %.c
echo "target: $@ prereq: $
1 komentar
Pendapat Hacker News
Pernah melihat langsung seseorang di lab Graphics Boston University pada tahun 1985 membuat renderer 3D untuk animasi dengan Makefile. Orang itu adalah programmer Lisp, sedang mengerjakan pembuatan prosedur awal dan sistem aktor 3D, lalu membuat Makefile yang sangat elegan hanya sekitar 10 baris. Strukturnya menghasilkan ratusan animasi secara otomatis hanya dengan dependensi tanggal file yang sederhana. Bentuk 3D tiap frame dibuat dengan Lisp, lalu Make menghasilkan frame-frame tersebut. Pada tahun 1985, berbeda dengan sekarang ketika 3D dan animasi dianggap biasa, semua orang sangat takjub, dan yang diingat adalah bahwa orang itu adalah Brian Gardner, yang kemudian menangani renderer 3D untuk Iron Giant dan Coraline
Mengungkapkan rasa penasaran apakah orang yang dimaksud adalah orang yang ada di 3d-consultant.com/bio.html
Memastikan apakah yang dimaksud memang film Coraline
Memperkenalkan beberapa flag berguna yang kurang dikenal saat memakai Make
--output-sync=recurse -j10: artinya stdout/stderr dikumpulkan lalu ditampilkan saat pekerjaan tiap target selesai; kalau tidak, log akan tercampur dan sulit dianalisis--load-averagealih-alih-juntuk mengatur beban sistem saat paralelisasi (make -j10 --load-average=10)--shuffle, yang mengacak jadwal target build, berguna di lingkungan CI untuk menangkap masalah dependensi di dalam MakefileMenyebut gagasan bahwa kalau berbagai opsi make dirangkum secara resmi lalu disertakan ke dalam program dalam bentuk teks atau dokumentasi, aksesibilitas pengguna akan meningkat
Opsi yang paling sering dipakai adalah flag
-Buntuk build paksa penuhKarena sering melihat masalah yang disebabkan
make -jpada mesin DOS, fenomena itu dianggap sebagai bugBertanya apakah masalah paralelisasi di sistem sibuk atau lingkungan multi-pengguna bukan sesuatu yang seharusnya ditangani scheduler OS
Flag-flag ini berguna, tetapi karena tidak portabel, disarankan untuk tidak memakainya kecuali di proyek privat untuk diri sendiri
Menganggap bahwa melewatkan .PHONY di tutorial hanya karena tidak dipakai adalah alasan yang lemah. Menurutnya, yang benar adalah mengajarkan cara memakai alat dengan benar
Ada klaim bahwa Make adalah alat yang dikhususkan untuk build codebase C berskala besar
Menurut pendapat lain, Make lebih merupakan alat shell umum yang mengubah shell script linear menjadi bentuk dependensi deklaratif daripada sekadar job runner
Ada pandangan bahwa melihat Make sebagai alat build khusus codebase C saja sudah tidak tepat lagi. Disebutkan kenyataan bahwa selama 20 tahun terakhir sudah dikembangkan sistem build yang lebih kokoh dan lebih jelas. Ada seruan agar cara pandang itu diperbarui
Pertanyaan tentang apa itu job runner yang baik. (Disertai permintaan maaf karena ternyata salah memahami arti job runner)
Merekomendasikan just sebagai alat modern yang menggantikan bagian-bagian Makefile yang menjadi rumit
just bagus sebagai pengganti daftar shell script, tetapi tidak bisa menggantikan fungsi esensial Make, yaitu “jalankan hanya rule yang perlu dijalankan ulang”
Selain itu, alternatif lain antara lain
Alat-alat alternatif menyebut dirinya pengganti Make, tetapi menurutnya sebenarnya sangat berbeda sehingga sulit dibandingkan. Inti Make adalah menghasilkan artefak dan tidak membangun ulang hal yang sudah dibangun. Sementara just hanya berperan sebagai eksekutor perintah sederhana
Keuntungan memakai Make sebagai eksekutor perintah adalah kestabilannya sebagai alat standar yang terpasang hampir di mana-mana. Walaupun alternatif mungkin dibuat lebih baik, tetap ada beban instalasi tambahan sehingga tidak terasa perlu dipakai
Task dipakai dengan baik untuk proyek hobi sederhana berbasis C, tetapi masih sulit menilai apakah cocok juga untuk proyek besar (situs resmi Task)
Menarik bahwa baru-baru ini CMake menilai Makefile tidak cocok untuk dukungan modul C++20, sehingga memilih ninja sebagai default (panduan CMake)
clang-scan-deps(slide teknis)Menurut pendapat lain, batasan ini sebenarnya keputusan dari pihak CMake atau karena generator Makefile tidak punya kontributor yang mendukungnya. ninja juga tidak bisa langsung mendukung modul C++ (isu terkait), dan justru punya lebih sedikit fitur daripada Make serta mengharuskan semua dependensi dinyatakan secara statis
Ada pendapat bahwa pengenalan modul itu sendiri rumit dan membingungkan
Bertanya apakah ada yang punya pengalaman memakai tup. (dokumentasi resmi)
Memperkenalkan diri sebagai pencipta dan maintainer utama alat alternatif Make bernama Task. Sudah dikembangkan lebih dari 8 tahun dan terus berkembang
just juga direkomendasikan sebagai alternatif Make lainnya (GitHub just)
Sebagai kebetulan yang lucu, ia sendiri sering memakai Task dan pagi ini juga mengajukan issue
Tutorial ini punya masalah yang berbahaya dan subtil
ifneq (,$(findstring t,$(firstword -$(MAKEFLAGS))))Punya kebiasaan selalu menyertakan Makefile di setiap repo GitHub
makeagar perilaku yang diharapkan untuk tiap proyek langsung dijalankan tanpa perlu mengingatnya lagi