如何在GNU Make中有条件地使用$(error ...)?

14

我想使用$(error ...)来在特定前提条件不满足时终止我的make过程。当test -d /foobar失败时,fails_to_work目标应该中止。

BAD.mk

all: this_works fails_to_work

this_works:
        @echo echo works...
        @test -d ~ || echo ~ is not a directory
        @test -d /foobar || echo /foobar is not a directory

fails_to_work:
        @echo error does not work...
        @test -d ~ || $(error ~ is not a directory)
        @test -d /foobar || $(error /foobar is not a directory)

$ make -f BAD.mk

转译为中文后为:

$ make -f BAD.mk

echo works...
/foobar is not a directory
BAD.mk:9: *** ~ is not a directory.  Stop.

正如您所看到的,甚至连“error does not work…”也没有被输出到屏幕上。 fails_to_work 的解决方案在开始之前就失败了。我该怎么解决?我的一个用例是@test -d $(MY_ENV_VAR),但我不认为这与示例中给出的硬编码路径有什么区别。

更新(版本信息)

$ make --version

GNU Make 3.81
Copyright (C) 2006  Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.

This program built for x86_64-pc-linux-gnu
3个回答

12

您正在尝试在配方中获取shell内容以有条件地调用makefile内容, 但这并不起作用,正如您所发现的那样。

我可以想到两个选择:

  1. 简单地删除$(error)内容。如果test失败,则它将返回非零退出状态,并且Make进程将在该点终止。

  2. 将测试从规则中移出,并使用Make条件(反过来调用shell功能),例如:

  3. ifeq ($(shell test -d /foobar; echo $$?),1)
    $(error Not a directory)
    endif
    

1
谢谢。您的第一个建议有效(我会这样采用:if ! test -d /foobar ; then echo "/foobar is not a directory"; exit 1; fi),但是您的第二个建议无效。即使有一个有效的目录,错误仍然会出现。 - Chris Betti

12

对于makefile中的Shell命令,它们被有效地存储为一个递归展开的变量。当make决定运行该recipe时,它会展开该变量,然后在每个单独的shell调用中运行每行命令。任何被展开的$(error ...)都会导致make在调用第一个命令之前中止。

请注意,$(if ...)$(or ...)等未采取的分支将不会被展开。因此,您可以这样做:

.PHONY: rule-with-assert
rule-with-assert:
    $(if $(realpath ${should-be-file}/),$(error Assertion failure: ${should-be-file} is a folder!))
    ⋮

请注意,realpath 中的尾随 /
当然,宏可以帮助大大简化这个过程。
assert-is-file = $(if $(realpath $1/),$(error Assertion failure: [$1] is a folder!))

.PHONY: rule-with-assert
rule-with-assert:
    $(call assert-is-file,${should-be-file})
    ⋮

值得再次注意的是,在配方中放置$(call assert-is-file,…)的位置并不重要。任何$(error)都将在扩展配方时生成,在运行任何shell命令之前。

4
为什么不直接使用exit 1 shell命令而使用$(error ...)?使用后者有什么原因吗?
try_this:
    @test -d /foobar || { echo /foobar is not a directory; exit 1; }

or_this:
    @if [ ! -d /foobar ]; then echo /foobar is not a directory; exit 1; fi

如果不指定-k标志,这两个命令都会中止make过程。

-k --keep-going

即使出现错误也尽可能地继续。虽然失败的目标及其依赖项无法重新生成,但可以处理这些目标的其他先决条件。


我曾试图在 Windows cmd shell / sh 之间实现可移植性,错误地认为 test -d 在两者中都可用。然而,我仍然对有条件地调用 $(error ...) 的正确方法感兴趣。 - Chris Betti

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接