Makefile compare string input - shell

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.

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.

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)

Makefile function with multiple statements

[HEADS UP] : There are some similar questions that are already present here on stackoverflow but they seem to not completely resolve my issue. Therefore, I am posting this question.
I am trying to write a makefile function that should set a value to a variable that is passed as argument to the function.
So, I am calling this function as -
RESULT :=
$(eval $(call myfunction,RESULT,value,res1,res2))
here 'res1' and 'res2' are two possible resulting values for RESULT and the argument 'value' will be used for some test condition.
Following is my attempt of the definition of myfunction. But it seems that it is not working.
define myfunction
TEST1 := $(shell test `mybinary` -ge 5 && printf "TEST")
TEST2 := $(findstring $(2),$(SOME_SHELL_ENV))
$(info "$(TEST1)")
$(info "$(TEST2)")
ifneq "$$(or $(TEST1),$(TEST2)" ""
LOCAL_RESULT := true
else
LOCAL_RESULT := false
endif
ifeq($(LOCAL_RESULT),true)
$(1) = $(3)
else
$(1) = $(4)
endif
endef
To me it appears that the local variables TEST1 and TEST2 are not even getting set.
Can somebody tell me why my function is not working correctly and what changes do I have to make to resolve the issues?
Quoting eval manual page:
The eval function is very special: [...] The argument to the eval function is expanded, then the results of that expansion are parsed as makefile syntax.
It’s important to realize that the eval argument is expanded twice; first by the eval function, then the results of that expansion are expanded again when they are parsed as makefile syntax. This means you may need to provide extra levels of escaping for “$” characters when using eval.
This happens after $(call) arguments are substituted, so $1 etc are already expanded by the time $(eval) is called, and need not to be $-escaped.
To make variables expand during the last (parsing) stage of $(eval), escape $s of non-numeric variables by doubling them.
define myfunction
TEST1 := $$(shell test `echo 6` -ge 5 && printf "TEST")
TEST2 := $$(findstring $(2),$$(PATH))
$$(info "$$(TEST1)")
$$(info "$$(TEST2)")
ifneq "$$(or $$(TEST1),$$(TEST2))" ""
LOCAL_RESULT := true
else
LOCAL_RESULT := false
endif
ifeq ($$(LOCAL_RESULT), true)
$(1) = $(3)
else
$(1) = $(4)
endif
endef
$(eval $(call myfunction,RESULT,value,res1,res2))
test:
echo "$(LOCAL_RESULT)"
Plus, you had a missing closing brace in $(or).
It's not clear if res1 and res2 are variable names or values; depending on this, the last two assignments need or need not to look like $(1) := $($(4)).
Try to always use eager assignments: :=, for fewer surprises from lazy variable expansion.

Make args: is there a variable that contains all of them?

In make, I can define a file as follows:
.PHONY: echo-foo
echo-foo:
echo ${foo}
Usage:
make echo-foo foo=foo
Now suppose I would like to do the following:
make echo-vars foo=foo bar=bar
And I start by writing:
echo-vars:
echo ${???}
Is there a variable automatically defined which will output:
// either
foo=foo bar=bar
// or
foo bar
In other words: is there a MAKEARGS or some variable along those lines defined as part of the make env?
There's a special variable just for that:
echo-vars:
echo ${MAKEOVERRIDES}
.VARIABLES is somewhat close.
This prints only command-line variables:
test:
echo "$(foreach VAR_NAME,$(.VARIABLES),\
$(if $(filter command line,$(origin $(VAR_NAME))),$(VAR_NAME)))"
$(filter command line,$(origin VAR_NAME)) equals to "command line" if it's the value of $(origin VAR_NAME), and is empty otherwise. $(filter <needle>,<haystack>) is a make's boolean test for "<needle> is in or equals to <haystack> and is not empty".
$(if <condition>,<value>) returns <value> if <condition> is nonempty, otherwise an empty string.
$(foreach VAR_NAME,SET,EXPRESSION) returns a joined result of EXPRESSION applied to each element of a (space-separated) SET, where VAR_NAME is substituted with each element of the SET.
Add $(strip) to get rid of excess spaces.

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

Resources