Home | History | Annotate | Line # | Download | only in unit-tests
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