aboutsummaryrefslogtreecommitdiff
path: root/contrib/bmake/unit-tests
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/bmake/unit-tests')
-rw-r--r--contrib/bmake/unit-tests/Makefile63
-rwxr-xr-xcontrib/bmake/unit-tests/cmd-interrupt.exp1
-rwxr-xr-xcontrib/bmake/unit-tests/cmd-interrupt.mk4
-rw-r--r--contrib/bmake/unit-tests/cond-cmp-numeric.exp8
-rw-r--r--contrib/bmake/unit-tests/cond-cmp-numeric.mk11
-rw-r--r--contrib/bmake/unit-tests/cond-cmp-string.exp16
-rw-r--r--contrib/bmake/unit-tests/cond-cmp-string.mk10
-rw-r--r--contrib/bmake/unit-tests/cond-func-empty.exp4
-rw-r--r--contrib/bmake/unit-tests/cond-func-empty.mk47
-rw-r--r--contrib/bmake/unit-tests/cond-short.exp11
-rw-r--r--contrib/bmake/unit-tests/cond-short.mk140
-rw-r--r--contrib/bmake/unit-tests/cond-token-number.exp1
-rw-r--r--contrib/bmake/unit-tests/cond-token-number.mk22
-rw-r--r--contrib/bmake/unit-tests/cond-token-plain.exp1
-rw-r--r--contrib/bmake/unit-tests/cond-token-plain.mk8
-rwxr-xr-xcontrib/bmake/unit-tests/cond-undef-lint.exp3
-rwxr-xr-xcontrib/bmake/unit-tests/dep-var.exp24
-rwxr-xr-xcontrib/bmake/unit-tests/dep-var.mk21
-rw-r--r--contrib/bmake/unit-tests/deptgt-delete_on_error.exp2
-rw-r--r--contrib/bmake/unit-tests/meta-ignore.inc63
-rw-r--r--contrib/bmake/unit-tests/opt-debug-lint.exp2
-rw-r--r--contrib/bmake/unit-tests/opt.mk4
-rw-r--r--contrib/bmake/unit-tests/parse-var.exp6
-rw-r--r--contrib/bmake/unit-tests/parse-var.mk40
-rw-r--r--contrib/bmake/unit-tests/var-eval-short.exp4
-rw-r--r--contrib/bmake/unit-tests/var-scope-cmdline.exp4
-rw-r--r--contrib/bmake/unit-tests/var-scope-cmdline.mk6
-rw-r--r--contrib/bmake/unit-tests/varcmd.mk6
-rw-r--r--contrib/bmake/unit-tests/varmod-ifelse.exp7
-rw-r--r--contrib/bmake/unit-tests/varmod-ifelse.mk66
-rw-r--r--contrib/bmake/unit-tests/varmod-loop.exp6
-rw-r--r--contrib/bmake/unit-tests/varmod-loop.mk24
-rw-r--r--contrib/bmake/unit-tests/varmod-no-match.mk98
-rw-r--r--contrib/bmake/unit-tests/varmod-order-shuffle.mk7
-rw-r--r--contrib/bmake/unit-tests/varmod-order.exp2
-rw-r--r--contrib/bmake/unit-tests/varmod-order.mk16
-rw-r--r--contrib/bmake/unit-tests/varname-dot-make-meta-ignore_filter.exp10
-rw-r--r--contrib/bmake/unit-tests/varname-dot-make-meta-ignore_filter.mk7
-rw-r--r--contrib/bmake/unit-tests/varname-dot-make-meta-ignore_paths.exp10
-rw-r--r--contrib/bmake/unit-tests/varname-dot-make-meta-ignore_paths.mk7
-rw-r--r--contrib/bmake/unit-tests/varname-dot-make-meta-ignore_patterns.exp10
-rw-r--r--contrib/bmake/unit-tests/varname-dot-make-meta-ignore_patterns.mk7
-rw-r--r--contrib/bmake/unit-tests/varname-dot-makeflags.exp11
-rw-r--r--contrib/bmake/unit-tests/varname-dot-makeflags.mk39
-rw-r--r--contrib/bmake/unit-tests/varname-dot-makeoverrides.exp7
-rw-r--r--contrib/bmake/unit-tests/varname-dot-makeoverrides.mk25
-rw-r--r--contrib/bmake/unit-tests/varname-makeflags.exp20
-rw-r--r--contrib/bmake/unit-tests/varname-makeflags.mk182
-rw-r--r--contrib/bmake/unit-tests/varparse-errors.exp8
-rw-r--r--contrib/bmake/unit-tests/varparse-errors.mk3
50 files changed, 837 insertions, 267 deletions
diff --git a/contrib/bmake/unit-tests/Makefile b/contrib/bmake/unit-tests/Makefile
index f4d4425e204c..bcfe853642a5 100644
--- a/contrib/bmake/unit-tests/Makefile
+++ b/contrib/bmake/unit-tests/Makefile
@@ -1,6 +1,6 @@
-# $Id: Makefile,v 1.191 2023/01/24 06:09:49 sjg Exp $
+# $Id: Makefile,v 1.193 2023/02/25 20:03:25 sjg Exp $
#
-# $NetBSD: Makefile,v 1.331 2023/01/24 00:24:02 sjg Exp $
+# $NetBSD: Makefile,v 1.333 2023/02/25 19:30:32 sjg Exp $
#
# Unit tests for make(1)
#
@@ -32,6 +32,21 @@
.MAKE.OS?= ${uname -s:L:sh}
.MAKE.UID?= ${id -u:L:sh}
+# for many tests we need a TMPDIR that will not collide
+# with other users.
+.if ${.OBJDIR} != ${.CURDIR}
+# easy
+TMPDIR:= ${.OBJDIR}/tmp
+.elif defined(TMPDIR)
+TMPDIR:= ${TMPDIR}/uid${.MAKE.UID}
+.else
+TMPDIR:= /tmp/uid${.MAKE.UID}
+.endif
+# make sure it exists
+.if !exist(${TMPDIR})
+_!= mkdir -p ${TMPDIR}
+.endif
+
# Each test is in a sub-makefile.
# Keep the list sorted.
# Any test that is commented out must be ignored in
@@ -406,12 +421,16 @@ TESTS+= varname-dot-make-makefiles
TESTS+= varname-dot-make-meta-bailiwick
TESTS+= varname-dot-make-meta-created
TESTS+= varname-dot-make-meta-files
+.if ${.MAKE.PATH_FILEMON:Uno:Nktrace:N/dev*} == "" && ${TMPDIR:N/tmp*:N/var/tmp*} != ""
+# these tests will not work if TMPDIR is or is a subdir of
+# /tmp or /var/tmp
TESTS+= varname-dot-make-meta-ignore_filter
TESTS+= varname-dot-make-meta-ignore_paths
TESTS+= varname-dot-make-meta-ignore_patterns
+TESTS+= varname-dot-make-path_filemon
+.endif
TESTS+= varname-dot-make-meta-prefix
TESTS+= varname-dot-make-mode
-TESTS+= varname-dot-make-path_filemon
TESTS+= varname-dot-make-pid
TESTS+= varname-dot-make-ppid
TESTS+= varname-dot-make-save_dollars
@@ -538,6 +557,9 @@ SED_CMDS.opt-chdir= -e 's,\(nonexistent\).[1-9][0-9]*,\1,' \
-e '/name/s,file,File,' \
-e 's,no such,No such,' \
-e 's,Filename,File name,'
+
+# meta line numbers can vary based on filemon implementation
+SED_CMDS.meta-ignore= -e 's,\(\.meta:\) [1-9][0-9]*:,\1 <line>:,'
SED_CMDS.opt-debug-graph1= ${STD_SED_CMDS.dg1}
SED_CMDS.opt-debug-graph2= ${STD_SED_CMDS.dg2}
SED_CMDS.opt-debug-graph3= ${STD_SED_CMDS.dg3}
@@ -571,6 +593,9 @@ SED_CMDS.var-op-shell+= ${STD_SED_CMDS.white-space}
SED_CMDS.vardebug+= -e 's,${.SHELL},</path/to/shell>,'
SED_CMDS.varmod-subst-regex+= ${STD_SED_CMDS.regex}
SED_CMDS.varparse-errors+= ${STD_SED_CMDS.timestamp}
+SED_CMDS.varname-dot-make-meta-ignore_filter+= ${SED_CMDS.meta-ignore}
+SED_CMDS.varname-dot-make-meta-ignore_paths+= ${SED_CMDS.meta-ignore}
+SED_CMDS.varname-dot-make-meta-ignore_patterns+= ${SED_CMDS.meta-ignore}
SED_CMDS.varname-dot-parsedir= -e '/in some cases/ s,^make: "[^"]*,make: "<normalized>,'
SED_CMDS.varname-dot-parsefile= -e '/in some cases/ s,^make: "[^"]*,make: "<normalized>,'
SED_CMDS.varname-dot-shell= -e 's, = /[^ ]*, = (details omitted),g'
@@ -713,22 +738,6 @@ LANG= C
_MKMSG_TEST= :
.endif
-
-# for many tests we need a TMPDIR that will not collide
-# with other users.
-.if ${.OBJDIR} != ${.CURDIR}
-# easy
-TMPDIR:= ${.OBJDIR}/tmp
-.elif defined(TMPDIR)
-TMPDIR:= ${TMPDIR}/uid${.MAKE.UID}
-.else
-TMPDIR:= /tmp/uid${.MAKE.UID}
-.endif
-# make sure it exists
-.if !exist(${TMPDIR})
-_!= mkdir -p ${TMPDIR}
-.endif
-
# Some Linux systems such as Fedora have deprecated egrep in favor of grep -E.
.if ${.MAKE.OS:NLinux} == ""
EGREP= grep -E
@@ -768,23 +777,23 @@ LIMIT_RESOURCES?= :
# Postprocess the test output to make the output platform-independent.
#
-# always pretend .MAKE was called 'make'
-_SED_CMDS+= -e 's,^${TEST_MAKE:T:S,.,\\.,g}[][0-9]*:,make:,'
-_SED_CMDS+= -e 's,${TEST_MAKE:S,.,\\.,g},make,'
-_SED_CMDS+= -e 's,^usage: ${TEST_MAKE:T:S,.,\\.,g} ,usage: make ,'
-# replace anything after 'stopped in' with unit-tests
+# Replace anything after 'stopped in' with unit-tests
_SED_CMDS+= -e '/stopped/s, /.*, unit-tests,'
# Allow the test files to be placed anywhere.
_SED_CMDS+= -e 's,\(\.PARSEDIR}\) = `'"/[^']*'"',\1 = <some-dir>,'
_SED_CMDS+= -e 's,\(\.INCLUDEDFROMDIR}\) = `'"/[^']*'"',\1 = <some-dir>,'
-_SED_CMDS+= -e 's,${TMPDIR},<tmpdir>,g'
+_SED_CMDS+= -e 's,${TMPDIR},<tmpdir>,g' -e 's,${TMPDIR:tA},<tmpdir>,g'
# canonicalize ${.OBJDIR} and ${.CURDIR}
+_SED_CMDS+= -e 's,${.CURDIR},<curdir>,g'
.if ${.OBJDIR} != ${.CURDIR}
# yes this is inaccurate but none of the tests expect <objdir> anywhere
# which we get depending on how MAKEOBJDIR is set.
-_SED_CMDS+= -e 's,${.OBJDIR},<curdir>,g'
+_SED_CMDS+= -e 's,${.OBJDIR},<curdir>,g' -e 's,${.OBJDIR:tA},<curdir>,g'
.endif
-_SED_CMDS+= -e 's,${.CURDIR},<curdir>,g'
+# always pretend .MAKE was called 'make'
+_SED_CMDS+= -e 's,^${TEST_MAKE:T:S,.,\\.,g}[][0-9]*:,make:,'
+_SED_CMDS+= -e 's,${TEST_MAKE:S,.,\\.,g},make,'
+_SED_CMDS+= -e 's,^usage: ${TEST_MAKE:T:S,.,\\.,g} ,usage: make ,'
_SED_CMDS+= -e 's,<curdir>/,,g'
_SED_CMDS+= -e 's,${UNIT_TESTS:S,.,\\.,g}/,,g'
_SED_CMDS+= -e '/MAKE_VERSION/d'
diff --git a/contrib/bmake/unit-tests/cmd-interrupt.exp b/contrib/bmake/unit-tests/cmd-interrupt.exp
index 91f4439e7bea..242db1d9507d 100755
--- a/contrib/bmake/unit-tests/cmd-interrupt.exp
+++ b/contrib/bmake/unit-tests/cmd-interrupt.exp
@@ -2,7 +2,6 @@
make: *** cmd-interrupt-ordinary removed
interrupt-ordinary: ok
> cmd-interrupt-phony
-make: *** cmd-interrupt-phony removed
interrupt-phony: ok
> cmd-interrupt-precious
interrupt-precious: ok
diff --git a/contrib/bmake/unit-tests/cmd-interrupt.mk b/contrib/bmake/unit-tests/cmd-interrupt.mk
index fa0d85fc9063..140651b55c62 100755
--- a/contrib/bmake/unit-tests/cmd-interrupt.mk
+++ b/contrib/bmake/unit-tests/cmd-interrupt.mk
@@ -1,4 +1,4 @@
-# $NetBSD: cmd-interrupt.mk,v 1.3 2020/11/15 14:07:53 rillig Exp $
+# $NetBSD: cmd-interrupt.mk,v 1.4 2023/03/18 22:20:12 sjg Exp $
#
# Tests for interrupting a command.
#
@@ -30,7 +30,7 @@ interrupt-ordinary:
interrupt-phony: .PHONY
@${.MAKE} ${MAKEFLAGS} -f ${MAKEFILE} cmd-interrupt-phony || true
# The ././ is necessary to work around the file cache.
- @echo ${.TARGET}: ${exists(././cmd-interrupt-phony) :? error : ok }
+ @echo ${.TARGET}: ${exists(././cmd-interrupt-phony) :? ok : error }
interrupt-precious: .PRECIOUS
@${.MAKE} ${MAKEFLAGS} -f ${MAKEFILE} cmd-interrupt-precious || true
diff --git a/contrib/bmake/unit-tests/cond-cmp-numeric.exp b/contrib/bmake/unit-tests/cond-cmp-numeric.exp
index c03ecd1b311e..69a8a1e4fca0 100644
--- a/contrib/bmake/unit-tests/cond-cmp-numeric.exp
+++ b/contrib/bmake/unit-tests/cond-cmp-numeric.exp
@@ -1,15 +1,15 @@
CondParser_Eval: !(${:UINF} > 1e100)
-make: "cond-cmp-numeric.mk" line 11: Comparison with '>' requires both operands 'INF' and '1e100' to be numeric
+make: "cond-cmp-numeric.mk" line 15: Comparison with '>' requires both operands 'INF' and '1e100' to be numeric
CondParser_Eval: ${:UNaN} > NaN
-make: "cond-cmp-numeric.mk" line 16: Comparison with '>' requires both operands 'NaN' and 'NaN' to be numeric
+make: "cond-cmp-numeric.mk" line 21: Comparison with '>' requires both operands 'NaN' and 'NaN' to be numeric
CondParser_Eval: !(${:UNaN} == NaN)
Comparing "NaN" == "NaN"
CondParser_Eval: 123 ! 123
-make: "cond-cmp-numeric.mk" line 34: Malformed conditional (123 ! 123)
+make: "cond-cmp-numeric.mk" line 38: Malformed conditional (123 ! 123)
CondParser_Eval: ${:U 123} < 124
Comparing 123.000000 < 124.000000
CondParser_Eval: ${:U123 } < 124
-make: "cond-cmp-numeric.mk" line 50: Comparison with '<' requires both operands '123 ' and '124' to be numeric
+make: "cond-cmp-numeric.mk" line 54: Comparison with '<' requires both operands '123 ' and '124' to be numeric
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/contrib/bmake/unit-tests/cond-cmp-numeric.mk b/contrib/bmake/unit-tests/cond-cmp-numeric.mk
index 5386e4a97297..e025b99b27cd 100644
--- a/contrib/bmake/unit-tests/cond-cmp-numeric.mk
+++ b/contrib/bmake/unit-tests/cond-cmp-numeric.mk
@@ -1,6 +1,9 @@
-# $NetBSD: cond-cmp-numeric.mk,v 1.6 2022/09/04 22:55:00 rillig Exp $
+# $NetBSD: cond-cmp-numeric.mk,v 1.7 2023/03/04 08:07:29 rillig Exp $
#
# Tests for numeric comparisons in .if conditions.
+#
+# See also:
+# cond-token-number.mk
.MAKEFLAGS: -dc
@@ -8,11 +11,13 @@
# Even if strtod(3) parses "INF" as +Infinity, make does not accept this
# since it is not really a number; see TryParseNumber.
+# expect+1: Comparison with '>' requires both operands 'INF' and '1e100' to be numeric
.if !(${:UINF} > 1e100)
. error
.endif
# Neither is NaN a number; see TryParseNumber.
+# expect+1: Comparison with '>' requires both operands 'NaN' and 'NaN' to be numeric
.if ${:UNaN} > NaN
. error
.endif
@@ -29,8 +34,7 @@
# whether the operator is valid, leaving the rest of the work to the
# evaluation functions EvalCompareNum and EvalCompareStr. Ensure that this
# parse error is properly reported.
-#
-# XXX: The warning message does not mention the actual operator.
+# expect+1: Malformed conditional (123 ! 123)
.if 123 ! 123
. error
.else
@@ -54,4 +58,3 @@
.endif
all:
- @:;
diff --git a/contrib/bmake/unit-tests/cond-cmp-string.exp b/contrib/bmake/unit-tests/cond-cmp-string.exp
index c9c7a0777383..e0aabfdadca4 100644
--- a/contrib/bmake/unit-tests/cond-cmp-string.exp
+++ b/contrib/bmake/unit-tests/cond-cmp-string.exp
@@ -1,11 +1,11 @@
-make: "cond-cmp-string.mk" line 18: Malformed conditional (str != str)
-make: "cond-cmp-string.mk" line 42: Malformed conditional ("string" != "str""ing")
-make: "cond-cmp-string.mk" line 49: Malformed conditional (!("value" = "value"))
-make: "cond-cmp-string.mk" line 56: Malformed conditional (!("value" === "value"))
-make: "cond-cmp-string.mk" line 113: Comparison with '<' requires both operands 'string' and 'string' to be numeric
-make: "cond-cmp-string.mk" line 120: Comparison with '<=' requires both operands 'string' and 'string' to be numeric
-make: "cond-cmp-string.mk" line 127: Comparison with '>' requires both operands 'string' and 'string' to be numeric
-make: "cond-cmp-string.mk" line 134: Comparison with '>=' requires both operands 'string' and 'string' to be numeric
+make: "cond-cmp-string.mk" line 19: Malformed conditional (str != str)
+make: "cond-cmp-string.mk" line 44: Malformed conditional ("string" != "str""ing")
+make: "cond-cmp-string.mk" line 52: Malformed conditional (!("value" = "value"))
+make: "cond-cmp-string.mk" line 60: Malformed conditional (!("value" === "value"))
+make: "cond-cmp-string.mk" line 118: Comparison with '<' requires both operands 'string' and 'string' to be numeric
+make: "cond-cmp-string.mk" line 126: Comparison with '<=' requires both operands 'string' and 'string' to be numeric
+make: "cond-cmp-string.mk" line 134: Comparison with '>' requires both operands 'string' and 'string' to be numeric
+make: "cond-cmp-string.mk" line 142: Comparison with '>=' requires both operands 'string' and 'string' to be numeric
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/contrib/bmake/unit-tests/cond-cmp-string.mk b/contrib/bmake/unit-tests/cond-cmp-string.mk
index 8b504ba0c0d6..44d8beceacdd 100644
--- a/contrib/bmake/unit-tests/cond-cmp-string.mk
+++ b/contrib/bmake/unit-tests/cond-cmp-string.mk
@@ -1,4 +1,4 @@
-# $NetBSD: cond-cmp-string.mk,v 1.16 2022/05/08 06:51:27 rillig Exp $
+# $NetBSD: cond-cmp-string.mk,v 1.17 2023/03/28 14:38:29 rillig Exp $
#
# Tests for string comparisons in .if conditions.
@@ -15,6 +15,7 @@
# The left-hand side of the comparison must be enclosed in quotes.
# This one is not enclosed in quotes and thus generates an error message.
+# expect+1: Malformed conditional (str != str)
.if str != str
. error
.endif
@@ -39,6 +40,7 @@
# It is not possible to concatenate two string literals to form a single
# string. In C, Python and the shell this is possible, but not in make.
+# expect+1: Malformed conditional ("string" != "str""ing")
.if "string" != "str""ing"
. error
.else
@@ -46,6 +48,7 @@
.endif
# There is no = operator for strings.
+# expect+1: Malformed conditional (!("value" = "value"))
.if !("value" = "value")
. error
.else
@@ -53,6 +56,7 @@
.endif
# There is no === operator for strings either.
+# expect+1: Malformed conditional (!("value" === "value"))
.if !("value" === "value")
. error
.else
@@ -110,6 +114,7 @@
.endif
# Strings cannot be compared relationally, only for equality.
+# expect+1: Comparison with '<' requires both operands 'string' and 'string' to be numeric
.if "string" < "string"
. error
.else
@@ -117,6 +122,7 @@
.endif
# Strings cannot be compared relationally, only for equality.
+# expect+1: Comparison with '<=' requires both operands 'string' and 'string' to be numeric
.if "string" <= "string"
. error
.else
@@ -124,6 +130,7 @@
.endif
# Strings cannot be compared relationally, only for equality.
+# expect+1: Comparison with '>' requires both operands 'string' and 'string' to be numeric
.if "string" > "string"
. error
.else
@@ -131,6 +138,7 @@
.endif
# Strings cannot be compared relationally, only for equality.
+# expect+1: Comparison with '>=' requires both operands 'string' and 'string' to be numeric
.if "string" >= "string"
. error
.else
diff --git a/contrib/bmake/unit-tests/cond-func-empty.exp b/contrib/bmake/unit-tests/cond-func-empty.exp
index d1dfda7c03ee..35d83adad5cb 100644
--- a/contrib/bmake/unit-tests/cond-func-empty.exp
+++ b/contrib/bmake/unit-tests/cond-func-empty.exp
@@ -1,5 +1,5 @@
-make: "cond-func-empty.mk" line 149: Unclosed variable "WORD"
-make: "cond-func-empty.mk" line 149: Malformed conditional (empty(WORD)
+make: "cond-func-empty.mk" line 153: Unclosed variable "WORD"
+make: "cond-func-empty.mk" line 153: Malformed conditional (empty(WORD)
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/contrib/bmake/unit-tests/cond-func-empty.mk b/contrib/bmake/unit-tests/cond-func-empty.mk
index 24cb7a680b2a..293305281298 100644
--- a/contrib/bmake/unit-tests/cond-func-empty.mk
+++ b/contrib/bmake/unit-tests/cond-func-empty.mk
@@ -1,4 +1,4 @@
-# $NetBSD: cond-func-empty.mk,v 1.17 2021/12/28 22:13:56 rillig Exp $
+# $NetBSD: cond-func-empty.mk,v 1.18 2023/03/04 21:15:30 rillig Exp $
#
# Tests for the empty() function in .if conditions, which tests a variable
# expression for emptiness.
@@ -24,11 +24,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, it 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 +47,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
@@ -90,8 +94,8 @@ WORD= word
# 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.
+# Var_Parse without VARE_UNDEFERR, the value of the undefined variable ""
+# would be returned as an empty string.
${:U }= space
.if empty( )
. error
@@ -120,9 +124,9 @@ ${: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 variable 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'
@@ -159,10 +163,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
diff --git a/contrib/bmake/unit-tests/cond-short.exp b/contrib/bmake/unit-tests/cond-short.exp
index 2865dcb6ef33..745d7e912c2b 100644
--- a/contrib/bmake/unit-tests/cond-short.exp
+++ b/contrib/bmake/unit-tests/cond-short.exp
@@ -7,10 +7,7 @@ expected M pattern
expected or
expected or exists
expected or empty
-defined(V42) && ${V42} > 0: Ok
-defined(V66) && ( "${iV2}" < ${V42} ): Ok
-1 || ${iV1} < ${V42}: Ok
-1 || ${iV2:U2} < ${V42}: Ok
-0 || ${iV1} <= ${V42}: Ok
-0 || ${iV2:U2} < ${V42}: Ok
-exit status 0
+make: "cond-short.mk" line 214: Comparison with '<' requires both operands '' and '42' to be numeric
+make: Fatal errors encountered -- cannot continue
+make: stopped in unit-tests
+exit status 1
diff --git a/contrib/bmake/unit-tests/cond-short.mk b/contrib/bmake/unit-tests/cond-short.mk
index f4e8f87043b5..525ff7d0f2ab 100644
--- a/contrib/bmake/unit-tests/cond-short.mk
+++ b/contrib/bmake/unit-tests/cond-short.mk
@@ -1,4 +1,4 @@
-# $NetBSD: cond-short.mk,v 1.19 2021/12/27 18:54:19 rillig Exp $
+# $NetBSD: cond-short.mk,v 1.20 2023/03/04 13:42:36 rillig Exp $
#
# Demonstrates that in conditions, the right-hand side of an && or ||
# is only evaluated if it can actually influence the result.
@@ -135,28 +135,32 @@ VAR= # empty again, for the following tests
.elif ${echo "unexpected nested or" 1>&2 :L:sh}
.endif
-# make sure these do not cause complaint
-#.MAKEFLAGS: -dc
-# TODO: Rewrite this whole section and check all the conditions and variables.
-# Several of the assumptions are probably wrong here.
-# TODO: replace 'x=' with '.info' or '.error'.
-V42= 42
-iV1= ${V42}
-iV2= ${V66}
+NUMBER= 42
+INDIR_NUMBER= ${NUMBER}
+INDIR_UNDEF= ${UNDEF}
-.if defined(V42) && ${V42} > 0
-x= Ok
+.if defined(NUMBER) && ${NUMBER} > 0
.else
-x= Fail
+. error
.endif
-x!= echo 'defined(V42) && $${V42} > 0: $x' >&2; echo
-# With cond.c 1.76 from 2020-07-03, the following condition triggered a
-# warning: "String comparison operator should be either == or !=".
-# This was because the variable expression ${iV2} was defined, but the
-# contained variable V66 was undefined. The left-hand side of the comparison
-# therefore evaluated to the string "${V66}", which is obviously not a number.
+# Starting with var.c 1.226 from from 2020-07-02, the following condition
+# triggered a warning: "String comparison operator should be either == or !=".
+#
+# The left-hand side of the '&&' evaluated to false, which should have made
+# the right-hand side irrelevant.
+#
+# On the right-hand side of the '&&', the expression ${INDIR_UNDEF} was
+# defined and had the value '${UNDEF}', but the nested variable UNDEF was
+# undefined. The right hand side "${INDIR_UNDEF}" still needed to be parsed,
+# and in parse-only mode, the "value" of the parsed expression was the
+# uninterpreted variable value, in this case '${UNDEF}'. And even though the
+# right hand side of the '&&' should have been irrelevant, the two sides of
+# the comparison were still parsed and evaluated. Comparing these two values
+# numerically was not possible since the string '${UNDEF}' is not a number,
+# so the comparison fell back to string comparison, which then complained
+# about the '>' operator.
#
# This was fixed in cond.c 1.79 from 2020-07-09 by not evaluating irrelevant
# comparisons. Instead, they are only parsed and then discarded.
@@ -164,59 +168,77 @@ x!= echo 'defined(V42) && $${V42} > 0: $x' >&2; echo
# At that time, there was not enough debug logging to see the details in the
# -dA log. To actually see it, add debug logging at the beginning and end of
# Var_Parse.
-.if defined(V66) && ( ${iV2} < ${V42} )
-x= Fail
-.else
-x= Ok
+.if defined(UNDEF) && ${INDIR_UNDEF} < ${NUMBER}
+. error
+.endif
+# Adding a ':U' modifier to the irrelevant expression didn't help, as that
+# expression was only parsed, not evaluated. The resulting literal string
+# '${INDIR_UNDEF:U2}' was not numeric either, for the same reason as above.
+.if defined(UNDEF) && ${INDIR_UNDEF:U2} < ${NUMBER}
+. error
.endif
-# XXX: This condition doesn't match the one above. The quotes are missing
-# above. This is a crucial detail since without quotes, the variable
-# expression ${iV2} evaluates to "${V66}", and with quotes, it evaluates to ""
-# since undefined variables are allowed and expand to an empty string.
-x!= echo 'defined(V66) && ( "$${iV2}" < $${V42} ): $x' >&2; echo
-.if 1 || ${iV1} < ${V42}
-x= Ok
-.else
-x= Fail
+# Enclosing the expression in double quotes changes how that expression is
+# evaluated. In irrelevant expressions that are enclosed in double quotes,
+# expressions based on undefined variables are allowed and evaluate to an
+# empty string.
+#
+# The manual page stated from at least 1993 on that irrelevant conditions were
+# not evaluated, but that was wrong. These conditions were evaluated, the
+# only difference was that undefined variables in them didn't trigger an
+# error. Since numeric conditions are quite rare, this subtle difference
+# didn't catch much attention, as most other conditions such as pattern
+# matches or equality comparisons worked fine and never produced error
+# messages.
+.if defined(UNDEF) && "${INDIR_UNDEF}" < ${NUMBER}
+. error
.endif
-x!= echo '1 || $${iV1} < $${V42}: $x' >&2; echo
-# With cond.c 1.76 from 2020-07-03, the following condition triggered a
-# warning: "String comparison operator should be either == or !=".
-# This was because the variable expression ${iV2} was defined, but the
-# contained variable V66 was undefined. The left-hand side of the comparison
-# therefore evaluated to the string "${V66}", which is obviously not a number.
+# Since the condition is relevant, the indirect undefined variable is
+# evaluated as usual, resolving nested undefined expressions to an empty
+# string.
#
-# This was fixed in cond.c 1.79 from 2020-07-09 by not evaluating irrelevant
-# comparisons. Instead, they are only parsed and then discarded.
+# Comparing an empty string numerically is not possible, however, make has an
+# ugly hack in TryParseNumber that treats an empty string as a valid numerical
+# value, thus hiding bugs in the makefile.
+.if ${INDIR_UNDEF} < ${NUMBER}
+# only due to the ugly hack
+.else
+. error
+.endif
+
+# Due to the quotes around the left-hand side of the '<', the operand is
+# marked as a string, thus preventing a numerical comparison.
#
-# At that time, there was not enough debug logging to see the details in the
-# -dA log. To actually see it, add debug logging at the beginning and end of
-# Var_Parse.
-.if 1 || ${iV2:U2} < ${V42}
-x= Ok
+# expect+1: Comparison with '<' requires both operands '' and '42' to be numeric
+.if "${INDIR_UNDEF}" < ${NUMBER}
+. info yes
.else
-x= Fail
+. info no
.endif
-x!= echo '1 || $${iV2:U2} < $${V42}: $x' >&2; echo
-# the same expressions are fine when the lhs is expanded
-# ${iV1} expands to 42
-.if 0 || ${iV1} <= ${V42}
-x= Ok
+# The right-hand side of '||' is irrelevant and thus not evaluated.
+.if 1 || ${INDIR_NUMBER} < ${NUMBER}
.else
-x= Fail
+. error
+.endif
+
+# The right-hand side of '||' is relevant and thus evaluated normally.
+.if 0 || ${INDIR_NUMBER} < ${NUMBER}
+. error
.endif
-x!= echo '0 || $${iV1} <= $${V42}: $x' >&2; echo
-# ${iV2:U2} expands to 2
-.if 0 || ${iV2:U2} < ${V42}
-x= Ok
+# The right-hand side of '||' evaluates to an empty string, as the variable
+# 'INDIR_UNDEF' is defined, therefore the modifier ':U2' has no effect.
+# Comparing an empty string numerically is not possible, however, make has an
+# ugly hack in TryParseNumber that treats an empty string as a valid numerical
+# value, thus hiding bugs in the makefile.
+.if 0 || ${INDIR_UNDEF:U2} < ${NUMBER}
+# only due to the ugly hack
.else
-x= Fail
+. error
.endif
-x!= echo '0 || $${iV2:U2} < $${V42}: $x' >&2; echo
+
# The right-hand side of the '&&' is irrelevant since the left-hand side
# already evaluates to false. Before cond.c 1.79 from 2020-07-09, it was
@@ -229,8 +251,8 @@ x!= echo '0 || $${iV2:U2} < $${V42}: $x' >&2; echo
# Ensure that irrelevant conditions do not influence the result of the whole
# condition. As of cond.c 1.302 from 2021-12-11, an irrelevant function call
-# evaluates to true (see CondParser_FuncCall and CondParser_FuncCallEmpty), an
-# irrelevant comparison evaluates to false (see CondParser_Comparison).
+# evaluated to true (see CondParser_FuncCall and CondParser_FuncCallEmpty), an
+# irrelevant comparison evaluated to false (see CondParser_Comparison).
#
# An irrelevant true bubbles up to the outermost CondParser_And, where it is
# ignored. An irrelevant false bubbles up to the outermost CondParser_Or,
diff --git a/contrib/bmake/unit-tests/cond-token-number.exp b/contrib/bmake/unit-tests/cond-token-number.exp
index f078cb007323..1d472b63bc77 100644
--- a/contrib/bmake/unit-tests/cond-token-number.exp
+++ b/contrib/bmake/unit-tests/cond-token-number.exp
@@ -2,7 +2,6 @@ make: "cond-token-number.mk" line 15: Malformed conditional (-0)
make: "cond-token-number.mk" line 25: Malformed conditional (+0)
make: "cond-token-number.mk" line 35: Malformed conditional (!-1)
make: "cond-token-number.mk" line 45: Malformed conditional (!+1)
-make: "cond-token-number.mk" line 89: End of the tests.
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/contrib/bmake/unit-tests/cond-token-number.mk b/contrib/bmake/unit-tests/cond-token-number.mk
index eef528f4b7c6..d1dd7371f447 100644
--- a/contrib/bmake/unit-tests/cond-token-number.mk
+++ b/contrib/bmake/unit-tests/cond-token-number.mk
@@ -1,4 +1,4 @@
-# $NetBSD: cond-token-number.mk,v 1.7 2022/01/02 02:57:39 rillig Exp $
+# $NetBSD: cond-token-number.mk,v 1.8 2023/03/04 08:07:29 rillig Exp $
#
# Tests for number tokens in .if conditions.
#
@@ -85,7 +85,21 @@ HEX= dead
. error
.endif
-# Ensure that parsing continues until here.
-.info End of the tests.
+# Very small numbers round to 0.
+.if 12345e-400
+. error
+.endif
+.if 12345e-200
+.else
+. error
+.endif
+
+# Very large numbers round up to infinity on IEEE 754 implementations, or to
+# the largest representable number (VAX); in particular, make does not fall
+# back to checking whether a variable of that name is defined.
+.if 12345e400
+.else
+. error
+.endif
-all: # nothing
+all:
diff --git a/contrib/bmake/unit-tests/cond-token-plain.exp b/contrib/bmake/unit-tests/cond-token-plain.exp
index a508bf62a5ea..572e8b3b2c9f 100644
--- a/contrib/bmake/unit-tests/cond-token-plain.exp
+++ b/contrib/bmake/unit-tests/cond-token-plain.exp
@@ -49,6 +49,7 @@ make: "cond-token-plain.mk" line 172: Now the variable '\\' is defined.
CondParser_Eval: "unquoted\"quoted" != unquoted"quoted
Comparing "unquoted"quoted" != "unquoted"quoted"
CondParser_Eval: $$$$$$$$ != ""
+make: "cond-token-plain.mk" line 186: Malformed conditional ($$$$$$$$ != "")
CondParser_Eval: left == right
make: "cond-token-plain.mk" line 195: Malformed conditional (left == right)
CondParser_Eval: ${0:?:} || left == right
diff --git a/contrib/bmake/unit-tests/cond-token-plain.mk b/contrib/bmake/unit-tests/cond-token-plain.mk
index 5fb4a72b74a5..54bdd4fbf626 100644
--- a/contrib/bmake/unit-tests/cond-token-plain.mk
+++ b/contrib/bmake/unit-tests/cond-token-plain.mk
@@ -1,4 +1,4 @@
-# $NetBSD: cond-token-plain.mk,v 1.16 2022/09/25 12:51:37 rillig Exp $
+# $NetBSD: cond-token-plain.mk,v 1.17 2023/02/14 20:49:09 rillig Exp $
#
# Tests for plain tokens (that is, string literals without quotes)
# in .if conditions. These are also called bare words.
@@ -89,7 +89,7 @@
# a coincidence that the '!' is both used in the '!=' comparison operator
# as well as for negating a comparison result.
#
-# The boolean operators '&' and '|' don't terminate a comparison operand.
+# The characters '&' and '|' are part of the comparison operand.
.if ${:Uvar}&&name != "var&&name"
. error
.endif
@@ -97,8 +97,8 @@
. error
.endif
-# A bare word may appear alone in a condition, without any comparison
-# operator. It is implicitly converted into defined(bare).
+# A bare word may occur alone in a condition, without any comparison
+# operator. It is interpreted as the function call 'defined(bare)'.
.if bare
. error
.else
diff --git a/contrib/bmake/unit-tests/cond-undef-lint.exp b/contrib/bmake/unit-tests/cond-undef-lint.exp
index 2c4feb0376ff..55073b2116c6 100755
--- a/contrib/bmake/unit-tests/cond-undef-lint.exp
+++ b/contrib/bmake/unit-tests/cond-undef-lint.exp
@@ -1,7 +1,10 @@
make: "cond-undef-lint.mk" line 23: Variable "UNDEF" is undefined
+make: "cond-undef-lint.mk" line 23: Malformed conditional (${UNDEF})
make: "cond-undef-lint.mk" line 38: Variable "UNDEF" is undefined
make: "cond-undef-lint.mk" line 38: Variable "VAR." is undefined
+make: "cond-undef-lint.mk" line 38: Malformed conditional (${VAR.${UNDEF}})
make: "cond-undef-lint.mk" line 49: Variable "VAR.defined" is undefined
+make: "cond-undef-lint.mk" line 49: Malformed conditional (${VAR.${DEF}})
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/contrib/bmake/unit-tests/dep-var.exp b/contrib/bmake/unit-tests/dep-var.exp
index d32aca455ceb..4e38057bf6b9 100755
--- a/contrib/bmake/unit-tests/dep-var.exp
+++ b/contrib/bmake/unit-tests/dep-var.exp
@@ -1,3 +1,27 @@
+Var_Parse: ${UNDEF1} (eval-defined)
+Global: .ALLTARGETS = all
+Global: .ALLTARGETS = all ${DEF2}
+Global: .ALLTARGETS = all ${DEF2} a-${DEF2}-b
+Global: .ALLTARGETS = all ${DEF2} a-${DEF2}-b ${UNDEF3}
+Global: .ALLTARGETS = all ${DEF2} a-${DEF2}-b ${UNDEF3} 1-${INDIRECT_1}-1
+Global: INDIRECT_1 = 2-$${INDIRECT_2}-2
+Global: INDIRECT_2 = 3-$${INDIRECT_3}-3
+Global: INDIRECT_3 = indirect
+Global: UNDEF1 = undef1
+Global: DEF2 = def2
+Global: .ALLTARGETS = all ${DEF2} a-${DEF2}-b ${UNDEF3} 1-${INDIRECT_1}-1 $$)
+Var_Parse: ${:U\$)}: (eval-defined)
+Evaluating modifier ${:U...} on value "" (eval-defined, undefined)
+Result of ${:U\$)} is "$)" (eval-defined, defined)
+Global: .ALLTARGETS = all ${DEF2} a-${DEF2}-b ${UNDEF3} 1-${INDIRECT_1}-1 $$) undef1
+Global: .ALLTARGETS = all ${DEF2} a-${DEF2}-b ${UNDEF3} 1-${INDIRECT_1}-1 $$) undef1 def2
+Global: .ALLTARGETS = all ${DEF2} a-${DEF2}-b ${UNDEF3} 1-${INDIRECT_1}-1 $$) undef1 def2 a-def2-b
+Var_Parse: $INDIRECT_2-2-1 $): (parse-only)
+Global: .ALLTARGETS = all ${DEF2} a-${DEF2}-b ${UNDEF3} 1-${INDIRECT_1}-1 $$) undef1 def2 a-def2-b 1-2-$INDIRECT_2-2-1
+Var_Parse: $): (parse-only)
+Global: .ALLTARGETS = all ${DEF2} a-${DEF2}-b ${UNDEF3} 1-${INDIRECT_1}-1 $$) undef1 def2 a-def2-b 1-2-$INDIRECT_2-2-1 $)
+Global: .MAKEFLAGS = -r -k -d v -d
+Global: .MAKEFLAGS = -r -k -d v -d 0
make: Malformed variable expression at "$)"
def2
a-def2-b
diff --git a/contrib/bmake/unit-tests/dep-var.mk b/contrib/bmake/unit-tests/dep-var.mk
index 4503424e31ab..f4a724f0ce7c 100755
--- a/contrib/bmake/unit-tests/dep-var.mk
+++ b/contrib/bmake/unit-tests/dep-var.mk
@@ -1,14 +1,16 @@
-# $NetBSD: dep-var.mk,v 1.6 2021/04/04 10:13:09 rillig Exp $
+# $NetBSD: dep-var.mk,v 1.7 2023/02/13 21:01:46 rillig Exp $
#
# Tests for variable references in dependency declarations.
#
# Uh oh, this feels so strange that probably nobody uses it. But it seems to
# be the only way to reach the lower half of SuffExpandChildren.
-# XXX: The -dv log says:
-# Var_Parse: ${UNDEF1} with VARE_UNDEFERR|VARE_WANTRES
-# but no error message is generated for this line.
-# The variable expression ${UNDEF1} simply expands to an empty string.
+.MAKEFLAGS: -dv
+
+# expect: Var_Parse: ${UNDEF1} (eval-defined)
+# Even though undefined expressions should lead to errors, no error message is
+# generated for this line. The variable expression ${UNDEF1} simply expands
+# to an empty string.
all: ${UNDEF1}
# Using a double dollar in order to circumvent immediate variable expansion
@@ -20,8 +22,8 @@ all: ${UNDEF1}
all: $${DEF2} a-$${DEF2}-b
# This variable is not defined at all.
-# XXX: The -dv log says:
-# Var_Parse: ${UNDEF3} with VARE_UNDEFERR|VARE_WANTRES
+# XXX: The -dv log says later when expanding the sources of 'all':
+# Var_Parse: ${UNDEF3} (eval-defined)
# but no error message is generated for this line, just like for UNDEF1.
# The variable expression ${UNDEF3} simply expands to an empty string.
all: $${UNDEF3}
@@ -81,8 +83,13 @@ all: $$$$)
# Since 2020-09-13, this generates a parse error in lint mode (-dL), but not
# in normal mode since ParseDependency does not handle any errors after
# calling Var_Parse.
+# expect: Var_Parse: ${:U\$)}: (eval-defined)
+# expect: Var_Parse: $INDIRECT_2-2-1 $): (parse-only)
+# expect: Var_Parse: $): (parse-only)
undef1 def2 a-def2-b 1-2-$$INDIRECT_2-2-1 ${:U\$)}:
@echo ${.TARGET:Q}
+.MAKEFLAGS: -d0
+
# XXX: Why is the exit status still 0, even though Parse_Error is called
# with PARSE_FATAL in SuffExpandChildren?
diff --git a/contrib/bmake/unit-tests/deptgt-delete_on_error.exp b/contrib/bmake/unit-tests/deptgt-delete_on_error.exp
index 9d9f1dc3e5ec..75fbbe12472f 100644
--- a/contrib/bmake/unit-tests/deptgt-delete_on_error.exp
+++ b/contrib/bmake/unit-tests/deptgt-delete_on_error.exp
@@ -7,10 +7,8 @@ make: *** deptgt-delete_on_error-regular removed
make: *** deptgt-delete_on_error-regular-delete removed
> deptgt-delete_on_error-phony; false
*** Error code 1 (continuing)
-make: *** deptgt-delete_on_error-phony removed
> deptgt-delete_on_error-phony-delete; false
*** Error code 1 (continuing)
-make: *** deptgt-delete_on_error-phony-delete removed
> deptgt-delete_on_error-precious; false
*** Error code 1 (continuing)
> deptgt-delete_on_error-precious-delete; false
diff --git a/contrib/bmake/unit-tests/meta-ignore.inc b/contrib/bmake/unit-tests/meta-ignore.inc
new file mode 100644
index 000000000000..ed74f4d79017
--- /dev/null
+++ b/contrib/bmake/unit-tests/meta-ignore.inc
@@ -0,0 +1,63 @@
+# $NetBSD: meta-ignore.inc,v 1.2 2023/02/25 19:30:32 sjg Exp $
+
+# common logic for testing .MAKE.META.IGNORE_*
+
+# we want a directory outside of .OBJDIR to drop a file
+# that our meta file refers to.
+# Note: these tests will not work if TMPDIR is /tmp or /var/tmp
+# or a subdir thereof
+IGNORE:= ${TMPDIR}/ignore
+OBJ:= ${TMPDIR}/obj
+
+# this is always ignored so make sure it isn't used above
+TMPDIR= /tmp/nothanks
+
+all: one two three
+
+setup:
+ @mkdir -p ${IGNORE} ${OBJ}
+ @echo > ${IGNORE}/check
+ @rm -f ${OBJ}/check-ignore
+
+makefile:= ${.INCLUDEDFROMDIR}/${.INCLUDEDFROMFILE}
+TEST:= ${.INCLUDEDFROMFILE:R:C,.*meta-,,:S,-,_,g:tu}
+
+DESC.one= Initialize check-ignore.meta
+DESC.two= Use .MAKE.META.${TEST} - check-ignore is up to date
+DESC.three= Skip .MAKE.META.${TEST} - check-ignore is out of date
+
+# just in case someone runs us with -jN
+.ORDER: one two three
+one two three: .MAKE setup
+ @echo "${DESC.${.TARGET}}"; \
+ ${MAKE} -C ${.CURDIR} -f ${makefile} check-ignore parent=${.TARGET}
+
+.if make(check-ignore)
+.MAKEFLAGS: -dM
+.MAKE.MODE = meta verbose silent=yes
+.OBJDIR: ${OBJ}
+.if ${parent} == "two"
+.if ${TEST} == "IGNORE_PATHS"
+# this is a prefix list - any path that matches
+# one of these prefixes will be ignored
+.MAKE.META.IGNORE_PATHS = ${IGNORE}
+.elif ${TEST} == "IGNORE_PATTERNS"
+# more flexible but more expensive
+# this example is equivalent to M*/ignore/*
+# a match means ignore
+.MAKE.META.IGNORE_PATTERNS = */ignore/*
+.elif ${TEST} == "IGNORE_FILTER"
+# this is the most flexible, but also most expensive
+# if this expands to nothing - ignore the path
+.MAKE.META.IGNORE_FILTER = N${IGNORE}/*
+.endif
+.endif
+
+# : < just reads from ${IGNORE}/check
+# so that our filemon trace will have a reference to it
+# we ensure it is always newer than the target.
+check-ignore: .META .NOPATH
+ @: < ${IGNORE}/check > ${.TARGET}
+ @sleep 1; echo ${.TARGET} > ${IGNORE}/check
+
+.endif
diff --git a/contrib/bmake/unit-tests/opt-debug-lint.exp b/contrib/bmake/unit-tests/opt-debug-lint.exp
index 05b341b30dae..83643e9a8772 100644
--- a/contrib/bmake/unit-tests/opt-debug-lint.exp
+++ b/contrib/bmake/unit-tests/opt-debug-lint.exp
@@ -1,5 +1,7 @@
make: "opt-debug-lint.mk" line 19: Variable "X" is undefined
+make: "opt-debug-lint.mk" line 19: Malformed conditional ($X)
make: "opt-debug-lint.mk" line 41: Variable "UNDEF" is undefined
+make: "opt-debug-lint.mk" line 41: Malformed conditional (${UNDEF})
make: "opt-debug-lint.mk" line 61: Missing delimiter ':' after modifier "L"
make: "opt-debug-lint.mk" line 61: Missing delimiter ':' after modifier "P"
make: "opt-debug-lint.mk" line 69: Unknown modifier "${"
diff --git a/contrib/bmake/unit-tests/opt.mk b/contrib/bmake/unit-tests/opt.mk
index 0931a66d3d15..939d5ec35aeb 100644
--- a/contrib/bmake/unit-tests/opt.mk
+++ b/contrib/bmake/unit-tests/opt.mk
@@ -1,4 +1,4 @@
-# $NetBSD: opt.mk,v 1.6 2020/11/18 01:06:59 sjg Exp $
+# $NetBSD: opt.mk,v 1.7 2023/02/25 00:07:08 rillig Exp $
#
# Tests for the command line options.
@@ -7,7 +7,7 @@
all: .IGNORE
# The options from the top-level make are passed to the sub-makes via
# the environment variable MAKEFLAGS. This is where the " -r -k -d 0"
- # comes from. See MainParseArg.
+ # comes from. See MainParseOption.
${MAKE} -r -f /dev/null -V MAKEFLAGS
@echo
diff --git a/contrib/bmake/unit-tests/parse-var.exp b/contrib/bmake/unit-tests/parse-var.exp
index bae925e8c869..39a9383953dd 100644
--- a/contrib/bmake/unit-tests/parse-var.exp
+++ b/contrib/bmake/unit-tests/parse-var.exp
@@ -1,5 +1 @@
-make: Unfinished modifier for "BRACE_GROUP" (',' missing)
-make: "parse-var.mk" line 130: Malformed conditional (0 && ${BRACE_GROUP:S,${BRACE_PAIR:S,{,{{,},<lbraces>,})
-make: Fatal errors encountered -- cannot continue
-make: stopped in unit-tests
-exit status 1
+exit status 0
diff --git a/contrib/bmake/unit-tests/parse-var.mk b/contrib/bmake/unit-tests/parse-var.mk
index cca6931d14a1..6205664c558e 100644
--- a/contrib/bmake/unit-tests/parse-var.mk
+++ b/contrib/bmake/unit-tests/parse-var.mk
@@ -1,4 +1,4 @@
-# $NetBSD: parse-var.mk,v 1.6 2022/09/25 21:26:23 rillig Exp $
+# $NetBSD: parse-var.mk,v 1.8 2023/02/18 11:16:09 rillig Exp $
#
# Tests for parsing variable expressions.
#
@@ -68,8 +68,7 @@
#
# Effects:
# How much does the parsing position advance (pp)?
-# What's the value of the expression (out_val)?
-# What's the status after parsing the expression (VarParseResult)?
+# What's the value of the expression (return value)?
# What error messages are printed (Parse_Error)?
# What no-effect error messages are printed (Error)?
# What error messages should be printed but aren't?
@@ -86,12 +85,9 @@ VAR.${:U param }= value
. error
.endif
-# XXX: The following paragraph already uses past tense, in the hope that the
-# parsing behavior can be cleaned up soon.
-
-# Since var.c 1.323 from 2020-07-26 18:11 and except for var.c 1.1028 from
-# 2022-08-08, the exact way of parsing an expression depended on whether the
-# expression was actually evaluated or merely parsed.
+# Since var.c 1.323 from 2020-07-26 18:11 and until var.c 1.1047 from
+# 2023-02-18, the exact way of parsing an expression with subexpressions
+# depended on whether the expression was actually evaluated or merely parsed.
#
# If it was evaluated, nested expressions were parsed correctly, parsing each
# modifier according to its exact definition (see varmod.mk).
@@ -103,30 +99,28 @@ VAR.${:U param }= value
# expression was not parsed correctly. Instead, make only counted the opening
# and closing delimiters, which failed for nested modifiers with unbalanced
# braces.
-#
-# This naive brace counting was implemented in ParseModifierPartDollar. As of
-# var.c 1.1029, there are still several other places that merely count braces
-# instead of properly parsing subexpressions.
#.MAKEFLAGS: -dcpv
# Keep these braces outside the conditions below, to keep them simple to
-# understand. If the BRACE_PAIR had been replaced with ':U{}', the '}' would
-# have to be escaped, but not the '{'. This asymmetry would have made the
-# example even more complicated to understand.
+# understand. If the expression ${BRACE_PAIR:...} had been replaced with the
+# literal ${:U{}}, the '}' would have to be escaped, but not the '{'. This
+# asymmetry would have made the example even more complicated to understand.
BRACE_PAIR= {}
-# In this test word, the '{{}' in the middle will be replaced.
+# In this test word, the below conditions will replace the '{{}' in the middle
+# with the string '<lbraces>'.
BRACE_GROUP= {{{{}}}}
# The inner ':S' modifier turns the word '{}' into '{{}'.
# The outer ':S' modifier then replaces '{{}' with '<lbraces>'.
-# In the first case, the outer expression is relevant and is parsed correctly.
+# Due to the always-true condition '1', the outer expression is relevant and
+# is parsed correctly.
.if 1 && ${BRACE_GROUP:S,${BRACE_PAIR:S,{,{{,},<lbraces>,}
.endif
-# In the second case, the outer expression was irrelevant. In this case, in
-# the parts of the outer ':S' modifier, make only counted the braces, and since
-# the inner expression '${BRACE_PAIR:...}' contains more '{' than '}', parsing
-# failed with the error message 'Unfinished modifier for "BRACE_GROUP"'. Fixed
-# in var.c 1.1028 from 2022-08-08, reverted in var.c 1.1029 from 2022-08-23.
+# Due to the always-false condition '0', the outer expression is irrelevant.
+# In this case, in the parts of the outer ':S' modifier, the expression parser
+# only counted the braces, and since the inner expression '${BRACE_PAIR:...}'
+# contains more '{' than '}', parsing failed with the error message 'Unfinished
+# modifier for "BRACE_GROUP"'. Fixed in var.c 1.1047 from 2023-02-18.
.if 0 && ${BRACE_GROUP:S,${BRACE_PAIR:S,{,{{,},<lbraces>,}
.endif
#.MAKEFLAGS: -d0
diff --git a/contrib/bmake/unit-tests/var-eval-short.exp b/contrib/bmake/unit-tests/var-eval-short.exp
index f574a6444e1b..34380ec61c41 100644
--- a/contrib/bmake/unit-tests/var-eval-short.exp
+++ b/contrib/bmake/unit-tests/var-eval-short.exp
@@ -7,7 +7,9 @@ make: "var-eval-short.mk" line 98: Malformed conditional (0 && ${:Uword:localtim
CondParser_Eval: 0 && ${0:?${FAIL}then:${FAIL}else}
Var_Parse: ${0:?${FAIL}then:${FAIL}else} (parse-only)
Parsing modifier ${0:?...}
+Var_Parse: ${FAIL}then:${FAIL}else} (parse-only)
Modifier part: "${FAIL}then"
+Var_Parse: ${FAIL}else} (parse-only)
Modifier part: "${FAIL}else"
Result of ${0:?${FAIL}then:${FAIL}else} is "" (parse-only, defined)
Parsing line 163: DEFINED= defined
@@ -17,7 +19,9 @@ Var_Parse: ${DEFINED:L:?${FAIL}then:${FAIL}else} (parse-only)
Parsing modifier ${DEFINED:L}
Result of ${DEFINED:L} is "defined" (parse-only, regular)
Parsing modifier ${DEFINED:?...}
+Var_Parse: ${FAIL}then:${FAIL}else} (parse-only)
Modifier part: "${FAIL}then"
+Var_Parse: ${FAIL}else} (parse-only)
Modifier part: "${FAIL}else"
Result of ${DEFINED:?${FAIL}then:${FAIL}else} is "defined" (parse-only, regular)
Parsing line 166: .MAKEFLAGS: -d0
diff --git a/contrib/bmake/unit-tests/var-scope-cmdline.exp b/contrib/bmake/unit-tests/var-scope-cmdline.exp
index a1227a1dd1f2..281b5af291ca 100644
--- a/contrib/bmake/unit-tests/var-scope-cmdline.exp
+++ b/contrib/bmake/unit-tests/var-scope-cmdline.exp
@@ -1,4 +1,4 @@
-make: "var-scope-cmdline.mk" line 67: global
-make: "var-scope-cmdline.mk" line 76: makeflags
+make: "var-scope-cmdline.mk" line 71: global
+make: "var-scope-cmdline.mk" line 80: makeflags
makeflags
exit status 0
diff --git a/contrib/bmake/unit-tests/var-scope-cmdline.mk b/contrib/bmake/unit-tests/var-scope-cmdline.mk
index 1f4a3e700253..fbfb2d0290a4 100644
--- a/contrib/bmake/unit-tests/var-scope-cmdline.mk
+++ b/contrib/bmake/unit-tests/var-scope-cmdline.mk
@@ -1,4 +1,4 @@
-# $NetBSD: var-scope-cmdline.mk,v 1.1 2022/01/23 16:25:54 rillig Exp $
+# $NetBSD: var-scope-cmdline.mk,v 1.2 2023/04/07 05:54:16 rillig Exp $
#
# Tests for variables specified on the command line.
#
@@ -61,6 +61,10 @@
# Most cmdline variables are set at the very beginning, when parsing the
# command line arguments. Using the special target '.MAKEFLAGS', it is
# possible to set cmdline variables at any later time.
+#
+# See also:
+# varcmd.mk
+# varname-makeflags.mk
# A normal global variable, without any cmdline variable nearby.
VAR= global
diff --git a/contrib/bmake/unit-tests/varcmd.mk b/contrib/bmake/unit-tests/varcmd.mk
index 12739df30926..ec0cf96ed75c 100644
--- a/contrib/bmake/unit-tests/varcmd.mk
+++ b/contrib/bmake/unit-tests/varcmd.mk
@@ -1,4 +1,4 @@
-# $NetBSD: varcmd.mk,v 1.6 2021/02/16 19:43:09 rillig Exp $
+# $NetBSD: varcmd.mk,v 1.7 2023/04/07 05:54:16 rillig Exp $
#
# Test behaviour of recursive make and vars set on command line.
#
@@ -12,6 +12,10 @@
# be rewritten to make it clear why there is a difference and why this is
# actually intended. Removing that large block of code makes only this test
# and vardebug.mk fail, which is not enough.
+#
+# See also:
+# var-scope-cmdline.mk
+# varname-makeflags.mk
FU= fu
FOO?= foo
diff --git a/contrib/bmake/unit-tests/varmod-ifelse.exp b/contrib/bmake/unit-tests/varmod-ifelse.exp
index 80361ebf6d61..94cbcbdeae82 100644
--- a/contrib/bmake/unit-tests/varmod-ifelse.exp
+++ b/contrib/bmake/unit-tests/varmod-ifelse.exp
@@ -27,6 +27,13 @@ make: "varmod-ifelse.mk" line 167: true
make: "varmod-ifelse.mk" line 169: false
make: Bad conditional expression ' ' in ' ?true:false'
make: "varmod-ifelse.mk" line 171:
+CondParser_Eval: 0 && ${1:?${:Uthen0:S,}},,}:${:Uelse0:S,}},,}} != "not evaluated"
+CondParser_Eval: 1 && ${0:?${:Uthen1:S,}},,}:${:Uelse1:S,}},,}} != "else1"
+CondParser_Eval: 0
+Comparing "else1" != "else1"
+CondParser_Eval: 2 && ${1:?${:Uthen2:S,}},,}:${:Uelse2:S,}},,}} != "then2"
+CondParser_Eval: 1
+Comparing "then2" != "then2"
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/contrib/bmake/unit-tests/varmod-ifelse.mk b/contrib/bmake/unit-tests/varmod-ifelse.mk
index 2d1c54943ca1..0df45cae6870 100644
--- a/contrib/bmake/unit-tests/varmod-ifelse.mk
+++ b/contrib/bmake/unit-tests/varmod-ifelse.mk
@@ -1,4 +1,4 @@
-# $NetBSD: varmod-ifelse.mk,v 1.20 2022/09/25 12:51:37 rillig Exp $
+# $NetBSD: varmod-ifelse.mk,v 1.21 2023/02/18 18:23:58 rillig Exp $
#
# Tests for the ${cond:?then:else} variable modifier, which evaluates either
# the then-expression or the else-expression, depending on the condition.
@@ -182,3 +182,67 @@ PRIMES= 2 3 5 7 11
"1:not_prime 2:prime 3:prime 4:not_prime 5:prime"
. error
.endif
+
+# When parsing the modifier ':?', there are 3 possible cases:
+#
+# 1. The whole expression is only parsed.
+# 2. The expression is parsed and the 'then' branch is evaluated.
+# 3. The expression is parsed and the 'else' branch is evaluated.
+#
+# In all of these cases, the expression must be parsed in the same way,
+# especially when one of the branches contains unbalanced '{}' braces.
+#
+# At 2020-01-01, the expressions from the 'then' and 'else' branches were
+# parsed differently, depending on whether the branch was taken or not. When
+# the branch was taken, the parser recognized that in the modifier ':S,}},,',
+# the '}}' were ordinary characters. When the branch was not taken, the
+# parser only counted balanced '{' and '}', ignoring any escaping or other
+# changes in the interpretation.
+#
+# In var.c 1.285 from 2020-07-20, the parsing of the expressions changed so
+# that in both cases the expression is parsed in the same way, taking the
+# unbalanced braces in the ':S' modifiers into account. This change was not
+# on purpose, the commit message mentioned 'has the same effect', which was a
+# wrong assumption.
+#
+# In var.c 1.323 from 2020-07-26, the unintended fix from var.c 1.285 was
+# reverted, still not knowing about the difference between regular parsing and
+# balanced-mode parsing.
+#
+# In var.c 1.1028 from 2022-08-08, there was another attempt at fixing this
+# inconsistency in parsing, but since that broke parsing of the modifier ':@',
+# it was reverted in var.c 1.1029 from 2022-08-23.
+#
+# In var.c 1.1047 from 2023-02-18, the inconsistency in parsing was finally
+# fixed. The modifier ':@' now parses the body in balanced mode, while
+# everywhere else the modifier parts have their subexpressions parsed in the
+# same way, no matter whether they are evaluated or not.
+#
+# The modifiers ':@' and ':?' are similar in that they conceptually contain
+# text to be evaluated later or conditionally, still they parse that text
+# differently. The crucial difference is that the body of the modifier ':@'
+# is always parsed using balanced mode. The modifier ':?', on the other hand,
+# must parse both of its branches in the same way, no matter whether they are
+# evaluated or not. Since balanced mode and standard mode are incompatible,
+# it's impossible to use balanced mode in the modifier ':?'.
+.MAKEFLAGS: -dc
+.if 0 && ${1:?${:Uthen0:S,}},,}:${:Uelse0:S,}},,}} != "not evaluated"
+# At 2020-01-07, the expression evaluated to 'then0,,}}', even though it was
+# irrelevant as the '0' had already been evaluated to 'false'.
+. error
+.endif
+.if 1 && ${0:?${:Uthen1:S,}},,}:${:Uelse1:S,}},,}} != "else1"
+. error
+.endif
+.if 2 && ${1:?${:Uthen2:S,}},,}:${:Uelse2:S,}},,}} != "then2"
+# At 2020-01-07, the whole expression evaluated to 'then2,,}}' instead of the
+# expected 'then2'. The 'then' branch of the ':?' modifier was parsed
+# normally, parsing and evaluating the ':S' modifier, thereby treating the
+# '}}' as ordinary characters and resulting in 'then2'. The 'else' branch was
+# parsed in balanced mode, ignoring that the inner '}}' were ordinary
+# characters. The '}}' were thus interpreted as the end of the 'else' branch
+# and the whole expression. This left the trailing ',,}}', which together
+# with the 'then2' formed the result 'then2,,}}'.
+. error
+.endif
+.MAKEFLAGS: -d0
diff --git a/contrib/bmake/unit-tests/varmod-loop.exp b/contrib/bmake/unit-tests/varmod-loop.exp
index 9b432c806885..356946f63a9b 100644
--- a/contrib/bmake/unit-tests/varmod-loop.exp
+++ b/contrib/bmake/unit-tests/varmod-loop.exp
@@ -1,10 +1,10 @@
-Parsing line 78: USE_8_DOLLARS= ${:U1:@var@${8_DOLLARS}@} ${8_DOLLARS} $$$$$$$$
+Parsing line 91: USE_8_DOLLARS= ${:U1:@var@${8_DOLLARS}@} ${8_DOLLARS} $$$$$$$$
CondParser_Eval: ${USE_8_DOLLARS} != "\$\$\$\$ \$\$\$\$ \$\$\$\$"
Comparing "$$$$ $$$$ $$$$" != "$$$$ $$$$ $$$$"
-Parsing line 83: SUBST_CONTAINING_LOOP:= ${USE_8_DOLLARS}
+Parsing line 96: SUBST_CONTAINING_LOOP:= ${USE_8_DOLLARS}
CondParser_Eval: ${SUBST_CONTAINING_LOOP} != "\$\$ \$\$\$\$ \$\$\$\$"
Comparing "$$ $$$$ $$$$" != "$$ $$$$ $$$$"
-Parsing line 108: .MAKEFLAGS: -d0
+Parsing line 121: .MAKEFLAGS: -d0
ParseDependency(.MAKEFLAGS: -d0)
:varname-overwriting-target: :x1y x2y x3y: ::
mod-loop-dollar:1:
diff --git a/contrib/bmake/unit-tests/varmod-loop.mk b/contrib/bmake/unit-tests/varmod-loop.mk
index d3cc0228efd9..d09e49a6d842 100644
--- a/contrib/bmake/unit-tests/varmod-loop.mk
+++ b/contrib/bmake/unit-tests/varmod-loop.mk
@@ -1,6 +1,20 @@
-# $NetBSD: varmod-loop.mk,v 1.21 2022/08/23 21:13:46 rillig Exp $
+# $NetBSD: varmod-loop.mk,v 1.23 2023/02/18 11:55:20 rillig Exp $
#
-# Tests for the :@var@...${var}...@ variable modifier.
+# Tests for the expression modifier ':@var@body@', which replaces each word of
+# the expression with the expanded body, which may contain references to the
+# variable 'var'. For example, '${1 2 3:L:@word@<${word}>@}' encloses each
+# word in angle quotes, resulting in '<1> <2> <3>'.
+#
+# The variable name can be chosen freely, except that it must not contain a
+# '$'. For simplicity and readability, variable names should only use the
+# characters 'A-Za-z0-9'.
+#
+# The body may contain subexpressions in the form '${...}' or '$(...)'. These
+# subexpressions differ from everywhere else in makefiles in that the parser
+# only scans '${...}' for balanced '{' and '}', likewise for '$(...)'. Any
+# other '$' is left as-is during parsing. Later, when the body is expanded
+# for each word, each '$$' is interpreted as a single '$', and the remaining
+# '$' are interpreted as expressions, like when evaluating a regular variable.
# Force the test results to be independent of the default value of this
# setting, which is 'yes' for NetBSD's usr.bin/make but 'no' for the bmake
@@ -19,7 +33,6 @@ varname-overwriting-target:
@echo :$@: :${:U1 2 3:@\@@x${@}y@}: :$@:
-
# Demonstrate that it is possible to generate dollar signs using the
# :@ modifier.
#
@@ -192,15 +205,14 @@ CMDLINE= global # needed for deleting the environment
# except for '$i', which is replaced with the then-current value '1' of the
# iteration variable.
#
-# XXX: was broken in var.c 1.1028 from 2022-08-08, reverted in var.c 1.1029
-# from 2022-08-23; see parse-var.mk, keyword 'BRACE_GROUP'.
+# See parse-var.mk, keyword 'BRACE_GROUP'.
all: varmod-loop-literal-dollar
varmod-loop-literal-dollar: .PHONY
: ${:U1:@i@ t=$$(( $${t:-0} + $i ))@}
# When parsing the loop body, each '\$', '\@' and '\\' is unescaped to '$',
-# '@' and '\'; all other backslashes are retained.
+# '@' and '\', respectively; all other backslashes are retained.
#
# In practice, the '$' is not escaped as '\$', as there is a second round of
# unescaping '$$' to '$' later when the loop body is expanded after setting the
diff --git a/contrib/bmake/unit-tests/varmod-no-match.mk b/contrib/bmake/unit-tests/varmod-no-match.mk
index 2acb27e2e727..c03b4bf94e70 100644
--- a/contrib/bmake/unit-tests/varmod-no-match.mk
+++ b/contrib/bmake/unit-tests/varmod-no-match.mk
@@ -1,9 +1,97 @@
-# $NetBSD: varmod-no-match.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+# $NetBSD: varmod-no-match.mk,v 1.3 2023/02/26 06:08:06 rillig Exp $
#
-# Tests for the :N variable modifier, which filters words that do not match
-# the given pattern.
+# Tests for the expression modifier ':N', which filters words that do not
+# match the given pattern.
+
+
+# Keep all words except for 'two'.
+.if ${:U one two three :Ntwo} != "one three"
+. error
+.endif
+
+# Keep all words except those starting with 't'.
+# See varmod-match.mk for the details of pattern matching.
+.if ${:U one two three four six :Nt*} != "one four six"
+. error
+.endif
+
+
+# Idiom: normalize whitespace
+#
+# The modifier ':N' can be used with an empty pattern. As that pattern never
+# matches a word, the only effect is that the string is split into words and
+# then joined again, thereby normalizing whitespace around and between the
+# words. And even though the 'N' in ':N' might serve as a mnemonic for
+# "normalize whitespace", this idiom is not used in practice, resorting to the
+# much more common ':M*' to "select all words" instead.
+.if ${:U :N} != ""
+. error
+.endif
+.if ${:U one two three :N} != "one two three"
+. error
+.endif
+.if ${:U one two three :M*} != "one two three"
+. error
+.endif
+
+
+# Idiom: single-word expression equals any of several words or patterns
+#
+# If an expression is guaranteed to consist of a single word, the modifier
+# ':N' can be chained to compare the expression to several words or even
+# patterns in a sequence. If one of the patterns matches, the final
+# expression will be the empty string.
+#
+.if ${:U word :None:Ntwo:Nthree} != ""
+# good
+.else
+. error
+.endif
+.if ${:U two :None:Ntwo:Nthree} != ""
+. error
+.else
+# good
+.endif
+#
+# The modifier ':N' is seldom used in general since positive matches with ':M'
+# are easier to grasp. Chaining the ':N' modifier is even more difficult to
+# grasp due to the many negations involved.
+#
+# The final '!= ""' adds to the confusion because at first glance, the
+# condition may look like '${VAR} != ""', which for a single-word variable is
+# always true.
+#
+# The '!= ""' can be omitted if the expression cannot have the numeric value
+# 0, which is common in practice. In that form, each ':N' can be pronounced
+# as 'neither' or 'nor', which makes the expression sound more natural.
+#
+.if ${:U word :None:Ntwo:Nthree}
+# good
+.else
+. error
+.endif
+.if ${:U two :None:Ntwo:Nthree}
+. error
+.else
+# good
+.endif
+#
+# Replacing the '${...} != ""' with '!empty(...)' doesn't improve the
+# situation as the '!' adds another level of negations, and the word 'empty'
+# is a negation on its own, thereby creating a triple negation. Furthermore,
+# due to the '!empty', the expression to be evaluated no longer starts with
+# '$' and is thus more difficult to spot quickly.
+#
+.if !empty(:U word :None:Ntwo:Nthree)
+# good
+.else
+. error
+.endif
+.if !empty(:U two :None:Ntwo:Nthree)
+. error
+.else
+# good
+.endif
-# TODO: Implementation
all:
- @:;
diff --git a/contrib/bmake/unit-tests/varmod-order-shuffle.mk b/contrib/bmake/unit-tests/varmod-order-shuffle.mk
index 16121d7e498f..e9898600355a 100644
--- a/contrib/bmake/unit-tests/varmod-order-shuffle.mk
+++ b/contrib/bmake/unit-tests/varmod-order-shuffle.mk
@@ -1,4 +1,4 @@
-# $NetBSD: varmod-order-shuffle.mk,v 1.7 2021/08/03 04:46:49 rillig Exp $
+# $NetBSD: varmod-order-shuffle.mk,v 1.8 2023/02/26 06:08:06 rillig Exp $
#
# Tests for the :Ox variable modifier, which returns the words of the
# variable, shuffled.
@@ -6,8 +6,9 @@
# The variable modifier :Ox is available since 2005-06-01.
#
# As of 2020-08-16, make uses random(3) seeded by the current time in seconds.
-# This makes the random numbers completely predictable since there is no other
-# part of make that uses random numbers.
+# This makes the random numbers completely predictable since the only other
+# part of make that uses random numbers is the 'randomize-targets' mode, which
+# is off by default.
#
# Tags: probabilistic
diff --git a/contrib/bmake/unit-tests/varmod-order.exp b/contrib/bmake/unit-tests/varmod-order.exp
index 46dc45e9f6d6..e5d03f887fc7 100644
--- a/contrib/bmake/unit-tests/varmod-order.exp
+++ b/contrib/bmake/unit-tests/varmod-order.exp
@@ -19,6 +19,8 @@ make: Bad modifier ":Onrr" for variable "NUMBERS"
make: "varmod-order.mk" line 77: Malformed conditional (${NUMBERS:Onrr})
make: Bad modifier ":Orrn" for variable "NUMBERS"
make: "varmod-order.mk" line 86: Malformed conditional (${NUMBERS:Orrn})
+make: Bad modifier ":On=Off" for variable "SWITCH"
+make: "varmod-order.mk" line 100: Malformed conditional (${SWITCH:On=Off} != "Off")
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/contrib/bmake/unit-tests/varmod-order.mk b/contrib/bmake/unit-tests/varmod-order.mk
index c6028fc10abd..b4d5452263c7 100644
--- a/contrib/bmake/unit-tests/varmod-order.mk
+++ b/contrib/bmake/unit-tests/varmod-order.mk
@@ -1,4 +1,4 @@
-# $NetBSD: varmod-order.mk,v 1.8 2022/01/15 12:35:18 rillig Exp $
+# $NetBSD: varmod-order.mk,v 1.10 2023/02/27 08:29:36 rillig Exp $
#
# Tests for the :O variable modifier and its variants, which either sort the
# words of the value or shuffle them.
@@ -89,4 +89,18 @@ _:= ${NUMBERS:Onr
. error
.endif
+
+# If a modifier that starts with ':O' is not one of the known sort or shuffle
+# forms, it is a parse error. Several other modifiers such as ':H' or ':u'
+# fall back to the SysV modifier, for example, ':H=new' is not the standard
+# ':H' modifier but instead replaces a trailing 'H' with 'new' in each word.
+# There is no such fallback for the ':O' modifiers.
+SWITCH= On
+# expect: make: Bad modifier ":On=Off" for variable "SWITCH"
+.if ${SWITCH:On=Off} != "Off"
+. error
+.else
+. error
+.endif
+
all:
diff --git a/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_filter.exp b/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_filter.exp
index 39a9383953dd..045d26dc080e 100644
--- a/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_filter.exp
+++ b/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_filter.exp
@@ -1 +1,11 @@
+Initialize check-ignore.meta
+Building <tmpdir>/obj/check-ignore
+Skipping meta for .END: .SPECIAL
+Use .MAKE.META.IGNORE_FILTER - check-ignore is up to date
+`check-ignore' is up to date.
+Skipping meta for .END: .SPECIAL
+Skip .MAKE.META.IGNORE_FILTER - check-ignore is out of date
+<tmpdir>/obj/check-ignore.meta: <line>: file '<tmpdir>/ignore/check' is newer than the target...
+Building <tmpdir>/obj/check-ignore
+Skipping meta for .END: .SPECIAL
exit status 0
diff --git a/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_filter.mk b/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_filter.mk
index c41aec4acdf8..0adf6a202857 100644
--- a/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_filter.mk
+++ b/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_filter.mk
@@ -1,8 +1,5 @@
-# $NetBSD: varname-dot-make-meta-ignore_filter.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+# $NetBSD: varname-dot-make-meta-ignore_filter.mk,v 1.3 2023/02/23 05:20:45 sjg Exp $
#
# Tests for the special .MAKE.META.IGNORE_FILTER variable.
-# TODO: Implementation
-
-all:
- @:;
+.include "meta-ignore.inc"
diff --git a/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_paths.exp b/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_paths.exp
index 39a9383953dd..161d09c2eff4 100644
--- a/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_paths.exp
+++ b/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_paths.exp
@@ -1 +1,11 @@
+Initialize check-ignore.meta
+Building <tmpdir>/obj/check-ignore
+Skipping meta for .END: .SPECIAL
+Use .MAKE.META.IGNORE_PATHS - check-ignore is up to date
+`check-ignore' is up to date.
+Skipping meta for .END: .SPECIAL
+Skip .MAKE.META.IGNORE_PATHS - check-ignore is out of date
+<tmpdir>/obj/check-ignore.meta: <line>: file '<tmpdir>/ignore/check' is newer than the target...
+Building <tmpdir>/obj/check-ignore
+Skipping meta for .END: .SPECIAL
exit status 0
diff --git a/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_paths.mk b/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_paths.mk
index 4ae34f51608b..2c58849af0be 100644
--- a/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_paths.mk
+++ b/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_paths.mk
@@ -1,8 +1,5 @@
-# $NetBSD: varname-dot-make-meta-ignore_paths.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+# $NetBSD: varname-dot-make-meta-ignore_paths.mk,v 1.3 2023/02/23 05:20:45 sjg Exp $
#
# Tests for the special .MAKE.META.IGNORE_PATHS variable.
-# TODO: Implementation
-
-all:
- @:;
+.include "meta-ignore.inc"
diff --git a/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_patterns.exp b/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_patterns.exp
index 39a9383953dd..ebc1c0c6261c 100644
--- a/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_patterns.exp
+++ b/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_patterns.exp
@@ -1 +1,11 @@
+Initialize check-ignore.meta
+Building <tmpdir>/obj/check-ignore
+Skipping meta for .END: .SPECIAL
+Use .MAKE.META.IGNORE_PATTERNS - check-ignore is up to date
+`check-ignore' is up to date.
+Skipping meta for .END: .SPECIAL
+Skip .MAKE.META.IGNORE_PATTERNS - check-ignore is out of date
+<tmpdir>/obj/check-ignore.meta: <line>: file '<tmpdir>/ignore/check' is newer than the target...
+Building <tmpdir>/obj/check-ignore
+Skipping meta for .END: .SPECIAL
exit status 0
diff --git a/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_patterns.mk b/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_patterns.mk
index ea9fc49f1718..d3d6e065857d 100644
--- a/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_patterns.mk
+++ b/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_patterns.mk
@@ -1,8 +1,5 @@
-# $NetBSD: varname-dot-make-meta-ignore_patterns.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+# $NetBSD: varname-dot-make-meta-ignore_patterns.mk,v 1.3 2023/02/23 05:20:45 sjg Exp $
#
# Tests for the special .MAKE.META.IGNORE_PATTERNS variable.
-# TODO: Implementation
-
-all:
- @:;
+.include "meta-ignore.inc"
diff --git a/contrib/bmake/unit-tests/varname-dot-makeflags.exp b/contrib/bmake/unit-tests/varname-dot-makeflags.exp
index dbf96469f86b..28cc64fd66c8 100644
--- a/contrib/bmake/unit-tests/varname-dot-makeflags.exp
+++ b/contrib/bmake/unit-tests/varname-dot-makeflags.exp
@@ -1,3 +1,10 @@
-echo "$MAKEFLAGS"
- -r -k -d 00000 -D VARNAME WITH SPACES
+make: "varname-dot-makeflags.mk" line 10: MAKEFLAGS=<undefined>
+make: "varname-dot-makeflags.mk" line 11: .MAKEFLAGS=< -r -k>
+make: "varname-dot-makeflags.mk" line 12: .MAKEOVERRIDES=<>
+make: "varname-dot-makeflags.mk" line 18: MAKEFLAGS=<undefined>
+make: "varname-dot-makeflags.mk" line 20: .MAKEFLAGS=< -r -k -D VARNAME -r>
+make: "varname-dot-makeflags.mk" line 22: .MAKEOVERRIDES=< VAR>
+runtime: MAKEFLAGS=< -r -k -D VARNAME -r VAR=value>
+runtime: .MAKEFLAGS=< -r -k -D VARNAME -r>
+runtime: .MAKEOVERRIDES=< VAR>
exit status 0
diff --git a/contrib/bmake/unit-tests/varname-dot-makeflags.mk b/contrib/bmake/unit-tests/varname-dot-makeflags.mk
index 10d1903022cb..cca285f93013 100644
--- a/contrib/bmake/unit-tests/varname-dot-makeflags.mk
+++ b/contrib/bmake/unit-tests/varname-dot-makeflags.mk
@@ -1,15 +1,36 @@
-# $NetBSD: varname-dot-makeflags.mk,v 1.1 2020/12/01 20:37:30 rillig Exp $
+# $NetBSD: varname-dot-makeflags.mk,v 1.7 2023/02/25 19:24:07 rillig Exp $
#
# Tests for the special .MAKEFLAGS variable, which collects almost all
# command line arguments and passes them on to any child processes via
# the environment variable MAKEFLAGS (without leading '.').
+#
+# See also:
+# varname-dot-makeoverrides.mk
+
+.info MAKEFLAGS=<${MAKEFLAGS:Uundefined}>
+.info .MAKEFLAGS=<${.MAKEFLAGS}>
+.info .MAKEOVERRIDES=<${.MAKEOVERRIDES:Uundefined}>
+
+# Append an option with argument, a plain option and a variable assignment.
+.MAKEFLAGS: -DVARNAME -r VAR=value
+
+# expect+1: MAKEFLAGS=<undefined>
+.info MAKEFLAGS=<${MAKEFLAGS:Uundefined}>
+# expect+1: .MAKEFLAGS=< -r -k -D VARNAME -r>
+.info .MAKEFLAGS=<${.MAKEFLAGS}>
+# expect+1: .MAKEOVERRIDES=< VAR>
+.info .MAKEOVERRIDES=<${.MAKEOVERRIDES}>
-# When options are parsed, the option and its argument are appended as
-# separate words to .MAKEFLAGS. Special characters in the option argument
-# are not quoted though. It seems to have not been necessary at least from
-# 1993 until 2020.
-.MAKEFLAGS: -d00000 -D"VARNAME WITH SPACES"
+# The environment variable 'MAKEFLAGS' is not available to child processes
+# when parsing the makefiles. This is different from exported variables,
+# which are already available during parse time.
+.if ${:!echo "\${MAKEFLAGS-undef}"!} != "undef"
+. error
+.endif
-all:
- echo "$$MAKEFLAGS"
- @:;
+# After parsing, the environment variable 'MAKEFLAGS' is set based on the
+# special variables '.MAKEFLAGS' and '.MAKEOVERRIDES'.
+runtime:
+ @echo '$@: MAKEFLAGS=<'${MAKEFLAGS:Q}'>'
+ @echo '$@: .MAKEFLAGS=<'${.MAKEFLAGS:Q}'>'
+ @echo '$@: .MAKEOVERRIDES=<'${.MAKEOVERRIDES:Q}'>'
diff --git a/contrib/bmake/unit-tests/varname-dot-makeoverrides.exp b/contrib/bmake/unit-tests/varname-dot-makeoverrides.exp
index 39a9383953dd..78c0296f8d76 100644
--- a/contrib/bmake/unit-tests/varname-dot-makeoverrides.exp
+++ b/contrib/bmake/unit-tests/varname-dot-makeoverrides.exp
@@ -1 +1,8 @@
+all: overrides=<>
+make -f varname-dot-makeoverrides.mk stage_1 VAR=value
+stage_1: overrides=< VAR>
+make -f varname-dot-makeoverrides.mk stage_2
+stage_2: overrides=< VAR>
+make -f varname-dot-makeoverrides.mk stage_3
+stage_3: overrides=< VAR>
exit status 0
diff --git a/contrib/bmake/unit-tests/varname-dot-makeoverrides.mk b/contrib/bmake/unit-tests/varname-dot-makeoverrides.mk
index a897f4667175..f3f3897f8aa4 100644
--- a/contrib/bmake/unit-tests/varname-dot-makeoverrides.mk
+++ b/contrib/bmake/unit-tests/varname-dot-makeoverrides.mk
@@ -1,8 +1,23 @@
-# $NetBSD: varname-dot-makeoverrides.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+# $NetBSD: varname-dot-makeoverrides.mk,v 1.5 2023/02/25 06:54:08 rillig Exp $
#
-# Tests for the special .MAKE.MAKEOVERRIDES variable.
-
-# TODO: Implementation
+# Tests for the special .MAKEOVERRIDES variable, which lists the names of the
+# variables that are passed on to child processes via the MAKEFLAGS
+# environment variable.
+#
+# See also:
+# varname-dot-makeflags.mk
all:
- @:;
+ @echo '$@: overrides=<'${.MAKEOVERRIDES:Uundefined:Q}'>'
+ ${MAKE} -f ${MAKEFILE} stage_1 VAR=value
+
+stage_1:
+ @echo '$@: overrides=<'${.MAKEOVERRIDES:Q}'>'
+ ${MAKE} -f ${MAKEFILE} stage_2
+
+stage_2:
+ @echo '$@: overrides=<'${.MAKEOVERRIDES:Q}'>'
+ ${MAKE} -f ${MAKEFILE} stage_3
+
+stage_3:
+ @echo '$@: overrides=<'${.MAKEOVERRIDES:Q}'>'
diff --git a/contrib/bmake/unit-tests/varname-makeflags.exp b/contrib/bmake/unit-tests/varname-makeflags.exp
index 39a9383953dd..c1354177ca47 100644
--- a/contrib/bmake/unit-tests/varname-makeflags.exp
+++ b/contrib/bmake/unit-tests/varname-makeflags.exp
@@ -1 +1,21 @@
+spaces_stage_0: MAKEFLAGS=< -r -k >
+spaces_stage_0: env MAKEFLAGS=< -r -k >
+spaces_stage_1: MAKEFLAGS=< -r -k -d 00000 -D VARNAME WITH SPACES >
+spaces_stage_1: env MAKEFLAGS=< -r -k -d 00000 -D VARNAME WITH SPACES >
+dollars_stage_0: MAKEFLAGS=< -r -k >
+dollars_stage_1: env MAKEFLAGS=< -r -k DOLLARS=\$\{varname\}>
+dollars_stage_1: MAKEFLAGS=< -r -k DOLLARS=\{varname\}>
+dollars_stage_1: MAKEFLAGS:q=< -r -k DOLLARS=\{varname\}>
+dollars_stage_2: env MAKEFLAGS=< -r -k DOLLARS=>
+dollars_stage_2: dollars=<>
+dollars_stage_2: MAKEFLAGS=< -r -k DOLLARS=>
+dollars_stage_3: env MAKEFLAGS=< -r -k DOLLARS=>
+dollars_stage_3: dollars=<>
+dollars_stage_3: MAKEFLAGS=< -r -k DOLLARS=>
+append_stage_0: MAKEFLAGS=< -r -k >
+append_stage_1: MAKEFLAGS=< -r -k -D before-0 -D after-0 VAR0=value>
+append_stage_2: MAKEFLAGS=< -r -k -D before-0 -D after-0 -D before-1 -D after-1 VAR0=value VAR1=value>
+append_stage_3: MAKEFLAGS=< -r -k -D before-0 -D after-0 -D before-1 -D after-1 -D before-2 -D after-2 VAR0=value VAR1=value VAR2=value>
+override_stage_1: run MAKEFLAGS=< -r -k STAGE=1 VAR=value>
+override_stage_2: STAGE=<2> VAR=<value>
exit status 0
diff --git a/contrib/bmake/unit-tests/varname-makeflags.mk b/contrib/bmake/unit-tests/varname-makeflags.mk
index f7840c2eb7a5..da339ba9c5e2 100644
--- a/contrib/bmake/unit-tests/varname-makeflags.mk
+++ b/contrib/bmake/unit-tests/varname-makeflags.mk
@@ -1,44 +1,182 @@
-# $NetBSD: varname-makeflags.mk,v 1.5 2022/01/16 18:16:06 sjg Exp $
+# $NetBSD: varname-makeflags.mk,v 1.7 2023/02/25 19:24:07 rillig Exp $
#
-# Tests for the special MAKEFLAGS variable, which is basically just a normal
-# environment variable. It is closely related to .MAKEFLAGS but captures the
-# state of .MAKEFLAGS at the very beginning of make, before any makefiles are
-# read.
+# Tests for the environment variable 'MAKEFLAGS', from which additional
+# command line arguments are read before the actual command line arguments.
+#
+# After reading the makefiles and before making the targets, the arguments
+# that were collected in '.MAKEFLAGS' and '.MAKEOVERRIDES' are written back to
+# the environment variable 'MAKEFLAGS'.
+
+all: spaces_stage_0 dollars_stage_0 append_stage_0 override_stage_0
-# TODO: Implementation
-.MAKEFLAGS: -d0
+.if !make(*stage*)
# The unit tests are run with an almost empty environment. In particular,
-# the variable MAKEFLAGS is not set. The '.MAKEFLAGS:' above also doesn't
-# influence the environment variable MAKEFLAGS, therefore it is still
-# undefined at this point.
-.if ${MAKEFLAGS:Uundefined} != "undefined"
-. error
-.endif
+# the variable MAKEFLAGS is not set.
+. if ${MAKEFLAGS:Uundefined} != "undefined"
+. error
+. endif
# The special variable .MAKEFLAGS is influenced though.
# See varname-dot-makeflags.mk for more details.
-.if ${.MAKEFLAGS} != " -r -k -d 0"
-. error
-.endif
+. if ${.MAKEFLAGS} != " -r -k"
+. error
+. endif
# In POSIX mode, the environment variable MAKEFLAGS can contain letters only,
# for compatibility. These letters are exploded to form regular options.
OUTPUT!= env MAKEFLAGS=ikrs ${MAKE} -f /dev/null -v .MAKEFLAGS
-.if ${OUTPUT} != " -i -k -r -s -V .MAKEFLAGS"
-. error
-.endif
+. if ${OUTPUT} != " -i -k -r -s -V .MAKEFLAGS"
+. error
+. endif
# As soon as there is a single non-alphabetic character in the environment
# variable MAKEFLAGS, it is no longer split. In this example, the word
# "d0ikrs" is treated as a target, but the option '-v' prevents any targets
# from being built.
OUTPUT!= env MAKEFLAGS=d0ikrs ${MAKE} -r -f /dev/null -v .MAKEFLAGS
-.if ${OUTPUT} != " -r -V .MAKEFLAGS"
-. error ${OUTPUT}
+. if ${OUTPUT} != " -r -V .MAKEFLAGS"
+. error ${OUTPUT}
+. endif
+
+.endif
+
+
+# When options are parsed, the option and its argument are appended as
+# separate words to the MAKEFLAGS for the child processes. Special characters
+# in the option arguments are not quoted though.
+spaces_stage_0:
+ @echo '$@: MAKEFLAGS=<'${MAKEFLAGS:Q}'>'
+ @echo "$@: env MAKEFLAGS=<$$MAKEFLAGS>"
+ @${MAKE} -f ${MAKEFILE} spaces_stage_1 -d00000 -D"VARNAME WITH SPACES"
+
+# At this point, the 'VARNAME WITH SPACES' is no longer recognizable as a
+# single command line argument. In practice, variable names don't contain
+# spaces.
+spaces_stage_1:
+ @echo '$@: MAKEFLAGS=<'${MAKEFLAGS:Q}'>'
+ @echo "$@: env MAKEFLAGS=<$$MAKEFLAGS>"
+
+
+# Demonstrate that '$' characters are altered when they are passed on to child
+# make processes via MAKEFLAGS.
+dollars_stage_0:
+ @echo '$@: MAKEFLAGS=<'${MAKEFLAGS:Q}'>'
+
+ # The '$$$$' becomes a literal '$$' when building the '${MAKE}'
+ # command line, making the actual argument 'DOLLARS=$${varname}'.
+ # At this stage, MAKEFLAGS is not yet involved.
+ @${MAKE} -f ${MAKEFILE} dollars_stage_1 DOLLARS='$$$${varname}'
+
+.if make(dollars_stage_1)
+# At this point, the variable 'DOLLARS' contains '$${varname}', which
+# evaluates to a literal '$' followed by '{varname}'.
+. if ${DOLLARS} != "\${varname}"
+. error
+. endif
.endif
+dollars_stage_1:
+ # At this point, the stage 1 make provides the environment variable
+ # 'MAKEFLAGS' to its child processes, even if the child process is not
+ # another make.
+ #
+ # expect: dollars_stage_1: env MAKEFLAGS=< -r -k DOLLARS=\$\{varname\}>
+ #
+ # The 'DOLLARS=\$\{varname\}' assignment is escaped so that the stage
+ # 2 make will see it as a single word.
+ @echo "$@: env MAKEFLAGS=<$$MAKEFLAGS>"
+ # At this point, evaluating the environment variable 'MAKEFLAGS' leads
+ # to strange side effects as the string '\$\{varname\}' is interpreted
+ # as:
+ #
+ # \ a literal string of a single backslash
+ # $\ the value of the variable named '\'
+ # {varname\} a literal string
+ #
+ # Since the variable name '\' is not defined, the resulting value is
+ # '\{varname\}'. Make doesn't handle isolated '$' characters in
+ # strings well, instead each '$' has to be part of a '$$' or be part
+ # of a subexpression like '${VAR}'.
+ @echo '$@: MAKEFLAGS=<'${MAKEFLAGS:Q}'>'
+
+ # The modifier ':q' preserves a '$$' in an expression value instead of
+ # expanding it to a single '$', but it's already too late, as that
+ # modifier applies after the expression has been evaluated. Except
+ # for debug logging, there is no way to process strings that contain
+ # isolated '$'.
+ @echo '$@: MAKEFLAGS:q=<'${MAKEFLAGS:q}'>'
+
+ @${MAKE} -f ${MAKEFILE} dollars_stage_2
+
+.if make(dollars_stage_2)
+# At this point, the variable 'DOLLARS' contains '${varname}', and since
+# 'varname' is undefined, that expression evaluates to an empty string.
+. if ${DOLLARS} != ""
+. error
+. endif
+varname= varvalue
+. if ${DOLLARS} != "varvalue"
+. error
+. endif
+. undef varname
+.endif
+dollars_stage_2:
+ @echo "$@: env MAKEFLAGS=<$$MAKEFLAGS>"
+ @echo '$@: dollars=<'${DOLLARS:Q}'>'
+ @echo '$@: MAKEFLAGS=<'${MAKEFLAGS:Q}'>'
+ @${MAKE} -f ${MAKEFILE} dollars_stage_3
+
+dollars_stage_3:
+ @echo "$@: env MAKEFLAGS=<$$MAKEFLAGS>"
+ @echo '$@: dollars=<'${DOLLARS:Uundefined:Q}'>'
+ @echo '$@: MAKEFLAGS=<'${MAKEFLAGS:Q}'>'
+
+
+# Demonstrates in which exact order the MAKEFLAGS are built together from the
+# parent MAKEFLAGS and the flags from the command line, in particular that
+# variable assignments are passed at the end, after the options.
+append_stage_0:
+ @echo '$@: MAKEFLAGS=<'${MAKEFLAGS:Q}'>'
+ @${MAKE} -Dbefore-0 -f ${MAKEFILE} append_stage_1 VAR0=value -Dafter-0
+
+append_stage_1:
+ @echo '$@: MAKEFLAGS=<'${MAKEFLAGS:Q}'>'
+ @${MAKE} -Dbefore-1 -f ${MAKEFILE} append_stage_2 VAR1=value -Dafter-1
+
+append_stage_2:
+ @echo '$@: MAKEFLAGS=<'${MAKEFLAGS:Q}'>'
+ @${MAKE} -Dbefore-2 -f ${MAKEFILE} append_stage_3 VAR2=value -Dafter-2
+
+append_stage_3:
+ @echo '$@: MAKEFLAGS=<'${MAKEFLAGS:Q}'>'
+
+
+# Demonstrates the implementation details of 'MAKEFLAGS', in particular that
+# it is an environment variable rather than a global variable.
+override_stage_0:
+ @${MAKE} -f ${MAKEFILE} STAGE=1 VAR=value override_stage_1
+
+.if make(override_stage_1)
+# While parsing the makefiles, 'MAKEFLAGS' is the value of the environment
+# variable, in this case provided by stage 0.
+. if ${MAKEFLAGS:M*} != "-r -k"
+. error
+. endif
+MAKEFLAGS= overridden # temporarily override it
+. if ${MAKEFLAGS} != "overridden"
+. error
+. endif
+.undef MAKEFLAGS # make the environment variable visible again
+. if ${MAKEFLAGS:M*} != "-r -k"
+. error
+. endif
+.endif
+override_stage_1:
+ @echo '$@: run MAKEFLAGS=<'${MAKEFLAGS:Q}'>'
+ @${MAKE} -f ${MAKEFILE} STAGE=2 override_stage_2
-all:
+override_stage_2:
+ @echo '$@: STAGE=<${STAGE}> VAR=<${VAR}>'
diff --git a/contrib/bmake/unit-tests/varparse-errors.exp b/contrib/bmake/unit-tests/varparse-errors.exp
index 2c3568e468ca..fc20e427296a 100644
--- a/contrib/bmake/unit-tests/varparse-errors.exp
+++ b/contrib/bmake/unit-tests/varparse-errors.exp
@@ -1,10 +1,10 @@
-make: "varparse-errors.mk" line 38: Unknown modifier "Z"
-make: "varparse-errors.mk" line 46: Unknown modifier "Z"
+make: "varparse-errors.mk" line 37: Unknown modifier "Z"
+make: "varparse-errors.mk" line 45: Unknown modifier "Z"
make: Bad modifier ":OX" for variable ""
-make: "varparse-errors.mk" line 68: Undefined variable "${:U:OX"
+make: "varparse-errors.mk" line 67: Undefined variable "${:U:OX"
make: Bad modifier ":OX" for variable ""
make: Bad modifier ":OX" for variable ""
-make: "varparse-errors.mk" line 68: Undefined variable "${:U:OX"
+make: "varparse-errors.mk" line 67: Undefined variable "${:U:OX"
make: Bad modifier ":OX" for variable ""
make: Unclosed variable expression, expecting '}' for modifier "Q" of variable "" with value ""
make: Unclosed variable expression, expecting '}' for modifier "sh" of variable "" with value ""
diff --git a/contrib/bmake/unit-tests/varparse-errors.mk b/contrib/bmake/unit-tests/varparse-errors.mk
index 9174d264db0f..9ce41cc3be9a 100644
--- a/contrib/bmake/unit-tests/varparse-errors.mk
+++ b/contrib/bmake/unit-tests/varparse-errors.mk
@@ -1,4 +1,4 @@
-# $NetBSD: varparse-errors.mk,v 1.7 2022/08/24 22:09:41 rillig Exp $
+# $NetBSD: varparse-errors.mk,v 1.8 2023/02/14 21:56:48 rillig Exp $
# Tests for parsing and evaluating all kinds of variable expressions.
#
@@ -6,7 +6,6 @@
# Var_Subst, collecting typical and not so typical use cases.
#
# See also:
-# VarParseResult
# Var_Parse
# Var_Subst