Ini adalah perintah makepp_tutorial yang dapat dijalankan di penyedia hosting gratis OnWorks menggunakan salah satu dari beberapa workstation online gratis kami seperti Ubuntu Online, Fedora Online, emulator online Windows atau emulator online MAC OS
PROGRAM:
NAMA
makepp_tutorial -- Tutorial menulis makefile
DESKRIPSI
A makefile adalah kumpulan instruksi yang Anda gunakan untuk memberi tahu makepp cara membuat
program. Makepp dapat menerima sebagian besar makefile yang ditulis untuk merek Unix standar, tetapi jika
Anda memulai dari awal, seringkali lebih mudah menggunakan beberapa makepp canggih
fitur. Ini adalah pengantar untuk menulis makefile yang khusus untuk makepp.
Jika Anda sudah tahu banyak tentang menulis makefile, Anda mungkin ingin setidaknya membaca dengan teliti
bagian selanjutnya dari file ini karena mereka menunjukkan cara yang lebih disukai untuk melakukan sesuatu dengan makepp,
yang seringkali berbeda dengan cara tradisional yang dilakukan dengan make. Sumber lain dari
contoh dan saran penulisan makefile untuk makepp adalah makepp_cookbook.
Membangun program dari file sumbernya bisa menjadi rumit dan memakan waktu
operasi. Perintah terlalu panjang untuk diketik secara manual setiap saat. Namun,
skrip shell langsung jarang digunakan untuk mengkompilasi program, karena itu juga
memakan waktu untuk mengkompilasi ulang semua modul ketika hanya satu dari mereka yang berubah.
Namun, terlalu rawan kesalahan untuk memungkinkan manusia memberi tahu komputer file mana yang perlu
dikompilasi ulang. Lupa untuk mengkompilasi ulang file dapat menyebabkan proses debug yang membuat frustrasi. A
alat otomatis yang andal diperlukan untuk menentukan dengan tepat modul mana yang dibutuhkan
kompilasi ulang.
Makepp (kependekan dari Make-plus-plus, atau buat++) adalah alat untuk memecahkan masalah ini dengan tepat.
Ini adalah perbaikan pada membuat program, alat standar yang sudah ada selama ini
bertahun-tahun. Itu bergantung pada pengetahuan bawaannya sendiri (dalam kasus yang sangat sederhana), atau pada file
disebut a Makefile yang berisi resep rinci untuk membangun program.
Biasanya, file input adalah kode sumber program, dan file output dapat dieksekusi,
tapi makepp tidak peduli apa itu. Anda dapat menggunakan makefile untuk mengontrol segala jenis
prosedur di mana Anda perlu menjalankan perintah tertentu secara selektif tergantung pada file mana
telah berubah. Anda dapat, misalnya, menggunakan makepp untuk melakukan analisis data, di mana input Anda
file adalah data mentah dan program analisis, dan file keluaran Anda adalah data yang diproses atau
grafik atau apapun. Makepp akan mencari tahu mana dari file data yang diproses perlu
diperbarui setiap kali beberapa file data atau program analisis berubah. Contohnya dalam hal ini
pengantar akan menganggap Anda sedang membangun program yang dapat dieksekusi dari kode sumber, tetapi Anda
dapat melakukan lebih banyak dengan makepp daripada hanya itu jika Anda menggunakan imajinasi Anda.
Jika program Anda terdiri dari satu modul, Anda mungkin tidak memerlukan makepp, karena Anda
ketahuilah bahwa setiap perubahan yang Anda buat memerlukan kompilasi ulang modul itu. Namun, jika Anda
program terdiri dari bahkan hanya dua modul, maka Anda pasti ingin menggunakan program
seperti makepp.
Do I perlu a file make?
Jika program Anda relatif sederhana dan tidak memerlukan sesuatu yang khusus,
makepp mungkin sudah tahu cara membuatnya tanpa Anda memberikan instruksi secara eksplisit. Untuk
contoh, misalkan Anda memiliki program dalam satu file sumber, yang disebut "test.c". Kamu bisa
cukup ketik "makepp test" dan program Anda akan dibuat seperti ini:
% tes makepp
makepp: Masuk ke direktori `/somewhere/or/other'
gcc -g -Dinding -c test.c -o test.o
gcc -g -Tes dinding.o -o tes
Peringatan: di Unix, untuk menjalankan program bernama 'test', Anda harus mengetik
./tes
bukan hanya 'ujian'.
Ini adalah perintah dasar yang diperlukan untuk mengkompilasi program di Unix. Jika perintah ini tidak
masuk akal bagi Anda, lihat makepp_tutorial_compilation.
Makepp berisi aturan bawaan untuk C, C++, dan Fortran.
Makepp terkadang dapat mengetahui cara mengkompilasi program yang terdapat di lebih dari
satu file sumber, atau program yang harus ditautkan dengan berbagai pustaka sistem. memang
ini dengan menebak file sumber dan perpustakaan mana yang Anda butuhkan berdasarkan file yang Anda
termasuk. Algoritma sebenarnya terlalu rumit untuk dibahas di sini dalam tutorial (tapi lihat
makepp_builtin); Anda dapat mencobanya, dan jika itu tidak bekerja secara otomatis untuk Anda, Anda perlu
tulis makefile Anda sendiri.
Secara default, untuk C dan C++, makepp mengkompilasi program dengan informasi debug dan tanpa
optimasi. Jika Anda ingin mengaktifkan optimasi agar program Anda berjalan lebih cepat,
ubah baris perintah menjadi:
makepp CFLAGS=-O2 tes
Jika Anda mengompilasi C++ alih-alih C, gunakan "CXXFLAGS=-O2" alih-alih "CFLAGS=-O2". Untuk sebuah
daftar lengkap opsi lain yang dapat Anda konfigurasikan tanpa menulis makefile, lihat
makepp_builtin.
Aturan bawaan Makepp agak lebih kuat daripada standar Unix, tetapi jika Anda
menulis program dengan kerumitan apa pun, kemungkinan Anda akan memerlukan makefile pada akhirnya untuk
beri tahu makepp apa yang harus dilakukan.
Jika Anda tidak terbiasa dengan perintah kompilasi Unix, mungkin akan membantu pada saat ini untuk
baca makepp_tutorial_compilation untuk deskripsi kompilasi Unix yang samar ini
perintah lakukan.
A sederhana makefile
Misalkan Anda sedang menulis program C++ yang memiliki dua modul sumber, "processing.cxx" dan
"gui.cxx", bersama dengan banyak file yang disertakan. Jika Anda membangun program Anda dari
awal, Anda perlu menjalankan sesuatu seperti perintah ini:
c++ -c pemrosesan.cxx -o pemrosesan.o
c++ -c gui.cxx -o gui.o
c++ memproses.o gui.o -o program_saya
Dua perintah pertama adalah perintah kompilasi, dan yang ketiga memanggil linker ke
menggabungkan dua file objek menjadi satu executable. Jika Anda membuat perubahan pada "gui.cxx"
tetapi tidak untuk "processing.cxx", maka Anda tidak perlu menjalankan kembali perintah pertama, tetapi Anda
perlu menjalankan dua perintah terakhir. makepp dapat mencari tahu ini untuk Anda
otomatis.
(Jika Anda belum pernah bekerja dengan make sebelumnya, Anda mungkin berpikir bahwa Anda dapat menggabungkan
di atas tiga perintah menjadi satu perintah, seperti ini:
c++ pemrosesan.cxx gui.cxx -o program_saya
Saat Anda menghilangkan opsi "-c" ke kompiler, ini menggabungkan kompilasi dan penautan
melangkah. Ini seringkali cukup nyaman ketika Anda tidak sedang menulis makefile. Namun, itu
bukan ide yang baik untuk melakukan ini di makefile, karena selalu mengkompilasi ulang kedua modul
jika salah satunya tidak berubah, dan ini bisa memakan banyak waktu ekstra.)
Untuk menggunakan makepp untuk mengontrol proses pembuatan, Anda harus menulis a makefile. itu
makefile adalah file teks yang berisi resep untuk membangun program Anda. Itu biasanya
berada di direktori yang sama dengan sumber, dan biasanya disebut "Makeppfile",
"Makefile" atau, hanya di root pohon build Anda "RootMakeppfile".
Masing-masing dari perintah ini harus terpisah memerintah dalam file make. Aturan adalah
instruksi untuk membangun satu atau lebih file output dari satu atau lebih file input. Makepp
menentukan aturan mana yang perlu dieksekusi ulang dengan menentukan apakah ada file input
untuk aturan telah berubah sejak terakhir kali aturan dieksekusi.
Aturan memiliki sintaks seperti ini:
nama_berkas keluaran : nama_berkas masukan
tindakan
Baris pertama aturan berisi daftar file keluaran yang dipisahkan spasi, diikuti oleh a
titik dua, diikuti oleh daftar file input yang dipisahkan oleh spasi. File output juga
bernama target, dan file input juga disebut ketergantungan; kami mengatakan bahwa target
file tergantung pada dependensi, karena jika salah satu dependensi berubah, target
harus dibangun kembali.
Baris aturan yang tersisa ( tindakan) adalah perintah shell yang akan dieksekusi. Setiap
tindakan harus diindentasi dengan setidaknya satu spasi (make tradisional memerlukan tab
karakter). Biasanya, hanya ada satu baris tindakan, tetapi bisa sebanyak yang Anda inginkan;
setiap baris dieksekusi secara berurutan, dan jika salah satu dari mereka gagal, sisanya tidak
dieksekusi. Aturan berakhir pada baris pertama yang tidak menjorok.
Beberapa atau semua tindakan mungkin juga pernyataan Perl. Ini mungkin dalam satu baris dan
dikelilingi oleh "perl { ... }". Atau mereka dapat menjangkau beberapa baris, dalam hal ini kurung kurawal
harus digandakan, "perl {{ ... }}".
Anda dapat menempatkan aturan dalam urutan apa pun di makefile, tetapi biasanya menulis
aturan yang menghubungkan program terlebih dahulu, diikuti oleh aturan kompilasi. Satu alasan untuk ini
adalah jika Anda cukup mengetik ""makepp"", maka makepp mencoba membangun target pertama di
file, yang berarti akan membangun seluruh program Anda dan bukan hanya sebagian saja.
(Jika Anda ingin membangun sesuatu selain target pertama, Anda harus menentukan namanya
target pada baris perintah, misalnya, ""makepp processing.o"".)
Perintah kompilasi di atas harus ditulis sebagai tiga aturan terpisah. Sebuah makefile untuk
membangun program ini bisa terlihat seperti ini:
# Perintah tautan:
my_program: pemrosesan.o gui.o
c++ memproses.o gui.o -o program_saya
perl {{
print "program_saya sudah dibuat\n";
}}
# Perintah kompilasi:
pengolahan.o: pengolahan.cxx
c++ -c pemrosesan.cxx -o pemrosesan.o
gui.o: gui.cxx
c++ -c gui.cxx -o gui.o
(Karakter pada baris setelah "#" diabaikan; itu hanya komentar. Anda tidak
membutuhkan komentar ""# Tautan:"" di makefile sama sekali.)
Untuk menggunakan makefile ini, cukup cd ke direktori dan ketik ""makepp"". Makepp akan mencoba
untuk membangun target pertama di makefile, yaitu "my_program". (Jika Anda tidak menginginkannya
untuk membangun target pertama, maka Anda harus memberikan nama target Anda sebenarnya
ingin membangun di baris perintah.)
Ketika makepp mencoba membangun "my_program", ia menyadari bahwa ia harus membangun terlebih dahulu
"processing.o" dan "gui.o" sebelum dapat menjalankan perintah link. Jadi terlihat pada
aturan lain di makefile untuk menentukan cara membuatnya.
Untuk membangun "processing.o", makepp menggunakan aturan kedua. Sejak "memproses.o"
tergantung pada "processing.cxx", makepp juga akan mencoba membuat "processing.cxx". Tidak ada
aturan untuk membuat "processing.cxx"; itu harus sudah ada.
Makepp memeriksa apakah "processing.cxx" telah berubah sejak terakhir kali "processing.o" digunakan
dibuat. Secara default, ini menentukan ini dengan melihat tanggal pada file. Makepp
ingat tanggal berapa "processing.cxx" terakhir kali "processing.o" dibuat oleh
menyimpannya dalam file terpisah (dalam subdirektori bernama ".makepp"). Makepp akan mengeksekusi
tindakan untuk membangun target jika salah satu dari berikut ini benar:
· Target tidak ada.
· Target ada, tetapi makepp tidak memiliki informasi tentang build terakhir.
· Tanggal pada setiap file input telah berubah sejak build terakhir, dan ada
perbedaan yang signifikan dalam file input. Untuk file C atau C++ ini berarti perubahan
selain spasi atau di dalam komentar.
· Tanggal pada setiap target telah berubah sejak build terakhir, dan ada perbedaan dalam
berkas sasaran.
· Tindakan telah berubah sejak build terakhir.
· Pembangunan terakhir terjadi pada arsitektur yang berbeda (jenis CPU atau operasi yang berbeda
tipe sistem).
· Setiap variabel lingkungan yang tercantum pada aturan telah berubah nilainya.
Mungkin tampak sedikit lucu bahwa makepp mengeksekusi tindakan jika file keluaran atau
file input telah berubah sejak build terakhir. Makepp dirancang untuk menjamin bahwa
build Anda benar, sesuai dengan perintah di makefile. Jika Anda pergi dan memodifikasi
file sendiri, maka makepp tidak dapat menjamin bahwa file yang dimodifikasi benar-benar benar,
sehingga bersikeras untuk membangun kembali. (Untuk informasi lebih lanjut tentang bagaimana makepp memutuskan apakah akan
membangun kembali, dan bagaimana Anda dapat mengontrol ini, lihat makepp_signatures dan makepp_command.)
Sekarang "processing.o" mungkin tidak hanya bergantung pada "processing.cxx"; jika "processing.cxx" termasuk
file ".h" apa pun, maka perlu dikompilasi ulang jika ada file ".h" yang berubah,
bahkan jika "processing.cxx" itu sendiri tidak berubah. Anda dapat mengubah aturan seperti ini:
# Daftar file .h yang tidak perlu
pemrosesan.o: pemrosesan.cxx pemrosesan.h simple_vector.h daftar.h
c++ -c pemrosesan.cxx -o pemrosesan.o
Namun, itu adalah gangguan nyata untuk memodifikasi makefile setiap kali Anda mengubah daftar
file yang disertakan, dan juga sangat rawan kesalahan. Anda tidak hanya harus
daftar file yang "processing.cxx" termasuk, tetapi juga semua file yang file-file itu
termasuk, dll. Kamu tidak memiliki untuk do ini. Makepp cukup pintar untuk memeriksa termasuk
file secara otomatis. Setiap kali ia melihat perintah yang terlihat seperti kompilasi C atau C++
(dengan melihat kata pertama dari tindakan), itu dibaca di file sumber yang dicari
arahan "#include". Ia tahu di mana mencari file yang disertakan dengan mem-parsing kompiler Anda
baris perintah untuk opsi "-I". File apa pun yang disertakan secara otomatis ditambahkan ke
daftar dependensi, dan file apa pun yang disertakan. Jika salah satu dari mereka telah berubah,
file akan dikompilasi ulang.
Setelah makepp mengetahui bahwa "processing.o" sudah diperbarui, makepp akan menentukan apakah "gui.o"
perlu dibangun kembali dengan menerapkan prosedur yang sama pada aturan ketiga. Ketika keduanya
"processing.o" dan "gui.o" diketahui dibangun dengan benar, lalu makepp berlaku sama
prosedur untuk melihat apakah perintah tautan perlu dijalankan kembali.
Makefile di atas akan berfungsi, tetapi bahkan untuk masalah sederhana ini, pengguna yang berpengalaman tidak
cenderung menulis makefile-nya dengan cara ini. Beberapa perbaikan dibahas selanjutnya
bagian.
Menggunakan variabel
Sejauh ini, makefile kami untuk mengkompilasi program kami dari dua modul terlihat seperti ini:
# Perintah tautan:
my_program: pemrosesan.o gui.o
c++ memproses.o gui.o -o program_saya
# Perintah kompilasi:
pengolahan.o: pengolahan.cxx
c++ -c pemrosesan.cxx -o pemrosesan.o
gui.o: gui.cxx
c++ -c gui.cxx -o gui.o
Ini bekerja dengan sangat baik, tetapi misalkan sekarang kita ingin mengubah beberapa opsi kompilasi. Atau
mungkin kita ingin menggunakan compiler yang berbeda. Kita harus mengubah ketiga kompilasi
garis.
Demikian pula, misalkan kita ingin mengubah daftar modul yang akan dikompilasi. Kita harus berubah
itu di dua tempat.
Duplikasi informasi seperti ini adalah resep bencana. Jika Anda pergi dan mengubah
makefile, dijamin suatu saat kamu atau orang lain akan lupa
untuk mengubah salah satu tempat. Tergantung pada apa perubahannya (terutama jika itu mempengaruhi
definisi praprosesor), ini dapat menyebabkan masalah halus dan sulit di-debug di
program.
Cara untuk menghindari duplikasi informasi adalah dengan menentukan informasi hanya sekali dan
menyimpannya dalam sebuah variabel, yang dapat diakses setiap kali informasi dibutuhkan.
# Tentukan simbol yang mungkin ingin kita ubah:
CXX := c++
BENDERA CXX := -g
OBJEK := processing.o gui.o
program_saya: $(OBJEK)
$(CXX) $(OBJEK) -o program_saya
pengolahan.o: pengolahan.cxx
$(CXX) $(TERMASUK) $(CXXFLAGS) -c processing.cxx -o processing.o
gui.o: gui.cxx
$(CXX) $(CXXFLAGS) -c gui.cxx -o gui.o
Di sini "$(CXX)" diperluas menjadi nilai variabel "CXX", dan juga untuk
"$(CXXFLAGS)" dan "$(OBJECTS)". Sekarang kita bisa mengubah satu baris di makefile, dan semuanya
perintah kompilasi yang relevan terpengaruh.
Bahkan, kita bahkan tidak perlu mengubah makefile untuk mengubah opsi kompilasi.
Tugas yang ditentukan pada baris perintah menimpa tugas di makefile. Untuk
contoh, kita bisa mengetik ini ke shell:
makepp CXXFLAGS="-g -O2"
yang mengesampingkan pengaturan "CXXFLAGS" di makefile. Seolah-olah makefile
berisi garis
BENDERA CXX := -g -O2
alih-alih definisi yang dikandungnya.
Mungkin sama sekali tidak berguna untuk dapat mengesampingkan hal-hal ini untuk Anda sendiri
pengembangan, tetapi jika Anda mendistribusikan sumber Anda ke orang lain, mereka mungkin menghargainya.
Nama variabel peka huruf besar/kecil (misalnya, "OBJEK" berbeda dari "objek"). Biasanya
orang menulis sebagian besar variabel hanya dalam huruf besar, tetapi Anda tidak harus melakukannya.
Jika Anda perlu memasukkan tanda dolar literal ke dalam tindakan aturan, tulislah dengan dolar ganda
tanda, seperti ini:
uji:
untuk testfile di *.test; lakukan run_test $$testfile; selesai
Secara konvensional, ada beberapa variabel yang mungkin ingin Anda atur. Ini hanya
konvensi, tetapi Anda akan melihatnya di banyak makefile.
CC := cc # Kompilator C.
CFLAGS := -g # C opsi kompilasi yang berhubungan dengan
# optimasi atau debugging (biasanya
# hanya -g atau -O). Biasanya ini tidak akan
# sertakan opsi -I untuk menentukan
# sertakan direktori, karena Anda
# tidak dapat menimpanya di baris perintah
# dengan mudah seperti pada contoh di atas.
CXX := c++ # Kompiler C++. (Terkadang "CPP" sebagai gantinya
# dari CXX.)
CXXFLAGS := -g # Opsi kompilasi C++ terkait dengan
# optimasi atau debugging (-O atau -g).
F77 := f77 # Kompilator Fortran.
FFLAGS := # Bendera pengoptimalan untuk Fortran.
Makepp akan menebak nilai yang sesuai untuk beberapa variabel ini jika Anda tidak menentukannya
(lihat makepp_builtin), tetapi biasanya yang terbaik adalah mengaturnya secara eksplisit--ini membuatnya lebih mudah
siapa pun yang membaca makefile Anda.
Ada banyak hal yang lebih kuat yang dapat Anda lakukan dengan variabel, tetapi pertama-tama kita
perlu menjelaskan beberapa hal lagi tentang makefile.
pola aturan
Memiliki satu aturan untuk setiap perintah kompilasi baik-baik saja ketika hanya ada beberapa file, tetapi
bagaimana jika program Anda terdiri dari puluhan file sumber? Kebanyakan dari mereka harus dikompilasi
dengan perintah yang sangat mirip. Sangat membosankan untuk mengetik aturan terpisah untuk setiap sumber
file, dan kemudian jika Anda memutuskan untuk mengubah aturan, Anda harus mengubah makefile di a
selusin tempat. Solusi yang lebih baik untuk masalah ini adalah dengan menggunakan belt hold memerintah.
Aturan pola adalah cara ringkas untuk menentukan aturan untuk banyak file sekaligus. Peraturan
akan tergantung pada nama file, tetapi biasanya tergantung pada mereka dengan cara yang sederhana. Anda
tentukan pola dengan menggunakan wildcard "%". Saat ada dalam daftar ketergantungan, "%"
cocok dengan string apa pun dengan panjang berapa pun; ketika ada dalam daftar target, "%" adalah singkatan dari
string yang "%" dalam daftar ketergantungan cocok.
Aturan pola berikut akan mengambil file ".c" dan mengompilasinya menjadi file ".o":
%.o: %.c
$(CC) $(CFLAGS) $(TERMASUK) -c $(input) -o $(output)
(Ini mengasumsikan bahwa Anda memiliki variabel "CC", "CFLAGS", dan "TERMASUK" yang didefinisikan sebagai
sesuatu yang cocok. Makepp akan menebak nilai untuk "CC" dan "CFLAGS".)
Baris pertama aturan mengatakan bahwa itu berlaku untuk setiap file input yang mungkin cocok
pola "%.c". File ".c" ini dapat diubah menjadi file ".o" yang sesuai
menggunakan tindakan yang ditentukan.
Tindakan aturan sangat mirip dengan tindakan lain yang telah kita lihat sebelumnya, kecuali
yang digunakannya otomatis variabel. Variabel otomatis adalah variabel yang nilainya
secara otomatis diatur oleh makepp tergantung pada aturan yang muncul. Yang paling berguna
variabel otomatis adalah:
"$(masukan)"
Nama file input pertama. Dalam aturan ini, ini akan menjadi file yang cocok
pola "%.c". "$(ketergantungan)" adalah sinonim untuk "$(masukan)". Di makefile yang lebih lama,
Anda juga akan melihat simbol samar $< digunakan juga.
"$(keluaran)"
Nama file keluaran pertama. Dalam aturan ini, ini akan menjadi file yang cocok
pola "%.o". "$(target)" dan $@ adalah sinonim.
"$(masukan)"
Nama semua file input yang terdaftar secara eksplisit. Dalam hal ini, karena hanya ada satu,
"$(input)" sama dengan "$(input)". "$(dependensi)" dan $^ adalah sinonim.
"$(keluaran)"
Nama semua target yang dicantumkan secara eksplisit. Dalam hal ini, karena hanya ada satu,
"$(keluaran)" sama dengan "$(keluaran)". "$(target)" adalah sinonim untuk
"$(keluaran)".
Perhatikan bahwa variabel ini adalah huruf kecil.
Anda dapat menggunakan variabel otomatis ini bahkan untuk aturan non-pola. Ini menghindari pengulangan
nama file sasaran.
Anda sebenarnya dapat melakukan hal-hal yang jauh lebih rumit dengan aturan pola. Sebagai contoh,
# Letakkan file objek ke dalam direktori terpisah:
objek/%.o: %.cpp
$(CXX) $(CXXFLAGS) -c $(masukan) -o $(keluaran)
# Jalankan preprocessor untuk membuat file sumber:
moc_%.cxx: %.h
$(MOC) $(masukan) -o $(keluaran)
Menggunakan aturan pola dan variabel otomatis, kita mungkin akan menulis ulang makefile kita untuk
program sederhana seperti ini:
CXX := c++
BENDERA CXX := -g
TERMASUK := -I. # Ini akan berisi opsi -I apa pun untuk
# compiler, jika ada.
LIBS := -L/usr/X11R6/lib -lX11 # Berisi library yang perlu kita tautkan.
OBJEK := processing.o gui.o
program_saya: $(OBJEK)
$(CXX) $(masukan) -o $(keluaran) $(LIBS)
%.o: %.cxx
$(CXX) $(TERMASUK) $(CXXFLAGS) -c $(masukan) -o $(keluaran)
Sekarang kita tidak harus memiliki aturan eksplisit untuk setiap file objek yang perlu kita hasilkan. Jika kita
ingin menambahkan modul lain ke program kami, kami hanya perlu mengubah satu baris itu
mendefinisikan variabel "OBJEK". Perhatikan bahwa makefile ini sekarang jauh lebih ringkas daripada kami
file make asli. Setiap informasi hanya muncul sekali jadi tidak ada kemungkinan
membuat kesalahan dengan mengubah informasi di satu tempat dan lupa untuk mengubahnya
lainnya
Saat Anda menggunakan aturan pola, tidak jarang ada dua aturan berbeda yang bisa
menghasilkan file yang sama. Jika kedua aturan tersebut adalah aturan pola, maka aturan yang muncul kemudian di
makefile benar-benar digunakan. Jika satu aturan adalah aturan pola, dan yang lainnya adalah aturan
aturan eksplisit (yang benar-benar menamai file target secara eksplisit), maka aturan eksplisit
digunakan. Ini sering membantu jika Anda ingin mengkompilasi sebagian besar modul dengan perintah yang sama,
tetapi ada satu modul yang membutuhkan opsi kompilasi yang sedikit berbeda, seperti yang ditunjukkan pada
fragmen makefile ini:
BENDERA CXX := -g -O2
FAST_CXXFLAGS := -DNO_DEBUG -O6 -malign-double -funroll-all-loop
%.o: %.cpp
$(CXX) $(CXXFLAGS) -c $(masukan) -o $(keluaran)
waktu_kritis_subs.o: waktu_kritis_subs.cpp
$(CXX) $(FAST_CXXFLAGS) -c $(masukan) -o $(keluaran)
Ada juga sintaks lain yang lebih nyaman untuk memengaruhi opsi kompilasi
hanya untuk satu atau beberapa target. Dimungkinkan untuk memberi tahu makepp bahwa suatu variabel harus memiliki
nilai yang berbeda untuk target spesifik tertentu. Dalam contoh ini, akan terlihat seperti ini:
BENDERA CXX := -g -O2
FAST_CXXFLAGS := -DNO_DEBUG -O6 -malign-double -funroll-all-loop
%.o: %.cpp
$(CXX) $(CXXFLAGS) -c $(masukan) -o $(keluaran)
time_critical_subs.o: CXXFLAGS := $(FAST_CXXFLAGS)
Secara umum, jika Anda menentukan nama variabel setelah daftar target, maka dibutuhkan a
nilai yang berbeda ketika perintah build untuk target tersebut sedang ditentukan.
Jika Anda menemukan diri Anda ingin melakukan sesuatu dengan pola yang tidak mudah diungkapkan
menggunakan wildcard "%", makepp memiliki sintaks lain yang agak sulit dibaca, tetapi
jauh lebih kuat. Lihat klausa foreach dalam aturan untuk lebih jelasnya.
Makepp sebenarnya memiliki aturan bawaan untuk mengkompilasi kode C atau C++ atau Fortran, yaitu:
tersedia jika Anda tidak menimpanya dengan aturan Anda sendiri. Aturan bawaannya hampir
identik dengan contoh di atas. Kebanyakan makefile berisi aturan pola untuk kompilasi,
tetapi Anda dapat bergantung pada aturan bawaan jika Anda mau.
Palsu target
Seringkali lebih mudah untuk memasukkan perintah ke dalam makefile yang sebenarnya tidak membuat file,
tetapi entah bagaimana secara logis terkait dengan proses pembuatan. Misalnya, sangat umum
prosedur di makefiles adalah seperti ini:
awalan=/ usr / local
instal: our_program
&instal -m 0755 our_program $(awalan)/tempat sampah
&instal -m 0644 $(wildcard *.png) $(prefix)/share/our_program/icons
.PHONY: instal
Ketika seseorang mengetik "makepp install", maka makepp terlebih dahulu membangun "our_program", lalu menjalankan
perintah yang terkait dengan target instal. Perintah "install" hanya menyalinnya
argumen ke direktori yang ditentukan, dan mengatur perlindungan file ke yang ditunjukkan
nilai. Jadi itu menyalin program_kami ke / usr / local / bin, dan beberapa file data terkait ke dalam
/usr/local/share/our_program/icons. Tapi ini tidak membuat file bernama "install" di
direktori saat ini.
Perintah itu sendiri, &install, didahului oleh ampersand. Ini berarti bahwa itu bukan
Perintah Shell, tetapi perintah bawaan yang serupa. (Anda melihat perbedaan yang Anda butuhkan
fungsi "$(wildcard)", karena Shell tidak mengembangkannya untuk Anda.) Makepp memiliki
beberapa di antaranya, keduanya karena alasan portabilitas -- "instal", padahal sebenarnya ada
hadir, sangat berbeda, dan bahkan perintah sepele seperti "echo" lakukan -- dan performa. Lihat
perintah bawaan untuk lebih jelasnya.
Target "instal" di sini disebut a palsu target karena makepp memperlakukannya seolah-olah
file nyata, tetapi sebenarnya bukan file, itu hanya trik untuk memaksa makepp untuk membangun
dependensinya dan kemudian jalankan beberapa perintah.
Itulah garisnya
.PHONY: instal
adalah untuk. Ini memberitahu makepp bahwa seharusnya tidak mengharapkan file "./install" ada
setelah perintah dieksekusi. Jika Anda lupa deklarasi palsu, maka makepp akan
berharap file "instal" ada setelah menjalankan perintah, dan itu akan mengeluh
keras jika tidak.
Anda juga dapat menulis pernyataan palsu seperti ini:
$(instal palsu): program_kami
...
dan kemudian hilangkan baris ".PHONY: install". Ini berarti Anda dapat mendeklarasikan target sebagai
phony pada baris yang sama seperti yang Anda definisikan, yang mungkin membuat makefile Anda lebih mudah dibaca.
Target palsu sangat umum di makefile. Di hampir semua makefile, yang pertama
target adalah target palsu "semua", seperti ini:
$(semua palsu): program1 program2 program3
Jika tidak ada target yang ditentukan pada baris perintah, makepp mencoba membangun target pertama
dalam file. Jika makefile Anda membuat lebih dari satu program, kemungkinan besar Anda ingin
membangun semua program secara default. Dalam contoh ini, jika programmer hanya mengetik
"makepp" tanpa argumen apa pun, makepp mencoba membangun "semua", yang memaksanya untuk membangun
ketiga program dari direktori ini.
Berikut adalah contoh fragmen makefile yang menggambarkan beberapa target palsu yang umum digunakan:
PROGRAM := kombobulator diskombobator
$(palsu semua): $(PROGRAM) # Semua adalah target pertama, jadi ini adalah default.
kombobulator: $(COMBOBULATOR_OBJS)
$(CXX) $(masukan) -o $(keluaran)
pengurai: $(DISCOMBOBULATOR_OBJS)
$(CXX) $(masukan) -o $(keluaran)
#
# Target ini memastikan semuanya dikompilasi, dan kemudian menempatkan
# program ke tempat di mana semua orang dapat mengaksesnya. Kami membuat
# direktori jika belum ada. Kami tidak menggunakan &mkdir, karena
# &instal melacak semua yang dilakukannya, jadi &uninstal dapat dilakukan nanti
# batalkan ini.
#
awalan := / usr / local
$(instal palsu): semua
&instal -d $(awalan)/tempat sampah $(awalan)/share/combobulate
&instal -m 0755 $(PROGRAM) $(awalan)/tempat sampah
&instal -m 0644 $(wildcard *.xbm) $(awalan)/bagikan/combobulate
#
# Target ini membuat distribusi sumber untuk pengiriman ke seseorang.
#
VERSI := 3.14
$(distribusi palsu):
rm -rf combobulate-$(VERSION) # Singkirkan sampah sebelumnya, jika ada.
&mkdir combobulate-$(VERSI)
&cp -l $(wildcard *.c *.h) Makefile README INSTALL combobulate-$(VERSION)
tar cf - combobulate-$(VERSION) | gzip -9c > combobulate-$(VERSION).tar.gz
rm -rf combobulate-$(VERSI)
#
# Target ini menjalankan tes regresi untuk memastikan programnya
# melakukan apa yang seharusnya mereka lakukan.
#
$(tes palsu): $(PROGRAM)
noecho untuk testfile di *.test; melakukan \
./combobulate $$testfile | ./discombobulate -> junk_output; \
if cmp -s junk_output $$testfile; kemudian \
echo lulus $$testfile; \
lain \
echo gagal $$testfile; \
fi; \
dilakukan
#
# Jika "noecho" adalah kata pertama dari tindakan, tindakan itu sendiri tidak
# dicetak sebelum dieksekusi. Dalam hal ini, mencetak tindakan
# hanya akan mengacaukan layar sehingga sangat umum untuk ditekan
# mencetak untuk perintah yang begitu panjang.
#
# Perhatikan penggunaan tanda dolar ganda untuk memberikan tanda dolar tunggal ke
# kerang. Perhatikan juga garis miring terbalik di akhir baris untuk menunjukkan
# bahwa perintah shell berlanjut ke baris berikutnya.
#
Kerja dengan beberapa direktori
Jika program Anda tumbuh ke ukuran yang besar, atau jika menggunakan perpustakaan yang perlu dibangun
tetapi harus tetap terpisah, kemungkinan besar Anda telah membagi sumber Anda menjadi
beberapa direktori. Salah satu motivasi utama menulis makepp adalah membuat deal
dengan beberapa direktori jauh lebih mudah daripada dengan utilitas make standar. Jika kamu
akrab dengan merek Unix standar, Anda akan melihat bahwa dengan makepp, Anda tidak perlu melakukannya
main-main dengan kompleksitas jelek seperti permintaan rekursif dari make.
Dengan makepp, Anda cukup meletakkan makefile terpisah di setiap direktori yang membangun yang relevan
file dalam direktori itu. Ketika makefile merujuk ke file yang perintah build-nya ada di
makefile yang berbeda, makepp secara otomatis menemukan aturan build yang sesuai di yang lain
makefile. Semua tindakan di setiap makefile dieksekusi dengan direktori saat ini disetel menjadi
direktori yang berisi makefile, sehingga setiap makefile dapat ditulis secara independen dari
semua yang lain. Tidak ada makefile yang harus tahu apa-apa tentang makefile lain; itu tidak
bahkan harus memberitahu makepp untuk memuat aturan dari makefile lain tersebut.
Ketika Anda telah menulis makefile Anda, cd ke direktori yang berisi program utama Anda,
dan ketik "makepp" seperti biasanya. Makepp akan memuat di makefile dari itu
direktori. Ini akan melihat bahwa makefile ini merujuk ke file di direktori lain, dan itu
akan memeriksa direktori lain tersebut untuk melihat apakah ada makefile di dalamnya. Lewat sini,
semua makefile yang relevan akan dimuat.
Sebagai contoh sederhana, misalkan direktori tingkat atas Anda berisi makefile berikut:
(nama yang disarankan adalah "RootMakeppfile", tetapi "Makeppfile" juga akan berfungsi):
# Makefile tingkat atas:
CXX := c++
BENDERA CXX := -O2
program_saya: main.o barang/libgoodies.so
$(CXX) $(masukan) -o $(keluaran)
%.o: %.cxx
$(CXX) $(CXXFLAGS) -c $(masukan) -o $(keluaran)
Anda perlu menulis makefile di direktori "goodies" yang dibuat
"libgoodies.so", seperti ini:
# barang/Makefile
CXX := c++
BENDERA CXX := -O2
MODUL = candy.o chips.o licorice.o cookies.o popcorn.o bayam.o
libgoodies.so: $(MODUL)
$(CXX) -dibagi $(input) -o $(output)
# Perhatikan bahwa perintah ditulis dengan asumsi bahwa
# direktori saat ini adalah subdirektori
# "barang", bukan subdirektori tingkat atas.
# Masukkan cd ke direktori ini sebelum dijalankan
# perintah apa pun dari makefile ini.
%.o: %.cxx
$(CXX) $(CXXFLAGS) -fpic -c $(masukan) -o $(keluaran)
Dan hanya itu yang perlu Anda lakukan.
Variabel apa pun yang Anda tentukan pada baris perintah menimpa definisi dari
variabel dalam semua makefile. Jadi, misalnya, jika Anda mengetik "makepp CXXFLAGS="-g"", all
modul akan dikompilasi ulang untuk debug karena definisi "CXXFLAGS" di keduanya
makefile ditimpa.
Direktori yang berisi sumber lain tidak perlu berupa subdirektori tingkat atas
direktori (seperti dalam contoh ini). Mereka bisa berada di mana saja di sistem file; makepp
akan secara otomatis memuat makefile dari direktori mana pun yang berisi file yang a
ketergantungan beberapa target yang coba dibangun. Itu juga akan memuat makefile dari mana saja
direktori yang dipindai oleh wildcard.
Pemuatan otomatis berfungsi jika file yang dibuat oleh makefile Anda semua berada di direktori yang sama
sebagai makefile itu sendiri. Jika Anda menulis makefile Anda sehingga aturannya menghasilkan file dalam a
direktori yang berbeda dari makefile itu sendiri, maka Anda mungkin harus memberi tahu makepp ke mana harus
cari makefile, karena tidak ada cara untuk menebak. Anda dapat melakukan ini menggunakan
pernyataan "load_makefile" di makefile Anda. Untuk informasi lebih lanjut tentang ini dan lainnya
masalah yang terkait dengan pembuatan multi-direktori, lihat "Tips untuk beberapa direktori" di
makepp_buku masak.
Satu peringatan: jika Anda mereferensikan variabel "$(MAKE)" di makefile Anda, makepp secara otomatis
masuk ke mode kompatibilitas mundur dan mematikan pemuatan otomatis.
Contoh or pelat ketel arsip
Makepp memiliki beberapa fitur lain yang membuat hidup sedikit lebih mudah bagi programmer yang memiliki
untuk memelihara program yang mencakup beberapa direktori. Dalam contoh di atas, Anda akan melihat
bahwa definisi variabel "CXX" dan "CXXFLAGS" harus diulang di masing-masing
makefile. Ini bisa menjadi gangguan untuk memasukkan kembali informasi yang sama ke setiap makefile, dan
itu bisa menjadi masalah jika Anda memutuskan untuk mengubahnya--Anda mungkin harus memodifikasi lusinan
makefile yang berbeda.
Apa yang dapat Anda lakukan adalah meletakkan semua informasi yang umum untuk setiap makefile
ke dalam file terpisah, mungkin terletak di bagian atas pohon direktori. Umum
informasi biasanya mencakup definisi variabel, dan terkadang juga aturan pola. (Di dalam
contoh di atas, bagaimanapun, aturan polanya tidak sama di kedua makefile.) Mari kita
misalkan Anda telah memanggil file ini "standard_defs.mk". Maka setiap makefile hanya perlu
berisi pernyataan seperti ini:
sertakan standard_defs.mk
Ketika makepp melihat pernyataan ini, ia memasukkan isi file ke dalam makefile di
titik itu. Pernyataan "sertakan" pertama-tama mencari file di direktori saat ini,
kemudian di induk direktori saat ini, dan seterusnya hingga tingkat atas file
sistem, jadi Anda sebenarnya tidak perlu menentukan "../standard_defs.mk" atau
"../../../../standard_defs.mk".
Jadi kita bisa menulis ulang makefile di atas agar terlihat seperti ini. "standard_defs.mk" akan ada
di direktori tingkat atas, dan mungkin berisi definisi berikut:
# standar_defs.mk
CXX := c++
BENDERA CXX := -O2
#
# Kami juga menyertakan aturan pola yang mungkin berguna dalam satu atau lebih
# subdirektori. Aturan pola ini untuk kompilasi C untuk menempatkan
# hal-hal ke perpustakaan bersama (itulah gunanya opsi -fpic).
#
%.o: %.cxx
$(CXX) $(CXXFLAGS) -fpic -c $(masukan) -o $(keluaran)
Perhatikan bahwa karena file yang disertakan sebenarnya dimasukkan ke dalam setiap makefile, aturan di
file yang disertakan diterapkan dengan direktori default yang disetel ke direktori yang berisi
makefile yang menyertakan file, bukan direktori yang berisi file include.
Tingkat atas "Makefile" mungkin terlihat seperti ini:
# Makefile tingkat atas
sertakan standard_defs.mk
program_saya: main.o barang/libgoodies.so
$(CXX) $(masukan) -o $(keluaran)
#
# Perhatikan bahwa aturan pola ini mengesampingkan yang ditemukan di standard_defs.mk,
# karena makepp melihatnya nanti. Aturan pola ini untuk kompilasi untuk
# modul yang tidak termasuk dalam pustaka bersama.
#
%.o: %.cxx
$(CXX) $(CXXFLAGS) $(masukan) -o $(keluaran)
Dan makefile subdirektori mungkin terlihat seperti ini:
# barang/Makefile
sertakan standard_defs.mk
MODUL = candy.o chips.o licorice.o cookies.o popcorn.o bayam.o
libgoodies.so: $(MODUL)
$(CXX) -dibagi $(input) -o $(output)
# Kami tidak memerlukan aturan pola untuk kompilasi file .cxx ke .o,
# karena terkandung dalam standard_defs.mk.
-F kompilasi Option
Jika Anda menjalankan makepp dari dalam editor seperti emacs, dan Anda mengedit sumber dari
beberapa direktori yang berbeda, Anda mungkin menemukan bahwa direktori default untuk makepp berbeda
tergantung pada file mana yang terakhir Anda edit. Akibatnya, makepp mungkin tidak dimuat
makefile yang benar.
Apa yang dapat Anda lakukan untuk memastikan bahwa makepp selalu memuat makefile yang benar, apa pun yang terjadi
direktori kebetulan adalah direktori Anda saat ini, adalah dengan menggunakan opsi baris perintah "-F",
seperti ini:
makepp-F ~/src/program_saya
Makepp pertama-tama akan cd ke direktori "~/src/program_saya" sebelum mencoba memuat a
makefile.
Menggunakan Kartu liar
Sampai saat ini, kami harus secara eksplisit mendaftar semua modul yang masuk ke a
program atau perpustakaan. Makefile sebelumnya, misalnya, berisi baris ini:
MODUL = candy.o chips.o licorice.o cookies.o popcorn.o bayam.o
libgoodies.so: $(MODUL)
$(CXX) -dibagi $(input) -o $(output)
Dalam hal ini, daftar semua modul yang masuk ke "libgoodies.so" tidak terlalu besar
kesepakatan karena tidak terlalu banyak dari mereka. Tapi terkadang itu bisa sangat mengganggu
daftar semua file objek, terutama jika daftar ini berubah dengan cepat selama
perkembangan. Seringkali, Anda ingin setiap modul di seluruh direktori menjadi
dikompilasi ke dalam program atau perpustakaan Anda. Akan jauh lebih mudah jika Anda bisa memberi tahu
makepp untuk melakukan itu tanpa mencantumkan semuanya.
Anda bisa. Baris di atas dapat ditulis ulang menjadi:
libgoodies.jadi: *.o
$(CXX) -dibagi $(input) -o $(output)
Wildcard "*.o" cocok dengan file ".o" yang ada, atau file ".o" yang belum
ada tetapi dapat dibuat dengan aturan apa pun yang diketahui makepp dari makefile mana pun yang
itu telah membaca. Jadi wildcard akan mengembalikan daftar file yang sama, tidak peduli apakah Anda
belum mengkompilasi apa pun, atau apakah semua modul telah dikompilasi sebelumnya.
Tentu saja, jika Anda mengotori direktori Anda dengan file tambahan yang tidak boleh dikompilasi
langsung ke perpustakaan Anda, (misalnya, jika Anda menulis program uji kecil dan membiarkannya di
direktori yang sama dengan file sumber perpustakaan), maka modul ini akan salah
dimasukkan ke dalam perpustakaan Anda. Jika Anda memilih untuk menggunakan wildcard, terserah Anda untuk menyimpannya
direktori cukup bersih.
Makepp mendukung wildcard Unix biasa dan satu tambahan:
"*"
Mencocokkan string apa pun dengan 0 karakter atau lebih. Itu tidak akan cocok dengan karakter "/". Untuk
contoh, "a*c" cocok dengan "ac", "abc", dan "aaaaabc", tetapi tidak "aa/bc".
"?" Cocok dengan tepat satu karakter (tidak termasuk "/"). Misalnya, "???.o" cocok dengan semua
nama file yang memiliki 3 karakter sebelum ekstensi ".o".
[karakter]
Cocok dengan salah satu daftar karakter pada posisi itu. Misalnya, "[abc].o" cocok
"ao", "bo", "co", tetapi bukan "abc.o" atau "do". Anda juga dapat menentukan kisaran
karakter, misalnya, "data_[0-9]" akan cocok dengan "data_0", "data_1", dll.
" **"
Ini adalah wildcard khusus, hanya ditemukan di makepp (dan shell zsh, dari mana saya
mencuri idenya, seperti yang telah dilakukan bash sejak itu). Ini cocok dengan sejumlah intervensi
direktori. Misalnya, "**/*.o" cocok dengan "xyz.o", "test_programs/abc.o", dan
"a/deeply/bersarang/subdirektori/def.o".
Jika sumber Anda terdapat dalam beberapa subdirektori, dan Anda ingin menautkan semua
modul objek bersama-sama, Anda dapat menulisnya seperti ini:
liboodles.so: **/*.o
$(CXX) -dibagi $(input) -o $(output)
Fungsi dan Advanced Variabel penggunaan
Makepp memiliki sejumlah cara yang sangat ampuh untuk memanipulasi teks. Tutorial ini menunjukkan
beberapa cara yang lebih berguna, tetapi Anda mungkin ingin melihat-lihat makepp_variables dan
makepp_functions untuk daftar yang lebih lengkap.
daftar of sesuai arsip
Masalah umum dalam makefile adalah pemeliharaan dua daftar file yang sesuai.
Perhatikan dua variabel berikut:
SUMBER := a.cpp bc.cpp def.cpp
OBJS := ao bc.o def.o
Kami mungkin ingin memiliki daftar sumber jika makefile dapat membangun distribusi sumber,
dan kita mungkin memerlukan daftar objek untuk perintah link. Membosankan untuk mengubah keduanya
baris setiap kali modul baru ditambahkan, dan bukan tidak mungkin seorang programmer akan berubah
satu baris dan lupa untuk mengubah yang lain. Di sini kami akan menunjukkan empat cara berbeda untuk menghindari
duplikasi.
Fungsi patsubst
Yang pertama adalah menggunakan fungsi makepp untuk mengubah satu daftar menjadi daftar lainnya. Sebuah fungsi
doa terlihat sedikit seperti variabel, kecuali bahwa suatu fungsi dapat mengambil argumen:
$(fungsi arg1 arg2 arg3 ...)
Makepp menyediakan banyak fungsi yang kuat, tetapi mungkin yang paling berguna adalah
fungsi "patsubst". Anda dapat menulis baris di atas seperti ini:
SUMBER = a.cpp bc.cpp def.cpp
OBJS = $(patsubst %.cpp, %.o, $(SUMBER))
Fungsi "patsubst" menerapkan pola untuk setiap kata dalam daftar kata, dan
melakukan substitusi tekstual sederhana. Kata apa saja dalam daftar yang cocok dengan polanya
dalam argumen pertama dimasukkan ke dalam output setelah melakukan substitusi yang ditunjukkan
oleh argumen kedua. Wildcard "%" cocok dengan string apa pun yang terdiri dari 0 karakter atau lebih.
Dalam contoh ini, pola "%.cpp" diterapkan ke setiap kata di "$(SOURCES)". NS
kata pertama, "a.cpp" cocok dengan polanya, dan wildcard "%" cocok dengan string "a".
The "%" dalam argumen kedua kemudian diganti dengan "a", dan hasilnya adalah "ao". Untuk
argumen kedua, "%" cocok dengan "bc", jadi hasilnya adalah "bc.o".
Fungsi Makepp dapat menghapus nama direktori, menghapus ekstensi, menyaring pencocokan
kata-kata, mengembalikan output dari perintah shell, dan trik berguna lainnya. Tambahan,
Anda juga dapat menulis fungsi Anda sendiri dalam perl yang dapat dipanggil dari bagian lain
file make. Lihat makepp_extending untuk detailnya.
Referensi substitusi
Karena fungsi "patsubst" sangat umum, ada sintaks yang disingkat untuknya
disebut a substitusi referensi. Kita bisa menulis baris di atas seperti ini:
SUMBER = a.cpp bc.cpp def.cpp
OBJS = $(SUMBER:%.cpp=%.o)
OBJS = $(SOURCES:.cpp=.o) # Singkatan, ketika keduanya dimulai dengan %.
substitusi gaya rc
Terkadang pemanggilan "patsubst" atau referensi substitusi yang setara dapat menjadi
agak samar. Makepp menyediakan opsi lain yang terkadang lebih nyaman:
gaya rc substitusi (disebut demikian karena dipelopori oleh rc shell).
MODUL := a bc def
SUMBER := $(MODULES).cpp
OBJS := $(MODUL).o
Apa yang terjadi di sini adalah ketika dievaluasi "$(MODULES).cpp", makepp menambahkan ".cpp"
untuk setiap kata di "$(MODULES)", dan juga untuk "$(MODULES).o". Secara umum, setiap
karakter sebelum "$(variable)" (hingga pembatas kata) ditempatkan sebelum masing-masing
kata di "$(variable)", dan karakter apa pun setelah "$(variable)" ditempatkan setelah
setiap kata dalam "$(variabel)". Jadi hasil evaluasi "x$(MODULES)y" adalah
"xay xbcy xdefy".
Kode Perl sebaris
Jika Anda tahu Perl, Anda dapat memasukkan kode Perl untuk melakukan manipulasi sewenang-wenang pada
variabel ke dalam makefile Anda. Ini paling baik diilustrasikan dengan sebuah contoh:
SUMBER := a.cpp bc.cpp def.cpp
perl_begin
($OBJS = $SUMBER) =~ s/\.cpp/.o/g;
perl_end
Setiap teks antara pernyataan "perl_begin" dan pernyataan "perl_end" dilewatkan
kepada penerjemah perl. Anda juga dapat menggunakan sintaks yang lebih perlish yang setara dari "perl
{ ... }". Semua variabel dalam makefile (kecuali variabel otomatis) dapat diakses
sebagai skalar Perl. Variabel apa pun yang Anda atur dengan kode Perl akan dapat diakses di
makefile.
Jadi yang dilakukan contoh di atas adalah menyalin teks dari $SOURCES ke $OBJS, lalu
ganti setiap kemunculan ".cpp" dengan ".o".
Dalam contoh ini, menggunakan kode Perl sebaris mungkin tidak diperlukan karena ada yang lebih mudah
dan cara yang lebih jelas untuk melakukan manipulasi yang sama. Tapi kekuatan penuh dari perl
juru bahasa tersedia jika Anda membutuhkannya.
Sumber/Objek Pemisahan dan Varian membangun
Sampai saat ini semua makefile yang kita lihat menempatkan file objek di tempat yang sama
direktori sebagai file sumber. Ini biasanya cara makefile ditulis, dan itu
tentu cara paling sederhana untuk melakukan sesuatu. Namun, misalkan Anda harus mengkompilasi
program pada mesin Linux dan mesin Solaris. Biner dari dua mesin
tidak kompatibel, tentu saja. Berbeda dengan make tradisional, makepp cukup pintar untuk mengetahuinya
bahwa jika kompilasi terakhir ada di Linux, dan kompilasi saat ini ada di Solaris, a
kompilasi ulang dari segala sesuatu yang diperlukan.
Tapi ini masih menyisakan masalah: ketika Anda mengkompilasi ulang di Solaris, Anda menghapus Linux Anda
binari. Kemudian ketika Anda beralih kembali ke Linux, Anda harus mengkompilasi ulang semuanya lagi,
padahal source filenya tidak berubah.
Masalah terkait adalah jika Anda membangun program dengan beberapa opsi berbeda. Memperkirakan
misalnya Anda biasanya mengkompilasi program Anda dengan optimasi:
CFLAG := -O2
%.o: %.c
$(CC) $(CFLAGS) -c $(masukan) -o $(keluaran)
program_saya: *.o
$(CC) $(masukan) -o $(keluaran)
Namun, Anda menemukan bug, dan Anda ingin mengaktifkan debugging pada semua file, jadi Anda melakukannya
ubah "CFLAGS":
CFLAGS := -g -DMALLOC_DEBUG
Makepp menyadari bahwa perintah build telah berubah, dan perlu dikompilasi ulang
semuanya. Tetapi sekali lagi, kompilasi ulang dengan debugging diaktifkan menghapus binari lama Anda, jadi
jika Anda ingin mengaktifkan kembali pengoptimalan, semuanya harus dikompilasi ulang lagi, bahkan
file yang tidak berubah.
Solusi yang jelas untuk masalah ini adalah dengan menempatkan arsitektur-dependen atau build-
file yang bergantung pada varian dalam subdirektori terpisah. Ada dua teknik dasar untuk
melakukan ini: secara eksplisit menentukan direktori alternatif, atau menggunakan repositori.
Eksplisit spesifikasi of bergantian direktori
Anda bisa menulis ulang aturan di makefile Anda untuk membuang objek ke yang berbeda
direktori, seperti ini:
ARCH ;= $(Shell uname -s)-$(Shell uname -m)-$(Shell uname -r)
# ARCH menjadi output dari perintah uname.
# Terkadang orang hanya menggunakan $(shell uname -m), tapi
# ini akan sama untuk FreeBSD dan Linux di
#x86. -r tidak terlalu berguna di Linux, tetapi adalah
# penting untuk OS lain: binari untuk SunOS 5.8
# biasanya tidak berjalan di SunOS 5.7. Perhatikan ;= yang
# berarti mengevaluasi ini paling banyak sekali, ketika pertama kali
# diperlukan.
CFLAG := -O2
OBJDIR ;= $(ARCH)-optimal
$(OBJDIR)/%.o: %.c
$(CC) $(CFLAGS) -c $(masukan) -o $(keluaran)
$(OBJDIR)/program_saya: $(OBJDIR)/*.o
$(CC) $(masukan) -o $(keluaran)
Sekarang ketika Anda menjalankan makepp, "ARCH" secara otomatis diatur ke sesuatu yang berbeda untuk masing-masing
arsitektur, dan semua objek ditempatkan di direktori yang berbeda untuk masing-masing
arsitektur, sehingga mereka tidak saling menimpa. Jika Anda ingin mengkompilasi ulang, nyalakan
debugging, maka Anda harus mengubah "CFLAGS" dan "OBJDIR".
Satu masalah dengan pendekatan ini adalah bahwa pemuatan implisit tidak akan berfungsi lagi. Satu-satunya
tempat yang makepp tahu untuk mencari makefile ketika perlu membangun sesuatu ada di
direktori file yang coba dibuat. Jika ini masalah bagi Anda, maka Anda bisa
secara eksplisit memberi tahu makepp ke mana harus mencari menggunakan pernyataan "load_makefile".
Repositori
Repositori adalah cara ajaib menggunakan makefile yang ditulis untuk meletakkan objek di
direktori yang sama, tetapi memiliki makepp secara otomatis menempatkan objek di direktori yang berbeda.
Misalkan kita mulai dengan makefile asli di atas (sebelum kita memodifikasinya untuk menempatkan
objek di direktori yang berbeda), dan kami telah bekerja di Linux jadi direktori sumber kami
diisi dengan binari Linux. Ketika kami ingin mengkompilasi ulang kode kami di Solaris alih-alih
Linux, kami menggunakan perintah berikut daripada hanya mengetik "makepp":
%mkdir solaris
% cd surya
% makepp-R..
Apa yang dilakukan opsi "-R" untuk makepp dalam kasus ini adalah mendeklarasikan direktori ".." (yang
adalah direktori sumber asli) sebagai repositori. Repositori hanyalah cara untuk mendapatkan
makepp untuk mengelabui semua tindakan agar percaya bahwa semua file dalam satu pohon direktori adalah
sebenarnya terletak di pohon direktori yang berbeda dalam sistem file. Dalam contoh di atas,
makepp berpura-pura bahwa semua file di ".." (dan semua subdirektori "..") sebenarnya
di direktori saat ini (dan subdirektori yang sesuai).
Lebih tepatnya, repositori adalah tempat makepp mencari jika perlu menemukan file yang
tidak ada di pohon direktori saat ini. Jika file ada di direktori saat ini
pohon, itu digunakan; jika tidak ada, tetapi file ada di repositori, makepp membuat
tautan simbolik sementara dari file dalam repositori ke direktori saat ini. (A
tautan simbolik adalah alias untuk file asli. Ini seperti salinan, kecuali mencoba untuk
mengakses tautan sebenarnya mengakses file asli.) Tindakan aturan kemudian bertindak pada
file di direktori saat ini, tetapi sebenarnya merujuk file di repositori.
Dalam contoh ini, awalnya kami memulai dengan direktori baru yang kosong solaris. (Tidak
harus kosong, tentu saja, dan ini tidak akan menjadi kedua kalinya Anda menjalankan makepp.) Makepp adalah
jalankan di direktori ini, dan ia melihat bahwa tidak ada makefile di sana. Namun, ada
makefile di repositori, jadi itu menautkan yang dari repositori, dan membacanya. NS
aturan pola di makefile yang mengubah file ".c" menjadi file ".o" menyebabkan makepp menjadi
tautkan semua file ".c" yang dibutuhkan dari repositori, dan jalankan perintah kompilasi
dari solaris subdirektori. Oleh karena itu file ".o" sekarang ditempatkan ke dalam solaris
subdirektori, bukan di direktori tingkat atas. Ketika perintah build selesai, sembarang
file yang ditautkan dari repositori dihapus, jadi solaris subdirektori akan berisi
hanya file biner untuk Solaris. Semua file ".o" yang ada di repositori adalah
tidak dimodifikasi, jadi ketika Anda kembali ke mesin Linux dan menjalankan kembali makepp, sebagian besar
program tidak perlu dikompilasi ulang.
Kadang-kadang mungkin lebih nyaman menggunakan bentuk perintah repositori yang berbeda.
Tiga perintah shell di atas dapat sepenuhnya diganti dengan satu perintah berikut:
% makepp -R solaris=. -F solaris
Apa yang dilakukan adalah untuk mengatakan bahwa file di direktori saat ini harus ditautkan ke dalam
solaris subdirektori sesuai kebutuhan. (NS solaris subdirektori akan dibuat
otomatis jika tidak ada.) Kemudian opsi "-F" menyebabkan makepp melakukan cd ke
solaris direktori dan jalankan makefile di sana (yang akan ditautkan dari
gudang).
Menggunakan repositori tidak memiliki kelemahan yang sama seperti secara eksplisit menentukan objek
direktori; makefiles akan dimuat secara implisit seperti yang diharapkan, karena sejauh makepp adalah
bersangkutan, makefile sebenarnya berada di direktori yang sama dengan file target. Namun,
jika build Anda tidak hanya melibatkan satu tetapi beberapa pohon direktori, menggunakan repositori dapat
menjadi cukup rumit.
Repositori hanyalah cara berpura-pura bahwa hal-hal yang terletak di satu tempat di file
sistem sebenarnya berada di tempat yang berbeda selama durasi build. Ini sangat
teknik kuat yang dapat digunakan untuk lebih dari sekadar memisahkan sumber Anda dan
binari. Untuk detail lebih lanjut, lihat makepp_repositories.
Debugging Makefile
Log File
Jika Anda memiliki prosedur pembuatan yang rumit, Anda akan menemukan bahwa makepp lebih banyak membangun kembali
sering daripada yang Anda pikir mereka perlu dibangun kembali. Atau Anda mungkin menemukan bahwa itu tidak membangun kembali
hal-hal ketika seharusnya. Anda tidak harus terus menatap makefile Anda sampai Anda melihat
masalah. Pada setiap build, makepp menghasilkan file log yang menjelaskan aturan mana yang dipikirkannya
itu seharusnya digunakan untuk membangun setiap target, file apa yang menurutnya bergantung pada setiap target
aktif, dan (jika memang memutuskan untuk membangun kembali) mengapa dianggap perlu membangun kembali. Biner ini
file dilihat makepplog, utilitas mppl.
Format output kurang lebih cukup jelas. Indentasi menyampaikan kedalaman dalam makepp's
pohon inferensi. Misalkan targetnya adalah "semua", dan "semua" tergantung pada "program_saya", dan
"my_program" bergantung pada "*.o", yang bergantung pada file ".c" yang sesuai. Pesan log
terkait dengan "semua" tidak akan diindentasi, pesan log yang terkait dengan membangun target
"my_program" akan diindentasi dua spasi, mencatat pesan yang terkait dengan membangun salah satu dari
file objek akan diindentasi 4 spasi, dan pesan log yang terkait dengan membangun salah satu dari
file sumber akan diindentasi 6 spasi.
Jika Anda melakukan pembuatan paralel (menggunakan opsi baris perintah "-j"), urutannya
pesan dalam file log hampir tidak masuk akal karena pesan dari yang berbeda
target akan diselingi. Anda dapat mencoba men-debug serial make terlebih dahulu.
Umum kesalahan in makefile
Tidak menentukan semua dependensi
Makepp dirancang untuk menjadi sangat pintar dalam menemukan dependensi, dan jika Anda hanya
gunakan perintah kompiler Unix C atau C++ standar, sebenarnya agak sulit untuk
dapatkan makepp untuk melewatkan sesuatu. (Tolong kirimkan saya contoh jika Anda menemukan bahwa itu tidak terjawab
sesuatu, jadi saya bisa membuat makepp lebih pintar.) Namun, jika Anda menjalankan perintah lainnya
daripada kompilasi, atau berurusan dengan bahasa selain C atau C++, jauh lebih mudah untuk
mengalami masalah.
Jika Anda tidak memberi tahu makepp semua dependensi file, dan itu tidak dapat menyimpulkannya dengan
menguraikan baris perintah atau memindai file untuk disertakan, maka mungkin tidak membangun kembali a
file ketika seharusnya. Anda dapat membuat kesalahan semacam ini lebih kecil kemungkinannya dengan hanya menggunakan
variabel otomatis dalam tindakan Anda, daripada mengulangi daftar ketergantungan. Untuk
contoh,
gabungan_file: abc
lakukan_sesuatu abcd > file_gabungan
memiliki kesalahan karena d disebutkan dalam tindakan tetapi tidak dalam daftar ketergantungan. Jika
perintah telah ditulis menggunakan variabel otomatis seperti ini:
gabungan_file: abcd
lakukan_sesuatu $(input) > gabungan_file
maka tidak mungkin melakukan kesalahan ini.
Cara lain agar ketergantungan yang hilang dapat terjadi adalah jika suatu program benar-benar menggunakan file
tetapi tidak mengambil nama file pada baris perintah. Misalnya, jika Anda
kompilasi kode Fortran, makepp saat ini tidak tahu cara memindai yang disertakan
file. Dengan demikian, Anda harus secara eksplisit mencantumkan file apa pun yang disertakan.
Satu hal yang terkadang berguna untuk pengujian adalah memulai dengan benar-benar bersih
direktori--hanya minimum yang menurut Anda perlu--dan membangun kembali absolut
semuanya dari awal. Ini paling mudah dilakukan dengan menggunakan repositori,
seperti ini:
% rm -rf uji-bangun-dir
% makepp -R uji-bangun-dir=. -F uji-pembangunan-dir
Jika build gagal karena beberapa file tidak ada, itu berarti makepp tidak
menyadari beberapa file adalah ketergantungan, karena hanya menautkan file dari repositori
yang menurutnya dibutuhkan. Melakukan tes ini sesekali dapat menghemat waktu
debug nanti. Saya telah mengerjakan proyek di mana ini tidak pernah dilakukan selama berbulan-bulan
karena kompilasi ulang begitu lama. Akibatnya, banyak masalah kecil merayap masuk.
Ada beberapa file objek yang tidak memiliki file sumber lagi, beberapa file sumber
yang tidak pernah dibangun kembali dengan benar oleh perintah preprocessing, dll.
Tentu saja, ini tidak akan menangkap semua dependensi yang hilang, tetapi akan menangkap beberapa di antaranya.
Tidak menentukan semua target
Anda harus menentukan semua file yang diubah oleh perintah tertentu sebagai target, atau makepp
mungkin tidak menyadari bahwa mereka telah berubah. Anda dapat menentukan lebih dari satu target. Untuk
contoh,
y.tab.h y.tab.c: parse.y
yacc -d parse.y
Jika Anda lupa untuk menentukan y.tab.h sebagai target, maka makepp tidak akan tahu
membangun kembali y.tab.h menggunakan perintah ini, dan file yang bergantung pada y.tab.h mungkin tidak
dikompilasi ulang setelah perintah yacc dijalankan.
Tolong sarankan hal-hal yang menurut Anda membingungkan atau berbahaya, dan saya akan mencatatnya
mereka atau coba perbaiki makepp agar tidak berbahaya lagi.
Gunakan makepp_tutorial online menggunakan layanan onworks.net