如果未设置变量,则中止makefile


150

如何基于未设置/赋值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

Answers:


270

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魔术和辅助扩展技巧来克服这些限制,尽管我不确定是否值得。


到底是什么 从未使用过Mac,尽管我猜它默认已安装了Make的另一种实现(例如BSD Make而不是GNU Make)。我建议您先检查一下make --version
Eldar Abusalimov 2015年

1
这似乎在3.81版中不起作用。即使定义了变量(并且可以将其回显),它也始终会出错。
OrangeDog

嗯,您需要像链接的副本中那样精确地构造它。
OrangeDog

1
@bibstha我添加了想到的选项,请阅读更新的答案。
Eldar Abusalimov

2
我要添加一个澄清点(就像我一样),即ifndef不必缩进:)我发现该技巧在其他地方出现,突然我的所有错误都变得有意义了。
helios

40

使用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

3
这是我在遇到相同问题时使用的-谢谢,Messa!我做了两个小修改:1)我创建了一个checkforsomething仅包含目标testfoo依赖于此的目标,以及2)我将检查@if test -z "$(something)"; then echo "helpful error here"; exit 1; fi改为。这使我能够添加一个有用的错误,并使我能够使新目标的名称也更能说明问题所在。
布赖恩·杰拉德

我得到了Makefile:5: *** missing separator. Stop.
silgon

7
为了紧凑起见,我通常test -n "$(something) || (echo "message" ; exit 1)避免使用露骨的if
user295691 '18

@silgon您可能使用空格而不是制表符缩进。
eweb

9

您可以使用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相同。但是,这更加紧凑,并且包含错​​误消息的生成。
user295691 '18

6

对未设置的变量使用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

如果您需要自定义错误消息,请在后面添加?

$ 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

2

为了简洁起见:

$ 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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.