xref: /freebsd/contrib/bmake/unit-tests/cond-func-empty.mk (revision 6a7405f5a6b639682cacf01e35d561411ff556aa)
1# $NetBSD: cond-func-empty.mk,v 1.28 2025/01/11 20:54:45 rillig Exp $
2#
3# Tests for the empty() function in .if conditions, which tests an
4# expression for emptiness.
5#
6# Note that the argument in the parentheses is a variable name, not an
7# expression.  That name may be followed by ':...' modifiers.
8#
9
10.undef UNDEF
11EMPTY=	# empty
12SPACE=	${:U }
13ZERO=	0
14WORD=	word
15
16# An undefined variable counts as empty.
17.if !empty(UNDEF)
18.  error
19.endif
20
21# An undefined variable has the empty string as the value, and the :M
22# variable modifier does not change that.
23#
24.if !empty(UNDEF:M*)
25.  error
26.endif
27
28# The :S modifier replaces the empty value with an actual word.  After
29# applying the :S modifier to the expression, its value is 'empty', so it is
30# no longer empty, but it is still based on an undefined variable.  There are
31# a few modifiers that turn an undefined expression into a defined expression,
32# among them :U and :D, but not :S.  Therefore, at the end of evaluating the
33# expression, the expression is still undefined, so its final value becomes an
34# empty string.
35#
36# XXX: This is hard to explain to someone who doesn't know these
37# implementation details.
38#
39.if !empty(UNDEF:S,^$,value,W)
40.  error
41.endif
42
43# The :U modifier changes the state of a previously undefined expression from
44# DEF_UNDEF to DEF_DEFINED.  This marks the expression as "being interesting
45# enough to be further processed".
46#
47.if empty(UNDEF:S,^$,value,W:Ufallback)
48.  error
49.endif
50
51# When an expression is based on an undefined variable, its modifiers interact
52# in sometimes surprising ways.  Applying the :S modifier to the undefined
53# expression makes its value non-empty, but doesn't change that the expression
54# is based on an undefined variable.  The :U modifier that follows only looks
55# at the definedness state to decide whether the variable is defined or not.
56# This kind of makes sense since the :U modifier tests the _variable_, not the
57# _expression_.
58#
59# Since the variable was undefined to begin with, the fallback value from the
60# :U modifier is used in this expression, instead of keeping the 'value' from
61# the :S modifier.
62#
63.if ${UNDEF:S,^$,value,W:Ufallback} != "fallback"
64.  error
65.endif
66
67# The variable EMPTY is completely empty (0 characters).
68.if !empty(EMPTY)
69.  error
70.endif
71
72# The variable SPACE has a single space, which counts as being empty.
73.if !empty(SPACE)
74.  error
75.endif
76
77# The variable .newline has a single newline, which counts as being empty.
78.if !empty(.newline)
79.  error
80.endif
81
82# The variable ZERO has the numeric value 0, but is not empty.  This is a
83# subtle difference between using either 'empty(ZERO)' or the expression
84# '${ZERO}' in a condition.
85.if empty(ZERO)
86.  error
87.elif ${ZERO}
88.  error
89.elif ${ZERO} == ""
90.  error
91.endif
92
93# The following example constructs an expression with the variable name ""
94# and the value " ".  This expression counts as empty since the value contains
95# only whitespace.
96#
97# Contrary to the other functions in conditionals, the trailing space is not
98# stripped off, as can be seen in the -dv debug log.  If the space had been
99# stripped, it wouldn't make a difference in this case, but in other cases.
100#
101.if !empty(:U )
102.  error
103.endif
104
105# Now the variable named " " gets a non-empty value, which demonstrates that
106# neither leading nor trailing spaces are trimmed in the argument of the
107# function.  If the spaces were trimmed, the variable name would be "", and
108# that variable is indeed undefined.  Since CondParser_FuncCallEmpty allows
109# subexpressions to be based on undefined variables, the value of the
110# undefined variable "" would be returned as an empty string.
111${:U }=	space
112.if empty( )
113.  error
114.endif
115
116# The value of the following expression is " word", which is not empty.  To be
117# empty, _all_ characters in the expression value have to be whitespace, not
118# only the first.
119.if empty(:U word)
120.  error
121.endif
122
123# The :L modifier creates an expression that has the same value as
124# its name, which both are "VAR" in this case.  The value is therefore not
125# empty.
126.if empty(VAR:L)
127.  error
128.endif
129
130# The variable WORD has the value "word", which does not count as empty.
131.if empty(WORD)
132.  error
133.endif
134
135# The expression ${} for a variable with the empty name always evaluates
136# to an empty string (see Var_Parse, varUndefined).
137.if !empty()
138.  error
139.endif
140
141# Ensure that expressions that appear as part of the function call
142# argument are properly parsed.  Typical use cases for this are .for loops,
143# which are expanded to exactly these ${:U} expressions.
144#
145# The argument expands to "WORD", and that variable is defined at the
146# beginning of this file.  The surrounding 'W' and 'D' ensure that
147# CondParser_FuncCallEmpty keeps track of the parsing position, both before
148# and after the call to Var_Parse.
149.if empty(W${:UOR}D)
150.  error
151.endif
152
153# There may be spaces outside the parentheses.
154# Spaces inside the parentheses are interpreted as part of the variable name.
155.if ! empty ( WORD )
156.  error
157.endif
158
159${:U WORD }=	variable name with spaces
160
161# Now there is a variable named " WORD ", and it is not empty.
162.if empty ( WORD )
163.  error
164.endif
165
166# expect+1: Unclosed variable "WORD"
167.if empty(WORD
168.  error
169.else
170.  error
171.endif
172
173# Since cond.c 1.76 from 2020-06-28 and before var.c 1.226 from 2020-07-02,
174# the following example generated a wrong error message "Variable VARNAME is
175# recursive".
176#
177# Since at least 1993, the manual page claimed that irrelevant parts of
178# conditions were not evaluated, but that was wrong for a long time.  The
179# expressions in irrelevant parts of the condition were actually evaluated,
180# they just allowed undefined variables to be used in the conditions.  These
181# unnecessary evaluations were fixed in several commits, starting with var.c
182# 1.226 from 2020-07-02.
183#
184# In this example, the variable "VARNAME2" is not defined, so evaluation of
185# the condition should have stopped at this point, and the rest of the
186# condition should have been processed in parse-only mode.  The right-hand
187# side containing the '!empty' was evaluated though, as it had always been.
188#
189# When evaluating the !empty condition, the variable name was parsed as
190# "VARNAME${:U2}", but without expanding any nested expression, in
191# this case the ${:U2}.  The expression '${:U2}' was replaced with an empty
192# string, the resulting variable name was thus "VARNAME".  This conceptually
193# wrong variable name should have been discarded quickly after parsing it, to
194# prevent it from doing any harm.
195#
196# The expression was evaluated, and this was wrong.  The evaluation was done
197# without VARE_EVAL (called VARF_WANTRES back then) though.  This had the
198# effect that the ${:U1} from the value of VARNAME evaluated to an empty
199# string.  This in turn created the seemingly recursive definition
200# VARNAME=${VARNAME}, and that definition was evaluated even though it was
201# never meant to be evaluated.
202#
203# This was fixed by evaluating nested expressions in the variable name only
204# when the whole expression was evaluated as well.
205VARNAME=	${VARNAME${:U1}}
206.if defined(VARNAME${:U2}) && !empty(VARNAME${:U2})
207.endif
208
209# Expressions in the argument of a function call don't have to be defined.
210.if !empty(${UNDEF})
211.  error
212.endif
213
214# If the word 'empty' is not followed by '(', it is not a function call but an
215# ordinary bare word.  This bare word is interpreted as 'defined(empty)', and
216# since there is no variable named 'empty', the condition evaluates to false.
217.if empty
218.  error
219.endif
220
221empty=		# defined but empty
222.if empty
223.else
224.  error
225.endif
226