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