Tentukan variabel make pada waktu pelaksanaan aturan

209

Di GNUmakefile saya, saya ingin memiliki aturan yang menggunakan direktori sementara. Sebagai contoh:

out.tar: TMP := $(shell mktemp -d)
        echo hi $(TMP)/hi.txt
        tar -C $(TMP) cf $@ .
        rm -rf $(TMP)

Seperti yang tertulis, aturan di atas membuat direktori sementara pada saat aturan diuraikan . Ini berarti bahwa, bahkan saya tidak melihat. Sepanjang waktu, banyak direktori sementara dibuat. Saya ingin menghindari / tmp saya dikotori dengan direktori sementara yang tidak digunakan.

Apakah ada cara untuk menyebabkan variabel hanya didefinisikan ketika aturan dipecat, berbeda dengan setiap kali didefinisikan?

Pikiran utama saya adalah untuk membuang mktemp dan tar ke dalam skrip shell tetapi sepertinya agak tidak enak dipandang.

Emil Sit
sumber

Jawaban:

322

Dalam contoh Anda, TMPvariabel ditetapkan (dan direktori sementara dibuat) setiap kali aturan untuk out.tardievaluasi. Untuk membuat direktori hanya ketika out.tarbenar-benar diaktifkan, Anda perlu memindahkan pembuatan direktori ke dalam langkah-langkah:

out.tar : 
    $(eval TMP := $(shell mktemp -d))
    @echo hi $(TMP)/hi.txt
    tar -C $(TMP) cf $@ .
    rm -rf $(TMP)

Fungsi eval mengevaluasi string seolah-olah telah diketik ke dalam makefile secara manual. Dalam hal ini, ia menetapkan TMPvariabel ke hasil shellpemanggilan fungsi.

edit (dalam menanggapi komentar):

Untuk membuat variabel unik, Anda dapat melakukan hal berikut:

out.tar : 
    $(eval $@_TMP := $(shell mktemp -d))
    @echo hi $($@_TMP)/hi.txt
    tar -C $($@_TMP) cf $@ .
    rm -rf $($@_TMP)

Ini akan menambahkan nama target (out.tar, dalam kasus ini) ke variabel, menghasilkan variabel dengan nama out.tar_TMP. Semoga itu cukup untuk mencegah konflik.

e.James
sumber
2
Keren beberapa klarifikasi ... ini tidak akan cakupan TMP untuk target ini, kan? Jadi jika ada aturan lain yang memiliki penggunaan $ (TMP) sendiri (mungkin paralel dengan -j), mungkin ada konflik? Juga, apakah @echo bahkan perlu? Sepertinya Anda bisa mengabaikannya.
Emil Sit
3
Tampaknya melakukan trik (meskipun agak buram untuk guru non-Make rata-rata :-) Terima kasih!
Emil Sit
29
Hati-hati dengan solusi ini! $(eval $@_TMP := $(shell mktemp -d))akan terjadi ketika Makefile pertama kali dievaluasi bukan dalam urutan prosedur aturan. Dengan kata lain, $(eval ...)terjadi lebih cepat dari yang Anda pikirkan. Meskipun itu mungkin oke untuk contoh ini, metode ini akan menyebabkan masalah untuk beberapa operasi berurutan.
JamesThomasMoon1979
1
@ JamesThomasMoon1979 apakah Anda tahu cara mengatasi keterbatasan itu, misalnya mengevaluasi beberapa variabel yang harusnya merupakan hasil dari langkah-langkah yang dilakukan sebelumnya dalam aturan?
Vadim Kotov
2
Menjawab pertanyaan saya sendiri: solusi bagi saya adalah membuat file selama pelaksanaan aturan 1, dan mencoba untuk mengevaluasi mereka di langkah pertama aturan 2.
Vadim Kotov
63

Cara yang relatif mudah untuk melakukan ini adalah dengan menulis seluruh urutan sebagai skrip shell.

out.tar:
   set -e ;\
   TMP=$$(mktemp -d) ;\
   echo hi $$TMP/hi.txt ;\
   tar -C $$TMP cf $@ . ;\
   rm -rf $$TMP ;\

Saya telah menggabungkan beberapa kiat terkait di sini: https://stackoverflow.com/a/29085684/86967

bangsawan
sumber
3
Ini jelas merupakan yang paling sederhana dan karena itu jawaban terbaik (menghindari @dan evaldan melakukan pekerjaan yang sama). Perhatikan bahwa dalam output Anda, Anda melihat $TMP(misalnya tar -C $TMP ...) meskipun nilainya benar diteruskan ke perintah.
Karl Richter
Inilah yang biasanya dilakukan; Saya harap orang melihat jawaban ini karena dalam praktik tidak ada yang melewati semua upaya yang diterima.
pmos
Bagaimana jika Anda menggunakan perintah khusus shell? Dalam hal ini perintah shell harus menggunakan bahasa shell yang sama dengan yang dibuat oleh panggilan, bukan? Saya telah melihat beberapa makefile dengan shebang.
ptitpion
@ptitpion: Anda mungkin ingin SHELL := /bin/bashdi makefile Anda mengaktifkan fitur spesifik BASH.
nobar
31

Kemungkinan lain adalah dengan menggunakan baris terpisah untuk mengatur variabel Make ketika aturan menyala.

Sebagai contoh, ini adalah makefile dengan dua aturan. Jika aturan diaktifkan, itu menciptakan dir temp dan mengatur TMP ke nama temp dir.

PHONY = ruleA ruleB display

all: ruleA

ruleA: TMP = $(shell mktemp -d testruleA_XXXX)
ruleA: display

ruleB: TMP = $(shell mktemp -d testruleB_XXXX)
ruleB: display

display:
    echo ${TMP}

Menjalankan kode menghasilkan hasil yang diharapkan:

$ ls
Makefile
$ make ruleB
echo testruleB_Y4Ow
testruleB_Y4Ow
$ ls
Makefile  testruleB_Y4Ow
pengguna3124434
sumber
Sama seperti pengingat, GNU make memiliki sintaks untuk prasyarat hanya pesanan yang mungkin diperlukan untuk melengkapi pendekatan ini.
anol
11
Hati-hati dengan solusi ini! ruleA: TMP = $(shell mktemp -d testruleA_XXXX)dan ruleB: TMP = $(shell mktemp -d testruleB_XXXX)akan terjadi ketika Makefile pertama kali dievaluasi. Dengan kata lain, ruleA: TMP = $(shell ...terjadi lebih cepat dari yang Anda pikirkan. Meskipun ini mungkin bekerja untuk kasus khusus ini, metode ini akan menyebabkan masalah untuk beberapa operasi berurutan.
JamesThomasMoon1979
1

Saya tidak suka jawaban "Jangan", tapi ... jangan.

makeVariabel bersifat global dan seharusnya dievaluasi selama tahap "parsing" makefile, bukan selama tahap eksekusi.

Dalam hal ini, selama variabel lokal ke target tunggal, ikuti jawaban @ nobar dan jadikan variabel shell.

Variabel spesifik target juga dianggap berbahaya oleh implementasi make lainnya: kati , Mozilla pymake . Karena itu, target dapat dibangun secara berbeda tergantung pada apakah itu dibangun sendiri, atau sebagai ketergantungan dari target induk dengan variabel target-spesifik. Dan Anda tidak akan tahu ke mana itu , karena Anda tidak tahu apa yang sudah dibangun.

Victor Sergienko
sumber