"ifeq" conditional syntax in makefile - makefile

As the conditional-directive ifeq is frequently used to compare word(s) expanded from variables, which often contains white-space, we may want and, in fact need, for Make to strip any leading or trailing white-space.
In fact, you may have an opposite view, i.e. Make should keep verbatim all of the arguments to an ifeq conditional, because the user may have had those whitespace as part of the "test", with the intention for those whitespace to play a deciding factor, when evaluating this ifeq directive, as true or false.
I can not decide, which of them is more correct.
In fact, I'm not alone!
Make itself cannot decide, which of them is correct. So, it may or may not strip leading or trailing whitespace.
In fact, sometimes it will strip only leading whitespace.
Not disappointing, Make will sometimes strip only trailing whitespace.
Of course, there are too many cases to check, so I will "do" only a few of them.
A makefile (VERSION 1), is:
ifeq ( a, a)
all::
echo 'true'
else
all::
echo 'false'
endif
Executing, I get:
$ make -r
echo 'false'
false
A makefile (VERSION 2), is:
ifeq (a ,a )
all::
echo 'true'
else
all::
echo 'false'
endif
Executing, I get:
$ make -r
echo 'false'
false
A makefile (VERSION 3), is:
ifeq ( a , a )
all::
echo 'true'
else
all::
echo 'false'
endif
Executing, I get:
$ make -r
echo 'false'
false
A makefile (VERSION 4), is:
ifeq (a , a)
all::
echo 'true'
else
all::
echo 'false'
endif
Executing, I get:
$ make -r
echo 'true'
true
A makefile (VERSION 5), is:
ifeq (a, a)
all::
echo 'true'
else
all::
echo 'false'
endif
Executing, I get:
$ make -r
echo 'true'
true
Summing up, just a few of the cases, we have:
# Both, have only leading whitespace.
ifeq( a, a) as: false.
# Both, have only trailing whitespace.
ifeq(a ,a ) as: false.
# Both, have trailing AND trailing whitespace.
ifeq( a , a ) as: false.
# Left-hand-size has only trailing, and right-hand-size has only leading whitepsace.
ifeq(a , a) as: true.
# Left-hand-size has NO whitespace at-all, and right-hand-size has only leading whitepsace.
ifeq(a, a) as: true.
So, this methodology, that Make uses to evaluate the truthfulness of an ifeq conditional directive, is definitely turning it into:
Less consistent.
Less maintainable.
Harder to debug.
Prone to error.
Finally, a lot of "fun"!
Do we agree?

You should read this:
Commas and unmatched parentheses or braces cannot appear in the text of an argument as written; leading spaces cannot appear in the text of the first argument as written. These characters can be put into the argument value by variable substitution. First define variables comma and space whose values are isolated comma and space characters, then substitute these variables where such characters are wanted, like this:
comma:= ,
empty:=
space:= $(empty) $(empty)
foo:= a b c
bar:= $(subst $(space),$(comma),$(foo))
# bar is now ‘a,b,c’.
You should also use the strip function when in doubt.
Here an example Makefile:
empty:=
space:= $(empty) $(empty)
x := $(space)a$(space)
y := $(space)a$(space)
ifeq ($(x),$(y))
all::
#echo 'regular: true'
else
all::
#echo 'regular: false'
endif
ifeq ($(strip $(x)),$(strip $(y)))
all::
#echo 'strip: true'
else
all::
#echo 'strip: false'
endif
And the result:
1:
x = $(space)a
y = $(space)a
regular: true
strip: true
2:
x = a$(space)
y = a$(space)
regular: true
strip: true
3:
x = $(space)a$(space)
y = $(space)a$(space)
regular: true
strip: true
4:
x = a$(space)
y = $(space)a
regular: false
strip: true
4:
x = a
y = $(space)a
regular: false
strip: true

Related

GNU Make: Test if either OR variables are null or undefined

As I understand it $(and should short circuit and return "" if either VAR1 OR VAR2 is null or undefined. This should make the ifeq true and print the message. Otherwise if both variables have values it should return the value of VAR2 which is obviously not "" making the ifeq evaluation false and not printing the message.
It's not working. What am I missing?
ifeq ($(and $(VAR1),$(VAR2)), "")
$(info *************************************)
$(info * To avoid prompts set *)
$(info * VAR1 and VAR2 *)
$(info * prior to running make *)
$(info *************************************)
endif
Yes. Basically: all arguments have contents, then the expansion is the contents of the last one. Otherwise, the expansion is empty.
Note, none of these situations result in the expansion being the value "", which is not empty it's two " characters in a row. Make doesn't do anything special with quotes: they are just like any other character. So, saying ifeq (,"") is always false. You don't want to compare a string with "" unless you want to check if the string literally contains two double-quote characters in a row.
So I think (if I understand you correctly) you want:
ifeq ($(and $(VAR1),$(VAR2)),)

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.

"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?

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