Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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
like image 846
Chris Betti Avatar asked Jun 18 '12 19:06

Chris Betti


1 Answers

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.

like image 193
bobbogo Avatar answered Oct 22 '22 08:10

bobbogo