如果未设置变量,如何中止makefile?
Posted
技术标签:
【中文标题】如果未设置变量,如何中止makefile?【英文标题】:How to abort makefile if variable not set? 【发布时间】:2012-06-07 03:58:59 【问题描述】:如何根据未设置/赋值的 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)
我认为这样的做法是个好主意,但在 make 的手册中没有找到任何内容:
ifndef MY_FLAG
.ABORT
endif
【问题讨论】:
Makefile variable as prerequisite 的可能重复项 【参考方案1】:
TL;DR:使用error
function:
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 在 cmets 中所建议的,可能需要稍微不同的行为:
$(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
特殊目标
我个人会使用上面简单直接的解决方案。但是,例如,this answer 建议使用特殊目标来执行实际检查。可以尝试将其概括并将目标定义为隐式模式规则:
# 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
被列为 order-only (|...
) 先决条件。
优点:
(可以说)更简洁的语法缺点:
无法指定自定义错误消息 运行make -t
(参见Instead of Executing Recipes)会用大量check-defined-...
文件污染您的根目录。这是模式规则 can't be declared .PHONY
的一个可悲缺点。
我相信,可以使用一些 eval
魔术和 secondary expansion hack 来克服这些限制,尽管我不确定这是否值得。
【讨论】:
究竟是什么?从未使用过 Mac,尽管我猜它默认安装了另一个 Make 实现(例如 BSD Make 而不是 GNU Make)。我建议您首先检查make --version
。
这似乎不适用于 make 3.81。即使定义了变量(并且可以回显),它也总是出错。
啊,你需要按照链接副本中的方式来构造它。
@bibstha 我添加了想到的选项,请阅读更新后的答案。
我会为新手(像我一样)添加一个澄清,即 ifndef 不需要缩进 :) 我在其他地方发现了这个提示,突然间我所有的错误都变得有意义了。【参考方案2】:
使用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
【讨论】:
这是我在遇到同样问题时使用的 - 谢谢,梅萨!我做了两个细微的修改:1)我创建了一个checkforsomething
目标,其中只有test
,并使foo
依赖于它,2)我将检查改为@if test -z "$(something)"; then echo "helpful error here"; exit 1; fi
。这使我能够添加一个有用的错误,并让我能够使新目标的名称更能说明问题所在。
有了这个我得到Makefile:5: *** missing separator. Stop.
为了简洁起见,我使用了test -n "$(something) || (echo "message" ; exit 1)
来避免显式的if
。
@silgon 您可能使用空格而不是制表符缩进。【参考方案3】:
对未设置的变量使用 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
【讨论】:
【参考方案4】:您可以使用 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
【讨论】:
[
是命令test
的别名,所以这与上面的@Messa 答案相同。不过,这更紧凑,并且包括错误消息生成。
在大多数情况下,这似乎是最干净的答案。【参考方案5】:
为了简单明了:
$ 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
【讨论】:
可以被$ touch check-foo; make bar
愚弄...在其他答案中需要 check_defined_FORCE 技巧以上是关于如果未设置变量,如何中止makefile?的主要内容,如果未能解决你的问题,请参考以下文章
makefile.am 变量在 make 命令中的目标文件之后设置一些库
CMake 错误:CMake 无法找到与“Unix Makefiles”对应的构建程序。 CMAKE_MAKE_PROGRAM 未设置