1

I'm trying to require that an environment variable be set in a Makefile when running a specific target. I'm using the technique from the answer to this question, where you setup another target that will guarantee an environment variable to be set.

Mine looks like this:

require-%:
    @ if [ "${${*}}" = "" ]; then \
        $(error You must pass the $* environment variable); \
    fi

With that target setup, this is expected:

$ make require-FOO
Makefile:3: *** You must pass the FOO environment variable.  Stop.

However, when testing, I can never get it to not error:

$ make require-FOO FOO=something
Makefile:3: *** You must pass the FOO environment variable.  Stop.

$ make require-FOO FOO=true
Makefile:3: *** You must pass the FOO environment variable.  Stop.

$ make require-FOO FOO='a string'
Makefile:3: *** You must pass the FOO environment variable.  Stop.

Even when I comment out the if block in the target:

require-%:
    # @ if [ "${${*}}" = "" ]; then \
    #   $(error You must pass the $* environment variable); \
    # fi

I still get an error when running it:

$ make require-FOO FOO=something
Makefile:3: *** You must pass the FOO environment variable.  Stop.

What's am I doing wrong? How can I get this to work?

Community
  • 1
  • 1
Ian Storm Taylor
  • 8,520
  • 12
  • 55
  • 72

1 Answers1

7

You modified the solution presented in that linked answer without understanding the difference.

The linked answer uses a shell echo and a shell exit to do the message output and exiting.

Your modification uses the make $(error) function.

The difference is that the shell commands only execute when the shell logic says they should but the make function executes before make runs the shell commands at all (and always expands/executes). (Even in shell comments because those are shell comments.)

If you want this asserted at shell time then you need to use shell constructs to test and exit. Like the original answer.

If you want this asserted at recipe expansion time then you need to use make constructs to test and exit. Like this (untested):

require-%:
    @: $(if ${${*}},,$(error You must pass the $* environment variable))
    @echo 'Had the variable (in make).'
Etan Reisner
  • 77,877
  • 8
  • 106
  • 148
  • Wouldn't the Make conditional in your final snippet still be executed at parse time? Anyway, a simple `set -u` would suffice for getting the shell to bail if an undefined shell variable is referenced. – tripleee Mar 16 '16 at 20:08
  • Wow thank you, that was an incredibly dumb mistake. I must have done that right away and completely forgotten. Thanks! – Ian Storm Taylor Mar 16 '16 at 21:31
  • @tripleee It shouldn't, no. (Though I'll admit to not having tested this when I wrote it.) Recipes aren't expanded until they are going to be executed so that they can include expansions of the target-specific variables, etc. If I'd used `ifeq (...)` then it would be expanded at parse-time but the `$(if ...)` function shouldn't be in the recipe. – Etan Reisner Mar 17 '16 at 12:41