help-make
[Top][All Lists]
Advanced

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

Re: Variable definition in eval'd function


From: Bryan Ischo
Subject: Re: Variable definition in eval'd function
Date: Thu, 3 Jan 2008 07:01:09 -0500 (EST)
User-agent: SquirrelMail/1.4.8-4.fc5

Philip Guenther wrote:
>
> I can only conclude that you haven't looked at very many examples of
> makefiles using $(call).   I suggest you take a look at the "GNU Make
> Standard Library" found at http://gmsl.sf.net and examine closely the
> uses of $(call) therein.  Please explain how you would define a
> recursive function like 'reverse' if there was only your version of
> $(eval) and no $(call).
>
> Please also explain how to translate the following makefile to work
> with your proposed behavior:
> -------
> pathsearch = $(firstword $(wildcard $(addsuffix /$(1),$(subst :,
,$(PATH)))))
> map = $(foreach a,$(2),$(call $(1),$(a)))
>
> all:
>         echo $(call map,pathsearch,ls echo find)
> ------
>
> (The 'pathsearch' and 'map' functions above come straight from the
> section in the GNU make info pages that describes $(call))

First, let me apologize for some of the wording I used in my email.  I can
tell from your tone that it was not appreciated; I should not have used
phrases like 'totally degenerate' and implied that GNU Make was broken.
It was not meant to be an affront to anyone; and some of it did come out
of residual frustration that I have felt when spending hours poring over
makefiles that aren't working quite the way I expected due to subtleties
of $(eval) and $(call) and escaping rules and command variables and such.
I feel that there *must* be improvements that could be made to GNU Make
that would allow my simplified syntax as I gave in my last example to
work, and I am just trying to figure out what those improvements could be.

Now as to your example above, you've thrown me a bit.  I tried the text in
a makefile and it did not do what I expected.  I *thought* that $(call)
just did text replacement of its argument variables into the template
text.  But it seems that it also executes any function calls that it finds
in the resulting text as well, and it does it recursively, until there are
no more text functions, as the following example shows:

---
call1 = X $(1)
call2 = Y $(call call1,X $(1))
call3 = Y $(call call2,X $(1))
call4 = Y $(call call3,X $(1))
call5 = Y $(call call4,X $(1))

bar:
        @echo $(call call5,foo)
---

'make bar' results in:

Y Y Y Y X X X X X foo

So I can see from this that all of the $(call) functions are being
evaluated 'recursively'.  What I had *expected* 'make bar' to result in
was this:

Y $(call call4,X foo)

In other words I thought that make would only do the first text
substitution and then stop, considering the result to just be text that
didn't need further processing.  I thought that $(eval) would be necessary
to get make to further process the resulting text.  And so @echo would
just be echoing the result of the first text substitution via the
top-level $(call), the text which resulted from that, although containing
a $(call) function, would not be processed further by make and would just
be treated like text.

I think the key part of the make manual describing $(call) that I
misunderstood is this:

---
     $(call VARIABLE,PARAM,PARAM,...)

   When `make' expands this function, it assigns each PARAM to
temporary variables `$(1)', `$(2)', etc.  The variable `$(0)' will
contain VARIABLE.  There is no maximum number of parameter arguments.
There is no minimum, either, but it doesn't make sense to use `call'
with no parameters.

   Then VARIABLE is expanded as a `make' variable in the context of
these temporary assignments.  Thus, any reference to `$(1)' in the
value of VARIABLE will resolve to the first PARAM in the invocation of
`call'.
---

It is this last paragraph that I think I misunderstood.  I guess the
'expansion' of a make variable includes evaluating any function
invocations contained therein.  I didn't realize that the text resulting
from the $(call) is expanded in this way.

So my explanation for the double-$(eval) in my original email is I think
completely innacurate (hey, I said I often get confused by this stuff!).
Please ignore the explanation I gave in my previous email about why both
$(eval) calls were necessary - it is wrong!

Now I need to understand the reason why two $(eval) calls were needed
myself.  So let me re-phrase the original problem:

1. I want to write a template which uses -include to include a file whose
name is given by a template argument
2. I want the -included file to define variables
3. I want to use the values of those variables in rules that the template
subsequently defines
4. I want the variables to be used in the rule commands, not just the rule
target or dependencies

If it weren't for (4), this would be easy; I would do something like:

---
FRAGMENTS = foo bar

define TEST_RULE

.PHONY: $(1)
$(1): $$(VARIABLE)
        @echo $$^

$$(VARIABLE):
        \touch $$@

endef

define PROCESS_FRAGMENT

-include $(1).mk

$(call TEST_RULE,$(1))

endef

$(foreach i, $(FRAGMENTS), $(eval $(call PROCESS_FRAGMENT,$(i))))
---

Works as expected; $$(VARIABLE) in TEST_RULE is substituted with the value
of VARIABLE as set by the file -included by the PROCESS_FRAGMENT template,
and gets the correct value for each rule; i.e.

bji$ make foo
foo_variable
bji$ make bar
bar_variable

However, once I try to satisfy (4) I have problems.  Consider the
following change to my makefile:

---
FRAGMENTS = foo bar

define TEST_RULE

.PHONY: $(1)
$(1):
        @echo $$(VARIABLE)

endef

define PROCESS_FRAGMENT

-include $(1).mk

$(call TEST_RULE,$(1))

endef

$(foreach i, $(FRAGMENTS), $(eval $(call PROCESS_FRAGMENT,$(i))))
---

Whoops!  It doesn't work like I want to anymore:

bji$ make foo
bar_variable
bji$ make bar
bar_variable

The problem is I can't reference $$(VARIABLE) in the rule command -
because make treats variables in commands differently than variables
everywhere else.  It doesn't expand them immediately when the makefile is
parsed; it only evaluates them when the command is actually run.  By that
time, VARIABLE will already have been set to its final value of
bar_variable.  That is why even the foo target prints out bar_variable.

So the only solution I could was to defer the evaluation of the template,
by replacing:

define PROCESS_FRAGMENT

-include $(1).mk

$(call TEST_RULE,$(1))

endef

with:

define PROCESS_FRAGMENT

-include $(1).mk

$$(call TEST_RULE,$(1))

endef

but in order to make that work, I had to wrap it with a deferred eval as in:

define PROCESS_FRAGMENT

-include $(1).mk

$$(eval $$(call TEST_RULE,$(1)))

endef

That worked.  That's why there are two evals.

It's really late now and my brain is too tired to try to figure out why my
solution worked.  But it's not a solution I like, and I wish there was a
better way in GNU Make.  Maybe there is and someone call tell me.

Please note that the GNU Make manual does not describe the special
behavior of variables in commands, at least as far as I can find.  The
most appropriate section would seem to be "Basics of Variable References",
which reads in part:

"Variable references can be used in any context: targets, prerequisites,
commands, most directives, and new variable values."

But it doesn't say anything about how variable references in commands work
differently than everywhere else.  If someone could point me to the part
of the GNU Make manual that describes this behavior, I would really
appreciate it!

>> 3. Change the way that variables are handled in rules; I'm still pretty
>> confused on this but I do know that the way that GNU Make (it inherited
>> this behavior from the original make) handles variables in rule commands
>> is counterintuitive; they don't end up getting replaced until the very
>> last step, when the rule is actually applied, which means that this
>> doesn't work like you'd think it would:
>>
>> VARIABLE=foo_variable
>> .PHONY: foo
>> foo:
>>      @echo $(VARIABLE)
>> VARIABLE=bar_variable
>> .PHONY: bar
>> bar:
>>      @echo $(VARIABLE)
>
> If you want target-specific variables, then use target-specific variables!
>
> foo: VARIABLE = foo_variable
> bar: VARIABLE = bar_variable
> .PHONY: foo bar
> foo bar:
>         @echo $(VARIABLE)
>
> What, did you not read about those in the info pages?

Yes, I did.  But they don't solve my problem as far as I can tell.  If you
can use target-specific variables to somehow allow you to cause a variable
referenced in a rule command to be evaluated when the rule is defined,
instead of when the command is executed, please show me how.

> So, without making a complete analysis of its effects, you want to
> make a major change to the semantics of makefiles.  Furthermore,
> you're willing to label any makefile whose behavior is changed by this
> as "badly-written".  Nice.

I'm just putting it up for discussion.  I'll once again apologize for some
of the wording that I used in my email that made it sound like GNU Make
was obviously broken, when it was just to some degree my
misunderstandings.  Now let's move forward in this discussion with
mutually respectful tones, if you don't mind :)

> As for having a switch to provide both semantics:
> 1) cool, so for each makefile I have to chase down whether it needs this
switch?

No; if you already expect the semantics that make is using for variables
in commands, then you wouldn't even need to know or care about the switch.
 If you wanted the new semantics, you'd need to know about the switch.
But you'd already be reading the documentation to find out about the new
semantics that are available, so knowing about the switch would just come
along as part of that.

> 2) I don't think you have a grasp on the level of complexity your
suggestion
>     would add to the GNU Make source.

I don't know anything about the GNU Make source.  So no, I do not.  But I
was not considering the effort that would be involved giving GNU Make the
capacity to obey these semantics yet; I just wanted to come to an
understanding of the benefit of the feature and how it would work.

>> I think that would be much more logical, and that correspondingly more
>> complex GNU Make files would benefit even more (remember that my example
>> here was a very simple one!  complex GNU Makefiles using $(eval) and
>> $(call) extensively can get very hairy indeed!).
>
> And many of them would be much *more* complex with your change than
> they are now.  But you haven't completed the analysis which would show
> you that.

I just think it's weird that the following:

VARIABLE = foo

$(VARIABLE):
        @echo $(VARIABLE)

VARIABLE = bar

$(VARIABLE):
        @echo $(VARIABLE)

produces this:

bji$ make foo
bar
bji$ make bar
bar

I think that anyone who doesn't know the very special rule of how
variables are treated differently in rule commands than they are anywhere
else, would also be surprised.  It seems inconsistent.  And it gets
*really* confusing when you throw $(call) and $(eval) into the mix.

And it seems to me that if someone writes a makefile like the above, then
it would be more consistent for the results to be:

bji$ make foo
foo
bji$ make bar
bar

Then if they really wanted the behavior that GNU Make gives for the first
case, then they would write their makefile in a more sensible way:

VARIABLE = bar

TARGET = foo

$(TARGET):
        @echo $(VARIABLE)

TARGET = bar

$(TARGET):
        @echo $(VARIABLE)

Now nobody would be surprised when this resulted in:

bji$ make foo
bar
bji$ make bar
bar

Also I do fail to see how the behavior I suggest would make some makefiles
more complex; but it's too late in the evening right now for me to do the
analysis you suggest; I will do it tomorrow and hopefully learn something.

> Now, you're perfectly free to hack your copy of the GNU make sources
> to give it the behavior you desire.  I have no problems with that at
> all.  What seems unwise to me is to then suggest that such a program
> should call itself 'make'.  It does not behave the way described in
> all the existing books on 'make', it does not follow the published
> standards for how 'make' should behave on UNIX systems, and it will
> mishandle many existing makefiles.  Introducing a gratuitous
> incompatibility into a system utility just because you find the
> current behavior illogical seems like a *really* bad idea.

These points are all premature.  We're just talking about ideas here.
Nobody is trying to force anyone to change GNU Make yet.  To be frank, I
would never take it past the discussion phase myself because I really have
no desire to even try to force the GNU Make maintainers to do anything.  I
just want to talk about it and see if people agree with what I am saying,
and if not, understand why not.

Thanks,
Bryan







reply via email to

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