简而言之: 使用error
函数:
ifndef MY_FLAG
$(error MY_FLAG is not set)
endif
请注意,这些行不能缩进。更准确地说,这些行之前不能有制表符。
通用解决方案
如果您要测试多个变量,值得定义一个辅助函数:
check_defined = \
$(strip $(foreach 1,$1, \
$(call __check_defined,$1,$(strip $(value 2)))))
__check_defined = \
$(if $(value $1),, \
$(error Undefined $1$(if $2, ($2))))
以下是如何使用它的方法:
$(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)
这会输出如下错误信息:
Makefile:17: *** Undefined OUT_DIR (build directory). Stop.
注:
真正的检查在这里进行:
$(if $(value $1),,$(error ...))
这反映了
ifndef
条件的行为,因此将变量定义为空值也视为“未定义”。但这仅适用于简单变量和明确为空的递归变量:
explicitly_empty =
simple_empty := $(explicitly_empty)
recursive_empty = $(explicitly_empty)
根据评论区@VictorSergienko的建议,可能需要略微不同的行为:
$(if $(value $1)
测试值是否非空。如果变量定义为空,则有时可以使用它。我会使用$(if $(filter undefined,$(origin $1)) ...
并且:
此外,如果它是一个目录并且在运行检查时必须存在,我会使用$(if $(wildcard $1))
。但这将是另一个函数。
特定于目标的检查
还可以扩展解决方案,以便只能在调用某个特定目标时要求变量。
$(call check_defined, ...)
从配方内部
只需将检查移至配方中:
foo :
@:$(call check_defined, BAR, baz value)
领先的@
符号关闭命令回显,:
是实际命令,一个shell no-op stub。
显示目标名称
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 `$@')))
因此,现在一个失败的检查会产生一个格式良好的输出:
Makefile:7: *** Undefined BAR (baz value) required by target `foo'. Stop.
check-defined-MY_FLAG
特殊目标
个人而言,我会使用上面简单直接的解决方案。但是,例如这个答案建议使用特殊目标来执行实际检查。可以尝试将其概括,并将目标定义为隐式模式规则:
check-defined-% : __check_defined_FORCE
@:$(call check_defined, $*, target-specific)
.PHONY : __check_defined_FORCE
__check_defined_FORCE :
用法:
foo :|check-defined-BAR
注意,
check-defined-BAR
被列为
order-only(
|...
)的先决条件。
优点:
缺点:
我认为,可以使用一些eval
魔法和secondary expansion技巧克服这些限制,尽管我不确定这是否值得。