Değişken ayarlanmamışsa makefile'ı iptal et


151

Bir makefile değişkeninin ayarlanmadığı / değerlenmediği bir make / makefile yürütmesini nasıl durdurabilirim?

Ben bu ile geldi, ama sadece arayan açıkça bir hedef (yani makesadece çalışır ) çalıştırmazsa çalışır .

ifeq ($(MY_FLAG),)
abort:   ## This MUST be the first target :( ugly
    @echo Variable MY_FLAG not set && false
endif

all:
    @echo MY_FLAG=$(MY_FLAG)

Bunun gibi bir şeyin iyi bir fikir olacağını düşünüyorum, ancak marka kılavuzunda hiçbir şey bulamadık:

ifndef MY_FLAG
.ABORT
endif

Yanıtlar:


271

TL; DR : errorFonksiyonu kullanın :

ifndef MY_FLAG
$(error MY_FLAG is not set)
endif

Satırların girintili olmaması gerektiğini unutmayın. Daha doğrusu, bu satırlardan önce hiçbir sekme olmamalıdır.


Genel çözüm

Birçok değişkeni test edecekseniz, bunun için yardımcı bir işlev tanımlamaya değer:

# Check that given variables are set and all have non-empty values,
# die with an error otherwise.
#
# Params:
#   1. Variable name(s) to test.
#   2. (optional) Error message to print.
check_defined = \
    $(strip $(foreach 1,$1, \
        $(call __check_defined,$1,$(strip $(value 2)))))
__check_defined = \
    $(if $(value $1),, \
      $(error Undefined $1$(if $2, ($2))))

Ve işte nasıl kullanılacağı:

$(call check_defined, MY_FLAG)

$(call check_defined, OUT_DIR, build directory)
$(call check_defined, BIN_DIR, where to put binary artifacts)
$(call check_defined, \
            LIB_INCLUDE_DIR \
            LIB_SOURCE_DIR, \
        library path)


Bu, şöyle bir hata üretir:

Makefile:17: *** Undefined OUT_DIR (build directory).  Stop.

Notlar:

Gerçek kontrol burada yapılır:

$(if $(value $1),,$(error ...))

Bu, ifndefkoşullu davranışı yansıtır , böylece boş bir değere tanımlanan bir değişken de "tanımsız" olarak kabul edilir. Ancak bu sadece basit değişkenler ve açıkça boş yinelemeli değişkenler için geçerlidir:

# ifndef and check_defined consider these UNDEFINED:
explicitly_empty =
simple_empty := $(explicitly_empty)

# ifndef and check_defined consider it OK (defined):
recursive_empty = $(explicitly_empty)

Yorumlarda @VictorSergienko tarafından önerildiği gibi, biraz farklı bir davranış istenebilir:

$(if $(value $1)değerin boş olup olmadığını test eder. Değişken boş bir değerle tanımlanırsa bazen sorun olmaz . Kullanırdım$(if $(filter undefined,$(origin $1)) ...

Ve:

Ayrıca, eğer bu bir dizin ve kontrol çalıştırıldığında var olması gerekir , ben kullanmak istiyorum $(if $(wildcard $1)). Ama başka bir işlev olurdu.

Hedefe özgü kontrol

Çözümü, yalnızca belirli bir hedef çağrıldığında değişken gerektirebilecek şekilde genişletmek de mümkündür.

$(call check_defined, ...) tarifin içinden

Sadece çekleri tarife taşıyın:

foo :
    @:$(call check_defined, BAR, baz value)

Önde gelen @işaret, komut yankısını kapatır ve :gerçek komuttur, bir kabuk no-op saplamasıdır .

Hedef adı gösteriliyor

check_definedİşlevi de çıkış (sağlanan hedef ismi geliştirilebilir $@değişken):

check_defined = \
    $(strip $(foreach 1,$1, \
        $(call __check_defined,$1,$(strip $(value 2)))))
__check_defined = \
    $(if $(value $1),, \
        $(error Undefined $1$(if $2, ($2))$(if $(value @), \
                required by target `$@')))

Böylece, başarısız bir kontrol güzelce biçimlendirilmiş bir çıktı üretir:

Makefile:7: *** Undefined BAR (baz value) required by target `foo'.  Stop.

check-defined-MY_FLAG özel hedef

Şahsen yukarıdaki basit ve anlaşılır çözümü kullanırdım. Bununla birlikte, örneğin, bu cevap , gerçek kontrolü gerçekleştirmek için özel bir hedef kullanmanızı önerir. Bunu genellemeye çalışabilir ve hedefi örtük bir kalıp kuralı olarak tanımlayabiliriz:

# Check that a variable specified through the stem is defined and has
# a non-empty value, die with an error otherwise.
#
#   %: The name of the variable to test.
#   
check-defined-% : __check_defined_FORCE
    @:$(call check_defined, $*, target-specific)

# Since pattern rules can't be listed as prerequisites of .PHONY,
# we use the old-school and hackish FORCE workaround.
# You could go without this, but otherwise a check can be missed
# in case a file named like `check-defined-...` exists in the root 
# directory, e.g. left by an accidental `make -t` invocation.
.PHONY : __check_defined_FORCE
__check_defined_FORCE :

Kullanımı:

foo :|check-defined-BAR

Bunun yalnızca sipariş ( ) önkoşulu check-defined-BARolarak listelendiğine dikkat edin .|...

Artıları:

  • (tartışmasız) daha temiz bir sözdizimi

Eksileri:

  • Özel bir hata mesajı belirtilemez
  • Çalıştırmak make -t(bkz . Tarifleri Yürütmek Yerine ) kök dizininizi birçok dosyayla kirletir check-defined-.... Bu, kalıp kurallarının bildirilememesi.PHONY gerçeğinin üzücü bir dezavantajıdır .

Ben buna değdiğinden emin olmasam da , bu sınırlamaların bazı evalsihirli ve ikincil genişletme saldırıları kullanılarak aşılabileceğine inanıyorum .


Tam olarak ne? Mac'i hiç kullanmadım, ancak varsayılan olarak yüklü bir başka uygulaması var sanırım (örn. GNU Make yerine BSD Make). make --versionİlk adım olarak kontrol etmenizi öneririm .
Eldar Abusalimov

1
Bu marka 3.81'de işe yaramıyor gibi görünüyor. Değişken tanımlanmış olsa bile (ve yinelenebilir) her zaman hata verir.
OrangeDog

Ah, bağlantılı kopyadaki gibi tam olarak yapılandırmanız gerekir.
OrangeDog

1
@bibstha Akla gelen seçenekleri ekledim, lütfen güncellenmiş cevabı okuyun.
Eldar Abusalimov

2
Bence girintili olmaması gerekiyorsa noobies (benim gibi) için bir açıklama eklemek istiyorum :) Ben başka bir yerde bu ipucu bulundu ve aniden tüm hatalar mantıklı.
helios

40

Kabuk işlevini kullanın test:

foo:
    test $(something)

Kullanımı:

$ make foo
test 
Makefile:2: recipe for target 'foo' failed
make: *** [foo] Error 1
$ make foo something=x
test x

3
Aynı sorunla karşılaştığımda kullandığım bu - teşekkürler, Messa! İki küçük değişiklik yaptım: 1) checkforsomethingSadece içinde olan testve buna foobağlı hale gelen bir hedef oluşturdum ve 2) @if test -z "$(something)"; then echo "helpful error here"; exit 1; fibunun yerine kontrolü değiştirdim . Bu bana yararlı bir hata ekleme yeteneğini verdi ve yeni hedefin adını neyin yanlış gittiğini biraz daha göstergesel yapmama izin verdi.
Brian Gerard

Bunu alıyorumMakefile:5: *** missing separator. Stop.
silgon

7
Kompaktlık test -n "$(something) || (echo "message" ; exit 1)için açık olanlardan kaçınırdım if.
user295691

silgon, muhtemelen bir sekme yerine boşluklar kullanarak girintili oluyorsunuz.
eweb

9

Test etmek için bir IF kullanabilirsiniz:

check:
        @[ "${var}" ] || ( echo ">> var is not set"; exit 1 )

Sonuç:

$ make check
>> var is not set
Makefile:2: recipe for target 'check' failed
make: *** [check] Error 1

[komutu için bir takma testaddır, bu yüzden bu yukarıdaki @Messa ile aynı cevaptır. Bu daha kompakttır ve hata mesajı oluşturmayı içerir.
user295691

6

Ayarlanmamış değişkenler için kabuk hata işlemeyi kullanın (çifte not edin $):

$ cat Makefile
foo:
        echo "something is set to $${something:?}"

$ make foo
echo "something is set to ${something:?}"
/bin/sh: something: parameter null or not set
make: *** [foo] Error 127


$ make foo something=x
echo "something is set to ${something:?}"
something is set to x

Özel bir hata iletisine ihtiyacınız varsa, şunu ekleyin ?:

$ cat Makefile
hello:
        echo "hello $${name:?please tell me who you are via \$$name}"

$ make hello
echo "hello ${name:?please tell me who you are via \$name}"
/bin/sh: name: please tell me who you are via $name
make: *** [hello] Error 127

$ make hello name=jesus
echo "hello ${name:?please tell me who you are via \$name}"
hello jesus

2

Basitlik ve kısalık için:

$ cat Makefile
check-%:
        @: $(if $(value $*),,$(error $* is undefined))

bar:| check-foo
        echo "foo is $$foo"

Çıkışlarla:

$ make bar
Makefile:2: *** foo is undefined. Stop.
$ make bar foo="something"
echo "foo is $$foo"
foo is something
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.