directive-include-guard.mk revision 1.11 1 1.11 sjg # $NetBSD: directive-include-guard.mk,v 1.11 2023/06/21 21:21:52 sjg 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.11 sjg # Another common form of guard target is __${.PARSEDIR}/${.PARSEFILE}__
464 1.11 sjg # or __${.PARSEDIR:tA}/${.PARSEFILE}__ to be truely unique.
465 1.9 rillig INCS+= target-indirect-PARSEDIR-PARSEFILE
466 1.9 rillig LINES.target-indirect-PARSEDIR-PARSEFILE= \
467 1.9 rillig '.if !target(__$${.PARSEDIR}/$${.PARSEFILE}__)' \
468 1.9 rillig '__$${.PARSEDIR}/$${.PARSEFILE}__: .NOTMAIN' \
469 1.9 rillig '.endif'
470 1.9 rillig # expect: Parse_PushInput: file target-indirect-PARSEDIR-PARSEFILE.tmp, line 1
471 1.9 rillig # expect: Skipping 'target-indirect-PARSEDIR-PARSEFILE.tmp' because '__target-indirect-PARSEDIR-PARSEFILE.tmp__' is defined
472 1.9 rillig # The actual target starts with '__${.OBJDIR}/', see the .rawout file, but the
473 1.9 rillig # string '${.OBJDIR}/' gets stripped in post processing.
474 1.9 rillig
475 1.9 rillig # Using the combination of '.PARSEDIR' and '.PARSEFILE', a file in a
476 1.9 rillig # subdirectory gets a different guard target name than the previous one.
477 1.9 rillig INCS+= subdir/target-indirect-PARSEDIR-PARSEFILE
478 1.9 rillig LINES.subdir/target-indirect-PARSEDIR-PARSEFILE= \
479 1.9 rillig '.if !target(__$${.PARSEDIR}/$${.PARSEFILE}__)' \
480 1.9 rillig '__$${.PARSEDIR}/$${.PARSEFILE}__: .NOTMAIN' \
481 1.9 rillig '.endif'
482 1.9 rillig # expect: Parse_PushInput: file subdir/target-indirect-PARSEDIR-PARSEFILE.tmp, line 1
483 1.9 rillig # expect: Skipping 'subdir/target-indirect-PARSEDIR-PARSEFILE.tmp' because '__subdir/target-indirect-PARSEDIR-PARSEFILE.tmp__' is defined
484 1.9 rillig # The actual target starts with '__${.OBJDIR}/', see the .rawout file, but the
485 1.9 rillig # string '${.OBJDIR}/' gets stripped in post processing.
486 1.9 rillig
487 1.9 rillig # If the guard target is not defined when including the file the next time,
488 1.9 rillig # the file is processed again.
489 1.7 rillig INCS+= target-unguarded
490 1.7 rillig LINES.target-unguarded= \
491 1.7 rillig '.if !target(target-unguarded)' \
492 1.7 rillig '.endif'
493 1.7 rillig # expect: Parse_PushInput: file target-unguarded.tmp, line 1
494 1.7 rillig # expect: Parse_PushInput: file target-unguarded.tmp, line 1
495 1.7 rillig
496 1.7 rillig # The guard condition must consist of only the guard target, nothing else.
497 1.7 rillig INCS+= target-plus
498 1.7 rillig LINES.target-plus= \
499 1.7 rillig '.if !target(target-plus) && 1' \
500 1.9 rillig 'target-plus: .NOTMAIN' \
501 1.7 rillig '.endif'
502 1.7 rillig # expect: Parse_PushInput: file target-plus.tmp, line 1
503 1.7 rillig # expect: Parse_PushInput: file target-plus.tmp, line 1
504 1.7 rillig
505 1.9 rillig # If the guard target is defined before the file is included the first time,
506 1.9 rillig # the file is not considered guarded.
507 1.10 rillig INCS+= target-already-defined
508 1.10 rillig LINES.target-already-defined= \
509 1.10 rillig '.if !target(target-already-defined)' \
510 1.10 rillig 'target-already-defined: .NOTMAIN' \
511 1.10 rillig '.endif'
512 1.10 rillig target-already-defined: .NOTMAIN
513 1.10 rillig # expect: Parse_PushInput: file target-already-defined.tmp, line 1
514 1.10 rillig # expect: Skipping 'target-already-defined.tmp' because 'target-already-defined' is defined
515 1.5 rillig
516 1.9 rillig # A target name cannot contain the character '!'. In the condition, the '!'
517 1.9 rillig # is syntactically valid, but in the dependency declaration line, the '!' is
518 1.9 rillig # interpreted as the '!' dependency operator, no matter whether it occurs at
519 1.9 rillig # the beginning or in the middle of a target name. Escaping it as '${:U!}'
520 1.9 rillig # doesn't work, as the whole line is first expanded and then scanned for the
521 1.9 rillig # dependency operator. Escaping it as '\!' doesn't work either, even though
522 1.9 rillig # the '\' escapes the '!' from being a dependency operator, but when reading
523 1.9 rillig # the target name, the '\' is kept, resulting in the target name
524 1.9 rillig # '\!target-name-exclamation' instead of '!target-name-exclamation'.
525 1.9 rillig INCS+= target-name-exclamation
526 1.9 rillig LINES.target-name-exclamation= \
527 1.9 rillig '.if !target(!target-name-exclamation)' \
528 1.9 rillig '\!target-name-exclamation: .NOTMAIN' \
529 1.9 rillig '.endif'
530 1.9 rillig # expect: Parse_PushInput: file target-name-exclamation.tmp, line 1
531 1.9 rillig # expect: Parse_PushInput: file target-name-exclamation.tmp, line 1
532 1.9 rillig
533 1.9 rillig # Now run all test cases by including each of the files twice and looking at
534 1.9 rillig # the debug output. The files that properly guard against multiple inclusion
535 1.9 rillig # generate a 'Skipping' line, the others repeat the 'Parse_PushInput' line.
536 1.1 rillig #
537 1.1 rillig # Some debug output lines are suppressed in the .exp file, see ./Makefile.
538 1.1 rillig .for i in ${INCS}
539 1.4 rillig . for fname in $i.tmp
540 1.9 rillig _:= ${fname:H:N.:@dir@${:!mkdir -p ${dir}!}@}
541 1.1 rillig _!= printf '%s\n' ${LINES.$i} > ${fname}
542 1.1 rillig .MAKEFLAGS: -dp
543 1.1 rillig .include "${.CURDIR}/${fname}"
544 1.9 rillig .undef ${UNDEF_BETWEEN.$i:U}
545 1.1 rillig .include "${.CURDIR}/${fname}"
546 1.1 rillig .MAKEFLAGS: -d0
547 1.1 rillig _!= rm ${fname}
548 1.9 rillig _:= ${fname:H:N.:@dir@${:!rmdir ${dir}!}@}
549 1.1 rillig . endfor
550 1.1 rillig .endfor
551 1.1 rillig
552 1.1 rillig all:
553