Makefile check existence of variable from list - makefile

I have a Makefile:
#Build Configurations
CONFIGS = Debug Release Profile
#Config flags
Debug_Flags=dasd
Release_Flags=
.SECONDEXPANSION:
#Debug_safety_check Release_safety_check Profile_safety_check targets
$(addsuffix _safety_check,$(CONFIGS)):
#Check existence of variable
ifeq '$(origin $(subst safety_check,FLAGS,$#))' 'undefined'
$(error $(subst safety_check,FLAGS,$#) variable undefined)
endif
#How to make our configurations (do corresponding safety_checks)
$(CONFIGS): $$#_safety_check
This line is incorrect:
ifeq '$(origin $(subst safety_check,FLAGS,$#))' 'undefined'
I think, It is because $# expansion occurs, when calling corresponding safety check. But ifeq expansion occurs "immediately", so, in fact, we get such line:
ifeq '$(origin ' ') 'undefined'
Does some method of check variables definition from list exist?

Ufff, gotcha.
#Build Configurations
CONFIGS = Debug Release Profile
#Config flags
Debug_Flags=dasd
Release_Flags=
#Profile_Flags=
#Adds _flags suffix for each variable,
#get it's origin ('undefined' for undefined variables).
#If $(findstring) a lookup for the word 'undefined' succeeds,
#adds to the result variable
undef_flags = $(foreach c, $(CONFIGS), \
$(if $(findstring undefined, $(origin $c_Flags)), $c))
#Count words for undefined configs, must be not '0'
ifneq ($(words $(undef_flags)),0)
#strip because if flag is undefined we'll get the trash spaces
$(error Flags ($(strip $(undef_flags))) must be defined)
endif

Related

Makefile: conditionals inside define

I am trying to check that variable value is yes or no, but the following always fails:
FLAG1 ?= no
FLAG2 ?= yes
define check_
ifneq ($(filter $(2),$($(1))),$($(1)))
$(error Bad $(1) argument)
endif
endef
$(call check_,FLAG1,yes no)
$(call check_,FLAG2,yes no)
What am I doing wrong?
FLAG1 ?= no
FLAG2 ?= yes
define check_
$(if $(filter $($(1)),$(2)),,$(error BAD $(1) argument))
endef
$(call check_,FLAG1,yes no)
$(call check_,FLAG2,yes no)
Notice that you added a space before FLAG1, which means $$(1) resolves to $( FLAG1), which in turn resolves to blank. The next part is that I'm not sure about is the use if ifneq inside of a define. You can use $(if ) instead
---- EDIT ------
Actually, it's a combination of the missing space and #MadScientists answer... The following also works:
define check_
ifneq ($(filter $($(1)),$(2)),$($(1)))
$$(error Bad $(1) argument [$($(1))] / [$(filter $($(1)),$(2))])
endif
endef
$(eval $(call check_,FLAG1,yes no))
Thus ifneq can be used inside of a macro... (and as #MadScientist pointed out, you have to escape the $ in front of $(error) to prevent it from being expanded by call...)
You can't use plain call with a multi-line macro. You have to use $(eval $(call ...)) if the result of the call function consists of more than one line of makefile content.
You can use this:
define check_
ifneq ($$(filter $(2),$$($(1))),$$($(1)))
$$(error Bad $(1) argument)
endif
endef
Basically, anything you want to be interpreted by the eval needs to be escaped so that call doesn't see it.

conditional execution in makefile

I have the following makefile:
C_FILE=""
cfg:
## C to CFG ####
# echo $(C_FILE)
ifndef C_FILE
$(error variable C_FILE not set)
endif
$(eval CFG_FILE := ./outputs/temp/$(shell basename $(C_FILE) .c).cfg)
gcc -fdump-tree-cfg=$(CFG_FILE) $(C_FILE)
When I run the command make cfg C_FILE="./inputs/Fib.c" it always
terminates saying variable C_FILE not set.
Lines beginning with a tab character (by default) aren't parsed by make (other than for variable expansion), they're sent directly to the shell, get rid of the indents on the lines with the make conditionals
C_FILE=""
cfg:
## C to CFG ####
# echo $(C_FILE)
ifndef C_FILE
$(error variable C_FILE not set)
endif
$(eval CFG_FILE := ./outputs/temp/$(shell basename $(C_FILE) .c).cfg)
gcc -fdump-tree-cfg=$(CFG_FILE) $(C_FILE)
I'd like to add some comments to user657267's answer.
ifndef C_FILE is always false. C_FILE is defined on the first line, or from command line. Consider using ifeq "" "$(C_FILE)".
Quote (") is normal character in makefile. Define empty variable this way:
C_FILE=
instead of using $(shell ) function, use makefile built-ins:
$(basename $(notdir $(C_FILE)))
avoid using $(eval ) if not really needed. Extract relevant code outside recipe.
My proposal is:
C_FILE=
ifeq "" "$(C_FILE)"
$(error variable C_FILE not set)
endif
CFG_FILE=./outputs/temp/$(basename $(notdir $(C_FILE))).cfg
cfg:
## C to CFG ####
# echo $(C_FILE)
gcc -fdump-tree-cfg=$(CFG_FILE) $(C_FILE)

Getting exit code of shell execution inside define

I have something like this in my makefile:
exit_code := $(shell some_script.py; echo $$?)
ifneq ($(exit_code),0)
$(error Error occured)
endif
and it works properly - the echo $$? returns exit code of python script
I need to put that code into define like that:
define run-python-script
exit_code := $(shell some_script.py; echo $$?)
ifneq ($(exit_code),0)
$(error Error occured)
endif
endef
$(call run-python-script)
but then exit_code does not contain the exit code. And $(error Error occured) is always executed.
How to make work the version with define?
I need to put that code into define like that:
The if()/endif are processed and evaluated when the Makefile is parsed. You can't use them inside a variable definition.
The exit_code := ... in first snippet is a definition of a make variable, while in the second it is just a string, part of the make's variable called run-python-script.
You can try this (your script is replaced with false for test purposes):
eq = $(and $(findstring $(1),$(2)),$(findstring $(2),$(1)))
the-command = false; echo $$?
run-python-script = $(if $(call eq,0,$(shell $(the-command))),,$(error Error occured))
$(run-python-script)
(The $(call) is redundant in that case.)

Test if a list of variables are defined in a Makefile

My Makefile is based on multiple variables defined in a configuration file or ENV vars. My current solution is to test all of them manually:
NOGOAL = help clean distclean mrproper
ifeq ($(strip $(filter $(NOGOAL), $(MAKECMDGOALS))),)
VAR1 ?= $(error VAR1 undefined)
VAR2 ?= $(error VAR2 undefined)
VAR3 ?= $(error VAR3 undefined)
...
VARn ?= $(error VARn undefined)
endif
I would like to use a foreach loop instead:
ifeq ($(strip $(filter $(NOGOAL), $(MAKECMDGOALS))),)
TESTVAR = TEST1 TEST2 TEST3
$(foreach v, $(TESTVAR), $(eval $v ?= $$(warning Error: $v undefined)))
endif
Unfortunately eval doesn't work as I expected. Did I miss something?
Here a full test of my tests with 2 implementations of the tests. Even if TEST3 is not defined I don't get any error
TEST1 = 1
TEST2 = 1
#TEST3 = 1 # NOT DEFINED
TESTVAR := TEST1 TEST2 TEST3
# First implementation
$(foreach v, $(TESTVAR), $(eval $v ?= $$(warning Error: $v undefined)))
# Second implementation
$(foreach v, $(TESTVAR), $(eval $(call TESTER,$v)))
define TESTER
ifndef $1
$(warning $1 not defined)
endif
endef
# Dummy rule
all:
#echo Hello World
However, my first implementation works if I use $(TEST3) somewhere.
EDIT
Here I get no error but TEST3 is not defined:
~$ cat Makefile
TEST1 = 1
TEST2 = 1
#TEST3 = 1 # NOT DEFINED
TESTVAR := TEST1 TEST2 TEST3
# First implementation
$(foreach v, $(TESTVAR), $(eval $v ?= $$(warning Error: $v undefined)))
# Dummy rule
all:
#echo Hello World
~$ make
Hello World
Well, I guess I don't get it. Your original version, that you say works the way you want it, will not print any warnings unless you USE one of the variables which is not defined. Your first alternative with foreach works the same way: it will print a warning but only when you use the variable that's undefined.
If you want it that way, then testing for clean, etc. doesn't really make much sense since presumably those rules won't use the variables that are not defined so you won't get any errors (and if they did use the variables that weren't defined, presumably you'd want those rules to fail as well).
But in your second edit, you say that you want the make to fail immediately if the variables are not defined, regardless of whether or not they're used (in your last example you don't define TEST3, but you don't use TEST3 for anything either so no warning is printed). If that's what you want I don't see why you are assigning values to the variables with ?= at all, or using eval. Just write something like:
ifeq ($(strip $(filter $(NOGOAL), $(MAKECMDGOALS))),)
$(foreach v,$(TESTVAR),$(if $($v),,$(error Error: $v undefined))
endif
(In this version you do need to check MAKECMDGOALS since it fails immediately on an unset variable).

Using GNU-make functions to check if variables are defined

I'm writing a makefile that requires some enviroment variables to be defined. I am trying to use something like this to acheive this:
define check-var-defined
ifndef $(1)
$(error $(1) is not defined)
endif
endef
$(call check-var-defined,VAR1)
$(call check-var-defined,VAR2)
$(call check-var-defined,VAR3)
rule1:
#stuff
When I run make with no args I get this:
$ make
Makefile:7: *** VAR1 is not defined. Stop.
But when I run it with VAR1 specified I get the same error.
$ make VAR1=hello
Makefile:7: *** VAR1 is not defined. Stop.
Any ideas why this doesn't work? What can I do to make this work? Thanks in advance.
(Note that I need to check that the variables are actually defined when the makefile is run, as I need to include another makefle further down and the variables need to be set correctly by the time I do this).
The $(call ...) function does not evaluate the results of the function as if it were makefile code, so you can't things like ifdef there.
What happens is that the contents of check-var-defined are expanded and since it doesn't recognize the ifdef operation, it just proceeds to expand the $(error ...) function every time.
If you want to use ifdef you have to use $(eval ...) with $(call ...) which will evaluate the result as if it were a makefile.
Simpler is to use the $(if ...) function, like this:
check-var-defined = $(if $(1),,$(error $(1) is not defined))
Note that this will fail if the variable is empty, which is not quite the same thing as being undefined; it could have been defined to be empty (as VAR1=). But that's the way ifdef works, too, confusingly.
the macro in 1st answer is great but doesn't actually report the name of the 'empty' variable. here is a slight improvement with example/test:
# -*- mode: makefile -*-
check-var-defined = $(if $(strip $($1)),,$(error "$1" is not defined))
my_def1:=hello
my_def3:=bye
$(call check-var-defined,my_def1)
$(call check-var-defined,my_def2)
$(call check-var-defined,my_def3)
and the result:
Makefile:10: * "my_def2" is not defined. Stop.
defined = $(strip $(filter-out undefined,$(flavor $1)))
ensure-defined = \
$(eval .ensure-defined :=) \
$(foreach V,$(sort $1), \
$(if $(call defined,$V),,$(eval .ensure-defined += $V)) \
) \
$(if $(strip ${.ensure-defined}), \
$(foreach V,${.ensure-defined}, \
$(info NOT DEFINED: $$$V) \
) \
$(error Required variables not defined) \
)
ifFOO = $(if $(call defined,FOO), \
$(info FOO is defined: '${FOO}'), \
$(info FOO not defined) \
)
$(ifFOO)
FOO := foo
$(ifFOO)
$(call ensure-defined,FOO BAR)
all: ; #:
OUTPUT:
$ make -f foo.mk
FOO not defined
FOO is defined: 'foo'
NOT DEFINED: $BAR
foo.mk:25: *** Required variables not defined. Stop.

Resources