I have a function in a Makefile where I am trying to evaluate input parameters in ifeq statements like below:
define set_target_dts
ifeq ($(1),virgo)
@echo "Building for Virgo"
ifeq ($(2),soc_prototype)
@echo "Building for soc prototype"
DTS_FILE_NAME := virgo_soc_proto.dts
else
@echo "Building for soc"
DTS_FILE_NAME := virgo.dts
endif
else ifeq ($(1),gemini)
@echo "Building for Gemini"
ifeq ($(2),soc_prototype)
DTS_FILE_NAME := gemini_soc_proto.dts
else
DTS_FILE_NAME := gemini.dts
endif
else
$(info Selected Target is:$(1) $(2))
$(error Please select the correct target...)
endif
endef
Whenever I call this function using
$(call set_target_dts,virgo,soc_prototype)
It is throwing me this error:
Selected Target is:virgo soc
Makefile:48: *** Please select the correct target.... Stop.
Which means that neither the ifeq is evaluated correctly, nor the second parameter value is correctly retreived.
Please guide me about my mistakes. I have never made Makefile functions before.
At the outset I'll set aside your secondary problem:
nor the second parameter value is correctly retreived.
This is not reproduced for me when I run a make calling
$(call set_target_dts,virgo,soc_prototype)
with the definition of set_target_dts
that you have posted. I get:
Selected Target is:virgo soc_prototype
Makefile:24: *** Please select the correct target.... Stop.
as it should be, not:
Selected Target is:virgo soc
Makefile:48: *** Please select the correct target.... Stop.
as you did. I think something not very make-related is amiss there.
And so onward
You are misapprehending how define
-ed variables are expanded and how
function calls are expanded. A "user defined function"
in GNU Make is conventionally a variable with a multi-line definition enclosed by define varname
and endef
, with deferred expansion of the enclosed body so that arguments can be substituted for $(n)
parameters in that body when the the function-call construction $(func,args,...)
is
expanded1. make
does not recognise any "logic" in the body of a variable definition while expanding it. (Although make
might recognize such logic later if make
itself uses the expansion).
First you should carefully read the GNU Make manual: 3.7 How make Reads a Makefile.
Having grasped the distinction between immediate and deferred expansion,
note that the schema that fits your definition of set_target_dts
is:
define *immediate*
*deferred*
endef
The expansion of the definition body is deferred until the variable is used - e.g. by
function-call expansion $(call foo,args...)
. That is the right choice. You will see from the documentation
that you could in theory adapt your definition to, say, the schema:
define *immediate* :=
*immediate*
endef
But if you did that then the definition would not be viable for
functions calls, because the definition would be frozen with all $(n)
parameters empty. Deferred expansion is necessary to give you a chance to
supply args...
.
With that in mind a worked example will best clarify what you've
observed, and what's misconceived about the definition of set_target_dts
.
Consider this makefile:
$ cat Makefile
define foo
ifeq (1,0)
$(info In condition 1 = 0)
$(error In condition 1 = 0)
else
$(info In condition 1 ~= 0)
$(info $$(1) = [$(1)])
echo "Parameter $$(1) = [$(1)]"
endif
endef
$(info $$(foo,bar) = <<<$(call foo,bar)>>>)
all:
Let's just run it:
$ make
In condition 1 = 0
Makefile:12: *** In condition 1 = 0. Stop.
Observe: the fact that the $(error ...)
call was within the scope of the unsatisfiable
condition ifeq (1,0)
didn't stop the the $(error ...)
call from being
fatally executed.
Let's remove the error call so we can get further. I'll also label the lines
within the definition of foo
so I can talk through them:
$ cat Makefile
define foo
ifeq (1,0) # A
$(info In condition 1 = 0) # B
else # C
$(info In condition 1 ~= 0) # D
$(info $$(1) = [$(1)]) # E
echo "Parameter $$(1) = [$(1)]" # F
endif # G
endef
$(info $$(foo,bar) = <<<$(call foo,bar)>>>)
all:
We know that all the labelled lines fall into deferred expansion. In our Makefile, that will happen in:
<<<$(call foo,bar)>>>
(It will not happen in $$(foo,bar)
, because that expression is escaped.)
In performing the expansion of the definition, make
works like so:
$(n)
is replaced with the n
th argument, if any, otherwise the empty string.$(name[...])
construct is expanded.$$...
is expanded to $...
$
goes through unchanged.So here is what will happen, line by line, as make
expands $(call foo,bar)
:
$
s here. Add it unchanged to the expansion.$(info ...)
call now. That action prints ...
to the standard output now
and returns the empty string. Add that empty string to the expansion.$$(1)
is replaced with literal $(1)
. The un-escaped $(1)
in replaced with bar
. The resulting line is added to the expansion.Thus the final expansion of $(call foo,bar)
is:
ifeq (1,0) # A
# B
else # C
# D
# E
echo "Parameter $(1) = [bar]" # F
endif # G
Let's confirm that by running the amended Makefile:
$ make
In condition 1 = 0
In condition 1 ~= 0
$(1) = [bar]
$(foo,bar) = <<< ifeq (1,0) # A
# B
else # C
# D
# E
echo "Parameter $(1) = [bar]" # F
endif # G>>>
make: Nothing to be done for 'all'.
Note that the expansion of:
$(info In condition 1 = 0) # B => prints "In condition 1 = 0"
was uninhibited by apparently being in the scope of the unsatisfiable
condition ifeq (1,0)
and that in the expanded definition it
does not even appear to be that scope: the only text in that scope
is <empty_string># B
.
By analogy you should now see why the $(error ...)
call that
we removed was executed despite lying "within the scope" of ifeq (1,0)
.
In the context of expanding the definition of foo
, that condition is
merely some text that requires no expansion itself and is just
added to the expansion of the function call. The $(error ...)
call is some
more text that does require expansion, and doing that immediately terminates
make
with failure.
And in that light you can see that in your own case,
$(call set_target_dts,virgo,soc_prototype)
will execute:
$(info Selected Target is:$(1) $(2))
and also:
$(error Please select the correct target...)
regardless of any surrounding conditional "make-logic", because in the
context of expanding the function call make
does not care what the
surrounding text is: it isn't doing any make-logic here.
But...
If you intend to call your function in the context of a recipe,
then it certainly does matter what the text of the expansion is,
since recipes are executed by the shell. And
since your definition of set_target_dts
contains shell commands (echo
) you evidently intend to use it that way. Our makefiles so far
have also contained an echo
command to hint at the same intention.
Let's make a final revision of our Makefile to check that out:
$ cat Makefile
define foo
ifeq (1,0) # A
$(info In condition 1 = 0) # B
else # C
$(info In condition 1 ~= 0) # D
$(info $$(1) = [$(1)]) # E
echo "Parameter $$(1) = [$(1)]" # F
endif # G
endef
$(info $$(foo,bar) = <<<$(call foo,bar)>>>)
all:
@echo "Building all"
$(call foo,bar)
$ make
In condition 1 = 0
In condition 1 ~= 0
$(1) = [bar]
$(foo,bar) = <<< ifeq (1,0) # A
# B
else # C
# D
# E
echo "Parameter $(1) = [bar]" # F
endif # G>>>
In condition 1 = 0
In condition 1 ~= 0
$(1) = [bar]
Building all
ifeq (1,0) # A
/bin/sh: 1: Syntax error: word unexpected (expecting ")")
make: *** [Makefile:15: all] Error 2
See here that the $(call foo,bar)
within the recipe for target all
, just like the the previous previous one in <<<$(call foo,bar)>>>
,
is expanded before execution of the recipe (before "Building all"). It
would have to be, wouldn't it?
Then the expansion is given to the shell to execute and of course,
the shell chokes on line A: ifeq (1,0)
does not make sense to the shell.
If the definition of set_target_dts
is meant to be such that,
after functional expansion with arguments, a shell command or expression will result
then you must write that definition in such a way that, after expansion, you get nothing
but a shell command or expression. Make-logic will not "do its job and vanish" in the
context of a variable definition expansion destined for recipe execution. It will
not do its job, and it will not vanish.
1 @MadScientist correctly commented: "Just to note, a user-defined function in make doesn't have to be multiline or created with define. Any macro can be used with $(call ...). Generally any macro that refers to at least one variable (e.g., $1, $2) is a user-defined function. You could even have zero arguments but it only makes sense to use n-ary macros where n > 0: $(call foo) is legal but is the same thing as just $(foo)."