如何让$(错误...)在GNU Make中有条件地工作?

时间:2012-06-18 19:51:29

标签: makefile gnu-make

如果不满足某些先决条件,我想使用$(error ...)中止我的制作过程。如果fails_to_work失败,test -d /foobar目标应该中止。

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

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

正如您所看到的,甚至“错误都不起作用......”也会回显到屏幕上。 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 个答案:

答案 0 :(得分:7)

你正试图在一个配方中获取shell的东西来有条件地调用makefile的东西,这是不行的,正如你所发现的那样。

我可以想到两个选择:

  1. 只需删除$(error)内容即可。如果test失败,则它将返回非零退出状态,并且Make过程将在此时终止。

  2. 从规则中取出测试,并使用Make条件(进而调用shell功能),例如:

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

答案 1 :(得分:6)

make配方的Shell命令有效地存储为单个递归扩展变量。在make决定运行配方时,它会扩展变量,然后在自己的shell调用中运行每一行。 任何扩展的 $(error ...)都会导致make在调用第一个命令之前中止

请注意$(if ...)$(or ...)& c的未分支分支。不会扩大。因此,你可以做到

.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命令。

答案 2 :(得分:1)

为什么不使用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 flag,否则这两个都将中止制作过程。

  

-k   --keep-going

     

发生错误后尽可能继续。虽然失败的目标和依赖它的目标无法重新制作,但这些目标的其他先决条件可以完全相同。