Makefile 如何使$(error…)在GNU Make中有条件地工作?

Makefile 如何使$(error…)在GNU Make中有条件地工作?,makefile,gnu-make,Makefile,Gnu Make,如果不满足某些先决条件,我想使用$(error…中止make过程。当测试失败-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 fail

如果不满足某些先决条件,我想使用
$(error…
中止make过程。当
测试失败-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.
正如您所看到的,甚至“错误不起作用…”都不会在屏幕上出现。
的配方无法工作
在开始之前就失败了。我如何解决这个问题?我的一个用例是
@test-d$(my_ENV_VAR)
,但我认为这与示例中给出的硬编码路径没有什么不同

更新(版本信息)

$make--版本


您试图在配方中获取shell内容,以有条件地调用makefile内容,但正如您所发现的那样,这是行不通的

我可以想出两个选择:

  • 只需删除
    $(错误)
    内容。如果
    test
    失败,则它将返回非零退出状态,并且Make进程将在该点终止

  • 将测试从规则中去掉,并使用Make条件(这反过来调用shell功能),例如:


  • 为什么不直接使用
    exit 1
    shell命令而不是
    $(错误…
    ?有什么理由使用后者吗

    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配方的Shell命令有效地存储为单个递归扩展变量。在make决定运行recipe时,它展开变量,然后在自己的shell调用中运行每一行。任何扩展的
    $(错误…
    )都将导致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})
        ⋮
    
    再次值得注意的是,将
    $(callassert是file,…)
    放在配方的何处并不重要。 任何
    $(错误)
    都将在配方展开时生成,
    在运行任何shell命令之前。

    谢谢。您的第一个建议有效(我采用的是这样的:
    if!test-d/foobar;then echo”/foobar不是目录);退出1;fi
    ),但您的第二个建议无效。如果有一个有效的目录,这个错误仍然存在。我在错误地认为
    test-d
    在这两个目录中都可用的情况下,为windows cmd shell/sh的可移植性而努力。但是,我仍然对有条件地调用$(error…)的正确方法感兴趣。
    .PHONY: rule-with-assert
    rule-with-assert:
        $(if $(realpath ${should-be-file}/),$(error Assertion failure: ${should-be-file} is a folder!))
        ⋮
    
    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})
        ⋮