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