Ön koşul olarak makefile değişkeni


134

Bir Makefile'de, bir deploytarifin ENVkendini doğru şekilde yürütmek için bir ortam değişkenine ihtiyacı vardır , ancak diğerleri umursamaz, örneğin:

ENV = 

.PHONY: deploy hello

deploy:
    rsync . $(ENV).example.com:/var/www/myapp/

hello:
    echo "I don't care about ENV, just saying hello!"

Bu değişkenin ayarlandığından nasıl emin olabilirim, örneğin: bu makefile değişkenini konuşlandırma tarifinin önkoşulu olarak bildirmenin bir yolu var mı:

deploy: make-sure-ENV-variable-is-set

?

Teşekkür ederim.


Ne demek, "bu değişkenin ayarlandığından emin olun"? Doğrulama mı yoksa sağlama mı demek istiyorsun? Daha önce ayarlanmamışsa, makeayarlamalı veya bir uyarı vermeli veya önemli bir hata mı oluşturmalısınız?
Beta

1
Bu değişken kullanıcının kendisi tarafından belirtilmelidir - ortamını bilen tek kişi olduğu için (dev, prod ...) - örneğin arayarak make ENV=devancak unutursa ENV=dev, deploytarif başarısız olur ...
abernier

Yanıtlar:


171

Bu ENV, tanımsızsa ve bir şeye gereksinim duyarsa ölümcül bir hataya neden olur (yine de GNUMake'de).

.PHONY: check-env'yi dağıtın

dağıtma: check-env
	...

diğer-şey-o-ihtiyaç-env: check-env
	...

check-env:
eğer ENV
	$ (ENV hatası tanımsız)
endif

(Eğer undef ve endif girintili değilse - Makefile çalıştırılmadan önce etkili olan "görenler" i kontrol ederler . "$ (Error" bir sekme ile girintilidir, böylece sadece kural bağlamında çalışır.)


12
Önkoşul olarak check-env içermeyenENV is undefined bir görev çalıştırırken alıyorum .
raine

@rane: Bu ilginç. Minimal bir tam örnek verebilir misiniz?
Beta

2
@rane boşluklar ile sekme karakteri arasındaki fark nedir?
Ağustos'ta

8
@esmit: Evet; Bunu cevaplamalıydım. Benim çözümümde, çizgi bir SEKME ile başlar, bu yüzden check-envkuraldaki bir komuttur ; Make, kuralı uygulamadan / sonuna kadar genişletmez. Bir SEKME ile başlamazsa (@ rane örneğinde olduğu gibi), Make bunu bir kuralda olmadığı şeklinde yorumlar ve hedeften bağımsız olarak herhangi bir kural çalıştırmadan önce değerlendirir.
Beta

1
`` Çözümümde çizgi bir SEKME ile başlıyor, bu yüzden check -v kuralında bir komut; `` Hangi satırdan bahsediyorsun? Benim durumumda, if koşulu TAB ile başlıyorsa bile if koşulu her seferinde değerlendirilir
Dhawal

103

Kökteki değişkenin tanımlandığını kontrol eden örtük bir koruma hedefi oluşturabilirsiniz:

guard-%:
    @ if [ "${${*}}" = "" ]; then \
        echo "Environment variable $* not set"; \
        exit 1; \
    fi

Daha sonra guard-ENVVAR, bir değişkenin tanımlandığını iddia etmek istediğiniz yere aşağıdaki gibi bir hedef eklersiniz :

change-hostname: guard-HOSTNAME
        ./changeHostname.sh ${HOSTNAME}

Çağrıyı make change-hostnameeklemeden HOSTNAME=somehostnameçağırırsanız, bir hata alırsınız ve derleme başarısız olur.


5
Bu akıllıca bir çözüm, beğendim :)
Elliot Chance

Bunun eski bir yanıt olduğunu biliyorum, ama belki de hala biri izliyor, aksi takdirde bunu yeni bir soru olarak tekrar gönderebilirim ... Belirlenen ortam değişkenlerini kontrol etmek için bu örtük hedef "muhafızı" uygulamaya çalışıyorum ve işe yarıyor ancak prensip olarak "guard-%" kuralındaki komutlar aslında kabuğa yazdırılır. Bunu bastırmak istiyorum. Bu nasıl mümkün olabilir?
genomicsio

2
TAMAM. çözümü kendim buldum ... @ kural komut satırlarının başında arkadaşım ...
genomicsio

4
Tek astar: if [ -z '${${*}}' ]; then echo 'Environment variable $* not set' && exit 1; fiD
c24w

4
bu seçilen cevap olmalıdır. daha temiz bir uygulama.
sb32134

46

Satır içi varyant

Benim makefiles, normalde gibi bir ifade kullanın:

deploy:
    test -n "$(ENV)"  # $$ENV
    rsync . $(ENV).example.com:/var/www/myapp/

Nedenler:

  • basit bir astar
  • kompakt
  • değişkeni kullanan komutların yakınında bulunur

Hata ayıklama için önemli olan yorumu unutmayın:

test -n ""
Makefile:3: recipe for target 'deploy' failed
make: *** [deploy] Error 1

... sizi Makefile'yi aramaya zorlar ...

test -n ""  # $ENV
Makefile:3: recipe for target 'deploy' failed
make: *** [deploy] Error 1

... neyin yanlış olduğunu doğrudan açıklar

Global varyant (bütünlük için fakat sorulmamış)

Makefile'nizin üstüne şunları da yazabilirsiniz:

ifeq ($(ENV),)
  $(error ENV is not set)
endif

Uyarılar:

  • bu blokta sekme kullanma
  • dikkatli kullanın: cleanENV ayarlanmadıysa hedef bile başarısız olur. Aksi takdirde, Hudson'ın daha karmaşık olan cevabına bakın

Vay. "O blokta sekme kullanma" yı görene kadar bu konuda sorun yaşıyordum. Teşekkür ederim!
Alex K

İyi bir alternatif, ancak başarılı olsa bile "hata mesajı" nın görünmesini sevmiyorum (tüm satır yazdırılıyor)
Jeff

@Jeff Bu makefile temelleri. Çizginin önüne a ekleyin @. -> gnu.org/software/make/manual/make.html#Echoing
Daniel Alder

Bunu denedim, ancak hata durumunda hata mesajı görünmeyecek. Hmm tekrar deneyeceğim. Cevabını kesin olarak oyladı.
Jeff

1
Test yaklaşımını seviyorum. Böyle bir şey kullandım:@test -n "$(name)" || (echo 'A name must be defined for the backup. Ex: make backup name=xyz' && exit 1)
swampfox357

6

Şu ana kadar verilen cevaplarla ilgili olası bir sorun, markaya bağımlılık sırasının tanımlanmamış olmasıdır. Örneğin;

make -j target

targetbirkaç bağımlılığı olduğunda , bunların herhangi bir sırada çalışacağını garanti etmez.

Bunun çözümü (tarifler seçilmeden önce ENV'nin kontrol edileceğini garanti etmek için) herhangi bir reçete dışında markanın ilk geçişi sırasında ENV'yi kontrol etmektir:

## Are any of the user's goals dependent on ENV?
ifneq ($(filter deploy other-thing-that-needs-ENV,$(MAKECMDGOALS)),$())
ifndef ENV 
$(error ENV not defined)
endif
endif

.PHONY: deploy

deploy: foo bar
    ...

other-thing-that-needs-ENV: bar baz bono
    ...

Burada kullanılan farklı işlevler / değişkenler hakkında bilgi edinebilirsiniz ve $()"hiçbir şey" ile karşılaştırdığımızı açıkça belirtmenin bir yoludur.


6

En iyi cevabı buldum, diğer PHONY hedefleri hariç, bir gereklilik olarak kullanılamaz. Gerçek bir dosya olan bir hedef için bağımlılık olarak kullanılırsa, kullanıldığında check-envbu dosya hedefi yeniden oluşturulmaya zorlanır.

Diğer cevaplar geneldir (örneğin, değişken Makefile'deki tüm hedefler için gereklidir ) veya kabuğu kullanın, örneğin ENV eksikse, hedeften bağımsız olarak sonlandırılır.

Her iki konuya da bulduğum bir çözüm

ndef = $(if $(value $(1)),,$(error $(1) not set))

.PHONY: deploy
deploy:
    $(call ndef,ENV)
    echo "deploying $(ENV)"

.PHONY: build
build:
    echo "building"

Çıktı şöyle görünüyor

$ make build
echo "building"
building
$ make deploy
Makefile:5: *** ENV not set.  Stop.
$ make deploy ENV="env"
echo "deploying env"
deploying env
$

value bazı korkutucu uyarılar var, ama bu basit kullanım için en iyi seçim olduğuna inanıyorum.


5

Gördüğüm gibi komutun kendisi ENV değişkenine ihtiyaç duyar, böylece komutun kendisinde kontrol edebilirsiniz:

.PHONY: deploy check-env

deploy: check-env
    rsync . $(ENV).example.com:/var/www/myapp/

check-env:
    if test "$(ENV)" = "" ; then \
        echo "ENV not set"; \
        exit 1; \
    fi

Buradaki sorun, deploybu değişkene ihtiyaç duyan tek tarifin mutlaka olması değildir. Bu çözümle, ENVher birinin durumunu test etmek zorundayken ... bununla tek bir tür önkoşul olarak ilgilenmek isterim.
abernier

4

Bunun eski olduğunu biliyorum, ama gelecekteki ziyaretçiler için kendi deneyimlerime gireceğimi düşündüm, çünkü biraz daha temiz bir IMHO.

Tipik haliyle, makekullanır sh(varsayılan kabuk özel ile ayarlanan SHELLdeğişken ). İçinde shve türevlerinde, bir ortam değişkeni alınmamışsa hata iletisi ile çıkmak veya: ile null yapmak önemsizdir ${VAR?Variable VAR was not set or null}.

Bunu genişleterek, bir ortam değişkeni ayarlanmadıysa diğer hedefleri başarısızlığa sokmak için kullanılabilen yeniden kullanılabilir bir hedef yazabiliriz:

.check-env-vars:
    @test $${ENV?Please set environment variable ENV}


deploy: .check-env-vars
    rsync . $(ENV).example.com:/var/www/myapp/


hello:
    echo "I don't care about ENV, just saying hello!"

Dikkat edilmesi gerekenler:

  • Kaçan dolar işareti ( $$), içinde değil, kabuğa genişletmeyi ertelemek için gereklidirmake
  • Kullanımı testsadece kabuğun içeriğini yürütmeye çalışmasını engellemektir VAR(başka önemli bir amaca hizmet etmez)
  • .check-env-varstrivially daha çevre değişkenleri kontrol etmek için uzatılabilir, her biri (örneğin, sadece bir satır ekler @test $${NEWENV?Please set environment variable NEWENV})

ENVBoşluklar varsa , bu başarısız görünüyor (en azından benim için)
eddiegroves

2

ifdefFarklı bir hedef yerine kullanabilirsiniz .

.PHONY: deploy
deploy:
    ifdef ENV
        rsync . $(ENV).example.com:/var/www/myapp/
    else
        @echo 1>&2 "ENV must be set"
        false                            # Cause deploy to fail
    endif

Hey, cevabınız için teşekkürler deploy, ancak önerinizin ürettiği yinelenen kod nedeniyle kabul edemezsiniz ... ENVdurum değişkenini kontrol etmek zorunda olan tek bir tarif daha fazlası değildir .
abernier

sonra sadece refactor. İfdef bloğundan önce .PHONY: deployve deploy:ifadelerini kullanın ve çoğaltmayı kaldırın. (btw Cevabı doğru yöntemi yansıtacak şekilde düzenledim)
Dwight Spencer
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.