1# $NetBSD: cond-token-plain.mk,v 1.18 2023/06/01 20:56:35 rillig Exp $ 2# 3# Tests for plain tokens (that is, string literals without quotes) 4# in .if conditions. These are also called bare words. 5 6.MAKEFLAGS: -dc 7 8# The word 'value' after the '!=' is a bare word. 9.if ${:Uvalue} != value 10. error 11.endif 12 13# Using a '#' in a string literal in a condition leads to a malformed 14# condition since comment parsing is done in an early phase and removes the 15# '#' and everything after it long before the condition parser gets to see it. 16# 17# XXX: The error message is missing for this malformed condition. 18# The right-hand side of the comparison is just a '"', before unescaping. 19.if ${:U} != "#hash" 20. error 21.endif 22 23# To get a '#' into a condition, it has to be escaped using a backslash. 24# This prevents the comment parser from removing it, and in turn, it becomes 25# visible to CondParser_String. 26.if ${:U\#hash} != "\#hash" 27. error 28.endif 29 30# Since 2002-12-30, and still as of 2020-09-11, CondParser_Token handles 31# the '#' specially, even though at this point, there should be no need for 32# comment handling anymore. The comments are supposed to be stripped off 33# in a very early parsing phase. 34# 35# See https://gnats.netbsd.org/19596 for example makefiles demonstrating the 36# original problems. At that time, the parser didn't recognize the comment in 37# the line '.else # comment3'. This workaround is not needed anymore since 38# comments are stripped in an earlier phase. See "case '#'" in 39# CondParser_Token. 40# 41# XXX: Missing error message for the malformed condition. The right-hand 42# side before unescaping is double-quotes, backslash, backslash. 43.if ${:U\\} != "\\#hash" 44. error 45.endif 46 47# The right-hand side of a comparison is not parsed as a token, therefore 48# the code from CondParser_Token does not apply to it. 49# TODO: Explain the consequences. 50# TODO: Does this mean that more syntactic variants are allowed here? 51.if ${:U\#hash} != \#hash 52. error 53.endif 54 55# XXX: What is the purpose of treating an escaped '#' in the following 56# condition as a comment? And why only at the beginning of a token, 57# just as in the shell? 58.if 0 \# This is treated as a comment, but why? 59. error 60.endif 61 62# Ah, ok, this can be used to add an end-of-condition comment. But does 63# anybody really use this? This is neither documented nor obvious since 64# the '#' is escaped. It's much clearer to write a comment in the line 65# above the condition. 66.if ${0 \# comment:?yes:no} != no 67. error 68.endif 69.if ${1 \# comment:?yes:no} != yes 70. error 71.endif 72 73# Usually there is whitespace around the comparison operator, but this is 74# not required. 75.if ${UNDEF:Uundefined}!=undefined 76. error 77.endif 78.if ${UNDEF:U12345}>12345 79. error 80.endif 81.if ${UNDEF:U12345}<12345 82. error 83.endif 84.if (${UNDEF:U0})||0 85. error 86.endif 87 88# Only the comparison operator terminates the comparison operand, and it's 89# a coincidence that the '!' is both used in the '!=' comparison operator 90# as well as for negating a comparison result. 91# 92# The characters '&' and '|' are part of the comparison operand. 93.if ${:Uvar}&&name != "var&&name" 94. error 95.endif 96.if ${:Uvar}||name != "var||name" 97. error 98.endif 99 100# A bare word may occur alone in a condition, without any comparison 101# operator. It is interpreted as the function call 'defined(bare)'. 102.if bare 103. error 104.else 105# expect+1: A bare word is treated like defined(...), and the variable 'bare' is not defined. 106. info A bare word is treated like defined(...), and the variable $\ 107 'bare' is not defined. 108.endif 109 110VAR= defined 111.if VAR 112# expect+1: A bare word is treated like defined(...). 113. info A bare word is treated like defined(...). 114.else 115. error 116.endif 117 118# Bare words may be intermixed with variable expressions. 119.if V${:UA}R 120# expect+1: ok 121. info ok 122.else 123. error 124.endif 125 126# In bare words, even undefined variables are allowed. Without the bare 127# words, undefined variables are not allowed. That feels inconsistent. 128.if V${UNDEF}AR 129# expect+1: Undefined variables in bare words expand to an empty string. 130. info Undefined variables in bare words expand to an empty string. 131.else 132. error 133.endif 134 135.if 0${:Ux00} 136. error 137.else 138# expect+1: Numbers can be composed from literals and variable expressions. 139. info Numbers can be composed from literals and variable expressions. 140.endif 141 142.if 0${:Ux01} 143# expect+1: Numbers can be composed from literals and variable expressions. 144. info Numbers can be composed from literals and variable expressions. 145.else 146. error 147.endif 148 149# If the right-hand side is missing, it's a parse error. 150# expect+1: Missing right-hand side of operator '==' 151.if "" == 152. error 153.else 154. error 155.endif 156 157# If the left-hand side is missing, it's a parse error as well, but without 158# a specific error message. 159# expect+1: Malformed conditional (== "") 160.if == "" 161. error 162.else 163. error 164.endif 165 166# The '\\' is not a line continuation. Neither is it an unquoted string 167# literal. Instead, it is parsed as a bare word (ParseWord), 168# and in that context, the backslash is just an ordinary character. The 169# function argument thus stays '\\' (2 backslashes). This string is passed 170# to FuncDefined, and since there is no variable named '\\', the condition 171# evaluates to false. 172.if \\ 173. error 174.else 175# expect+1: The variable '\\' is not defined. 176. info The variable '\\' is not defined. 177.endif 178 179${:U\\\\}= backslash 180.if \\ 181# expect+1: Now the variable '\\' is defined. 182. info Now the variable '\\' is defined. 183.else 184. error 185.endif 186 187# Anything that doesn't start with a double quote is considered a "bare word". 188# Strangely, a bare word may contain double quotes inside. Nobody should ever 189# depend on this since it may well be unintended. See CondParser_String. 190.if "unquoted\"quoted" != unquoted"quoted 191. error 192.endif 193 194# FIXME: In CondParser_String, Var_Parse returns var_Error without a 195# corresponding error message. 196# expect+1: Malformed conditional ($$$$$$$$ != "") 197.if $$$$$$$$ != "" 198. error 199.else 200. error 201.endif 202 203# In a condition in an .if directive, the left-hand side must not be an 204# unquoted string literal. 205# expect+1: Malformed conditional (left == right) 206.if left == right 207.endif 208# Before cond.c 1.276 from 2021-09-21, a variable expression containing the 209# modifier ':?:' allowed unquoted string literals for the rest of the 210# condition. This was an unintended implementation mistake. 211# expect+1: Malformed conditional (${0:?:} || left == right) 212.if ${0:?:} || left == right 213.endif 214# This affected only the comparisons after the expression, so the following 215# was still a syntax error. 216# expect+1: Malformed conditional (left == right || ${0:?:}) 217.if left == right || ${0:?:} 218.endif 219 220# See cond-token-string.mk for similar tests where the condition is enclosed 221# in "quotes". 222 223.MAKEFLAGS: -d0 224 225 226# As of cond.c 1.320 from 2021-12-30, the code in CondParser_ComparisonOrLeaf 227# looks suspicious of evaluating the expression twice: first for parsing a 228# bare word and second for parsing the left-hand side of a comparison. 229# 230# In '.if' directives, the left-hand side of a comparison must not be a bare 231# word though, and this keeps CondParser_Leaf from evaluating the expression 232# for the second time. The right-hand side of a comparison may be a bare 233# word, but that side has no risk of being parsed more than once. 234# 235# expect+1: Malformed conditional (VAR.${IF_COUNT::+=1} != "") 236.if VAR.${IF_COUNT::+=1} != "" 237. error 238.else 239. error 240.endif 241.if ${IF_COUNT} != "1" 242. error 243.endif 244 245# A different situation is when CondParser.leftUnquotedOK is true. This 246# situation arises in expressions of the form ${cond:?yes:no}. As of 247# 2021-12-30, the condition in such an expression is evaluated before parsing 248# the condition, see varmod-ifelse.mk. To pass a variable expression to the 249# condition parser, it needs to be escaped. This rarely happens in practice, 250# in most cases the conditions are simple enough that it doesn't matter 251# whether the condition is first evaluated and then parsed, or vice versa. 252# A half-baked attempt at hiding this implementation detail is 253# CondParser.leftUnquotedOK, but that is a rather leaky abstraction. 254 255#.MAKEFLAGS: -dcv 256COND= VAR.$${MOD_COUNT::+=1} 257.if ${${COND} == "VAR.":?yes:no} != "yes" 258. error 259.endif 260 261# The value "1 1" demonstrates that the expression ${MOD_COUNT::+=1} was 262# evaluated twice. In practice, expressions that occur in conditions do not 263# have side effects, making this problem rather academic, but it is there. 264.if ${MOD_COUNT} != "1 1" 265. error 266.endif 267#.MAKEFLAGS: -d0 268