Переменная Makefile как необходимое условие - PullRequest
119 голосов
/ 18 января 2011

В 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

Спасибо.

Ответы [ 7 ]

146 голосов
/ 19 января 2011

Это приведет к фатальной ошибке, если ENV не определено и что-то в этом нуждается (в любом случае, в GNUMake).

.PHONY: deploy check-env

deploy: check-env
	...

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

check-env:
ifndef ENV
	$(error ENV is undefined)
endif

(Обратите внимание, что ifndef и endif не имеют отступов - они контролируютто, что make «видит» , вступает в силу до запуска Makefile. «$ (error» имеет отступ с вкладкой, поэтому он запускается только в контексте правила.)

86 голосов
/ 10 сентября 2011

Вы можете создать неявную защитную цель, которая проверяет, что переменная в стволе определена, как это:

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 в вызове, вы получите ошибку, и сборка завершится неудачей.

39 голосов
/ 07 марта 2016

Встроенный вариант

В моих make-файлах я обычно использую выражение вроде:

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 потерпит неудачу, если ENV не установлен. В противном случае смотрите ответ Гудона, который является более сложным
5 голосов
/ 03 апреля 2018

Я нашел с лучшим ответом, не может использоваться как требование, за исключением других целей PHONY.При использовании в качестве зависимости для цели, являющейся фактическим файлом, использование check-env приведет к перестроению этой цели.

Другие ответы являются глобальными (например, переменная требуется для all цели в 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 голосов
/ 19 января 2011

Как я вижу, самой команде нужна переменная 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
4 голосов
/ 26 мая 2013

Одна из возможных проблем с данными ответами на данный момент заключается в том, что порядок зависимости в make не определен. Например, работает:

make -j target

, когда target имеет несколько зависимостей, не гарантирует, что они будут работать в любом заданном порядке.

Решением для этого (чтобы гарантировать, что ENV будет проверен до выбора рецептов) является проверка ENV во время первого прохода make вне любого рецепта:

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

Вы можете прочитать о различных используемых функциях / переменных здесь и $() - это просто способ явно заявить, что мы сравниваем с «ничем».

2 голосов
/ 18 января 2011

Вы можете использовать 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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...