Check if variable is defined in a Makefilecheck_defined = \ $(strip $(foreach 1,$1, \ $(call __check_defined,$1,$(strip $(value 2))))) __check_defined = \ $(if $(value $1),, \ $(error Undefined $1$(if $2, ($2)))) install: $(call check_defined, var1) $(call check_defined, var2) # do stuff here..
$$ means be interpreted as a $ by the shell. the $(UNZIP_PATH) gets expanded by make before being interpreted by the shell.
The variable $@ represents the name of the target and $< represents the first prerequisite required to create the output file.
TL;DR: Use the error
function:
ifndef MY_FLAG
$(error MY_FLAG is not set)
endif
Note that the lines must not be indented. More precisely, no tabs must precede these lines.
In case you're going to test many variables, it's worth defining an auxiliary function for that:
# Check that given variables are set and all have non-empty values,
# die with an error otherwise.
#
# Params:
# 1. Variable name(s) to test.
# 2. (optional) Error message to print.
check_defined = \
$(strip $(foreach 1,$1, \
$(call __check_defined,$1,$(strip $(value 2)))))
__check_defined = \
$(if $(value $1),, \
$(error Undefined $1$(if $2, ($2))))
And here is how to use it:
$(call check_defined, MY_FLAG)
$(call check_defined, OUT_DIR, build directory)
$(call check_defined, BIN_DIR, where to put binary artifacts)
$(call check_defined, \
LIB_INCLUDE_DIR \
LIB_SOURCE_DIR, \
library path)
This would output an error like this:
Makefile:17: *** Undefined OUT_DIR (build directory). Stop.
The real check is done here:
$(if $(value $1),,$(error ...))
This reflects the behavior of the ifndef
conditional, so that a variable defined to an empty value is also considered "undefined". But this is only true for simple variables and explicitly empty recursive variables:
# ifndef and check_defined consider these UNDEFINED:
explicitly_empty =
simple_empty := $(explicitly_empty)
# ifndef and check_defined consider it OK (defined):
recursive_empty = $(explicitly_empty)
As suggested by @VictorSergienko in the comments, a slightly different behavior may be desired:
$(if $(value $1)
tests if the value is non-empty. It's sometimes OK if the variable is defined with an empty value. I'd use$(if $(filter undefined,$(origin $1)) ...
And:
Moreover, if it's a directory and it must exist when the check is run, I'd use
$(if $(wildcard $1))
. But would be another function.
It is also possible to extend the solution so that one can require a variable only if a certain target is invoked.
$(call check_defined, ...)
from inside the recipeJust move the check into the recipe:
foo :
@:$(call check_defined, BAR, baz value)
The leading @
sign turns off command echoing and :
is the actual command, a shell no-op stub.
The check_defined
function can be improved to also output the target name (provided through the $@
variable):
check_defined = \
$(strip $(foreach 1,$1, \
$(call __check_defined,$1,$(strip $(value 2)))))
__check_defined = \
$(if $(value $1),, \
$(error Undefined $1$(if $2, ($2))$(if $(value @), \
required by target `$@')))
So that, now a failed check produces a nicely formatted output:
Makefile:7: *** Undefined BAR (baz value) required by target `foo'. Stop.
check-defined-MY_FLAG
special targetPersonally I would use the simple and straightforward solution above. However, for example, this answer suggests using a special target to perform the actual check. One could try to generalize that and define the target as an implicit pattern rule:
# Check that a variable specified through the stem is defined and has
# a non-empty value, die with an error otherwise.
#
# %: The name of the variable to test.
#
check-defined-% : __check_defined_FORCE
@:$(call check_defined, $*, target-specific)
# Since pattern rules can't be listed as prerequisites of .PHONY,
# we use the old-school and hackish FORCE workaround.
# You could go without this, but otherwise a check can be missed
# in case a file named like `check-defined-...` exists in the root
# directory, e.g. left by an accidental `make -t` invocation.
.PHONY : __check_defined_FORCE
__check_defined_FORCE :
Usage:
foo :|check-defined-BAR
Notice that the check-defined-BAR
is listed as the order-only (|...
) prerequisite.
Pros:
Cons:
make -t
(see Instead of Executing Recipes) will pollute your root directory with lots of check-defined-...
files. This is a sad drawback of the fact that pattern rules can't be declared .PHONY
.I believe, these limitations can be overcome using some eval
magic and secondary expansion hacks, although I'm not sure it's worth it.
Use the shell function test
:
foo:
test $(something)
Usage:
$ make foo
test
Makefile:2: recipe for target 'foo' failed
make: *** [foo] Error 1
$ make foo something=x
test x
You can use an IF to test:
check:
@[ "${var}" ] || ( echo ">> var is not set"; exit 1 )
Result:
$ make check
>> var is not set
Makefile:2: recipe for target 'check' failed
make: *** [check] Error 1
Use the shell error handling for unset variables (note the double $
):
$ cat Makefile
foo:
echo "something is set to $${something:?}"
$ make foo
echo "something is set to ${something:?}"
/bin/sh: something: parameter null or not set
make: *** [foo] Error 127
$ make foo something=x
echo "something is set to ${something:?}"
something is set to x
If you need a custom error message, add it after the ?
:
$ cat Makefile
hello:
echo "hello $${name:?please tell me who you are via \$$name}"
$ make hello
echo "hello ${name:?please tell me who you are via \$name}"
/bin/sh: name: please tell me who you are via $name
make: *** [hello] Error 127
$ make hello name=jesus
echo "hello ${name:?please tell me who you are via \$name}"
hello jesus
For simplicity and brevity:
$ cat Makefile
check-%:
@: $(if $(value $*),,$(error $* is undefined))
bar:| check-foo
echo "foo is $$foo"
With outputs:
$ make bar
Makefile:2: *** foo is undefined. Stop.
$ make bar foo="something"
echo "foo is $$foo"
foo is something
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With