Makefile conditional variable assignment - makefile

I wish to make the following target:
ifeq ("$(VARENV1)", "true")
VARENV3=$(VARENV2)
endif
dummy:
echo $(VARENV3)
I am then calling my makefile with
VARENV1=true VARENV2=test make dummy
expecting to get a display of "test".
But this fail as VARENV3 is empty.
However, if I use the following makefile:
ANYVARNAME=ANYVALUE
ifeq ("$(VARENV1)", "true")
VARENV3=$(VARENV2)
endif
dummy:
echo $(VARENV3)
Purpose is to override VARENV3 with VARENV2 in some condition.
Then I will correctly display "test".
I am bit confused at why, any clue?

Related

How to debug GNU make's variable assignment

My Makefile contains something like
BASE_VAR=$(cat path/to/value.txt)
DERIVED_VAR=$(if $(BASE_VAR),foo,bar)
This does not produce the results I expected. make --debug=a produces detailed trace but path/to/value.txt doesn't appear anywhere in it. How do I debug this kind of variable assignment in a makefile?
The problem comes from:
BASE_VAR=$(cat path/to/value.txt)
As there is not make variable or macro named cat path/to/value.txt the expansion
$(cat path/to/value.txt) produces the empty string. If you want to set variable BASE_VAR with the content of file path/to/value.txt you need the shell make function:
BASE_VAR = $(shell cat path/to/value.txt)
And about your question on debugging variables, the simplest in your case is probably to use the info make function:
$ cat Makefile
BASE_VAR = $(shell cat path/to/value.txt)
DERIVED_VAR = $(if $(BASE_VAR),foo,bar)
.PHONY: debug
debug:
$(info BASE_VAR = $(BASE_VAR))
$(info DERIVED_VAR = $(DERIVED_VAR))
$ make debug
BASE_VAR = blah
DERIVED_VAR = foo
make: Nothing to be done for 'debug'.
But that target-specific variables have different values in different targets. Use $(info ...) in the recipe of the target of interest:
target: VAR = value
target: ...
$(info VAR of $# = $(VAR))

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).

Makefile check existence of variable from list

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

Resources