DEV Community

Daniel Brady
Daniel Brady

Posted on

Guarding Makefile targets

This may just be my favorite one liner of my own devising in recent years.

I love it because of a combination of its utility (🔧), the story behind how it works (💭), and its power to induce headaches to its readers (😅).

It belongs in a Makefile :

## Returns true if the stem is a non-empty environment variable, or else raises an error. guard-%: @#$(or ${$*}, $(error $* is not set)) 
Enter fullscreen mode Exit fullscreen mode

And has a simple function:

➜ make guard-FOO Makefile:12: *** FOO is not set. Stop. ➜ FOO=42 make guard-FOO ➜ 
Enter fullscreen mode Exit fullscreen mode

Deciphering from top to bottom, left to right:

  • The % symbol used in the target declaration a “static pattern wildcard”, and in more familiar regex terms it translates to guard-(.*) ; the “stem” of the pattern, i.e. the value of the match group, is stored in an automatic variable called $*.
  • The @ prefix prevents make from printing out the command it is about to evaluate. (Hint: Take this away to actually see what’s happening.)
  • The # is your typical shell comment character: anything that comes after it will be interpreted by the shell (and make) as a comment, and thus ignored in a "truthy" way.
  • The $(or …, …, ...) is a make short-circuiting conditional function: it expands each item until it finds one that expands to a non-empty string, at which point it stops processing and returns the expansion. (Note: It does not evaluate it.)
  • The ${…} notation is the make syntax for evaluating a variable.
  • The $* as mentioned previously, holds the value of the matched pattern in the target name.
  • The $(error ...) is a make control-flow form, and raises an error as soon as it is evaluated, after allowing for its arguments to be expanded.

So, putting it all together: FOO=42 make guard-FOO expands to:

#$(or ${FOO}, $(error FOO is not set)) 
Enter fullscreen mode Exit fullscreen mode

which in turn expands to:

#$(or 42, $(error FOO is not set)) 
Enter fullscreen mode Exit fullscreen mode

which, finally, expands to:

#42 
Enter fullscreen mode Exit fullscreen mode

which is evaluated by the shell as a comment and ignored, but in a truthy way.

However, make guard-FOO expands to:

#$(or ${FOO}, $(error FOO is not set)) 
Enter fullscreen mode Exit fullscreen mode

and then:

#$(or , $(error FOO is not set)) 
Enter fullscreen mode Exit fullscreen mode

and then:

#$(error FOO is not set) 
Enter fullscreen mode Exit fullscreen mode

and then a fatal error.

The value of this is as a prerequisite for other targets, to easily guard against missing environment variables, e.g.

say-something: guard-SOMETHING echo ${SOMETHING} 
Enter fullscreen mode Exit fullscreen mode
➜ make say-something Makefile:12: *** SOMETHING is not set. Stop. ➜ SOMETHING='Hello, world!' make say-something echo Hello, world! Hello, world! 
Enter fullscreen mode Exit fullscreen mode

Top comments (0)