directive-include-guard.mk revision 1.14 1 1.14 rillig # $NetBSD: directive-include-guard.mk,v 1.14 2023/12/17 08:53:55 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.9 rillig # expect: Parse_PushInput: file variable-ifndef.tmp, line 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.10 rillig # expect: Parse_PushInput: file variable-ifndef-reuse.tmp, line 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.13 rillig # expect: Parse_PushInput: file variable-ifndef-zero.tmp, line 1
58 1.13 rillig # expect: Parse_PushInput: file variable-ifndef-zero.tmp, line 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.13 rillig # expect: Parse_PushInput: file variable-ifndef-one.tmp, line 1
67 1.13 rillig # expect: Parse_PushInput: file variable-ifndef-one.tmp, line 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.4 rillig # expect: Parse_PushInput: file comments.tmp, line 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.9 rillig # expect: Parse_PushInput: file variable-if.tmp, line 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.10 rillig # expect: Parse_PushInput: file variable-if-reuse.tmp, line 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.9 rillig # expect: Parse_PushInput: file variable-if-triple-negation.tmp, line 1
112 1.9 rillig # expect: Parse_PushInput: file variable-if-triple-negation.tmp, line 1
113 1.9 rillig
114 1.9 rillig # A conditional other than '.if' or '.ifndef' does not guard the file, even if
115 1.9 rillig # it is otherwise equivalent to the above accepted forms.
116 1.14 rillig CASES+= variable-ifdef-negated
117 1.9 rillig LINES.variable-ifdef-negated= \
118 1.9 rillig '.ifdef !VARIABLE_IFDEF_NEGATED' \
119 1.9 rillig 'VARIABLE_IFDEF_NEGATED=' \
120 1.6 rillig '.endif'
121 1.9 rillig # expect: Parse_PushInput: file variable-ifdef-negated.tmp, line 1
122 1.9 rillig # expect: Parse_PushInput: file variable-ifdef-negated.tmp, line 1
123 1.6 rillig
124 1.1 rillig # The variable names in the '.if' and the assignment must be the same.
125 1.14 rillig CASES+= variable-name-mismatch
126 1.9 rillig LINES.variable-name-mismatch= \
127 1.9 rillig '.ifndef VARIABLE_NAME_MISMATCH' \
128 1.9 rillig 'VARIABLE_NAME_DIFFERENT=' \
129 1.9 rillig '.endif'
130 1.9 rillig # expect: Parse_PushInput: file variable-name-mismatch.tmp, line 1
131 1.9 rillig # expect: Parse_PushInput: file variable-name-mismatch.tmp, line 1
132 1.9 rillig
133 1.9 rillig # The variable name '!VARNAME' cannot be used in an '.ifndef' directive, as
134 1.9 rillig # the '!' would be a negation. It is syntactically valid in a '.if !defined'
135 1.12 rillig # condition, but this case is so uncommon that the guard mechanism doesn't
136 1.12 rillig # accept '!' in the guard variable name. Furthermore, when defining the
137 1.12 rillig # variable, the character '!' has to be escaped, to prevent it from being
138 1.12 rillig # interpreted as the '!' dependency operator.
139 1.14 rillig CASES+= variable-name-exclamation
140 1.9 rillig LINES.variable-name-exclamation= \
141 1.9 rillig '.if !defined(!VARIABLE_NAME_EXCLAMATION)' \
142 1.9 rillig '${:U!}VARIABLE_NAME_EXCLAMATION=' \
143 1.9 rillig '.endif'
144 1.9 rillig # expect: Parse_PushInput: file variable-name-exclamation.tmp, line 1
145 1.9 rillig # expect: Parse_PushInput: file variable-name-exclamation.tmp, line 1
146 1.9 rillig
147 1.13 rillig # In general, a variable name can contain a '!' in the middle, as that
148 1.13 rillig # character is interpreted as an ordinary character in conditions as well as
149 1.13 rillig # on the left side of a variable assignment. For guard variable names, the
150 1.13 rillig # '!' is not supported in any place, though.
151 1.14 rillig CASES+= variable-name-exclamation-middle
152 1.9 rillig LINES.variable-name-exclamation-middle= \
153 1.9 rillig '.ifndef VARIABLE_NAME!MIDDLE' \
154 1.9 rillig 'VARIABLE_NAME!MIDDLE=' \
155 1.9 rillig '.endif'
156 1.9 rillig # expect: Parse_PushInput: file variable-name-exclamation-middle.tmp, line 1
157 1.9 rillig # expect: Parse_PushInput: file variable-name-exclamation-middle.tmp, line 1
158 1.9 rillig
159 1.9 rillig # A variable name can contain balanced parentheses, at least in conditions and
160 1.9 rillig # on the left side of a variable assignment. There are enough places in make
161 1.9 rillig # where parentheses or braces are handled inconsistently to make this naming
162 1.9 rillig # choice a bad idea, therefore these characters are not allowed in guard
163 1.9 rillig # variable names.
164 1.14 rillig CASES+= variable-name-parentheses
165 1.9 rillig LINES.variable-name-parentheses= \
166 1.9 rillig '.ifndef VARIABLE_NAME(&)PARENTHESES' \
167 1.9 rillig 'VARIABLE_NAME(&)PARENTHESES=' \
168 1.1 rillig '.endif'
169 1.9 rillig # expect: Parse_PushInput: file variable-name-parentheses.tmp, line 1
170 1.9 rillig # expect: Parse_PushInput: file variable-name-parentheses.tmp, line 1
171 1.1 rillig
172 1.6 rillig # The guard condition must consist of only the guard variable, nothing else.
173 1.14 rillig CASES+= variable-ifndef-plus
174 1.9 rillig LINES.variable-ifndef-plus= \
175 1.9 rillig '.ifndef VARIABLE_IFNDEF_PLUS && VARIABLE_IFNDEF_SECOND' \
176 1.9 rillig 'VARIABLE_IFNDEF_PLUS=' \
177 1.9 rillig 'VARIABLE_IFNDEF_SECOND=' \
178 1.6 rillig '.endif'
179 1.9 rillig # expect: Parse_PushInput: file variable-ifndef-plus.tmp, line 1
180 1.9 rillig # expect: Parse_PushInput: file variable-ifndef-plus.tmp, line 1
181 1.6 rillig
182 1.6 rillig # The guard condition must consist of only the guard variable, nothing else.
183 1.14 rillig CASES+= variable-if-plus
184 1.9 rillig LINES.variable-if-plus= \
185 1.9 rillig '.if !defined(VARIABLE_IF_PLUS) && !defined(VARIABLE_IF_SECOND)' \
186 1.9 rillig 'VARIABLE_IF_PLUS=' \
187 1.9 rillig 'VARIABLE_IF_SECOND=' \
188 1.6 rillig '.endif'
189 1.9 rillig # expect: Parse_PushInput: file variable-if-plus.tmp, line 1
190 1.9 rillig # expect: Parse_PushInput: file variable-if-plus.tmp, line 1
191 1.6 rillig
192 1.6 rillig # The variable name in an '.ifndef' guard must be given directly, it must not
193 1.6 rillig # contain any '$' expression.
194 1.14 rillig CASES+= variable-ifndef-indirect
195 1.9 rillig LINES.variable-ifndef-indirect= \
196 1.9 rillig '.ifndef $${VARIABLE_IFNDEF_INDIRECT:L}' \
197 1.9 rillig 'VARIABLE_IFNDEF_INDIRECT=' \
198 1.6 rillig '.endif'
199 1.9 rillig # expect: Parse_PushInput: file variable-ifndef-indirect.tmp, line 1
200 1.9 rillig # expect: Parse_PushInput: file variable-ifndef-indirect.tmp, line 1
201 1.9 rillig
202 1.9 rillig # The variable name in an '.if' guard must be given directly, it must not
203 1.9 rillig # contain any '$' expression.
204 1.14 rillig CASES+= variable-if-indirect
205 1.9 rillig LINES.variable-if-indirect= \
206 1.9 rillig '.if !defined($${VARIABLE_IF_INDIRECT:L})' \
207 1.9 rillig 'VARIABLE_IF_INDIRECT=' \
208 1.9 rillig '.endif'
209 1.9 rillig # expect: Parse_PushInput: file variable-if-indirect.tmp, line 1
210 1.9 rillig # expect: Parse_PushInput: file variable-if-indirect.tmp, line 1
211 1.6 rillig
212 1.5 rillig # The variable name in the guard condition must only contain alphanumeric
213 1.12 rillig # characters and underscores. The place where the guard variable is defined
214 1.12 rillig # is more flexible, as long as the variable is defined at the point where the
215 1.12 rillig # file is included the next time.
216 1.14 rillig CASES+= variable-assign-indirect
217 1.9 rillig LINES.variable-assign-indirect= \
218 1.9 rillig '.ifndef VARIABLE_ASSIGN_INDIRECT' \
219 1.9 rillig '$${VARIABLE_ASSIGN_INDIRECT:L}=' \
220 1.1 rillig '.endif'
221 1.9 rillig # expect: Parse_PushInput: file variable-assign-indirect.tmp, line 1
222 1.9 rillig # expect: Skipping 'variable-assign-indirect.tmp' because 'VARIABLE_ASSIGN_INDIRECT' is defined
223 1.1 rillig
224 1.10 rillig # The time at which the guard variable is defined doesn't matter, as long as
225 1.10 rillig # it is defined at the point where the file is included the next time.
226 1.14 rillig CASES+= variable-assign-late
227 1.9 rillig LINES.variable-assign-late= \
228 1.9 rillig '.ifndef VARIABLE_ASSIGN_LATE' \
229 1.9 rillig 'VARIABLE_ASSIGN_LATE_OTHER=' \
230 1.9 rillig 'VARIABLE_ASSIGN_LATE=' \
231 1.1 rillig '.endif'
232 1.9 rillig # expect: Parse_PushInput: file variable-assign-late.tmp, line 1
233 1.9 rillig # expect: Skipping 'variable-assign-late.tmp' because 'VARIABLE_ASSIGN_LATE' is defined
234 1.1 rillig
235 1.10 rillig # The time at which the guard variable is defined doesn't matter, as long as
236 1.10 rillig # it is defined at the point where the file is included the next time.
237 1.14 rillig CASES+= variable-assign-nested
238 1.9 rillig LINES.variable-assign-nested= \
239 1.9 rillig '.ifndef VARIABLE_ASSIGN_NESTED' \
240 1.2 rillig '. if 1' \
241 1.9 rillig '. for i in once' \
242 1.9 rillig 'VARIABLE_ASSIGN_NESTED=' \
243 1.9 rillig '. endfor' \
244 1.1 rillig '. endif' \
245 1.1 rillig '.endif'
246 1.9 rillig # expect: Parse_PushInput: file variable-assign-nested.tmp, line 1
247 1.9 rillig # expect: Skipping 'variable-assign-nested.tmp' because 'VARIABLE_ASSIGN_NESTED' is defined
248 1.1 rillig
249 1.7 rillig # If the guard variable is defined before the file is included for the first
250 1.10 rillig # time, the file is considered guarded as well. In such a case, the parser
251 1.10 rillig # skips almost all lines, as they are irrelevant, but the structure of the
252 1.10 rillig # top-level '.if/.endif' conditional can be determined reliably enough to
253 1.10 rillig # decide whether the file is guarded.
254 1.14 rillig CASES+= variable-already-defined
255 1.9 rillig LINES.variable-already-defined= \
256 1.9 rillig '.ifndef VARIABLE_ALREADY_DEFINED' \
257 1.9 rillig 'VARIABLE_ALREADY_DEFINED=' \
258 1.9 rillig '.endif'
259 1.9 rillig VARIABLE_ALREADY_DEFINED=
260 1.9 rillig # expect: Parse_PushInput: file variable-already-defined.tmp, line 1
261 1.10 rillig # expect: Skipping 'variable-already-defined.tmp' because 'VARIABLE_ALREADY_DEFINED' is defined
262 1.10 rillig
263 1.10 rillig # If the guard variable is defined before the file is included the first time,
264 1.10 rillig # the file is processed but its content is skipped. If that same guard
265 1.10 rillig # variable is undefined when the file is included the second time, the file is
266 1.10 rillig # processed as usual.
267 1.14 rillig CASES+= variable-defined-then-undefined
268 1.10 rillig LINES.variable-defined-then-undefined= \
269 1.10 rillig '.ifndef VARIABLE_DEFINED_THEN_UNDEFINED' \
270 1.10 rillig '.endif'
271 1.10 rillig VARIABLE_DEFINED_THEN_UNDEFINED=
272 1.10 rillig UNDEF_BETWEEN.variable-defined-then-undefined= \
273 1.10 rillig VARIABLE_DEFINED_THEN_UNDEFINED
274 1.10 rillig # expect: Parse_PushInput: file variable-defined-then-undefined.tmp, line 1
275 1.10 rillig # expect: Parse_PushInput: file variable-defined-then-undefined.tmp, line 1
276 1.1 rillig
277 1.1 rillig # The whole file content must be guarded by a single '.if' conditional, not by
278 1.12 rillig # several, as each of these conditionals would require its separate guard.
279 1.12 rillig # This case is not expected to occur in practice, as the two parts would
280 1.12 rillig # rather be split into separate files.
281 1.14 rillig CASES+= variable-two-times
282 1.9 rillig LINES.variable-two-times= \
283 1.9 rillig '.ifndef VARIABLE_TWO_TIMES_1' \
284 1.9 rillig 'VARIABLE_TWO_TIMES_1=' \
285 1.1 rillig '.endif' \
286 1.9 rillig '.ifndef VARIABLE_TWO_TIMES_2' \
287 1.9 rillig 'VARIABLE_TWO_TIMES_2=' \
288 1.1 rillig '.endif'
289 1.9 rillig # expect: Parse_PushInput: file variable-two-times.tmp, line 1
290 1.9 rillig # expect: Parse_PushInput: file variable-two-times.tmp, line 1
291 1.1 rillig
292 1.9 rillig # When multiple files use the same guard variable name, the optimization of
293 1.10 rillig # skipping the file affects each of these files.
294 1.10 rillig #
295 1.9 rillig # Choosing unique guard names is the responsibility of the makefile authors.
296 1.9 rillig # A typical pattern of guard variable names is '${PROJECT}_${DIR}_${FILE}_MK'.
297 1.9 rillig # System-provided files typically start the guard names with '_'.
298 1.14 rillig CASES+= variable-clash
299 1.9 rillig LINES.variable-clash= \
300 1.9 rillig ${LINES.variable-if}
301 1.9 rillig # expect: Parse_PushInput: file variable-clash.tmp, line 1
302 1.10 rillig # expect: Skipping 'variable-clash.tmp' because 'VARIABLE_IF' is defined
303 1.1 rillig
304 1.1 rillig # The conditional must come before the assignment, otherwise the conditional
305 1.1 rillig # is useless, as it always evaluates to false.
306 1.14 rillig CASES+= variable-swapped
307 1.9 rillig LINES.variable-swapped= \
308 1.1 rillig 'SWAPPED=' \
309 1.1 rillig '.ifndef SWAPPED' \
310 1.9 rillig '. error' \
311 1.1 rillig '.endif'
312 1.9 rillig # expect: Parse_PushInput: file variable-swapped.tmp, line 1
313 1.9 rillig # expect: Parse_PushInput: file variable-swapped.tmp, line 1
314 1.1 rillig
315 1.9 rillig # If the guard variable is undefined between the first and the second time the
316 1.9 rillig # file is included, the guarded file is included again.
317 1.14 rillig CASES+= variable-undef-between
318 1.9 rillig LINES.variable-undef-between= \
319 1.9 rillig '.ifndef VARIABLE_UNDEF_BETWEEN' \
320 1.9 rillig 'VARIABLE_UNDEF_BETWEEN=' \
321 1.9 rillig '.endif'
322 1.9 rillig UNDEF_BETWEEN.variable-undef-between= \
323 1.9 rillig VARIABLE_UNDEF_BETWEEN
324 1.9 rillig # expect: Parse_PushInput: file variable-undef-between.tmp, line 1
325 1.9 rillig # expect: Parse_PushInput: file variable-undef-between.tmp, line 1
326 1.9 rillig
327 1.9 rillig # If the guard variable is undefined while the file is included the first
328 1.9 rillig # time, the guard does not have an effect, and the file is included again.
329 1.14 rillig CASES+= variable-undef-inside
330 1.9 rillig LINES.variable-undef-inside= \
331 1.9 rillig '.ifndef VARIABLE_UNDEF_INSIDE' \
332 1.9 rillig 'VARIABLE_UNDEF_INSIDE=' \
333 1.9 rillig '.undef VARIABLE_UNDEF_INSIDE' \
334 1.9 rillig '.endif'
335 1.9 rillig # expect: Parse_PushInput: file variable-undef-inside.tmp, line 1
336 1.9 rillig # expect: Parse_PushInput: file variable-undef-inside.tmp, line 1
337 1.9 rillig
338 1.9 rillig # If the file does not define the guard variable, the guard does not have an
339 1.9 rillig # effect, and the file is included again.
340 1.14 rillig CASES+= variable-not-defined
341 1.9 rillig LINES.variable-not-defined= \
342 1.9 rillig '.ifndef VARIABLE_NOT_DEFINED' \
343 1.2 rillig '.endif'
344 1.9 rillig # expect: Parse_PushInput: file variable-not-defined.tmp, line 1
345 1.9 rillig # expect: Parse_PushInput: file variable-not-defined.tmp, line 1
346 1.2 rillig
347 1.2 rillig # The outermost '.if' must not have an '.elif' branch.
348 1.14 rillig CASES+= elif
349 1.12 rillig LINES.elif= \
350 1.12 rillig '.ifndef ELIF' \
351 1.12 rillig 'ELIF=' \
352 1.2 rillig '.elif 1' \
353 1.2 rillig '.endif'
354 1.12 rillig # expect: Parse_PushInput: file elif.tmp, line 1
355 1.12 rillig # expect: Parse_PushInput: file elif.tmp, line 1
356 1.2 rillig
357 1.10 rillig # When a file with an '.if/.elif/.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.14 rillig CASES+= elif-reuse
360 1.12 rillig LINES.elif-reuse= \
361 1.12 rillig '.ifndef ELIF' \
362 1.10 rillig 'syntax error' \
363 1.10 rillig '.elif 1' \
364 1.10 rillig '.endif'
365 1.12 rillig # expect: Parse_PushInput: file elif-reuse.tmp, line 1
366 1.12 rillig # expect: Parse_PushInput: file elif-reuse.tmp, line 1
367 1.10 rillig
368 1.2 rillig # The outermost '.if' must not have an '.else' branch.
369 1.14 rillig CASES+= else
370 1.12 rillig LINES.else= \
371 1.12 rillig '.ifndef ELSE' \
372 1.12 rillig 'ELSE=' \
373 1.2 rillig '.else' \
374 1.2 rillig '.endif'
375 1.12 rillig # expect: Parse_PushInput: file else.tmp, line 1
376 1.12 rillig # expect: Parse_PushInput: file else.tmp, line 1
377 1.2 rillig
378 1.10 rillig # When a file with an '.if/.else/.endif' conditional at the top level is
379 1.10 rillig # included, it is never optimized, as one of its branches is taken.
380 1.14 rillig CASES+= else-reuse
381 1.12 rillig LINES.else-reuse= \
382 1.12 rillig '.ifndef ELSE' \
383 1.10 rillig 'syntax error' \
384 1.10 rillig '.else' \
385 1.10 rillig '.endif'
386 1.12 rillig # expect: Parse_PushInput: file else-reuse.tmp, line 1
387 1.12 rillig # expect: Parse_PushInput: file else-reuse.tmp, line 1
388 1.10 rillig
389 1.9 rillig # The inner '.if' directives may have an '.elif' or '.else', and it doesn't
390 1.9 rillig # matter which of their branches are taken.
391 1.14 rillig CASES+= inner-if-elif-else
392 1.10 rillig LINES.inner-if-elif-else= \
393 1.2 rillig '.ifndef INNER_IF_ELIF_ELSE' \
394 1.2 rillig 'INNER_IF_ELIF_ELSE=' \
395 1.2 rillig '. if 0' \
396 1.2 rillig '. elif 0' \
397 1.2 rillig '. else' \
398 1.2 rillig '. endif' \
399 1.2 rillig '. if 0' \
400 1.2 rillig '. elif 1' \
401 1.2 rillig '. else' \
402 1.2 rillig '. endif' \
403 1.2 rillig '. if 1' \
404 1.2 rillig '. elif 1' \
405 1.2 rillig '. else' \
406 1.2 rillig '. endif' \
407 1.2 rillig '.endif'
408 1.4 rillig # expect: Parse_PushInput: file inner-if-elif-else.tmp, line 1
409 1.7 rillig # expect: Skipping 'inner-if-elif-else.tmp' because 'INNER_IF_ELIF_ELSE' is defined
410 1.7 rillig
411 1.9 rillig # The guard can also be a target instead of a variable. Using a target as a
412 1.9 rillig # guard has the benefit that a target cannot be undefined once it is defined.
413 1.9 rillig # The target should be declared '.NOTMAIN'. Since the target names are
414 1.9 rillig # usually chosen according to a pattern that doesn't interfere with real
415 1.9 rillig # target names, they don't need to be declared '.PHONY' as they don't generate
416 1.9 rillig # filesystem operations.
417 1.14 rillig CASES+= target
418 1.7 rillig LINES.target= \
419 1.7 rillig '.if !target(__target.tmp__)' \
420 1.9 rillig '__target.tmp__: .NOTMAIN' \
421 1.7 rillig '.endif'
422 1.7 rillig # expect: Parse_PushInput: file target.tmp, line 1
423 1.7 rillig # expect: Skipping 'target.tmp' because '__target.tmp__' is defined
424 1.7 rillig
425 1.9 rillig # When used for system files, the target name may include '<' and '>', for
426 1.9 rillig # symmetry with the '.include <sys.mk>' directive. The characters '<' and '>'
427 1.9 rillig # are ordinary characters.
428 1.14 rillig CASES+= target-sys
429 1.7 rillig LINES.target-sys= \
430 1.7 rillig '.if !target(__<target-sys.tmp>__)' \
431 1.9 rillig '__<target-sys.tmp>__: .NOTMAIN' \
432 1.7 rillig '.endif'
433 1.7 rillig # expect: Parse_PushInput: file target-sys.tmp, line 1
434 1.7 rillig # expect: Skipping 'target-sys.tmp' because '__<target-sys.tmp>__' is defined
435 1.7 rillig
436 1.9 rillig # The target name may include variable references. These references are
437 1.9 rillig # expanded as usual. Due to the current implementation, the expressions are
438 1.9 rillig # evaluated twice: Once for checking whether the condition evaluates to true,
439 1.9 rillig # and once for determining the guard name. This double evaluation should not
440 1.9 rillig # matter in practice, as guard expressions are expected to be simple,
441 1.9 rillig # deterministic and without side effects.
442 1.14 rillig CASES+= target-indirect
443 1.7 rillig LINES.target-indirect= \
444 1.7 rillig '.if !target($${target-indirect.tmp:L})' \
445 1.9 rillig 'target-indirect.tmp: .NOTMAIN' \
446 1.7 rillig '.endif'
447 1.7 rillig # expect: Parse_PushInput: file target-indirect.tmp, line 1
448 1.8 sjg # expect: Skipping 'target-indirect.tmp' because 'target-indirect.tmp' is defined
449 1.8 sjg
450 1.9 rillig # A common form of guard target is __${.PARSEFILE}__. This form can only be
451 1.9 rillig # used if all files using this form have unique basenames. To get a robust
452 1.9 rillig # pattern based on the same idea, use __${.PARSEDIR}/${.PARSEFILE}__ instead.
453 1.9 rillig # This form does not work when the basename contains whitespace characters, as
454 1.9 rillig # it is not possible to define a target with whitespace, not even by cheating.
455 1.14 rillig CASES+= target-indirect-PARSEFILE
456 1.8 sjg LINES.target-indirect-PARSEFILE= \
457 1.8 sjg '.if !target(__$${.PARSEFILE}__)' \
458 1.8 sjg '__$${.PARSEFILE}__: .NOTMAIN' \
459 1.8 sjg '.endif'
460 1.8 sjg # expect: Parse_PushInput: file target-indirect-PARSEFILE.tmp, line 1
461 1.8 sjg # expect: Skipping 'target-indirect-PARSEFILE.tmp' because '__target-indirect-PARSEFILE.tmp__' is defined
462 1.8 sjg
463 1.9 rillig # Two files with different basenames can both use the same syntactic pattern
464 1.9 rillig # for the target guard name, as the expressions expand to different strings.
465 1.14 rillig CASES+= target-indirect-PARSEFILE2
466 1.8 sjg LINES.target-indirect-PARSEFILE2= \
467 1.8 sjg '.if !target(__$${.PARSEFILE}__)' \
468 1.8 sjg '__$${.PARSEFILE}__: .NOTMAIN' \
469 1.8 sjg '.endif'
470 1.8 sjg # expect: Parse_PushInput: file target-indirect-PARSEFILE2.tmp, line 1
471 1.8 sjg # expect: Skipping 'target-indirect-PARSEFILE2.tmp' because '__target-indirect-PARSEFILE2.tmp__' is defined
472 1.8 sjg
473 1.9 rillig # Using plain .PARSEFILE without .PARSEDIR leads to name clashes. The include
474 1.10 rillig # guard is the same as in the test case 'target-indirect-PARSEFILE', as the
475 1.12 rillig # guard name only contains the basename but not the directory name. So even
476 1.12 rillig # without defining the guard variable, the file is considered guarded.
477 1.14 rillig CASES+= subdir/target-indirect-PARSEFILE
478 1.9 rillig LINES.subdir/target-indirect-PARSEFILE= \
479 1.9 rillig '.if !target(__$${.PARSEFILE}__)' \
480 1.9 rillig '.endif'
481 1.9 rillig # expect: Parse_PushInput: file subdir/target-indirect-PARSEFILE.tmp, line 1
482 1.10 rillig # expect: Skipping 'subdir/target-indirect-PARSEFILE.tmp' because '__target-indirect-PARSEFILE.tmp__' is defined
483 1.9 rillig
484 1.11 sjg # Another common form of guard target is __${.PARSEDIR}/${.PARSEFILE}__
485 1.12 rillig # or __${.PARSEDIR:tA}/${.PARSEFILE}__ to be truly unique.
486 1.14 rillig CASES+= target-indirect-PARSEDIR-PARSEFILE
487 1.9 rillig LINES.target-indirect-PARSEDIR-PARSEFILE= \
488 1.9 rillig '.if !target(__$${.PARSEDIR}/$${.PARSEFILE}__)' \
489 1.9 rillig '__$${.PARSEDIR}/$${.PARSEFILE}__: .NOTMAIN' \
490 1.9 rillig '.endif'
491 1.9 rillig # expect: Parse_PushInput: file target-indirect-PARSEDIR-PARSEFILE.tmp, line 1
492 1.9 rillig # expect: Skipping 'target-indirect-PARSEDIR-PARSEFILE.tmp' because '__target-indirect-PARSEDIR-PARSEFILE.tmp__' is defined
493 1.9 rillig # The actual target starts with '__${.OBJDIR}/', see the .rawout file, but the
494 1.9 rillig # string '${.OBJDIR}/' gets stripped in post processing.
495 1.9 rillig
496 1.9 rillig # Using the combination of '.PARSEDIR' and '.PARSEFILE', a file in a
497 1.9 rillig # subdirectory gets a different guard target name than the previous one.
498 1.14 rillig CASES+= subdir/target-indirect-PARSEDIR-PARSEFILE
499 1.9 rillig LINES.subdir/target-indirect-PARSEDIR-PARSEFILE= \
500 1.9 rillig '.if !target(__$${.PARSEDIR}/$${.PARSEFILE}__)' \
501 1.9 rillig '__$${.PARSEDIR}/$${.PARSEFILE}__: .NOTMAIN' \
502 1.9 rillig '.endif'
503 1.9 rillig # expect: Parse_PushInput: file subdir/target-indirect-PARSEDIR-PARSEFILE.tmp, line 1
504 1.9 rillig # expect: Skipping 'subdir/target-indirect-PARSEDIR-PARSEFILE.tmp' because '__subdir/target-indirect-PARSEDIR-PARSEFILE.tmp__' is defined
505 1.9 rillig # The actual target starts with '__${.OBJDIR}/', see the .rawout file, but the
506 1.9 rillig # string '${.OBJDIR}/' gets stripped in post processing.
507 1.9 rillig
508 1.9 rillig # If the guard target is not defined when including the file the next time,
509 1.9 rillig # the file is processed again.
510 1.14 rillig CASES+= target-unguarded
511 1.7 rillig LINES.target-unguarded= \
512 1.7 rillig '.if !target(target-unguarded)' \
513 1.7 rillig '.endif'
514 1.7 rillig # expect: Parse_PushInput: file target-unguarded.tmp, line 1
515 1.7 rillig # expect: Parse_PushInput: file target-unguarded.tmp, line 1
516 1.7 rillig
517 1.7 rillig # The guard condition must consist of only the guard target, nothing else.
518 1.14 rillig CASES+= target-plus
519 1.7 rillig LINES.target-plus= \
520 1.7 rillig '.if !target(target-plus) && 1' \
521 1.9 rillig 'target-plus: .NOTMAIN' \
522 1.7 rillig '.endif'
523 1.7 rillig # expect: Parse_PushInput: file target-plus.tmp, line 1
524 1.7 rillig # expect: Parse_PushInput: file target-plus.tmp, line 1
525 1.7 rillig
526 1.9 rillig # If the guard target is defined before the file is included the first time,
527 1.12 rillig # the file is read once and then considered guarded.
528 1.14 rillig CASES+= target-already-defined
529 1.10 rillig LINES.target-already-defined= \
530 1.10 rillig '.if !target(target-already-defined)' \
531 1.10 rillig 'target-already-defined: .NOTMAIN' \
532 1.10 rillig '.endif'
533 1.10 rillig target-already-defined: .NOTMAIN
534 1.10 rillig # expect: Parse_PushInput: file target-already-defined.tmp, line 1
535 1.10 rillig # expect: Skipping 'target-already-defined.tmp' because 'target-already-defined' is defined
536 1.5 rillig
537 1.9 rillig # A target name cannot contain the character '!'. In the condition, the '!'
538 1.9 rillig # is syntactically valid, but in the dependency declaration line, the '!' is
539 1.9 rillig # interpreted as the '!' dependency operator, no matter whether it occurs at
540 1.9 rillig # the beginning or in the middle of a target name. Escaping it as '${:U!}'
541 1.9 rillig # doesn't work, as the whole line is first expanded and then scanned for the
542 1.9 rillig # dependency operator. Escaping it as '\!' doesn't work either, even though
543 1.9 rillig # the '\' escapes the '!' from being a dependency operator, but when reading
544 1.9 rillig # the target name, the '\' is kept, resulting in the target name
545 1.9 rillig # '\!target-name-exclamation' instead of '!target-name-exclamation'.
546 1.14 rillig CASES+= target-name-exclamation
547 1.9 rillig LINES.target-name-exclamation= \
548 1.9 rillig '.if !target(!target-name-exclamation)' \
549 1.9 rillig '\!target-name-exclamation: .NOTMAIN' \
550 1.9 rillig '.endif'
551 1.9 rillig # expect: Parse_PushInput: file target-name-exclamation.tmp, line 1
552 1.9 rillig # expect: Parse_PushInput: file target-name-exclamation.tmp, line 1
553 1.9 rillig
554 1.12 rillig
555 1.9 rillig # Now run all test cases by including each of the files twice and looking at
556 1.9 rillig # the debug output. The files that properly guard against multiple inclusion
557 1.9 rillig # generate a 'Skipping' line, the others repeat the 'Parse_PushInput' line.
558 1.1 rillig #
559 1.1 rillig # Some debug output lines are suppressed in the .exp file, see ./Makefile.
560 1.14 rillig .for i in ${CASES}
561 1.4 rillig . for fname in $i.tmp
562 1.9 rillig _:= ${fname:H:N.:@dir@${:!mkdir -p ${dir}!}@}
563 1.1 rillig _!= printf '%s\n' ${LINES.$i} > ${fname}
564 1.1 rillig .MAKEFLAGS: -dp
565 1.1 rillig .include "${.CURDIR}/${fname}"
566 1.9 rillig .undef ${UNDEF_BETWEEN.$i:U}
567 1.1 rillig .include "${.CURDIR}/${fname}"
568 1.1 rillig .MAKEFLAGS: -d0
569 1.1 rillig _!= rm ${fname}
570 1.9 rillig _:= ${fname:H:N.:@dir@${:!rmdir ${dir}!}@}
571 1.1 rillig . endfor
572 1.1 rillig .endfor
573 1.1 rillig
574 1.1 rillig all:
575