전제 조건으로 Makefile 변수


134

Makefile에서 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!"

이 변수가 설정되어 있는지 확인하는 방법 : 예 :이 makefile 변수를 배포 레시피의 전제 조건으로 선언하는 방법이 있습니까?

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

?

감사합니다.


"이 변수가 설정되어 있는지 확인하십시오"는 무슨 뜻입니까? 확인 또는 확인 하시겠습니까? 이전에 설정하지 않은 경우 make설정하거나 경고하거나 치명적인 오류를 생성해야합니까?
Beta

1
이 변수는 사용자가 직접 지정해야합니다 - 호출하여 - 예를 들어, 그는 하나의 자신의 환경 (dev에, 자극을 ...) 알고있는 사람처럼 make ENV=dev하지만 그는에 잊어 버린 경우 ENV=dev, deploy... 조리법이 실패합니다
abernier

답변:


171

ENV정의되지 않았고 무언가가 필요한 경우 (GNUMake에서) 치명적인 오류가 발생합니다 .

.PHONY : check-env 배포

배포 : check-env
	...

다른 것들이 필요하다 : env : check-env
	...

점검 환경 :
ifndef ENV
	$ (오류 ENV가 정의되지 않았습니다)
엔디 프

(ifndef와 endif는 들여 쓰기되지 않습니다 . Makefile이 실행되기 전에 적용되는 "sees"를 제어 합니다. "$ (error"는 규칙의 컨텍스트에서만 실행되도록 탭으로 들여 쓰기됩니다.)


12
내가지고있어 ENV is undefined작업 실행시 하지 않는 전제 조건으로 체크 ENV이 있습니다.
raine

@rane : 흥미 롭습니다. 최소한의 완전한 예를 들어 줄 수 있습니까?
Beta

2
@rane은 공백과 탭 문자의 차이입니까?
22:00에

8
@esmit : 예; 나는 이것에 대해 대답해야했다. 내 솔루션에서 줄은 TAB으로 시작하므로 check-env규칙 의 명령입니다 . 규칙을 실행할 때까지는 확장하지 않습니다. @rane의 예에서와 같이 TAB으로 시작하지 않으면 Make는 규칙에없는 것으로 해석하고 대상에 관계없이 규칙을 실행하기 전에이를 평가합니다.
베타

1
```내 솔루션에서 줄은 TAB으로 시작하므로 check-env 규칙의 명령입니다 .``` 어떤 줄에 대해 이야기하고 있습니까? 필자의 경우 ifndef 이후 행이 TAB로 시작될 때마다 if 조건이 평가됩니다.
Dhawal

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
그것은 영리한 해결책입니다, 나는 그것을 좋아합니다 :)
Elliot Chance

나는 이것이 고대의 대답이라는 것을 알고 있지만, 아마도 누군가는 여전히 그것을보고 있습니다. 그렇지 않으면 이것을 새로운 질문으로 다시 게시 할 수 있습니다 ... 환경 변수 설정을 확인하기 위해이 암시 적 대상 "가드"를 구현하려고 시도하고 작동합니다. 그러나 원칙적으로 "guard- %"규칙의 명령은 실제로 셸에 인쇄됩니다. 이것을 억제하고 싶습니다. 이것이 어떻게 가능한지?
genomicsio

2
확인. 규칙 명령 줄의 시작 부분에 @가 해결책을 직접 찾았습니다.
genomicsio

4
원 라이너 : if [ -z '${${*}}' ]; then echo 'Environment variable $* not set' && exit 1; fi: D
c24w

4
이것이 정답이어야합니다. 더 깨끗한 구현입니다.
sb32134

46

인라인 변형

내 makefile에서 일반적으로 다음과 같은 표현식을 사용합니다.

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

경고 :

  • 해당 블록에서 탭을 사용하지 마십시오
  • 주의해서 사용하십시오 : cleanENV를 설정하지 않으면 대상도 실패합니다. 그렇지 않으면 더 복잡한 Hudon의 답변을 참조하십시오

와. "해당 블록에서 탭을 사용하지 마십시오."가 나타날 때까지 문제가있었습니다. 감사합니다!
Alex K

좋은 대안이지만, 성공하더라도 "오류 메시지"가 표시되는 것을 좋아하지 않습니다 (전체 줄이 인쇄 됨)
Jeff

@Jeff 그것이 makefile의 기본입니다. 행 앞에 접두사를 붙 @입니다. -> gnu.org/software/make/manual/make.html#Echoing
Daniel Alder

시도했지만 오류가 발생하면 오류 메시지가 표시되지 않습니다. 흠, 다시해볼 게요. 답을 확실히 올렸습니다.
Jeff

1
나는 테스트 접근법을 좋아한다. 나는 다음과 같이 사용했다 :@test -n "$(name)" || (echo 'A name must be defined for the backup. Ex: make backup name=xyz' && exit 1)
swampfox357

6

지금까지 주어진 답변으로 가능한 한 가지 문제는 make의 종속성 순서가 정의되어 있지 않다는 것입니다. 예를 들어 다음을 실행합니다.

make -j target

target몇 가지 종속성이 이러한 특정 순서로 실행을 보장하지 않습니다.

이것에 대한 해결책은 (레시피를 선택하기 전에 ENV가 확인되도록 보장하는 것입니다)는 레시피 외부에서 make의 첫 번째 패스 동안 ENV를 확인하는 것입니다.

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

다른 답변을 제외하고는 최상의 답변을 요구 사항으로 사용할 수 없습니다. 실제 파일 인 대상에 대한 종속성으로 사용되는 경우를 사용하면 check-env해당 파일 대상이 강제로 다시 작성됩니다.

다른 답변은 전역 적입니다 (예 : Makefile의 모든 대상에 변수가 필요함 ). 예를 들어 ENV가없는 경우 make는 대상에 관계없이 종료됩니다.

두 가지 문제에 대해 찾은 해결책은

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각각의 상태를 테스트해야 하지만 ... 하나의 전제 조건으로 처리하고 싶습니다.
abernier

4

나는 이것이 오래되었다는 것을 알고 있지만, 조금 더 깔끔한 IMHO이기 때문에 미래의 방문객을위한 내 경험으로 차임 할 것이라고 생각했습니다.

일반적으로 기본 쉘로 make사용 sh됩니다 ( 특수 SHELL변수 를 통해 설정 ). 에서 sh그 유도체와, 그것을 할 사소한 가 설정하거나 null가 아닌 경우, 환경 변수를 검색 할 때 오류 메시지와 함께 종료 수행하여 : ${VAR?Variable VAR was not set or null}.

이를 확장하여 환경 변수가 설정되지 않은 경우 다른 대상을 실패시키는 데 사용할 수있는 재사용 가능한 make 대상을 작성할 수 있습니다.

.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상태 변수 를 확인 해야하는 유일한 조리법은 아닙니다 .
abernier

그런 다음 리팩터링하십시오. ifdef 블록 앞에 .PHONY: deployand deploy:문을 사용하여 중복을 제거하십시오. (btw 올바른 방법을 반영하기 위해 답을 편집했습니다)
Dwight Spencer
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.