Subject: bin/30967: obscure corner-case bug in conditional handling in make
To: None <gnats-admin@netbsd.org, netbsd-bugs@netbsd.org>
From: None <dholland@eecs.harvard.edu>
List: netbsd-bugs
Date: 08/10/2005 23:22:00
>Number:         30967
>Category:       bin
>Synopsis:       obscure corner-case bug in conditional handling in make
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    bin-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Wed Aug 10 23:22:00 +0000 2005
>Originator:     David A. Holland / dholland@eecs.harvard.edu
>Release:        NetBSD 3.99.7 (-20050718)
>Organization:
    Harvard EECS
>Environment:
System: NetBSD tanaqui 3.99.7 NetBSD 3.99.7 (TANAQUI) #1: Mon Jul 18 21:43:19 EDT 2005 dholland@tanaqui:/usr/src/sys/arch/i386/compile/TANAQUI i386
Architecture: i386
Machine: i386
>Description:

Conditionals of the form ".if A==A",  appear to be always false. This is
less than totally desirable behavior on the face of it, but also seems
to reflect a bug.

".if A!=A" is always false too.

The behavior when an unrecognized word appears in a conditional
expression is to run a default function on it. In the case of .if this
function is "defined". (*)

However, whether or not A is defined, applying defined() to both As
should still give an expression that's true -- either 0==0 or 1==1.
And any ordinary (even if wrong/unexpected) evaluation path should
still give opposite results for "A==A" and "A!=A", which isn't
happening.

It's not immediately clear to me what's going on, but something's
definitely wrong, probably in the parsing logic.


(*) This is not documented in the man page, either, which only
discusses this behavior in connection with .if(n)def and
.if(n)make. Patch below.

Alternatively, one could make unrecognized words in .if expressions
errors.

It's also not clear that sharing expression syntax between .if and
.ifdef is a good idea in the first place. I don't think it really
makes sense to allow
	.ifdef "$(A)"=="$(B)"
and have it behave the same as .if. But this is a different issue.

>How-To-Repeat:

Try the following makefile fragment. (Removing the assignment of A
doesn't change the behavior.)

A=6
foo:
.if A==A
	echo YES
.else
	echo NO
.endif

>Fix:

Fix for the man page (also includes a typo fix):

Index: make.1
===================================================================
RCS file: /cvsroot/src/usr.bin/make/make.1,v
retrieving revision 1.118
diff -u -r1.118 make.1
--- make.1	27 Jun 2005 09:51:09 -0000	1.118
+++ make.1	10 Aug 2005 22:38:49 -0000
@@ -1278,13 +1278,14 @@
 .Pp
 When
 .Nm
-is evaluating one of these conditional expression, and it encounters
+is evaluating one of these conditional expressions, and it encounters
 a word it doesn't recognize, either the ``make'' or ``defined''
 expression is applied to it, depending on the form of the conditional.
 If the form is
 .Ql Ic .ifdef
-or
 .Ql Ic .ifndef ,
+or
+.Ql Ic .if ,
 the ``defined'' expression
 is applied.
 Similarly, if the form is