如何根据未设置/值的makefile变量中止make/makefile执行?
我想出了这个,但只有在调用者没有显式运行目标(即make
仅运行)时才有效.
ifeq ($(MY_FLAG),)
abort: ## This MUST be the first target :( ugly
@echo Variable MY_FLAG not set && false
endif
all:
@echo MY_FLAG=$(MY_FLAG)
Run Code Online (Sandbox Code Playgroud)
我认为这样的事情是个好主意,但在make的手册中没有找到任何内容:
ifndef MY_FLAG
.ABORT
endif
Run Code Online (Sandbox Code Playgroud)
Eld*_*mov 235
TL; DR:使用error
功能:
ifndef MY_FLAG
$(error MY_FLAG is not set)
endif
Run Code Online (Sandbox Code Playgroud)
请注意,这些行不得缩进.更准确地说,这些行之前不得有任何标签.
如果您要测试许多变量,那么为此定义一个辅助函数是值得的:
# Check that given variables are set and all have non-empty values,
# die with an error otherwise.
#
# Params:
# 1. Variable name(s) to test.
# 2. (optional) Error message to print.
check_defined = \
$(strip $(foreach 1,$1, \
$(call __check_defined,$1,$(strip $(value 2)))))
__check_defined = \
$(if $(value $1),, \
$(error Undefined $1$(if $2, ($2))))
Run Code Online (Sandbox Code Playgroud)
以下是如何使用它:
$(call check_defined, MY_FLAG)
$(call check_defined, OUT_DIR, build directory)
$(call check_defined, BIN_DIR, where to put binary artifacts)
$(call check_defined, \
LIB_INCLUDE_DIR \
LIB_SOURCE_DIR, \
library path)
Run Code Online (Sandbox Code Playgroud)
这会输出如下错误:
Makefile:17: *** Undefined OUT_DIR (build directory). Stop.
Run Code Online (Sandbox Code Playgroud)
真正的检查在这里完成:
$(if $(value $1),,$(error ...))
Run Code Online (Sandbox Code Playgroud)
这反映了ifndef
条件的行为,因此定义为空值的变量也被视为"未定义".但这仅适用于简单变量和显式空的递归变量:
# ifndef and check_defined consider these UNDEFINED:
explicitly_empty =
simple_empty := $(explicitly_empty)
# ifndef and check_defined consider it OK (defined):
recursive_empty = $(explicitly_empty)
Run Code Online (Sandbox Code Playgroud)
正如@VictorSergienko在评论中所建议的那样,可能需要稍微不同的行为:
$(if $(value $1)
测试值是否为非空.如果使用空值定义变量,有时可以.我用了$(if $(filter undefined,$(origin $1)) ...
和:
此外,如果它是一个目录,并且它必须在运行检查时存在,我会使用
$(if $(wildcard $1))
.但是会是另一个功能.
也可以扩展解决方案,以便只有在调用某个目标时才需要变量.
$(call check_defined, ...)
从食谱里面只需将支票移入配方:
foo :
@:$(call check_defined, BAR, baz value)
Run Code Online (Sandbox Code Playgroud)
前导@
符号关闭命令回显并且:
是实际命令,shell 无操作存根.
check_defined
可以改进该函数以输出目标名称(通过$@
变量提供):
check_defined = \
$(strip $(foreach 1,$1, \
$(call __check_defined,$1,$(strip $(value 2)))))
__check_defined = \
$(if $(value $1),, \
$(error Undefined $1$(if $2, ($2))$(if $(value @), \
required by target `$@')))
Run Code Online (Sandbox Code Playgroud)
所以,现在失败的检查会产生一个格式很好的输出:
Makefile:7: *** Undefined BAR (baz value) required by target `foo'. Stop.
Run Code Online (Sandbox Code Playgroud)
check-defined-MY_FLAG
特殊目标就个人而言,我会使用上面简单直接的解决方案.但是,例如,此答案建议使用特殊目标来执行实际检查.可以尝试概括并将目标定义为隐式模式规则:
# Check that a variable specified through the stem is defined and has
# a non-empty value, die with an error otherwise.
#
# %: The name of the variable to test.
#
check-defined-% : __check_defined_FORCE
@:$(call check_defined, $*, target-specific)
# Since pattern rules can't be listed as prerequisites of .PHONY,
# we use the old-school and hackish FORCE workaround.
# You could go without this, but otherwise a check can be missed
# in case a file named like `check-defined-...` exists in the root
# directory, e.g. left by an accidental `make -t` invocation.
.PHONY : __check_defined_FORCE
__check_defined_FORCE :
Run Code Online (Sandbox Code Playgroud)
用法:
foo :|check-defined-BAR
Run Code Online (Sandbox Code Playgroud)
请注意,它check-defined-BAR
被列为order-only(|...
)先决条件.
优点:
缺点:
make -t
(请参阅而不是执行配方)将污染具有大量check-defined-...
文件的根目录.这是一个令人遗憾的缺点,即无法声明.PHONY
模式规则.我相信,使用一些eval
魔术和二次扩展黑客可以克服这些限制,虽然我不确定它是否值得.
Mes*_*ssa 34
使用shell函数test
:
foo:
test $(something)
Run Code Online (Sandbox Code Playgroud)
用法:
$ make foo
test
Makefile:2: recipe for target 'foo' failed
make: *** [foo] Error 1
$ make foo something=x
test x
Run Code Online (Sandbox Code Playgroud)
Hol*_*Cat 13
另外一个选择:
MY_FLAG = $(error Please set this flag)
Run Code Online (Sandbox Code Playgroud)
尝试在任何地方使用此变量都会导致错误,除非从命令行覆盖它。
要接受环境变量,请使用?=
:
MY_FLAG ?= $(error Please set this flag)
Run Code Online (Sandbox Code Playgroud)
您可以使用IF来测试:
check:
@[ "${var}" ] || ( echo ">> var is not set"; exit 1 )
Run Code Online (Sandbox Code Playgroud)
结果:
$ make check
>> var is not set
Makefile:2: recipe for target 'check' failed
make: *** [check] Error 1
Run Code Online (Sandbox Code Playgroud)
对未设置的变量使用shell错误处理(请注意double $
):
$ cat Makefile
foo:
echo "something is set to $${something:?}"
$ make foo
echo "something is set to ${something:?}"
/bin/sh: something: parameter null or not set
make: *** [foo] Error 127
$ make foo something=x
echo "something is set to ${something:?}"
something is set to x
Run Code Online (Sandbox Code Playgroud)
如果您需要自定义错误消息,请在后面添加?
:
$ cat Makefile
hello:
echo "hello $${name:?please tell me who you are via \$$name}"
$ make hello
echo "hello ${name:?please tell me who you are via \$name}"
/bin/sh: name: please tell me who you are via $name
make: *** [hello] Error 127
$ make hello name=jesus
echo "hello ${name:?please tell me who you are via \$name}"
hello jesus
Run Code Online (Sandbox Code Playgroud)
为简单起见:
$ cat Makefile
check-%:
@: $(if $(value $*),,$(error $* is undefined))
bar:| check-foo
echo "foo is $$foo"
Run Code Online (Sandbox Code Playgroud)
有输出:
$ make bar
Makefile:2: *** foo is undefined. Stop.
$ make bar foo="something"
echo "foo is $$foo"
foo is something
Run Code Online (Sandbox Code Playgroud)