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