== 注 ==
文章内容来自
http://lists.gnu.org/archive/html/help-make/2007-07/msg00050.html

== 问题 ==
Why does this print:
bye
hello
?

test.mk:

define test_impl
ifeq (hello,bye)
$(info bye)
else
$(info hello)
endif
endef
test = $(eval $(call test_impl))
$(call test)
all:

% make -f test.mk

This is GNU make 3.81.

== 解决 ==
Running it under remake[*] is informative:
/tmp $ remake -X -f test.mk
Reading makefiles…
calling test()
calling test_impl()
bye
hello
test_impl() returns ”
ifeq (hello,bye)
else
endif

test() returns “”
Updating makefiles….
Updating goal targets….
/tmp/test.mk:15        File `all’ does not exist.
(/tmp/test.mk:15)
all:
mdb<0> q

This is showing that the $(info ) function is being called when test()
expands test_impl().  If you change the definition of test_impl to:

define test_impl
ifeq (hello,bye)
$$(info bye)
else
$$(info hello)
endif
endef

it will do what you want.

cheers,
DaveK
[*] – Remake, the make debugger: http://bashdb.sourceforge.net/remake/

== 更好的解释 ==
Gack up a moment a think about what the argument to the $(eval) is.
GNU make has to expand $(call test_impl) to get that value, so it
expands the ‘test_impl’ variable.  As part of that expansion, it
recursively expands the variable and function references inside
test_impl’s value.  In particular, it expands both $(info) calls,
because at this point it’s just performing variable expansion and not
evaluation.  Once the expansion of $(call test_impl) is complete, it
then evaluates the result as makefile syntax, including another round
of variable expansion.

In general, when using $(eval), you need to think carefully about each
variable and function used in the makefile input and whether you want
that use to be expanded _before_ the evaluation or as _part_ of the
evaluation.  Normally, the only variables uses that you want to expand
before the evaluation are those that actually contain makefile syntax
(ifeq, define, assignments, rules, etc).  Other variables uses that
only appear as part of the above should be expanded as part of the
evaluation so that they can be affected by the make syntax rules.

So, in this case, the solution is simple: you want to expand
‘test_impl’ before the evaluation (because it contains the makefile
syntax of interest) but you don’t want any variables *inside*
‘temp_impl’ to be expanded before the evaluation.  GNU make has a
handy function for doing that: $(value).  If you change the line:

test = $(eval $(call test_impl))
to
test = $(eval $(value test_impl))

Then things will work the way you expect.

That solution is simple because it uses a simple rule: don’t expand
*any* variables or functions inside ‘temp_impl’ before evaluation.  If
you need some variables or functions inside of ‘temp_impl’ to be
expanded before evaluation, then instead of using $(value) you’ll need
to change ‘temp_impl’ to mark which should be delayed.  For example,
consider the following, where we want to expand the $(set_foo) inside
‘temp_impl’ but not the $(info) calls.  To do so, we mark the latter
by doubling the dollar-sign.

set_foo = foo = bar
define test_impl
$(set_foo)
ifeq (hello,bye)
$$(info bye)
else
$$(info hello)
endif
endef
test = $(eval $(test_impl))
$(test)
all:
@echo $(foo)

With the above, the expansion of $(test_impl) results in the text:

foo = bar
ifeq (hello,bye)
$(info bye)
else
$(info hello)
endif

Thus, the $(info) calls are delayed until the eval.

Two final points:

1) When you’re not sure what an $(eval) is doing, try replacing it with $(info),thus letting you see exactly what make is doing the evaluation of.If you don’t see a variable reference in the output that you expected, then it was expanded too soon!

2) Your original example had two uses of $(call variable), where there are no arguments to be ‘passed’.  That is almost exactly equivalent to simply saying $(variable).  If you don’t actually know what the differences between them are,then you should use the simpler $(variable) form, as *one* of the differences is that $(call) disables one of GNU make’s checks for infinite loops.  Disabling that check without a good reason is a easy way to waste your own time debugging makefiles.

(The other difference between $(call foo) and $(foo) that I can
think of is that the former clears all the argument variables ($1, $2, etc) during
the expansion of ‘foo’.)

Philip Guenther

Advertisements