अगर वेरिएबल सेट नहीं है तो मेकफाइल एबट करें


151

मैं मेकफाइल के वैरिएबल के सेट / वैल्यू के आधार पर मेक / मेफाइल निष्पादन को कैसे रद्द कर सकता / सकती हूं?

मैं इसके साथ आया था, लेकिन केवल तभी काम करता है जब कॉलर स्पष्ट रूप से एक लक्ष्य नहीं चलाता (यानी makeकेवल चलाता है )।

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)

मुझे लगता है कि यह एक अच्छा विचार होगा, लेकिन मेक इन मैनुअल में कुछ भी नहीं मिला:

ifndef MY_FLAG
.ABORT
endif

जवाबों:


271

TL; DR : errorफ़ंक्शन का उपयोग करें :

ifndef MY_FLAG
$(error MY_FLAG is not set)
endif

ध्यान दें कि लाइनों को इंडेंट नहीं किया जाना चाहिए। अधिक सटीक रूप से, इन पंक्तियों से पहले कोई टैब नहीं होना चाहिए।


सामान्य समाधान

यदि आप कई चर का परीक्षण करने जा रहे हैं, तो यह उसके लिए एक सहायक कार्य को परिभाषित करने के लायक है:

# 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))))

और यहाँ इसका उपयोग कैसे करना है:

$(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)


यह इस तरह एक त्रुटि उत्पादन होगा:

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

टिप्पणियाँ:

असली जाँच यहाँ की जाती है:

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

यह ifndefसशर्त के व्यवहार को दर्शाता है , जिससे कि एक खाली मान के लिए परिभाषित चर को "अपरिभाषित" भी माना जाता है। लेकिन यह केवल सरल चर और स्पष्ट रूप से खाली पुनरावर्ती चर के लिए सच है:

# 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)

जैसा कि टिप्पणियों में @VictorSergienko ने सुझाव दिया है, थोड़ा अलग व्यवहार वांछित हो सकता है:

$(if $(value $1)परीक्षण यदि मान गैर-रिक्त है। यदि वेरिएबल को खाली मान से परिभाषित किया जाए तो यह कभी-कभी ठीक होता है । मैं उपयोग करूंगा$(if $(filter undefined,$(origin $1)) ...

तथा:

इसके अलावा, अगर यह एक निर्देशिका है और यह मौजूद होना चाहिए जब चेक चलाया जाता है, तो मैं उपयोग करूंगा $(if $(wildcard $1))। लेकिन एक और समारोह होगा।

लक्ष्य-विशिष्ट जाँच

समाधान का विस्तार करना भी संभव है ताकि किसी व्यक्ति को केवल एक चर की आवश्यकता हो सकती है यदि एक निश्चित लक्ष्य को लागू किया जाता है।

$(call check_defined, ...) नुस्खा के अंदर से

बस नुस्खा में जाँच ले जाएँ:

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

प्रमुख @संकेत कमांड गूंजता है और :वास्तविक कमांड है, शेल नो-ऑप स्टब

लक्ष्य नाम दिखा रहा है

check_definedसमारोह भी उत्पादन लक्ष्य का नाम (के माध्यम से प्रदान करने के लिए सुधार किया जा सकता $@चर):

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 `$@')))

ताकि, अब एक विफल चेक एक अच्छी तरह से स्वरूपित आउटपुट का उत्पादन करे:

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

check-defined-MY_FLAG विशेष लक्ष्य

व्यक्तिगत रूप से मैं ऊपर दिए गए सरल और सरल समाधान का उपयोग करूंगा। हालांकि, उदाहरण के लिए, यह उत्तर वास्तविक जांच करने के लिए एक विशेष लक्ष्य का उपयोग करने का सुझाव देता है। कोई इसे सामान्य बनाने की कोशिश कर सकता है और लक्ष्य को एक अंतर्निहित पैटर्न नियम के रूप में परिभाषित कर सकता है:

# 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 :

उपयोग:

foo :|check-defined-BAR

ध्यान दें कि ऑर्डर-ओनली ( ) शर्त के check-defined-BARरूप में सूचीबद्ध है ।|...

पेशेवरों:

  • (यकीनन) एक अधिक स्वच्छ वाक्यविन्यास

विपक्ष:

मेरा मानना ​​है कि इन सीमाओं को कुछ evalजादू और माध्यमिक विस्तार हैक का उपयोग करके दूर किया जा सकता है , हालांकि मुझे यकीन नहीं है कि यह इसके लायक है।


वास्तव में क्या? मैक का उपयोग कभी नहीं किया, हालांकि मुझे लगता है कि इसमें डिफ़ॉल्ट रूप से स्थापित मेक का एक और कार्यान्वयन है (उदाहरण के लिए जीएनयू मेक के बजाय बीएसडी मेक)। मैं आपको make --versionपहले चरण के रूप में जांचने का सुझाव दूंगा।
एल्डार अबुलिसिमोव

1
यह 3.81 बनाने में काम नहीं कर रहा है। यह हमेशा त्रुटियों, भले ही चर परिभाषित किया गया है (और गूँज सकता है)।
ऑरेंजडॉग

आह, आपको इसे लिंक किए गए डुप्लिकेट की तरह सटीक रूप से संरचना करना होगा।
ऑरेंजडॉग

1
@bibstha मैंने मन में आने वाले विकल्पों को जोड़ा, कृपया अद्यतन उत्तर पढ़ें।
एल्डर एबुलिसिमोव

2
मैं एक स्पष्टीकरण-के लिए-noobies (मेरे जैसे) जोड़ूंगा कि ifndef को इंडेंट करने की आवश्यकता नहीं है :) मुझे वह टिप कहीं और मिला और अचानक मेरी सभी त्रुटियों का अर्थ हो गया।
हेलिओस

40

शेल फ़ंक्शन का उपयोग करें test:

foo:
    test $(something)

उपयोग:

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

3
यह वही है जब मैंने उसी मुद्दे का सामना किया था - धन्यवाद, मेसा! मैंने दो मामूली संशोधन किए: 1) मैंने एक checkforsomethingलक्ष्य बनाया जो केवल उसमें था testऔर fooउस पर निर्भर बना था , और 2) मैंने चेक को @if test -z "$(something)"; then echo "helpful error here"; exit 1; fiबदले में बदल दिया । इससे मुझे एक उपयोगी त्रुटि जोड़ने की क्षमता मिली, और मुझे नए लक्ष्य का नाम थोड़ा और अधिक संकेत देने की अनुमति दी कि क्या गलत हुआ।
ब्रायन गेरार्ड

इस के साथ मैंMakefile:5: *** missing separator. Stop.
silgon

7
कॉम्पैक्टनेस के लिए मैंने test -n "$(something) || (echo "message" ; exit 1)स्पष्ट से बचने के लिए उपयोग किया if
15:29 बजे user295691

@ साइलॉगन आप शायद टैब के बजाय रिक्त स्थान का उपयोग करने के लिए प्रेरित कर रहे हैं।
eweb

9

आप परीक्षण करने के लिए एक IF का उपयोग कर सकते हैं:

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

परिणाम:

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

[कमांड के लिए एक उपनाम है test, इसलिए यह @Messa के समान ही उत्तर है। यह अधिक कॉम्पैक्ट है, हालांकि, और त्रुटि संदेश पीढ़ी शामिल है।
15:29 बजे user295691

6

अनसेट वैरिएबल के लिए शेल एरर हैंडलिंग का उपयोग करें (डबल नोट करें $):

$ 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

यदि आपको एक कस्टम त्रुटि संदेश की आवश्यकता है, तो इसे निम्नलिखित में जोड़ें ?:

$ 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

सादगी और संक्षिप्तता के लिए:

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

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

आउटपुट के साथ:

$ make bar
Makefile:2: *** foo is undefined. Stop.
$ make bar foo="something"
echo "foo is $$foo"
foo is something
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.