[Top][All Lists]

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

Re: 6.2 The Two Flavors of Variables

From: grischka
Subject: Re: 6.2 The Two Flavors of Variables
Date: Fri, 25 Feb 2011 01:19:11 +0100
User-agent: Thunderbird (Windows/20090812)

Paul Smith wrote:
Now make wants to expand $(CFLAGS) and it sees that the value of that
variable is '$(CFLAGS) -O'.  So it then tries to expand that, and sees
that $(CFLAGS) expands to '$(CFLAGS) -O'.  And so on until you run out
of memory.
I'd still argue that this is nothing more than a pretty accurate
description of an implementation bug.

You may consider it so, but along with the use of TABs to prefix recipe
lines you must blame the inventors of the original make, back in the
1970's, and also of course the writers of the POSIX spec.  This is the
one and only type of variable that is defined and supported in those
makes and so it must be defined and supported this way in GNU make.

POSIX doesn't talk about "recursive variables".  POSIX just requires
lazy evaluation of what it calls "macros".

Even less does POSIX require or even suggest a second flavor of non-
POSIX-compliant variables to work around a flaw with the normal

Which is also the only way to explain why gnu-make invented the term
"recursive variable" to denote a variable that must not be used

No.  They're termed "recursive variables" because they are expanded
recursively; that is, the value is expanded and the expansion of the
value is expanded and the value of THAT is expanded, etc. recursively
until a point where there is nothing left to expand.

The behavior in question is exactly a result of this definition: there
can never be "nothing left to expand" when the value includes a
reference to the variable itself.

Compare to simple variables (:=) where the value of the variable is
substituted directly, without expansion, when it's referenced.

Lazy macro evaluation exists elsewhere too (for example in C preprocessors).
Still I've never seen the term "recursive variables" in any context
other than with gnu-make and then always to explain why it doesn't work
as people expect.

For example, there is no difference semantically whether you write
     CFLAGS = $(CFLAGS) -O
     CFLAGS += -O

Now since gnu-make does support the latter there cannot be a quasi
natural logical reason why it can't support the former, because +=
also implicitly references an value of the variable which if it was
expanded like you describe above would cause the same memory overflow.

No it doesn't.  The difference is very simple to see if you think about
the assignment correctly.  A recursive assignment doesn't expand the
value at all on assignment.  So the value of the variable CFLAGS is the
literal string '$(CFLAGS) -O'.

The += operator for recursive variables is nothing more than string
concatenation: all make does is add more on to the end of the literal
string: you get '$(CFLAGS) -O -O' as the value.  The value is NOT
expanded there.  No recursion, no harm, no foul.

I have no doubt that foul vs. no foul is making the difference.  It's
just the question by whom.

Look at it like this:  With '+=' gnu-make correctly stores the old value
(implicitly as part of the concatenated string) whereas with the general
'=' assignment it simply forgets the old value.  Which is then the reason
why it has problems with recursion during late expansion.

In order for your theoretical feature to work, make would have to keep
some kind of stack of all past values of CFLAGS, so that it could be
expanded backward to the beginning of time.

Make does already keep track of lots of things.

See also:
which basically discusses a -= operator as in
     CFLAGS -= -O

Yes, and see my response which gives more examples of the complexities
behind this apparently simple suggestion :-).

The algorithm IS simple.  Just slightly less simple than what it already
has ;)

The example in your response missed a very important case: what if the
SAME variable is used MULTIPLE times in the same expansion, possible at
different levels?

You mean like
    B = + $(A)
    A = 1
    A = $(A) $(B)

Here "$(A)" would expand to "1 + 1".

Does each instance of the variable start at the "top" of the expansion, so that each instance requires its own pointer into the stack of potential

No.  You don't run expansion on instances of variables but you enter
an instance of expansion for a certain variable.  Think of the function
that takes a variable name and returns the expanded string:  It only
ever deals with a single variable at the same time.

This function increments the "expansion-level" of the variable on
entrance and decrements it before it returns.  The end condition
for recursion is represented by the maximum level which is equal to
the number of assignments made to this variable in the makefile.

Or do they start at the current value so you could keep one "pointer"?

Yes, you could use a linked list with values, assigned to a pointer
in the variable.  Basically v->values instead of v->value.

I would think the latter would be completely unworkable, from a usability standpoint, but unless I'm mistaken the former doesn't
solve the problem (you can still easily get recursion).

Me thinks you would just miss answering questions about "recursive
variables" and how they make the difference. ;)

--- grischka

reply via email to

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