Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Testing the eval function of GNU make

Tags:

makefile

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?

like image 441
shijun zhao Avatar asked Jul 25 '17 06:07

shijun zhao


People also ask

What does eval do in makefile?

The eval function is very special: it allows you to define new makefile constructs that are not constant; which are the result of evaluating other variables and functions. The argument to the eval function is expanded, then the results of that expansion are parsed as makefile syntax.

How do I use IFEQ in makefile?

The ifeq directive begins the conditional, and specifies the condition. It contains two arguments, separated by a comma and surrounded by parentheses. Variable substitution is performed on both arguments and then they are compared.

How do you call a function in makefile?

The call function is unique in that it can be used to create new parameterized functions. You can write a complex expression as the value of a variable, then use call to expand it with different values. The syntax of the call function is: $(call variable , param , param ,…)

What is $$ in makefile?

$$ means be interpreted as a $ by the shell. the $(UNZIP_PATH) gets expanded by make before being interpreted by the shell.


2 Answers

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.

like image 195
Renaud Pacalet Avatar answered Sep 30 '22 02:09

Renaud Pacalet


$(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.

like image 22
Florian Weimer Avatar answered Sep 30 '22 03:09

Florian Weimer