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