help-make
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: $(eval ) and $(shell )


From: Philip Guenther
Subject: Re: $(eval ) and $(shell )
Date: Fri, 3 Jul 2009 15:15:18 -0600

On Fri, Jul 3, 2009 at 12:03 AM, CHEN Cheng<address@hidden> wrote:
> On Fri, Jul 03, 2009 at 02:55:53PM +1000, address@hidden wrote:
>> Could someone explain me the actual evaluation order of variables and
>> how $, $$, $(value ), $(eval ), $(call ) and $(shell ) operate?
>>
>> Reading the info page of $(eval ) gave me the impression that it gets its
>> argument, evaluates it once then parses the resulting text as makfile
>> source (and interprets it). Obviously that's a rather naive assumption and
>> make is doing something more sophisticated. I'd appreciate if someone
>> could tell me what or at least directed me towards some docs (other than
>> the source of make).
>
> IMHO, there are three passes in the whole make process:
>
> 1. eval pass, in which evals are expanded.
...
> 2. substitution pass, in which variables (and ifdef, etc.) are expanded.
...
> 3. real make pass, in which targets are built consequently.
...

Well, not quite.  $(eval) doesn't get a pass of its own.  Instead, it
results in make going back and redoing an earlier step on new text.

The order is more like

1) read the makefile, performing immediate expansions (per "3.9 How
`make' Reads a Makefile") as they are encountered.  This includes all
processing of conditionals (ifdef, etc) and includes.  Note that
top-level variable expansions, such as the use of $(eval) in the
original post, are performed at this point.

Now that the makefile is parsed and a DAG is available, start walking
the dependency tree, deciding what to build.  When a target needs to
be built:

2) perform all the deferred expansions for that target, including for
*all* lines in the commands
3) pass the commands to the shell

$(eval) works by redoing step (1) for that chunk of text, even if
we're still in step (1).

Note that step (3) doesn't start until step (2) is complete.  That's
why the original poster always sees the error message before the first
line of the rule output.  There is no way to interleave the processing
of commands for a single rule with make variable expansion.  If you
need to perform some shell steps, then some make expansion, then some
more shell steps, you must either
(a) put the shell steps on different targets, or
(b) perform the earlier shell steps via $(shell)


So, back to the original poster's questions:

>> Could someone explain me the actual evaluation order of variables

I think this is answered by the above.


>> and how $, $$, $(value ), $(eval ), $(call ) and $(shell ) operate?

What do you mean by 'operate'?  make operates *on* them by expanding
them: replacing them with new text and continuing the expansion with
what follows.  For most of them, that's *all* it is, but even if an
expansion has side-effects, it still has a result:
  $$ --> $
  $(call func,args...) --> the result of fully expanding the function
  $(shell command...) --> the output of the named command
  $(var) --> the result of fully expanding the variable
  $(value var) --> the current value of the variable
  $(eval) --> nothing

Some of those may have side-effects: $(shell) may invoke a command
that does something other than write to stdout.  $(eval) is used
because it has the side-effect of performing step (1) processing on
its expanded argument.


>> Reading the info page of $(eval ) gave me the impression that it gets its
>> argument, evaluates it once then parses the resulting text as makfile
>> source (and interprets it). Obviously that's a rather naive assumption and
>> make is doing something more sophisticated. I'd appreciate if someone
>> could tell me what or at least directed me towards some docs (other than
>> the source of make).

Umm, your 'naive' assumption looks correct to me.  I think your
confusion is more around the mixing of shell invocation and make
expansion.  If your example makefile was a bit more complicated, you
would see the difference between the original version and the version
in which just one of $(VAR) and $(shell) were delayed:

$ cat Makefile
VAR = $(shell non-existent-command $@)
all:    foo
define myrule
$(1): bar
       @echo "rule entered"
       @echo "shell is <" $(VAR) ">"
       @echo "rule finished"
endef
$(eval $(call myrule,foo))
bar:
        @echo doing bar
$ make
gmake: non-existent-command: Command not found
doing bar
rule entered
shell is < >
rule finished
$ cat Makefile2
VAR = $$(shell non-existent-command $@)
all:    foo
define myrule
$(1): bar
       @echo "rule entered"
       @echo "shell is <" $(VAR) ">"
       @echo "rule finished"
endef
$(eval $(call myrule,foo))
bar:
        @echo doing bar
$ make -f Makefile2
doing bar
make: non-existent-command: Command not found
rule entered
shell is < >
rule finished
$


See the difference?  In the former, the $(shell) expansion occurs
during step (1), while make is expanding $(eval)'s argument *before*
parsing it as Makefile input, so it occurs before the 'bar' target is
even known about.  In the latter, the $(shell) expansion is delayed
until make decides it needs to run the rules for the 'foo' target,
after the 'bar' target is built.

Make sense?


Philip Guenther




reply via email to

[Prev in Thread] Current Thread [Next in Thread]