directive-include-guard.mk revision 1.19 1 1.19 rillig # $NetBSD: directive-include-guard.mk,v 1.19 2025/04/11 17:21:31 rillig Exp $
2 1.1 rillig #
3 1.1 rillig # Tests for multiple-inclusion guards in makefiles.
4 1.1 rillig #
5 1.7 rillig # A file that is guarded by a multiple-inclusion guard has one of the
6 1.7 rillig # following forms:
7 1.1 rillig #
8 1.7 rillig # .ifndef GUARD_VARIABLE
9 1.1 rillig # .endif
10 1.1 rillig #
11 1.7 rillig # .if !defined(GUARD_VARIABLE)
12 1.7 rillig # .endif
13 1.7 rillig #
14 1.7 rillig # .if !target(guard-target)
15 1.7 rillig # .endif
16 1.7 rillig #
17 1.7 rillig # When such a file is included for the second or later time, and the guard
18 1.13 rillig # variable or the guard target is defined, the file is skipped completely, as
19 1.13 rillig # including it would not have any effect, not even on the special variable
20 1.13 rillig # '.MAKE.MAKEFILES', as that variable skips duplicate pathnames.
21 1.1 rillig #
22 1.2 rillig # See also:
23 1.2 rillig # https://gcc.gnu.org/onlinedocs/cppinternals/Guard-Macros.html
24 1.1 rillig
25 1.9 rillig # Each of the following test cases creates a temporary file named after the
26 1.9 rillig # test case and writes some lines of text to that file. That file is then
27 1.9 rillig # included twice, to see whether the second '.include' is skipped.
28 1.1 rillig
29 1.9 rillig
30 1.9 rillig # This is the canonical form of a variable-based multiple-inclusion guard.
31 1.14 rillig CASES+= variable-ifndef
32 1.9 rillig LINES.variable-ifndef= \
33 1.9 rillig '.ifndef VARIABLE_IFNDEF' \
34 1.9 rillig 'VARIABLE_IFNDEF=' \
35 1.1 rillig '.endif'
36 1.19 rillig # expect: Parse_PushInput: variable-ifndef.tmp:1
37 1.9 rillig # expect: Skipping 'variable-ifndef.tmp' because 'VARIABLE_IFNDEF' is defined
38 1.1 rillig
39 1.10 rillig # A file that reuses a guard from a previous file (or whose guard is defined
40 1.10 rillig # for any other reason) is only processed once, to see whether it is guarded.
41 1.10 rillig # Its content is skipped, therefore the syntax error is not detected.
42 1.14 rillig CASES+= variable-ifndef-reuse
43 1.10 rillig LINES.variable-ifndef-reuse= \
44 1.10 rillig '.ifndef VARIABLE_IFNDEF' \
45 1.10 rillig 'syntax error' \
46 1.10 rillig '.endif'
47 1.19 rillig # expect: Parse_PushInput: variable-ifndef-reuse.tmp:1
48 1.10 rillig # expect: Skipping 'variable-ifndef-reuse.tmp' because 'VARIABLE_IFNDEF' is defined
49 1.10 rillig
50 1.13 rillig # The guard variable cannot be a number, as numbers are interpreted
51 1.13 rillig # differently from bare words.
52 1.14 rillig CASES+= variable-ifndef-zero
53 1.13 rillig LINES.variable-ifndef-zero= \
54 1.13 rillig '.ifndef 0e0' \
55 1.13 rillig 'syntax error' \
56 1.13 rillig '.endif'
57 1.19 rillig # expect: Parse_PushInput: variable-ifndef-zero.tmp:1
58 1.19 rillig # expect: Parse_PushInput: variable-ifndef-zero.tmp:1
59 1.13 rillig
60 1.13 rillig # The guard variable cannot be a number, as numbers are interpreted
61 1.13 rillig # differently from bare words.
62 1.14 rillig CASES+= variable-ifndef-one
63 1.13 rillig LINES.variable-ifndef-one= \
64 1.13 rillig '.ifndef 1' \
65 1.13 rillig '.endif'
66 1.19 rillig # expect: Parse_PushInput: variable-ifndef-one.tmp:1
67 1.19 rillig # expect: Parse_PushInput: variable-ifndef-one.tmp:1
68 1.13 rillig
69 1.9 rillig # Comments and empty lines do not affect the multiple-inclusion guard.
70 1.14 rillig CASES+= comments
71 1.1 rillig LINES.comments= \
72 1.1 rillig '\# comment' \
73 1.1 rillig '' \
74 1.3 rillig '.ifndef COMMENTS' \
75 1.1 rillig '\# comment' \
76 1.3 rillig 'COMMENTS=\#comment' \
77 1.1 rillig '.endif' \
78 1.1 rillig '\# comment'
79 1.19 rillig # expect: Parse_PushInput: comments.tmp:1
80 1.7 rillig # expect: Skipping 'comments.tmp' because 'COMMENTS' is defined
81 1.1 rillig
82 1.1 rillig # An alternative form uses the 'defined' function. It is more verbose than
83 1.9 rillig # the canonical form but avoids the '.ifndef' directive, as that directive is
84 1.9 rillig # not commonly used.
85 1.14 rillig CASES+= variable-if
86 1.9 rillig LINES.variable-if= \
87 1.9 rillig '.if !defined(VARIABLE_IF)' \
88 1.9 rillig 'VARIABLE_IF=' \
89 1.9 rillig '.endif'
90 1.19 rillig # expect: Parse_PushInput: variable-if.tmp:1
91 1.9 rillig # expect: Skipping 'variable-if.tmp' because 'VARIABLE_IF' is defined
92 1.9 rillig
93 1.10 rillig # A file that reuses a guard from a previous file (or whose guard is defined
94 1.10 rillig # for any other reason) is only processed once, to see whether it is guarded.
95 1.10 rillig # Its content is skipped, therefore the syntax error is not detected.
96 1.14 rillig CASES+= variable-if-reuse
97 1.10 rillig LINES.variable-if-reuse= \
98 1.10 rillig '.if !defined(VARIABLE_IF)' \
99 1.10 rillig 'syntax error' \
100 1.10 rillig '.endif'
101 1.19 rillig # expect: Parse_PushInput: variable-if-reuse.tmp:1
102 1.10 rillig # expect: Skipping 'variable-if-reuse.tmp' because 'VARIABLE_IF' is defined
103 1.10 rillig
104 1.9 rillig # Triple negation is so uncommon that it's not recognized, even though it has
105 1.9 rillig # the same effect as a single negation.
106 1.14 rillig CASES+= variable-if-triple-negation
107 1.9 rillig LINES.variable-if-triple-negation= \
108 1.9 rillig '.if !!!defined(VARIABLE_IF_TRIPLE_NEGATION)' \
109 1.9 rillig 'VARIABLE_IF_TRIPLE_NEGATION=' \
110 1.9 rillig '.endif'
111 1.19 rillig # expect: Parse_PushInput: variable-if-triple-negation.tmp:1
112 1.19 rillig # expect: Parse_PushInput: variable-if-triple-negation.tmp:1
113 1.9 rillig
114 1.15 rillig # If the guard variable is enclosed in spaces, it does not have an effect, as
115 1.15 rillig # that form is not common in practice.
116 1.15 rillig CASES+= variable-if-spaced
117 1.15 rillig LINES.variable-if-spaced= \
118 1.15 rillig '.if !defined( VARIABLE_IF_SPACED )' \
119 1.15 rillig 'VARIABLE_IF_SPACED=' \
120 1.15 rillig '.endif'
121 1.19 rillig # expect: Parse_PushInput: variable-if-spaced.tmp:1
122 1.19 rillig # expect: Parse_PushInput: variable-if-spaced.tmp:1
123 1.15 rillig
124 1.15 rillig # If the guard variable condition is enclosed in parentheses, it does not have
125 1.15 rillig # an effect, as that form is not common in practice.
126 1.15 rillig CASES+= variable-if-parenthesized
127 1.15 rillig LINES.variable-if-parenthesized= \
128 1.15 rillig '.if (!defined(VARIABLE_IF_PARENTHESIZED))' \
129 1.15 rillig 'VARIABLE_IF_PARENTHESIZED=' \
130 1.15 rillig '.endif'
131 1.19 rillig # expect: Parse_PushInput: variable-if-parenthesized.tmp:1
132 1.19 rillig # expect: Parse_PushInput: variable-if-parenthesized.tmp:1
133 1.15 rillig
134 1.9 rillig # A conditional other than '.if' or '.ifndef' does not guard the file, even if
135 1.9 rillig # it is otherwise equivalent to the above accepted forms.
136 1.14 rillig CASES+= variable-ifdef-negated
137 1.9 rillig LINES.variable-ifdef-negated= \
138 1.9 rillig '.ifdef !VARIABLE_IFDEF_NEGATED' \
139 1.9 rillig 'VARIABLE_IFDEF_NEGATED=' \
140 1.6 rillig '.endif'
141 1.19 rillig # expect: Parse_PushInput: variable-ifdef-negated.tmp:1
142 1.19 rillig # expect: Parse_PushInput: variable-ifdef-negated.tmp:1
143 1.6 rillig
144 1.1 rillig # The variable names in the '.if' and the assignment must be the same.
145 1.14 rillig CASES+= variable-name-mismatch
146 1.9 rillig LINES.variable-name-mismatch= \
147 1.9 rillig '.ifndef VARIABLE_NAME_MISMATCH' \
148 1.9 rillig 'VARIABLE_NAME_DIFFERENT=' \
149 1.9 rillig '.endif'
150 1.19 rillig # expect: Parse_PushInput: variable-name-mismatch.tmp:1
151 1.19 rillig # expect: Parse_PushInput: variable-name-mismatch.tmp:1
152 1.9 rillig
153 1.15 rillig # If the guard variable condition is enclosed in parentheses, it does not have
154 1.15 rillig # an effect, as that form is not common in practice.
155 1.15 rillig CASES+= variable-ifndef-parenthesized
156 1.15 rillig LINES.variable-ifndef-parenthesized= \
157 1.15 rillig '.ifndef (VARIABLE_IFNDEF_PARENTHESIZED)' \
158 1.15 rillig 'VARIABLE_IFNDEF_PARENTHESIZED=' \
159 1.15 rillig '.endif'
160 1.19 rillig # expect: Parse_PushInput: variable-ifndef-parenthesized.tmp:1
161 1.19 rillig # expect: Parse_PushInput: variable-ifndef-parenthesized.tmp:1
162 1.15 rillig
163 1.9 rillig # The variable name '!VARNAME' cannot be used in an '.ifndef' directive, as
164 1.9 rillig # the '!' would be a negation. It is syntactically valid in a '.if !defined'
165 1.12 rillig # condition, but this case is so uncommon that the guard mechanism doesn't
166 1.12 rillig # accept '!' in the guard variable name. Furthermore, when defining the
167 1.12 rillig # variable, the character '!' has to be escaped, to prevent it from being
168 1.12 rillig # interpreted as the '!' dependency operator.
169 1.14 rillig CASES+= variable-name-exclamation
170 1.9 rillig LINES.variable-name-exclamation= \
171 1.9 rillig '.if !defined(!VARIABLE_NAME_EXCLAMATION)' \
172 1.9 rillig '${:U!}VARIABLE_NAME_EXCLAMATION=' \
173 1.9 rillig '.endif'
174 1.19 rillig # expect: Parse_PushInput: variable-name-exclamation.tmp:1
175 1.19 rillig # expect: Parse_PushInput: variable-name-exclamation.tmp:1
176 1.9 rillig
177 1.13 rillig # In general, a variable name can contain a '!' in the middle, as that
178 1.13 rillig # character is interpreted as an ordinary character in conditions as well as
179 1.13 rillig # on the left side of a variable assignment. For guard variable names, the
180 1.13 rillig # '!' is not supported in any place, though.
181 1.14 rillig CASES+= variable-name-exclamation-middle
182 1.9 rillig LINES.variable-name-exclamation-middle= \
183 1.9 rillig '.ifndef VARIABLE_NAME!MIDDLE' \
184 1.9 rillig 'VARIABLE_NAME!MIDDLE=' \
185 1.9 rillig '.endif'
186 1.19 rillig # expect: Parse_PushInput: variable-name-exclamation-middle.tmp:1
187 1.19 rillig # expect: Parse_PushInput: variable-name-exclamation-middle.tmp:1
188 1.9 rillig
189 1.9 rillig # A variable name can contain balanced parentheses, at least in conditions and
190 1.9 rillig # on the left side of a variable assignment. There are enough places in make
191 1.9 rillig # where parentheses or braces are handled inconsistently to make this naming
192 1.9 rillig # choice a bad idea, therefore these characters are not allowed in guard
193 1.9 rillig # variable names.
194 1.14 rillig CASES+= variable-name-parentheses
195 1.9 rillig LINES.variable-name-parentheses= \
196 1.9 rillig '.ifndef VARIABLE_NAME(&)PARENTHESES' \
197 1.9 rillig 'VARIABLE_NAME(&)PARENTHESES=' \
198 1.1 rillig '.endif'
199 1.19 rillig # expect: Parse_PushInput: variable-name-parentheses.tmp:1
200 1.19 rillig # expect: Parse_PushInput: variable-name-parentheses.tmp:1
201 1.1 rillig
202 1.6 rillig # The guard condition must consist of only the guard variable, nothing else.
203 1.14 rillig CASES+= variable-ifndef-plus
204 1.9 rillig LINES.variable-ifndef-plus= \
205 1.9 rillig '.ifndef VARIABLE_IFNDEF_PLUS && VARIABLE_IFNDEF_SECOND' \
206 1.9 rillig 'VARIABLE_IFNDEF_PLUS=' \
207 1.9 rillig 'VARIABLE_IFNDEF_SECOND=' \
208 1.6 rillig '.endif'
209 1.19 rillig # expect: Parse_PushInput: variable-ifndef-plus.tmp:1
210 1.19 rillig # expect: Parse_PushInput: variable-ifndef-plus.tmp:1
211 1.6 rillig
212 1.6 rillig # The guard condition must consist of only the guard variable, nothing else.
213 1.14 rillig CASES+= variable-if-plus
214 1.9 rillig LINES.variable-if-plus= \
215 1.9 rillig '.if !defined(VARIABLE_IF_PLUS) && !defined(VARIABLE_IF_SECOND)' \
216 1.9 rillig 'VARIABLE_IF_PLUS=' \
217 1.9 rillig 'VARIABLE_IF_SECOND=' \
218 1.6 rillig '.endif'
219 1.19 rillig # expect: Parse_PushInput: variable-if-plus.tmp:1
220 1.19 rillig # expect: Parse_PushInput: variable-if-plus.tmp:1
221 1.6 rillig
222 1.6 rillig # The variable name in an '.ifndef' guard must be given directly, it must not
223 1.6 rillig # contain any '$' expression.
224 1.14 rillig CASES+= variable-ifndef-indirect
225 1.9 rillig LINES.variable-ifndef-indirect= \
226 1.9 rillig '.ifndef $${VARIABLE_IFNDEF_INDIRECT:L}' \
227 1.9 rillig 'VARIABLE_IFNDEF_INDIRECT=' \
228 1.6 rillig '.endif'
229 1.19 rillig # expect: Parse_PushInput: variable-ifndef-indirect.tmp:1
230 1.19 rillig # expect: Parse_PushInput: variable-ifndef-indirect.tmp:1
231 1.9 rillig
232 1.9 rillig # The variable name in an '.if' guard must be given directly, it must not
233 1.9 rillig # contain any '$' expression.
234 1.14 rillig CASES+= variable-if-indirect
235 1.9 rillig LINES.variable-if-indirect= \
236 1.9 rillig '.if !defined($${VARIABLE_IF_INDIRECT:L})' \
237 1.9 rillig 'VARIABLE_IF_INDIRECT=' \
238 1.9 rillig '.endif'
239 1.19 rillig # expect: Parse_PushInput: variable-if-indirect.tmp:1
240 1.19 rillig # expect: Parse_PushInput: variable-if-indirect.tmp:1
241 1.6 rillig
242 1.5 rillig # The variable name in the guard condition must only contain alphanumeric
243 1.12 rillig # characters and underscores. The place where the guard variable is defined
244 1.12 rillig # is more flexible, as long as the variable is defined at the point where the
245 1.12 rillig # file is included the next time.
246 1.14 rillig CASES+= variable-assign-indirect
247 1.9 rillig LINES.variable-assign-indirect= \
248 1.9 rillig '.ifndef VARIABLE_ASSIGN_INDIRECT' \
249 1.9 rillig '$${VARIABLE_ASSIGN_INDIRECT:L}=' \
250 1.1 rillig '.endif'
251 1.19 rillig # expect: Parse_PushInput: variable-assign-indirect.tmp:1
252 1.9 rillig # expect: Skipping 'variable-assign-indirect.tmp' because 'VARIABLE_ASSIGN_INDIRECT' is defined
253 1.1 rillig
254 1.10 rillig # The time at which the guard variable is defined doesn't matter, as long as
255 1.10 rillig # it is defined at the point where the file is included the next time.
256 1.14 rillig CASES+= variable-assign-late
257 1.9 rillig LINES.variable-assign-late= \
258 1.9 rillig '.ifndef VARIABLE_ASSIGN_LATE' \
259 1.9 rillig 'VARIABLE_ASSIGN_LATE_OTHER=' \
260 1.9 rillig 'VARIABLE_ASSIGN_LATE=' \
261 1.1 rillig '.endif'
262 1.19 rillig # expect: Parse_PushInput: variable-assign-late.tmp:1
263 1.9 rillig # expect: Skipping 'variable-assign-late.tmp' because 'VARIABLE_ASSIGN_LATE' is defined
264 1.1 rillig
265 1.10 rillig # The time at which the guard variable is defined doesn't matter, as long as
266 1.10 rillig # it is defined at the point where the file is included the next time.
267 1.14 rillig CASES+= variable-assign-nested
268 1.9 rillig LINES.variable-assign-nested= \
269 1.9 rillig '.ifndef VARIABLE_ASSIGN_NESTED' \
270 1.2 rillig '. if 1' \
271 1.9 rillig '. for i in once' \
272 1.9 rillig 'VARIABLE_ASSIGN_NESTED=' \
273 1.9 rillig '. endfor' \
274 1.1 rillig '. endif' \
275 1.1 rillig '.endif'
276 1.19 rillig # expect: Parse_PushInput: variable-assign-nested.tmp:1
277 1.9 rillig # expect: Skipping 'variable-assign-nested.tmp' because 'VARIABLE_ASSIGN_NESTED' is defined
278 1.1 rillig
279 1.7 rillig # If the guard variable is defined before the file is included for the first
280 1.10 rillig # time, the file is considered guarded as well. In such a case, the parser
281 1.10 rillig # skips almost all lines, as they are irrelevant, but the structure of the
282 1.10 rillig # top-level '.if/.endif' conditional can be determined reliably enough to
283 1.10 rillig # decide whether the file is guarded.
284 1.14 rillig CASES+= variable-already-defined
285 1.9 rillig LINES.variable-already-defined= \
286 1.9 rillig '.ifndef VARIABLE_ALREADY_DEFINED' \
287 1.9 rillig 'VARIABLE_ALREADY_DEFINED=' \
288 1.9 rillig '.endif'
289 1.9 rillig VARIABLE_ALREADY_DEFINED=
290 1.19 rillig # expect: Parse_PushInput: variable-already-defined.tmp:1
291 1.10 rillig # expect: Skipping 'variable-already-defined.tmp' because 'VARIABLE_ALREADY_DEFINED' is defined
292 1.10 rillig
293 1.10 rillig # If the guard variable is defined before the file is included the first time,
294 1.10 rillig # the file is processed but its content is skipped. If that same guard
295 1.10 rillig # variable is undefined when the file is included the second time, the file is
296 1.10 rillig # processed as usual.
297 1.14 rillig CASES+= variable-defined-then-undefined
298 1.10 rillig LINES.variable-defined-then-undefined= \
299 1.10 rillig '.ifndef VARIABLE_DEFINED_THEN_UNDEFINED' \
300 1.10 rillig '.endif'
301 1.10 rillig VARIABLE_DEFINED_THEN_UNDEFINED=
302 1.10 rillig UNDEF_BETWEEN.variable-defined-then-undefined= \
303 1.10 rillig VARIABLE_DEFINED_THEN_UNDEFINED
304 1.19 rillig # expect: Parse_PushInput: variable-defined-then-undefined.tmp:1
305 1.19 rillig # expect: Parse_PushInput: variable-defined-then-undefined.tmp:1
306 1.1 rillig
307 1.1 rillig # The whole file content must be guarded by a single '.if' conditional, not by
308 1.12 rillig # several, as each of these conditionals would require its separate guard.
309 1.12 rillig # This case is not expected to occur in practice, as the two parts would
310 1.12 rillig # rather be split into separate files.
311 1.14 rillig CASES+= variable-two-times
312 1.9 rillig LINES.variable-two-times= \
313 1.9 rillig '.ifndef VARIABLE_TWO_TIMES_1' \
314 1.9 rillig 'VARIABLE_TWO_TIMES_1=' \
315 1.1 rillig '.endif' \
316 1.9 rillig '.ifndef VARIABLE_TWO_TIMES_2' \
317 1.9 rillig 'VARIABLE_TWO_TIMES_2=' \
318 1.1 rillig '.endif'
319 1.19 rillig # expect: Parse_PushInput: variable-two-times.tmp:1
320 1.19 rillig # expect: Parse_PushInput: variable-two-times.tmp:1
321 1.1 rillig
322 1.9 rillig # When multiple files use the same guard variable name, the optimization of
323 1.10 rillig # skipping the file affects each of these files.
324 1.10 rillig #
325 1.9 rillig # Choosing unique guard names is the responsibility of the makefile authors.
326 1.9 rillig # A typical pattern of guard variable names is '${PROJECT}_${DIR}_${FILE}_MK'.
327 1.9 rillig # System-provided files typically start the guard names with '_'.
328 1.14 rillig CASES+= variable-clash
329 1.9 rillig LINES.variable-clash= \
330 1.9 rillig ${LINES.variable-if}
331 1.19 rillig # expect: Parse_PushInput: variable-clash.tmp:1
332 1.10 rillig # expect: Skipping 'variable-clash.tmp' because 'VARIABLE_IF' is defined
333 1.1 rillig
334 1.1 rillig # The conditional must come before the assignment, otherwise the conditional
335 1.1 rillig # is useless, as it always evaluates to false.
336 1.14 rillig CASES+= variable-swapped
337 1.9 rillig LINES.variable-swapped= \
338 1.1 rillig 'SWAPPED=' \
339 1.1 rillig '.ifndef SWAPPED' \
340 1.9 rillig '. error' \
341 1.1 rillig '.endif'
342 1.19 rillig # expect: Parse_PushInput: variable-swapped.tmp:1
343 1.19 rillig # expect: Parse_PushInput: variable-swapped.tmp:1
344 1.1 rillig
345 1.9 rillig # If the guard variable is undefined between the first and the second time the
346 1.9 rillig # file is included, the guarded file is included again.
347 1.14 rillig CASES+= variable-undef-between
348 1.9 rillig LINES.variable-undef-between= \
349 1.9 rillig '.ifndef VARIABLE_UNDEF_BETWEEN' \
350 1.9 rillig 'VARIABLE_UNDEF_BETWEEN=' \
351 1.9 rillig '.endif'
352 1.9 rillig UNDEF_BETWEEN.variable-undef-between= \
353 1.9 rillig VARIABLE_UNDEF_BETWEEN
354 1.19 rillig # expect: Parse_PushInput: variable-undef-between.tmp:1
355 1.19 rillig # expect: Parse_PushInput: variable-undef-between.tmp:1
356 1.9 rillig
357 1.9 rillig # If the guard variable is undefined while the file is included the first
358 1.9 rillig # time, the guard does not have an effect, and the file is included again.
359 1.14 rillig CASES+= variable-undef-inside
360 1.9 rillig LINES.variable-undef-inside= \
361 1.9 rillig '.ifndef VARIABLE_UNDEF_INSIDE' \
362 1.9 rillig 'VARIABLE_UNDEF_INSIDE=' \
363 1.9 rillig '.undef VARIABLE_UNDEF_INSIDE' \
364 1.9 rillig '.endif'
365 1.19 rillig # expect: Parse_PushInput: variable-undef-inside.tmp:1
366 1.19 rillig # expect: Parse_PushInput: variable-undef-inside.tmp:1
367 1.9 rillig
368 1.9 rillig # If the file does not define the guard variable, the guard does not have an
369 1.9 rillig # effect, and the file is included again.
370 1.14 rillig CASES+= variable-not-defined
371 1.9 rillig LINES.variable-not-defined= \
372 1.9 rillig '.ifndef VARIABLE_NOT_DEFINED' \
373 1.2 rillig '.endif'
374 1.19 rillig # expect: Parse_PushInput: variable-not-defined.tmp:1
375 1.19 rillig # expect: Parse_PushInput: variable-not-defined.tmp:1
376 1.2 rillig
377 1.2 rillig # The outermost '.if' must not have an '.elif' branch.
378 1.14 rillig CASES+= elif
379 1.12 rillig LINES.elif= \
380 1.12 rillig '.ifndef ELIF' \
381 1.12 rillig 'ELIF=' \
382 1.2 rillig '.elif 1' \
383 1.2 rillig '.endif'
384 1.19 rillig # expect: Parse_PushInput: elif.tmp:1
385 1.19 rillig # expect: Parse_PushInput: elif.tmp:1
386 1.2 rillig
387 1.10 rillig # When a file with an '.if/.elif/.endif' conditional at the top level is
388 1.10 rillig # included, it is never optimized, as one of its branches is taken.
389 1.14 rillig CASES+= elif-reuse
390 1.12 rillig LINES.elif-reuse= \
391 1.12 rillig '.ifndef ELIF' \
392 1.10 rillig 'syntax error' \
393 1.10 rillig '.elif 1' \
394 1.10 rillig '.endif'
395 1.19 rillig # expect: Parse_PushInput: elif-reuse.tmp:1
396 1.19 rillig # expect: Parse_PushInput: elif-reuse.tmp:1
397 1.10 rillig
398 1.2 rillig # The outermost '.if' must not have an '.else' branch.
399 1.14 rillig CASES+= else
400 1.12 rillig LINES.else= \
401 1.12 rillig '.ifndef ELSE' \
402 1.12 rillig 'ELSE=' \
403 1.2 rillig '.else' \
404 1.2 rillig '.endif'
405 1.19 rillig # expect: Parse_PushInput: else.tmp:1
406 1.19 rillig # expect: Parse_PushInput: else.tmp:1
407 1.2 rillig
408 1.10 rillig # When a file with an '.if/.else/.endif' conditional at the top level is
409 1.10 rillig # included, it is never optimized, as one of its branches is taken.
410 1.14 rillig CASES+= else-reuse
411 1.12 rillig LINES.else-reuse= \
412 1.12 rillig '.ifndef ELSE' \
413 1.10 rillig 'syntax error' \
414 1.10 rillig '.else' \
415 1.10 rillig '.endif'
416 1.19 rillig # expect: Parse_PushInput: else-reuse.tmp:1
417 1.19 rillig # expect: Parse_PushInput: else-reuse.tmp:1
418 1.10 rillig
419 1.9 rillig # The inner '.if' directives may have an '.elif' or '.else', and it doesn't
420 1.9 rillig # matter which of their branches are taken.
421 1.14 rillig CASES+= inner-if-elif-else
422 1.10 rillig LINES.inner-if-elif-else= \
423 1.2 rillig '.ifndef INNER_IF_ELIF_ELSE' \
424 1.2 rillig 'INNER_IF_ELIF_ELSE=' \
425 1.2 rillig '. if 0' \
426 1.2 rillig '. elif 0' \
427 1.2 rillig '. else' \
428 1.2 rillig '. endif' \
429 1.2 rillig '. if 0' \
430 1.2 rillig '. elif 1' \
431 1.2 rillig '. else' \
432 1.2 rillig '. endif' \
433 1.2 rillig '. if 1' \
434 1.2 rillig '. elif 1' \
435 1.2 rillig '. else' \
436 1.2 rillig '. endif' \
437 1.2 rillig '.endif'
438 1.19 rillig # expect: Parse_PushInput: inner-if-elif-else.tmp:1
439 1.7 rillig # expect: Skipping 'inner-if-elif-else.tmp' because 'INNER_IF_ELIF_ELSE' is defined
440 1.7 rillig
441 1.9 rillig # The guard can also be a target instead of a variable. Using a target as a
442 1.9 rillig # guard has the benefit that a target cannot be undefined once it is defined.
443 1.9 rillig # The target should be declared '.NOTMAIN'. Since the target names are
444 1.9 rillig # usually chosen according to a pattern that doesn't interfere with real
445 1.9 rillig # target names, they don't need to be declared '.PHONY' as they don't generate
446 1.9 rillig # filesystem operations.
447 1.14 rillig CASES+= target
448 1.7 rillig LINES.target= \
449 1.7 rillig '.if !target(__target.tmp__)' \
450 1.9 rillig '__target.tmp__: .NOTMAIN' \
451 1.7 rillig '.endif'
452 1.19 rillig # expect: Parse_PushInput: target.tmp:1
453 1.7 rillig # expect: Skipping 'target.tmp' because '__target.tmp__' is defined
454 1.7 rillig
455 1.9 rillig # When used for system files, the target name may include '<' and '>', for
456 1.9 rillig # symmetry with the '.include <sys.mk>' directive. The characters '<' and '>'
457 1.9 rillig # are ordinary characters.
458 1.14 rillig CASES+= target-sys
459 1.7 rillig LINES.target-sys= \
460 1.7 rillig '.if !target(__<target-sys.tmp>__)' \
461 1.9 rillig '__<target-sys.tmp>__: .NOTMAIN' \
462 1.7 rillig '.endif'
463 1.19 rillig # expect: Parse_PushInput: target-sys.tmp:1
464 1.7 rillig # expect: Skipping 'target-sys.tmp' because '__<target-sys.tmp>__' is defined
465 1.7 rillig
466 1.9 rillig # The target name may include variable references. These references are
467 1.9 rillig # expanded as usual. Due to the current implementation, the expressions are
468 1.9 rillig # evaluated twice: Once for checking whether the condition evaluates to true,
469 1.9 rillig # and once for determining the guard name. This double evaluation should not
470 1.9 rillig # matter in practice, as guard expressions are expected to be simple,
471 1.9 rillig # deterministic and without side effects.
472 1.14 rillig CASES+= target-indirect
473 1.7 rillig LINES.target-indirect= \
474 1.7 rillig '.if !target($${target-indirect.tmp:L})' \
475 1.9 rillig 'target-indirect.tmp: .NOTMAIN' \
476 1.7 rillig '.endif'
477 1.19 rillig # expect: Parse_PushInput: target-indirect.tmp:1
478 1.8 sjg # expect: Skipping 'target-indirect.tmp' because 'target-indirect.tmp' is defined
479 1.8 sjg
480 1.9 rillig # A common form of guard target is __${.PARSEFILE}__. This form can only be
481 1.9 rillig # used if all files using this form have unique basenames. To get a robust
482 1.9 rillig # pattern based on the same idea, use __${.PARSEDIR}/${.PARSEFILE}__ instead.
483 1.9 rillig # This form does not work when the basename contains whitespace characters, as
484 1.9 rillig # it is not possible to define a target with whitespace, not even by cheating.
485 1.14 rillig CASES+= target-indirect-PARSEFILE
486 1.8 sjg LINES.target-indirect-PARSEFILE= \
487 1.8 sjg '.if !target(__$${.PARSEFILE}__)' \
488 1.8 sjg '__$${.PARSEFILE}__: .NOTMAIN' \
489 1.8 sjg '.endif'
490 1.19 rillig # expect: Parse_PushInput: target-indirect-PARSEFILE.tmp:1
491 1.8 sjg # expect: Skipping 'target-indirect-PARSEFILE.tmp' because '__target-indirect-PARSEFILE.tmp__' is defined
492 1.8 sjg
493 1.9 rillig # Two files with different basenames can both use the same syntactic pattern
494 1.9 rillig # for the target guard name, as the expressions expand to different strings.
495 1.14 rillig CASES+= target-indirect-PARSEFILE2
496 1.8 sjg LINES.target-indirect-PARSEFILE2= \
497 1.8 sjg '.if !target(__$${.PARSEFILE}__)' \
498 1.8 sjg '__$${.PARSEFILE}__: .NOTMAIN' \
499 1.8 sjg '.endif'
500 1.19 rillig # expect: Parse_PushInput: target-indirect-PARSEFILE2.tmp:1
501 1.8 sjg # expect: Skipping 'target-indirect-PARSEFILE2.tmp' because '__target-indirect-PARSEFILE2.tmp__' is defined
502 1.8 sjg
503 1.9 rillig # Using plain .PARSEFILE without .PARSEDIR leads to name clashes. The include
504 1.10 rillig # guard is the same as in the test case 'target-indirect-PARSEFILE', as the
505 1.12 rillig # guard name only contains the basename but not the directory name. So even
506 1.15 rillig # without defining the guard target, the file is considered guarded.
507 1.14 rillig CASES+= subdir/target-indirect-PARSEFILE
508 1.9 rillig LINES.subdir/target-indirect-PARSEFILE= \
509 1.9 rillig '.if !target(__$${.PARSEFILE}__)' \
510 1.9 rillig '.endif'
511 1.19 rillig # expect: Parse_PushInput: subdir/target-indirect-PARSEFILE.tmp:1
512 1.10 rillig # expect: Skipping 'subdir/target-indirect-PARSEFILE.tmp' because '__target-indirect-PARSEFILE.tmp__' is defined
513 1.9 rillig
514 1.11 sjg # Another common form of guard target is __${.PARSEDIR}/${.PARSEFILE}__
515 1.12 rillig # or __${.PARSEDIR:tA}/${.PARSEFILE}__ to be truly unique.
516 1.14 rillig CASES+= target-indirect-PARSEDIR-PARSEFILE
517 1.9 rillig LINES.target-indirect-PARSEDIR-PARSEFILE= \
518 1.9 rillig '.if !target(__$${.PARSEDIR}/$${.PARSEFILE}__)' \
519 1.9 rillig '__$${.PARSEDIR}/$${.PARSEFILE}__: .NOTMAIN' \
520 1.9 rillig '.endif'
521 1.19 rillig # expect: Parse_PushInput: target-indirect-PARSEDIR-PARSEFILE.tmp:1
522 1.9 rillig # expect: Skipping 'target-indirect-PARSEDIR-PARSEFILE.tmp' because '__target-indirect-PARSEDIR-PARSEFILE.tmp__' is defined
523 1.9 rillig # The actual target starts with '__${.OBJDIR}/', see the .rawout file, but the
524 1.9 rillig # string '${.OBJDIR}/' gets stripped in post processing.
525 1.9 rillig
526 1.9 rillig # Using the combination of '.PARSEDIR' and '.PARSEFILE', a file in a
527 1.9 rillig # subdirectory gets a different guard target name than the previous one.
528 1.14 rillig CASES+= subdir/target-indirect-PARSEDIR-PARSEFILE
529 1.9 rillig LINES.subdir/target-indirect-PARSEDIR-PARSEFILE= \
530 1.9 rillig '.if !target(__$${.PARSEDIR}/$${.PARSEFILE}__)' \
531 1.9 rillig '__$${.PARSEDIR}/$${.PARSEFILE}__: .NOTMAIN' \
532 1.9 rillig '.endif'
533 1.19 rillig # expect: Parse_PushInput: subdir/target-indirect-PARSEDIR-PARSEFILE.tmp:1
534 1.9 rillig # expect: Skipping 'subdir/target-indirect-PARSEDIR-PARSEFILE.tmp' because '__subdir/target-indirect-PARSEDIR-PARSEFILE.tmp__' is defined
535 1.9 rillig # The actual target starts with '__${.OBJDIR}/', see the .rawout file, but the
536 1.9 rillig # string '${.OBJDIR}/' gets stripped in post processing.
537 1.9 rillig
538 1.9 rillig # If the guard target is not defined when including the file the next time,
539 1.9 rillig # the file is processed again.
540 1.14 rillig CASES+= target-unguarded
541 1.7 rillig LINES.target-unguarded= \
542 1.7 rillig '.if !target(target-unguarded)' \
543 1.7 rillig '.endif'
544 1.19 rillig # expect: Parse_PushInput: target-unguarded.tmp:1
545 1.19 rillig # expect: Parse_PushInput: target-unguarded.tmp:1
546 1.7 rillig
547 1.7 rillig # The guard condition must consist of only the guard target, nothing else.
548 1.14 rillig CASES+= target-plus
549 1.7 rillig LINES.target-plus= \
550 1.7 rillig '.if !target(target-plus) && 1' \
551 1.9 rillig 'target-plus: .NOTMAIN' \
552 1.7 rillig '.endif'
553 1.19 rillig # expect: Parse_PushInput: target-plus.tmp:1
554 1.19 rillig # expect: Parse_PushInput: target-plus.tmp:1
555 1.7 rillig
556 1.9 rillig # If the guard target is defined before the file is included the first time,
557 1.12 rillig # the file is read once and then considered guarded.
558 1.14 rillig CASES+= target-already-defined
559 1.10 rillig LINES.target-already-defined= \
560 1.10 rillig '.if !target(target-already-defined)' \
561 1.10 rillig 'target-already-defined: .NOTMAIN' \
562 1.10 rillig '.endif'
563 1.10 rillig target-already-defined: .NOTMAIN
564 1.19 rillig # expect: Parse_PushInput: target-already-defined.tmp:1
565 1.10 rillig # expect: Skipping 'target-already-defined.tmp' because 'target-already-defined' is defined
566 1.5 rillig
567 1.9 rillig # A target name cannot contain the character '!'. In the condition, the '!'
568 1.9 rillig # is syntactically valid, but in the dependency declaration line, the '!' is
569 1.9 rillig # interpreted as the '!' dependency operator, no matter whether it occurs at
570 1.9 rillig # the beginning or in the middle of a target name. Escaping it as '${:U!}'
571 1.9 rillig # doesn't work, as the whole line is first expanded and then scanned for the
572 1.9 rillig # dependency operator. Escaping it as '\!' doesn't work either, even though
573 1.9 rillig # the '\' escapes the '!' from being a dependency operator, but when reading
574 1.9 rillig # the target name, the '\' is kept, resulting in the target name
575 1.9 rillig # '\!target-name-exclamation' instead of '!target-name-exclamation'.
576 1.14 rillig CASES+= target-name-exclamation
577 1.9 rillig LINES.target-name-exclamation= \
578 1.9 rillig '.if !target(!target-name-exclamation)' \
579 1.9 rillig '\!target-name-exclamation: .NOTMAIN' \
580 1.9 rillig '.endif'
581 1.19 rillig # expect: Parse_PushInput: target-name-exclamation.tmp:1
582 1.19 rillig # expect: Parse_PushInput: target-name-exclamation.tmp:1
583 1.9 rillig
584 1.18 rillig # If the guard target name has leading spaces, it does not have an effect,
585 1.15 rillig # as that form is not common in practice.
586 1.17 rillig CASES+= target-name-leading-space
587 1.17 rillig LINES.target-name-leading-space= \
588 1.17 rillig '.if !target( target-name-leading-space)' \
589 1.17 rillig 'target-name-leading-space: .NOTMAIN' \
590 1.17 rillig '.endif'
591 1.19 rillig # expect: Parse_PushInput: target-name-leading-space.tmp:1
592 1.19 rillig # expect: Parse_PushInput: target-name-leading-space.tmp:1
593 1.17 rillig
594 1.18 rillig # If the guard target name has trailing spaces, it does not have an effect,
595 1.18 rillig # as that form is not common in practice.
596 1.17 rillig CASES+= target-name-trailing-space
597 1.17 rillig LINES.target-name-trailing-space= \
598 1.17 rillig '.if !target(target-name-trailing-space )' \
599 1.17 rillig 'target-name-trailing-space: .NOTMAIN' \
600 1.15 rillig '.endif'
601 1.19 rillig # expect: Parse_PushInput: target-name-trailing-space.tmp:1
602 1.19 rillig # expect: Parse_PushInput: target-name-trailing-space.tmp:1
603 1.15 rillig
604 1.15 rillig # If the guard target condition is enclosed in parentheses, it does not have
605 1.15 rillig # an effect, as that form is not common in practice.
606 1.15 rillig CASES+= target-call-parenthesized
607 1.15 rillig LINES.target-call-parenthesized= \
608 1.15 rillig '.if (!target(target-call-parenthesized))' \
609 1.15 rillig 'target-call-parenthesized: .NOTMAIN' \
610 1.15 rillig '.endif'
611 1.19 rillig # expect: Parse_PushInput: target-call-parenthesized.tmp:1
612 1.19 rillig # expect: Parse_PushInput: target-call-parenthesized.tmp:1
613 1.15 rillig
614 1.16 rillig # If the '.if' or '.ifndef' directive spans more than a single line, it is
615 1.16 rillig # still recognized as a guard condition. This case is entirely uncommon, but
616 1.16 rillig # at the point where the guard condition is checked, line continuations have
617 1.16 rillig # already been converted to spaces.
618 1.16 rillig CASES+= multiline
619 1.16 rillig LINES.multiline= \
620 1.16 rillig '.\' \
621 1.16 rillig ' ifndef \' \
622 1.16 rillig ' MULTILINE' \
623 1.16 rillig 'MULTILINE=' \
624 1.16 rillig '.endif'
625 1.19 rillig # expect: Parse_PushInput: multiline.tmp:1
626 1.16 rillig # expect: Skipping 'multiline.tmp' because 'MULTILINE' is defined
627 1.16 rillig
628 1.12 rillig
629 1.9 rillig # Now run all test cases by including each of the files twice and looking at
630 1.9 rillig # the debug output. The files that properly guard against multiple inclusion
631 1.9 rillig # generate a 'Skipping' line, the others repeat the 'Parse_PushInput' line.
632 1.1 rillig #
633 1.1 rillig # Some debug output lines are suppressed in the .exp file, see ./Makefile.
634 1.14 rillig .for i in ${CASES}
635 1.4 rillig . for fname in $i.tmp
636 1.9 rillig _:= ${fname:H:N.:@dir@${:!mkdir -p ${dir}!}@}
637 1.1 rillig _!= printf '%s\n' ${LINES.$i} > ${fname}
638 1.1 rillig .MAKEFLAGS: -dp
639 1.1 rillig .include "${.CURDIR}/${fname}"
640 1.9 rillig .undef ${UNDEF_BETWEEN.$i:U}
641 1.1 rillig .include "${.CURDIR}/${fname}"
642 1.1 rillig .MAKEFLAGS: -d0
643 1.1 rillig _!= rm ${fname}
644 1.9 rillig _:= ${fname:H:N.:@dir@${:!rmdir ${dir}!}@}
645 1.1 rillig . endfor
646 1.1 rillig .endfor
647 1.1 rillig
648 1.1 rillig all:
649