How to compare strings in a conditional assignment? - makefile

Is difficult to understand this official guide... How to check string condition?
This assign is working fine, but is ugly:
x := $$( if [ $(HOME) = "/root" ]; then echo "IS ROOT"; else echo "IS OTHER"; fi )
This is not working:
ifeq ($(HOME),"/root")
x = "IS ROOT"
else
x = "IS OTHER"
endif
make --version says:
GNU Make 4.2.1
Built for x86_64-pc-linux-gnu

When you write:
ifeq ($(HOME),"/root")
you're using the form (from the doc you cite):
ifeq (arg1, arg2)
and arg1 is set to $(HOME) and arg2 is set to "/root". Suppose the HOME variable is set to the path /root.
So, make will compare /root to "/root". Are these the same string? No: one has quotes around it so they are not the same string.
You want to use:
ifeq ($(HOME),/root)

Related

Makefile ifeq always true

I have the following Makefile target:
target1:
$(eval count_abc := $(shell grep -c "ABC" myFileA))
$(eval count_def := $(shell grep -c "DEF" myFileB))
echo $(count_abc)
echo $(count_def)
ifeq ($(count_abc),$(count_def))
echo "TRUE"
else
echo "FALSE"
endif
But the output is always TRUE, e.g.:
echo 22
22
echo 21
21
echo TRUE
TRUE
What am I doing wrong here? What I want is INSIDE the target do 2 greps and compare their outputs and do something or something else based on the result. Please note that the greps must be done within the target since myFileA and myFileB get created on the target before and don't exist at the beginning when running make.
Thanks,
Amir
The rule file for "make" is declarative in nature - the makefile defines rules and targets, and then the make program evaluate the rules, and decide which action to take based on the target. As a result, execution is not always in the order the lines are entered into the file.
More specifically, the "ifeq" is evaluated at the rule definition stage, but the actions for building the target (eval count_abc ...) are executed when the target is built. As a result, when the ifeq is processed, both count_abc and count_def are still uninitialized, expanded to empty strings.
For the specific case you described - building a target that will compare the grep -c output from the two files, you can try something like below, effectively using shell variables (evaluated when target is evaluated), and not make variables (which are mostly declarative, evaluated when makefile is read)
target1:
count_abc=$(grep -c "ABC" myFileA) ; \
count_def=$(grep -c "DEF" myFileB) ; \
echo $(count_abc) ; \
echo $(count_def) ; \
if [ "$count_abc" -eq "$count_def" ] ; then echo TRUE ; else echo FALSE ; fi
Disclaimer: I did not run the revised makefile, not having access to desktop at this time.

Makefile compare string input

To understand better string variables in a Makefile, I have tried to do this example :
KEYWORD=Nothing
test:
$(call myFunc)
define myFunc
ifeq ($(KEYWORD), "Apple")
echo "You have found the key"
else
echo "Try again"
endif
endef
But when I'm typing
make test KEYWORD="Fork"
It prints errors
syntax error near unexpected token `Fork,'
`ifeq (Fork, "Apple")'
I have also tried :
Put Apple or 'Apple' in ifeq
Put a space or not after "," : ifeq ($(KEYWORD), "Apple")
Run command with KEYWORD=Fork
Did it using shell (if [ ${KEYWORD} -eq "Apple" ])
I'm running out of ideas because I don't understand how Makefille / Shell interpret the assignment KEYWORD="Fork"
Thanks
MadScientist identifies the problem. Perhaps the solution you're looking for is simply evaluating the conditional earlier. eg:
KEYWORD ?= NOTHING
...
ifeq ($(KEYWORD), Apple)
define myFunc
echo "You have found the key"
endef
else
define myFunc
echo "Try again"
endef
endif
First, $(call myfunc) is 100% identical to writing $(myfunc). The call function in make merely expands a variable with some other local values (the arguments) bound first. If you don't provide any local values, then you're just expanding the variable.
Expanding a variable just replaces the variable reference with what it expands to. So writing:
FOO = bar
foo:
echo $(FOO)
is 100% identical to writing:
foo:
echo bar
So in your situation,
test:
$(call myFunc)
is the same as:
test:
$(myFunc)
which is the same as:
test:
ifeq ($(KEYWORD), "Apple")
echo "You have found the key"
else
echo "Try again"
endif
which is why you get the output you did: these are not valid shell commands, but since you've expanded the variable as part of a recipe, they are sent to the shell as part of the recipe.

Make variable doesn't evaluate

I am trying to evaluate a make variable which name is stored in another variable but it is not evaluating. Below is simplified version of my problem:
VAR=MYDEV
MYDEV_init=UART_init
define create_kernels_c
dev=$$(echo MYDEV); #this is mandatory code, simplified here
echo dev = $$dev;
initfn=$$dev\_init;
echo initfn= $$initfn
devinit=$($$initfn)
echo devinit= $$devinit
endef
.ONESHELL:
all:
#$(call create_kernels_c)
I want to evaluate $$devinit to UART_init, but it is not evaluating. What mistake I am making in my code?

Makefile - run time decisions using ifeq

I am trying to solve a particular problem where variables are assigned in one recipe and then interpreted in other recipes, all during run time. As I understand it, ifeq conditions are evaluated during parsing which doesn't work for me as some of them are always false. Is there a way to implement what I'm trying to do (expected output is below)? I'll provide more info if needed.
I'm using make version 3.81 on Linux Mint 17.1.
Here is what I have so far:
fourth =
all: check valueOfFourth definitionOfFourth
.PHONY: all
check:
#echo "TEST"$(cnt)
ifeq ($(first),$(second))
#echo "1. First condition"
$(eval fourth = "first")
else ifeq ($(first),$(third))
#echo "1. Second condition"
$(eval fourth = "second")
else
#echo "1. Conditions weren't met"
endif
valueOfFourth:
ifeq ($(fourth),"first")
#echo "2. First"
else ifeq ($(fourth),"second")
#echo "2. Second"
else
#echo "2."
endif
definitionOfFourth:
ifeq ($(fourth),)
#echo "3. Variable is not defined"
else
#echo "3. Variable is defined"
endif
It is invoked like this:
make cnt="1" first="x" second="x" third="y" && printf "\n" && \
make cnt="2" first="x" second="y" third="x" && printf "\n" && \
make cnt="3" first="x" second="y" third="z"
Expected output:
TEST1
1. First condition
2. First
3. Variable is defined
TEST2
1. Second condition
2. Second
3. Variable is defined
TEST3
1. Conditions weren't met
2.
3. Variable is not defined
Actual output:
TEST1
1. First condition
2.
3. Variable is not defined
TEST2
1. Second condition
2.
3. Variable is not defined
TEST3
1. Conditions weren't met
2.
3. Variable is not defined
It is evident that only the "check" target does what it is supposed to do, the other two simply do not work.
I'm still not entirely clear how these targets are supposed to interact (any interaction between them is generally a bad idea as parallel make execution means that without being explicitly sequenced via prerequisites between them execution order is not guaranteed). But assuming non-parallel make and that each target is supposed to output one of the lines of output I believe this does what you want.
.PHONY: all
all: check valueOfFourth definitionOfFourth
ifeq ($(first),$(second))
fourth = First
condmsg = $(fourth) condition
else ifeq ($(first),$(third))
fourth = Second
condmsg = $(fourth) condition
else
condmsg = Conditions weren'\''t met
endif
check:
#echo 'TEST$(cnt)'
#echo '1. $(condmsg)'
valueOfFourth:
#echo '2. $(fourth)'
definitionOfFourth:
ifeq ($(fourth),)
#echo "3. Variable is not defined"
else
#echo "3. Variable is defined"
endif

get a default value when variable is unset in make

(edit: question more accurate based on #Michael feedback)
In bash, I often use parameter expansion: the following commands print "default value" when $VARNAME is unset, otherwise it prints the VARNAME content.
echo ${VARNAME:-default value} #if VARNAME empty => print "default value"
echo ${VARNAME-default value} #if VARNAME empty => print "" (VARNAME string)
I did not find a similar feature on GNU make.
I finally wrote in my Makefile:
VARNAME ?= "default value"
all:
echo ${VARNAME}
But I am not happy with this solution: it always creates the variable VARNAME and this may change the behavior on some makefiles.
Is there a simpler way to get a default value on unset variable?
If you want to use the expansion of a GNU make variable if it is non-empty and a default value if it is empty, but not set the variable, you can do something like this:
all:
echo $(or $(VARNAME),default value)
If you want to test if a variable has a non-empty value, you can use:
ifeq ($(VARNAME),)
VARNAME="default value"
else
do_something_else
endif
For checking if a variable has been defined or not, use ifdef.
Refer to Syntax of Conditionals in the manual for more.
I have a similar case where the result of filtering a shell command could be a single word or empty string. When empty, it should fallback to the default word. In the example below APPLE_LINUX will be 'apple' on macOS or 'linux' on other platforms. MSG will be set to the message for the appropriate platform. The example intentionality avoids using ifeq.
MACHINE := $(shell $(COMPILE.cpp) -dumpmachine)
MACHINE_APPLE := $(findstring apple,$(MACHINE))
APPLE_LINUX := $(firstword $(MACHINE_APPLE) linux)
apple.MSG := You are building on macOS
linux.MSG := You are building on Linux or another OS
MSG := $($(APPLE_LINUX).MSG)
Just remove the colon. If you use :- in your substitution the default value will be used if the variable is null, an empty string or it does not exist, but just using - on its own will only substitute the default value if the variable has not been defined.
# var1=default
# var2=
# echo var2 is ${var2:-$var1}
var2 is something
# echo var3 is ${var3:-$var1}
var3 is something
# echo var2 is ${var2-$var1}
var2 is
# echo var3 is ${var3-$var1}
var3 is something

Resources