1 # $NetBSD: cond-token-plain.mk,v 1.23 2025/07/06 07:56:16 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 # 18 # expect+1: Unfinished string literal """ 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 # 42 # expect+1: Unfinished string literal ""\\" 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 110 VAR= 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 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 expressions. 139 . info Numbers can be composed from literals and expressions. 140 .endif 141 142 .if 0${:Ux01} 143 # expect+1: Numbers can be composed from literals and expressions. 144 . info Numbers can be composed from literals and 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, an 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 an 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 256 COND= 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 269 270 # A trailing backslash in a bare word does not escape anything. 271 # expect+1: Unfinished backslash escape sequence 272 .if ${${:U str == str\\}:?yes:no} 273 . error 274 .else 275 . error 276 .endif 277 278 # A trailing backslash in an unfinished string literal word does not escape 279 # anything. 280 # expect+2: Unfinished backslash escape sequence 281 # expect+1: Unfinished string literal ""str\" 282 .if ${${:U str == "str\\}:?yes:no} 283 . error 284 .else 285 . error 286 .endif 287 288 # expect+1: Unfinished string literal ""str" 289 .if ${${:U str == "str}:?yes:no} 290 . error 291 .else 292 . error 293 .endif 294