Kural yürütme zamanında make değişkenini tanımla


209

GNUmakefile'mda geçici bir dizin kullanan bir kurala sahip olmak istiyorum. Örneğin:

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

Yazıldığı gibi, yukarıdaki kural, kuralın ayrıştırıldığı sırada geçici dizini oluşturur . Bu demektir ki, ben bile yapamıyorum. Yıldız her zaman, birçok geçici dizin oluşturulur. / Tmp dosyamın kullanılmayan geçici dizinlerle dolmasından kaçınmak istiyorum.

Değişkenin yalnızca kural tetiklendiğinde tanımlanmasına neden olmanın bir yolu var mı?

Ana düşüncem mktemp ve katranı bir kabuk betiğine dökmek ama biraz çirkin görünüyor.

Yanıtlar:


322

Senin örnekte, TMPher değişken ayarlanır (ve geçici dizini oluşturulan) kuralları için out.tardeğerlendirilir. Dizini yalnızca out.targerçekten tetiklendiğinde oluşturmak için, dizin oluşturma işlemini adımlara taşımanız gerekir:

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

Eval elle makefile içine yazılan sanki işlev bir dize değerlendirir. Bu durumda, TMPdeğişkeni shellişlev çağrısının sonucuna ayarlar .

Düzenle (yorumlara yanıt olarak):

Benzersiz bir değişken oluşturmak için aşağıdakileri yapabilirsiniz:

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

Bu, hedefin adını (bu durumda out.tar) değişkene ekler ve ada sahip bir değişken üretir out.tar_TMP. Umarım bu, çatışmaları önlemek için yeterlidir.


2
Birkaç açıklama yapın ... bu TMP'yi bu hedefe kapsamaz, değil mi? Peki, kendi $ (TMP) kullanımına sahip başka kurallar varsa (muhtemelen -j ile paralel), çakışmalar olabilir mi? Ayrıca, @echo bile gerekli mi? Dışarıda bırakabileceğin anlaşılıyor.
Emil Sit

3
Hile yapmak gibi görünüyor (gerçi biraz ortalama olmayan guru yapmak için opak :-) Teşekkürler!
Emil Sit

29
Bu çözüme dikkat edin! $(eval $@_TMP := $(shell mktemp -d))Makefile kural prosedürlerine göre değil ilk değerlendirildiğinde gerçekleşecektir. Başka bir deyişle, $(eval ...)düşündüğünüzden daha erken gerçekleşir. Bu örnek için bu uygun olabilir, ancak bu yöntem bazı ardışık işlemler için sorunlara neden olacaktır.
JamesThomasMoon1979

1
@ JamesThomasMoon1979, bu sınırlamaların üstesinden nasıl geleceğinizi biliyor musunuz, örneğin, kuralın daha önceki adımlarının bir sonucu olması gereken bazı değişkenleri değerlendirin?
Vadim Kotov

2
Kendi sorumu cevaplıyorum: benim için geçici çözüm 1. kural yürütme sırasında dosya oluşturmak ve 2. kuralın ilk adımında bunları değerlendirmeye çalışmaktı.
Vadim Kotov

63

Bunu yapmanın nispeten kolay bir yolu, tüm diziyi kabuk betiği olarak yazmaktır.

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

İlgili bazı ipuçlarını burada birleştirdim: https://stackoverflow.com/a/29085684/86967


3
Bu kesinlikle en basit ve bu nedenle en iyi cevap (kaçınarak olduğunu @ve evalve aynı işi yapan). Çıktınızda , değerin komuta doğru olarak iletilmesine rağmen $TMP(ör. tar -C $TMP ...) Gördüğünüzü unutmayın.
Karl Richter

Normalde böyle yapılır; Umarım insanlar bu yanıtı görürler çünkü pratikte kimse kabul edilenin tüm çabasını yaşamaz.
pmos

Kabuğa özel komutlar kullanıyorsanız ne olur? Bu durumda kabuk komutları, çağıran dil ile aynı kabuk dilinde olmalıdır, değil mi? Ben shebang ile bazı makefile görmüştü.
ptitpion

@ptitpion: Dosyanızda BASH'a SHELL := /bin/bashözgü özellikleri de etkinleştirmek isteyebilirsiniz .
nobar

31

Başka bir olasılık, bir kural tetiklendiğinde Değişkenler oluştur'u ayarlamak için ayrı satırlar kullanmaktır.

Örneğin, işte iki kurallı bir marka. Bir kural tetiklenirse, geçici bir dizin oluşturur ve TMP'yi geçici dizin adına ayarlar.

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}

Kodun çalıştırılması beklenen sonucu verir:

$ ls
Makefile
$ make ruleB
echo testruleB_Y4Ow
testruleB_Y4Ow
$ ls
Makefile  testruleB_Y4Ow

Hatırlatmak gerekirse , GNU markası bu yaklaşımı tamamlamak için gerekli olabilecek sadece sipariş önkoşulları için bir sözdizimine sahiptir .
anol

11
Bu çözüme dikkat edin! ruleA: TMP = $(shell mktemp -d testruleA_XXXX)ve ruleB: TMP = $(shell mktemp -d testruleB_XXXX)Makefile ilk değerlendirildiğinde gerçekleşecek. Başka bir deyişle, ruleA: TMP = $(shell ...düşündüğünüzden daha erken gerçekleşir. Bu, bu özel durum için işe yarayabilirken, bu yöntem bazı ardışık işlemler için sorunlara neden olacaktır.
JamesThomasMoon1979

1

"Yapma" cevaplarından hoşlanmıyorum, ama ... yapma.

makedeğişkenleri globaldir ve yürütme aşamasında değil, makefile'ın "ayrıştırma" aşamasında değerlendirilmelidir.

Bu durumda, tek bir hedef için yerel değişken olduğu sürece, @ nobar'ın cevabını takip edin ve onu bir kabuk değişkeni yapın.

Hedefe özgü değişkenler de diğer marka uygulamaları tarafından zararlı kabul edilir: kati , Mozilla pymake . Bu nedenle, bir hedef, bağımsız olarak oluşturulmasına veya hedefe özgü bir değişkene sahip bir üst hedefin bağımlılığına bağlı olarak farklı şekilde oluşturulabilir. Ve bunun hangi yoldan olduğunu bilemezsiniz , çünkü zaten neyin inşa edildiğini bilmiyorsunuz.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.