Kita diharuskan menggunakan Makefile untuk mengumpulkan semuanya untuk proyek kita, tetapi profesor kita tidak pernah menunjukkan caranya.
Saya hanya punya satu file a3driver.cpp
,. Pengemudi mengimpor kelas dari lokasi "/user/cse232/Examples/example32.sequence.cpp"
,.
Itu dia. Segala sesuatu yang lain terkandung dengan .cpp
.
Bagaimana saya bisa membuat Makefile sederhana yang membuat executable disebut a3a.exe
?
Jawaban:
Karena ini untuk Unix, executable tidak memiliki ekstensi.
Satu hal yang perlu diperhatikan adalah
root-config
utilitas yang menyediakan kompilasi yang tepat dan menghubungkan bendera; dan pustaka yang tepat untuk membangun aplikasi terhadap root. Itu hanya detail yang terkait dengan audiens asli untuk dokumen ini.Make Me Baby
atau Anda Tidak Pernah Lupa Saat Pertama Kali Dibuat
Diskusi pengantar make, dan cara menulis makefile sederhana
Apa itu Make? Dan Mengapa Saya Harus Peduli?
Alat yang disebut Make adalah pengelola dependensi build. Yaitu, perlu mengetahui perintah apa yang harus dijalankan dalam rangka untuk mengambil proyek perangkat lunak Anda dari kumpulan file sumber, file objek, perpustakaan, header, dll, dll .- beberapa di antaranya mungkin telah berubah baru-baru ini --- dan mengubahnya menjadi versi program yang benar dan terkini.
Sebenarnya, Anda dapat menggunakan Make untuk hal-hal lain juga, tetapi saya tidak akan membicarakan hal itu.
Makefile yang Sepele
Misalkan Anda memiliki direktori yang berisi:,
tool
tool.cc
tool.o
support.cc
support.hh
dansupport.o
yang bergantung padaroot
dan seharusnya dikompilasi ke dalam program yang disebuttool
, dan anggap bahwa Anda telah meretas file sumber (yang berarti yang adatool
sekarang sudah ketinggalan zaman) dan ingin kompilasi program.Untuk melakukan ini sendiri, Anda bisa
Periksa apakah salah satu
support.cc
atausupport.hh
lebih baru darisupport.o
, dan jika demikian jalankan perintah sukaPeriksa apakah salah satu
support.hh
atautool.cc
lebih baru daritool.o
, dan jika demikian jalankan perintah sukaPeriksa apakah
tool.o
lebih baru daritool
, dan jika demikian jalankan perintah sepertiFiuh! Sungguh merepotkan! Ada banyak yang harus diingat dan beberapa peluang untuk melakukan kesalahan. (BTW-- rincian baris perintah yang ditampilkan di sini tergantung pada lingkungan perangkat lunak kami. Yang ini berfungsi di komputer saya.)
Tentu saja, Anda bisa menjalankan ketiga perintah setiap waktu. Itu akan berhasil, tetapi itu tidak skala baik ke bagian besar perangkat lunak (seperti DOGS yang membutuhkan lebih dari 15 menit untuk dikompilasi dari bawah ke atas di MacBook saya).
Alih-alih, Anda dapat menulis file bernama
makefile
seperti ini:dan cukup ketik
make
di baris perintah. Yang akan melakukan tiga langkah yang ditunjukkan di atas secara otomatis.Baris yang tidak diindentifikasi di sini memiliki bentuk "target: dependencies" dan beri tahu Make bahwa perintah yang terkait (indents lines) harus dijalankan jika ada dependensi yang lebih baru daripada target. Yaitu, garis ketergantungan menggambarkan logika apa yang perlu dibangun kembali untuk mengakomodasi perubahan dalam berbagai file. Jika
support.cc
perubahan itu berartisupport.o
harus dibangun kembali, tetapitool.o
dapat dibiarkan sendiri. Kapansupport.o
perubahantool
harus dibangun kembali.Perintah-perintah yang terkait dengan setiap baris dependensi dimatikan dengan sebuah tab (lihat di bawah) harus memodifikasi target (atau setidaknya menyentuhnya untuk memperbarui waktu modifikasi).
Variabel, Aturan yang Terpasang, dan Barang Lainnya
Pada titik ini, makefile kami hanya mengingat pekerjaan yang perlu dilakukan, tetapi kami masih harus mencari tahu dan mengetik setiap perintah yang diperlukan secara keseluruhan. Tidak harus seperti itu: Make adalah bahasa yang kuat dengan variabel, fungsi manipulasi teks, dan banyak aturan bawaan yang bisa membuat ini lebih mudah bagi kita.
Buat Variabel
Sintaks untuk mengakses variabel make adalah
$(VAR)
.Sintaks untuk menetapkan ke variabel Make adalah:
VAR = A text value of some kind
(atauVAR := A different text value but ignore this for the moment
).Anda dapat menggunakan variabel dalam aturan seperti versi perbaikan dari makefile kami:
yang sedikit lebih mudah dibaca, tetapi masih membutuhkan banyak pengetikan
Buat Fungsi
GNU make mendukung berbagai fungsi untuk mengakses informasi dari sistem file atau perintah lain pada sistem. Dalam hal ini kami tertarik untuk
$(shell ...)
memperluas ke output dari argumen, dan$(subst opat,npat,text)
yang menggantikan semua contohopat
dengannpat
dalam teks.Mengambil keuntungan dari ini memberi kita:
yang lebih mudah diketik dan lebih mudah dibaca.
Perhatikan itu
Aturan Implisit dan Pola
Kami biasanya berharap bahwa semua file sumber C ++ harus diperlakukan dengan cara yang sama, dan Make menyediakan tiga cara untuk menyatakan ini:
Aturan implisit sudah ada, dan beberapa akan dibahas di bawah ini. Aturan pola ditentukan dalam bentuk seperti
yang berarti bahwa file objek dihasilkan dari file sumber C dengan menjalankan perintah yang ditunjukkan, di mana variabel "otomatis"
$<
meluas ke nama dependensi pertama.Aturan bawaan
Make memiliki seluruh aturan bawaan yang berarti bahwa seringkali, sebuah proyek dapat dikompilasi oleh makefile yang sangat sederhana.
GNU make built in rule untuk file sumber C adalah yang ditunjukkan di atas. Demikian pula kita membuat file objek dari file sumber C ++ dengan aturan seperti
$(CXX) -c $(CPPFLAGS) $(CFLAGS)
.File objek tunggal ditautkan menggunakan
$(LD) $(LDFLAGS) n.o $(LOADLIBES) $(LDLIBS)
, tetapi ini tidak akan berfungsi dalam kasus kami, karena kami ingin menautkan banyak file objek.Variabel Digunakan Dengan Aturan Built-in
Aturan bawaan menggunakan seperangkat variabel standar yang memungkinkan Anda menentukan informasi lingkungan lokal (seperti tempat menemukan ROOT termasuk file) tanpa menulis ulang semua aturan. Yang paling mungkin menarik bagi kami adalah:
CC
- kompiler C untuk digunakanCXX
- kompiler C ++ untuk digunakanLD
- penghubung untuk digunakanCFLAGS
- flag kompilasi untuk file sumber CCXXFLAGS
- flag kompilasi untuk file sumber C ++CPPFLAGS
- flag untuk c-preprocessor (biasanya menyertakan path file dan simbol yang didefinisikan pada baris perintah), digunakan oleh C dan C ++LDFLAGS
- bendera tautanLDLIBS
- perpustakaan untuk ditautkanMakefile Dasar
Dengan memanfaatkan aturan bawaan kami dapat menyederhanakan makefile kami menjadi:
Kami juga telah menambahkan beberapa target standar yang melakukan tindakan khusus (seperti membersihkan direktori sumber).
Perhatikan bahwa ketika make dipanggil tanpa argumen, ia menggunakan target pertama yang ditemukan dalam file (dalam hal ini semua), tetapi Anda juga dapat memberi nama target untuk mendapatkan apa yang membuat
make clean
menghapus file objek dalam kasus ini.Kami masih memiliki semua dependensi yang dikodekan.
Beberapa Perbaikan Misterius
Perhatikan itu
make
makals -A
Anda melihat file bernama.depend
yang berisi hal-hal yang terlihat seperti membuat garis ketergantunganBacaan Lainnya
Tahu Bug dan Catatan Sejarah
Bahasa input untuk Make adalah spasi putih. Secara khusus, garis tindakan dependensi harus dimulai dengan tab . Tetapi serangkaian ruang bisa terlihat sama (dan memang ada editor yang akan secara diam-diam mengubah tab menjadi spasi atau sebaliknya), yang menghasilkan file Make yang terlihat benar dan masih tidak berfungsi. Ini diidentifikasi sebagai bug sejak awal, tetapi ( ceritanya ) itu tidak diperbaiki, karena sudah ada 10 pengguna.
(Ini disalin dari pos wiki yang saya tulis untuk mahasiswa pascasarjana fisika.)
sumber
-pthread
flag menyebabkangcc
untuk mendefinisikan macro yang diperlukan,-D_REENTRANT
tidak perlu.root-config
). Alternatif yang lebih umum dengan kemampuan yang sama harus diusulkan jika ada atau harus ditinggalkan begitu saja. Saya tidak downvote karena daftar dan penjelasan tentang makro make paling sering digunakan.Saya selalu berpikir ini lebih mudah untuk dipelajari dengan contoh terperinci, jadi inilah cara saya memikirkan makefile. Untuk setiap bagian Anda memiliki satu baris yang tidak berlekuk dan itu menunjukkan nama bagian diikuti oleh dependensi. Ketergantungan dapat berupa bagian lain (yang akan dijalankan sebelum bagian saat ini) atau file (yang jika diperbarui akan menyebabkan bagian saat ini dijalankan lagi di lain waktu Anda menjalankan
make
).Berikut ini adalah contoh cepat (perlu diingat bahwa saya menggunakan 4 spasi di mana saya harus menggunakan tab, Stack Overflow tidak akan membiarkan saya menggunakan tab):
Saat Anda mengetik
make
, ia akan memilih bagian pertama (a3driver). a3driver tergantung pada a3driver.o, jadi itu akan masuk ke bagian itu. a3driver.o tergantung pada a3driver.cpp, jadi itu hanya akan berjalan jika a3driver.cpp telah berubah sejak dijalankan terakhir. Dengan asumsi itu telah (atau belum pernah dijalankan), ia akan mengkompilasi a3driver.cpp ke file .o, kemudian kembali ke a3driver dan kompilasi executable akhir.Karena hanya ada satu file, bahkan dapat dikurangi menjadi:
Alasan saya menunjukkan contoh pertama adalah karena ia menunjukkan kekuatan makefile. Jika Anda perlu mengkompilasi file lain, Anda bisa menambahkan bagian lain. Berikut ini contoh dengan secondFile.cpp (yang memuat dalam header bernama secondFile.h):
Dengan cara ini jika Anda mengubah sesuatu di secondFile.cpp atau secondFile.h dan mengkompilasi ulang, itu hanya akan mengkompilasi ulang secondFile.cpp (bukan a3driver.cpp). Atau secara bergantian, jika Anda mengubah sesuatu di a3driver.cpp, itu tidak akan mengkompilasi ulang secondFile.cpp.
Beri tahu saya jika Anda memiliki pertanyaan tentangnya.
Ini juga tradisional untuk memasukkan bagian bernama "semua" dan bagian bernama "bersih". "all" biasanya akan membangun semua executable, dan "clean" akan menghapus "build artefacts" seperti file .o dan executable:
EDIT: Saya tidak melihat Anda menggunakan Windows. Saya pikir satu-satunya perbedaan adalah mengubah
-o a3driver
ke-o a3driver.exe
.sumber
Mengapa semua orang suka mencantumkan file sumber? Perintah find sederhana dapat menangani itu dengan mudah.
Berikut adalah contoh dari C ++ Makefile yang sederhana. Cukup taruh di direktori yang berisi
.C
file dan kemudian ketikmake
...sumber
Anda memiliki dua opsi.
Opsi 1: makefile paling sederhana = NO MAKEFILE.
Ganti nama "a3driver.cpp" menjadi "a3a.cpp", dan kemudian pada baris perintah tulis:
Dan itu saja. Jika Anda menggunakan GNU Make, gunakan "make" atau "gmake" atau apa pun.
Opsi 2: makefile 2-baris.
sumber
nmake
. Barislink
perintah juga terlihat sangat spesifik untuk kompiler tertentu, dan paling tidak harus mendokumentasikan yang mana.File Make Anda akan memiliki satu atau dua aturan ketergantungan tergantung pada apakah Anda mengkompilasi dan menautkan dengan satu perintah, atau dengan satu perintah untuk kompilasi dan satu untuk tautan.
Ketergantungan adalah pohon aturan yang terlihat seperti ini (perhatikan bahwa indentasi harus TAB):
Ada harus menjadi baris kosong setelah perintah untuk target, dan harus ada tidak ada garis kosong sebelum perintah. Target pertama dalam makefile adalah tujuan keseluruhan, dan target lain dibangun hanya jika target pertama bergantung padanya.
Jadi makefile Anda akan terlihat seperti ini.
sumber
Saya sarankan (perhatikan bahwa indentasi adalah TAB):
atau
Saran terakhir sedikit lebih baik karena menggunakan kembali GNU Membuat aturan implisit. Namun, agar dapat berfungsi, file sumber harus memiliki nama yang sama dengan yang dapat dieksekusi akhir (yaitu:
tool.c
dantool
).Perhatikan, tidak perlu mendeklarasikan sumber. File objek menengah dihasilkan menggunakan aturan implisit. Akibatnya, ini
Makefile
berfungsi untuk C dan C ++ (dan juga untuk Fortran, dll ...).Perhatikan juga, secara default, Makefile menggunakan
$(CC)
sebagai tautan.$(CC)
tidak berfungsi untuk menautkan file objek C ++. Kami memodifikasiLINK.o
hanya karena itu. Jika Anda ingin mengkompilasi kode C, Anda tidak perlu memaksakanLINK.o
nilainya.Tentu, Anda juga dapat menambahkan flag kompilasi Anda dengan variabel
CFLAGS
dan menambahkan pustaka AndaLDLIBS
. Sebagai contoh:Catatan satu sisi: jika Anda harus menggunakan perpustakaan eksternal, saya sarankan untuk menggunakan pkg-config untuk mengatur
CFLAGS
danLDLIBS
:Pembaca yang penuh perhatian akan melihat bahwa ini
Makefile
tidak membangun kembali dengan benar jika satu header diubah. Tambahkan baris ini untuk memperbaiki masalah:-MMD
memungkinkan untuk membangun file .d yang berisi fragmen Makefile tentang dependensi header. Baris kedua hanya menggunakannya.Yang pasti, Makefile yang ditulis dengan baik juga harus mencakup
clean
dandistclean
aturan:Perhatikan,
$(RM)
sama denganrm -f
, tetapi praktik yang baik untuk tidak meneleponrm
langsung.The
all
rule juga dihargai. Agar berfungsi, itu harus menjadi aturan pertama dari file Anda:Anda juga dapat menambahkan
install
aturan:DESTDIR
kosong secara default. Pengguna dapat mengaturnya untuk menginstal program Anda di sistem alternatif (wajib untuk proses cross-kompilasi). Pengelola paket untuk beberapa distribusi juga dapat berubahPREFIX
untuk menginstal paket Anda/usr
.Satu kata terakhir: Jangan letakkan file sumber di dalam sub-direktori. Jika Anda benar-benar ingin melakukannya, simpan ini
Makefile
di direktori root dan gunakan path lengkap untuk mengidentifikasi file Anda (yaitusubdir/file.o
).Jadi untuk meringkas, Makefile lengkap Anda akan terlihat seperti:
sumber
make
yang saya tahu (GNU Make dan BSD Make) membutuhkan baris kosong antara aturan. Namun, ada banyakmake
implementasi dengan bug mereka sendiri ^ Spesifikasi khusus.Saya menggunakan jawaban friedmud . Saya melihat ini sebentar, dan sepertinya ini cara yang baik untuk memulai. Solusi ini juga memiliki metode yang jelas untuk menambahkan flag compiler. Saya menjawab lagi, karena saya membuat perubahan agar berfungsi di lingkungan saya, Ubuntu dan g ++. Lebih banyak contoh kerja adalah guru terbaik, kadang-kadang.
Makefiles tampaknya sangat kompleks. Saya menggunakan satu, tapi itu menghasilkan kesalahan yang terkait dengan tidak menautkan di pustaka g ++. Konfigurasi ini menyelesaikan masalah itu.
sumber