Переменная Makefile в качестве предпосылки
В 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
?
Спасибо.
Ответы
Ответ 1
Это приведет к фатальной ошибке, если 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" имеет отступ с вкладкой, так что он запускается только в контексте правила.)
Ответ 2
Вы можете создать неявную цель защиты, которая проверяет, что переменная в стебле определена, например:
guard-%:
@ if [ "${${*}}" = "" ]; then \
echo "Environment variable $* not set"; \
exit 1; \
fi
Затем вы добавляете цель guard-ENVVAR в любом месте, где хотите утверждать, что переменная определена, например:
change-hostname: guard-HOSTNAME
./changeHostname.sh ${HOSTNAME}
Если вы вызываете "make change-hostname", не добавляя HONNAME = somehostname в вызов, вы получите сообщение об ошибке, и сборка завершится с ошибкой.
Ответ 3
Встроенный вариант
В моих make файлах я обычно использую выражение типа:
deploy:
test -n "$(ENV)" # $$ENV
rsync . $(ENV).example.com:/var/www/myapp/
Причины:
- это простой однострочный
- it compact
- он расположен близко к командам, в которых используется переменная
Не забывайте комментарий, важный для отладки:
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 не установлен. В противном случае см. Hudon answer, который является более сложным.
Ответ 4
Как я вижу, для самой команды нужна переменная 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
Ответ 5
Я обнаружил, что лучший ответ не может быть использован в качестве требования, за исключением других целей PHONY. При использовании в качестве зависимости для цели, которая является реальным файлом, использование 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
имеет некоторые пугающие предостережения, но для этого простого использования я считаю, что это лучший выбор.
Ответ 6
Одной из возможных проблем с данными ответами пока является то, что порядок зависимостей в make не определен. Например, запуск:
make -j target
когда target
имеет несколько зависимостей, не гарантирует, что они будут работать в любом заданном порядке.
Решение для этого (чтобы гарантировать, что ENV будет проверено до того, как будут выбраны рецепты), необходимо проверить ENV во время первого прохода make вне рецепта:
## Are any of the user 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
...
Вы можете прочитать о различных используемых функциях/переменных здесь и $()
- это просто способ явно указать, что мы сравниваем "ничего".
Ответ 7
Вы можете использовать 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
Ответ 8
Я знаю, что это старо, но я подумала, что поделюсь своим собственным опытом для будущих посетителей, так как это немного более аккуратно, ИМХО.
Как правило, make
будет использовать sh
качестве оболочки по умолчанию (устанавливается через специальную переменную SHELL
). В sh
и его производных, тривиально выйти с сообщением об ошибке при извлечении переменной среды, если она не установлена или равна нулю, выполнив: ${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}
)