Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 13 Feb 2005 13:10:33 +0200
From:      Ruslan Ermilov <ru@freebsd.org>
To:        Kris Kennaway <kris@obsecurity.org>
Cc:        hackers@freebsd.org
Subject:   Re: Makefile .for and .if expansion
Message-ID:  <20050213111033.GB88954@ip.net.ua>
In-Reply-To: <20050213023201.GB24426@xor.obsecurity.org>
References:  <20050213023201.GB24426@xor.obsecurity.org>

next in thread | previous in thread | raw e-mail | index | archive | help

--3uo+9/B/ebqu+fSQ
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline
Content-Transfer-Encoding: quoted-printable

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:
>=20
> MANLANG?=3Dfoo ""
> all:
> .for i in ${MANLANG}
> .if empty(${i})
>         @echo foo ${i}
> .endif
> .endfor
>=20
> ports-i386%make
> foo foo
> foo
>=20
> 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.
>=20
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:
>=20
> MANLANG?=3Dfoo ""
> .for i in ${MANLANG}
> j=3D      ${i}
> .if (${j} !=3D "\"\"")
> .for l in ${j}
> k+=3D     ${l}
> .endfor
> .endif
> .endfor
> all:
>         @echo ${k}
>=20
> ports-i386%make
> foo
>=20
> 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.
>=20
> This is extremely nasty.
>=20
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:

=2Efor i in foo bar
=2Eif ${i} =3D=3D "foo"
	echo ${i}
=2Eendif
=2Eendfor

doesn't work.  This fragment is rewritten by make(1) before further
parsing as follows:

=2Eif foo =3D=3D "foo"
	echo foo
=2Eendif
=2Eif bar =3D=3D "foo"
	echo bar
=2Eendif

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:

=2Efor i in foo bar
j=3D${i}
k+=3D${j}
=2Eendfor

is equivalent to

j=3Dfoo
k+=3D${j}
j=3Dbar
k+=3D${j}

which means that `k' will get a value of "bar bar".  When you use
an inner loop,

=2Efor i in foo bar
j=3D${i}
=2Efor l in ${j}
k+=3D${l}
=2Eendfor
=2Eendfor

it first gets rewritten to:

j=3Dfoo
=2Efor l in ${j}
k+=3D${l}
=2Eendfor
j=3Dbar
=2Efor l in ${j}
k+=3D${l}
=2Eendfor

then to:

j=3Dfoo
k+=3Dfoo
j=3Dbar
k+=3Dbar

which DTRT, but also has a side effect of setting "j" to "bar".

> Am I missing an easier way to do this?
>=20
May I suggest the following instead:

%%%
MANLANG?=3D	foo "" bar
all:
=2Efor i in ${MANLANG:N""}
	@echo foo ${i}
=2Eendfor
%%%

Note that `""' is not an empty value in make(1), it's just a
regular value consisting of two double quotes.


Cheers,
--=20
Ruslan Ermilov
ru@FreeBSD.org
FreeBSD committer

--3uo+9/B/ebqu+fSQ
Content-Type: application/pgp-signature
Content-Disposition: inline

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.6 (FreeBSD)

iD8DBQFCDzWpqRfpzJluFF4RAoPjAKCNqVWvNF9dMDhdtHzN6RsIr/xBawCfR/h9
D2aBJUMMRYwLTtWxTN1QPLk=
=wLUw
-----END PGP SIGNATURE-----

--3uo+9/B/ebqu+fSQ--



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20050213111033.GB88954>