xref: /freebsd/contrib/bmake/unit-tests/directive-include-guard.mk (revision aa1a8ff2d6dbc51ef058f46f3db5a8bb77967145)
1# $NetBSD: directive-include-guard.mk,v 1.16 2023/12/17 14:07:22 rillig Exp $
2#
3# Tests for multiple-inclusion guards in makefiles.
4#
5# A file that is guarded by a multiple-inclusion guard has one of the
6# following forms:
7#
8#	.ifndef GUARD_VARIABLE
9#	.endif
10#
11#	.if !defined(GUARD_VARIABLE)
12#	.endif
13#
14#	.if !target(guard-target)
15#	.endif
16#
17# When such a file is included for the second or later time, and the guard
18# variable or the guard target is defined, the file is skipped completely, as
19# including it would not have any effect, not even on the special variable
20# '.MAKE.MAKEFILES', as that variable skips duplicate pathnames.
21#
22# See also:
23#	https://gcc.gnu.org/onlinedocs/cppinternals/Guard-Macros.html
24
25# Each of the following test cases creates a temporary file named after the
26# test case and writes some lines of text to that file.  That file is then
27# included twice, to see whether the second '.include' is skipped.
28
29
30# This is the canonical form of a variable-based multiple-inclusion guard.
31CASES+=	variable-ifndef
32LINES.variable-ifndef= \
33	'.ifndef VARIABLE_IFNDEF' \
34	'VARIABLE_IFNDEF=' \
35	'.endif'
36# expect: Parse_PushInput: file variable-ifndef.tmp, line 1
37# expect: Skipping 'variable-ifndef.tmp' because 'VARIABLE_IFNDEF' is defined
38
39# A file that reuses a guard from a previous file (or whose guard is defined
40# for any other reason) is only processed once, to see whether it is guarded.
41# Its content is skipped, therefore the syntax error is not detected.
42CASES+=	variable-ifndef-reuse
43LINES.variable-ifndef-reuse= \
44	'.ifndef VARIABLE_IFNDEF' \
45	'syntax error' \
46	'.endif'
47# expect: Parse_PushInput: file variable-ifndef-reuse.tmp, line 1
48# expect: Skipping 'variable-ifndef-reuse.tmp' because 'VARIABLE_IFNDEF' is defined
49
50# The guard variable cannot be a number, as numbers are interpreted
51# differently from bare words.
52CASES+=	variable-ifndef-zero
53LINES.variable-ifndef-zero= \
54	'.ifndef 0e0' \
55	'syntax error' \
56	'.endif'
57# expect: Parse_PushInput: file variable-ifndef-zero.tmp, line 1
58# expect: Parse_PushInput: file variable-ifndef-zero.tmp, line 1
59
60# The guard variable cannot be a number, as numbers are interpreted
61# differently from bare words.
62CASES+=	variable-ifndef-one
63LINES.variable-ifndef-one= \
64	'.ifndef 1' \
65	'.endif'
66# expect: Parse_PushInput: file variable-ifndef-one.tmp, line 1
67# expect: Parse_PushInput: file variable-ifndef-one.tmp, line 1
68
69# Comments and empty lines do not affect the multiple-inclusion guard.
70CASES+=	comments
71LINES.comments= \
72	'\# comment' \
73	'' \
74	'.ifndef COMMENTS' \
75	'\# comment' \
76	'COMMENTS=\#comment' \
77	'.endif' \
78	'\# comment'
79# expect: Parse_PushInput: file comments.tmp, line 1
80# expect: Skipping 'comments.tmp' because 'COMMENTS' is defined
81
82# An alternative form uses the 'defined' function.  It is more verbose than
83# the canonical form but avoids the '.ifndef' directive, as that directive is
84# not commonly used.
85CASES+=	variable-if
86LINES.variable-if= \
87	'.if !defined(VARIABLE_IF)' \
88	'VARIABLE_IF=' \
89	'.endif'
90# expect: Parse_PushInput: file variable-if.tmp, line 1
91# expect: Skipping 'variable-if.tmp' because 'VARIABLE_IF' is defined
92
93# A file that reuses a guard from a previous file (or whose guard is defined
94# for any other reason) is only processed once, to see whether it is guarded.
95# Its content is skipped, therefore the syntax error is not detected.
96CASES+=	variable-if-reuse
97LINES.variable-if-reuse= \
98	'.if !defined(VARIABLE_IF)' \
99	'syntax error' \
100	'.endif'
101# expect: Parse_PushInput: file variable-if-reuse.tmp, line 1
102# expect: Skipping 'variable-if-reuse.tmp' because 'VARIABLE_IF' is defined
103
104# Triple negation is so uncommon that it's not recognized, even though it has
105# the same effect as a single negation.
106CASES+=	variable-if-triple-negation
107LINES.variable-if-triple-negation= \
108	'.if !!!defined(VARIABLE_IF_TRIPLE_NEGATION)' \
109	'VARIABLE_IF_TRIPLE_NEGATION=' \
110	'.endif'
111# expect: Parse_PushInput: file variable-if-triple-negation.tmp, line 1
112# expect: Parse_PushInput: file variable-if-triple-negation.tmp, line 1
113
114# If the guard variable is enclosed in spaces, it does not have an effect, as
115# that form is not common in practice.
116CASES+=	variable-if-spaced
117LINES.variable-if-spaced= \
118	'.if !defined( VARIABLE_IF_SPACED )' \
119	'VARIABLE_IF_SPACED=' \
120	'.endif'
121# expect: Parse_PushInput: file variable-if-spaced.tmp, line 1
122# expect: Parse_PushInput: file variable-if-spaced.tmp, line 1
123
124# If the guard variable condition is enclosed in parentheses, it does not have
125# an effect, as that form is not common in practice.
126CASES+=	variable-if-parenthesized
127LINES.variable-if-parenthesized= \
128	'.if (!defined(VARIABLE_IF_PARENTHESIZED))' \
129	'VARIABLE_IF_PARENTHESIZED=' \
130	'.endif'
131# expect: Parse_PushInput: file variable-if-parenthesized.tmp, line 1
132# expect: Parse_PushInput: file variable-if-parenthesized.tmp, line 1
133
134# A conditional other than '.if' or '.ifndef' does not guard the file, even if
135# it is otherwise equivalent to the above accepted forms.
136CASES+=	variable-ifdef-negated
137LINES.variable-ifdef-negated= \
138	'.ifdef !VARIABLE_IFDEF_NEGATED' \
139	'VARIABLE_IFDEF_NEGATED=' \
140	'.endif'
141# expect: Parse_PushInput: file variable-ifdef-negated.tmp, line 1
142# expect: Parse_PushInput: file variable-ifdef-negated.tmp, line 1
143
144# The variable names in the '.if' and the assignment must be the same.
145CASES+=	variable-name-mismatch
146LINES.variable-name-mismatch= \
147	'.ifndef VARIABLE_NAME_MISMATCH' \
148	'VARIABLE_NAME_DIFFERENT=' \
149	'.endif'
150# expect: Parse_PushInput: file variable-name-mismatch.tmp, line 1
151# expect: Parse_PushInput: file variable-name-mismatch.tmp, line 1
152
153# If the guard variable condition is enclosed in parentheses, it does not have
154# an effect, as that form is not common in practice.
155CASES+=	variable-ifndef-parenthesized
156LINES.variable-ifndef-parenthesized= \
157	'.ifndef (VARIABLE_IFNDEF_PARENTHESIZED)' \
158	'VARIABLE_IFNDEF_PARENTHESIZED=' \
159	'.endif'
160# expect: Parse_PushInput: file variable-ifndef-parenthesized.tmp, line 1
161# expect: Parse_PushInput: file variable-ifndef-parenthesized.tmp, line 1
162
163# The variable name '!VARNAME' cannot be used in an '.ifndef' directive, as
164# the '!' would be a negation.  It is syntactically valid in a '.if !defined'
165# condition, but this case is so uncommon that the guard mechanism doesn't
166# accept '!' in the guard variable name. Furthermore, when defining the
167# variable, the character '!' has to be escaped, to prevent it from being
168# interpreted as the '!' dependency operator.
169CASES+=	variable-name-exclamation
170LINES.variable-name-exclamation= \
171	'.if !defined(!VARIABLE_NAME_EXCLAMATION)' \
172	'${:U!}VARIABLE_NAME_EXCLAMATION=' \
173	'.endif'
174# expect: Parse_PushInput: file variable-name-exclamation.tmp, line 1
175# expect: Parse_PushInput: file variable-name-exclamation.tmp, line 1
176
177# In general, a variable name can contain a '!' in the middle, as that
178# character is interpreted as an ordinary character in conditions as well as
179# on the left side of a variable assignment.  For guard variable names, the
180# '!' is not supported in any place, though.
181CASES+=	variable-name-exclamation-middle
182LINES.variable-name-exclamation-middle= \
183	'.ifndef VARIABLE_NAME!MIDDLE' \
184	'VARIABLE_NAME!MIDDLE=' \
185	'.endif'
186# expect: Parse_PushInput: file variable-name-exclamation-middle.tmp, line 1
187# expect: Parse_PushInput: file variable-name-exclamation-middle.tmp, line 1
188
189# A variable name can contain balanced parentheses, at least in conditions and
190# on the left side of a variable assignment.  There are enough places in make
191# where parentheses or braces are handled inconsistently to make this naming
192# choice a bad idea, therefore these characters are not allowed in guard
193# variable names.
194CASES+=	variable-name-parentheses
195LINES.variable-name-parentheses= \
196	'.ifndef VARIABLE_NAME(&)PARENTHESES' \
197	'VARIABLE_NAME(&)PARENTHESES=' \
198	'.endif'
199# expect: Parse_PushInput: file variable-name-parentheses.tmp, line 1
200# expect: Parse_PushInput: file variable-name-parentheses.tmp, line 1
201
202# The guard condition must consist of only the guard variable, nothing else.
203CASES+=	variable-ifndef-plus
204LINES.variable-ifndef-plus= \
205	'.ifndef VARIABLE_IFNDEF_PLUS && VARIABLE_IFNDEF_SECOND' \
206	'VARIABLE_IFNDEF_PLUS=' \
207	'VARIABLE_IFNDEF_SECOND=' \
208	'.endif'
209# expect: Parse_PushInput: file variable-ifndef-plus.tmp, line 1
210# expect: Parse_PushInput: file variable-ifndef-plus.tmp, line 1
211
212# The guard condition must consist of only the guard variable, nothing else.
213CASES+=	variable-if-plus
214LINES.variable-if-plus= \
215	'.if !defined(VARIABLE_IF_PLUS) && !defined(VARIABLE_IF_SECOND)' \
216	'VARIABLE_IF_PLUS=' \
217	'VARIABLE_IF_SECOND=' \
218	'.endif'
219# expect: Parse_PushInput: file variable-if-plus.tmp, line 1
220# expect: Parse_PushInput: file variable-if-plus.tmp, line 1
221
222# The variable name in an '.ifndef' guard must be given directly, it must not
223# contain any '$' expression.
224CASES+=	variable-ifndef-indirect
225LINES.variable-ifndef-indirect= \
226	'.ifndef $${VARIABLE_IFNDEF_INDIRECT:L}' \
227	'VARIABLE_IFNDEF_INDIRECT=' \
228	'.endif'
229# expect: Parse_PushInput: file variable-ifndef-indirect.tmp, line 1
230# expect: Parse_PushInput: file variable-ifndef-indirect.tmp, line 1
231
232# The variable name in an '.if' guard must be given directly, it must not
233# contain any '$' expression.
234CASES+=	variable-if-indirect
235LINES.variable-if-indirect= \
236	'.if !defined($${VARIABLE_IF_INDIRECT:L})' \
237	'VARIABLE_IF_INDIRECT=' \
238	'.endif'
239# expect: Parse_PushInput: file variable-if-indirect.tmp, line 1
240# expect: Parse_PushInput: file variable-if-indirect.tmp, line 1
241
242# The variable name in the guard condition must only contain alphanumeric
243# characters and underscores.  The place where the guard variable is defined
244# is more flexible, as long as the variable is defined at the point where the
245# file is included the next time.
246CASES+=	variable-assign-indirect
247LINES.variable-assign-indirect= \
248	'.ifndef VARIABLE_ASSIGN_INDIRECT' \
249	'$${VARIABLE_ASSIGN_INDIRECT:L}=' \
250	'.endif'
251# expect: Parse_PushInput: file variable-assign-indirect.tmp, line 1
252# expect: Skipping 'variable-assign-indirect.tmp' because 'VARIABLE_ASSIGN_INDIRECT' is defined
253
254# The time at which the guard variable is defined doesn't matter, as long as
255# it is defined at the point where the file is included the next time.
256CASES+=	variable-assign-late
257LINES.variable-assign-late= \
258	'.ifndef VARIABLE_ASSIGN_LATE' \
259	'VARIABLE_ASSIGN_LATE_OTHER=' \
260	'VARIABLE_ASSIGN_LATE=' \
261	'.endif'
262# expect: Parse_PushInput: file variable-assign-late.tmp, line 1
263# expect: Skipping 'variable-assign-late.tmp' because 'VARIABLE_ASSIGN_LATE' is defined
264
265# The time at which the guard variable is defined doesn't matter, as long as
266# it is defined at the point where the file is included the next time.
267CASES+=	variable-assign-nested
268LINES.variable-assign-nested= \
269	'.ifndef VARIABLE_ASSIGN_NESTED' \
270	'.  if 1' \
271	'.    for i in once' \
272	'VARIABLE_ASSIGN_NESTED=' \
273	'.    endfor' \
274	'.  endif' \
275	'.endif'
276# expect: Parse_PushInput: file variable-assign-nested.tmp, line 1
277# expect: Skipping 'variable-assign-nested.tmp' because 'VARIABLE_ASSIGN_NESTED' is defined
278
279# If the guard variable is defined before the file is included for the first
280# time, the file is considered guarded as well.  In such a case, the parser
281# skips almost all lines, as they are irrelevant, but the structure of the
282# top-level '.if/.endif' conditional can be determined reliably enough to
283# decide whether the file is guarded.
284CASES+=	variable-already-defined
285LINES.variable-already-defined= \
286	'.ifndef VARIABLE_ALREADY_DEFINED' \
287	'VARIABLE_ALREADY_DEFINED=' \
288	'.endif'
289VARIABLE_ALREADY_DEFINED=
290# expect: Parse_PushInput: file variable-already-defined.tmp, line 1
291# expect: Skipping 'variable-already-defined.tmp' because 'VARIABLE_ALREADY_DEFINED' is defined
292
293# If the guard variable is defined before the file is included the first time,
294# the file is processed but its content is skipped.  If that same guard
295# variable is undefined when the file is included the second time, the file is
296# processed as usual.
297CASES+=	variable-defined-then-undefined
298LINES.variable-defined-then-undefined= \
299	'.ifndef VARIABLE_DEFINED_THEN_UNDEFINED' \
300	'.endif'
301VARIABLE_DEFINED_THEN_UNDEFINED=
302UNDEF_BETWEEN.variable-defined-then-undefined= \
303	VARIABLE_DEFINED_THEN_UNDEFINED
304# expect: Parse_PushInput: file variable-defined-then-undefined.tmp, line 1
305# expect: Parse_PushInput: file variable-defined-then-undefined.tmp, line 1
306
307# The whole file content must be guarded by a single '.if' conditional, not by
308# several, as each of these conditionals would require its separate guard.
309# This case is not expected to occur in practice, as the two parts would
310# rather be split into separate files.
311CASES+=	variable-two-times
312LINES.variable-two-times= \
313	'.ifndef VARIABLE_TWO_TIMES_1' \
314	'VARIABLE_TWO_TIMES_1=' \
315	'.endif' \
316	'.ifndef VARIABLE_TWO_TIMES_2' \
317	'VARIABLE_TWO_TIMES_2=' \
318	'.endif'
319# expect: Parse_PushInput: file variable-two-times.tmp, line 1
320# expect: Parse_PushInput: file variable-two-times.tmp, line 1
321
322# When multiple files use the same guard variable name, the optimization of
323# skipping the file affects each of these files.
324#
325# Choosing unique guard names is the responsibility of the makefile authors.
326# A typical pattern of guard variable names is '${PROJECT}_${DIR}_${FILE}_MK'.
327# System-provided files typically start the guard names with '_'.
328CASES+=	variable-clash
329LINES.variable-clash= \
330	${LINES.variable-if}
331# expect: Parse_PushInput: file variable-clash.tmp, line 1
332# expect: Skipping 'variable-clash.tmp' because 'VARIABLE_IF' is defined
333
334# The conditional must come before the assignment, otherwise the conditional
335# is useless, as it always evaluates to false.
336CASES+=	variable-swapped
337LINES.variable-swapped= \
338	'SWAPPED=' \
339	'.ifndef SWAPPED' \
340	'.  error' \
341	'.endif'
342# expect: Parse_PushInput: file variable-swapped.tmp, line 1
343# expect: Parse_PushInput: file variable-swapped.tmp, line 1
344
345# If the guard variable is undefined between the first and the second time the
346# file is included, the guarded file is included again.
347CASES+=	variable-undef-between
348LINES.variable-undef-between= \
349	'.ifndef VARIABLE_UNDEF_BETWEEN' \
350	'VARIABLE_UNDEF_BETWEEN=' \
351	'.endif'
352UNDEF_BETWEEN.variable-undef-between= \
353	VARIABLE_UNDEF_BETWEEN
354# expect: Parse_PushInput: file variable-undef-between.tmp, line 1
355# expect: Parse_PushInput: file variable-undef-between.tmp, line 1
356
357# If the guard variable is undefined while the file is included the first
358# time, the guard does not have an effect, and the file is included again.
359CASES+=	variable-undef-inside
360LINES.variable-undef-inside= \
361	'.ifndef VARIABLE_UNDEF_INSIDE' \
362	'VARIABLE_UNDEF_INSIDE=' \
363	'.undef VARIABLE_UNDEF_INSIDE' \
364	'.endif'
365# expect: Parse_PushInput: file variable-undef-inside.tmp, line 1
366# expect: Parse_PushInput: file variable-undef-inside.tmp, line 1
367
368# If the file does not define the guard variable, the guard does not have an
369# effect, and the file is included again.
370CASES+=	variable-not-defined
371LINES.variable-not-defined= \
372	'.ifndef VARIABLE_NOT_DEFINED' \
373	'.endif'
374# expect: Parse_PushInput: file variable-not-defined.tmp, line 1
375# expect: Parse_PushInput: file variable-not-defined.tmp, line 1
376
377# The outermost '.if' must not have an '.elif' branch.
378CASES+=	elif
379LINES.elif= \
380	'.ifndef ELIF' \
381	'ELIF=' \
382	'.elif 1' \
383	'.endif'
384# expect: Parse_PushInput: file elif.tmp, line 1
385# expect: Parse_PushInput: file elif.tmp, line 1
386
387# When a file with an '.if/.elif/.endif' conditional at the top level is
388# included, it is never optimized, as one of its branches is taken.
389CASES+=	elif-reuse
390LINES.elif-reuse= \
391	'.ifndef ELIF' \
392	'syntax error' \
393	'.elif 1' \
394	'.endif'
395# expect: Parse_PushInput: file elif-reuse.tmp, line 1
396# expect: Parse_PushInput: file elif-reuse.tmp, line 1
397
398# The outermost '.if' must not have an '.else' branch.
399CASES+=	else
400LINES.else= \
401	'.ifndef ELSE' \
402	'ELSE=' \
403	'.else' \
404	'.endif'
405# expect: Parse_PushInput: file else.tmp, line 1
406# expect: Parse_PushInput: file else.tmp, line 1
407
408# When a file with an '.if/.else/.endif' conditional at the top level is
409# included, it is never optimized, as one of its branches is taken.
410CASES+=	else-reuse
411LINES.else-reuse= \
412	'.ifndef ELSE' \
413	'syntax error' \
414	'.else' \
415	'.endif'
416# expect: Parse_PushInput: file else-reuse.tmp, line 1
417# expect: Parse_PushInput: file else-reuse.tmp, line 1
418
419# The inner '.if' directives may have an '.elif' or '.else', and it doesn't
420# matter which of their branches are taken.
421CASES+=	inner-if-elif-else
422LINES.inner-if-elif-else= \
423	'.ifndef INNER_IF_ELIF_ELSE' \
424	'INNER_IF_ELIF_ELSE=' \
425	'.  if 0' \
426	'.  elif 0' \
427	'.  else' \
428	'.  endif' \
429	'.  if 0' \
430	'.  elif 1' \
431	'.  else' \
432	'.  endif' \
433	'.  if 1' \
434	'.  elif 1' \
435	'.  else' \
436	'.  endif' \
437	'.endif'
438# expect: Parse_PushInput: file inner-if-elif-else.tmp, line 1
439# expect: Skipping 'inner-if-elif-else.tmp' because 'INNER_IF_ELIF_ELSE' is defined
440
441# The guard can also be a target instead of a variable.  Using a target as a
442# guard has the benefit that a target cannot be undefined once it is defined.
443# The target should be declared '.NOTMAIN'.  Since the target names are
444# usually chosen according to a pattern that doesn't interfere with real
445# target names, they don't need to be declared '.PHONY' as they don't generate
446# filesystem operations.
447CASES+=	target
448LINES.target= \
449	'.if !target(__target.tmp__)' \
450	'__target.tmp__: .NOTMAIN' \
451	'.endif'
452# expect: Parse_PushInput: file target.tmp, line 1
453# expect: Skipping 'target.tmp' because '__target.tmp__' is defined
454
455# When used for system files, the target name may include '<' and '>', for
456# symmetry with the '.include <sys.mk>' directive.  The characters '<' and '>'
457# are ordinary characters.
458CASES+=	target-sys
459LINES.target-sys= \
460	'.if !target(__<target-sys.tmp>__)' \
461	'__<target-sys.tmp>__: .NOTMAIN' \
462	'.endif'
463# expect: Parse_PushInput: file target-sys.tmp, line 1
464# expect: Skipping 'target-sys.tmp' because '__<target-sys.tmp>__' is defined
465
466# The target name may include variable references.  These references are
467# expanded as usual.  Due to the current implementation, the expressions are
468# evaluated twice:  Once for checking whether the condition evaluates to true,
469# and once for determining the guard name.  This double evaluation should not
470# matter in practice, as guard expressions are expected to be simple,
471# deterministic and without side effects.
472CASES+=	target-indirect
473LINES.target-indirect= \
474	'.if !target($${target-indirect.tmp:L})' \
475	'target-indirect.tmp: .NOTMAIN' \
476	'.endif'
477# expect: Parse_PushInput: file target-indirect.tmp, line 1
478# expect: Skipping 'target-indirect.tmp' because 'target-indirect.tmp' is defined
479
480# A common form of guard target is __${.PARSEFILE}__.  This form can only be
481# used if all files using this form have unique basenames.  To get a robust
482# pattern based on the same idea, use __${.PARSEDIR}/${.PARSEFILE}__ instead.
483# This form does not work when the basename contains whitespace characters, as
484# it is not possible to define a target with whitespace, not even by cheating.
485CASES+=	target-indirect-PARSEFILE
486LINES.target-indirect-PARSEFILE= \
487	'.if !target(__$${.PARSEFILE}__)' \
488	'__$${.PARSEFILE}__: .NOTMAIN' \
489	'.endif'
490# expect: Parse_PushInput: file target-indirect-PARSEFILE.tmp, line 1
491# expect: Skipping 'target-indirect-PARSEFILE.tmp' because '__target-indirect-PARSEFILE.tmp__' is defined
492
493# Two files with different basenames can both use the same syntactic pattern
494# for the target guard name, as the expressions expand to different strings.
495CASES+=	target-indirect-PARSEFILE2
496LINES.target-indirect-PARSEFILE2= \
497	'.if !target(__$${.PARSEFILE}__)' \
498	'__$${.PARSEFILE}__: .NOTMAIN' \
499	'.endif'
500# expect: Parse_PushInput: file target-indirect-PARSEFILE2.tmp, line 1
501# expect: Skipping 'target-indirect-PARSEFILE2.tmp' because '__target-indirect-PARSEFILE2.tmp__' is defined
502
503# Using plain .PARSEFILE without .PARSEDIR leads to name clashes.  The include
504# guard is the same as in the test case 'target-indirect-PARSEFILE', as the
505# guard name only contains the basename but not the directory name.  So even
506# without defining the guard target, the file is considered guarded.
507CASES+=	subdir/target-indirect-PARSEFILE
508LINES.subdir/target-indirect-PARSEFILE= \
509	'.if !target(__$${.PARSEFILE}__)' \
510	'.endif'
511# expect: Parse_PushInput: file subdir/target-indirect-PARSEFILE.tmp, line 1
512# expect: Skipping 'subdir/target-indirect-PARSEFILE.tmp' because '__target-indirect-PARSEFILE.tmp__' is defined
513
514# Another common form of guard target is __${.PARSEDIR}/${.PARSEFILE}__
515# or __${.PARSEDIR:tA}/${.PARSEFILE}__ to be truly unique.
516CASES+=	target-indirect-PARSEDIR-PARSEFILE
517LINES.target-indirect-PARSEDIR-PARSEFILE= \
518	'.if !target(__$${.PARSEDIR}/$${.PARSEFILE}__)' \
519	'__$${.PARSEDIR}/$${.PARSEFILE}__: .NOTMAIN' \
520	'.endif'
521# expect: Parse_PushInput: file target-indirect-PARSEDIR-PARSEFILE.tmp, line 1
522# expect: Skipping 'target-indirect-PARSEDIR-PARSEFILE.tmp' because '__target-indirect-PARSEDIR-PARSEFILE.tmp__' is defined
523# The actual target starts with '__${.OBJDIR}/', see the .rawout file, but the
524# string '${.OBJDIR}/' gets stripped in post processing.
525
526# Using the combination of '.PARSEDIR' and '.PARSEFILE', a file in a
527# subdirectory gets a different guard target name than the previous one.
528CASES+=	subdir/target-indirect-PARSEDIR-PARSEFILE
529LINES.subdir/target-indirect-PARSEDIR-PARSEFILE= \
530	'.if !target(__$${.PARSEDIR}/$${.PARSEFILE}__)' \
531	'__$${.PARSEDIR}/$${.PARSEFILE}__: .NOTMAIN' \
532	'.endif'
533# expect: Parse_PushInput: file subdir/target-indirect-PARSEDIR-PARSEFILE.tmp, line 1
534# expect: Skipping 'subdir/target-indirect-PARSEDIR-PARSEFILE.tmp' because '__subdir/target-indirect-PARSEDIR-PARSEFILE.tmp__' is defined
535# The actual target starts with '__${.OBJDIR}/', see the .rawout file, but the
536# string '${.OBJDIR}/' gets stripped in post processing.
537
538# If the guard target is not defined when including the file the next time,
539# the file is processed again.
540CASES+=	target-unguarded
541LINES.target-unguarded= \
542	'.if !target(target-unguarded)' \
543	'.endif'
544# expect: Parse_PushInput: file target-unguarded.tmp, line 1
545# expect: Parse_PushInput: file target-unguarded.tmp, line 1
546
547# The guard condition must consist of only the guard target, nothing else.
548CASES+=	target-plus
549LINES.target-plus= \
550	'.if !target(target-plus) && 1' \
551	'target-plus: .NOTMAIN' \
552	'.endif'
553# expect: Parse_PushInput: file target-plus.tmp, line 1
554# expect: Parse_PushInput: file target-plus.tmp, line 1
555
556# If the guard target is defined before the file is included the first time,
557# the file is read once and then considered guarded.
558CASES+=	target-already-defined
559LINES.target-already-defined= \
560	'.if !target(target-already-defined)' \
561	'target-already-defined: .NOTMAIN' \
562	'.endif'
563target-already-defined: .NOTMAIN
564# expect: Parse_PushInput: file target-already-defined.tmp, line 1
565# expect: Skipping 'target-already-defined.tmp' because 'target-already-defined' is defined
566
567# A target name cannot contain the character '!'.  In the condition, the '!'
568# is syntactically valid, but in the dependency declaration line, the '!' is
569# interpreted as the '!' dependency operator, no matter whether it occurs at
570# the beginning or in the middle of a target name.  Escaping it as '${:U!}'
571# doesn't work, as the whole line is first expanded and then scanned for the
572# dependency operator.  Escaping it as '\!' doesn't work either, even though
573# the '\' escapes the '!' from being a dependency operator, but when reading
574# the target name, the '\' is kept, resulting in the target name
575# '\!target-name-exclamation' instead of '!target-name-exclamation'.
576CASES+=	target-name-exclamation
577LINES.target-name-exclamation= \
578	'.if !target(!target-name-exclamation)' \
579	'\!target-name-exclamation: .NOTMAIN' \
580	'.endif'
581# expect: Parse_PushInput: file target-name-exclamation.tmp, line 1
582# expect: Parse_PushInput: file target-name-exclamation.tmp, line 1
583
584# If the guard target name is enclosed in spaces, it does not have an effect,
585# as that form is not common in practice.
586CASES+=	target-name-parenthesized
587LINES.target-name-parenthesized= \
588	'.if !target( target-name-parenthesized )' \
589	'target-name-parenthesized: .NOTMAIN' \
590	'.endif'
591# expect: Parse_PushInput: file target-name-parenthesized.tmp, line 1
592# expect: Parse_PushInput: file target-name-parenthesized.tmp, line 1
593
594# If the guard target condition is enclosed in parentheses, it does not have
595# an effect, as that form is not common in practice.
596CASES+=	target-call-parenthesized
597LINES.target-call-parenthesized= \
598	'.if (!target(target-call-parenthesized))' \
599	'target-call-parenthesized: .NOTMAIN' \
600	'.endif'
601# expect: Parse_PushInput: file target-call-parenthesized.tmp, line 1
602# expect: Parse_PushInput: file target-call-parenthesized.tmp, line 1
603
604# If the '.if' or '.ifndef' directive spans more than a single line, it is
605# still recognized as a guard condition.  This case is entirely uncommon, but
606# at the point where the guard condition is checked, line continuations have
607# already been converted to spaces.
608CASES+=	multiline
609LINES.multiline= \
610	'.\' \
611	'  ifndef \' \
612	'  MULTILINE' \
613	'MULTILINE=' \
614	'.endif'
615# expect: Parse_PushInput: file multiline.tmp, line 1
616# expect: Skipping 'multiline.tmp' because 'MULTILINE' is defined
617
618
619# Now run all test cases by including each of the files twice and looking at
620# the debug output.  The files that properly guard against multiple inclusion
621# generate a 'Skipping' line, the others repeat the 'Parse_PushInput' line.
622#
623# Some debug output lines are suppressed in the .exp file, see ./Makefile.
624.for i in ${CASES}
625.  for fname in $i.tmp
626_:=	${fname:H:N.:@dir@${:!mkdir -p ${dir}!}@}
627_!=	printf '%s\n' ${LINES.$i} > ${fname}
628.MAKEFLAGS: -dp
629.include "${.CURDIR}/${fname}"
630.undef ${UNDEF_BETWEEN.$i:U}
631.include "${.CURDIR}/${fname}"
632.MAKEFLAGS: -d0
633_!=	rm ${fname}
634_:=	${fname:H:N.:@dir@${:!rmdir ${dir}!}@}
635.  endfor
636.endfor
637
638all:
639