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