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