How to ensure Makefile variable is set as a prerequisite?

This will cause a fatal error if ENV is undefined and something needs it (in GNUMake, anyway).

.PHONY: deploy check-env

deploy: check-env
	...

other-thing-that-needs-env: check-env
	...

check-env:
ifndef ENV
	$(error ENV is undefined)
endif

(Note that ifndef and endif are not indented - they control what make "sees", taking effect before the Makefile is run. "$(error" is indented with a tab so that it only runs in the context of the rule.)


You can create an implicit guard target, that checks that the variable in the stem is defined, like this:

guard-%:
    @ if [ "${${*}}" = "" ]; then \
        echo "Environment variable $* not set"; \
        exit 1; \
    fi

You then add a guard-ENVVAR target anywhere you want to assert that a variable is defined, like this:

change-hostname: guard-HOSTNAME
        ./changeHostname.sh ${HOSTNAME}

If you call make change-hostname, without adding HOSTNAME=somehostname in the call, then you'll get an error, and the build will fail.


Inline variant

In my makefiles, I normally use an expression like:

deploy:
    test -n "$(ENV)"  # $$ENV
    rsync . $(ENV).example.com:/var/www/myapp/

The reasons:

  • it's a simple one-liner
  • it's compact
  • it's located close to the commands which use the variable

Don't forget the comment which is important for debugging:

test -n ""
Makefile:3: recipe for target 'deploy' failed
make: *** [deploy] Error 1

... forces you to lookup the Makefile while ...

test -n ""  # $ENV
Makefile:3: recipe for target 'deploy' failed
make: *** [deploy] Error 1

... explains directly what's wrong

Global variant (for completeness, but not asked)

On top of your Makefile, you could also write:

ifeq ($(ENV),)
  $(error ENV is not set)
endif

Warnings:

  • don't use tab in that block
  • use with care: even the clean target will fail if ENV is not set. Otherwise see Hudon's answer which is more complex

Tags:

Makefile