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>