Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 3 Feb 2012 15:20:10 -0600
From:      "Conrad J. Sabatier" <conrads@cox.net>
To:        Matthew Seaman <m.seaman@infracaninophile.co.uk>
Cc:        freebsd-ports <freebsd-ports@freebsd.org>
Subject:   Re: BSD make -- Malformed conditional
Message-ID:  <20120203152010.3b4d0588@cox.net>
In-Reply-To: <4F22CB51.6070507@infracaninophile.co.uk>
References:  <4F22CB51.6070507@infracaninophile.co.uk>

next in thread | previous in thread | raw e-mail | index | archive | help
On Fri, 27 Jan 2012 16:05:37 +0000
Matthew Seaman <m.seaman@infracaninophile.co.uk> wrote:

> 
> Dear all,
> 
> Posting this mostly for the archives, but it's probably relevant to
> some people here too.
> 
> When hacking on Makefiles, should you wish to match an item in a list,
> you might write something like this:
> 
> .for item in ${LIST}
> .if ${item} == ${THING}  # Ooops!
> THING_FOUND=	1
> .endif
> .endfor
> 
> This however is a snare and a delusion, and will lead to much weeping
> and wailing, and error messages like so:
> 
> % make
> "Makefile", line 7: Malformed conditional (foo == ${THING})
> "Makefile", line 9: if-less endif
> "Makefile", line 7: Malformed conditional (bar == ${THING})
> "Makefile", line 9: if-less endif
> "Makefile", line 7: Malformed conditional (baz == ${THING})
> "Makefile", line 9: if-less endif
> "Makefile", line 7: Malformed conditional (blurfl == ${THING})
> "Makefile", line 9: if-less endif
> make: fatal errors encountered -- cannot continue
> 
> Instead you should write your loops like this:
> 
> .for item in ${LIST}
> .if ${THING} == ${item}
> THING_FOUND=    1
> .endif
> .endfor
> 
> As the make(1) manual page says on the subject of string comparisons
> using == or != :
> 
>      An expression may also be a numeric or string comparison: in
> this case, the left-hand side must be a variable expansion, whereas
> the right-hand side can be a constant or a variable expansion.
> 
> So it seems that despite appearing and behaving almost exactly like
> one, the iterator in a .for loop is not actually a variable as such.
> It also means that to match a constant string, you can't just write:
> 
> .for item in ${LIST}
> .if ${item} == "this"  # Ooops
> THIS_FOUND=1
> .endif
> .endfor
> 
> but have to assign the text "this" to a variable somewhere, and use
> the second form.
> 
> Yes, you can use ${LIST:Mthis} instead, but using this construct can
> be a bit tricky in itself...
> 
> % cat Makefile
> 
> LIST=	foo bar baz blurfl
> 
> THING=	baz
> 
> all:
> 	@echo "OK     \$${LIST:Mfoo} = ${LIST:Mfoo}"
> 	@echo "Not OK \$${LIST:M\$${THING}} = ${LIST:M${THING}}"
> % make
> OK     ${LIST:Mfoo} = foo
> Not OK ${LIST:M${THING}} = }
> 
> 	Cheers,
> 
> 	Matthew
> 

Wow, that is a pretty obscure bit of an oddity, isn't it?  How'd you
ever discover that?

Looking at the man page, it seems more than a little misleading, as it
does call the iterator in .for loops a "variable" (which for all
intents and purposes, I'd say that it is, in fact).  One could easily
lose one's marbles trying to "debug" something like this!

Looks like a bug, smells like a bug to me.  Or at least, some *very*
quirky behavior/a serious design flaw that surely wouldn't be missed,
were someone to take the initiative to change it. <hint-hint>  No
language should require you to jump through this sort of torturous,
totally anti-intuitive hoop to accomplish what you want to do.  Talk
about your POLA!  :-)

-- 
Conrad J. Sabatier
conrads@cox.net



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