cond-token-plain.mk revision 1.23 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