xref: /freebsd/contrib/bmake/unit-tests/directive-for-if.mk (revision 5956d97f4b3204318ceb6aa9c77bd0bc6ea87a41)
1# $NetBSD: directive-for-if.mk,v 1.1 2021/08/30 17:08:13 rillig Exp $
2#
3# Test for a .for directive that contains an .if directive.
4#
5# Before for.c 1.39 from 2008-12-21, when expanding the variables of a .for
6# loop, their values were placed verbatim in the expanded body.  Since then,
7# each variable value expands to an expression of the form ${:Uvalue}.
8#
9# Before that change, the following adventurous code was possible:
10#
11#	.for directive in if ifdef ifndef
12#	.  ${directive} "1" != "0"
13#	.  endif
14#	.endfor
15#
16# A more practical usage of the .for loop that often led to surprises was the
17# following:
18#
19#	.for var in VAR1 VAR2 VAR3
20#	.  if ${var} != "VAR2"
21#	.  endif
22#	.endfor
23#
24# The .for loop body expanded to this string:
25#
26#	.  if VAR1 != "VAR2"
27#	.  endif
28#
29# Since bare words were not allowed at the left-hand side of a condition,
30# make complained about a "Malformed conditional", which was surprising since
31# the code before expanding the .for loop body looked quite well.
32#
33# In cond.c 1.48 from 2008-11-29, just a month before the expansion of .for
34# loops changed from plain textual value to using expressions of the form
35# ${:Uvalue}, this surprising behavior was documented in the code, and a
36# workaround was implemented that allowed bare words when they are followed
37# by either '!' or '=', as part of the operators '!=' or '=='.
38#
39# Since cond.c 1.68 from 2015-05-05, bare words are allowed on the left-hand
40# side of a condition, but that applies only to expression of the form
41# ${${cond} :? then : else}, it does not apply to conditions in ordinary .if
42# directives.
43
44# The following snippet worked in 2005, when the variables from the .for loop
45# expanded to their bare textual value.
46.for directive in if ifdef ifndef
47.  ${directive} "1" != "0"
48.  endif
49.endfor
50# In 2021, the above code does not generate an error message, even though the
51# code looks clearly malformed.  This is due to the '!', which is interpreted
52# as a dependency operator, similar to ':' and '::'.  The parser turns this
53# line into a dependency with the 3 targets '.', 'if', '"1"' and the 2 sources
54# '=' and '"0"'.  Since that line is not interpreted as an '.if' directive,
55# the error message 'if-less endif' makes sense.
56
57# In 2005, make complained:
58#
59#	.if line:	Malformed conditional (VAR1 != "VAR2")
60#	.endif line:	if-less endif
61#	.endif line:	Need an operator
62#
63# 2008.11.30.22.37.55 does not complain about the left-hand side ${var}.
64.for var in VAR1 VAR2 VAR3
65.  if ${var} != "VAR2"
66_!=	echo "${var}" 1>&2; echo # In 2005, '.info' was not invented yet.
67.  endif
68.endfor
69
70# Before for.c 1.39 from 2008-12-21, a common workaround was to surround the
71# variable expression from the .for loop with '"'.  Such a string literal
72# has been allowed since cond.c 1.23 from 2004-04-13.  Between that commit and
73# the one from 2008, the parser would still get confused if the value from the
74# .for loop contained '"', which was effectively a code injection.
75#
76# Surrounding ${var} with quotes disabled the check for typos though.  For
77# ordinary variables, referring to an undefined variable on the left-hand side
78# of the comparison resulted in a "Malformed conditional".  Since the .for
79# loop was usually close to the .if clause, this was not a problem in
80# practice.
81.for var in VAR1 VAR2 VAR3
82.  if "${var}" != "VAR2"
83.  endif
84.endfor
85
86all:
87