5
votes

The GNU make manual says that the eval function expands the arguments and then feeds the results of the expansion to make parser. The following is quoted from GNU make manual.

The argument to the eval function is expanded, then the results of that expansion are parsed as makefile syntax. 

I don't quite understand how the make parser process the text fed by eval, so I write following makefile to test.

define myprint
    echo "this is a line"
endef

goal:
    $(eval $(call myprint))
    gcc -o goal test.c

I know that the correct invocation of myprint should be only use the call function: $(call myprint) and delete the 'Tab' character before echo. I write the makefile in this form just to test the eval function.

My expectation: first the eval function expands myprint, which is an echo command preceded by a 'Tab', and the 'Tab' is used to make the expanded text to be a legal recipe. Then the eval feeds the expanded text to maker parser, who will identify the text to be a recipe, and run it. As the command is legal, the makefile should run properly.

However, I meet such an error:

Makefile:6: *** recipe commences before first target.  Stop.

Could somebody explain why make produce such an error?

3

3 Answers

9
votes

the results of that expansion are parsed as makefile syntax

Your use of eval is different: you would like it to be parsed as shell syntax. You can write:

define myprint
echo "this is a line"
endef

goal:
    $(myprint)
    gcc -o goal test.c

or:

define myprint
echo "this is a $(1)"
endef

goal:
    $(call myprint,line)
    gcc -o goal test.c

Because after make expansion the recipes are valid shell syntax. But not what you wrote because the expansion of eval is still interpreted as make syntax, not shell. To illustrate a typical use of eval and call, consider this:

define myprint
echo "this is a $(1)"
endef

define mygoal
$(1):
    $$(call myprint,line)
    gcc -o $(1) $(2).c
endef
$(eval $(call mygoal,goal,test))

It is a bit more tricky than two first examples (without eval) but it illustrates the real purpose of eval: programmatically instantiate make constructs. Here is how it works, step by step:

During the first phase of its 2-phases algorithm, make expands the $(eval... function call, that is:

  1. Expand the parameter of the $(eval... function (the $(call... function):
    1. Expand the parameters of the $(call... function (goal and test). No effect in our case.
    2. Assign the result to the temporary variables $(1) and $(2).
    3. Expand the mygoal variable in this context, which replaces $(1), $(2) and $$(call... by goal, test and $(call..., respectively.
  2. Instantiates (in memory) the result as a make construct, a complete rule in this case:

    goal:
        $(call myprint,line)
        gcc -o goal test.c
    

The first phase continues but it has no effect on this instantiated rule because the recipes are expanded by make during the second phase.

During the second phase, when the time comes to build the goal target, make expands the recipe before executing it, that is:

  1. Expand the $(call myprint... parameter (line, no effect).
  2. Assign the result to temporary parameter $(1).
  3. Expand variable myprint in this context, which produces:

    echo "this is a line"
    

All this is thus the same as if we had written the rule:

goal:
    echo "this is a line"
    gcc -o goal test.c

Note the double $$ in the initial definition of mygoal:

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.

4
votes

$(eval …) needs a syntactically complete makefile fragment. It cannot be used to paste tokens into other makefile constructs. Perhaps the manual does not explain this clearly enough, but it's implemented by reading its argument as if it were an included makefile.

0
votes

@RenaudPacalet, I write following makefile to test whether the expansion of call 'eats' one dollar.

define myprint
echo "In my print $$(ls)"
endef

goal:
    $(call myprint)
    $(info $(call myprint))
    gcc -o goal test.c

Its output is:

echo "In my print $(ls)"
echo "In my print $(ls)"
In my print call.mk ... (files list)

As $(call myprint) outputs "In my print $(ls)" correctly, it must be expanded to echo "In my print $$(ls)" first, then it will be expanded to the correct shell command echo "In my print $(ls)". So I think the call function does not 'eats' one dollar.

Another evidence is the output of info function. The GNU make manual says:

$(info text…)
This function does nothing more than print its (expanded) argument(s) to standard output. 

From the manual we can infer that make will expand the arguments of the info function. As the output of the info is echo "In my print $(ls)", so the arguments before expansion should be echo "In my print $$(ls)". So we can conclude that the call function doesn't 'eat' one dollar.