1# $NetBSD: directive-for-if.mk,v 1.3 2023/11/19 21:47:52 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# expect+3: if-less endif 49# expect+2: if-less endif 50# expect+1: if-less endif 51. endif 52.endfor 53# In 2021, the above code does not generate an error message, even though the 54# code looks clearly malformed. This is due to the '!', which is interpreted 55# as a dependency operator, similar to ':' and '::'. The parser turns this 56# line into a dependency with the 3 targets '.', 'if', '"1"' and the 2 sources 57# '=' and '"0"'. Since that line is not interpreted as an '.if' directive, 58# the error message 'if-less endif' makes sense. 59 60# In 2005, make complained: 61# 62# .if line: Malformed conditional (VAR1 != "VAR2") 63# .endif line: if-less endif 64# .endif line: Need an operator 65# 66# 2008.11.30.22.37.55 does not complain about the left-hand side ${var}. 67.for var in VAR1 VAR2 VAR3 68. if ${var} != "VAR2" 69_!= echo "${var}" 1>&2; echo # In 2005, '.info' was not invented yet. 70. endif 71.endfor 72 73# Before for.c 1.39 from 2008-12-21, a common workaround was to surround the 74# expression from the .for loop with '"'. Such a string literal 75# has been allowed since cond.c 1.23 from 2004-04-13. Between that commit and 76# the one from 2008, the parser would still get confused if the value from the 77# .for loop contained '"', which was effectively a code injection. 78# 79# Surrounding ${var} with quotes disabled the check for typos though. For 80# ordinary variables, referring to an undefined variable on the left-hand side 81# of the comparison resulted in a "Malformed conditional". Since the .for 82# loop was usually close to the .if clause, this was not a problem in 83# practice. 84.for var in VAR1 VAR2 VAR3 85. if "${var}" != "VAR2" 86. endif 87.endfor 88 89all: 90