पूर्वापेक्षा के रूप में मेकफाइल चर


134

मेकफाइल में, एक deployनुस्खा ENVको ठीक से निष्पादित करने के लिए सेट करने के लिए एक पर्यावरण चर की आवश्यकता होती है, जबकि अन्य परवाह नहीं करते हैं, जैसे:

ENV = 

.PHONY: deploy hello

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

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

मैं कैसे सुनिश्चित कर सकता हूं कि यह चर सेट है, उदाहरण के लिए: इस मेकफाइल चर को घोषित नुस्खा की शर्त के रूप में घोषित करने का एक तरीका है, जैसे:

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

?

धन्यवाद।


आपका क्या मतलब है, "सुनिश्चित करें कि यह चर सेट है"? क्या आपका मतलब सत्यापन या सुनिश्चित करना है? यदि यह पहले सेट नहीं किया गया था, तो इसे makeसेट करना चाहिए , या चेतावनी देनी चाहिए , या एक घातक त्रुटि उत्पन्न करनी चाहिए ?
बीटा

1
इस चर को स्वयं उपयोगकर्ता द्वारा निर्दिष्ट किया जाना है - क्योंकि वह केवल एक ही है जो अपने पर्यावरण (देव, उत्पादों ...) को जानता है - उदाहरण के लिए कॉल करके make ENV=devलेकिन अगर वह भूल जाता है ENV=dev, तो deployनुस्खा विफल हो जाएगा ...
abernier

जवाबों:


171

यह ENVअपरिभाषित होने पर घातक त्रुटि का कारण बनेगा और कुछ को इसकी जरूरत है (GNUMake में, वैसे भी)।

.PHONY: चेक-एनवी तैनात करें

तैनात: चेक-एन.वी.
	...

other-thing-that-need-env: check-env
	...

चेक-env:
ifndef ENV
	$ (त्रुटि ईएनवी अपरिभाषित है)
अगर अंत

(ध्यान दें कि ifndef और endif इंडेंट नहीं हैं - वे "क्या देखता है" को नियंत्रित करते हैं, मेकाइल चलाने से पहले प्रभावी होते हैं। "$ (त्रुटि" को एक टैब के साथ इंडेंट किया जाता है ताकि यह केवल नियम के संदर्भ में चले।)


12
मैं ENV is undefinedएक ऐसा कार्य कर रहा हूं, जिसमें एक शर्त के रूप में चेक-एनवी नहीं है।
Raine

@ क्रेन: यह दिलचस्प है। क्या आप एक न्यूनतम पूर्ण उदाहरण दे सकते हैं?
बीटा

2
@ क्रेन रिक्त स्थान बनाम टैब वर्ण में अंतर है?
निकलें

8
@ साइट: हाँ; मुझे इस बारे में जवाब देना चाहिए था। मेरे समाधान में, लाइन एक TAB से शुरू होती है, इसलिए यह check-envनियम में एक आदेश है ; जब तक / जब तक नियम को निष्पादित नहीं किया जाता है, तब तक इसका विस्तार न करें। यदि यह TAB से शुरू नहीं होता है (जैसा कि @ राणे के उदाहरण में), तो इसे नियम में नहीं होने के रूप में व्याख्या करें, और लक्ष्य की परवाह किए बिना किसी भी नियम को चलाने से पहले इसका मूल्यांकन करें।
बीटा

1
`` `मेरे समाधान में, लाइन एक TAB से शुरू होती है, इसलिए यह चेक-एनवी नियम में एक कमांड है?` `` आप किस लाइन के बारे में बात कर रहे हैं? मेरे मामले में, अगर शर्त का मूल्यांकन तब भी किया जाता है जब आईएंडएफ के बाद की रेखा TAB से शुरू होती है
धवल

103

आप एक अंतर्निहित गार्ड लक्ष्य बना सकते हैं, जो यह जांचता है कि स्टेम में चर इस तरह से परिभाषित किया गया है:

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

आप तब एक guard-ENVVARलक्ष्य जोड़ते हैं, जिसमें आप यह सुनिश्चित करना चाहते हैं कि एक चर परिभाषित किया जाए, जैसे:

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

यदि आप कॉल करते हैं make change-hostname, कॉल HOSTNAME=somehostnameमें जोड़े बिना , तो आपको एक त्रुटि मिलेगी, और बिल्ड विफल हो जाएगा।


5
यह एक चतुर समाधान है, मुझे यह पसंद है :)
इलियट संभावना

मुझे पता है कि यह एक प्राचीन उत्तर है, लेकिन शायद कोई अभी भी इसे देख रहा है अन्यथा मैं इसे फिर से एक नए प्रश्न के रूप में पोस्ट कर सकता हूं ... मैं इस निहित लक्ष्य "गार्ड" को लागू करने की कोशिश कर रहा हूं ताकि निर्धारित पर्यावरण चर की जांच हो सके और यह काम करे सिद्धांत रूप में, हालांकि "गार्ड-%" नियम में कमांड वास्तव में शेल पर मुद्रित होते हैं। इसे मैं दबाना चाहूंगा। यह कैसे हो सकता है?
Genomicsio

2
ठीक है। अपने आप को समाधान मिल गया ... @ नियम कमांड लाइनों की शुरुआत में मेरे दोस्त हैं ...
genomicsio

4
एक-लाइनर:: if [ -z '${${*}}' ]; then echo 'Environment variable $* not set' && exit 1; fiडी
c24w

4
यह चयनित उत्तर होना चाहिए। एक क्लीनर कार्यान्वयन।
sb32134

46

इनलाइन वैरिएंट

मेरे मेकफाइल्स में, मैं आमतौर पर एक अभिव्यक्ति का उपयोग करता हूं जैसे:

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

कारण:

  • यह एक साधारण लाइनर है
  • यह कॉम्पैक्ट है
  • यह उन आदेशों के करीब स्थित है जो चर का उपयोग करते हैं

उस टिप्पणी को न भूलें जो डीबगिंग के लिए महत्वपूर्ण है:

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

... जब आप Makefile को देखने के लिए बाध्य करते हैं ...

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

... सीधे समझाता है कि क्या गलत है

वैश्विक संस्करण (पूर्णता के लिए, लेकिन पूछा नहीं गया)

अपने Makefile के शीर्ष पर, आप यह भी लिख सकते हैं:

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

चेतावनी:

  • उस ब्लॉक में टैब का उपयोग न करें
  • देखभाल के साथ उपयोग करें: यहां तक ​​कि cleanईएनवी सेट नहीं होने पर भी लक्ष्य विफल हो जाएगा। अन्यथा हुडोन का उत्तर देखें जो अधिक जटिल है

वाह। जब तक मैंने देखा कि "मैं उस ब्लॉक में टैब का उपयोग नहीं करता हूं, मुझे इससे परेशानी हो रही थी।" धन्यवाद!
एलेक्स के

यह एक अच्छा विकल्प है, लेकिन मुझे यह पसंद नहीं है कि सफल होने पर भी "त्रुटि संदेश" दिखाई देता है (पूरी पंक्ति मुद्रित होती है)
जेफ

@ जेफ यह मेकफाइल मूल बातें है। बस एक के साथ लाइन उपसर्ग @। -> gnu.org/software/make/manual/make.html#Echoing
डैनियल एल्डर

मैंने कोशिश की, लेकिन तब विफलता के मामले में त्रुटि संदेश दिखाई नहीं देगा। हम्म मैं इसे फिर से कोशिश करूँगा। अपने जवाब को सुनिश्चित करने के लिए।
जेफ

1
मुझे टेस्ट अप्रोच पसंद है। मैंने कुछ इस तरह से इस्तेमाल किया:@test -n "$(name)" || (echo 'A name must be defined for the backup. Ex: make backup name=xyz' && exit 1)
swampfox357

6

अब तक दिए गए उत्तरों में एक संभावित समस्या यह है कि मेक इन डिपेंडेंसी ऑर्डर परिभाषित नहीं है। उदाहरण के लिए, दौड़ना:

make -j target

जब targetकुछ निर्भरताएं गारंटी नहीं देती हैं कि ये किसी भी क्रम में चलेंगे।

इसके लिए समाधान (यह गारंटी देने के लिए कि व्यंजनों को चुने जाने से पहले ईएनवी की जांच की जाएगी) ईएनवी को किसी भी नुस्खा के बाहर, पहले पास के दौरान जांचना है:

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

आप यहां उपयोग किए गए विभिन्न कार्यों / चर के बारे में पढ़ सकते हैं और $()स्पष्ट रूप से यह बताने का एक तरीका है कि हम "कुछ भी नहीं" के खिलाफ तुलना कर रहे हैं।


6

मैं सबसे अच्छा जवाब के साथ पाया है एक आवश्यकता के रूप में इस्तेमाल नहीं किया जा सकता है, अन्य PHONY लक्ष्यों को छोड़कर। यदि एक लक्ष्य के लिए एक निर्भरता के रूप में उपयोग किया जाता है जो एक वास्तविक फ़ाइल है, का उपयोग करcheck-env उस फ़ाइल लक्ष्य को फिर से बनाने के लिए मजबूर किया जाएगा।

अन्य उत्तर वैश्विक हैं (जैसे मेकफाइल में सभी लक्ष्यों के लिए चर आवश्यक है ) या शेल का उपयोग करें, उदाहरण के लिए यदि ईएनवी गायब था तो लक्ष्य की परवाह किए बिना समाप्त हो जाएगा।

एक समाधान जो मैंने दोनों मुद्दों के लिए पाया है

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

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

.PHONY: build
build:
    echo "building"

आउटपुट जैसा दिखता है

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

value कुछ डरावने कैविटीज़ हैं, लेकिन इस सरल उपयोग के लिए मेरा मानना ​​है कि यह सबसे अच्छा विकल्प है।


5

जैसा कि मैं देख रहा हूँ कि कमांड को केवल ENV वैरिएबल की जरूरत है ताकि आप इसे कमांड में ही देख सकें:

.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

इसके साथ समस्या यह है कि deployकेवल वही नुस्खा नहीं है जो इस चर की आवश्यकता है। इस समाधान के साथ, मुझे ENVप्रत्येक में से एक के लिए स्थिति का परीक्षण करना होगा ... जबकि मैं इसे एकल (प्रकार) शर्त के रूप में निपटाना चाहूंगा।
२१:

4

मुझे पता है कि यह पुराना है, लेकिन मुझे लगा कि मैं भविष्य के आगंतुकों के लिए अपने स्वयं के अनुभवों के साथ झंकार करूंगा, क्योंकि यह एक छोटा मामला है IMHO।

आमतौर पर, अपने डिफ़ॉल्ट शेल ( विशेष चर के माध्यम से सेट ) के रूप में makeउपयोग करेगा । इसमें और इसके व्युत्पन्न के लिए, यह एक त्रुटि संदेश के साथ बाहर निकलने के लिए तुच्छ है जब इसे सेट या शून्य नहीं किया जाता है:shSHELLsh${VAR?Variable VAR was not set or null}

इसका विस्तार करते हुए, हम एक पुन: प्रयोज्य बनाने का लक्ष्य लिख सकते हैं, जिसका उपयोग अन्य लक्ष्यों को विफल करने के लिए किया जा सकता है यदि कोई पर्यावरण चर सेट नहीं किया गया था:

.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!"

नोट की बातें:

  • बच गए डॉलर के संकेत ($$ ) को खोल के भीतर के बजाय विस्तार को स्थगित करना आवश्यक हैmake
  • का उपयोग testकेवल शेल को रोकने के लिए किया गया है ताकि सामग्री को निष्पादित करने की कोशिश की जा सकेVAR (यह कोई अन्य महत्वपूर्ण उद्देश्य नहीं है)
  • .check-env-varsअधिक पर्यावरण चर के लिए जाँच करने के लिए तुच्छ रूप से बढ़ाया जा सकता है, जिनमें से प्रत्येक केवल एक पंक्ति (जैसे @test $${NEWENV?Please set environment variable NEWENV}) जोड़ता है

यदि ENVरिक्त स्थान शामिल हैं, तो यह मेरे लिए कम से कम विफल रहता है
eddiegroves

2

आप ifdefएक अलग लक्ष्य के बजाय उपयोग कर सकते हैं ।

.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

अरे, अपने उत्तर के लिए thx, लेकिन इसे स्वीकार नहीं कर सकते क्योंकि डुप्लिकेट कोड के कारण आपका सुझाव उत्पन्न होता है ... सभी अधिक deployकेवल एक ही नुस्खा नहीं है जो ENVराज्य चर की जांच कर रहा है ।
अबर्नियर

उसके बाद सिर्फ रिफ्लेक्टर। Ifdef ब्लॉक से पहले .PHONY: deployऔर deploy:स्टेटमेंट्स का उपयोग करें और डुप्लीकेशन को हटा दें। (btw मैंने सही विधि को प्रतिबिंबित करने के लिए उत्तर संपादित किया है)
ड्वाइट स्पेंसर
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.