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