Makefile - run time decisions using ifeq - makefile

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

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.

How to compare strings in a conditional assignment?

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)

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?

"define" and "endef" syntax-rules in makefile

From the docs:
Note that lines beginning with the recipe prefix character are
considered part of a recipe, so any define or endef strings
appearing on such a line will not be considered make directives.
My worry is, that is not always the case.
As evident, by the following makfile:
ifeq "x" "y"
define xxx
else
foo = 1
endef
else
bar = 2
endif
all ::
#echo 'foo is: "$(foo)"'
#echo 'bar is: "$(bar)"'
Running, we get:
$ make
foo is: ""
bar is: "2"
Going back to the makefile, it is evident that the lines 3 and 4 inside the ifeq directive, i.e.
ifeq "x" "y"
define xxx
else
foo = 1
endef
else
bar = 2
endif
are ignored by Make.
Put simply, make ignores these 2 lines, within the define xx
else
foo = 1
Because they are inside a define, and therefore, Make does not evaluate the else as a directive (i.e. as a else part, for ifeq "x" "y").
But, Make clearly parses that endef line (5th line in the ifeq block), as a directive to end the define directive.
Hence, after ending the define block with the endef line, Make will parse later lines, that is:
else
bar = 2
endif
As part of the ifeq block, and because the opening directive ifeq "x" "y" evaluated to false, Make will take the else part as a true statement, and assign the value 2 to the variable bar, and so on.
Why is all this important?
Because, clearly, Make allows here a endef line to be parsed as a directive, even though, it is prefixed with a <tab> character.
That is in clear violation of the documentation, quoted above:
Note that lines beginning with the recipe prefix character are
considered part of a recipe, so any define or endef strings
appearing on such a line will not be considered make directives.
Agree?

Resources