Makefilevariables作为先决条件

在Makefile中, deploy配方需要设置一个环境variablesENV来正确执行自己,而另一些则不关心,例如:

 ENV = .PHONY: deploy hello deploy: rsync . $(ENV).example.com:/var/www/myapp/ hello: echo "I don't care about ENV, just saying hello!" 

我怎样才能确保这个variables设置,例如:是否有一种方法来声明这个makefilevariables作为部署配方的先决条件,如:

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

谢谢。

这会导致一个致命的错误,如果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不是缩进的 – 它们控制着什么使“看到”,在运行Makefile之前生效 。)

你可以创build一个隐式的守护目标,检查这个目录中的variables是否被定义,如下所示:

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

然后,您可以在任何想要声明variables已定义的位置添加guard-ENVVAR目标,如下所示:

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

如果您调用“make change-hostname”,而不是在调用中添加HOSTNAME = somehostname,那么您将得到一个错误,构build将失败。

内联变体

在我的makefile中,我通常使用如下的expression式:

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

原因:

  • 这是一个简单的一行
  • 它紧凑
  • 它靠近使用variables的命令

不要忘记对debugging非常重要的注释:

 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 

警告:

  • 不要使用该块中的选项卡
  • 小心使用:即使没有设置ENV, clean目标也会失败。 否则,请参阅Hudon的更复杂的答案

正如我看到的命令本身需要ENVvariables,所以你可以检查它在命令本身:

 .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 

到目前为止给出的答案的一个可能的问题是make中的依赖顺序没有被定义。 例如,运行:

 make -j target 

target有一些依赖不能保证这些将以任何给定的顺序运行。

这个解决scheme(保证在select食谱之前检查ENV)是在制作第一遍的时候检查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 ... 

你可以阅读这里使用的不同的函数/variables,而$()只是明确指出我们正在比较“无”的一种方式。

你可以使用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