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