aboutsummaryrefslogtreecommitdiff
path: root/contrib/bmake/unit-tests/cond-func-empty.mk
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/bmake/unit-tests/cond-func-empty.mk')
-rw-r--r--contrib/bmake/unit-tests/cond-func-empty.mk112
1 files changed, 67 insertions, 45 deletions
diff --git a/contrib/bmake/unit-tests/cond-func-empty.mk b/contrib/bmake/unit-tests/cond-func-empty.mk
index 24cb7a680b2a..ab097b026dcb 100644
--- a/contrib/bmake/unit-tests/cond-func-empty.mk
+++ b/contrib/bmake/unit-tests/cond-func-empty.mk
@@ -1,18 +1,19 @@
-# $NetBSD: cond-func-empty.mk,v 1.17 2021/12/28 22:13:56 rillig Exp $
+# $NetBSD: cond-func-empty.mk,v 1.29 2025/06/11 18:49:58 sjg Exp $
#
-# Tests for the empty() function in .if conditions, which tests a variable
+# Tests for the empty() function in .if conditions, which tests an
# expression for emptiness.
#
-# Note that the argument in the parentheses is a variable name, not a variable
-# expression, optionally followed by variable modifiers.
+# Note that the argument in the parentheses is a variable name, not an
+# expression. That name may be followed by ':...' modifiers.
#
.undef UNDEF
EMPTY= # empty
SPACE= ${:U }
+ZERO= 0
WORD= word
-# An undefined variable is empty.
+# An undefined variable counts as empty.
.if !empty(UNDEF)
. error
.endif
@@ -24,11 +25,13 @@ WORD= word
. error
.endif
-# The :S modifier replaces the empty value with an actual word. The
-# expression is now no longer empty, but it is still based on an undefined
-# variable (DEF_UNDEF). There are a few variable modifiers that turn an
-# undefined expression into a defined expression, among them :U and :D, but
-# not :S.
+# The :S modifier replaces the empty value with an actual word. After
+# applying the :S modifier to the expression, its value is 'empty', so it is
+# no longer empty, but it is still based on an undefined variable. There are
+# a few modifiers that turn an undefined expression into a defined expression,
+# among them :U and :D, but not :S. Therefore, at the end of evaluating the
+# expression, the expression is still undefined, so its final value becomes an
+# empty string.
#
# XXX: This is hard to explain to someone who doesn't know these
# implementation details.
@@ -45,15 +48,17 @@ WORD= word
. error
.endif
-# And now to the surprising part. Applying the following :S modifier to the
-# undefined expression makes it non-empty, but the expression is still in
-# state DEF_UNDEF. The :U modifier that follows only looks at the state
-# DEF_UNDEF to decide whether the variable is defined or not. This kind of
-# makes sense since the :U modifier tests the _variable_, not the
+# When an expression is based on an undefined variable, its modifiers interact
+# in sometimes surprising ways. Applying the :S modifier to the undefined
+# expression makes its value non-empty, but doesn't change that the expression
+# is based on an undefined variable. The :U modifier that follows only looks
+# at the definedness state to decide whether the variable is defined or not.
+# This kind of makes sense since the :U modifier tests the _variable_, not the
# _expression_.
#
-# But since the variable was undefined to begin with, the fallback value from
-# the :U modifier is used in this expression.
+# Since the variable was undefined to begin with, the fallback value from the
+# :U modifier is used in this expression, instead of keeping the 'value' from
+# the :S modifier.
#
.if ${UNDEF:S,^$,value,W:Ufallback} != "fallback"
. error
@@ -74,6 +79,17 @@ WORD= word
. error
.endif
+# The variable ZERO has the numeric value 0, but is not empty. This is a
+# subtle difference between using either 'empty(ZERO)' or the expression
+# '${ZERO}' in a condition.
+.if empty(ZERO)
+. error
+.elif ${ZERO}
+. error
+.elif ${ZERO} == ""
+. error
+.endif
+
# The following example constructs an expression with the variable name ""
# and the value " ". This expression counts as empty since the value contains
# only whitespace.
@@ -88,21 +104,23 @@ WORD= word
# Now the variable named " " gets a non-empty value, which demonstrates that
# neither leading nor trailing spaces are trimmed in the argument of the
-# function. If the spaces were trimmed, the variable name would be "" and
-# that variable is indeed undefined. Since CondParser_FuncCallEmpty calls
-# Var_Parse without VARE_UNDEFERR, the value of the undefined variable is
-# returned as an empty string.
+# function. If the spaces were trimmed, the variable name would be "", and
+# that variable is indeed undefined. Since CondParser_FuncCallEmpty allows
+# subexpressions to be based on undefined variables, the value of the
+# undefined variable "" would be returned as an empty string.
${:U }= space
.if empty( )
. error
.endif
-# The value of the following expression is " word", which is not empty.
+# The value of the following expression is " word", which is not empty. To be
+# empty, _all_ characters in the expression value have to be whitespace, not
+# only the first.
.if empty(:U word)
. error
.endif
-# The :L modifier creates a variable expression that has the same value as
+# The :L modifier creates an expression that has the same value as
# its name, which both are "VAR" in this case. The value is therefore not
# empty.
.if empty(VAR:L)
@@ -120,20 +138,21 @@ ${:U }= space
. error
.endif
-# Ensure that variable expressions that appear as part of the argument are
-# properly parsed. Typical use cases for this are .for loops, which are
-# expanded to exactly these ${:U} expressions.
+# Ensure that expressions that appear as part of the function call
+# argument are properly parsed. Typical use cases for this are .for loops,
+# which are expanded to exactly these ${:U} expressions.
#
-# If everything goes well, the argument expands to "WORD", and that variable
-# is defined at the beginning of this file. The surrounding 'W' and 'D'
-# ensure that CondParser_FuncCallEmpty keeps track of the parsing position,
-# both before and after the call to Var_Parse.
+# The argument expands to "WORD", and that variable is defined at the
+# beginning of this file. The surrounding 'W' and 'D' ensure that
+# CondParser_FuncCallEmpty keeps track of the parsing position, both before
+# and after the call to Var_Parse.
.if empty(W${:UOR}D)
. error
.endif
-# There may be spaces at the outside of the parentheses.
+# There may be spaces outside the parentheses.
# Spaces inside the parentheses are interpreted as part of the variable name.
+# expect+1: warning: Invalid character " " in variable name " WORD "
.if ! empty ( WORD )
. error
.endif
@@ -145,7 +164,7 @@ ${:U WORD }= variable name with spaces
. error
.endif
-# Parse error: missing closing parenthesis.
+# expect+1: Unclosed variable "WORD"
.if empty(WORD
. error
.else
@@ -159,10 +178,9 @@ ${:U WORD }= variable name with spaces
# Since at least 1993, the manual page claimed that irrelevant parts of
# conditions were not evaluated, but that was wrong for a long time. The
# expressions in irrelevant parts of the condition were actually evaluated,
-# they just allowed undefined variables to be used in the conditions, and the
-# result of evaluating them was not used further. These unnecessary
-# evaluations were fixed in several commits, starting with var.c 1.226 from
-# 2020-07-02.
+# they just allowed undefined variables to be used in the conditions. These
+# unnecessary evaluations were fixed in several commits, starting with var.c
+# 1.226 from 2020-07-02.
#
# In this example, the variable "VARNAME2" is not defined, so evaluation of
# the condition should have stopped at this point, and the rest of the
@@ -170,25 +188,29 @@ ${:U WORD }= variable name with spaces
# side containing the '!empty' was evaluated though, as it had always been.
#
# When evaluating the !empty condition, the variable name was parsed as
-# "VARNAME${:U2}", but without expanding any nested variable expression, in
+# "VARNAME${:U2}", but without expanding any nested expression, in
# this case the ${:U2}. The expression '${:U2}' was replaced with an empty
# string, the resulting variable name was thus "VARNAME". This conceptually
# wrong variable name should have been discarded quickly after parsing it, to
# prevent it from doing any harm.
#
-# The variable expression was expanded though, and this was wrong. The
-# expansion was done without VARE_WANTRES (called VARF_WANTRES back
-# then) though. This had the effect that the ${:U1} from the value of VARNAME
-# expanded to an empty string. This in turn created the seemingly recursive
-# definition VARNAME=${VARNAME}, and that definition was never meant to be
-# expanded.
+# The expression was evaluated, and this was wrong. The evaluation was done
+# without VARE_EVAL (called VARF_WANTRES back then) though. This had the
+# effect that the ${:U1} from the value of VARNAME evaluated to an empty
+# string. This in turn created the seemingly recursive definition
+# VARNAME=${VARNAME}, and that definition was evaluated even though it was
+# never meant to be evaluated.
#
-# This was fixed by expanding nested variable expressions in the variable name
-# only if the flag VARE_WANTRES is given.
+# This was fixed by evaluating nested expressions in the variable name only
+# when the whole expression was evaluated as well.
VARNAME= ${VARNAME${:U1}}
.if defined(VARNAME${:U2}) && !empty(VARNAME${:U2})
.endif
+# Expressions in the argument of a function call don't have to be defined.
+.if !empty(${UNDEF})
+. error
+.endif
# If the word 'empty' is not followed by '(', it is not a function call but an
# ordinary bare word. This bare word is interpreted as 'defined(empty)', and