Informasi yang memberi tahu make cara mengompilasi ulang suatu sistem diperoleh dengan membaca basis data yang disebut makefile.
MAKEFILESmake membaca makefileMakefile berisi lima jenis hal: aturan eksplisit, aturan implisit, definisi variabel, direktif, dan komentar. Aturan, variabel, dan direktif dijelaskan secara mendalam di bab-bab berikutnya.
objects sebagai daftar semua file objek (lihat Variabel membuat makefile lebih sederhana).
make untuk melakukan sesuatu yang khusus saat membaca makefile. Direktif ini meliputi:
# secara harfiah, escape-lah dengan garis miring terbalik (misalnya, \#). Komentar dapat muncul di baris mana pun dalam makefile, meskipun dalam situasi tertentu diperlakukan secara khusus.
Anda tidak dapat menggunakan komentar di dalam referensi variabel atau pemanggilan fungsi: kemunculan # di dalamnya akan diperlakukan secara harfiah, bukan sebagai awal komentar.
Komentar di dalam resep diteruskan ke shell, sama seperti teks resep lainnya. Shell yang menentukan cara menafsirkannya: apakah itu komentar atau bukan ditentukan oleh shell.
Di dalam direktif define, komentar tidak diabaikan selama definisi variabel berlangsung, melainkan tetap dipertahankan sebagai bagian dari nilai variabel tersebut. Ketika variabel itu diekspansi, komentar tersebut akan diperlakukan sebagai komentar make atau sebagai teks resep, tergantung pada konteks saat variabel itu dievaluasi.
Makefile menggunakan sintaksis “berbasis baris” yang menjadikan karakter baris baru istimewa dan menandai akhir suatu pernyataan. GNU make tidak membatasi panjang satu baris pernyataan, sepanjang memori komputer Anda mencukupi.
Namun, baris yang terlalu panjang untuk ditampilkan tanpa pelipatan atau penggulungan sulit dibaca. Karena itu, Anda dapat memformat makefile agar mudah dibaca dengan menyisipkan baris baru di tengah suatu pernyataan: caranya dengan meng-escape baris baru di tengah tersebut menggunakan karakter garis miring terbalik (\). Ketika perlu dibedakan, “baris fisik” mengacu pada satu baris yang diakhiri baris baru (baik di-escape maupun tidak), sedangkan “baris logis” mengacu pada satu pernyataan lengkap yang mencakup semua baris baru yang di-escape hingga baris baru pertama yang tidak di-escape.
Cara penanganan kombinasi garis miring terbalik dan baris baru bergantung pada apakah pernyataan tersebut merupakan baris resep atau bukan. Penanganan garis miring terbalik/baris baru pada baris resep dibahas kemudian (lihat Membagi baris resep ke beberapa baris).
Di luar baris resep, garis miring terbalik/baris baru diubah menjadi satu karakter spasi. Setelah itu, semua whitespace di sekitar garis miring terbalik/baris baru tersebut dipadatkan menjadi satu spasi: ini mencakup semua whitespace sebelum garis miring terbalik, semua whitespace di awal baris setelah garis miring terbalik/baris baru, dan setiap kombinasi garis miring terbalik/baris baru yang berurutan.
Jika target khusus .POSIX didefinisikan, penanganan garis miring terbalik/baris baru berubah sedikit agar sesuai dengan POSIX.2. Pertama, whitespace sebelum garis miring terbalik tidak dihapus. Kedua, garis miring terbalik/baris baru yang berurutan tidak dipadatkan.
Jika Anda perlu membagi suatu baris tetapi tidak ingin menambahkan whitespace apa pun, ada trik halus yang bisa dimanfaatkan: gantilah pasangan garis miring terbalik/baris baru dengan tiga karakter tanda dolar, garis miring terbalik, dan baris baru:
var := one$\
word
Setelah make menghapus garis miring terbalik/baris baru dan memadatkan baris berikutnya menjadi satu spasi, ini setara dengan:
var := one$ word
Selanjutnya make akan melakukan ekspansi variabel. Referensi variabel ‘$ ’ mengacu pada variabel bernama satu karakter ‘ ’ (spasi) yang tidak ada, sehingga diekspansi menjadi string kosong, dan menghasilkan penetapan akhir yang setara dengan:
var := oneword
Secara default, ketika make mencari makefile, ia mencoba nama-nama berikut secara berurutan: ‘GNUmakefile’, ‘makefile’, dan ‘Makefile’.
Biasanya Anda sebaiknya menamai makefile Anda ‘makefile’ atau ‘Makefile’. (Kami menyarankan ‘Makefile’ karena namanya tampak menonjol di dekat awal daftar isi direktori, berdekatan dengan file penting lain seperti ‘README’.) Nama pertama yang diperiksa, ‘GNUmakefile’, tidak disarankan untuk kebanyakan makefile. Gunakan nama ini hanya jika Anda memiliki makefile yang khusus untuk GNU make dan tidak akan dipahami oleh versi make lain. Program make lain mencari ‘makefile’ dan ‘Makefile’, tetapi tidak mencari ‘GNUmakefile’.
Jika make tidak menemukan satu pun dari nama-nama ini, ia tidak akan menggunakan makefile sama sekali. Dalam hal itu, Anda harus menentukan sasaran (goal) melalui argumen perintah, dan make akan mencoba mencari cara membuatnya ulang hanya dengan menggunakan aturan implisit bawaannya. Lihat Menggunakan aturan implisit.
Jika Anda ingin menggunakan nama nonstandar untuk makefile Anda, Anda dapat menentukan nama makefile dengan opsi ‘-f’ atau ‘--file’. Argumen ‘-f name’ atau ‘--file=name’ memberi tahu make untuk membaca file name sebagai makefile. Jika Anda menggunakan lebih dari satu opsi ‘-f’ atau ‘--file’, Anda dapat menentukan beberapa makefile. Semua makefile itu secara efektif digabungkan sesuai urutan yang ditentukan. Nama makefile default ‘GNUmakefile’, ‘makefile’, dan ‘Makefile’ tidak dicari secara otomatis jika Anda menentukan ‘-f’ atau ‘--file’.
Direktif include memberi tahu make untuk menghentikan sementara pembacaan makefile saat ini dan membaca satu atau beberapa makefile lain sebelum melanjutkan. Direktif ini berupa baris dalam makefile yang terlihat seperti berikut.
include filenames…
filenames dapat berisi pola nama file shell. Jika filenames kosong, tidak ada yang disertakan dan tidak ada error yang ditampilkan.
Spasi tambahan boleh ada dan akan diabaikan di awal baris, tetapi karakter pertama tidak boleh berupa tab (atau nilai .RECIPEPREFIX)—jika baris dimulai dengan tab, baris itu akan dianggap sebagai baris resep. Whitespace diperlukan di antara include dan nama file, serta di antara nama-nama file; whitespace tambahan di sana dan di akhir direktif diabaikan. Komentar yang diawali ‘#’ boleh ada di akhir baris. Jika nama file berisi referensi variabel atau fungsi, referensi tersebut akan diekspansi. Lihat Cara menggunakan variabel.
Misalnya, jika Anda memiliki tiga file .mk, yaitu a.mk, b.mk, dan c.mk, dan $(bar) diekspansi menjadi bish bash, maka ekspresi berikut
include foo *.mk $(bar)
setara dengan
include foo a.mk b.mk c.mk bish bash
Saat make memproses direktif include, ia menghentikan sementara pembacaan makefile yang memuatnya dan membaca setiap file yang terdaftar secara berurutan. Setelah selesai, make melanjutkan pembacaan makefile tempat direktif itu muncul.
Salah satu kegunaan direktif include adalah ketika beberapa program, yang masing-masing ditangani oleh makefile terpisah di direktori berbeda, perlu menggunakan sekumpulan definisi variabel (lihat Menetapkan variabel) atau aturan pola (lihat Mendefinisikan dan mendefinisikan ulang aturan pola) yang sama.
Kegunaan lainnya adalah ketika Anda ingin membuat prasyarat secara otomatis dari file sumber; prasyarat yang dihasilkan dapat ditempatkan dalam satu file yang disertakan oleh makefile utama. Cara ini umumnya lebih rapi dibandingkan kebiasaan menambahkan prasyarat di akhir makefile utama, seperti yang secara tradisional dilakukan oleh versi make lain. Lihat Membuat prasyarat secara otomatis.
Jika nama yang ditentukan tidak diawali garis miring (atau huruf drive dan titik dua jika GNU Make dikompilasi dengan dukungan path MS-DOS/MS-Windows), dan file itu tidak ditemukan di direktori saat ini, beberapa direktori lain akan dicari. Pertama, direktori mana pun yang telah Anda tentukan dengan opsi ‘-I’ atau ‘--include-dir’ akan dicari (lihat Ringkasan opsi). Kemudian, direktori berikut (jika ada) dicari dalam urutan ini: prefix/include (biasanya /usr/local/include 1), /usr/gnu/include, /usr/local/include, /usr/include.
Variabel .INCLUDE_DIRS berisi daftar direktori yang saat ini dicari oleh make untuk file yang disertakan. Lihat Variabel khusus lainnya.
Jika Anda ingin menghindari pencarian di direktori default ini, tambahkan opsi baris perintah -I dengan nilai khusus - (misalnya, -I-) ke baris perintah. Ini akan membuat make melupakan semua direktori penyertaan yang sudah ditetapkan, termasuk direktori default.
Jika makefile yang disertakan tidak ditemukan di salah satu direktori ini, itu bukan langsung menjadi error fatal; pemrosesan makefile yang memuat include tersebut tetap berlanjut. Setelah selesai membaca semua makefile, make akan mencoba membuat ulang makefile mana pun yang usang atau tidak ada. Lihat Cara makefile dibuat ulang. Hanya setelah gagal menemukan aturan untuk membuat ulang makefile tersebut, atau menemukan aturan tetapi resepnya gagal, barulah make mendiagnosis makefile yang hilang itu sebagai error fatal.
Jika Anda ingin make begitu saja mengabaikan makefile yang tidak ada atau tidak dapat dibuat ulang, tanpa pesan error, gunakan direktif -include sebagai pengganti include, seperti berikut.
-include filenames…
Direktif ini bertindak seperti include dalam segala hal, kecuali bahwa tidak ada error (bahkan peringatan sekalipun) jika salah satu filenames (atau salah satu prasyaratnya) tidak ada atau tidak dapat dibuat ulang.
Demi kompatibilitas dengan implementasi make lain, sinclude merupakan nama lain untuk -include.
MAKEFILES
Jika variabel lingkungan MAKEFILES didefinisikan, make menganggap nilainya sebagai daftar nama (dipisahkan whitespace) makefile tambahan yang harus dibaca sebelum makefile lain. Ini bekerja mirip dengan direktif include: berbagai direktori dicari untuk file-file tersebut (lihat Menyertakan makefile lain). Selain itu, sasaran default tidak pernah diambil dari salah satu makefile ini (atau makefile mana pun yang disertakannya), dan tidak menjadi error jika file yang terdaftar dalam MAKEFILES tidak ditemukan.
Kegunaan utama MAKEFILES adalah untuk komunikasi antar-pemanggilan make secara rekursif (lihat Penggunaan rekursif make). Biasanya tidak diinginkan untuk menetapkan variabel lingkungan ini sebelum pemanggilan make tingkat atas, karena umumnya lebih baik tidak mengutak-atik makefile dari luar. Namun, jika Anda menjalankan make tanpa makefile tertentu, makefile dalam MAKEFILES dapat melakukan hal-hal berguna untuk membantu aturan implisit bawaan bekerja lebih baik, seperti mendefinisikan path pencarian (lihat Mencari prasyarat dari direktori).
Beberapa pengguna tergoda untuk menetapkan MAKEFILES secara otomatis di lingkungan saat login, dan menulis makefile dengan asumsi bahwa hal itu sudah dilakukan. Ini adalah ide yang sangat buruk, karena makefile semacam itu akan gagal bekerja jika dijalankan oleh orang lain. Jauh lebih baik menuliskan direktif include secara eksplisit dalam makefile. Lihat Menyertakan makefile lain.
Terkadang makefile dapat dibuat ulang dari file lain, seperti file RCS atau SCCS. Jika suatu makefile dapat dibuat ulang dari file lain, kemungkinan Anda ingin make mendapatkan versi makefile yang mutakhir untuk dibaca.
Untuk itu, setelah membaca semua makefile, make akan menganggap setiap makefile sebagai target sasaran, sesuai urutan pemrosesannya, dan mencoba memperbaruinya. Jika build paralel diaktifkan (lihat Eksekusi paralel), makefile juga akan dibangun ulang secara paralel.
Jika suatu makefile memiliki aturan yang menunjukkan cara memperbaruinya sendiri (aturan itu bisa ada di makefile itu sendiri atau di makefile lain), atau jika ada aturan implisit yang berlaku untuknya (lihat Menggunakan aturan implisit), makefile tersebut akan diperbarui jika perlu. Setelah semua makefile diperiksa, jika ada yang benar-benar berubah, make akan memulai dari awal dan membaca ulang semua makefile dari awal. (Pada saat ini make juga akan mencoba memperbarui setiap makefile itu lagi, tetapi biasanya ini tidak akan mengubahnya lagi karena sudah mutakhir.) Setiap kali dimulai ulang, variabel khusus MAKE_RESTARTS akan diperbarui (lihat Variabel khusus lainnya).
Jika Anda tahu bahwa satu atau beberapa makefile Anda tidak dapat dibuat ulang dan Anda ingin mencegah make melakukan pencarian aturan implisit terhadapnya, mungkin demi alasan efisiensi, Anda dapat menggunakan cara biasa apa pun untuk mencegah pencarian aturan implisit. Misalnya, Anda dapat menulis aturan eksplisit dengan makefile itu sebagai target dan resep kosong (lihat Menggunakan resep kosong).
Jika makefile menentukan aturan titik dua ganda (double-colon) yang memiliki resep tetapi tidak memiliki prasyarat untuk membuat ulang suatu file, file itu akan selalu dibuat ulang (lihat Aturan titik dua ganda). Dalam kasus makefile, sebuah makefile yang memiliki aturan titik dua ganda dengan resep tetapi tanpa prasyarat akan dibuat ulang setiap kali make dijalankan, lalu dibuat ulang lagi setelah make memulai dari awal dan membaca ulang makefile. Hal ini akan menyebabkan perulangan tak terhingga: make akan terus-menerus membuat ulang makefile dan memulai ulang, dan tidak pernah melakukan hal lain. Karena itu, untuk menghindarinya, make tidak akan mencoba membuat ulang makefile yang ditentukan sebagai target dari aturan titik dua ganda dengan resep tetapi tanpa prasyarat.
Target semu (phony) (lihat Target semu (phony)) memberikan efek yang sama: target semu tidak pernah dianggap mutakhir, sehingga file yang disertakan dan ditandai semu akan menyebabkan make memulai ulang tanpa henti. Untuk menghindarinya, make tidak akan mencoba membuat ulang makefile yang ditandai semu.
Anda dapat memanfaatkan sifat ini untuk mengoptimalkan waktu startup: jika Anda tahu Makefile Anda tidak perlu dibuat ulang, Anda dapat mencegah make mencoba membuatnya ulang dengan menambahkan salah satu dari berikut.
.PHONY: Makefile
atau:
Makefile:: ;
Jika Anda tidak menentukan makefile apa pun untuk dibaca dengan opsi ‘-f’ atau ‘--file’, make akan mencoba nama makefile default; lihat Nama yang harus diberikan untuk makefile Anda. Berbeda dengan makefile yang diminta secara eksplisit dengan opsi ‘-f’ atau ‘--file’, make tidak yakin bahwa makefile default ini seharusnya ada. Namun, jika makefile default tidak ada tetapi dapat dibuat dengan menjalankan aturan make, Anda mungkin ingin aturan itu dijalankan agar makefile tersebut dapat digunakan.
Karena itu, jika tidak satu pun makefile default yang ada, make akan mencoba membuatnya satu per satu, sampai berhasil membuat salah satunya atau kehabisan nama yang bisa dicoba. Perlu dicatat, bukan error jika make tidak dapat menemukan atau membuat makefile apa pun; makefile tidak selalu diperlukan.
Ketika Anda menggunakan opsi ‘-t’ atau ‘--touch’ (lihat Tindakan selain menjalankan resep), Anda tentu tidak ingin menggunakan makefile yang usang untuk menentukan target mana yang akan di-touch. Karena itu opsi ‘-t’ tidak memengaruhi pembaruan makefile; makefile tetap benar-benar diperbarui meskipun ‘-t’ ditentukan. Demikian pula, ‘-q’ (atau ‘--question’) dan ‘-n’ (atau ‘--just-print’) tidak mencegah pembaruan makefile, karena makefile yang usang akan menghasilkan output yang salah untuk target lain. Oleh karena itu, ‘make -f mfile -n foo’ akan memperbarui mfile, membacanya, lalu menampilkan (tanpa menjalankannya) resep untuk memperbarui foo dan prasyaratnya. Resep yang ditampilkan untuk foo adalah resep yang ditentukan oleh isi mfile yang telah diperbarui.
Namun, dalam beberapa kasus Anda mungkin ingin mencegah bahkan pembaruan makefile itu sendiri. Anda dapat melakukannya dengan menentukan makefile itu sebagai sasaran pada baris perintah, selain menentukannya sebagai makefile. Ketika nama makefile ditentukan secara eksplisit sebagai sasaran, opsi seperti ‘-t’ juga berlaku untuknya.
Karena itu, ‘make -f mfile -n mfile foo’ akan membaca makefile mfile, menampilkan resep yang diperlukan untuk memperbaruinya (tanpa benar-benar menjalankannya), lalu menampilkan resep yang diperlukan untuk memperbarui foo (juga tanpa menjalankannya). Resep untuk foo adalah resep yang ditentukan oleh isi mfile yang sudah ada.
Terkadang berguna untuk memiliki makefile yang sebagian besar isinya sama dengan makefile lain. Anda sering dapat menggunakan direktif ‘include’ untuk menyertakan satu makefile ke makefile lain, lalu menambahkan target atau definisi variabel lagi. Namun, tidak sah jika dua makefile memberikan resep berbeda untuk target yang sama. Meski begitu, ada cara lain.
Dalam makefile yang menyertakan (yaitu, makefile yang ingin menyertakan makefile lain), Anda dapat menggunakan aturan pola yang cocok dengan apa saja untuk menyatakan bahwa untuk membuat ulang target apa pun yang tidak dapat dibuat dari informasi dalam makefile yang menyertakan itu, make harus melihat ke makefile lain. Lihat Mendefinisikan dan mendefinisikan ulang aturan pola untuk informasi lebih lanjut tentang aturan pola.
Misalnya, andaikan Anda memiliki makefile bernama Makefile yang menunjukkan cara membuat target ‘foo’ (dan target lainnya). Di sini, Anda dapat menulis makefile bernama GNUmakefile yang berisi:
foo:
frobnicate > foo
%: force
@$(MAKE) -f Makefile $@
force: ;
Jika Anda mengetik ‘make foo’, make akan menemukan dan membaca GNUmakefile, lalu mengetahui bahwa untuk membuat foo, cukup menjalankan resep ‘frobnicate > foo’. Jika Anda mengetik ‘make bar’, make tidak menemukan cara membuat bar di GNUmakefile, sehingga menggunakan resep dari aturan pola: ‘make -f Makefile bar’. Jika Makefile menyediakan aturan untuk memperbarui bar, make akan menerapkan aturan itu. Begitu pula untuk target lain mana pun yang tidak dijelaskan cara membuatnya oleh GNUmakefile.
Mekanisme ini berfungsi karena pola pada aturan pola tersebut hanyalah ‘%’, sehingga cocok dengan target apa pun. Aturan ini menentukan prasyarat force, yang memastikan resep akan tetap dijalankan meskipun file target sudah ada. Kita memberikan target force resep kosong agar make tidak mencari aturan implisit untuk membangunnya—jika tidak, make akan menerapkan aturan yang cocok dengan apa saja itu juga pada force sendiri dan menciptakan lingkaran prasyarat!
make membaca makefile
GNU make bekerja dalam dua tahap (fase) yang jelas berbeda. Pada tahap pertama, ia membaca semua makefile, makefile yang disertakan, dan sebagainya, lalu menginternalisasi semua variabel beserta nilainya, serta aturan implisit dan eksplisit, dan membangun graf dependensi dari semua target beserta prasyaratnya. Pada tahap kedua, make menggunakan data yang telah diinternalisasi ini untuk menentukan target mana yang perlu diperbarui dan menjalankan resep yang diperlukan untuk memperbaruinya.
Memahami pendekatan dua tahap ini penting karena berdampak langsung pada kapan ekspansi variabel dan fungsi terjadi; hal ini sering menjadi sumber kebingungan saat menulis makefile. Berikut adalah ringkasan berbagai konstruksi yang dapat muncul dalam makefile, beserta tahap terjadinya ekspansi untuk setiap bagian konstruksi tersebut.
Kita katakan ekspansi terjadi secara langsung (immediate) jika terjadi pada tahap pertama: make akan mengekspansi bagian konstruksi itu saat makefile diurai. Kita katakan ekspansi bersifat tertunda (deferred) jika tidak langsung. Ekspansi bagian konstruksi yang tertunda ditangguhkan hingga ekspansi itu digunakan: baik saat dirujuk dalam konteks langsung, maupun saat dibutuhkan pada tahap kedua.
Anda mungkin belum terbiasa dengan sebagian konstruksi ini. Anda dapat merujuk kembali ke bagian ini seiring semakin mengenalnya di bab-bab selanjutnya.
Definisi variabel diuraikan sebagai berikut.
immediate = deferred immediate ?= deferred immediate := immediate immediate ::= immediate immediate :::= immediate-with-escape immediate += deferred or immediate immediate != immediate define immediate deferred endef define immediate = deferred endef define immediate ?= deferred endef define immediate := immediate endef define immediate ::= immediate endef define immediate :::= immediate-with-escape endef define immediate += deferred or immediate endef define immediate != immediate endef
Untuk operator penambahan (append) ‘+=’, ruas kanan dianggap langsung jika variabel itu sebelumnya ditetapkan sebagai variabel sederhana (‘:=’ atau ‘::=’), dan tertunda jika tidak.
Untuk operator ‘:::=’ (ekspansi langsung dengan escape), nilai di ruas kanan langsung diekspansi tetapi kemudian di-escape (artinya, semua kemunculan $ pada hasil ekspansi diganti dengan $$).
Untuk operator penetapan shell ‘!=’, ruas kanan langsung dievaluasi dan diserahkan ke shell. Hasilnya disimpan dalam variabel yang tertulis di ruas kiri, dan variabel itu dianggap sebagai variabel ekspansi rekursif (sehingga akan dievaluasi ulang setiap kali dirujuk).
Direktif kondisional diuraikan secara langsung. Artinya, misalnya, variabel otomatis tidak dapat digunakan dalam direktif kondisional, karena variabel otomatis baru ditetapkan saat resep aturan tersebut dijalankan. Jika Anda perlu menggunakan variabel otomatis dalam suatu direktif kondisional, Anda harus memindahkan kondisi itu ke dalam resep dan menggunakan sintaksis kondisional shell sebagai gantinya.
Aturan, apa pun bentuknya, selalu diekspansi dengan cara yang sama.
immediate : immediate ; deferred
deferred
Artinya, bagian target dan prasyarat diekspansi secara langsung, sedangkan resep untuk membangun target selalu tertunda. Hal ini berlaku untuk aturan eksplisit, aturan pola, aturan sufiks, aturan pola statis, dan definisi prasyarat sederhana.
GNU make mengurai makefile baris demi baris. Penguraian berlangsung melalui langkah-langkah berikut.
make membaca makefile).
Konsekuensi penting dari hal ini adalah bahwa suatu makro dapat diekspansi menjadi seluruh aturan, jika panjangnya satu baris. Contoh berikut akan berfungsi.
myrule = target : ; echo built $(myrule)
Namun, contoh berikut tidak akan berfungsi karena make tidak membagi ulang baris setelah mengekspansinya.
define myrule
target:
echo built
endef
$(myrule)
Makefile di atas menghasilkan definisi target ‘target’ dengan prasyarat ‘echo’ dan ‘built’, bukan aturan dengan resep, seolah-olah makefile itu berisi target: echo built. Baris baru yang masih tersisa dalam suatu baris setelah ekspansi selesai diabaikan sebagai whitespace biasa.
Untuk mengekspansi makro multi-baris dengan benar, Anda harus menggunakan fungsi eval. Ini membuat parser make dijalankan pada hasil makro yang telah diekspansi (lihat Fungsi eval).
Sejauh ini kita telah mempelajari bahwa GNU make bekerja dalam dua tahap yang jelas berbeda: tahap pembacaan dan tahap pembaruan target (lihat Cara make membaca makefile). GNU Make juga memiliki kemampuan untuk mengaktifkan ekspansi kedua pada prasyarat (saja) untuk sebagian atau semua target yang didefinisikan dalam makefile. Agar ekspansi kedua ini terjadi, target khusus .SECONDEXPANSION harus didefinisikan sebelum daftar prasyarat pertama yang memanfaatkan fitur ini.
Jika .SECONDEXPANSION didefinisikan, maka ketika GNU make perlu memeriksa prasyarat suatu target, prasyarat itu akan diekspansi untuk kedua kalinya. Pada kebanyakan kasus, ekspansi sekunder ini tidak berpengaruh apa pun, karena semua referensi variabel dan fungsi sudah diekspansi saat penguraian awal makefile. Karena itu, untuk memanfaatkan tahap ekspansi sekunder pada parser, Anda perlu meng-escape referensi variabel atau fungsi dalam makefile. Dalam hal ini, ekspansi pertama hanya melepaskan escape dari referensi tersebut tanpa mengekspansinya, dan ekspansi diserahkan ke tahap ekspansi sekunder. Sebagai contoh, perhatikan makefile berikut:
.SECONDEXPANSION: ONEVAR = onefile TWOVAR = twofile myfile: $(ONEVAR) $$(TWOVAR)
Setelah tahap ekspansi pertama, daftar prasyarat target myfile akan menjadi onefile dan $(TWOVAR). Referensi variabel pertama (tidak di-escape) ke ONEVAR diekspansi, sedangkan referensi variabel kedua (di-escape) hanya dilepas escape-nya tanpa dikenali sebagai referensi variabel. Kemudian selama ekspansi sekunder, kata pertama diekspansi lagi, tetapi karena tidak mengandung referensi variabel atau fungsi apa pun, nilainya tetap onefile. Sementara itu, kata kedua kini menjadi referensi biasa ke variabel TWOVAR, yang diekspansi menjadi nilai twofile. Hasil akhirnya, ada dua prasyarat: onefile dan twofile.
Tentu saja, ini bukan contoh yang terlalu menarik karena hasil yang sama dapat lebih mudah dicapai dengan menuliskan kedua variabel itu tanpa di-escape di daftar prasyarat. Perbedaannya menjadi jelas ketika variabel ditetapkan ulang. Perhatikan contoh berikut:
.SECONDEXPANSION: AVAR = top onefile: $(AVAR) twofile: $$(AVAR) AVAR = bottom
Di sini, prasyarat onefile langsung diekspansi menjadi nilai top, sedangkan prasyarat twofile baru diekspansi sepenuhnya pada ekspansi sekunder dan menghasilkan nilai bottom.
Ini sedikit lebih menarik, tetapi kekuatan sesungguhnya dari fitur ini baru terlihat saat Anda menyadari bahwa ekspansi sekunder selalu terjadi dalam cakupan variabel otomatis target tersebut. Artinya, Anda dapat menggunakan variabel seperti $@ dan $* selama ekspansi sekunder, dan variabel tersebut akan memiliki nilai yang diharapkan, sama persis seperti dalam resep. Yang perlu Anda lakukan hanyalah meng-escape $ untuk menunda ekspansi. Selain itu, ekspansi sekunder terjadi baik untuk aturan eksplisit maupun aturan implisit (pola). Dengan mengetahui hal ini, kegunaan fitur ini bertambah secara dramatis. Sebagai contoh:
.SECONDEXPANSION: main_OBJS := main.o try.o test.o lib_OBJS := lib.o api.o main lib: $$($$@_OBJS)
Di sini, setelah ekspansi pertama, prasyarat target main dan lib keduanya menjadi $($@_OBJS). Selama ekspansi sekunder, variabel $@ ditetapkan ke nama target, sehingga hasil ekspansi untuk target main menjadi $(main_OBJS), yaitu main.o try.o test.o, sedangkan hasil ekspansi sekunder untuk target lib menjadi $(lib_OBJS), yaitu lib.o api.o.
Fungsi juga dapat dicampurkan di sini, selama di-escape dengan tepat.
main_SRCS := main.c try.c test.c lib_SRCS := lib.c api.c .SECONDEXPANSION: main lib: $$(patsubst %.c,%.o,$$($$@_SRCS))
Pada versi ini, pengguna dapat menentukan file sumber alih-alih file objek, tetapi hasil daftar prasyaratnya sama dengan contoh sebelumnya.
Evaluasi variabel otomatis selama tahap ekspansi sekunder, terutama variabel nama target $$@, berperilaku serupa dengan evaluasi dalam resep. Namun, ada beberapa perbedaan halus dan “kasus khusus” yang terkait dengan berbagai jenis definisi aturan yang dipahami make. Rincian penggunaan berbagai variabel otomatis dijelaskan di bawah ini.
Pada ekspansi sekunder aturan eksplisit, $$@ dan $$% masing-masing dievaluasi menjadi nama file target dan, jika target merupakan anggota arsip (archive), nama anggota target tersebut. Variabel $$< dievaluasi menjadi prasyarat pertama pada aturan pertama untuk target ini. $$^ dan $$+ dievaluasi menjadi daftar semua prasyarat dari aturan-aturan yang sudah muncul sebelumnya untuk target yang sama ($$+ termasuk duplikat, $$^ tidak). Contoh berikut akan membantu memahami perilaku ini.
.SECONDEXPANSION: foo: foo.1 bar.1 $$< $$^ $$+ # baris #1 foo: foo.2 bar.2 $$< $$^ $$+ # baris #2 foo: foo.3 bar.3 $$< $$^ $$+ # baris #3
Pada daftar prasyarat pertama, ketiga variabel ($$<, $$^, dan $$+) semuanya diekspansi menjadi string kosong. Pada yang kedua, nilainya masing-masing menjadi foo.1, foo.1 bar.1, dan foo.1 bar.1. Pada yang ketiga, nilainya masing-masing menjadi foo.1, foo.1 bar.1 foo.2 bar.2, dan foo.1 bar.1 foo.2 bar.2 foo.1 foo.1 bar.1 foo.1 bar.1.
Aturan mengalami ekspansi sekunder sesuai urutan penulisannya dalam makefile, kecuali aturan yang memiliki resep selalu dievaluasi terakhir.
Variabel $$? dan $$* tidak tersedia dan diekspansi menjadi string kosong.
Aturan ekspansi sekunder untuk aturan pola statis sama dengan aturan eksplisit di atas, dengan satu pengecualian: pada aturan pola statis, variabel $$* ditetapkan ke stem pola. Sama seperti aturan eksplisit, $$? tidak tersedia dan diekspansi menjadi string kosong.
Saat make mencari aturan implisit, ia terlebih dahulu menyisipkan stem, kemudian melakukan ekspansi sekunder untuk semua aturan yang polanya cocok dengan pola target. Nilai variabel otomatis diturunkan dengan cara yang sama seperti pada aturan pola statis. Sebagai contoh, perhatikan yang berikut.
.SECONDEXPANSION: foo: bar foo foz: fo%: bo% %oo: $$< $$^ $$+ $$*
Ketika aturan implisit ini dicoba untuk target foo, $$< diekspansi menjadi bar, $$^ diekspansi menjadi bar boo, $$+ juga diekspansi menjadi bar boo, dan $$* diekspansi menjadi f.
Perhatikan bahwa awalan direktori (D) yang dijelaskan pada Algoritma pencarian aturan implisit ditambahkan (setelah ekspansi) ke semua pola dalam daftar prasyarat. Sebagai contoh, perhatikan yang berikut.
.SECONDEXPANSION:
/tmp/foo.o:
%.o: $$(addsuffix /%.c,foo bar) foo.h
@echo $^
Daftar prasyarat yang ditampilkan, setelah ekspansi sekunder dan rekonstruksi awalan direktori, akan menjadi /tmp/foo/foo.c /tmp/bar/foo.c foo.h. Jika Anda tidak menginginkan rekonstruksi ini, Anda dapat menggunakan $$* alih-alih % dalam daftar prasyarat.
1 GNU make yang dikompilasi untuk MS-DOS dan MS-Windows berperilaku seolah-olah prefix didefinisikan sebagai akar pohon direktori DJGPP.