Johnny Metz
Johnny Metz

Reputation: 6055

Makefile: exit on conditional

I want to check that an environment variable is set before executing some code in a Makefile. If it's not set I want to throw an error with a simple error message:

run:
  [ -z "$(MY_APP)" ] && echo "MY_APP must be set" && exit 1
  echo "MY_APP is set. Yay!"
  echo "Let's continue on with the command..."

When MY_APP is not set I get the following error, which is desired:

[ -z "" ] && echo "MY_APP must be set" && exit 1
MY_APP must be set
make: *** [run] Error 1

However, when MY_APP is set I get the following error:

[ -z "EXAMPLE_NAME" ] && echo "MY_APP must be set" && exit 1
make: *** [run] Error 1

Any idea what I'm doing wrong? And is there a better way to do this?

Upvotes: 13

Views: 16610

Answers (5)

Holy semicolon
Holy semicolon

Reputation: 1389

IF you want to exit the make file without Makefile error code, refer to this answer:

https://stackoverflow.com/a/78390951/10669066

Upvotes: 0

dash-o
dash-o

Reputation: 14491

Recall that the && condition require that all conditions must be TRUE to pass. Since the first condition fail, the whole command will return a status of 1 (-> false), effectively stopping the make

You can use the following, so that the test will fail only when MY_APP is missing.

Note that I'm using false instead of exit 1. Also better to use "${MY_APP}", which make it easier to copy/paste from Make to shell prompt/script.

run:
    { [ -z "$(MY_APP)" ] && echo "MY_APP must be set" && false } || true
    ...

# Or just if-Then-Else
    if [ -z "${MY_APP}" ] ; then echo "MY_APP must be set" ; false ; fi
    ...

Upvotes: 8

Blcknx
Blcknx

Reputation: 2441

You can conditionally exit the Makefile using error control function, at least in the GNU version.

This snippet is a helpful condition to put into the head of the Makefile. It exits with a message of help, if make was not called from within the directory of the Makefile.

MAKEFILE_DIR:=$(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))
ifneq (${MAKEFILE_DIR}, $(shell pwd))
 INVALID_LOCATION:=`make` must be called from within ${MAKEFILE_DIR} (or with option -C ${MAKEFILE_DIR})
 $(error ERROR: $(INVALID_LOCATION))
endif

See: https://www.gnu.org/software/make/manual/html_node/Make-Control-Functions.html

Useful in case your paths are relative to the Makefile and you don't want them to prefix with a base.

Upvotes: 1

Geoff Williams
Geoff Williams

Reputation: 1420

You can test environment variables with Makefile conditional syntax, like this:

sometarget:
ifndef MY_APP
    @echo "MY_APP environment variable missing"
    exit 1
endif

    somecommand to_run_if_my_app_is_set

Note that ifndef/ifdef operate on the name of the variable, not the variable itself.

Upvotes: 4

Lesmana
Lesmana

Reputation: 27073

It seems that you are trying to use a Makefile to run commands which are not building targets (the target name run is a giveaway). You already got bitten by one of Makefile and shells caveats. Makefile caveat: exit status is inspected after each line and if not zero abort immediately. Shell caveat: the test command ([) returns a non zero exit status so the entire line returns non zero.

The rule of thumb is: a recipe of a rule should create a filename named like the target of the rule.

Here is a rule (to clarify the terms):

target:
  recipe command lines
  should create file named target

There are some exceptions to this rule of thumb. Most notably make clean and make install. Both typically do not create files named clean or install. One can argue that make run maybe also be an exception to this rule of thumb.

If your run is as simple as a typical clean then I might agree about making an exception. But usually commands are run with command line arguments. Before long you will want make run accept arguments. And making make accept custom command line arguments is not fun at all.

You tried to manipulate the behaviour using environment variables which is somewhat less problematic than command line arguments. But still problematic enough to make you trip over a caveat.

My suggestion for a fix:

  • Put complex recipes in a shell script. There you have all the power and flexibility of a shell script without the awkwardness of makefiles. For example as explained here: Basic if else statement in Makefile
  • In case of a typical run target write a wrapper shell script around the makefile which lets the makefile rebuild the target and then run the target. For exampe as explained here: Passing arguments to "make run"

Upvotes: 0

Related Questions