在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
谢谢。
答案 0 :(得分:144)
如果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没有缩进 - they control what make "sees",在Makefile运行之前生效。“$(错误”缩进了一个标签,以便它只在规则的上下文中运行。)
答案 1 :(得分:85)
您可以创建一个隐式守卫目标,用于检查词干中的变量是否已定义,如下所示:
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,那么您将收到错误,并且构建将失败。
答案 2 :(得分:38)
内嵌变体
在我的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
警告:
clean
目标也会失败。否则,请参阅Hudon的更复杂的答案答案 3 :(得分: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
答案 4 :(得分:4)
到目前为止,给定答案的一个可能问题是未定义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
...
您可以阅读有关使用的不同函数/变量here和$()
只是一种明确说明我们正在与“无”进行比较的方法。
答案 5 :(得分:4)
我发现最好的答案不能作为要求,除了其他PHONY目标。如果用作实际文件的目标的依赖项,则使用check-env
将强制重建该文件目标。
其他答案是全局的(例如,Makefile中所有目标所需的变量)或使用shell,例如如果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 :(得分: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
答案 7 :(得分:0)
我知道这很老了,但我想我会以自己的经验吸引以后的访客,因为恕我直言有点整洁。
通常,make
将使用sh
作为其默认外壳程序(set via the special SHELL
variable)。在sh
及其衍生物中,通过执行${VAR?Variable VAR was not set or null}
对exit with an error message when retrieving an environment variable if it is 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
只是为了防止shell尝试执行VAR
的内容(没有其他重要目的).check-env-vars
可以轻松扩展以检查更多的环境变量,每个环境变量仅添加一行(例如@test $${NEWENV?Please set environment variable NEWENV}
)