BSD make -- Malformed conditional

Chris Rees crees at freebsd.org
Fri Feb 3 21:29:46 UTC 2012


On 3 February 2012 21:20, Conrad J. Sabatier <conrads at cox.net> wrote:
> On Fri, 27 Jan 2012 16:05:37 +0000
> Matthew Seaman <m.seaman at 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!  :-)

Judging by the sheer volume of Makefiles out there that quite possibly
rely on this 'bug' for some things, it's not worth fixing ;)

Just think of it as yet another quirk of make.

Chris


More information about the freebsd-ports mailing list