TL; DR:使用error
功能:
ifndef MY_FLAG
$(error MY_FLAG is not set)
endif
请注意,这些行不得缩进。更准确地说,在这些行之前没有选项卡。
通用解决方案
如果要测试许多变量,则值得为此定义一个辅助函数:
# 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))))
这是如何使用它:
$(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
条件的行为,因此将定义为空值的变量也视为“未定义”。但这仅适用于简单变量和明确为空的递归变量:
# 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)
正如@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 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 :
用法:
foo :|check-defined-BAR
请注意,该check-defined-BAR
列为仅订购(|...
)前提。
优点:
缺点:
- 无法指定自定义错误消息
- 运行
make -t
(请参阅代替执行食谱)将污染大量check-defined-...
文件的根目录。这是不能声明.PHONY
模式规则的事实的不幸缺点。
我相信,可以使用一些eval
魔术和辅助扩展技巧来克服这些限制,尽管我不确定是否值得。