xref: /freebsd/contrib/bmake/unit-tests/directive-for-if.mk (revision d5e0a182cf153f8993a633b93d9220c99a89e760)
1*d5e0a182SSimon J. Gerraty# $NetBSD: directive-for-if.mk,v 1.3 2023/11/19 21:47:52 rillig Exp $
212904384SSimon J. Gerraty#
312904384SSimon J. Gerraty# Test for a .for directive that contains an .if directive.
412904384SSimon J. Gerraty#
512904384SSimon J. Gerraty# Before for.c 1.39 from 2008-12-21, when expanding the variables of a .for
612904384SSimon J. Gerraty# loop, their values were placed verbatim in the expanded body.  Since then,
712904384SSimon J. Gerraty# each variable value expands to an expression of the form ${:Uvalue}.
812904384SSimon J. Gerraty#
912904384SSimon J. Gerraty# Before that change, the following adventurous code was possible:
1012904384SSimon J. Gerraty#
1112904384SSimon J. Gerraty#	.for directive in if ifdef ifndef
1212904384SSimon J. Gerraty#	.  ${directive} "1" != "0"
1312904384SSimon J. Gerraty#	.  endif
1412904384SSimon J. Gerraty#	.endfor
1512904384SSimon J. Gerraty#
1612904384SSimon J. Gerraty# A more practical usage of the .for loop that often led to surprises was the
1712904384SSimon J. Gerraty# following:
1812904384SSimon J. Gerraty#
1912904384SSimon J. Gerraty#	.for var in VAR1 VAR2 VAR3
2012904384SSimon J. Gerraty#	.  if ${var} != "VAR2"
2112904384SSimon J. Gerraty#	.  endif
2212904384SSimon J. Gerraty#	.endfor
2312904384SSimon J. Gerraty#
2412904384SSimon J. Gerraty# The .for loop body expanded to this string:
2512904384SSimon J. Gerraty#
2612904384SSimon J. Gerraty#	.  if VAR1 != "VAR2"
2712904384SSimon J. Gerraty#	.  endif
2812904384SSimon J. Gerraty#
2912904384SSimon J. Gerraty# Since bare words were not allowed at the left-hand side of a condition,
3012904384SSimon J. Gerraty# make complained about a "Malformed conditional", which was surprising since
3112904384SSimon J. Gerraty# the code before expanding the .for loop body looked quite well.
3212904384SSimon J. Gerraty#
3312904384SSimon J. Gerraty# In cond.c 1.48 from 2008-11-29, just a month before the expansion of .for
3412904384SSimon J. Gerraty# loops changed from plain textual value to using expressions of the form
3512904384SSimon J. Gerraty# ${:Uvalue}, this surprising behavior was documented in the code, and a
3612904384SSimon J. Gerraty# workaround was implemented that allowed bare words when they are followed
3712904384SSimon J. Gerraty# by either '!' or '=', as part of the operators '!=' or '=='.
3812904384SSimon J. Gerraty#
3912904384SSimon J. Gerraty# Since cond.c 1.68 from 2015-05-05, bare words are allowed on the left-hand
4012904384SSimon J. Gerraty# side of a condition, but that applies only to expression of the form
4112904384SSimon J. Gerraty# ${${cond} :? then : else}, it does not apply to conditions in ordinary .if
4212904384SSimon J. Gerraty# directives.
4312904384SSimon J. Gerraty
4412904384SSimon J. Gerraty# The following snippet worked in 2005, when the variables from the .for loop
4512904384SSimon J. Gerraty# expanded to their bare textual value.
4612904384SSimon J. Gerraty.for directive in if ifdef ifndef
4712904384SSimon J. Gerraty.  ${directive} "1" != "0"
48148ee845SSimon J. Gerraty# expect+3: if-less endif
49148ee845SSimon J. Gerraty# expect+2: if-less endif
50148ee845SSimon J. Gerraty# expect+1: if-less endif
5112904384SSimon J. Gerraty.  endif
5212904384SSimon J. Gerraty.endfor
5312904384SSimon J. Gerraty# In 2021, the above code does not generate an error message, even though the
5412904384SSimon J. Gerraty# code looks clearly malformed.  This is due to the '!', which is interpreted
5512904384SSimon J. Gerraty# as a dependency operator, similar to ':' and '::'.  The parser turns this
5612904384SSimon J. Gerraty# line into a dependency with the 3 targets '.', 'if', '"1"' and the 2 sources
5712904384SSimon J. Gerraty# '=' and '"0"'.  Since that line is not interpreted as an '.if' directive,
5812904384SSimon J. Gerraty# the error message 'if-less endif' makes sense.
5912904384SSimon J. Gerraty
6012904384SSimon J. Gerraty# In 2005, make complained:
6112904384SSimon J. Gerraty#
6212904384SSimon J. Gerraty#	.if line:	Malformed conditional (VAR1 != "VAR2")
6312904384SSimon J. Gerraty#	.endif line:	if-less endif
6412904384SSimon J. Gerraty#	.endif line:	Need an operator
6512904384SSimon J. Gerraty#
6612904384SSimon J. Gerraty# 2008.11.30.22.37.55 does not complain about the left-hand side ${var}.
6712904384SSimon J. Gerraty.for var in VAR1 VAR2 VAR3
6812904384SSimon J. Gerraty.  if ${var} != "VAR2"
6912904384SSimon J. Gerraty_!=	echo "${var}" 1>&2; echo # In 2005, '.info' was not invented yet.
7012904384SSimon J. Gerraty.  endif
7112904384SSimon J. Gerraty.endfor
7212904384SSimon J. Gerraty
7312904384SSimon J. Gerraty# Before for.c 1.39 from 2008-12-21, a common workaround was to surround the
74*d5e0a182SSimon J. Gerraty# expression from the .for loop with '"'.  Such a string literal
7512904384SSimon J. Gerraty# has been allowed since cond.c 1.23 from 2004-04-13.  Between that commit and
7612904384SSimon J. Gerraty# the one from 2008, the parser would still get confused if the value from the
7712904384SSimon J. Gerraty# .for loop contained '"', which was effectively a code injection.
7812904384SSimon J. Gerraty#
7912904384SSimon J. Gerraty# Surrounding ${var} with quotes disabled the check for typos though.  For
8012904384SSimon J. Gerraty# ordinary variables, referring to an undefined variable on the left-hand side
8112904384SSimon J. Gerraty# of the comparison resulted in a "Malformed conditional".  Since the .for
8212904384SSimon J. Gerraty# loop was usually close to the .if clause, this was not a problem in
8312904384SSimon J. Gerraty# practice.
8412904384SSimon J. Gerraty.for var in VAR1 VAR2 VAR3
8512904384SSimon J. Gerraty.  if "${var}" != "VAR2"
8612904384SSimon J. Gerraty.  endif
8712904384SSimon J. Gerraty.endfor
8812904384SSimon J. Gerraty
8912904384SSimon J. Gerratyall:
90