Makefile .for and .if expansion
Ruslan Ermilov
ru at freebsd.org
Sun Feb 13 03:10:34 PST 2005
Hi Kris,
On Sat, Feb 12, 2005 at 06:32:01PM -0800, Kris Kennaway wrote:
> The following small makefile doesn't behave as one would naively
> expect:
>
> MANLANG?=foo ""
> all:
> .for i in ${MANLANG}
> .if empty(${i})
> @echo foo ${i}
> .endif
> .endfor
>
> ports-i386%make
> foo foo
> foo
>
> I think this is because the .if evaluation is happening too early, and
> it's not being done after the .for loop is expanded and the i variable
> is set.
>
This makefile is broken, you're abusing empty(). empty() expects
a variable name (without `$') as an argument, and ``.if empty(foo)''
means "true if ${foo} has an empty value". Note that in 4.x, "foo"
also needs to be a defined variable, for this to work at all. In
5.x and 6.x, undefined variables are treated like empty variable
by empty().
> In order to get this to work I seem to have to do the following:
>
> MANLANG?=foo ""
> .for i in ${MANLANG}
> j= ${i}
> .if (${j} != "\"\"")
> .for l in ${j}
> k+= ${l}
> .endfor
> .endif
> .endfor
> all:
> @echo ${k}
>
> ports-i386%make
> foo
>
> If I remove the inner .for it breaks, and if I remove the j assignment
> it breaks. Also if I try and remove the use of k and put an echo
> inside the inner .for (with the all: preceding the whole loop) it
> breaks.
>
> This is extremely nasty.
>
Yes. This behavior is documented in the BUGS section of the make(1)
manpage: .for loops are unrolled before tests, and .for variables
aren't real variables, so a fragment like this:
.for i in foo bar
.if ${i} == "foo"
echo ${i}
.endif
.endfor
doesn't work. This fragment is rewritten by make(1) before further
parsing as follows:
.if foo == "foo"
echo foo
.endif
.if bar == "foo"
echo bar
.endif
And since .if expects a ${variable} as its first argument, it fails.
About why you need an inner loop. Remember again that .for loops
are unrolled before parsing, it means that a fragment like this:
.for i in foo bar
j=${i}
k+=${j}
.endfor
is equivalent to
j=foo
k+=${j}
j=bar
k+=${j}
which means that `k' will get a value of "bar bar". When you use
an inner loop,
.for i in foo bar
j=${i}
.for l in ${j}
k+=${l}
.endfor
.endfor
it first gets rewritten to:
j=foo
.for l in ${j}
k+=${l}
.endfor
j=bar
.for l in ${j}
k+=${l}
.endfor
then to:
j=foo
k+=foo
j=bar
k+=bar
which DTRT, but also has a side effect of setting "j" to "bar".
> Am I missing an easier way to do this?
>
May I suggest the following instead:
%%%
MANLANG?= foo "" bar
all:
.for i in ${MANLANG:N""}
@echo foo ${i}
.endfor
%%%
Note that `""' is not an empty value in make(1), it's just a
regular value consisting of two double quotes.
Cheers,
--
Ruslan Ermilov
ru at FreeBSD.org
FreeBSD committer
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 187 bytes
Desc: not available
Url : http://lists.freebsd.org/pipermail/freebsd-hackers/attachments/20050213/6ac92735/attachment.bin
More information about the freebsd-hackers
mailing list