directive-for-if.mk revision 1.2
11.2Srillig# $NetBSD: directive-for-if.mk,v 1.2 2023/06/01 20:56:35 rillig Exp $ 21.1Srillig# 31.1Srillig# Test for a .for directive that contains an .if directive. 41.1Srillig# 51.1Srillig# Before for.c 1.39 from 2008-12-21, when expanding the variables of a .for 61.1Srillig# loop, their values were placed verbatim in the expanded body. Since then, 71.1Srillig# each variable value expands to an expression of the form ${:Uvalue}. 81.1Srillig# 91.1Srillig# Before that change, the following adventurous code was possible: 101.1Srillig# 111.1Srillig# .for directive in if ifdef ifndef 121.1Srillig# . ${directive} "1" != "0" 131.1Srillig# . endif 141.1Srillig# .endfor 151.1Srillig# 161.1Srillig# A more practical usage of the .for loop that often led to surprises was the 171.1Srillig# following: 181.1Srillig# 191.1Srillig# .for var in VAR1 VAR2 VAR3 201.1Srillig# . if ${var} != "VAR2" 211.1Srillig# . endif 221.1Srillig# .endfor 231.1Srillig# 241.1Srillig# The .for loop body expanded to this string: 251.1Srillig# 261.1Srillig# . if VAR1 != "VAR2" 271.1Srillig# . endif 281.1Srillig# 291.1Srillig# Since bare words were not allowed at the left-hand side of a condition, 301.1Srillig# make complained about a "Malformed conditional", which was surprising since 311.1Srillig# the code before expanding the .for loop body looked quite well. 321.1Srillig# 331.1Srillig# In cond.c 1.48 from 2008-11-29, just a month before the expansion of .for 341.1Srillig# loops changed from plain textual value to using expressions of the form 351.1Srillig# ${:Uvalue}, this surprising behavior was documented in the code, and a 361.1Srillig# workaround was implemented that allowed bare words when they are followed 371.1Srillig# by either '!' or '=', as part of the operators '!=' or '=='. 381.1Srillig# 391.1Srillig# Since cond.c 1.68 from 2015-05-05, bare words are allowed on the left-hand 401.1Srillig# side of a condition, but that applies only to expression of the form 411.1Srillig# ${${cond} :? then : else}, it does not apply to conditions in ordinary .if 421.1Srillig# directives. 431.1Srillig 441.1Srillig# The following snippet worked in 2005, when the variables from the .for loop 451.1Srillig# expanded to their bare textual value. 461.1Srillig.for directive in if ifdef ifndef 471.1Srillig. ${directive} "1" != "0" 481.2Srillig# expect+3: if-less endif 491.2Srillig# expect+2: if-less endif 501.2Srillig# expect+1: if-less endif 511.1Srillig. endif 521.1Srillig.endfor 531.1Srillig# In 2021, the above code does not generate an error message, even though the 541.1Srillig# code looks clearly malformed. This is due to the '!', which is interpreted 551.1Srillig# as a dependency operator, similar to ':' and '::'. The parser turns this 561.1Srillig# line into a dependency with the 3 targets '.', 'if', '"1"' and the 2 sources 571.1Srillig# '=' and '"0"'. Since that line is not interpreted as an '.if' directive, 581.1Srillig# the error message 'if-less endif' makes sense. 591.1Srillig 601.1Srillig# In 2005, make complained: 611.1Srillig# 621.1Srillig# .if line: Malformed conditional (VAR1 != "VAR2") 631.1Srillig# .endif line: if-less endif 641.1Srillig# .endif line: Need an operator 651.1Srillig# 661.1Srillig# 2008.11.30.22.37.55 does not complain about the left-hand side ${var}. 671.1Srillig.for var in VAR1 VAR2 VAR3 681.1Srillig. if ${var} != "VAR2" 691.1Srillig_!= echo "${var}" 1>&2; echo # In 2005, '.info' was not invented yet. 701.1Srillig. endif 711.1Srillig.endfor 721.1Srillig 731.1Srillig# Before for.c 1.39 from 2008-12-21, a common workaround was to surround the 741.1Srillig# variable expression from the .for loop with '"'. Such a string literal 751.1Srillig# has been allowed since cond.c 1.23 from 2004-04-13. Between that commit and 761.1Srillig# the one from 2008, the parser would still get confused if the value from the 771.1Srillig# .for loop contained '"', which was effectively a code injection. 781.1Srillig# 791.1Srillig# Surrounding ${var} with quotes disabled the check for typos though. For 801.1Srillig# ordinary variables, referring to an undefined variable on the left-hand side 811.1Srillig# of the comparison resulted in a "Malformed conditional". Since the .for 821.1Srillig# loop was usually close to the .if clause, this was not a problem in 831.1Srillig# practice. 841.1Srillig.for var in VAR1 VAR2 VAR3 851.1Srillig. if "${var}" != "VAR2" 861.1Srillig. endif 871.1Srillig.endfor 881.1Srillig 891.1Srilligall: 90