Testing if a file exists in makefile target, and quitting if not present

不问归期 提交于 2019-12-03 22:12:15

exit alone returns the status of the last command executed. In this case, it returns zero, which means everything is ok.

This is, because || and && have equal precedence, and the shell interprets the command as if it were written

( test ... || echo ... ) && exit

If you want to signal failure you must exit with a non zero value, e.g. exit 1. And if you want to echo and exit, just put the commands in sequence separated by ;

all: foo

foo:
    test -s /opt/local/bin/gsort || { echo "GNU sort does not exist! Exiting..."; exit 1; }

I realize this is a bit old at this point, but you don't need to even use a subshell to test if a file exists in Make.

It also depends on how you want/expect it to run.

Using the wildcard function, like so:

all: foo
foo:
ifeq (,$(wildcard /opt/local/bin/gsort))
    $(error GNU Sort does not exist!)
endif

is one good way to do it. Note here that the ifeq clause is not indented because it is evaluated before the target itself.

If you want this to happen unconditionally for every target, you can just move it outside of a target:

ifeq (,$(wildcard /opt/local/bin/gsort))
$(error GNU Sort does not exist!)
endif

Every command line in make runs in its own sub-shell. So running exit just exits that sub-shell--not the makefile as a whole. By default, make execution will stop if any sub-shell returns an unsuccessful exit status (by convention, 0 means success, so anything else will halt execution). The simplest method would be just to use the exit status of the test command:

all: foo

foo:
    test -s /opt/local/bin/gsort

Printing a diagnostic message complicates things slightly because commands like echo will return an exit status of 0, causing make to think everything is fine. To work around this, you need to run a command after it that will give the sub-shell a non-zero exit status:

all: foo

foo:
    test -s /opt/local/bin/gsort || { echo "GNU sort does not exist! Exiting..."; exit 1; }

or even just

all: foo

foo:
    test -s /opt/local/bin/gsort || { echo "GNU sort does not exist! Exiting..."; false; }

Simply do:

all: /opt/local/bin/gsort

and if /opt/local/bin/gsort is missing, you will get a "no rule to make target `/opt/local/bin/gsort'" error message.

But if you also want some nice explanation with it do:

/opt/local/bin/gsort:
    echo "GNU sort does not exist! Exiting..."
    false

In GNU/Make if the target is not declared .PHONEY and doesn't have any dependencies, the rule will be invoked if a file matching that target does not exist.

The code above will trigger the false command only when /opt/local/bin/gsort does not exist, will return a non 0 value, and make will fail.

Since you're checking whether gsort executable file exists, you can use which or type shell command for that, e.g.:

all: foo
  :

foo:
  which gsort || exit 1
  # or
  type gsort || exit 1

You don't need an error message, since it'll automatically print:

/bin/sh: line 0: type: gsort: not found

which is obvious.


Alternatively use test or [ (see: help test/help [ for syntax), e.g.

test -x /opt/local/bin/gsort || { echo Error msg; exit 1; }

which checks if given file exists and it's executable, otherwise show the message and exit. The parenthesis are important to override the normal precedence of operators (left to right) by grouping the commands (see Compound Commands section in man bash for further info).


Another way is to use the rule's targets to check if the file exists and add as dependency to all, e.g.

all: /opt/local/bin/gsort
  @echo Success.

/opt/local/bin/gsort:
  @echo "GNU sort does not exist! Exiting..."
  exit 1

Result:

$ make
GNU sort does not exist! Exiting...
exit 1
make: *** [/opt/local/bin/gsort] Error 1
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!