Home | History | Annotate | Line # | Download | only in unit-tests
directive-include-guard.mk revision 1.7
      1  1.7  rillig # $NetBSD: directive-include-guard.mk,v 1.7 2023/06/20 09:25:34 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.1  rillig 
     25  1.1  rillig # This is the canonical form of a multiple-inclusion guard.
     26  1.1  rillig INCS+=	guarded-ifndef
     27  1.1  rillig LINES.guarded-ifndef= \
     28  1.1  rillig 	'.ifndef GUARDED_IFNDEF' \
     29  1.1  rillig 	'GUARDED_IFNDEF=' \
     30  1.1  rillig 	'.endif'
     31  1.4  rillig # expect: Parse_PushInput: file guarded-ifndef.tmp, line 1
     32  1.7  rillig # expect: Skipping 'guarded-ifndef.tmp' because 'GUARDED_IFNDEF' is defined
     33  1.1  rillig 
     34  1.1  rillig # Comments and empty lines have no influence on the multiple-inclusion guard.
     35  1.1  rillig INCS+=	comments
     36  1.1  rillig LINES.comments= \
     37  1.1  rillig 	'\# comment' \
     38  1.1  rillig 	'' \
     39  1.3  rillig 	'.ifndef COMMENTS' \
     40  1.1  rillig 	'\# comment' \
     41  1.3  rillig 	'COMMENTS=\#comment' \
     42  1.1  rillig 	'.endif' \
     43  1.1  rillig 	'\# comment'
     44  1.4  rillig # expect: Parse_PushInput: file comments.tmp, line 1
     45  1.7  rillig # expect: Skipping 'comments.tmp' because 'COMMENTS' is defined
     46  1.1  rillig 
     47  1.1  rillig # An alternative form uses the 'defined' function.  It is more verbose than
     48  1.1  rillig # the canonical form.  There are other possible forms as well, such as with a
     49  1.1  rillig # triple negation, but these are not recognized as they are not common.
     50  1.1  rillig INCS+=	guarded-if
     51  1.1  rillig LINES.guarded-if= \
     52  1.1  rillig 	'.if !defined(GUARDED_IF)' \
     53  1.1  rillig 	'GUARDED_IF=' \
     54  1.1  rillig 	'.endif'
     55  1.4  rillig # expect: Parse_PushInput: file guarded-if.tmp, line 1
     56  1.7  rillig # expect: Skipping 'guarded-if.tmp' because 'GUARDED_IF' is defined
     57  1.1  rillig 
     58  1.1  rillig # Triple negation is so uncommon that it's not recognized.
     59  1.1  rillig INCS+=	triple-negation
     60  1.1  rillig LINES.triple-negation= \
     61  1.1  rillig 	'.if !!!defined(TRIPLE_NEGATION)' \
     62  1.1  rillig 	'TRIPLE_NEGATION=' \
     63  1.1  rillig 	'.endif'
     64  1.4  rillig # expect: Parse_PushInput: file triple-negation.tmp, line 1
     65  1.4  rillig # expect: Parse_PushInput: file triple-negation.tmp, line 1
     66  1.1  rillig 
     67  1.6  rillig # A conditional other than '.if' or '.ifndef' marks the file as non-guarded,
     68  1.6  rillig # even if it would actually work as a multiple-inclusion guard.
     69  1.6  rillig INCS+=	ifdef-negated
     70  1.6  rillig LINES.ifdef-negated= \
     71  1.6  rillig 	'.ifdef !IFDEF_NEGATED' \
     72  1.6  rillig 	'IFDEF_NEGATED=' \
     73  1.6  rillig 	'.endif'
     74  1.6  rillig # expect: Parse_PushInput: file ifdef-negated.tmp, line 1
     75  1.6  rillig # expect: Parse_PushInput: file ifdef-negated.tmp, line 1
     76  1.6  rillig 
     77  1.1  rillig # The variable names in the '.if' and the assignment must be the same.
     78  1.1  rillig INCS+=	varname-mismatch
     79  1.1  rillig LINES.varname-mismatch= \
     80  1.1  rillig 	'.ifndef VARNAME_MISMATCH' \
     81  1.1  rillig 	'OTHER_NAME=' \
     82  1.1  rillig 	'.endif'
     83  1.4  rillig # expect: Parse_PushInput: file varname-mismatch.tmp, line 1
     84  1.4  rillig # expect: Parse_PushInput: file varname-mismatch.tmp, line 1
     85  1.1  rillig 
     86  1.6  rillig # The guard condition must consist of only the guard variable, nothing else.
     87  1.6  rillig INCS+=	ifndef-plus
     88  1.6  rillig LINES.ifndef-plus= \
     89  1.6  rillig 	'.ifndef IFNDEF_PLUS && IFNDEF_SECOND' \
     90  1.6  rillig 	'IFNDEF_PLUS=' \
     91  1.6  rillig 	'IFNDEF_SECOND=' \
     92  1.6  rillig 	'.endif'
     93  1.6  rillig # expect: Parse_PushInput: file ifndef-plus.tmp, line 1
     94  1.6  rillig # expect: Parse_PushInput: file ifndef-plus.tmp, line 1
     95  1.6  rillig 
     96  1.6  rillig # The guard condition must consist of only the guard variable, nothing else.
     97  1.6  rillig INCS+=	if-plus
     98  1.6  rillig LINES.if-plus= \
     99  1.6  rillig 	'.if !defined(IF_PLUS) && !defined(IF_SECOND)' \
    100  1.6  rillig 	'IF_PLUS=' \
    101  1.6  rillig 	'IF_SECOND=' \
    102  1.6  rillig 	'.endif'
    103  1.6  rillig # expect: Parse_PushInput: file if-plus.tmp, line 1
    104  1.6  rillig # expect: Parse_PushInput: file if-plus.tmp, line 1
    105  1.6  rillig 
    106  1.6  rillig # The variable name in an '.ifndef' guard must be given directly, it must not
    107  1.6  rillig # contain any '$' expression.
    108  1.6  rillig INCS+=	ifndef-indirect
    109  1.6  rillig LINES.ifndef-indirect= \
    110  1.6  rillig 	'.ifndef $${IFNDEF_INDIRECT:L}' \
    111  1.6  rillig 	'IFNDEF_INDIRECT=' \
    112  1.6  rillig 	'.endif'
    113  1.6  rillig # expect: Parse_PushInput: file ifndef-indirect.tmp, line 1
    114  1.6  rillig # expect: Parse_PushInput: file ifndef-indirect.tmp, line 1
    115  1.6  rillig 
    116  1.6  rillig # The variable name in an '.if' guard must be given directly, it must not contain
    117  1.6  rillig # any '$' expression.
    118  1.6  rillig INCS+=	if-indirect
    119  1.6  rillig LINES.if-indirect= \
    120  1.6  rillig 	'.if !defined($${IF_INDIRECT:L})' \
    121  1.6  rillig 	'IF_INDIRECT=' \
    122  1.6  rillig 	'.endif'
    123  1.6  rillig # expect: Parse_PushInput: file if-indirect.tmp, line 1
    124  1.6  rillig # expect: Parse_PushInput: file if-indirect.tmp, line 1
    125  1.6  rillig 
    126  1.5  rillig # The variable name in the guard condition must only contain alphanumeric
    127  1.5  rillig # characters and underscores.  The guard variable is more flexible, it can be
    128  1.5  rillig # set anywhere, as long as it is set when the guarded file is included next.
    129  1.6  rillig INCS+=	varassign-indirect
    130  1.6  rillig LINES.varassign-indirect= \
    131  1.6  rillig 	'.ifndef VARASSIGN_INDIRECT' \
    132  1.6  rillig 	'$${VARASSIGN_INDIRECT:L}=' \
    133  1.1  rillig 	'.endif'
    134  1.6  rillig # expect: Parse_PushInput: file varassign-indirect.tmp, line 1
    135  1.7  rillig # expect: Skipping 'varassign-indirect.tmp' because 'VARASSIGN_INDIRECT' is defined
    136  1.1  rillig 
    137  1.5  rillig # The time at which the guard variable is set doesn't matter, as long as it is
    138  1.5  rillig # set when the file is included the next time.
    139  1.1  rillig INCS+=	late-assignment
    140  1.1  rillig LINES.late-assignment= \
    141  1.1  rillig 	'.ifndef LATE_ASSIGNMENT' \
    142  1.1  rillig 	'OTHER=' \
    143  1.1  rillig 	'LATE_ASSIGNMENT=' \
    144  1.1  rillig 	'.endif'
    145  1.4  rillig # expect: Parse_PushInput: file late-assignment.tmp, line 1
    146  1.7  rillig # expect: Skipping 'late-assignment.tmp' because 'LATE_ASSIGNMENT' is defined
    147  1.1  rillig 
    148  1.5  rillig # The time at which the guard variable is set doesn't matter, as long as it is
    149  1.5  rillig # set when the file is included the next time.
    150  1.1  rillig INCS+=	two-conditions
    151  1.1  rillig LINES.two-conditions= \
    152  1.1  rillig 	'.ifndef TWO_CONDITIONS' \
    153  1.2  rillig 	'.  if 1' \
    154  1.1  rillig 	'TWO_CONDITIONS=' \
    155  1.1  rillig 	'.  endif' \
    156  1.1  rillig 	'.endif'
    157  1.4  rillig # expect: Parse_PushInput: file two-conditions.tmp, line 1
    158  1.7  rillig # expect: Skipping 'two-conditions.tmp' because 'TWO_CONDITIONS' is defined
    159  1.1  rillig 
    160  1.7  rillig # If the guard variable is defined before the file is included for the first
    161  1.7  rillig # time, the file is not considered guarded.
    162  1.1  rillig INCS+=	already-set
    163  1.1  rillig LINES.already-set= \
    164  1.1  rillig 	'.ifndef ALREADY_SET' \
    165  1.1  rillig 	'ALREADY_SET=' \
    166  1.1  rillig 	'.endif'
    167  1.1  rillig ALREADY_SET=
    168  1.4  rillig # expect: Parse_PushInput: file already-set.tmp, line 1
    169  1.4  rillig # expect: Parse_PushInput: file already-set.tmp, line 1
    170  1.1  rillig 
    171  1.1  rillig # The whole file content must be guarded by a single '.if' conditional, not by
    172  1.1  rillig # several, even if they have the same effect.
    173  1.1  rillig INCS+=	twice
    174  1.1  rillig LINES.twice= \
    175  1.5  rillig 	'.ifndef TWICE_FIRST' \
    176  1.5  rillig 	'TWICE_FIRST=' \
    177  1.1  rillig 	'.endif' \
    178  1.5  rillig 	'.ifndef TWICE_SECOND' \
    179  1.5  rillig 	'TWICE_SECOND=' \
    180  1.1  rillig 	'.endif'
    181  1.4  rillig # expect: Parse_PushInput: file twice.tmp, line 1
    182  1.4  rillig # expect: Parse_PushInput: file twice.tmp, line 1
    183  1.1  rillig 
    184  1.1  rillig # When multiple files use the same guard variable name, they exclude each
    185  1.5  rillig # other.  It's the responsibility of the makefile authors to choose unique
    186  1.5  rillig # variable names.  Typical choices are ${PROJECT}_${DIR}_${FILE}_MK.  This is
    187  1.5  rillig # the same situation as in the 'already-set' test, and the file is not
    188  1.5  rillig # considered guarded.
    189  1.1  rillig INCS+=	reuse
    190  1.1  rillig LINES.reuse= \
    191  1.1  rillig 	${LINES.guarded-if}
    192  1.4  rillig # expect: Parse_PushInput: file reuse.tmp, line 1
    193  1.4  rillig # expect: Parse_PushInput: file reuse.tmp, line 1
    194  1.1  rillig 
    195  1.1  rillig # The conditional must come before the assignment, otherwise the conditional
    196  1.1  rillig # is useless, as it always evaluates to false.
    197  1.1  rillig INCS+=	swapped
    198  1.1  rillig LINES.swapped= \
    199  1.1  rillig 	'SWAPPED=' \
    200  1.1  rillig 	'.ifndef SWAPPED' \
    201  1.1  rillig 	'.endif'
    202  1.4  rillig # expect: Parse_PushInput: file swapped.tmp, line 1
    203  1.4  rillig # expect: Parse_PushInput: file swapped.tmp, line 1
    204  1.1  rillig 
    205  1.2  rillig # If the guard variable is undefined at some later point, the guarded file is
    206  1.2  rillig # included again.
    207  1.2  rillig INCS+=	undef-between
    208  1.2  rillig LINES.undef-between= \
    209  1.2  rillig 	'.ifndef UNDEF_BETWEEN' \
    210  1.2  rillig 	'UNDEF_BETWEEN=' \
    211  1.2  rillig 	'.endif'
    212  1.4  rillig # expect: Parse_PushInput: file undef-between.tmp, line 1
    213  1.4  rillig # expect: Parse_PushInput: file undef-between.tmp, line 1
    214  1.2  rillig 
    215  1.2  rillig # If the guarded file undefines the guard variable, the guarded file is
    216  1.2  rillig # included again.
    217  1.2  rillig INCS+=	undef-inside
    218  1.2  rillig LINES.undef-inside= \
    219  1.2  rillig 	'.ifndef UNDEF_INSIDE' \
    220  1.2  rillig 	'UNDEF_INSIDE=' \
    221  1.2  rillig 	'.undef UNDEF_INSIDE' \
    222  1.2  rillig 	'.endif'
    223  1.4  rillig # expect: Parse_PushInput: file undef-inside.tmp, line 1
    224  1.4  rillig # expect: Parse_PushInput: file undef-inside.tmp, line 1
    225  1.2  rillig 
    226  1.2  rillig # The outermost '.if' must not have an '.elif' branch.
    227  1.2  rillig INCS+=	if-elif
    228  1.2  rillig LINES.if-elif = \
    229  1.2  rillig 	'.ifndef IF_ELIF' \
    230  1.2  rillig 	'IF_ELIF=' \
    231  1.2  rillig 	'.elif 1' \
    232  1.2  rillig 	'.endif'
    233  1.4  rillig # expect: Parse_PushInput: file if-elif.tmp, line 1
    234  1.4  rillig # expect: Parse_PushInput: file if-elif.tmp, line 1
    235  1.2  rillig 
    236  1.2  rillig # The outermost '.if' must not have an '.else' branch.
    237  1.2  rillig INCS+=	if-else
    238  1.2  rillig LINES.if-else = \
    239  1.2  rillig 	'.ifndef IF_ELSE' \
    240  1.2  rillig 	'IF_ELSE=' \
    241  1.2  rillig 	'.else' \
    242  1.2  rillig 	'.endif'
    243  1.4  rillig # expect: Parse_PushInput: file if-else.tmp, line 1
    244  1.4  rillig # expect: Parse_PushInput: file if-else.tmp, line 1
    245  1.2  rillig 
    246  1.2  rillig # The inner '.if' directives may have an '.elif' or '.else'.
    247  1.2  rillig INCS+=	inner-if-elif-else
    248  1.2  rillig LINES.inner-if-elif-else = \
    249  1.2  rillig 	'.ifndef INNER_IF_ELIF_ELSE' \
    250  1.2  rillig 	'INNER_IF_ELIF_ELSE=' \
    251  1.2  rillig 	'.  if 0' \
    252  1.2  rillig 	'.  elif 0' \
    253  1.2  rillig 	'.  else' \
    254  1.2  rillig 	'.  endif' \
    255  1.2  rillig 	'.  if 0' \
    256  1.2  rillig 	'.  elif 1' \
    257  1.2  rillig 	'.  else' \
    258  1.2  rillig 	'.  endif' \
    259  1.2  rillig 	'.  if 1' \
    260  1.2  rillig 	'.  elif 1' \
    261  1.2  rillig 	'.  else' \
    262  1.2  rillig 	'.  endif' \
    263  1.2  rillig 	'.endif'
    264  1.4  rillig # expect: Parse_PushInput: file inner-if-elif-else.tmp, line 1
    265  1.7  rillig # expect: Skipping 'inner-if-elif-else.tmp' because 'INNER_IF_ELIF_ELSE' is defined
    266  1.7  rillig 
    267  1.7  rillig # The guard can not only be a variable, it can also be a target.
    268  1.7  rillig INCS+=	target
    269  1.7  rillig LINES.target= \
    270  1.7  rillig 	'.if !target(__target.tmp__)' \
    271  1.7  rillig 	'__target.tmp__: .PHONY' \
    272  1.7  rillig 	'.endif'
    273  1.7  rillig # expect: Parse_PushInput: file target.tmp, line 1
    274  1.7  rillig # expect: Skipping 'target.tmp' because '__target.tmp__' is defined
    275  1.7  rillig 
    276  1.7  rillig # When used for system files, the target name may include '<' and '>'.
    277  1.7  rillig INCS+=	target-sys
    278  1.7  rillig LINES.target-sys= \
    279  1.7  rillig 	'.if !target(__<target-sys.tmp>__)' \
    280  1.7  rillig 	'__<target-sys.tmp>__: .PHONY' \
    281  1.7  rillig 	'.endif'
    282  1.7  rillig # expect: Parse_PushInput: file target-sys.tmp, line 1
    283  1.7  rillig # expect: Skipping 'target-sys.tmp' because '__<target-sys.tmp>__' is defined
    284  1.7  rillig 
    285  1.7  rillig # The target name must not include '$' or other special characters.
    286  1.7  rillig INCS+=	target-indirect
    287  1.7  rillig LINES.target-indirect= \
    288  1.7  rillig 	'.if !target($${target-indirect.tmp:L})' \
    289  1.7  rillig 	'target-indirect.tmp: .PHONY' \
    290  1.7  rillig 	'.endif'
    291  1.7  rillig # expect: Parse_PushInput: file target-indirect.tmp, line 1
    292  1.7  rillig # expect: Parse_PushInput: file target-indirect.tmp, line 1
    293  1.7  rillig 
    294  1.7  rillig # If the target is not defined when including the file the next time, the file
    295  1.7  rillig # is not guarded.
    296  1.7  rillig INCS+=	target-unguarded
    297  1.7  rillig LINES.target-unguarded= \
    298  1.7  rillig 	'.if !target(target-unguarded)' \
    299  1.7  rillig 	'.endif'
    300  1.7  rillig # expect: Parse_PushInput: file target-unguarded.tmp, line 1
    301  1.7  rillig # expect: Parse_PushInput: file target-unguarded.tmp, line 1
    302  1.7  rillig 
    303  1.7  rillig # The guard condition must consist of only the guard target, nothing else.
    304  1.7  rillig INCS+=	target-plus
    305  1.7  rillig LINES.target-plus= \
    306  1.7  rillig 	'.if !target(target-plus) && 1' \
    307  1.7  rillig 	'target-plus: .PHONY' \
    308  1.7  rillig 	'.endif'
    309  1.7  rillig # expect: Parse_PushInput: file target-plus.tmp, line 1
    310  1.7  rillig # expect: Parse_PushInput: file target-plus.tmp, line 1
    311  1.7  rillig 
    312  1.7  rillig # If the guard target is defined before the file is included for the first
    313  1.7  rillig # time, the file is not considered guarded.
    314  1.7  rillig INCS+=	target-already-set
    315  1.7  rillig LINES.target-already-set= \
    316  1.7  rillig 	'.if !target(target-already-set)' \
    317  1.7  rillig 	'target-already-set: .PHONY' \
    318  1.7  rillig 	'.endif'
    319  1.7  rillig target-already-set: .PHONY
    320  1.7  rillig # expect: Parse_PushInput: file target-already-set.tmp, line 1
    321  1.7  rillig # expect: Parse_PushInput: file target-already-set.tmp, line 1
    322  1.5  rillig 
    323  1.1  rillig 
    324  1.1  rillig # Include each of the files twice.  The directive-include-guard.exp file
    325  1.1  rillig # contains a single entry for the files whose multiple-inclusion guard works,
    326  1.1  rillig # and two entries for the files that are not protected against multiple
    327  1.1  rillig # inclusion.
    328  1.1  rillig #
    329  1.1  rillig # Some debug output lines are suppressed in the .exp file, see ./Makefile.
    330  1.1  rillig .for i in ${INCS}
    331  1.4  rillig .  for fname in $i.tmp
    332  1.1  rillig _!=	printf '%s\n' ${LINES.$i} > ${fname}
    333  1.1  rillig .MAKEFLAGS: -dp
    334  1.1  rillig .include "${.CURDIR}/${fname}"
    335  1.2  rillig .undef ${i:Mundef-between:%=UNDEF_BETWEEN}
    336  1.1  rillig .include "${.CURDIR}/${fname}"
    337  1.1  rillig .MAKEFLAGS: -d0
    338  1.1  rillig _!=	rm ${fname}
    339  1.1  rillig .  endfor
    340  1.1  rillig .endfor
    341  1.1  rillig 
    342  1.1  rillig all:
    343