Saya mencoba melakukan hal berikut. Ada sebuah program, sebut saja foo-bin
, yang mengambil satu file masukan dan menghasilkan dua file keluaran. Aturan Makefile bodoh untuk ini adalah:
file-a.out file-b.out: input.in
foo-bin input.in file-a.out file-b.out
Namun, ini tidak menunjukkan make
dengan cara apa pun bahwa kedua target akan dibuat secara bersamaan. Tidak apa-apa saat dijalankan make
dalam serial, tetapi kemungkinan akan menyebabkan masalah jika seseorang mencoba make -j16
atau sesuatu yang sama gilanya.
Pertanyaannya adalah apakah ada cara untuk menulis aturan Makefile yang tepat untuk kasus seperti itu? Jelas, ini akan menghasilkan DAG, tetapi entah bagaimana panduan GNU make tidak menentukan bagaimana kasus ini dapat ditangani.
Menjalankan kode yang sama dua kali dan hanya menghasilkan satu hasil tidak mungkin dilakukan, karena penghitungannya membutuhkan waktu (pikirkan: jam). Mengeluarkan hanya satu file juga akan agak sulit, karena sering digunakan sebagai input ke GNUPLOT yang tidak tahu bagaimana menangani hanya sebagian kecil dari file data.
Jawaban:
Saya akan menyelesaikannya sebagai berikut:
Dalam hal ini paralel make akan 'membuat serial' membuat a dan b tetapi karena membuat b tidak melakukan apapun maka tidak perlu waktu.
sumber
file-b.out
dibuat seperti yang dinyatakan dan kemudian dihapus secara misterius,make
tidak akan dapat membuatnya kembali karenafile-a.out
masih ada. Ada pemikiran?file-a.out
maka solusi di atas berfungsi dengan baik. Ini menyarankan peretasan: ketika hanya satu dari a atau b yang ada, maka dependensi harus diurutkan sedemikian rupa sehingga file yang hilang muncul sebagai output dariinput.in
. Beberapa$(widcard...)
s,$(filter...)
s,$(filter-out...)
s dll, seharusnya sudah cukup. Ugh.foo-bin
jelas akan membuat salah satu file terlebih dahulu (menggunakan resolusi mikrodetik), jadi Anda harus memastikan Anda memiliki urutan dependensi yang benar ketika kedua file ada.touch file-a.out
sebagai perintah kedua setelahfoo-bin
pemanggilan.touch file-b.out
sebagai perintah kedua di aturan pertama dan duplikat perintah di aturan kedua. Jika salah satu file hilang atau lebih tua dariinput.in
, perintah hanya akan dijalankan sekali dan akan membuat ulang kedua file denganfile-b.out
lebih baru darifile-a.out
.Triknya adalah dengan menggunakan aturan pola dengan banyak target. Dalam kasus tersebut make akan mengasumsikan bahwa kedua target dibuat dengan satu pemanggilan perintah.
Perbedaan interpretasi antara aturan pola dan aturan normal ini tidak sepenuhnya masuk akal, tetapi berguna untuk kasus seperti ini, dan ini didokumentasikan di manual.
Trik ini dapat digunakan untuk sejumlah file keluaran selama namanya memiliki beberapa substring umum untuk
%
dicocokkan. (Dalam hal ini substring yang umum adalah ".")sumber
make
akan menjalankan perintah yang sama dua kali secara bersamaan.foo%in bar%src: input.in
:-)$(subst .,%,$(varA) $(varB)): input.in
(diuji dengan GNU make 4.1).Make tidak memiliki cara intuitif untuk melakukan ini, tetapi ada dua solusi yang layak.
Pertama, jika target yang terlibat memiliki batang yang sama, Anda dapat menggunakan aturan awalan (dengan GNU make). Artinya, jika Anda ingin memperbaiki aturan berikut:
Anda bisa menulisnya seperti ini:
(Menggunakan variabel pola-aturan $ *, yang merupakan singkatan dari bagian yang cocok dari pola)
Jika Anda ingin menjadi portabel untuk implementasi non-GNU Make atau jika file Anda tidak dapat dinamai agar sesuai dengan aturan pola, ada cara lain:
Ini memberitahu make bahwa input.in.intermediate tidak akan ada sebelum make dijalankan, jadi ketiadaannya (atau timestamp-nya) tidak akan menyebabkan foo-bin dijalankan secara palsu. Dan apakah file-a.out atau file-b.out atau keduanya sudah kedaluwarsa (relatif terhadap input.in), foo-bin hanya akan dijalankan satu kali. Anda dapat menggunakan .SECONDARY alih-alih .INTERMEDIATE, yang akan menginstruksikan agar TIDAK menghapus nama file hipotetis input.in.intermediate. Metode ini juga aman untuk build paralel.
Titik koma di baris pertama penting. Ini membuat resep kosong untuk aturan itu, sehingga Make tahu bahwa kami benar-benar akan memperbarui file-a.out dan file-b.out (terima kasih @siulkiulki dan orang lain yang menunjukkan hal ini)
sumber
-j1
build). Juga, jika makefile terputus dan membiarkaninput.in.intermediate
file di sekitarnya menjadi sangat bingung.file-a.out file-b.out: input.in.intermediate
kefile-a.out file-b.out: input.in.intermediate ;
Lihat stackoverflow.com/questions/37873522/… untuk lebih jelasnya.Ini didasarkan pada jawaban kedua @ deemer yang tidak bergantung pada aturan pola, dan ini memperbaiki masalah yang saya alami dengan penggunaan solusi bersarang.
Saya akan menambahkan ini sebagai komentar pada jawaban @ deemer, tetapi saya tidak bisa karena saya baru saja membuat akun ini dan tidak memiliki reputasi apa pun.
Penjelasan: Resep kosong diperlukan agar Make dapat melakukan pembukuan yang tepat untuk menandai
file-a.out
danfile-b.out
telah dibangun kembali. Jika Anda memiliki target perantara lain yang bergantung padanyafile-a.out
, Make akan memilih untuk tidak membangun perantara luar, dengan mengklaim:sumber
Setelah GNU Make 4.3 (19 Januari 2020), Anda dapat menggunakan "target eksplisit yang dikelompokkan". Ganti
:
dengan&:
.File NEWS dari GNU Make mengatakan:
sumber
Beginilah cara saya melakukannya. Pertama, saya selalu memisahkan prasyarat dari resep. Kemudian dalam hal ini target baru untuk melakukan resep tersebut.
Pertama kali:
1. Kami mencoba membangun file-a.out, tetapi dummy-a-and-b.out perlu dilakukan terlebih dahulu, jadi buatlah menjalankan resep dummy-a-and-b.out.
2. Kami mencoba untuk membangun file-b.out, dummy-a-and-b.out up to date.
Waktu kedua dan selanjutnya:
1. Kami mencoba membangun file-a.out: melihat prasyarat, prasyarat normal mutakhir, prasyarat sekunder tidak ada, jadi diabaikan.
2. Kami mencoba membangun file-b.out: lihat prasyarat, prasyarat normal mutakhir, prasyarat sekunder tidak ada, jadi diabaikan.
sumber
file-b.out
make tidak ada cara untuk membuatnya kembali.Sebagai perpanjangan dari jawaban @ deemer, saya telah menggeneralisasikannya menjadi sebuah fungsi.
Kerusakan:
Hapus spasi di awal / akhir dari argumen pertama
Buat target variabel ke nilai unik untuk digunakan sebagai target perantara. Untuk kasus ini, nilainya adalah .inter.file-a.out_file-b.out.
Buat resep kosong untuk keluaran dengan target unik sebagai ketergantungan.
Nyatakan target unik sebagai perantara.
Akhiri dengan referensi ke target unik sehingga fungsi ini dapat digunakan secara langsung dalam resep.
Perhatikan juga penggunaan eval di sini karena eval berkembang menjadi tidak ada sehingga perluasan fungsi sepenuhnya hanyalah target unik.
Harus memberikan kredit pada Aturan Atom di GNU Make yang diinspirasikan dari fungsi ini.
sumber
Untuk mencegah beberapa eksekusi paralel dari aturan dengan beberapa keluaran dengan
make -jN
, gunakan.NOTPARALLEL: outputs
. Dalam kasus Anda:sumber