如果变量未设置,如何中止makefile ?

我怎么能中止一个make/makefile执行基于一个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)

我认为这样的东西会是一个好主意,但在make的手册中没有找到任何东西:

ifndef MY_FLAG
.ABORT
endif
148791 次浏览

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)
< p > < / b > < br > 这将输出如下错误:

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)测试该值是否为非空。有时是如果变量定义为空值,则OK。我将使用$(if $(filter undefined,$(origin $1)) ...

和:

此外,当检查运行时,我会使用$(if $(wildcard $1))。而是另一个函数。

有针对性的检查

还可以扩展解决方案,以便只有在调用某个目标时才需要变量。

$(call check_defined, ...)从食谱内部

只需将检查移动到食谱:

foo :
@:$(call check_defined, BAR, baz value)

< / b >

前面的@符号关闭命令回显,而:是实际的命令,一个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 `$@')))

因此,现在一个失败的检查会产生一个格式化的输出:

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 :
< p >用法:

foo :|check-defined-BAR

注意,check-defined-BAR被列为只是期许 (|...)先决条件

优点:

  • (可以说)一个更干净的语法

缺点:

  • 不能指定自定义错误消息
  • 运行make -t(参见而不是执行食谱)会用大量的check-defined-...文件污染你的根目录。这是模式规则不能声明.PHONY的一个可悲的缺点。

我相信,这些限制可以通过使用一些eval魔法和二次扩张技巧来克服,尽管我不确定这样做是否值得。

使用shell函数test:

foo:
test $(something)

用法:

$ make foo
test
Makefile:2: recipe for target 'foo' failed
make: *** [foo] Error 1
$ make foo something=x
test x

使用shell错误处理未设置变量(注意双$):

$ 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

如果需要自定义错误消息,将其添加在?之后:

$ 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

你可以使用IF来测试:

check:
@[ "${var}" ] || ( echo ">> var is not set"; exit 1 )

结果:

$ make check
>> var is not set
Makefile:2: recipe for target 'check' failed
make: *** [check] Error 1

为了简单和简洁:

$ cat Makefile
check-%:
@: $(if $(value $*),,$(error $* is undefined))


bar:| check-foo
echo "foo is $$foo"

输出:

$ make bar
Makefile:2: *** foo is undefined. Stop.
$ make bar foo="something"
echo "foo is $$foo"
foo is something

另一个选择:

MY_FLAG = $(error Please set this flag)

尝试在任何地方使用此变量都会导致错误,除非从命令行覆盖它。

要接受环境变量,也可以使用?=:

MY_FLAG ?= $(error Please set this flag)