How do I get $(error …) to work conditionally in GNU Make?

馋奶兔 提交于 2020-01-22 14:19:06

问题


I'd like to use $(error ...) to abort my make process if certain preconditions aren't met. The fails_to_work target should abort when failing 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.

As you can see, not even "error does not work..." is echoed to the screen. The recipe for fails_to_work fails before it gets started. How do I solve this? One of my use cases is@test -d $(MY_ENV_VAR), but I don't think that differs from the hard-coded paths given in the example.

UPDATE (version information)

$ 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

回答1:


You're trying to get the shell stuff in a recipe to conditionally invoke makefile stuff, which doesn't work, as you've found.

I can think of two options:

  1. Simply remove the $(error) stuff. If test fails, then it will return a non-zero exit status, and the Make process will terminate at that point.

  2. Take the test out of the rule, and use a Make conditional (which in turn invokes shell functionality), e.g.:

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



回答2:


Shell commands for a make recipe are effectively stored as a single recursively expanded variable. At the point make decides to run the recipe, it expands the variable, and then runs each line in its own shell invocation. Any $(error ...) that gets expanded will cause make to abort even before invoking the first command.

Note though that the untaken branch of a $(if ...) or $(or ...) &c. will not be expanded. Thus, you could do

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

Note that trailing / in the realpath.

Of course macros help to tidy this up a lot.

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})
    ⋮

It's worth noting again that it doesn't matter where you put the $(call assert-is-file,…) in the recipe. Any $(error)will be generated as the recipe is expanded, before any shell commands are run.




回答3:


Why don't you just use exit 1 shell command instead of $(error ...)? Is there any reason to use the latter?

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

Both of these will abort the make process unless -k flag is specified.

-k --keep-going

Continue as much as possible after an error. While the target that failed, and those that depend on it, cannot be remade, the other prerequisites of these targets can be processed all the same.



来源:https://stackoverflow.com/questions/11090023/how-do-i-get-error-to-work-conditionally-in-gnu-make

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!