Resep (recipe) suatu aturan terdiri atas satu baris perintah shell atau lebih yang harus dijalankan, satu demi satu, sesuai urutan penulisannya. Biasanya, hasil dari menjalankan perintah-perintah ini adalah target aturan tersebut menjadi mutakhir.
Pengguna memakai berbagai program shell yang berbeda-beda, tetapi resep dalam makefile selalu diinterpretasikan oleh /bin/sh, kecuali jika makefile menentukan sebaliknya. Lihat bagian Eksekusi resep.
makefile memiliki sifat yang agak tidak biasa, yaitu dalam satu file sebenarnya terdapat dua sintaksis berbeda yang bercampur. Sebagian besar makefile menggunakan sintaksis make (lihat Menulis makefile). Namun, resep dimaksudkan untuk diinterpretasikan oleh shell, sehingga ditulis dengan sintaksis shell. Program make tidak berusaha memahami sintaksis shell: ia hanya melakukan sejumlah kecil transformasi khusus pada isi resep sebelum menyerahkannya ke shell.
Setiap baris dalam resep harus dimulai dengan tab (atau karakter pertama dalam nilai variabel .RECIPEPREFIX; lihat Variabel khusus lainnya), kecuali baris pertama resep boleh dilekatkan pada baris target-dan-prasyarat (prerequisite) dengan tanda titik koma di antaranya. Baris mana pun dalam makefile yang dimulai dengan tab dan muncul dalam “konteks aturan” (yaitu, setelah suatu aturan dimulai hingga muncul aturan atau definisi variabel lain) akan dianggap sebagai bagian dari resep untuk aturan tersebut. Baris kosong dan baris yang hanya berisi komentar boleh muncul di antara baris-baris resep; baris-baris tersebut diabaikan.
Beberapa konsekuensi dari aturan-aturan ini adalah:
make. Komentar tersebut akan diteruskan apa adanya ke shell. Apakah shell memperlakukannya sebagai komentar atau tidak, itu bergantung pada shell yang Anda gunakan.
make, dan diteruskan ke shell.
ifdef, ifeq, dan sebagainya; lihat Sintaksis kondisional) dalam “konteks aturan” yang karakter pertama pada barisnya diindentasi dengan tab akan dianggap sebagai bagian dari resep dan diteruskan ke shell.
Salah satu dari sedikit hal yang membuat make benar-benar menginterpretasikan resep adalah memeriksa keberadaan garis miring terbalik (backslash) tepat sebelum baris baru. Sama seperti dalam sintaksis makefile pada umumnya, satu baris resep secara logis dapat dipecah menjadi beberapa baris fisik dalam makefile dengan meletakkan backslash sebelum setiap baris baru. Rangkaian baris seperti ini dianggap sebagai satu baris resep, dan satu instans shell akan dijalankan untuk mengeksekusinya.
Namun, berbeda dengan perlakuannya di tempat lain dalam makefile (lihat Membagi baris panjang), pasangan backslash/baris-baru tidak dihilangkan dari resep. Baik backslash maupun karakter baris baru tetap dipertahankan dan diteruskan ke shell. Bagaimana pasangan backslash/baris-baru ini diinterpretasikan bergantung pada shell yang Anda gunakan. Jika karakter pertama pada baris berikutnya setelah backslash/baris-baru adalah karakter prefiks resep (secara default berupa tab; lihat Variabel khusus lainnya), maka karakter tersebut (dan hanya karakter itu saja) akan dihilangkan. Whitespace tidak akan pernah ditambahkan ke dalam resep.
Sebagai contoh, resep untuk target all pada makefile berikut ini:
all :
@echo no\
space
@echo no\
space
@echo one \
space
@echo one\
space
Ini terdiri atas empat perintah shell yang terpisah, dengan output sebagai berikut:
nospace nospace one space one space
Sebagai contoh yang sedikit lebih rumit, perhatikan makefile berikut ini:
all : ; @echo 'hello \
world' ; echo "hello \
world"
Ini akan menjalankan satu shell dengan perintah:
echo 'hello \
world' ; echo "hello \
world"
Kemudian, sesuai aturan pengutipan (quoting) shell, akan dihasilkan output berikut:
hello \ world hello world
Perhatikan bahwa pasangan backslash/baris-baru dihilangkan di dalam string yang diapit tanda kutip ganda (‘…’), tetapi tidak dihilangkan di dalam string yang diapit tanda kutip tunggal ('…'). Beginilah cara shell default (/bin/sh) menangani pasangan backslash/baris-baru. Jika Anda menentukan shell lain dalam makefile Anda, penanganannya mungkin berbeda.
Terkadang Anda ingin memecah baris panjang di dalam tanda kutip tunggal, tetapi tidak ingin backslash/baris-baru muncul di dalam konten yang dikutip. Hal ini sering terjadi ketika meneruskan skrip ke bahasa seperti Perl, karena backslash tambahan di dalam skrip dapat mengubah maknanya atau bahkan menyebabkan kesalahan sintaksis. Salah satu cara sederhana untuk mengatasinya adalah dengan menempatkan string yang dikutip, atau bahkan seluruh perintah, ke dalam variabel make, lalu menggunakan variabel tersebut dalam resep. Dengan cara ini, aturan pengutipan baris baru untuk makefile akan berlaku, dan backslash/baris-baru akan dihilangkan. Jika kita menulis ulang contoh di atas dengan cara ini:
HELLO = 'hello \ world' all : ; @echo $(HELLO)
Kita akan memperoleh output seperti berikut:
hello world
Jika Anda mau, Anda juga dapat menggunakan variabel khusus target (lihat Nilai variabel khusus target) untuk memperoleh korespondensi yang lebih erat antara variabel dan resep yang menggunakannya.
Cara lain make memproses resep adalah dengan mengekspansi referensi variabel apa pun di dalamnya (lihat Dasar-dasar referensi variabel). Hal ini terjadi setelah make selesai membaca semua makefile dan target ditentukan sudah usang; jadi, resep untuk target yang tidak dibangun ulang tidak akan pernah diekspansi.
Referensi variabel dan fungsi dalam resep memiliki sintaksis dan semantik yang identik dengan referensi di tempat lain dalam makefile. Aturan pengutipannya juga sama: jika Anda ingin tanda dolar muncul dalam resep, Anda harus menggandakannya (‘$$’). Untuk shell seperti shell default, yang menggunakan tanda dolar untuk menandai variabel, penting untuk membedakan dengan jelas dalam pikiran Anda apakah variabel yang ingin Anda rujuk adalah variabel make (gunakan satu tanda dolar) atau variabel shell (gunakan dua tanda dolar). Contohnya:
LIST = one two three
all:
for i in $(LIST); do \
echo $$i; \
done
Ini menghasilkan perintah berikut yang diteruskan ke shell:
for i in one two three; do \
echo $i; \
done
yang menghasilkan hasil yang diharapkan:
one two three
Biasanya make mencetak setiap baris resep sebelum baris itu dieksekusi. Kita menyebut ini echo karena tampilannya seolah-olah Anda mengetik baris-baris itu sendiri.
Jika suatu baris dimulai dengan ‘@’, penampilan echo baris tersebut ditiadakan. ‘@’ dibuang sebelum baris itu diteruskan ke shell. Biasanya ini digunakan untuk perintah yang satu-satunya efeknya adalah menampilkan sesuatu, misalnya perintah echo untuk menunjukkan kemajuan proses dalam makefile:
@echo About to make distribution files
Jika make diberi flag ‘-n’ atau ‘--just-print’, ia hanya menampilkan echo sebagian besar resep tanpa mengeksekusinya (lihat Ringkasan opsi). Dalam hal ini, bahkan baris resep yang dimulai dengan ‘@’ pun ikut dicetak. Flag ini berguna untuk mengetahui resep mana yang menurut make diperlukan tanpa benar-benar menjalankannya.
Flag ‘-s’ atau ‘--silent’ pada make mencegah semua penampilan echo, seolah-olah semua resep dimulai dengan ‘@’. Aturan dalam makefile untuk target khusus .SILENT tanpa prasyarat memiliki efek yang sama (lihat Nama target bawaan khusus).
Ketika tiba waktunya mengeksekusi resep untuk memperbarui suatu target, resep tersebut dieksekusi dengan menjalankan sub-shell baru untuk setiap baris resep, kecuali target khusus .ONESHELL sedang aktif (lihat Menjalankan dalam satu shell). (Pada praktiknya, make kadang mengambil jalan pintas yang tidak memengaruhi hasilnya.)
Perhatikan: hal ini berarti bahwa menetapkan variabel shell atau menjalankan perintah shell seperti cd, yang menetapkan konteks lokal untuk masing-masing proses, tidak akan memengaruhi baris-baris berikutnya dalam resep. (Catatan penerjemah: cd dan perintah sejenisnya bersifat independen pada tiap subshell, sehingga tidak diwariskan ke baris berikutnya.) Jika Anda ingin menggunakan cd agar memengaruhi pernyataan berikutnya, letakkan kedua pernyataan tersebut dalam satu baris resep. Dengan begitu, make akan menjalankan satu shell untuk mengeksekusi seluruh baris tersebut, dan shell akan mengeksekusi pernyataan-pernyataan itu secara berurutan. Contohnya:
foo : bar/lose
cd $(<D) && gobble $(<F) > ../$@
Di sini kita menggunakan operator AND shell (&&) sehingga jika perintah cd gagal, skrip akan gagal tanpa mencoba menjalankan perintah gobble di direktori yang salah, yang dapat menimbulkan masalah (dalam kasus ini, setidaknya ../foo pasti akan terpotong isinya hingga kosong).
Terkadang Anda lebih suka semua baris dalam resep diteruskan ke satu instans shell. Pada umumnya ada dua situasi yang membuat ini berguna. Pertama, ini dapat meningkatkan kinerja pada makefile yang resepnya terdiri atas banyak baris perintah, dengan menghindari proses tambahan. Kedua, Anda mungkin ingin baris baru disertakan dalam perintah resep Anda (misalnya jika Anda menggunakan interpreter yang sangat berbeda sebagai SHELL). Jika target khusus .ONESHELL muncul di mana pun dalam makefile, maka semua baris resep untuk setiap target akan diberikan ke satu instans shell. Baris baru di antara baris-baris resep akan dipertahankan. Contohnya:
.ONESHELL:
foo : bar/lose
cd $(<D)
gobble $(<F) > ../$@
Ini sekarang akan bekerja sebagaimana diharapkan, meskipun perintah-perintahnya berada pada baris resep yang berbeda.
Jika .ONESHELL ditentukan, hanya baris pertama resep yang akan diperiksa untuk karakter prefiks khusus (‘@’, ‘-’, dan ‘+’). Baris-baris berikutnya akan menyertakan karakter khusus tersebut dalam baris resep saat SHELL dijalankan. Jika Anda ingin resep Anda dimulai dengan salah satu karakter khusus ini, Anda perlu mengatur agar karakter tersebut bukan karakter pertama pada baris pertama, misalnya dengan menambahkan komentar atau semacamnya. Sebagai contoh, contoh berikut akan menjadi kesalahan sintaksis dalam Perl karena ‘@’ pertama dihilangkan oleh make:
.ONESHELL:
SHELL = /usr/bin/perl
.SHELLFLAGS = -e
show :
@f = qw(a b c);
print "@f\n";
Namun, kedua alternatif berikut akan bekerja dengan benar:
.ONESHELL:
SHELL = /usr/bin/perl
.SHELLFLAGS = -e
show :
# Pastikan "@" bukan karakter pertama pada baris pertama
@f = qw(a b c);
print "@f\n";
Atau
.ONESHELL:
SHELL = /usr/bin/perl
.SHELLFLAGS = -e
show :
my @f = qw(a b c);
print "@f\n";
Sebagai fitur khusus, jika SHELL ditentukan sebagai shell bergaya POSIX, karakter prefiks khusus pada baris resep “internal” (baris kedua dan seterusnya) akan dihilangkan sebelum resep diproses. Fitur ini dimaksudkan agar makefile yang sudah ada dapat menambahkan target khusus .ONESHELL dan tetap berjalan dengan benar tanpa modifikasi besar-besaran. Karena karakter prefiks khusus ini tidak sah di awal baris pada skrip shell POSIX, tidak ada fungsi yang hilang. Contohnya, ini akan bekerja sebagaimana diharapkan:
.ONESHELL:
foo : bar/lose
@cd $(@D)
@gobble $(@F) > ../$@
Meskipun ada fitur khusus ini, makefile dengan .ONESHELL tetap dapat berperilaku berbeda dengan cara yang bisa terlihat jelas. Misalnya, biasanya jika ada baris mana pun dalam resep yang gagal, hal itu menyebabkan aturan tersebut gagal dan tidak ada lagi baris resep yang diproses. Di bawah .ONESHELL, kegagalan pada baris resep mana pun kecuali baris terakhir tidak akan disadari oleh make. Anda dapat mengubah .SHELLFLAGS untuk menambahkan opsi -e ke shell, yang akan menyebabkan kegagalan di mana pun dalam baris perintah membuat shell gagal, tetapi hal ini sendiri dapat mengubah perilaku resep Anda. Pada akhirnya Anda mungkin perlu memperkuat (harden) baris-baris resep Anda agar dapat bekerja dengan .ONESHELL.
Program yang digunakan sebagai shell diambil dari variabel SHELL. Jika variabel ini tidak ditetapkan dalam makefile Anda, program /bin/sh akan digunakan sebagai shell. Argumen yang diteruskan ke shell diambil dari variabel .SHELLFLAGS. Nilai default .SHELLFLAGS biasanya adalah -c, atau -ec dalam mode sesuai-POSIX (POSIX-conforming).
Berbeda dengan kebanyakan variabel, variabel SHELL tidak pernah ditetapkan dari lingkungan. Ini karena variabel lingkungan SHELL digunakan untuk menentukan pilihan pribadi Anda atas program shell untuk penggunaan interaktif. Akan sangat buruk jika pilihan pribadi semacam ini memengaruhi cara kerja makefile. Lihat Variabel dari lingkungan.
Selain itu, ketika Anda menetapkan SHELL dalam makefile Anda, nilai tersebut tidak diekspor ke lingkungan untuk baris-baris resep yang dijalankan oleh make. Sebagai gantinya, nilai yang diwariskan dari lingkungan pengguna (jika ada) yang diekspor. Anda dapat mengubah perilaku ini dengan mengekspor SHELL secara eksplisit (lihat Menyampaikan variabel ke sub-make), sehingga nilai tersebut dipaksa untuk diteruskan sebagai variabel lingkungan ke baris-baris resep.
Namun, pada MS-DOS dan MS-Windows, nilai SHELL dari lingkungan tetap digunakan, karena pada sistem-sistem tersebut kebanyakan pengguna tidak menetapkan variabel ini, sehingga jika variabel tersebut ditetapkan, kemungkinan besar memang ditetapkan khusus untuk digunakan oleh make. Pada MS-DOS, jika pengaturan SHELL tidak cocok untuk make, Anda dapat menetapkan variabel MAKESHELL ke shell yang ingin digunakan oleh make; jika ditetapkan, variabel tersebut akan digunakan sebagai shell, menggantikan nilai SHELL.
Memilih shell di MS-DOS dan MS-Windows jauh lebih rumit dibandingkan pada sistem lain.
Pada MS-DOS, jika SHELL tidak ditetapkan, nilai variabel COMSPEC (yang selalu ditetapkan) akan digunakan sebagai gantinya.
Pemrosesan baris yang menetapkan variabel SHELL dalam makefile berbeda pada MS-DOS. Shell bawaan, command.com, memiliki fungsionalitas yang sangat terbatas, sehingga banyak pengguna make cenderung memasang shell pengganti. Oleh karena itu, pada MS-DOS, make memeriksa nilai SHELL dan mengubah perilakunya berdasarkan apakah nilai tersebut menunjuk ke shell bergaya Unix atau bergaya DOS. Ini memungkinkan fungsionalitas yang memadai meskipun SHELL menunjuk ke command.com.
Jika SHELL menunjuk ke shell bergaya Unix, make pada MS-DOS juga memeriksa apakah shell tersebut benar-benar dapat ditemukan; jika tidak, baris yang menetapkan SHELL akan diabaikan. Pada MS-DOS, GNU make mencari shell di tempat-tempat berikut:
SHELL. Misalnya, jika makefile menentukan ‘SHELL = /bin/sh’, make akan mencari di direktori /bin pada drive saat ini.
PATH, secara berurutan.
Di setiap direktori yang diperiksa, make pertama-tama akan mencari file dengan nama tersebut persis (sh pada contoh di atas). Jika tidak ditemukan, make juga akan mencari di direktori tersebut file dengan salah satu ekstensi yang dikenal sebagai penanda file yang dapat dieksekusi. Misalnya .exe, .com, .bat, .btm, .sh, dan beberapa lainnya.
Jika salah satu upaya ini berhasil, nilai SHELL akan ditetapkan ke nama path lengkap shell yang ditemukan. Namun, jika tidak satu pun ditemukan, nilai SHELL tidak akan diubah, sehingga baris yang menetapkannya pada dasarnya diabaikan. Ini dimaksudkan agar make hanya mendukung fitur khusus shell bergaya Unix jika shell semacam itu memang terpasang pada sistem tempat make dijalankan.
Perhatikan bahwa pencarian shell yang diperluas ini hanya berlaku untuk kasus ketika SHELL ditetapkan dari Makefile; jika ditetapkan di lingkungan atau baris perintah, Anda diharapkan menetapkannya dengan nama path lengkap shell, persis seperti pada Unix.
Akibat dari pemrosesan khusus DOS di atas adalah bahwa makefile yang berisi ‘SHELL = /bin/sh’ (seperti banyak makefile Unix) akan tetap berjalan tanpa perubahan pada MS-DOS jika Anda memasang, misalnya, sh.exe di suatu direktori sepanjang PATH Anda.
GNU make tahu cara mengeksekusi beberapa resep sekaligus. Biasanya, make hanya mengeksekusi satu resep pada satu waktu, menunggu resep itu selesai sebelum mengeksekusi resep berikutnya. Namun, opsi ‘-j’ atau ‘--jobs’ memerintahkan make untuk mengeksekusi banyak resep secara bersamaan. Anda dapat mencegah paralelisme untuk sebagian atau semua target dari dalam makefile (lihat Menonaktifkan eksekusi paralel).
Pada MS-DOS, opsi ‘-j’ tidak berpengaruh, karena sistem tersebut tidak mendukung multi-pemrosesan (multi-processing).
Jika opsi ‘-j’ diikuti oleh sebuah bilangan bulat, itu adalah jumlah resep yang dieksekusi sekaligus; ini disebut jumlah slot job (job slot). Jika tidak ada sesuatu yang menyerupai bilangan bulat setelah opsi ‘-j’, tidak ada batas untuk jumlah slot job. Jumlah default slot job adalah satu, yang berarti eksekusi serial (satu per satu).
Menangani pemanggilan make secara rekursif memunculkan masalah tersendiri untuk eksekusi paralel. Untuk informasi lebih lanjut, lihat Menyampaikan opsi ke sub-make.
Jika suatu resep gagal (dihentikan oleh sinyal atau keluar dengan status bukan nol) dan error untuk resep tersebut tidak diabaikan (lihat Error dalam resep), sisa baris resep untuk membuat ulang target yang sama tidak akan dijalankan. Jika suatu resep gagal dan opsi ‘-k’ atau ‘--keep-going’ tidak diberikan (lihat Ringkasan opsi), make membatalkan eksekusi. Jika make berhenti karena alasan apa pun (termasuk sinyal) sementara masih ada proses anak yang berjalan, ia akan menunggu proses-proses tersebut selesai sebelum benar-benar keluar.
Ketika sistem sedang mengalami beban tinggi, Anda mungkin ingin menjalankan lebih sedikit job dibandingkan saat beban rendah. Anda dapat menggunakan opsi ‘-l’ untuk memerintahkan make membatasi jumlah job yang dijalankan sekaligus, berdasarkan rata-rata beban (load average). Opsi ‘-l’ atau ‘--max-load’ diikuti oleh bilangan floating-point. Contohnya,
-l 2.5
tidak akan membiarkan make memulai lebih dari satu job jika rata-rata beban di atas 2.5. Opsi ‘-l’ tanpa angka setelahnya akan menghapus batas beban, jika sebelumnya ditetapkan dengan opsi ‘-l’ lain.
Lebih tepatnya, ketika make hendak memulai suatu job dan sudah ada setidaknya satu job yang berjalan, ia akan memeriksa rata-rata beban saat ini; jika nilainya tidak lebih rendah daripada batas yang diberikan dengan ‘-l’, make akan menunggu hingga rata-rata beban turun di bawah batas tersebut, atau hingga semua job lain selesai.
Secara default, tidak ada batas beban.
Jika suatu makefile mendefinisikan hubungan dependensi antar-semua targetnya secara lengkap dan akurat, make akan membangun sasaran (goal) dengan benar, apa pun status eksekusi paralel diaktifkan atau tidak. Ini adalah cara ideal untuk menulis makefile.
Namun, terkadang sebagian atau semua target dalam sebuah makefile tidak dapat dieksekusi secara paralel, dan tidak praktis untuk menambahkan prasyarat yang diperlukan agar make mengetahuinya. Dalam kasus seperti itu, makefile dapat menggunakan berbagai cara untuk menonaktifkan eksekusi paralel.
Jika target khusus .NOTPARALLEL tanpa prasyarat ditentukan di mana pun, maka seluruh instans make akan dijalankan secara serial, terlepas dari pengaturan paralel. Contohnya:
all: one two three one two three: ; @sleep 1; echo $@ .NOTPARALLEL:
Apa pun cara make dipanggil, target one, two, dan three akan dijalankan secara serial.
Jika target khusus .NOTPARALLEL memiliki prasyarat, maka setiap prasyarat tersebut akan dianggap sebagai target, dan semua prasyarat dari target-target itu akan dijalankan secara serial. Perhatikan bahwa prasyarat hanya dijalankan secara serial ketika membangun target ini: jika target lain mencantumkan prasyarat yang sama dan tidak termasuk dalam .NOTPARALLEL, prasyarat tersebut mungkin saja dijalankan secara paralel. Contohnya:
all: base notparallel base: one two three notparallel: one two three one two three: ; @sleep 1; echo $@ .NOTPARALLEL: notparallel
Di sini, ‘make -j base’ akan menjalankan target one, two, dan three secara paralel, sedangkan ‘make -j notparallel’ akan menjalankannya secara serial. Jika Anda menjalankan ‘make -j all’, target-target itu akan dijalankan secara paralel karena base mencantumkannya sebagai prasyarat dan tidak diserialkan.
Target .NOTPARALLEL tidak boleh memiliki perintah.
Terakhir, Anda dapat mengontrol serialisasi prasyarat tertentu secara lebih terperinci menggunakan target khusus .WAIT. Ketika target ini muncul dalam daftar prasyarat dan eksekusi paralel diaktifkan, make tidak akan membangun prasyarat mana pun di sebelah kanan .WAIT sampai semua prasyarat di sebelah kiri .WAIT selesai. Contohnya:
all: one two .WAIT three one two three: ; @sleep 1; echo $@
Jika eksekusi paralel diaktifkan, make akan mencoba membangun one dan two secara paralel, tetapi tidak akan mencoba membangun three sampai keduanya selesai.
Sama seperti target yang diberikan ke .NOTPARALLEL, .WAIT hanya berlaku ketika membangun target yang daftar prasyaratnya memuatnya. Jika prasyarat yang sama muncul di target lain tanpa .WAIT, prasyarat tersebut mungkin tetap dijalankan secara paralel. Karena itu, baik .NOTPARALLEL dengan target maupun .WAIT tidak sehandal mendefinisikan hubungan prasyarat dalam mengontrol eksekusi paralel. Meski begitu, keduanya mudah digunakan dan mungkin sudah cukup untuk situasi yang tidak terlalu rumit.
Prasyarat .WAIT tidak akan muncul dalam variabel otomatis mana pun untuk aturan tersebut.
Demi portabilitas, Anda boleh membuat target .WAIT sungguhan dalam makefile Anda, tetapi ini tidak diwajibkan untuk menggunakan fitur ini. Jika Anda membuat target .WAIT, target tersebut tidak boleh memiliki prasyarat atau perintah.
Fitur .WAIT juga diimplementasikan pada versi make lain dan ditetapkan dalam standar POSIX untuk make.
Ketika beberapa resep dijalankan secara paralel, output dari setiap resep muncul segera setelah dihasilkan, sehingga pesan dari resep yang berbeda-beda dapat bercampur, bahkan terkadang muncul pada baris yang sama. Ini dapat membuat output sangat sulit dibaca.
Untuk menghindarinya, Anda dapat menggunakan opsi ‘--output-sync’ (‘-O’). Opsi ini memerintahkan make untuk menyimpan output dari perintah-perintah yang dijalankannya dan mencetak semuanya sekaligus setelah perintah-perintah tersebut selesai. Selain itu, jika ada beberapa pemanggilan make rekursif yang berjalan secara paralel, semuanya akan saling berkomunikasi sehingga hanya satu yang menghasilkan output pada satu waktu.
Jika penampilan direktori kerja diaktifkan (lihat Opsi ‘--print-directory’), pesan “masuk direktori/keluar direktori” akan ditampilkan sebelum dan sesudah setiap kelompok output. Jika Anda tidak ingin melihat pesan-pesan ini, tambahkan opsi ‘--no-print-directory’ ke MAKEFLAGS.
Ada empat tingkat granularitas ketika menyinkronkan output, yang ditentukan dengan memberikan argumen pada opsi (misalnya, ‘-Oline’ atau ‘--output-sync=recurse’).
noneIni adalah default: semua output langsung dikirim begitu dihasilkan, dan tidak ada sinkronisasi yang dilakukan.
lineOutput dari setiap baris resep secara individu dikelompokkan dan dicetak segera setelah baris tersebut selesai. Jika suatu resep terdiri atas beberapa baris, baris-baris itu dapat bercampur dengan baris dari resep lain.
targetOutput dari seluruh resep untuk setiap target dikelompokkan dan dicetak setelah target tersebut selesai. Ini adalah default jika opsi --output-sync atau -O diberikan tanpa argumen.
recurseOutput dari setiap pemanggilan rekursif make dikelompokkan dan dicetak setelah pemanggilan rekursif tersebut selesai.
Apa pun mode yang dipilih, total waktu build akan tetap sama. Yang berbeda hanyalah cara output ditampilkan.
Mode ‘target’ dan ‘recurse’ sama-sama mengumpulkan output dari seluruh resep suatu target dan menampilkannya tanpa terputus ketika resep tersebut selesai. Perbedaan antara keduanya terletak pada cara resep yang berisi pemanggilan rekursif make diperlakukan (lihat Penggunaan rekursif make). Untuk semua resep yang tidak memiliki baris rekursif, mode ‘target’ dan ‘recurse’ berperilaku persis sama.
Jika mode ‘recurse’ dipilih, resep yang berisi pemanggilan make rekursif diperlakukan sama seperti target lain: output dari resep tersebut, termasuk output dari make rekursif, disimpan dan dicetak setelah seluruh resep selesai. Ini memastikan output dari semua target yang dibangun oleh satu instans make rekursif dikelompokkan bersama, yang mungkin membuat output lebih mudah dipahami. Namun, ini juga menyebabkan periode waktu yang panjang selama build tanpa output yang terlihat, diikuti oleh ledakan output dalam jumlah besar. Jika Anda tidak mengamati build saat berlangsung, melainkan melihat log build setelahnya, ini mungkin pilihan terbaik untuk Anda.
Jika Anda mengamati output secara langsung, jeda panjang yang sunyi selama build bisa membuat frustrasi. Mode sinkronisasi output ‘target’ mendeteksi kapan make akan dipanggil secara rekursif, menggunakan metode standar, dan tidak akan menyinkronkan output dari baris-baris tersebut. make rekursif akan melakukan sinkronisasi untuk targetnya sendiri, dan output dari masing-masing akan ditampilkan segera setelah selesai. Perhatikan bahwa output dari baris rekursif dalam resep tidak disinkronkan (misalnya, jika baris rekursif menampilkan pesan sebelum menjalankan make, pesan tersebut tidak akan disinkronkan).
Mode ‘line’ dapat berguna untuk front-end yang mengamati output make untuk melacak kapan resep dimulai dan selesai.
Beberapa program yang dipanggil oleh make dapat berperilaku berbeda tergantung pada apakah outputnya ditujukan ke terminal atau ke file (sering disebut mode “interaktif” versus “non-interaktif”). Misalnya, banyak program yang dapat menampilkan output berwarna tidak akan melakukannya jika mereka mendeteksi bahwa mereka tidak sedang menulis ke terminal. Jika makefile Anda memanggil program semacam ini, penggunaan opsi sinkronisasi output akan membuat program tersebut mengira dirinya berjalan dalam mode “non-interaktif”, padahal output tersebut pada akhirnya akan sampai ke terminal.
Dua proses tidak dapat menerima input dari perangkat yang sama secara bersamaan. Untuk memastikan bahwa hanya satu resep yang mencoba menerima input dari terminal pada satu waktu, make akan menonaktifkan stream input standar dari semua resep yang berjalan kecuali satu. Jika resep lain mencoba membaca dari input standar, biasanya akan terjadi error fatal (sinyal ‘Broken pipe’).
Tidak dapat diprediksi resep mana yang akan mendapatkan stream input standar yang valid (yang berasal dari terminal, atau dari mana pun Anda mengalihkan input standar make). Resep pertama yang dijalankan akan selalu mendapatkannya lebih dulu, dan resep pertama yang dimulai setelah resep itu selesai akan mendapatkannya berikutnya, dan seterusnya.
Kami akan mengubah cara kerja bagian ini dari make jika kami menemukan alternatif yang lebih baik. Sementara itu, Anda sebaiknya tidak mengandalkan resep mana pun untuk menggunakan input standar sama sekali jika Anda menggunakan fitur eksekusi paralel; tetapi jika Anda tidak menggunakan fitur ini, input standar akan berfungsi normal pada semua resep.
Setiap kali pemanggilan shell kembali, make memeriksa status keluarnya. Jika shell selesai dengan sukses (status keluar bernilai nol), baris berikutnya dalam resep akan dieksekusi dalam shell baru; setelah baris terakhir selesai, aturan tersebut selesai.
Jika terjadi error (status keluar bukan nol), make akan menyerah pada aturan saat ini, dan mungkin juga pada semua aturan.
Terkadang kegagalan pada suatu baris resep tidak menandakan adanya masalah. Misalnya, Anda mungkin menggunakan perintah mkdir untuk memastikan suatu direktori ada. Jika direktori tersebut sudah ada, mkdir akan melaporkan error, tetapi Anda mungkin tetap ingin make melanjutkan proses.
Untuk mengabaikan error pada suatu baris resep, tulislah ‘-’ di awal teks baris tersebut (setelah tab awal). ‘-’ akan dibuang sebelum baris tersebut diteruskan ke shell untuk dieksekusi.
Contohnya,
clean:
-rm -f *.o
Ini membuat make tetap melanjutkan proses meskipun rm tidak dapat menghapus suatu file.
Jika Anda menjalankan make dengan flag ‘-i’ atau ‘--ignore-errors’, error akan diabaikan pada semua resep dari semua aturan. Aturan dalam makefile untuk target khusus .IGNORE memiliki efek yang sama, jika tidak ada prasyarat. Cara ini kurang fleksibel, tetapi terkadang berguna.
Ketika error diabaikan, baik karena ‘-’ maupun flag ‘-i’, make memperlakukan hasil error sama seperti sukses, kecuali bahwa make mencetak pesan yang memberi tahu Anda kode status keluar shell dan menyatakan bahwa error tersebut telah diabaikan.
Ketika terjadi error yang tidak diperintahkan untuk diabaikan oleh make, itu berarti target saat ini tidak dapat dibuat ulang dengan benar, begitu pula target lain yang bergantung padanya, baik secara langsung maupun tidak langsung. Tidak ada lagi resep yang akan dieksekusi untuk target-target ini, karena prasyaratnya belum terpenuhi.
Biasanya, dalam situasi ini make langsung menyerah dan mengembalikan status bukan nol. Namun, jika flag ‘-k’ atau ‘--keep-going’ ditentukan, make akan terus mempertimbangkan prasyarat lain dari target yang tertunda, membuatnya ulang jika perlu, sebelum akhirnya menyerah dan mengembalikan status bukan nol. Misalnya, setelah terjadi error saat mengompilasi satu file objek, ‘make -k’ akan terus mengompilasi file objek lainnya meskipun sudah tahu bahwa proses linking-nya nanti tidak mungkin dilakukan. Lihat Ringkasan opsi.
Perilaku biasa mengasumsikan bahwa tujuan Anda adalah membuat target yang ditentukan menjadi mutakhir; begitu make mengetahui bahwa hal itu tidak mungkin, sebaiknya ia segera melaporkan kegagalan tersebut. Opsi ‘-k’ menyatakan bahwa tujuan sebenarnya adalah menguji sebanyak mungkin perubahan yang dibuat pada program, mungkin untuk menemukan beberapa masalah independen sekaligus, sehingga Anda dapat memperbaikinya semua sebelum mencoba mengompilasi lagi. Inilah sebabnya perintah compile pada Emacs secara default meneruskan flag ‘-k’.
Biasanya, ketika suatu baris resep gagal, jika baris itu sempat mengubah file target, file tersebut menjadi rusak dan tidak dapat digunakan—atau setidaknya belum diperbarui secara lengkap. Namun, stempel waktu file tersebut menunjukkan bahwa file itu sudah mutakhir, sehingga saat make dijalankan berikutnya, ia tidak akan mencoba memperbarui file tersebut. Situasi ini persis sama seperti ketika shell dihentikan oleh sinyal; lihat Menginterupsi atau menghentikan paksa make. Jadi, secara umum, tindakan yang benar adalah menghapus file target jika resep gagal setelah mulai mengubah file tersebut. make akan melakukan ini jika .DELETE_ON_ERROR muncul sebagai target. Ini hampir selalu merupakan hal yang Anda inginkan dari make, tetapi bukan praktik historisnya; jadi, demi kompatibilitas, Anda harus memintanya secara eksplisit.
Jika make menerima sinyal fatal saat suatu shell sedang berjalan, ia mungkin menghapus file target yang seharusnya diperbarui oleh resep tersebut. Ini dilakukan jika waktu modifikasi terakhir file target telah berubah sejak pertama kali diperiksa oleh make.
Tujuan menghapus target adalah untuk memastikan bahwa target tersebut dibuat ulang dari awal saat make dijalankan berikutnya. Mengapa demikian? Misalkan Anda menekan Ctrl-c saat kompiler sedang berjalan, dan kompiler itu baru saja mulai menulis file objek foo.o. Ctrl-c menghentikan kompiler, sehingga menyisakan file yang tidak lengkap dengan waktu modifikasi terakhir yang lebih baru daripada file sumber foo.c. Namun, make juga menerima sinyal Ctrl-c dan menghapus file yang tidak lengkap ini. Jika make tidak melakukan ini, pemanggilan make berikutnya akan mengira foo.o tidak perlu diperbarui—yang mengakibatkan linker mencoba menautkan file objek yang setengah hilang, dan memunculkan pesan error yang aneh.
Anda dapat mencegah penghapusan file target dengan cara ini dengan membuat target khusus .PRECIOUS bergantung padanya. Sebelum membuat ulang suatu target, make memeriksa apakah target tersebut muncul dalam prasyarat .PRECIOUS, dan dengan demikian menentukan apakah target tersebut harus dihapus jika terjadi sinyal. Beberapa alasan Anda mungkin melakukan ini adalah karena target diperbarui secara atomik, atau hanya ada untuk mencatat waktu modifikasi (isinya tidak penting), atau harus selalu ada untuk mencegah jenis masalah lain.
Meskipun make berusaha sebaik mungkin untuk membersihkan, ada situasi tertentu ketika pembersihan tidak mungkin dilakukan. Misalnya, make mungkin dihentikan oleh sinyal yang tidak dapat ditangkap. Atau, salah satu program yang dipanggil oleh make mungkin dihentikan atau mengalami crash, meninggalkan file target yang tampak mutakhir tetapi sebenarnya rusak: make tidak akan menyadari bahwa kegagalan ini memerlukan pembersihan target. Atau, make sendiri mungkin mengalami bug dan crash.
Karena alasan-alasan ini, sebaiknya Anda menulis resep defensif (defensive recipe), yaitu resep yang tidak akan meninggalkan target yang rusak meskipun gagal. Yang paling umum, resep semacam ini membuat file sementara alih-alih langsung memperbarui target, lalu mengganti nama file sementara tersebut menjadi nama target akhir. Beberapa kompiler sudah berperilaku seperti ini, sehingga Anda tidak perlu menulis resep defensif.
Penggunaan rekursif make berarti menggunakan make sebagai perintah dalam sebuah makefile. Teknik ini berguna ketika Anda ingin memiliki makefile terpisah untuk berbagai subsistem yang menyusun sistem yang lebih besar. Misalnya, andaikan Anda memiliki subdirektori subdir yang memiliki makefile-nya sendiri, dan Anda ingin makefile direktori induknya menjalankan make pada subdirektori tersebut. Anda dapat melakukannya dengan menulis:
subsystem:
cd subdir && $(MAKE)
atau, secara ekuivalen, seperti ini (lihat Ringkasan opsi):
subsystem:
$(MAKE) -C subdir
Anda dapat menulis perintah make rekursif hanya dengan menyalin contoh ini, tetapi ada banyak hal yang perlu Anda ketahui tentang bagaimana dan mengapa perintah tersebut bekerja, serta bagaimana sub-make berhubungan dengan make tingkat atas. Anda mungkin juga merasa berguna untuk mendeklarasikan target yang memanggil perintah make rekursif sebagai ‘.PHONY’ (untuk pembahasan lebih lanjut tentang kapan hal ini berguna, lihat Target semu (phony)).
Untuk kenyamanan Anda, ketika GNU make dijalankan (setelah memproses opsi -C jika ada), ia menetapkan variabel CURDIR ke nama path direktori kerja saat ini. Nilai ini tidak akan pernah disentuh lagi oleh make: secara khusus perhatikan bahwa jika Anda meng-include file dari direktori lain, nilai CURDIR tidak berubah. Nilai ini memiliki prioritas yang sama seperti jika ditetapkan dalam makefile (secara default, variabel lingkungan CURDIR tidak akan menimpa nilai ini). Perhatikan bahwa menetapkan variabel ini tidak berpengaruh pada operasi make (misalnya, ini tidak menyebabkan make mengubah direktori kerjanya).
Perintah make rekursif sebaiknya selalu menggunakan variabel MAKE, bukan nama perintah eksplisit ‘make’, seperti berikut:
subsystem:
cd subdir && $(MAKE)
Nilai variabel ini adalah nama file tempat make dijalankan. Jika nama file ini adalah /bin/make, resep yang dieksekusi adalah ‘cd subdir && /bin/make’. Jika Anda menggunakan versi khusus make untuk menjalankan makefile tingkat atas, versi khusus yang sama akan dijalankan untuk pemanggilan rekursif.
Sebagai fitur khusus, menggunakan variabel MAKE dalam resep suatu aturan mengubah efek opsi ‘-t’ (‘--touch’), ‘-n’ (‘--just-print’), atau ‘-q’ (‘--question’). Menggunakan variabel MAKE memiliki efek yang sama dengan menggunakan karakter ‘+’ di awal baris resep. Lihat Tindakan selain menjalankan resep. Fitur khusus ini hanya aktif jika variabel MAKE muncul langsung dalam resep: fitur ini tidak berlaku jika variabel MAKE dirujuk melalui ekspansi variabel lain. Dalam kasus terakhir ini, Anda harus menggunakan token ‘+’ untuk mendapatkan efek khusus tersebut.
Perhatikan perintah ‘make -t’ pada contoh di atas. (Opsi ‘-t’ sebenarnya menandai target sebagai mutakhir tanpa benar-benar menjalankan resep apa pun; lihat Tindakan selain menjalankan resep.) Mengikuti definisi biasa dari ‘-t’, perintah ‘make -t’ pada contoh ini seharusnya hanya membuat file bernama subsystem dan tidak melakukan hal lain. Yang sebenarnya Anda inginkan adalah menjalankan ‘cd subdir && make -t’; tetapi itu memerlukan eksekusi resep, dan ‘-t’ mengatakan untuk tidak menjalankan resep.
Fitur khusus ini membuatnya berjalan sesuai keinginan Anda: setiap kali baris resep suatu aturan mengandung variabel MAKE, flag ‘-t’, ‘-n’, dan ‘-q’ tidak berlaku pada baris tersebut. Baris resep yang mengandung MAKE akan dieksekusi secara normal meskipun ada flag yang membuat sebagian besar resep tidak dijalankan. Mekanisme MAKEFLAGS yang biasa meneruskan flag ke sub-make (lihat Menyampaikan opsi ke sub-make), sehingga permintaan Anda untuk menyentuh file, atau menampilkan resep, diteruskan ke subsistem.
Nilai variabel dari make tingkat atas dapat diteruskan ke sub-make melalui lingkungan jika diminta secara eksplisit. Variabel-variabel ini ditetapkan dalam sub-make sebagai nilai default, tetapi tidak akan menimpa variabel yang ditetapkan dalam makefile yang digunakan oleh sub-make, kecuali Anda menggunakan switch ‘-e’ (lihat Ringkasan opsi).
Untuk meneruskan, atau meng-ekspor (export), suatu variabel, make menambahkan variabel dan nilainya ke lingkungan untuk menjalankan setiap baris resep. Sub-make, pada gilirannya, menggunakan lingkungan tersebut untuk menginisialisasi tabel nilai variabelnya. Lihat Variabel dari lingkungan.
Kecuali diminta secara eksplisit, make hanya mengekspor suatu variabel jika variabel tersebut awalnya ditetapkan dalam lingkungan, atau jika ditetapkan pada baris perintah dan namanya hanya terdiri atas huruf, angka, dan garis bawah.
Nilai variabel make SHELL tidak diekspor. Sebagai gantinya, nilai variabel SHELL dari lingkungan pemanggil diteruskan ke sub-make. Anda dapat memaksa make mengekspor nilai SHELL dengan menggunakan direktif export, yang dijelaskan di bawah ini. Lihat Memilih shell.
Variabel khusus MAKEFLAGS selalu diekspor (kecuali Anda meng-unexport-nya). MAKEFILES diekspor jika Anda menetapkannya ke nilai apa pun.
make secara otomatis meneruskan nilai variabel yang ditetapkan pada baris perintah, dengan memasukkannya ke variabel MAKEFLAGS. Lihat Menyampaikan opsi ke sub-make.
Variabel biasanya tidak diteruskan ke bawah jika variabel tersebut dibuat secara default oleh make (lihat Variabel yang digunakan oleh aturan implisit). Sub-make akan mendefinisikan variabel-variabel ini sendiri.
Jika Anda ingin mengekspor variabel tertentu ke sub-make, gunakan direktif export, seperti ini:
export variable …
Sebaliknya, jika Anda ingin mencegah suatu variabel diekspor, gunakan direktif unexport, seperti ini:
unexport variable …
Pada kedua bentuk ini, argumen untuk export dan unexport diekspansi, sehingga argumen tersebut dapat berupa variabel atau fungsi yang diekspansi menjadi (daftar) nama variabel yang akan di(un)ekspor.
Sebagai kemudahan, Anda dapat mendefinisikan sekaligus mengekspor suatu variabel dengan menuliskan:
export variable = value
Ini memberikan hasil yang sama dengan:
variable = value export variable
Dan,
export variable := value
memberikan hasil yang sama dengan:
variable := value export variable
Demikian pula,
export variable += value
sama persis dengan:
variable += value export variable
Lihat Menambahkan teks ke variabel.
Anda mungkin menyadari bahwa direktif export dan unexport bekerja pada make dengan cara yang persis sama seperti pada shell sh.
Jika Anda ingin semua variabel diekspor secara default, Anda dapat menggunakan export sendirian:
export
Ini memberi tahu make bahwa variabel yang tidak disebutkan secara eksplisit dalam direktif export atau unexport harus diekspor. Variabel apa pun yang disebutkan dalam direktif unexport tetap tidak akan diekspor.
Perilaku yang ditimbulkan oleh direktif export yang berdiri sendiri ini merupakan default pada versi GNU make yang lebih lama. Jika makefile Anda bergantung pada perilaku ini dan Anda ingin tetap kompatibel dengan versi make yang lebih lama, Anda dapat menambahkan target khusus .EXPORT_ALL_VARIABLES ke makefile Anda, alih-alih menggunakan direktif export. Target ini akan diabaikan oleh make versi lama, sedangkan direktif export akan menyebabkan kesalahan sintaksis.
Ketika menggunakan export sendirian atau .EXPORT_ALL_VARIABLES untuk mengekspor variabel secara default, hanya variabel yang namanya terdiri atas huruf, angka, dan garis bawah saja yang akan diekspor. Untuk mengekspor variabel lain, Anda harus menyebutkannya secara khusus dalam direktif export.
Menambahkan nilai suatu variabel ke lingkungan mengharuskan nilai tersebut diekspansi. Jika ekspansi suatu variabel memiliki efek samping (seperti fungsi info atau eval dan sejenisnya), efek samping tersebut akan muncul setiap kali suatu perintah dijalankan. Anda dapat menghindari ini dengan memastikan variabel semacam itu memiliki nama yang tidak diekspor secara default. Namun, solusi yang lebih baik adalah tidak menggunakan fasilitas “ekspor secara default” ini sama sekali, dan sebagai gantinya meng-export variabel yang relevan secara eksplisit berdasarkan namanya.
Anda dapat menggunakan unexport sendirian untuk memberi tahu make agar tidak mengekspor variabel secara default. Karena ini adalah perilaku default, Anda hanya perlu melakukannya jika export sebelumnya pernah digunakan sendirian (mungkin dalam makefile yang di-include). Anda tidak dapat menggunakan export dan unexport sendirian untuk membuat variabel diekspor pada sebagian resep dan tidak pada resep lainnya. Direktif export atau unexport terakhir yang muncul sendirian menentukan perilaku untuk seluruh proses eksekusi make.
Sebagai fitur khusus, variabel MAKELEVEL berubah ketika diteruskan dari satu level ke level berikutnya di bawahnya. Nilai variabel ini adalah string yang menyatakan kedalaman level dalam bentuk desimal. Nilainya adalah ‘0’ untuk make tingkat atas, ‘1’ untuk sub-make, ‘2’ untuk sub-sub-make, dan seterusnya. Kenaikan ini terjadi ketika make menyiapkan lingkungan untuk suatu resep.
Kegunaan utama MAKELEVEL adalah untuk mengujinya dalam direktif kondisional (lihat Bagian kondisional dalam makefile); dengan cara ini Anda dapat menulis makefile yang berperilaku berbeda tergantung apakah dijalankan secara rekursif atau dijalankan langsung oleh Anda.
Anda dapat menggunakan variabel MAKEFILES agar semua perintah sub-make menggunakan makefile tambahan. Nilai MAKEFILES adalah daftar nama file yang dipisahkan oleh whitespace. Jika variabel ini didefinisikan dalam makefile tingkat luar, variabel ini akan diteruskan melalui lingkungan; kemudian variabel ini berfungsi sebagai daftar makefile tambahan yang dibaca oleh sub-make sebelum makefile biasa atau yang ditentukan. Lihat Variabel MAKEFILES.
Flag seperti ‘-s’ dan ‘-k’ diteruskan secara otomatis ke sub-make melalui variabel MAKEFLAGS. Variabel ini secara otomatis disiapkan oleh make untuk memuat huruf-huruf flag yang diterima oleh make. Jadi, jika Anda menjalankan ‘make -ks’, MAKEFLAGS akan bernilai ‘ks’.
Akibatnya, setiap sub-make mendapatkan nilai MAKEFLAGS dalam lingkungan-nya. Sebagai responsnya, sub-make mengambil flag dari nilai tersebut dan memprosesnya seolah-olah flag itu diberikan sebagai argumen. Lihat Ringkasan opsi. Ini berarti bahwa, tidak seperti variabel lingkungan lainnya, MAKEFLAGS yang ditentukan di lingkungan lebih diutamakan daripada MAKEFLAGS yang ditentukan dalam makefile.
Nilai MAKEFLAGS adalah sekumpulan karakter (boleh kosong) yang mewakili opsi satu huruf tanpa argumen, diikuti oleh spasi dan opsi apa pun yang memerlukan argumen atau memiliki nama opsi panjang. Jika suatu opsi memiliki versi satu huruf maupun versi nama panjang, versi satu huruf selalu diutamakan. Jika tidak ada opsi satu huruf pada baris perintah, nilai MAKEFLAGS dimulai dengan spasi.
Demikian pula, variabel yang ditetapkan pada baris perintah juga diteruskan ke sub-make melalui MAKEFLAGS. Kata dalam nilai MAKEFLAGS yang mengandung ‘=’ akan diperlakukan oleh make seolah-olah itu adalah definisi variabel yang muncul pada baris perintah. Lihat Direktif override.
Opsi ‘-C’, ‘-f’, ‘-o’, ‘-W’ tidak dimasukkan ke MAKEFLAGS; opsi-opsi ini tidak diteruskan ke bawah.
Opsi ‘-j’ adalah kasus khusus (lihat Eksekusi paralel). Jika Anda menetapkannya ke suatu nilai numerik ‘N’ dan sistem operasi Anda mendukungnya (kebanyakan sistem UNIX mendukung, sedangkan yang lain umumnya tidak), make induk dan semua sub-make akan berkoordinasi sehingga secara keseluruhan hanya ada ‘N’ job yang berjalan bersamaan di antara semuanya. Perhatikan bahwa job yang ditandai rekursif (lihat Tindakan selain menjalankan resep) tidak dihitung dalam total job (jika tidak, kita bisa mendapatkan ‘N’ sub-make yang berjalan dan tidak ada slot tersisa untuk pekerjaan sungguhan!).
Jika sistem operasi Anda tidak mendukung koordinasi di atas, ‘-j’ tidak akan ditambahkan ke MAKEFLAGS. Karena itu, sub-make akan berjalan dalam mode non-paralel. Jika opsi ‘-j’ diteruskan ke sub-make, Anda akan mendapatkan jauh lebih banyak job berjalan secara paralel daripada yang Anda minta. Jika Anda memberikan ‘-j’ tanpa argumen numerik, yang berarti menjalankan sebanyak mungkin job secara paralel, ini akan diteruskan ke bawah, karena berapa pun banyaknya nilai tak hingga tetaplah sama dengan satu nilai tak hingga.
Jika Anda tidak ingin meneruskan flag lain ke bawah, Anda harus mengubah nilai MAKEFLAGS, misalnya seperti ini:
subsystem:
cd subdir && $(MAKE) MAKEFLAGS=
Definisi variabel pada baris perintah sebenarnya muncul dalam variabel MAKEOVERRIDES, dan MAKEFLAGS memuat referensi ke variabel ini. Jika Anda ingin flag tetap diteruskan seperti biasa, tetapi tidak ingin meneruskan definisi variabel dari baris perintah, Anda dapat mereset MAKEOVERRIDES menjadi kosong, seperti ini:
MAKEOVERRIDES =
Ini biasanya tidak perlu dilakukan. Namun, beberapa sistem memiliki batas ukuran tetap yang kecil pada lingkungan, dan memasukkan terlalu banyak informasi ke nilai MAKEFLAGS dapat melampauinya. Jika Anda melihat pesan error ‘Arg list too long’, ini mungkin penyebabnya. (Untuk kepatuhan ketat terhadap POSIX.2, mengubah MAKEOVERRIDES tidak memengaruhi MAKEFLAGS jika target khusus ‘.POSIX’ muncul dalam makefile. Anda mungkin tidak perlu mengkhawatirkan hal ini.)
Demi kompatibilitas historis, ada juga variabel serupa MFLAGS. Variabel ini memiliki nilai yang sama dengan MAKEFLAGS, kecuali bahwa variabel ini tidak memuat definisi variabel dari baris perintah, dan selalu dimulai dengan tanda hubung kecuali jika kosong (MAKEFLAGS dimulai dengan tanda hubung hanya jika dimulai dengan opsi yang tidak memiliki versi satu huruf, seperti ‘--warn-undefined-variables’). MFLAGS secara tradisional digunakan secara eksplisit dalam perintah make rekursif, seperti ini:
subsystem:
cd subdir && $(MAKE) $(MFLAGS)
Tetapi sekarang, dengan adanya MAKEFLAGS, penggunaan ini menjadi tidak diperlukan lagi. Jika Anda ingin makefile Anda kompatibel dengan program make yang lebih lama, gunakan teknik ini; teknik ini tetap bekerja dengan baik pada versi make yang lebih baru.
Variabel MAKEFLAGS juga berguna jika Anda ingin mengaktifkan opsi tertentu, seperti ‘-k’ (lihat Ringkasan opsi), setiap kali Anda menjalankan make. Anda cukup menetapkan nilai MAKEFLAGS dalam lingkungan Anda. Anda juga dapat menetapkan MAKEFLAGS dalam makefile untuk menentukan flag tambahan yang juga harus berlaku untuk makefile tersebut. (Perhatikan bahwa Anda tidak dapat menggunakan MFLAGS dengan cara ini. Variabel tersebut hanya ditetapkan demi kompatibilitas; make tidak menginterpretasikan nilai yang Anda tetapkan untuknya dengan cara apa pun.)
Ketika make menginterpretasikan nilai MAKEFLAGS (baik dari lingkungan maupun dari makefile), pertama-tama ia menambahkan tanda hubung di depan jika nilai tersebut belum dimulai dengan tanda hubung. Kemudian nilai tersebut dipecah menjadi kata-kata yang dipisahkan oleh spasi, dan kata-kata itu diuraikan seolah-olah merupakan opsi yang diberikan pada baris perintah (kecuali ‘-C’, ‘-f’, ‘-h’, ‘-o’, ‘-W’, dan versi nama panjangnya diabaikan; dan tidak ada error untuk opsi yang tidak valid).
Jika Anda memasukkan MAKEFLAGS ke dalam lingkungan Anda, pastikan untuk tidak menyertakan opsi apa pun yang secara drastis memengaruhi tindakan make dan merusak tujuan makefile serta make itu sendiri. Misalnya, opsi ‘-t’, ‘-n’, dan ‘-q’, jika dimasukkan ke salah satu variabel ini, dapat menimbulkan akibat yang bencana, dan setidaknya pasti akan menghasilkan efek yang mengejutkan, bahkan mungkin menjengkelkan.
Jika Anda ingin menjalankan implementasi make lain selain GNU make, dan karena itu tidak ingin menambahkan flag khusus GNU make ke variabel MAKEFLAGS, Anda dapat menambahkannya ke variabel GNUMAKEFLAGS sebagai gantinya. Variabel ini diuraikan tepat sebelum MAKEFLAGS, dengan cara yang sama seperti MAKEFLAGS. Ketika make menyusun MAKEFLAGS untuk diteruskan ke make rekursif, ia akan menyertakan semua flag, termasuk yang diambil dari GNUMAKEFLAGS. Akibatnya, setelah menguraikan GNUMAKEFLAGS, GNU make menetapkan variabel ini menjadi string kosong untuk menghindari duplikasi flag selama rekursi.
Sebaiknya gunakan GNUMAKEFLAGS hanya dengan flag yang tidak akan secara substansial mengubah perilaku makefile Anda. Jika makefile Anda memang memerlukan GNU Make, cukup gunakan MAKEFLAGS. Flag seperti ‘--no-print-directory’ atau ‘--output-sync’ mungkin cocok untuk GNUMAKEFLAGS.
Jika Anda menggunakan beberapa tingkat pemanggilan make rekursif, opsi ‘-w’ atau ‘--print-directory’ dapat membuat output jauh lebih mudah dipahami dengan menampilkan setiap direktori saat make mulai memprosesnya dan saat make selesai memprosesnya. Misalnya, jika ‘make -w’ dijalankan di direktori /u/gnu/make, make akan mencetak baris dengan format berikut sebelum melakukan hal lain:
make: Entering directory `/u/gnu/make'.
dan baris dengan format berikut saat pemrosesan selesai:
make: Leaving directory `/u/gnu/make'.
Biasanya, Anda tidak perlu menentukan opsi ini karena ‘make’ melakukannya secara otomatis untuk Anda: ‘-w’ diaktifkan secara otomatis ketika Anda menggunakan opsi ‘-C’, dan pada sub-make. Namun, make tidak akan secara otomatis mengaktifkan ‘-w’ jika Anda juga menggunakan ‘-s’, yang berarti diam (silent), atau jika Anda menggunakan ‘--no-print-directory’ untuk menonaktifkannya secara eksplisit.
Ketika urutan perintah yang sama berguna untuk membangun berbagai target, Anda dapat mendefinisikannya sebagai urutan baku (canned sequence) dengan direktif define, lalu merujuk urutan baku tersebut dari resep target-target itu. Urutan baku sebenarnya adalah variabel, sehingga namanya tidak boleh bentrok dengan nama variabel lain.
Berikut contoh mendefinisikan resep baku:
define run-yacc = yacc $(firstword $^) mv y.tab.c $@ endef
Di sini run-yacc adalah nama variabel yang didefinisikan. endef menandai akhir definisi, dan baris-baris di antaranya adalah perintah. Direktif define tidak mengekspansi referensi variabel atau pemanggilan fungsi dalam urutan baku tersebut. Karakter ‘$’, tanda kurung, nama variabel, dan sebagainya semuanya menjadi bagian dari nilai variabel yang sedang Anda definisikan. Untuk penjelasan lengkap tentang define, lihat Mendefinisikan variabel multi-baris.
Perintah pertama dalam contoh ini menjalankan Yacc pada prasyarat pertama dari aturan mana pun yang menggunakan urutan baku ini. File output dari Yacc selalu bernama y.tab.c. Perintah kedua memindahkan output tersebut ke nama file target aturan.
Untuk menggunakan urutan baku, substitusikan variabelnya ke dalam resep suatu aturan. Anda dapat mensubstitusikannya seperti variabel lain mana pun (lihat Dasar-dasar referensi variabel). Karena variabel yang didefinisikan dengan define merupakan variabel ekspansi rekursif, semua referensi variabel yang Anda tulis di dalam define akan diekspansi sekarang. Contohnya:
foo.c : foo.y
$(run-yacc)
Ketika ‘$^’ muncul dalam nilai run-yacc, ia akan digantikan dengan ‘foo.y’, dan ‘$@’ akan digantikan dengan ‘foo.c’.
Ini adalah contoh yang realistis, tetapi contoh khusus ini sebenarnya tidak diperlukan dalam praktiknya karena make memiliki aturan implisit untuk menentukan perintah-perintah ini berdasarkan nama file yang terlibat (lihat Menggunakan aturan implisit).
Dalam eksekusi resep, setiap baris urutan baku diperlakukan persis seolah-olah baris itu muncul sendiri dalam aturan, didahului oleh tab. Secara khusus, make menjalankan sub-shell terpisah untuk setiap baris. Anda dapat menggunakan karakter prefiks khusus yang berlaku pada baris perintah (‘@’, ‘-’, dan ‘+’) pada setiap baris urutan baku. Lihat Menulis resep dalam aturan. Misalnya, dengan menggunakan urutan baku berikut:
define frobnicate = @echo "frobnicating target $@" frob-step-1 $< -o $@-step-1 frob-step-2 $@-step-1 -o $@ endef
make tidak akan menampilkan echo baris pertama, yaitu perintah echo. Namun, kedua baris resep berikutnya akan ditampilkan echo-nya.
Di sisi lain, karakter prefiks yang ditambahkan pada baris resep yang merujuk ke urutan baku akan berlaku untuk semua baris dalam urutan tersebut. Jadi, aturan berikut:
frob.out: frob.in
@$(frobnicate)
tidak menampilkan echo baris resep mana pun. (Untuk penjelasan lengkap tentang ‘@’, lihat Menampilkan echo resep.)
Terkadang berguna untuk mendefinisikan resep yang tidak melakukan apa pun. Ini dapat dilakukan hanya dengan memberikan resep yang hanya berisi whitespace. Contohnya:
target: ;
Ini mendefinisikan resep kosong untuk target. Anda juga dapat menggunakan baris yang dimulai dengan karakter prefiks resep untuk mendefinisikan resep kosong, tetapi ini akan membingungkan karena baris semacam itu tampak kosong.
Anda mungkin bertanya-tanya mengapa Anda ingin mendefinisikan resep yang tidak melakukan apa pun. Salah satu alasan ini berguna adalah untuk mencegah suatu target menerima resep implisit (dari aturan implisit atau target khusus .DEFAULT; lihat Menggunakan aturan implisit dan Mendefinisikan aturan default sebagai upaya terakhir).
Resep kosong juga dapat digunakan untuk menghindari error pada target yang dibuat sebagai efek samping dari resep lain: jika target tersebut tidak ada, resep kosong memastikan bahwa make tidak akan mengeluh karena tidak tahu cara membangun target tersebut, dan make akan menganggap target tersebut usang.
Anda mungkin tergoda untuk mendefinisikan resep kosong bagi target yang bukan file sungguhan, melainkan hanya ada agar prasyaratnya dapat dibuat ulang. Namun, ini bukan cara terbaik untuk melakukannya, karena prasyarat mungkin tidak dibuat ulang dengan benar jika file target tersebut memang ada. Lihat Target semu (phony) untuk cara yang lebih baik melakukan ini.