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