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