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