aboutsummaryrefslogtreecommitdiff
path: root/unit-tests
diff options
context:
space:
mode:
authorSimon J. Gerraty <sjg@FreeBSD.org>2021-06-25 18:16:24 +0000
committerSimon J. Gerraty <sjg@FreeBSD.org>2021-06-25 18:16:24 +0000
commitee914ef902ae018bd4f67192832120f9bf05651f (patch)
tree4974406fb050a22beaceba7bd0d2dcedd0b49421 /unit-tests
parent8b6f73e37baf5c37946844ec335a84856b1a9033 (diff)
downloadsrc-ee914ef902ae018bd4f67192832120f9bf05651f.tar.gz
src-ee914ef902ae018bd4f67192832120f9bf05651f.zip
Diffstat (limited to 'unit-tests')
-rw-r--r--unit-tests/Makefile28
-rw-r--r--unit-tests/archive.mk6
-rw-r--r--unit-tests/cmd-errors-jobs.exp2
-rw-r--r--unit-tests/cmd-errors-lint.exp2
-rw-r--r--unit-tests/cmd-errors.exp2
-rw-r--r--unit-tests/cond-func-empty.mk10
-rw-r--r--unit-tests/cond-func-make-main.mk6
-rw-r--r--unit-tests/cond-late.exp2
-rw-r--r--unit-tests/cond-short.mk14
-rw-r--r--unit-tests/cond-token-string.exp2
-rw-r--r--unit-tests/cond-token-var.mk23
-rw-r--r--unit-tests/cond1.exp2
-rwxr-xr-xunit-tests/counter-append.mk4
-rw-r--r--unit-tests/counter.mk4
-rwxr-xr-xunit-tests/dep-var.mk4
-rw-r--r--unit-tests/deptgt-makeflags.exp10
-rw-r--r--unit-tests/deptgt-order.exp3
-rw-r--r--unit-tests/deptgt-order.mk18
-rw-r--r--unit-tests/deptgt.exp8
-rw-r--r--unit-tests/deptgt.mk4
-rw-r--r--unit-tests/directive-export-impl.exp88
-rw-r--r--unit-tests/directive-export-impl.mk23
-rw-r--r--unit-tests/directive-export.mk15
-rw-r--r--unit-tests/directive-for-errors.exp2
-rw-r--r--unit-tests/directive-for-errors.mk6
-rw-r--r--unit-tests/directive-for-escape.exp32
-rw-r--r--unit-tests/directive-for-escape.mk21
-rwxr-xr-xunit-tests/directive-for.exp2
-rw-r--r--unit-tests/directive-undef.exp3
-rw-r--r--unit-tests/directive-undef.mk21
-rw-r--r--unit-tests/directive-unexport-env.exp20
-rw-r--r--unit-tests/directive.exp8
-rw-r--r--unit-tests/include-main.exp2
-rw-r--r--unit-tests/job-output-null.exp4
-rw-r--r--unit-tests/job-output-null.mk32
-rw-r--r--unit-tests/jobs-empty-commands-error.exp5
-rw-r--r--unit-tests/jobs-empty-commands-error.mk19
-rw-r--r--unit-tests/moderrs.exp97
-rw-r--r--unit-tests/moderrs.mk25
-rw-r--r--unit-tests/modts.exp4
-rw-r--r--unit-tests/modword.exp24
-rw-r--r--unit-tests/modword.mk3
-rw-r--r--unit-tests/opt-chdir.mk6
-rw-r--r--unit-tests/opt-debug-errors-jobs.exp48
-rw-r--r--unit-tests/opt-debug-errors-jobs.mk36
-rw-r--r--unit-tests/opt-debug-lint.exp2
-rw-r--r--unit-tests/opt-debug-lint.mk20
-rw-r--r--unit-tests/opt-debug.exp6
-rw-r--r--unit-tests/opt-file.mk6
-rw-r--r--unit-tests/opt-jobs-no-action.mk4
-rw-r--r--unit-tests/recursive.mk7
-rw-r--r--unit-tests/sh-jobs.mk8
-rw-r--r--unit-tests/shell-csh.mk4
-rw-r--r--unit-tests/suff-incomplete.exp10
-rw-r--r--unit-tests/suff-main-several.exp24
-rw-r--r--unit-tests/suff-rebuild.exp14
-rw-r--r--unit-tests/var-class-cmdline.exp3
-rw-r--r--unit-tests/var-class-cmdline.mk80
-rw-r--r--unit-tests/var-eval-short.exp29
-rw-r--r--unit-tests/var-eval-short.mk163
-rw-r--r--unit-tests/var-op-append.exp12
-rw-r--r--unit-tests/var-op-append.mk4
-rw-r--r--unit-tests/var-op-assign.mk4
-rw-r--r--unit-tests/var-op-sunsh.mk12
-rw-r--r--unit-tests/varcmd.mk14
-rw-r--r--unit-tests/vardebug.exp119
-rw-r--r--unit-tests/varmisc.exp2
-rw-r--r--unit-tests/varmod-assign.exp22
-rw-r--r--unit-tests/varmod-assign.mk39
-rw-r--r--unit-tests/varmod-defined.exp34
-rw-r--r--unit-tests/varmod-defined.mk6
-rw-r--r--unit-tests/varmod-edge.exp12
-rw-r--r--unit-tests/varmod-edge.mk26
-rw-r--r--unit-tests/varmod-hash.exp6
-rw-r--r--unit-tests/varmod-ifelse.exp22
-rw-r--r--unit-tests/varmod-ifelse.mk64
-rw-r--r--unit-tests/varmod-indirect.exp84
-rw-r--r--unit-tests/varmod-indirect.mk100
-rw-r--r--unit-tests/varmod-loop-varname.exp11
-rw-r--r--unit-tests/varmod-loop-varname.mk127
-rw-r--r--unit-tests/varmod-loop.exp19
-rw-r--r--unit-tests/varmod-loop.mk156
-rwxr-xr-xunit-tests/varmod-match-escape.exp74
-rwxr-xr-xunit-tests/varmod-match-escape.mk8
-rw-r--r--unit-tests/varmod-order.exp4
-rw-r--r--unit-tests/varmod-range.exp10
-rw-r--r--unit-tests/varmod-remember.exp2
-rw-r--r--unit-tests/varmod-remember.mk29
-rw-r--r--unit-tests/varmod-shell.mk9
-rw-r--r--unit-tests/varmod-subst-regex.exp25
-rw-r--r--unit-tests/varmod-subst-regex.mk54
-rw-r--r--unit-tests/varmod-subst.exp2
-rw-r--r--unit-tests/varmod-subst.mk10
-rw-r--r--unit-tests/varmod-sun-shell.exp2
-rw-r--r--unit-tests/varmod-sun-shell.mk21
-rw-r--r--unit-tests/varmod-sysv.exp147
-rw-r--r--unit-tests/varmod-sysv.mk57
-rw-r--r--unit-tests/varmod-to-separator.exp12
-rw-r--r--unit-tests/varmod-unique.mk12
-rwxr-xr-xunit-tests/varname-dot-shell.exp32
-rw-r--r--unit-tests/varname-empty.exp60
-rwxr-xr-xunit-tests/varname-empty.mk4
-rw-r--r--unit-tests/varname.exp33
-rw-r--r--unit-tests/varparse-dynamic.mk6
-rw-r--r--unit-tests/varparse-errors.exp4
-rw-r--r--unit-tests/varparse-errors.mk4
106 files changed, 1850 insertions, 746 deletions
diff --git a/unit-tests/Makefile b/unit-tests/Makefile
index d649c552a03a..784223a56652 100644
--- a/unit-tests/Makefile
+++ b/unit-tests/Makefile
@@ -1,6 +1,6 @@
-# $Id: Makefile,v 1.143 2021/02/06 18:31:30 sjg Exp $
+# $Id: Makefile,v 1.148 2021/06/16 19:18:56 sjg Exp $
#
-# $NetBSD: Makefile,v 1.269 2021/02/06 18:26:03 sjg Exp $
+# $NetBSD: Makefile,v 1.279 2021/06/16 09:39:48 rillig Exp $
#
# Unit tests for make(1)
#
@@ -203,7 +203,9 @@ TESTS+= impsrc
TESTS+= include-main
TESTS+= job-flags
#TESTS+= job-output-long-lines
+TESTS+= job-output-null
TESTS+= jobs-empty-commands
+TESTS+= jobs-empty-commands-error
TESTS+= jobs-error-indirect
TESTS+= jobs-error-nested
TESTS+= jobs-error-nested-make
@@ -228,6 +230,7 @@ TESTS+= opt-debug-curdir
TESTS+= opt-debug-cond
TESTS+= opt-debug-dir
TESTS+= opt-debug-errors
+TESTS+= opt-debug-errors-jobs
TESTS+= opt-debug-file
TESTS+= opt-debug-for
TESTS+= opt-debug-graph1
@@ -321,6 +324,7 @@ TESTS+= var-class-env
TESTS+= var-class-global
TESTS+= var-class-local
TESTS+= var-class-local-legacy
+TESTS+= var-eval-short
TESTS+= var-op
TESTS+= var-op-append
TESTS+= var-op-assign
@@ -347,6 +351,7 @@ TESTS+= varmod-indirect
TESTS+= varmod-l-name-to-value
TESTS+= varmod-localtime
TESTS+= varmod-loop
+TESTS+= varmod-loop-varname
TESTS+= varmod-match
TESTS+= varmod-match-escape
TESTS+= varmod-no-match
@@ -363,6 +368,7 @@ TESTS+= varmod-select-words
TESTS+= varmod-shell
TESTS+= varmod-subst
TESTS+= varmod-subst-regex
+TESTS+= varmod-sun-shell
TESTS+= varmod-sysv
TESTS+= varmod-tail
TESTS+= varmod-to-abs
@@ -484,6 +490,7 @@ SED_CMDS.job-output-long-lines= \
${:D marker should always be at the beginning of the line. } \
-e '/^aa*--- job-b ---$$/d' \
-e '/^bb*--- job-a ---$$/d'
+SED_CMDS.opt-chdir= -e 's,\(nonexistent\).[1-9][0-9]*,\1,'
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}
@@ -494,11 +501,12 @@ SED_CMDS.opt-debug-jobs+= -e 's,JobFinish: [0-9][0-9]*,JobFinish: <pid>,'
SED_CMDS.opt-debug-jobs+= -e 's,Command: ${.SHELL:T},Command: <shell>,'
# The "-q" may be there or not, see jobs.c, variable shells.
SED_CMDS.opt-debug-jobs+= -e 's,^\(.Command: <shell>\) -q,\1,'
+SED_CMDS.opt-debug-lint+= ${STD_SED_CMDS.regex}
SED_CMDS.opt-jobs-no-action= ${STD_SED_CMDS.hide-from-output}
SED_CMDS.opt-no-action-runflags= ${STD_SED_CMDS.hide-from-output}
-# For Compat_RunCommand, useShell == FALSE.
+# For Compat_RunCommand, useShell == false.
SED_CMDS.sh-dots= -e 's,^.*\.\.\.:.*,<not found: ...>,'
-# For Compat_RunCommand, useShell == TRUE.
+# For Compat_RunCommand, useShell == true.
SED_CMDS.sh-dots+= -e 's,^make: exec(\(.*\)) failed (.*)$$,<not found: \1>,'
SED_CMDS.sh-dots+= -e 's,^\(\*\*\* Error code \)[1-9][0-9]*,\1<nonzero>,'
SED_CMDS.sh-errctl= ${STD_SED_CMDS.dj}
@@ -509,8 +517,7 @@ SED_CMDS.suff-transform-debug+= ${STD_SED_CMDS.dg1}
SED_CMDS.var-op-shell+= ${STD_SED_CMDS.shell}
SED_CMDS.var-op-shell+= -e '/command/s,No such.*,not found,'
SED_CMDS.vardebug+= -e 's,${.SHELL},</path/to/shell>,'
-SED_CMDS.varmod-subst-regex+= \
- -e 's,\(Regex compilation error:\).*,\1 (details omitted),'
+SED_CMDS.varmod-subst-regex+= ${STD_SED_CMDS.regex}
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'
@@ -590,6 +597,11 @@ STD_SED_CMDS.shell+= -e 's,^${.SHELL:T}: line [0-9][0-9]*: ,,'
STD_SED_CMDS.shell+= -e 's,^${.SHELL:T}: [0-9][0-9]*: ,,'
STD_SED_CMDS.shell+= -e 's,^${.SHELL:T}: ,,'
+# The actual error messages for a failed regcomp or regexec differ between the
+# implementations.
+STD_SED_CMDS.regex= \
+ -e 's,\(Regex compilation error:\).*,\1 (details omitted),'
+
# End of the configuration helpers section.
.-include "Makefile.inc"
@@ -639,8 +651,10 @@ _MKMSG_TEST= :
.if ${.OBJDIR} != ${.CURDIR}
# easy
TMPDIR:= ${.OBJDIR}/tmp
+.elif defined(TMPDIR)
+TMPDIR:= ${TMPDIR}/uid${.MAKE.UID}
.else
-TMPDIR:= ${TMPDIR:U/tmp}/uid${.MAKE.UID}
+TMPDIR:= /tmp/uid${.MAKE.UID}
.endif
# make sure it exists
.if !exist(${TMPDIR})
diff --git a/unit-tests/archive.mk b/unit-tests/archive.mk
index f8815cf40a40..2cd43a99e9ad 100644
--- a/unit-tests/archive.mk
+++ b/unit-tests/archive.mk
@@ -1,4 +1,4 @@
-# $NetBSD: archive.mk,v 1.11 2020/11/15 14:07:53 rillig Exp $
+# $NetBSD: archive.mk,v 1.12 2021/04/09 14:42:00 christos Exp $
#
# Very basic demonstration of handling archives, based on the description
# in PSD.doc/tutorial.ms.
@@ -8,11 +8,11 @@
# several other tests.
ARCHIVE= libprog.a
-FILES= archive.mk modmisc.mk varmisc.mk
+FILES= archive.mk archive-suffix.mk modmisc.mk ternary.mk varmisc.mk
all:
.if ${.PARSEDIR:tA} != ${.CURDIR:tA}
- @cd ${MAKEFILE:H} && cp ${FILES} [at]*.mk ${.CURDIR}
+ @cd ${MAKEFILE:H} && cp ${FILES} ${.CURDIR}
.endif
# The following targets create and remove files. The filesystem cache in
# dir.c would probably not handle this correctly, therefore each of the
diff --git a/unit-tests/cmd-errors-jobs.exp b/unit-tests/cmd-errors-jobs.exp
index 6d9c6bb7f890..9ed0557975b3 100644
--- a/unit-tests/cmd-errors-jobs.exp
+++ b/unit-tests/cmd-errors-jobs.exp
@@ -3,7 +3,7 @@ make: Unclosed variable "UNCLOSED"
: unclosed-variable
make: Unclosed variable expression (expecting '}') for "UNCLOSED"
: unclosed-modifier
-make: Unknown modifier 'Z'
+make: Unknown modifier "Z"
: unknown-modifier eol
: end eol
exit status 0
diff --git a/unit-tests/cmd-errors-lint.exp b/unit-tests/cmd-errors-lint.exp
index 09924c538de0..90b63bbcb08e 100644
--- a/unit-tests/cmd-errors-lint.exp
+++ b/unit-tests/cmd-errors-lint.exp
@@ -3,7 +3,7 @@ make: Unclosed variable "UNCLOSED"
: unclosed-variable
make: Unclosed variable expression (expecting '}') for "UNCLOSED"
: unclosed-modifier
-make: Unknown modifier 'Z'
+make: Unknown modifier "Z"
: unknown-modifier
: end
exit status 2
diff --git a/unit-tests/cmd-errors.exp b/unit-tests/cmd-errors.exp
index 6d9c6bb7f890..9ed0557975b3 100644
--- a/unit-tests/cmd-errors.exp
+++ b/unit-tests/cmd-errors.exp
@@ -3,7 +3,7 @@ make: Unclosed variable "UNCLOSED"
: unclosed-variable
make: Unclosed variable expression (expecting '}') for "UNCLOSED"
: unclosed-modifier
-make: Unknown modifier 'Z'
+make: Unknown modifier "Z"
: unknown-modifier eol
: end eol
exit status 0
diff --git a/unit-tests/cond-func-empty.mk b/unit-tests/cond-func-empty.mk
index 5094924f1c8d..11a990cbbce1 100644
--- a/unit-tests/cond-func-empty.mk
+++ b/unit-tests/cond-func-empty.mk
@@ -1,4 +1,4 @@
-# $NetBSD: cond-func-empty.mk,v 1.11 2020/11/28 14:08:37 rillig Exp $
+# $NetBSD: cond-func-empty.mk,v 1.14 2021/04/11 13:35:56 rillig Exp $
#
# Tests for the empty() function in .if conditions, which tests a variable
# expression for emptiness.
@@ -42,7 +42,7 @@ WORD= word
.endif
# The :U modifier modifies expressions based on undefined variables
-# (VAR_JUNK) by adding the VAR_KEEP flag, which marks the expression
+# (DEF_UNDEF) by adding the DEF_DEFINED flag, which marks the expression
# as "being interesting enough to be further processed".
#
.if empty(UNDEF:S,^$,value,W:Ufallback)
@@ -93,8 +93,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 ParseEmptyArg calls Var_Parse
-# without VARE_UNDEFERR, the value of the undefined variable is returned as
-# an empty string.
+# without VARE_UNDEFERR, the value of the undefined variable is
+# returned as an empty string.
${:U }= space
.if empty( )
. error
@@ -168,7 +168,7 @@ ${:U WORD }= variable name with spaces
# parsing it, this unrealistic variable name should have done no harm.
#
# The variable expression was expanded though, and this was wrong. The
-# expansion was done without the VARE_WANTRES flag (called VARF_WANTRES back
+# 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
diff --git a/unit-tests/cond-func-make-main.mk b/unit-tests/cond-func-make-main.mk
index 31b370afabde..97b91f869991 100644
--- a/unit-tests/cond-func-make-main.mk
+++ b/unit-tests/cond-func-make-main.mk
@@ -1,4 +1,4 @@
-# $NetBSD: cond-func-make-main.mk,v 1.1 2020/11/22 19:37:27 rillig Exp $
+# $NetBSD: cond-func-make-main.mk,v 1.2 2021/04/04 10:13:09 rillig Exp $
#
# Test how accurately the make() function in .if conditions reflects
# what is actually made.
@@ -33,7 +33,7 @@ first-main-target:
# the line. This implies that several main targets can be set at the name
# time, but they have to be in the same dependency group.
#
-# See ParseDoDependencyTargetSpecial, branch SP_MAIN.
+# See ParseDependencyTargetSpecial, branch SP_MAIN.
.MAIN: dot-main-target-1a dot-main-target-1b
.if !make(dot-main-target-1a)
@@ -47,7 +47,7 @@ dot-main-target-{1,2}{a,b}:
: Making ${.TARGET}.
# At this point, the list of targets to be made (opts.create) is not empty
-# anymore. ParseDoDependencyTargetSpecial therefore treats the .MAIN as if
+# anymore. ParseDependencyTargetSpecial therefore treats the .MAIN as if
# it were an ordinary target. Since .MAIN is not listed as a dependency
# anywhere, it is not made.
.if target(.MAIN)
diff --git a/unit-tests/cond-late.exp b/unit-tests/cond-late.exp
index 46c4aa2f4230..e179e8c74cc4 100644
--- a/unit-tests/cond-late.exp
+++ b/unit-tests/cond-late.exp
@@ -1,4 +1,4 @@
-make: Bad conditional expression ` != "no"' in != "no"?:
+make: Bad conditional expression ' != "no"' in ' != "no"?:'
yes
no
exit status 0
diff --git a/unit-tests/cond-short.mk b/unit-tests/cond-short.mk
index 46c7ea26a97b..113c3fd08fed 100644
--- a/unit-tests/cond-short.mk
+++ b/unit-tests/cond-short.mk
@@ -1,4 +1,4 @@
-# $NetBSD: cond-short.mk,v 1.15 2020/12/01 19:37:23 rillig Exp $
+# $NetBSD: cond-short.mk,v 1.16 2021/03/14 11:49:37 rillig Exp $
#
# Demonstrates that in conditions, the right-hand side of an && or ||
# is only evaluated if it can actually influence the result.
@@ -13,8 +13,11 @@
# parse them. They were still evaluated though, the only difference to
# relevant variable expressions was that in the irrelevant variable
# expressions, undefined variables were allowed.
+#
+# See also:
+# var-eval-short.mk, for short-circuited variable modifiers
-# The && operator.
+# The && operator:
.if 0 && ${echo "unexpected and" 1>&2 :L:sh}
.endif
@@ -86,7 +89,7 @@ VAR= # empty again, for the following tests
. warning first=${FIRST} last=${LAST} appended=${APPENDED} ran=${RAN}
.endif
-# The || operator.
+# The || operator:
.if 1 || ${echo "unexpected or" 1>&2 :L:sh}
.endif
@@ -208,9 +211,4 @@ x!= echo '0 || $${iV2:U2} < $${V42}: $x' >&2; echo
. error
.endif
-# TODO: Test each modifier to make sure it is skipped when it is irrelevant
-# for the result. Since this test is already quite long, do that in another
-# test.
-
all:
- @:;:
diff --git a/unit-tests/cond-token-string.exp b/unit-tests/cond-token-string.exp
index 07b318caa81a..45f9993457d3 100644
--- a/unit-tests/cond-token-string.exp
+++ b/unit-tests/cond-token-string.exp
@@ -1,4 +1,4 @@
-make: "cond-token-string.mk" line 13: Unknown modifier 'Z'
+make: "cond-token-string.mk" line 13: Unknown modifier "Z"
make: "cond-token-string.mk" line 13: Malformed conditional ("" != "${:Uvalue:Z}")
make: "cond-token-string.mk" line 22: xvalue is not defined.
make: "cond-token-string.mk" line 28: Malformed conditional (x${:Uvalue} == "")
diff --git a/unit-tests/cond-token-var.mk b/unit-tests/cond-token-var.mk
index 30eba87ad4d2..168c63c46ac1 100644
--- a/unit-tests/cond-token-var.mk
+++ b/unit-tests/cond-token-var.mk
@@ -1,4 +1,4 @@
-# $NetBSD: cond-token-var.mk,v 1.5 2020/11/15 14:58:14 rillig Exp $
+# $NetBSD: cond-token-var.mk,v 1.6 2021/04/25 21:05:38 rillig Exp $
#
# Tests for variable expressions in .if conditions.
#
@@ -46,3 +46,24 @@ DEF= defined
# Since the expression is defined now, it doesn't generate any parse error.
.if ${UNDEF:U}
.endif
+
+# If the value of the variable expression is a number, it is compared against
+# zero.
+.if ${:U0}
+. error
+.endif
+.if !${:U1}
+. error
+.endif
+
+# If the value of the variable expression is not a number, any non-empty
+# value evaluates to true, even if there is only whitespace.
+.if ${:U}
+. error
+.endif
+.if !${:U }
+. error
+.endif
+.if !${:Uanything}
+. error
+.endif
diff --git a/unit-tests/cond1.exp b/unit-tests/cond1.exp
index 0acd935780a0..8b65d782524d 100644
--- a/unit-tests/cond1.exp
+++ b/unit-tests/cond1.exp
@@ -17,7 +17,7 @@ Passed:
5 is prime
make: String comparison operator must be either == or !=
-make: Bad conditional expression `"0" > 0' in "0" > 0?OK:No
+make: Bad conditional expression '"0" > 0' in '"0" > 0?OK:No'
OK
exit status 0
diff --git a/unit-tests/counter-append.mk b/unit-tests/counter-append.mk
index 1c4e00d6118c..d234835e5ec3 100755
--- a/unit-tests/counter-append.mk
+++ b/unit-tests/counter-append.mk
@@ -1,4 +1,4 @@
-# $NetBSD: counter-append.mk,v 1.4 2020/10/17 16:57:17 rillig Exp $
+# $NetBSD: counter-append.mk,v 1.5 2021/04/04 10:13:09 rillig Exp $
#
# Demonstrates how to let make count the number of times a variable
# is actually accessed, using the ::+= variable modifier.
@@ -15,7 +15,7 @@ COUNTER= # zero
NEXT= ${COUNTER::+=a}${COUNTER:[#]}
# This variable is first set to empty and then expanded.
-# See parse.c, function Parse_DoVar, keyword "!Var_Exists".
+# See parse.c, function Parse_Var, keyword "!Var_Exists".
A:= ${NEXT}
B:= ${NEXT}
C:= ${NEXT}
diff --git a/unit-tests/counter.mk b/unit-tests/counter.mk
index 3c75d7a5032a..7cf8fba72876 100644
--- a/unit-tests/counter.mk
+++ b/unit-tests/counter.mk
@@ -1,4 +1,4 @@
-# $NetBSD: counter.mk,v 1.5 2020/10/17 16:57:17 rillig Exp $
+# $NetBSD: counter.mk,v 1.6 2021/04/04 10:13:09 rillig Exp $
#
# Demonstrates how to let make count the number of times a variable
# is actually accessed, using the ::= variable modifier.
@@ -15,7 +15,7 @@ COUNTER= # zero
NEXT= ${COUNTER::=${COUNTER} a}${COUNTER:[#]}
# This variable is first set to empty and then expanded.
-# See parse.c, function Parse_DoVar, keyword "!Var_Exists".
+# See parse.c, function Parse_Var, keyword "!Var_Exists".
A:= ${NEXT}
B:= ${NEXT}
C:= ${NEXT}
diff --git a/unit-tests/dep-var.mk b/unit-tests/dep-var.mk
index 438a8a84a60d..4503424e31ab 100755
--- a/unit-tests/dep-var.mk
+++ b/unit-tests/dep-var.mk
@@ -1,4 +1,4 @@
-# $NetBSD: dep-var.mk,v 1.5 2020/09/13 20:04:26 rillig Exp $
+# $NetBSD: dep-var.mk,v 1.6 2021/04/04 10:13:09 rillig Exp $
#
# Tests for variable references in dependency declarations.
#
@@ -79,7 +79,7 @@ all: $$$$)
# undefined.
#
# Since 2020-09-13, this generates a parse error in lint mode (-dL), but not
-# in normal mode since ParseDoDependency does not handle any errors after
+# in normal mode since ParseDependency does not handle any errors after
# calling Var_Parse.
undef1 def2 a-def2-b 1-2-$$INDIRECT_2-2-1 ${:U\$)}:
@echo ${.TARGET:Q}
diff --git a/unit-tests/deptgt-makeflags.exp b/unit-tests/deptgt-makeflags.exp
index 7eb54eba7f30..11043bc5110c 100644
--- a/unit-tests/deptgt-makeflags.exp
+++ b/unit-tests/deptgt-makeflags.exp
@@ -1,10 +1,10 @@
Global:delete DOLLAR (not found)
-Command:DOLLAR = $$$$
-Global:.MAKEOVERRIDES = VAR DOLLAR
+Command: DOLLAR = $$$$
+Global: .MAKEOVERRIDES = VAR DOLLAR
CondParser_Eval: ${DOLLAR} != "\$\$"
-Var_Parse: ${DOLLAR} != "\$\$" with VARE_UNDEFERR|VARE_WANTRES
+Var_Parse: ${DOLLAR} != "\$\$" (eval-defined)
lhs = "$$", rhs = "$$", op = !=
-Global:.MAKEFLAGS = -r -k -D VAR -D VAR -d cv -d
-Global:.MAKEFLAGS = -r -k -D VAR -D VAR -d cv -d 0
+Global: .MAKEFLAGS = -r -k -D VAR -D VAR -d cv -d
+Global: .MAKEFLAGS = -r -k -D VAR -D VAR -d cv -d 0
make: Unterminated quoted string [make VAR=initial UNBALANCED=']
exit status 0
diff --git a/unit-tests/deptgt-order.exp b/unit-tests/deptgt-order.exp
index 39a9383953dd..5f7dde0ac69d 100644
--- a/unit-tests/deptgt-order.exp
+++ b/unit-tests/deptgt-order.exp
@@ -1 +1,4 @@
+: 'Making two out of one.'
+: 'Making three out of two.'
+: 'Making all out of three.'
exit status 0
diff --git a/unit-tests/deptgt-order.mk b/unit-tests/deptgt-order.mk
index 003552f57a49..f241331ae1e1 100644
--- a/unit-tests/deptgt-order.mk
+++ b/unit-tests/deptgt-order.mk
@@ -1,8 +1,18 @@
-# $NetBSD: deptgt-order.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+# $NetBSD: deptgt-order.mk,v 1.3 2021/06/17 15:25:33 rillig Exp $
#
# Tests for the special target .ORDER in dependency declarations.
-# TODO: Implementation
+all one two three: .PHONY
-all:
- @:;
+two: one
+ : 'Making $@ out of $>.'
+three: two
+ : 'Making $@ out of $>.'
+
+# This .ORDER creates a circular dependency since 'three' depends on 'one'
+# but 'one' is supposed to be built after 'three'.
+.ORDER: three one
+
+# XXX: The circular dependency should be detected here.
+all: three
+ : 'Making $@ out of $>.'
diff --git a/unit-tests/deptgt.exp b/unit-tests/deptgt.exp
index b2aeaa5a2850..bdac2aee3e6c 100644
--- a/unit-tests/deptgt.exp
+++ b/unit-tests/deptgt.exp
@@ -1,14 +1,14 @@
make: "deptgt.mk" line 10: warning: Extra target ignored
make: "deptgt.mk" line 28: Unassociated shell command ": command3 # parse error, since targets == NULL"
ParseReadLine (34): '${:U}: empty-source'
-ParseDoDependency(: empty-source)
+ParseDependency(: empty-source)
ParseReadLine (35): ' : command for empty targets list'
ParseReadLine (36): ': empty-source'
-ParseDoDependency(: empty-source)
+ParseDependency(: empty-source)
ParseReadLine (37): ' : command for empty targets list'
ParseReadLine (38): '.MAKEFLAGS: -d0'
-ParseDoDependency(.MAKEFLAGS: -d0)
-make: "deptgt.mk" line 46: Unknown modifier 'Z'
+ParseDependency(.MAKEFLAGS: -d0)
+make: "deptgt.mk" line 46: Unknown modifier "Z"
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/unit-tests/deptgt.mk b/unit-tests/deptgt.mk
index 09f381715e6d..15d7e59aeced 100644
--- a/unit-tests/deptgt.mk
+++ b/unit-tests/deptgt.mk
@@ -1,4 +1,4 @@
-# $NetBSD: deptgt.mk,v 1.10 2020/12/27 18:20:26 rillig Exp $
+# $NetBSD: deptgt.mk,v 1.11 2021/04/04 10:13:09 rillig Exp $
#
# Tests for special targets like .BEGIN or .SUFFIXES in dependency
# declarations.
@@ -12,7 +12,7 @@
# The following lines demonstrate how 'targets' is set and reset during
# parsing of dependencies. To see it in action, set breakpoints in:
#
-# ParseDoDependency at the beginning
+# ParseDependency at the beginning
# FinishDependencyGroup at "targets = NULL"
# Parse_File at "Lst_Free(targets)"
# Parse_File at "targets = Lst_New()"
diff --git a/unit-tests/directive-export-impl.exp b/unit-tests/directive-export-impl.exp
index 1a5cf34dbfb8..740daa605129 100644
--- a/unit-tests/directive-export-impl.exp
+++ b/unit-tests/directive-export-impl.exp
@@ -1,56 +1,56 @@
ParseReadLine (21): 'UT_VAR= <${REF}>'
-Global:UT_VAR = <${REF}>
+Global: UT_VAR = <${REF}>
ParseReadLine (28): '.export UT_VAR'
-Global:.MAKE.EXPORTED = UT_VAR
+Global: .MAKE.EXPORTED = UT_VAR
ParseReadLine (32): ': ${UT_VAR:N*}'
-Var_Parse: ${UT_VAR:N*} with VARE_UNDEFERR|VARE_WANTRES
-Var_Parse: ${REF}> with VARE_UNDEFERR|VARE_WANTRES
-Applying ${UT_VAR:N...} to "<>" (VARE_UNDEFERR|VARE_WANTRES, VAR_EXPORTED|VAR_REEXPORT, none)
-Pattern[UT_VAR] for [<>] is [*]
+Var_Parse: ${UT_VAR:N*} (eval-defined)
+Var_Parse: ${REF}> (eval-defined)
+Evaluating modifier ${UT_VAR:N...} on value "<>"
+Pattern for ':N' is "*"
ModifyWords: split "<>" into 1 words
-Result of ${UT_VAR:N*} is "" (VARE_UNDEFERR|VARE_WANTRES, VAR_EXPORTED|VAR_REEXPORT, none)
-ParseDoDependency(: )
+Result of ${UT_VAR:N*} is ""
+ParseDependency(: )
CondParser_Eval: ${:!echo "\$UT_VAR"!} != "<>"
-Var_Parse: ${:!echo "\$UT_VAR"!} != "<>" with VARE_UNDEFERR|VARE_WANTRES
-Applying ${:!...} to "" (VARE_UNDEFERR|VARE_WANTRES, none, VES_UNDEF)
+Var_Parse: ${:!echo "\$UT_VAR"!} != "<>" (eval-defined)
+Evaluating modifier ${:!...} on value "" (eval-defined, undefined)
Modifier part: "echo "$UT_VAR""
-Var_Parse: ${.MAKE.EXPORTED:O:u} with VARE_WANTRES
-Applying ${.MAKE.EXPORTED:O} to "UT_VAR" (VARE_WANTRES, none, none)
-Result of ${.MAKE.EXPORTED:O} is "UT_VAR" (VARE_WANTRES, none, none)
-Applying ${.MAKE.EXPORTED:u} to "UT_VAR" (VARE_WANTRES, none, none)
-Result of ${.MAKE.EXPORTED:u} is "UT_VAR" (VARE_WANTRES, none, none)
-Var_Parse: ${UT_VAR} with VARE_WANTRES
-Var_Parse: ${REF}> with VARE_WANTRES
-Result of ${:!echo "\$UT_VAR"!} is "<>" (VARE_UNDEFERR|VARE_WANTRES, none, VES_DEF)
+Var_Parse: ${.MAKE.EXPORTED:O:u} (eval)
+Evaluating modifier ${.MAKE.EXPORTED:O} on value "UT_VAR"
+Result of ${.MAKE.EXPORTED:O} is "UT_VAR"
+Evaluating modifier ${.MAKE.EXPORTED:u} on value "UT_VAR"
+Result of ${.MAKE.EXPORTED:u} is "UT_VAR"
+Var_Parse: ${UT_VAR} (eval)
+Var_Parse: ${REF}> (eval)
+Result of ${:!echo "\$UT_VAR"!} is "<>" (eval-defined, defined)
lhs = "<>", rhs = "<>", op = !=
-ParseReadLine (49): ': ${UT_VAR:N*}'
-Var_Parse: ${UT_VAR:N*} with VARE_UNDEFERR|VARE_WANTRES
-Var_Parse: ${REF}> with VARE_UNDEFERR|VARE_WANTRES
-Applying ${UT_VAR:N...} to "<>" (VARE_UNDEFERR|VARE_WANTRES, VAR_EXPORTED|VAR_REEXPORT, none)
-Pattern[UT_VAR] for [<>] is [*]
+ParseReadLine (50): ': ${UT_VAR:N*}'
+Var_Parse: ${UT_VAR:N*} (eval-defined)
+Var_Parse: ${REF}> (eval-defined)
+Evaluating modifier ${UT_VAR:N...} on value "<>"
+Pattern for ':N' is "*"
ModifyWords: split "<>" into 1 words
-Result of ${UT_VAR:N*} is "" (VARE_UNDEFERR|VARE_WANTRES, VAR_EXPORTED|VAR_REEXPORT, none)
-ParseDoDependency(: )
-ParseReadLine (53): 'REF= defined'
-Global:REF = defined
+Result of ${UT_VAR:N*} is ""
+ParseDependency(: )
+ParseReadLine (54): 'REF= defined'
+Global: REF = defined
CondParser_Eval: ${:!echo "\$UT_VAR"!} != "<defined>"
-Var_Parse: ${:!echo "\$UT_VAR"!} != "<defined>" with VARE_UNDEFERR|VARE_WANTRES
-Applying ${:!...} to "" (VARE_UNDEFERR|VARE_WANTRES, none, VES_UNDEF)
+Var_Parse: ${:!echo "\$UT_VAR"!} != "<defined>" (eval-defined)
+Evaluating modifier ${:!...} on value "" (eval-defined, undefined)
Modifier part: "echo "$UT_VAR""
-Var_Parse: ${.MAKE.EXPORTED:O:u} with VARE_WANTRES
-Applying ${.MAKE.EXPORTED:O} to "UT_VAR" (VARE_WANTRES, none, none)
-Result of ${.MAKE.EXPORTED:O} is "UT_VAR" (VARE_WANTRES, none, none)
-Applying ${.MAKE.EXPORTED:u} to "UT_VAR" (VARE_WANTRES, none, none)
-Result of ${.MAKE.EXPORTED:u} is "UT_VAR" (VARE_WANTRES, none, none)
-Var_Parse: ${UT_VAR} with VARE_WANTRES
-Var_Parse: ${REF}> with VARE_WANTRES
-Result of ${:!echo "\$UT_VAR"!} is "<defined>" (VARE_UNDEFERR|VARE_WANTRES, none, VES_DEF)
+Var_Parse: ${.MAKE.EXPORTED:O:u} (eval)
+Evaluating modifier ${.MAKE.EXPORTED:O} on value "UT_VAR"
+Result of ${.MAKE.EXPORTED:O} is "UT_VAR"
+Evaluating modifier ${.MAKE.EXPORTED:u} on value "UT_VAR"
+Result of ${.MAKE.EXPORTED:u} is "UT_VAR"
+Var_Parse: ${UT_VAR} (eval)
+Var_Parse: ${REF}> (eval)
+Result of ${:!echo "\$UT_VAR"!} is "<defined>" (eval-defined, defined)
lhs = "<defined>", rhs = "<defined>", op = !=
-ParseReadLine (61): 'all:'
-ParseDoDependency(all:)
-Global:.ALLTARGETS = all
-ParseReadLine (62): '.MAKEFLAGS: -d0'
-ParseDoDependency(.MAKEFLAGS: -d0)
-Global:.MAKEFLAGS = -r -k -d cpv -d
-Global:.MAKEFLAGS = -r -k -d cpv -d 0
+ParseReadLine (62): 'all:'
+ParseDependency(all:)
+Global: .ALLTARGETS = all
+ParseReadLine (63): '.MAKEFLAGS: -d0'
+ParseDependency(.MAKEFLAGS: -d0)
+Global: .MAKEFLAGS = -r -k -d cpv -d
+Global: .MAKEFLAGS = -r -k -d cpv -d 0
exit status 0
diff --git a/unit-tests/directive-export-impl.mk b/unit-tests/directive-export-impl.mk
index 556e5352d1c3..0ad290f653d4 100644
--- a/unit-tests/directive-export-impl.mk
+++ b/unit-tests/directive-export-impl.mk
@@ -1,4 +1,4 @@
-# $NetBSD: directive-export-impl.mk,v 1.1 2020/12/29 01:45:06 rillig Exp $
+# $NetBSD: directive-export-impl.mk,v 1.3 2021/04/03 23:08:30 rillig Exp $
#
# Test for the implementation of exporting variables to child processes.
# This involves marking variables for export, actually exporting them,
@@ -8,8 +8,8 @@
# Var_Export
# ExportVar
# VarExportedMode (global)
-# VAR_EXPORTED (per variable)
-# VAR_REEXPORT (per variable)
+# VarFlags.exported (per variable)
+# VarFlags.reexport (per variable)
# VarExportMode (per call of Var_Export and ExportVar)
: ${:U:sh} # side effect: initialize .SHELL
@@ -22,13 +22,13 @@ UT_VAR= <${REF}>
# At this point, ExportVar("UT_VAR", VEM_PLAIN) is called. Since the
# variable value refers to another variable, ExportVar does not actually
-# export the variable but only marks it as VAR_EXPORTED and VAR_REEXPORT.
-# After that, ExportVars registers the variable name in .MAKE.EXPORTED.
-# That's all for now.
+# export the variable but only marks it as VarFlags.exported and
+# VarFlags.reexport. After that, ExportVars registers the variable name in
+# .MAKE.EXPORTED. That's all for now.
.export UT_VAR
-# Evaluating this expression shows the variable flags in the debug log,
-# which are VAR_EXPORTED|VAR_REEXPORT.
+# The following expression has both flags 'exported' and 'reexport' set.
+# These flags do not show up anywhere, not even in the debug log.
: ${UT_VAR:N*}
# At the last moment before actually forking off the child process for the
@@ -43,9 +43,10 @@ UT_VAR= <${REF}>
. error
.endif
-# Evaluating this expression shows the variable flags in the debug log,
-# which are still VAR_EXPORTED|VAR_REEXPORT, which means that the variable
-# is still marked as being re-exported for each child process.
+# The following expression still has 'exported' and 'reexport' set.
+# These flags do not show up anywhere though, not even in the debug log.
+# These flags means that the variable is still marked as being re-exported
+# for each child process.
: ${UT_VAR:N*}
# Now the referenced variable gets defined. This does not influence anything
diff --git a/unit-tests/directive-export.mk b/unit-tests/directive-export.mk
index 40fda0968cb0..942d4b371bbd 100644
--- a/unit-tests/directive-export.mk
+++ b/unit-tests/directive-export.mk
@@ -1,4 +1,4 @@
-# $NetBSD: directive-export.mk,v 1.6 2020/12/13 01:07:54 rillig Exp $
+# $NetBSD: directive-export.mk,v 1.8 2021/02/16 19:01:18 rillig Exp $
#
# Tests for the .export directive.
#
@@ -28,8 +28,17 @@ VAR= value $$ ${INDIRECT}
. error
.endif
-# No argument means to export all variables.
+# No syntactical argument means to export all variables.
.export
+# An empty argument means no additional variables to export.
+.export ${:U}
+
+
+# Trigger the "This isn't going to end well" in ExportVarEnv.
+EMPTY_SHELL= ${:sh}
+.export EMPTY_SHELL # only marked for export at this point
+_!= :;: # Force the variable to be actually exported.
+
+
all:
- @:;
diff --git a/unit-tests/directive-for-errors.exp b/unit-tests/directive-for-errors.exp
index 6088a93c9a4a..da5eee473ec2 100644
--- a/unit-tests/directive-for-errors.exp
+++ b/unit-tests/directive-for-errors.exp
@@ -13,7 +13,7 @@ make: "directive-for-errors.mk" line 53: Wrong number of words (5) in .for subst
make: "directive-for-errors.mk" line 64: missing `in' in for
make: "directive-for-errors.mk" line 66: warning: Should not be reached.
make: "directive-for-errors.mk" line 67: for-less endfor
-make: "directive-for-errors.mk" line 73: Unknown modifier 'Z'
+make: "directive-for-errors.mk" line 73: Unknown modifier "Z"
make: "directive-for-errors.mk" line 74: warning: Should not be reached.
make: "directive-for-errors.mk" line 74: warning: Should not be reached.
make: "directive-for-errors.mk" line 74: warning: Should not be reached.
diff --git a/unit-tests/directive-for-errors.mk b/unit-tests/directive-for-errors.mk
index 7890e2375af4..602ecbf32e4e 100644
--- a/unit-tests/directive-for-errors.mk
+++ b/unit-tests/directive-for-errors.mk
@@ -1,4 +1,4 @@
-# $NetBSD: directive-for-errors.mk,v 1.1 2020/12/31 03:05:12 rillig Exp $
+# $NetBSD: directive-for-errors.mk,v 1.3 2021/04/04 10:13:09 rillig Exp $
#
# Tests for error handling in .for loops.
@@ -13,8 +13,8 @@
# XXX: The error message is misleading though. As of 2020-12-31, it says
# "Unknown directive "for"", but that directive is actually known. This is
# because ForEval does not detect the .for loop as such, so parsing
-# continues in ParseLine > ParseDependency > ParseDoDependency >
-# ParseDoDependencyTargets > ParseErrorNoDependency, and there the directive
+# continues in ParseLine > ParseDependencyLine > ParseDependency >
+# ParseDependencyTargets > ParseErrorNoDependency, and there the directive
# name is parsed a bit differently.
.for/i in 1 2 3
. warning ${i}
diff --git a/unit-tests/directive-for-escape.exp b/unit-tests/directive-for-escape.exp
index 89a8cbc2e229..59d4c2324f15 100644
--- a/unit-tests/directive-for-escape.exp
+++ b/unit-tests/directive-for-escape.exp
@@ -1,12 +1,12 @@
For: end for 1
For: loop body:
. info ${:U!"#$%&'()*+,-./0-9\:;<=>?@A-Z[\\]_^a-z{|\}~}
-make: Unclosed variable specification (expecting '}') for "" (value "!"") modifier U
+make: Unclosed variable expression, expecting '}' for modifier "U!"" of variable "" with value "!""
make: "directive-for-escape.mk" line 19: !"
For: end for 1
For: loop body:
. info ${:U!"\\\\#$%&'()*+,-./0-9\:;<=>?@A-Z[\\]_^a-z{|\}~}
-make: Unclosed variable specification (expecting '}') for "" (value "!"\\") modifier U
+make: Unclosed variable expression, expecting '}' for modifier "U!"\\\\" of variable "" with value "!"\\"
make: "directive-for-escape.mk" line 29: !"\\
For: end for 1
For: loop body:
@@ -37,19 +37,19 @@ make: "directive-for-escape.mk" line 55: end}
For: end for 1
For: loop body:
. info ${:Ubegin<${UNDEF:Ufallback:N{{{}}}}>end}
-make: "directive-for-escape.mk" line 66: begin<fallback>end
+make: "directive-for-escape.mk" line 67: begin<fallback>end
For: end for 1
For: loop body:
. info ${:U\$}
-make: "directive-for-escape.mk" line 74: $
+make: "directive-for-escape.mk" line 75: $
For: end for 1
For: loop body:
. info ${NUMBERS} ${:Ureplaced}
-make: "directive-for-escape.mk" line 82: one two three replaced
+make: "directive-for-escape.mk" line 83: one two three replaced
For: end for 1
For: loop body:
. info ${:Ureplaced}
-make: "directive-for-escape.mk" line 92: replaced
+make: "directive-for-escape.mk" line 93: replaced
For: end for 1
For: loop body:
. info . $$i: ${:Uinner}
@@ -62,14 +62,14 @@ For: loop body:
. info . $${i2}: ${i2}
. info . $${i,}: ${i,}
. info . adjacent: ${:Uinner}${:Uinner}${:Uinner:M*}${:Uinner}
-make: "directive-for-escape.mk" line 100: . $i: inner
-make: "directive-for-escape.mk" line 101: . ${i}: inner
-make: "directive-for-escape.mk" line 102: . ${i:M*}: inner
-make: "directive-for-escape.mk" line 103: . $(i): inner
-make: "directive-for-escape.mk" line 104: . $(i:M*): inner
-make: "directive-for-escape.mk" line 105: . ${i${:U}}: outer
-make: "directive-for-escape.mk" line 106: . ${i\}}: inner}
-make: "directive-for-escape.mk" line 107: . ${i2}: two
-make: "directive-for-escape.mk" line 108: . ${i,}: comma
-make: "directive-for-escape.mk" line 109: . adjacent: innerinnerinnerinner
+make: "directive-for-escape.mk" line 101: . $i: inner
+make: "directive-for-escape.mk" line 102: . ${i}: inner
+make: "directive-for-escape.mk" line 103: . ${i:M*}: inner
+make: "directive-for-escape.mk" line 104: . $(i): inner
+make: "directive-for-escape.mk" line 105: . $(i:M*): inner
+make: "directive-for-escape.mk" line 106: . ${i${:U}}: outer
+make: "directive-for-escape.mk" line 107: . ${i\}}: inner}
+make: "directive-for-escape.mk" line 108: . ${i2}: two
+make: "directive-for-escape.mk" line 109: . ${i,}: comma
+make: "directive-for-escape.mk" line 110: . adjacent: innerinnerinnerinner
exit status 0
diff --git a/unit-tests/directive-for-escape.mk b/unit-tests/directive-for-escape.mk
index d61f05cc53cc..babc4b8c6e88 100644
--- a/unit-tests/directive-for-escape.mk
+++ b/unit-tests/directive-for-escape.mk
@@ -1,4 +1,4 @@
-# $NetBSD: directive-for-escape.mk,v 1.6 2021/01/25 19:05:39 rillig Exp $
+# $NetBSD: directive-for-escape.mk,v 1.7 2021/02/15 07:58:19 rillig Exp $
#
# Test escaping of special characters in the iteration values of a .for loop.
# These values get expanded later using the :U variable modifier, and this
@@ -7,8 +7,8 @@
.MAKEFLAGS: -df
-# Even though the .for loops takes quotes into account when splitting the
-# string into words, the quotes don't need to be balances, as of 2020-12-31.
+# Even though the .for loops take quotes into account when splitting the
+# string into words, the quotes don't need to be balanced, as of 2020-12-31.
# This could be considered a bug.
ASCII= !"\#$$%&'()*+,-./0-9:;<=>?@A-Z[\]_^a-z{|}~
@@ -33,7 +33,7 @@ ASCII.2020-12-31= !"\\\#$$%&'()*+,-./0-9:;<=>?@A-Z[\]_^a-z{|}~
#
# XXX: It is unexpected that the variable V gets expanded in the loop body.
# The double '$$' should prevent exactly this. Probably nobody was
-# adventurous enough to use literal dollar signs in the values for a .for
+# adventurous enough to use literal dollar signs in the values of a .for
# loop.
V= value
VALUES= $$ $${V} $${V:=-with-modifier} $$(V) $$(V:=-with-modifier)
@@ -43,14 +43,14 @@ VALUES= $$ $${V} $${V:=-with-modifier} $$(V) $$(V:=-with-modifier)
# Try to cover the code for nested '{}' in for_var_len, without success.
#
-# The value of VALUES is not meant to be a variable expression. Instead, it
-# is meant to represent dollar, lbrace, "UNDEF:U", backslash, dollar,
-# backslash, dollar, space, nested braces, space, "end}".
+# The value of the variable VALUES is not meant to be a variable expression.
+# Instead, it is meant to represent literal text, the only escaping mechanism
+# being that each '$' is written as '$$'.
#
# The .for loop splits ${VALUES} into 3 words, at the space characters, since
# these are not escaped.
VALUES= $${UNDEF:U\$$\$$ {{}} end}
-# XXX: Where does the '\$$\$$' get converted into a single '\$'?
+# XXX: Where in the code does the '\$\$' get converted into a single '\$'?
.for i in ${VALUES}
. info $i
.endfor
@@ -59,8 +59,9 @@ VALUES= $${UNDEF:U\$$\$$ {{}} end}
#
# XXX: It is wrong that for_var_len requires the braces to be balanced.
# Each variable modifier has its own inconsistent way of parsing nested
-# variable expressions, braces and parentheses. The only sensible thing
-# to do is therefore to let Var_Parse do all the parsing work.
+# variable expressions, braces and parentheses. (Compare ':M', ':S', and
+# ':D' for details.) The only sensible thing to do is therefore to let
+# Var_Parse do all the parsing work.
VALUES= begin<$${UNDEF:Ufallback:N{{{}}}}>end
.for i in ${VALUES}
. info $i
diff --git a/unit-tests/directive-for.exp b/unit-tests/directive-for.exp
index bdaf4492baf0..4e882aad7b68 100755
--- a/unit-tests/directive-for.exp
+++ b/unit-tests/directive-for.exp
@@ -16,7 +16,7 @@ make: "directive-for.mk" line 140: ][ ][ ][
make: "directive-for.mk" line 140: }{ }{ }{
make: "directive-for.mk" line 148: outer value value
make: "directive-for.mk" line 148: outer "quoted" \"quoted\"
-make: "directive-for.mk" line 154: Unknown modifier 'Z'
+make: "directive-for.mk" line 154: Unknown modifier "Z"
make: "directive-for.mk" line 155: XXX: Not reached word1
make: "directive-for.mk" line 155: XXX: Not reached word3
make: Fatal errors encountered -- cannot continue
diff --git a/unit-tests/directive-undef.exp b/unit-tests/directive-undef.exp
index d64cb8b5afe0..56c871429397 100644
--- a/unit-tests/directive-undef.exp
+++ b/unit-tests/directive-undef.exp
@@ -1,5 +1,6 @@
make: "directive-undef.mk" line 29: The .undef directive requires an argument
-make: "directive-undef.mk" line 86: Unknown modifier 'Z'
+make: "directive-undef.mk" line 86: Unknown modifier "Z"
+make: "directive-undef.mk" line 103: warning: UT_EXPORTED is still listed in .MAKE.EXPORTED even though spaceit is not exported anymore.
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/unit-tests/directive-undef.mk b/unit-tests/directive-undef.mk
index b9a69f733517..41ea6b5bf8fa 100644
--- a/unit-tests/directive-undef.mk
+++ b/unit-tests/directive-undef.mk
@@ -1,4 +1,4 @@
-# $NetBSD: directive-undef.mk,v 1.9 2020/12/22 20:10:21 rillig Exp $
+# $NetBSD: directive-undef.mk,v 1.10 2021/02/16 18:02:19 rillig Exp $
#
# Tests for the .undef directive.
#
@@ -86,5 +86,22 @@ ${DOLLAR}= dollar
.undef ${VARNAMES:L:Z}
+UT_EXPORTED= exported-value
+.export UT_EXPORTED
+.if ${:!echo "\${UT_EXPORTED:-not-exported}"!} != "exported-value"
+. error
+.endif
+.if !${.MAKE.EXPORTED:MUT_EXPORTED}
+. error
+.endif
+.undef UT_EXPORTED # XXX: does not update .MAKE.EXPORTED
+.if ${:!echo "\${UT_EXPORTED:-not-exported}"!} != "not-exported"
+. error
+.endif
+.if ${.MAKE.EXPORTED:MUT_EXPORTED}
+. warning UT_EXPORTED is still listed in .MAKE.EXPORTED even though $\
+ it is not exported anymore.
+.endif
+
+
all:
- @:;
diff --git a/unit-tests/directive-unexport-env.exp b/unit-tests/directive-unexport-env.exp
index 677596ea4aa8..6d653e65fd32 100644
--- a/unit-tests/directive-unexport-env.exp
+++ b/unit-tests/directive-unexport-env.exp
@@ -1,18 +1,18 @@
make: "directive-unexport-env.mk" line 13: Unknown directive "unexport-en"
make: "directive-unexport-env.mk" line 15: Unknown directive "unexport-environment"
-Global:UT_EXPORTED = value
-Global:UT_UNEXPORTED = value
-Global:.MAKE.EXPORTED = UT_EXPORTED
+Global: UT_EXPORTED = value
+Global: UT_UNEXPORTED = value
+Global: .MAKE.EXPORTED = UT_EXPORTED
make: "directive-unexport-env.mk" line 21: The directive .unexport-env does not take arguments
-Var_Parse: ${.MAKE.EXPORTED:O:u} with VARE_WANTRES
-Applying ${.MAKE.EXPORTED:O} to "UT_EXPORTED" (VARE_WANTRES, none, none)
-Result of ${.MAKE.EXPORTED:O} is "UT_EXPORTED" (VARE_WANTRES, none, none)
-Applying ${.MAKE.EXPORTED:u} to "UT_EXPORTED" (VARE_WANTRES, none, none)
-Result of ${.MAKE.EXPORTED:u} is "UT_EXPORTED" (VARE_WANTRES, none, none)
+Var_Parse: ${.MAKE.EXPORTED:O:u} (eval)
+Evaluating modifier ${.MAKE.EXPORTED:O} on value "UT_EXPORTED"
+Result of ${.MAKE.EXPORTED:O} is "UT_EXPORTED"
+Evaluating modifier ${.MAKE.EXPORTED:u} on value "UT_EXPORTED"
+Result of ${.MAKE.EXPORTED:u} is "UT_EXPORTED"
Unexporting "UT_EXPORTED"
Global:delete .MAKE.EXPORTED
-Global:.MAKEFLAGS = -r -k -d v -d
-Global:.MAKEFLAGS = -r -k -d v -d 0
+Global: .MAKEFLAGS = -r -k -d v -d
+Global: .MAKEFLAGS = -r -k -d v -d 0
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/unit-tests/directive.exp b/unit-tests/directive.exp
index b93d768169ab..ee866b7ee2b3 100644
--- a/unit-tests/directive.exp
+++ b/unit-tests/directive.exp
@@ -2,11 +2,11 @@ make: "directive.mk" line 9: Unknown directive "indented"
make: "directive.mk" line 10: Unknown directive "indented"
make: "directive.mk" line 11: Unknown directive "indented"
make: "directive.mk" line 15: Unknown directive "info"
-Global:.info =
-Global:.info = value
+Global: .info =
+Global: .info = value
make: "directive.mk" line 26: := value
-Global:.MAKEFLAGS = -r -k -d v -d
-Global:.MAKEFLAGS = -r -k -d v -d 0
+Global: .MAKEFLAGS = -r -k -d v -d
+Global: .MAKEFLAGS = -r -k -d v -d 0
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/unit-tests/include-main.exp b/unit-tests/include-main.exp
index 61e716ad8ad7..c8a670a1c14a 100644
--- a/unit-tests/include-main.exp
+++ b/unit-tests/include-main.exp
@@ -9,7 +9,7 @@ make: "include-subsub.mk" line 5: subsub-ok
in .for loop from include-sub.mk:29
in .include from include-main.mk:27
ParseReadLine (6): '.MAKEFLAGS: -d0'
-ParseDoDependency(.MAKEFLAGS: -d0)
+ParseDependency(.MAKEFLAGS: -d0)
make: "include-sub.mk" line 38: sub-after-ok
make: "include-sub.mk" line 45: sub-after-for-ok
make: "include-main.mk" line 30: main-after-ok
diff --git a/unit-tests/job-output-null.exp b/unit-tests/job-output-null.exp
new file mode 100644
index 000000000000..af9b4e64dba3
--- /dev/null
+++ b/unit-tests/job-output-null.exp
@@ -0,0 +1,4 @@
+hello
+hello
+hello world without newline, hello world without newline, hello world without newline.
+exit status 0
diff --git a/unit-tests/job-output-null.mk b/unit-tests/job-output-null.mk
new file mode 100644
index 000000000000..7620bdf6a7ba
--- /dev/null
+++ b/unit-tests/job-output-null.mk
@@ -0,0 +1,32 @@
+# $NetBSD: job-output-null.mk,v 1.1 2021/04/15 19:02:29 rillig Exp $
+#
+# Test how null bytes in the output of a command are handled. Make processes
+# them using null-terminated strings, which may cut off some of the output.
+#
+# As of 2021-04-15, make handles null bytes from the child process
+# inconsistently. It's an edge case though since typically the child
+# processes output text.
+
+.MAKEFLAGS: -j1 # force jobs mode
+
+all: .PHONY
+ # The null byte from the command output is kept as-is.
+ # See CollectOutput, which looks like it intended to replace these
+ # null bytes with simple spaces.
+ @printf 'hello\0world%s\n' ''
+
+ # Give the parent process a chance to see the above output, but not
+ # yet the output from the next printf command.
+ @sleep 1
+
+ # All null bytes from the command output are kept as-is.
+ @printf 'hello\0world%s\n' '' '' '' '' '' ''
+
+ @sleep 1
+
+ # The null bytes are replaced with spaces since they are not followed
+ # by a newline.
+ #
+ # The three null bytes in a row test whether this output is
+ # compressed to a single space like in DebugFailedTarget. It isn't.
+ @printf 'hello\0world\0without\0\0\0newline%s' ', ' ', ' '.'
diff --git a/unit-tests/jobs-empty-commands-error.exp b/unit-tests/jobs-empty-commands-error.exp
new file mode 100644
index 000000000000..1639425d9013
--- /dev/null
+++ b/unit-tests/jobs-empty-commands-error.exp
@@ -0,0 +1,5 @@
+: 'Making existing-target out of nothing.'
+make: don't know how to make nonexistent-target (continuing)
+
+make: stopped in unit-tests
+exit status 2
diff --git a/unit-tests/jobs-empty-commands-error.mk b/unit-tests/jobs-empty-commands-error.mk
new file mode 100644
index 000000000000..b9ba4403078e
--- /dev/null
+++ b/unit-tests/jobs-empty-commands-error.mk
@@ -0,0 +1,19 @@
+# $NetBSD: jobs-empty-commands-error.mk,v 1.1 2021/06/16 09:39:48 rillig Exp $
+#
+# In jobs mode, the shell commands for creating a target are written to a
+# temporary file first, which is then run by the shell. In chains of
+# dependencies, these files would end up empty. Since job.c 1.399 from
+# 2021-01-29, these empty files are no longer created.
+#
+# After 2021-01-29, before job.c 1.435 2021-06-16, targets that could not be
+# made led to longer error messages than necessary.
+
+.MAKEFLAGS: -j1
+
+all: existing-target
+
+existing-target:
+ : 'Making $@ out of nothing.'
+
+all: nonexistent-target
+ : 'Not reached'
diff --git a/unit-tests/moderrs.exp b/unit-tests/moderrs.exp
index 0ca1aa2aedd5..9d8bd308c36c 100644
--- a/unit-tests/moderrs.exp
+++ b/unit-tests/moderrs.exp
@@ -1,143 +1,136 @@
mod-unknown-direct:
want: Unknown modifier 'Z'
-make: Unknown modifier 'Z'
+make: Unknown modifier "Z"
VAR:Z=before--after
mod-unknown-indirect:
want: Unknown modifier 'Z'
-make: Unknown modifier 'Z'
+make: Unknown modifier "Z"
VAR:Z=before-inner}-after
unclosed-direct:
-want: Unclosed variable specification (expecting '}') for "VAR" (value "Thevariable") modifier S
-make: Unclosed variable specification (expecting '}') for "VAR" (value "Thevariable") modifier S
+want: Unclosed variable expression, expecting '}' for modifier "S,V,v," of variable "VAR" with value "Thevariable"
+make: Unclosed variable expression, expecting '}' for modifier "S,V,v," of variable "VAR" with value "Thevariable"
VAR:S,V,v,=Thevariable
unclosed-indirect:
-want: Unclosed variable specification after complex modifier (expecting '}') for VAR
-make: Unclosed variable specification after complex modifier (expecting '}') for VAR
+want: Unclosed variable expression after indirect modifier, expecting '}' for variable "VAR"
+make: Unclosed variable expression after indirect modifier, expecting '}' for variable "VAR"
VAR:S,V,v,=Thevariable
unfinished-indirect:
want: Unfinished modifier for VAR (',' missing)
-make: Unfinished modifier for VAR (',' missing)
+make: Unfinished modifier for "VAR" (',' missing)
VAR:S,V,v=
unfinished-loop:
want: Unfinished modifier for UNDEF ('@' missing)
-make: Unfinished modifier for UNDEF ('@' missing)
+make: Unfinished modifier for "UNDEF" ('@' missing)
want: Unfinished modifier for UNDEF ('@' missing)
-make: Unfinished modifier for UNDEF ('@' missing)
+make: Unfinished modifier for "UNDEF" ('@' missing)
1 2 3
loop-close:
-make: Unclosed variable specification (expecting '}') for "UNDEF" (value "1}... 2}... 3}...") modifier @
+make: Unclosed variable expression, expecting '}' for modifier "@var@${var}}...@" of variable "UNDEF" with value "1}... 2}... 3}..."
1}... 2}... 3}...
1}... 2}... 3}...
words:
want: Unfinished modifier for UNDEF (']' missing)
-make: Unfinished modifier for UNDEF (']' missing)
+make: Unfinished modifier for "UNDEF" (']' missing)
want: Unfinished modifier for UNDEF (']' missing)
-make: Unfinished modifier for UNDEF (']' missing)
+make: Unfinished modifier for "UNDEF" (']' missing)
13=
-make: Bad modifier `:[123451234512345123451234512345]' for UNDEF
+make: Bad modifier ":[123451234512345123451234512345]" for variable "UNDEF"
12345=S,^ok,:S,^3ok,}
exclam:
want: Unfinished modifier for VARNAME ('!' missing)
-make: Unfinished modifier for VARNAME ('!' missing)
+make: Unfinished modifier for "VARNAME" ('!' missing)
want: Unfinished modifier for ! ('!' missing)
-make: Unfinished modifier for ! ('!' missing)
+make: Unfinished modifier for "!" ('!' missing)
mod-subst-delimiter:
-make: Missing delimiter for :S modifier
+make: Missing delimiter for modifier ':S'
1:
-make: Unfinished modifier for VAR (',' missing)
+make: Unfinished modifier for "VAR" (',' missing)
2:
-make: Unfinished modifier for VAR (',' missing)
+make: Unfinished modifier for "VAR" (',' missing)
3:
-make: Unfinished modifier for VAR (',' missing)
+make: Unfinished modifier for "VAR" (',' missing)
4:
-make: Unfinished modifier for VAR (',' missing)
+make: Unfinished modifier for "VAR" (',' missing)
5:
-make: Unclosed variable specification (expecting '}') for "VAR" (value "TheVariable") modifier S
+make: Unclosed variable expression, expecting '}' for modifier "S,from,to," of variable "VAR" with value "TheVariable"
6: TheVariable
7: TheVariable
mod-regex-delimiter:
make: Missing delimiter for :C modifier
1:
-make: Unfinished modifier for VAR (',' missing)
+make: Unfinished modifier for "VAR" (',' missing)
2:
-make: Unfinished modifier for VAR (',' missing)
+make: Unfinished modifier for "VAR" (',' missing)
3:
-make: Unfinished modifier for VAR (',' missing)
+make: Unfinished modifier for "VAR" (',' missing)
4:
-make: Unfinished modifier for VAR (',' missing)
+make: Unfinished modifier for "VAR" (',' missing)
5:
-make: Unclosed variable specification (expecting '}') for "VAR" (value "TheVariable") modifier C
+make: Unclosed variable expression, expecting '}' for modifier "C,from,to," of variable "VAR" with value "TheVariable"
6: TheVariable
7: TheVariable
-mod-regex-undefined-subexpression:
-one one 2 3 5 8 one3 2one 34
-make: No match for subexpression \2
-make: No match for subexpression \2
-make: No match for subexpression \1
-make: No match for subexpression \2
-make: No match for subexpression \1
-()+() ()+() ()+() 3 5 8 (3)+() ()+(1) 34
-
mod-ts-parse:
112358132134
15152535558513521534
-make: Bad modifier `:ts\65oct' for FIB
+make: Bad modifier ":ts\65oct" for variable "FIB"
+65oct}
+make: Bad modifier ":ts\65oct" for variable ""
65oct}
-make: Bad modifier `:tsxy' for FIB
+make: Bad modifier ":tsxy" for variable "FIB"
xy}
mod-t-parse:
-make: Bad modifier `:t' for FIB
+make: Bad modifier ":t" for variable "FIB"
-make: Bad modifier `:txy' for FIB
+make: Bad modifier ":txy" for variable "FIB"
y}
-make: Bad modifier `:t' for FIB
+make: Bad modifier ":t" for variable "FIB"
-make: Bad modifier `:t' for FIB
+make: Bad modifier ":t" for variable "FIB"
M*}
mod-ifelse-parse:
-make: Unfinished modifier for FIB (':' missing)
+make: Unfinished modifier for "FIB" (':' missing)
-make: Unfinished modifier for FIB (':' missing)
+make: Unfinished modifier for "FIB" (':' missing)
-make: Unfinished modifier for FIB ('}' missing)
+make: Unfinished modifier for "FIB" ('}' missing)
-make: Unfinished modifier for FIB ('}' missing)
+make: Unfinished modifier for "FIB" ('}' missing)
then
mod-remember-parse:
1 1 2 3 5 8 13 21 34
-make: Unknown modifier '_'
+make: Unknown modifier "__"
mod-sysv-parse:
-make: Unknown modifier '3'
-make: Unclosed variable specification (expecting '}') for "FIB" (value "") modifier 3
+make: Unknown modifier "3"
+make: Unclosed variable expression, expecting '}' for modifier "3" of variable "FIB" with value ""
-make: Unknown modifier '3'
-make: Unclosed variable specification (expecting '}') for "FIB" (value "") modifier 3
+make: Unknown modifier "3="
+make: Unclosed variable expression, expecting '}' for modifier "3=" of variable "FIB" with value ""
-make: Unknown modifier '3'
-make: Unclosed variable specification (expecting '}') for "FIB" (value "") modifier 3
+make: Unknown modifier "3=x3"
+make: Unclosed variable expression, expecting '}' for modifier "3=x3" of variable "FIB" with value ""
1 1 2 x3 5 8 1x3 21 34
diff --git a/unit-tests/moderrs.mk b/unit-tests/moderrs.mk
index 8fdcb496ee29..ffd920314c5d 100644
--- a/unit-tests/moderrs.mk
+++ b/unit-tests/moderrs.mk
@@ -1,4 +1,4 @@
-# $NetBSD: moderrs.mk,v 1.25 2020/11/15 20:20:58 rillig Exp $
+# $NetBSD: moderrs.mk,v 1.30 2021/06/21 08:28:37 rillig Exp $
#
# various modifier error tests
@@ -19,7 +19,6 @@ all: words
all: exclam
all: mod-subst-delimiter
all: mod-regex-delimiter
-all: mod-regex-undefined-subexpression
all: mod-ts-parse
all: mod-t-parse
all: mod-ifelse-parse
@@ -35,11 +34,11 @@ mod-unknown-indirect: print-header print-footer
@echo 'VAR:${MOD_UNKN}=before-${VAR:${MOD_UNKN}:inner}-after'
unclosed-direct: print-header print-footer
- @echo 'want: Unclosed variable specification (expecting $'}$') for "VAR" (value "Thevariable") modifier S'
+ @echo 'want: Unclosed variable expression, expecting $'}$' for modifier "S,V,v," of variable "VAR" with value "Thevariable"'
@echo VAR:S,V,v,=${VAR:S,V,v,
unclosed-indirect: print-header print-footer
- @echo 'want: Unclosed variable specification after complex modifier (expecting $'}$') for VAR'
+ @echo 'want: Unclosed variable expression after indirect modifier, expecting $'}$' for variable "VAR"'
@echo VAR:${MOD_TERM},=${VAR:${MOD_S}
unfinished-indirect: print-header print-footer
@@ -119,27 +118,11 @@ mod-regex-delimiter: print-header print-footer
@echo 6: ${VAR:C,from,to,
@echo 7: ${VAR:C,from,to,}
-# In regular expressions with alternatives, not all capturing groups are
-# always set; some may be missing. Warn about these.
-#
-# Since there is no way to turn off this warning, the combination of
-# alternative matches and capturing groups is seldom used, if at all.
-#
-# A newly added modifier 'U' such as in :C,(a.)|(b.),\1\2,U might be added
-# for treating undefined capturing groups as empty, but that would create a
-# syntactical ambiguity since the :S and :C modifiers are open-ended (see
-# mod-subst-chain). Luckily the modifier :U does not make sense after :C,
-# therefore this case does not happen in practice.
-# The sub-modifier for the :S and :C modifiers would have to be chosen
-# wisely, to not create ambiguities while parsing.
-mod-regex-undefined-subexpression: print-header print-footer
- @echo ${FIB:C,1(.*),one\1,} # all ok
- @echo ${FIB:C,1(.*)|2(.*),(\1)+(\2),:Q} # no match for subexpression
-
mod-ts-parse: print-header print-footer
@echo ${FIB:ts}
@echo ${FIB:ts\65} # octal 065 == U+0035 == '5'
@echo ${FIB:ts\65oct} # bad modifier
+ @echo ${:U${FIB}:ts\65oct} # bad modifier, variable name is ""
@echo ${FIB:tsxy} # modifier too long
mod-t-parse: print-header print-footer
diff --git a/unit-tests/modts.exp b/unit-tests/modts.exp
index 5db79fc96586..18837016add4 100644
--- a/unit-tests/modts.exp
+++ b/unit-tests/modts.exp
@@ -1,6 +1,6 @@
-make: Bad modifier `:tx' for LIST
+make: Bad modifier ":tx" for variable "LIST"
LIST:tx="}"
-make: Bad modifier `:ts\X' for LIST
+make: Bad modifier ":ts\X" for variable "LIST"
LIST:ts/x:tu="\X:tu}"
FU_mod-ts="a/b/cool"
FU_mod-ts:ts:T="cool" == cool?
diff --git a/unit-tests/modword.exp b/unit-tests/modword.exp
index 9fd7f1b494fe..02e9974c02d6 100644
--- a/unit-tests/modword.exp
+++ b/unit-tests/modword.exp
@@ -1,4 +1,4 @@
-make: Bad modifier `:[]' for LIST
+make: Bad modifier ":[]" for variable "LIST"
LIST:[]="" is an error
LIST:[0]="one two three four five six"
LIST:[0x0]="one two three four five six"
@@ -37,17 +37,17 @@ REALLYSPACE=" "
REALLYSPACE:[1]="" == "" ?
REALLYSPACE:[*]:[1]=" " == " " ?
LIST:[1]="one"
-make: Bad modifier `:[1.]' for LIST
+make: Bad modifier ":[1.]" for variable "LIST"
LIST:[1.]="" is an error
-make: Bad modifier `:[1].' for LIST
+make: Bad modifier ":[1]." for variable "LIST"
LIST:[1].="}" is an error
LIST:[2]="two"
LIST:[6]="six"
LIST:[7]=""
LIST:[999]=""
-make: Bad modifier `:[-]' for LIST
+make: Bad modifier ":[-]" for variable "LIST"
LIST:[-]="" is an error
-make: Bad modifier `:[--]' for LIST
+make: Bad modifier ":[--]" for variable "LIST"
LIST:[--]="" is an error
LIST:[-1]="six"
LIST:[-2]="five"
@@ -67,20 +67,22 @@ LIST:[*]:C/ /,/:[2]=""
LIST:[*]:C/ /,/:[*]:[2]=""
LIST:[*]:C/ /,/:[@]:[2]="three"
LONGLIST:[012..0x12]="10 11 12 13 14 15 16 17 18"
-make: Bad modifier `:[1.]' for LIST
+make: Bad modifier ":[1.]" for variable "LIST"
LIST:[1.]="" is an error
-make: Bad modifier `:[1..]' for LIST
+make: Bad modifier ":[1..]" for variable "LIST"
LIST:[1..]="" is an error
+make: Bad modifier ":[1.. ]" for variable "LIST"
+LIST:[1.. ]="" is an error
LIST:[1..1]="one"
-make: Bad modifier `:[1..1.]' for LIST
+make: Bad modifier ":[1..1.]" for variable "LIST"
LIST:[1..1.]="" is an error
LIST:[1..2]="one two"
LIST:[2..1]="two one"
LIST:[3..-2]="three four five"
LIST:[-4..4]="three four"
-make: Bad modifier `:[0..1]' for LIST
+make: Bad modifier ":[0..1]" for variable "LIST"
LIST:[0..1]="" is an error
-make: Bad modifier `:[-1..0]' for LIST
+make: Bad modifier ":[-1..0]" for variable "LIST"
LIST:[-1..0]="" is an error
LIST:[-1..1]="six five four three two one"
LIST:[0..0]="one two three four five six"
@@ -95,7 +97,7 @@ LIST:[${ONE}]="one"
LIST:[${MINUSONE}]="six"
LIST:[${STAR}]="one two three four five six"
LIST:[${AT}]="one two three four five six"
-make: Bad modifier `:[${EMPTY' for LIST
+make: Bad modifier ":[${EMPTY" for variable "LIST"
LIST:[${EMPTY}]="" is an error
LIST:[${LONGLIST:[21]:S/2//}]="one"
LIST:[${LIST:[#]}]="six"
diff --git a/unit-tests/modword.mk b/unit-tests/modword.mk
index 383c9dca975b..95bb1fec78c3 100644
--- a/unit-tests/modword.mk
+++ b/unit-tests/modword.mk
@@ -1,4 +1,4 @@
-# $NetBSD: modword.mk,v 1.5 2020/11/15 20:20:58 rillig Exp $
+# $NetBSD: modword.mk,v 1.6 2021/03/14 16:00:07 rillig Exp $
#
# Test behaviour of new :[] modifier
# TODO: When was this modifier new?
@@ -99,6 +99,7 @@ mod-squarebrackets-n:
mod-squarebrackets-start-end:
@echo 'LIST:[1.]="${LIST:[1.]}" is an error'
@echo 'LIST:[1..]="${LIST:[1..]}" is an error'
+ @echo 'LIST:[1.. ]="${LIST:[1.. ]}" is an error'
@echo 'LIST:[1..1]="${LIST:[1..1]}"'
@echo 'LIST:[1..1.]="${LIST:[1..1.]}" is an error'
@echo 'LIST:[1..2]="${LIST:[1..2]}"'
diff --git a/unit-tests/opt-chdir.mk b/unit-tests/opt-chdir.mk
index 20241f02740e..a8806149f31c 100644
--- a/unit-tests/opt-chdir.mk
+++ b/unit-tests/opt-chdir.mk
@@ -1,4 +1,4 @@
-# $NetBSD: opt-chdir.mk,v 1.5 2020/11/15 05:43:56 sjg Exp $
+# $NetBSD: opt-chdir.mk,v 1.6 2021/05/18 17:05:45 sjg Exp $
#
# Tests for the -C command line option, which changes the directory at the
# beginning.
@@ -23,5 +23,7 @@ chdir-root: .PHONY .IGNORE
@MAKE_OBJDIR_CHECK_WRITABLE=no ${MAKE} -C / -V 'cwd: $${.CURDIR}'
# Trying to change to a nonexistent directory exits immediately.
+# Note: just because the whole point of /nonexistent is that it should
+# not exist - doesn't mean it doesn't.
chdir-nonexistent: .PHONY .IGNORE
- @${MAKE} -C /nonexistent
+ @${MAKE} -C /nonexistent.${.MAKE.PID}
diff --git a/unit-tests/opt-debug-errors-jobs.exp b/unit-tests/opt-debug-errors-jobs.exp
new file mode 100644
index 000000000000..25eb2b470b72
--- /dev/null
+++ b/unit-tests/opt-debug-errors-jobs.exp
@@ -0,0 +1,48 @@
+echo '3 spaces'; false
+3 spaces
+
+*** Failed target: fail-spaces
+*** Failed commands:
+ echo '3 spaces'; false
+*** [fail-spaces] Error code 1
+
+make: stopped in unit-tests
+echo \ indented; false
+ indented
+
+*** Failed target: fail-escaped-space
+*** Failed commands:
+ echo \ indented; false
+*** [fail-escaped-space] Error code 1
+
+make: stopped in unit-tests
+echo 'line1
+line2'; false
+line1
+line2
+
+*** Failed target: fail-newline
+*** Failed commands:
+ echo 'line1${.newline}line2'; false
+*** [fail-newline] Error code 1
+
+make: stopped in unit-tests
+echo 'line1 line2'; false
+line1 line2
+
+*** Failed target: fail-multiline
+*** Failed commands:
+ echo 'line1 line2'; false
+*** [fail-multiline] Error code 1
+
+make: stopped in unit-tests
+echo 'word1' 'word2'; false
+word1 word2
+
+*** Failed target: fail-multiline-intention
+*** Failed commands:
+ echo 'word1' 'word2'; false
+*** [fail-multiline-intention] Error code 1
+
+make: stopped in unit-tests
+exit status 1
diff --git a/unit-tests/opt-debug-errors-jobs.mk b/unit-tests/opt-debug-errors-jobs.mk
new file mode 100644
index 000000000000..83b50987a752
--- /dev/null
+++ b/unit-tests/opt-debug-errors-jobs.mk
@@ -0,0 +1,36 @@
+# $NetBSD: opt-debug-errors-jobs.mk,v 1.1 2021/04/27 16:20:06 rillig Exp $
+#
+# Tests for the -de command line option, which adds debug logging for
+# failed commands and targets; since 2021-04-27 also in jobs mode.
+
+.MAKEFLAGS: -de -j1
+
+all: fail-spaces
+all: fail-escaped-space
+all: fail-newline
+all: fail-multiline
+all: fail-multiline-intention
+
+fail-spaces:
+ echo '3 spaces'; false
+
+fail-escaped-space:
+ echo \ indented; false
+
+fail-newline:
+ echo 'line1${.newline}line2'; false
+
+# The line continuations in multiline commands are turned into an ordinary
+# space before the command is actually run.
+fail-multiline:
+ echo 'line1\
+ line2'; false
+
+# It is a common style to align the continuation backslashes at the right
+# of the lines, usually at column 73. All spaces before the continuation
+# backslash are preserved and are usually outside a shell word and thus
+# irrelevant. Since "usually" is not "always", these space characters are
+# not merged into a single space.
+fail-multiline-intention:
+ echo 'word1' \
+ 'word2'; false
diff --git a/unit-tests/opt-debug-lint.exp b/unit-tests/opt-debug-lint.exp
index f2123f20e37f..05b341b30dae 100644
--- a/unit-tests/opt-debug-lint.exp
+++ b/unit-tests/opt-debug-lint.exp
@@ -2,7 +2,7 @@ make: "opt-debug-lint.mk" line 19: Variable "X" is undefined
make: "opt-debug-lint.mk" line 41: Variable "UNDEF" is undefined
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 '$'
+make: "opt-debug-lint.mk" line 69: Unknown modifier "${"
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/unit-tests/opt-debug-lint.mk b/unit-tests/opt-debug-lint.mk
index bb1b38feb717..155e1a3de3be 100644
--- a/unit-tests/opt-debug-lint.mk
+++ b/unit-tests/opt-debug-lint.mk
@@ -1,4 +1,4 @@
-# $NetBSD: opt-debug-lint.mk,v 1.12 2020/12/20 19:10:53 rillig Exp $
+# $NetBSD: opt-debug-lint.mk,v 1.14 2021/03/14 10:57:12 rillig Exp $
#
# Tests for the -dL command line option, which runs additional checks
# to catch common mistakes, such as unclosed variable expressions.
@@ -77,5 +77,19 @@ ${UNDEF}: ${UNDEF}
. error
.endif
-all:
- @:;
+# In lint mode, the whole variable text is evaluated to check for unclosed
+# expressions and unknown operators. During this check, the subexpression
+# '${:U2}' is not expanded, instead it is copied verbatim into the regular
+# expression, leading to '.*=.{1,${:U2}}$'.
+#
+# Before var.c 1.856 from 2021-03-14, this regular expression was then
+# compiled even though that was not necessary for checking the syntax at the
+# level of variable expressions. The unexpanded '$' then resulted in a wrong
+# error message.
+#
+# This only happened in lint mode since in default mode the early check for
+# unclosed expressions and unknown modifiers is skipped.
+#
+# See VarCheckSyntax, ApplyModifier_Regex.
+#
+VARMOD_REGEX= ${:UA=111 B=222 C=33:C/.*=.{1,${:U2}}$//g}
diff --git a/unit-tests/opt-debug.exp b/unit-tests/opt-debug.exp
index 52a36c71b4ee..6a5f7b4cb3e7 100644
--- a/unit-tests/opt-debug.exp
+++ b/unit-tests/opt-debug.exp
@@ -1,4 +1,4 @@
-Global:VAR = value
-Global:.MAKEFLAGS = -r -k -d v -d
-Global:.MAKEFLAGS = -r -k -d v -d 0
+Global: VAR = value
+Global: .MAKEFLAGS = -r -k -d v -d
+Global: .MAKEFLAGS = -r -k -d v -d 0
exit status 0
diff --git a/unit-tests/opt-file.mk b/unit-tests/opt-file.mk
index 3ab8ef4e3c7d..b7a1c09e6d16 100644
--- a/unit-tests/opt-file.mk
+++ b/unit-tests/opt-file.mk
@@ -1,4 +1,4 @@
-# $NetBSD: opt-file.mk,v 1.11 2020/12/22 08:57:23 rillig Exp $
+# $NetBSD: opt-file.mk,v 1.12 2021/04/04 10:13:09 rillig Exp $
#
# Tests for the -f command line option.
@@ -28,10 +28,10 @@ all: file-containing-null-byte
# ParseReadLine (1): 'VAR=value\<A5><A5><A5><A5><A5><A5>'
# Global:VAR = value\<A5><A5><A5><A5><A5><A5>value\<A5><A5><A5><A5><A5><A5>
# ParseReadLine (2): 'alue\<A5><A5><A5><A5><A5><A5>'
-# ParseDoDependency(alue\<A5><A5><A5><A5><A5><A5>)
+# ParseDependency(alue\<A5><A5><A5><A5><A5><A5>)
# make-2014.01.01.00.00.00: "(stdin)" line 2: Need an operator
# ParseReadLine (3): '<A5><A5><A5>ZZZZZZZZZZZZZZZZ'
-# ParseDoDependency(<A5><A5><A5>ZZZZZZZZZZZZZZZZ)
+# ParseDependency(<A5><A5><A5>ZZZZZZZZZZZZZZZZ)
#
file-ending-in-backslash: .PHONY
@printf '%s' 'VAR=value\' \
diff --git a/unit-tests/opt-jobs-no-action.mk b/unit-tests/opt-jobs-no-action.mk
index a75fc38cf2fa..19d82c5bf4b8 100644
--- a/unit-tests/opt-jobs-no-action.mk
+++ b/unit-tests/opt-jobs-no-action.mk
@@ -1,4 +1,4 @@
-# $NetBSD: opt-jobs-no-action.mk,v 1.8 2020/12/10 23:54:41 rillig Exp $
+# $NetBSD: opt-jobs-no-action.mk,v 1.9 2021/04/04 09:58:51 rillig Exp $
#
# Tests for the combination of the options -j and -n, which prints the
# commands instead of actually running them.
@@ -23,7 +23,7 @@
# this is handled by the [0] != '\0' checks in Job_ParseShell.
# The '\#' is handled by ParseGetLine.
# The '\n' is handled by Str_Words in Job_ParseShell.
-# The '$$' is handled by Var_Subst in ParseDependency.
+# The '$$' is handled by Var_Subst in ParseDependencyLine.
.SHELL: \
name=sh \
path=${.SHELL} \
diff --git a/unit-tests/recursive.mk b/unit-tests/recursive.mk
index 73a8409fe030..5265cec59a2d 100644
--- a/unit-tests/recursive.mk
+++ b/unit-tests/recursive.mk
@@ -1,12 +1,12 @@
-# $NetBSD: recursive.mk,v 1.4 2020/11/09 20:50:56 rillig Exp $
+# $NetBSD: recursive.mk,v 1.5 2021/03/15 12:15:03 rillig Exp $
#
# In -dL mode, a variable may get expanded before it makes sense.
# This would stop make from doing anything since the "recursive" error
# is fatal and exits immediately.
#
# The purpose of evaluating that variable early was just to detect
-# whether there are unclosed variables. It might be enough to parse the
-# variable value without VARE_WANTRES for that purpose.
+# whether there are unclosed variables. The variable value is therefore
+# parsed with VARE_PARSE_ONLY for that purpose.
#
# Seen in pkgsrc/x11/libXfixes, and probably many more package that use
# GNU Automake.
@@ -36,4 +36,3 @@ MISSING_BRACE_INDIRECT:= ${:U\${MISSING_BRACE}
UNCLOSED= $(MISSING_PAREN
UNCLOSED= ${MISSING_BRACE
UNCLOSED= ${MISSING_BRACE_INDIRECT}
-
diff --git a/unit-tests/sh-jobs.mk b/unit-tests/sh-jobs.mk
index e8d4f976109a..de80de56040c 100644
--- a/unit-tests/sh-jobs.mk
+++ b/unit-tests/sh-jobs.mk
@@ -1,4 +1,4 @@
-# $NetBSD: sh-jobs.mk,v 1.3 2020/12/11 01:06:10 rillig Exp $
+# $NetBSD: sh-jobs.mk,v 1.4 2021/04/16 16:49:27 rillig Exp $
#
# Tests for the "run in jobs mode" part of the "Shell Commands" section
# from the manual page.
@@ -14,14 +14,14 @@ all: .PHONY comment .WAIT comment-with-followup-line .WAIT no-comment
# would lead to a syntax error in the generated shell file, at least for
# bash and dash, but not for NetBSD sh and ksh.
#
-# See JobPrintCommand, cmdTemplate, runIgnTmpl
+# See JobWriteCommand, cmdTemplate, runIgnTmpl
comment: .PHONY
@# comment
# If a shell command starts with a comment character after stripping the
# leading '@', it is run in ignore-errors mode.
#
-# See JobPrintCommand, cmdTemplate, runIgnTmpl
+# See JobWriteCommand, cmdTemplate, runIgnTmpl
comment-with-followup-line: .PHONY
@# comment${.newline}echo '$@: This is printed.'; false
@true
@@ -29,7 +29,7 @@ comment-with-followup-line: .PHONY
# Without the comment, the commands are run in the default mode, which checks
# the exit status of every makefile line.
#
-# See JobPrintCommand, cmdTemplate, runChkTmpl
+# See JobWriteCommand, cmdTemplate, runChkTmpl
no-comment: .PHONY
@echo '$@: This is printed.'; false
@true
diff --git a/unit-tests/shell-csh.mk b/unit-tests/shell-csh.mk
index 99852e33ce16..47313563d22b 100644
--- a/unit-tests/shell-csh.mk
+++ b/unit-tests/shell-csh.mk
@@ -1,4 +1,4 @@
-# $NetBSD: shell-csh.mk,v 1.7 2020/12/13 02:09:55 sjg Exp $
+# $NetBSD: shell-csh.mk,v 1.8 2021/04/04 09:58:51 rillig Exp $
#
# Tests for using a C shell for running the commands.
@@ -12,7 +12,7 @@ CSH!= which csh 2> /dev/null || true
.endif
# In parallel mode, the shell->noPrint command is filtered from
-# the output, rather naively (in JobOutput).
+# the output, rather naively (in PrintOutput).
#
# Until 2020-10-03, the output in parallel mode was garbled because
# the definition of the csh had been wrong since 1993 at least.
diff --git a/unit-tests/suff-incomplete.exp b/unit-tests/suff-incomplete.exp
index 23b959d4b4e5..2331436d378e 100644
--- a/unit-tests/suff-incomplete.exp
+++ b/unit-tests/suff-incomplete.exp
@@ -1,19 +1,19 @@
ParseReadLine (9): '.SUFFIXES:'
-ParseDoDependency(.SUFFIXES:)
+ParseDependency(.SUFFIXES:)
Clearing all suffixes
ParseReadLine (11): '.SUFFIXES: .a .b .c'
-ParseDoDependency(.SUFFIXES: .a .b .c)
+ParseDependency(.SUFFIXES: .a .b .c)
Adding suffix ".a"
Adding suffix ".b"
Adding suffix ".c"
ParseReadLine (17): '.a.b:'
-ParseDoDependency(.a.b:)
+ParseDependency(.a.b:)
defining transformation from `.a' to `.b'
inserting ".a" (1) at end of list
inserting ".b" (2) at end of list
ParseReadLine (21): '.a.c: ${.PREFIX}.dependency'
deleting incomplete transformation from `.a' to `.b'
-ParseDoDependency(.a.c: ${.PREFIX}.dependency)
+ParseDependency(.a.c: ${.PREFIX}.dependency)
defining transformation from `.a' to `.c'
inserting ".a" (1) at end of list
inserting ".c" (3) at end of list
@@ -22,7 +22,7 @@ inserting ".c" (3) at end of list
# ${.PREFIX}.dependency, unmade, type none, flags none
ParseReadLine (23): '.DEFAULT:'
transformation .a.c complete
-ParseDoDependency(.DEFAULT:)
+ParseDependency(.DEFAULT:)
ParseReadLine (24): ' : Making ${.TARGET} from ${.IMPSRC} all ${.ALLSRC} by default.'
transformation .DEFAULT complete
Wildcard expanding "all"...
diff --git a/unit-tests/suff-main-several.exp b/unit-tests/suff-main-several.exp
index a494ddc68545..09fa6d63bffa 100644
--- a/unit-tests/suff-main-several.exp
+++ b/unit-tests/suff-main-several.exp
@@ -1,12 +1,12 @@
ParseReadLine (8): '.1.2 .1.3 .1.4:'
-ParseDoDependency(.1.2 .1.3 .1.4:)
+ParseDependency(.1.2 .1.3 .1.4:)
Setting main node to ".1.2"
ParseReadLine (9): ' : Making ${.TARGET} from ${.IMPSRC}.'
ParseReadLine (14): 'next-main:'
-ParseDoDependency(next-main:)
+ParseDependency(next-main:)
ParseReadLine (15): ' : Making ${.TARGET}'
ParseReadLine (19): '.SUFFIXES: .1 .2 .3 .4'
-ParseDoDependency(.SUFFIXES: .1 .2 .3 .4)
+ParseDependency(.SUFFIXES: .1 .2 .3 .4)
Adding suffix ".1"
Adding suffix ".2"
Setting main node from ".1.2" back to null
@@ -27,42 +27,42 @@ inserting ".1" (1) at end of list
inserting ".4" (4) at end of list
Setting main node to "next-main"
ParseReadLine (24): '.SUFFIXES:'
-ParseDoDependency(.SUFFIXES:)
+ParseDependency(.SUFFIXES:)
Clearing all suffixes
ParseReadLine (32): '.SUFFIXES: .4 .3 .2 .1'
-ParseDoDependency(.SUFFIXES: .4 .3 .2 .1)
+ParseDependency(.SUFFIXES: .4 .3 .2 .1)
Adding suffix ".4"
Adding suffix ".3"
Adding suffix ".2"
Adding suffix ".1"
ParseReadLine (33): '.SUFFIXES:'
-ParseDoDependency(.SUFFIXES:)
+ParseDependency(.SUFFIXES:)
Clearing all suffixes
ParseReadLine (34): '.SUFFIXES: .1 .2 .3 .4'
-ParseDoDependency(.SUFFIXES: .1 .2 .3 .4)
+ParseDependency(.SUFFIXES: .1 .2 .3 .4)
Adding suffix ".1"
Adding suffix ".2"
Adding suffix ".3"
Adding suffix ".4"
ParseReadLine (35): '.SUFFIXES:'
-ParseDoDependency(.SUFFIXES:)
+ParseDependency(.SUFFIXES:)
Clearing all suffixes
ParseReadLine (36): '.SUFFIXES: .4 .3 .2 .1'
-ParseDoDependency(.SUFFIXES: .4 .3 .2 .1)
+ParseDependency(.SUFFIXES: .4 .3 .2 .1)
Adding suffix ".4"
Adding suffix ".3"
Adding suffix ".2"
Adding suffix ".1"
ParseReadLine (38): 'suff-main-several.1:'
-ParseDoDependency(suff-main-several.1:)
+ParseDependency(suff-main-several.1:)
ParseReadLine (39): ' : Making ${.TARGET} out of nothing.'
ParseReadLine (40): 'next-main: suff-main-several.{2,3,4}'
-ParseDoDependency(next-main: suff-main-several.{2,3,4})
+ParseDependency(next-main: suff-main-several.{2,3,4})
# LinkSource: added child next-main - suff-main-several.{2,3,4}
# next-main, unmade, type OP_DEPENDS|OP_HAS_COMMANDS, flags none
# suff-main-several.{2,3,4}, unmade, type none, flags none
ParseReadLine (42): '.MAKEFLAGS: -d0 -dg1'
-ParseDoDependency(.MAKEFLAGS: -d0 -dg1)
+ParseDependency(.MAKEFLAGS: -d0 -dg1)
#*** Input graph:
# .1.2, unmade, type OP_TRANSFORM, flags none
# .1.3, unmade, type OP_TRANSFORM, flags none
diff --git a/unit-tests/suff-rebuild.exp b/unit-tests/suff-rebuild.exp
index ccb423a6086a..7ef53ae2e151 100644
--- a/unit-tests/suff-rebuild.exp
+++ b/unit-tests/suff-rebuild.exp
@@ -1,38 +1,38 @@
ParseReadLine (10): '.SUFFIXES:'
-ParseDoDependency(.SUFFIXES:)
+ParseDependency(.SUFFIXES:)
Clearing all suffixes
ParseReadLine (12): '.SUFFIXES: .a .b .c'
-ParseDoDependency(.SUFFIXES: .a .b .c)
+ParseDependency(.SUFFIXES: .a .b .c)
Adding suffix ".a"
Adding suffix ".b"
Adding suffix ".c"
ParseReadLine (14): 'suff-rebuild-example.a:'
-ParseDoDependency(suff-rebuild-example.a:)
+ParseDependency(suff-rebuild-example.a:)
Adding "suff-rebuild-example.a" to all targets.
ParseReadLine (15): ' : Making ${.TARGET} out of nothing.'
ParseReadLine (17): '.a.b:'
-ParseDoDependency(.a.b:)
+ParseDependency(.a.b:)
defining transformation from `.a' to `.b'
inserting ".a" (1) at end of list
inserting ".b" (2) at end of list
ParseReadLine (18): ' : Making ${.TARGET} from ${.IMPSRC}.'
ParseReadLine (19): '.b.c:'
transformation .a.b complete
-ParseDoDependency(.b.c:)
+ParseDependency(.b.c:)
defining transformation from `.b' to `.c'
inserting ".b" (2) at end of list
inserting ".c" (3) at end of list
ParseReadLine (20): ' : Making ${.TARGET} from ${.IMPSRC}.'
ParseReadLine (21): '.c:'
transformation .b.c complete
-ParseDoDependency(.c:)
+ParseDependency(.c:)
defining transformation from `.c' to `'
inserting ".c" (3) at end of list
inserting "" (0) at end of list
ParseReadLine (22): ' : Making ${.TARGET} from ${.IMPSRC}.'
ParseReadLine (44): '.SUFFIXES: .c .b .a'
transformation .c complete
-ParseDoDependency(.SUFFIXES: .c .b .a)
+ParseDependency(.SUFFIXES: .c .b .a)
Adding ".END" to all targets.
Wildcard expanding "all"...
SuffFindDeps "all"
diff --git a/unit-tests/var-class-cmdline.exp b/unit-tests/var-class-cmdline.exp
index 39a9383953dd..6df2155ca7eb 100644
--- a/unit-tests/var-class-cmdline.exp
+++ b/unit-tests/var-class-cmdline.exp
@@ -1 +1,4 @@
+make: "var-class-cmdline.mk" line 67: global
+make: "var-class-cmdline.mk" line 76: makeflags
+makeflags
exit status 0
diff --git a/unit-tests/var-class-cmdline.mk b/unit-tests/var-class-cmdline.mk
index c43b5351c329..679e051bb242 100644
--- a/unit-tests/var-class-cmdline.mk
+++ b/unit-tests/var-class-cmdline.mk
@@ -1,8 +1,80 @@
-# $NetBSD: var-class-cmdline.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+# $NetBSD: var-class-cmdline.mk,v 1.5 2021/02/23 21:59:31 rillig Exp $
#
# Tests for variables specified on the command line.
+#
+# Variables that are specified on the command line override those from the
+# global scope.
+#
+# For performance reasons, the actual implementation is more complex than the
+# above single-sentence rule, in order to avoid unnecessary lookups in scopes,
+# which before var.c 1.586 from 2020-10-25 calculated the hash value of the
+# variable name once for each lookup. Instead, when looking up the value of
+# a variable, the search often starts in the global scope since that is where
+# most of the variables are stored. This conflicts with the statement that
+# variables from the cmdline scope override global variables, since after the
+# common case of finding a variable in the global scope, another lookup would
+# be needed in the cmdline scope to ensure that there is no overriding
+# variable there.
+#
+# Instead of this costly lookup scheme, make implements it in a different
+# way:
+#
+# Whenever a global variable is created, this creation is ignored if
+# there is a cmdline variable of the same name.
+#
+# Whenever a cmdline variable is created, any global variable of the
+# same name is deleted.
+#
+# Whenever a global variable is deleted, nothing special happens.
+#
+# Deleting a cmdline variable is not possible.
+#
+# These 4 rules provide the guarantee that whenever a global variable exists,
+# there cannot be a cmdline variable of the same name. Therefore, after
+# finding a variable in the global scope, no additional lookup is needed in
+# the cmdline scope.
+#
+# The above ruleset provides the same guarantees as the simple rule "cmdline
+# overrides global". Due to an implementation mistake, the actual behavior
+# was not entirely equivalent to the simple rule though. The mistake was
+# that when a cmdline variable with '$$' in its name was added, a global
+# variable was deleted, but not with the exact same name as the cmdline
+# variable. Instead, the name of the global variable was expanded one more
+# time than the name of the cmdline variable. For variable names that didn't
+# have a '$$' in their name, it was implemented correctly all the time.
+#
+# The bug was added in var.c 1.183 on 2013-07-16, when Var_Set called
+# Var_Delete to delete the global variable. Just two months earlier, in var.c
+# 1.174 from 2013-05-18, Var_Delete had started to expand the variable name.
+# Together, these two changes made the variable name be expanded twice in a
+# row. This bug was fixed in var.c 1.835 from 2021-02-22.
+#
+# Another bug was the wrong assumption that "deleting a cmdline variable is
+# not possible". Deleting such a variable has been possible since var.c 1.204
+# from 2016-02-19, when the variable modifier ':@' started to delete the
+# temporary loop variable after finishing the loop. It was probably not
+# intended back then that a side effect of this seemingly simple change was
+# that both global and cmdline variables could now be undefined at will as a
+# side effect of evaluating a variable expression. As of 2021-02-23, this is
+# still possible.
+#
+# 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.
+
+# A normal global variable, without any cmdline variable nearby.
+VAR= global
+.info ${VAR}
-# TODO: Implementation
+# The global variable is "overridden" by simply deleting it and then
+# installing the cmdline variable instead. Since there is no obvious way to
+# undefine a cmdline variable, there is no need to remember the old value
+# of the global variable could become visible again.
+#
+# See varmod-loop.mk for a non-obvious way to undefine a cmdline variable.
+.MAKEFLAGS: VAR=makeflags
+.info ${VAR}
-all:
- @:;
+# If Var_SetWithFlags should ever forget to delete the global variable,
+# the below line would print "global" instead of the current "makeflags".
+.MAKEFLAGS: -V VAR
diff --git a/unit-tests/var-eval-short.exp b/unit-tests/var-eval-short.exp
new file mode 100644
index 000000000000..ae0aff7d7c2c
--- /dev/null
+++ b/unit-tests/var-eval-short.exp
@@ -0,0 +1,29 @@
+make: "var-eval-short.mk" line 41: In the :@ modifier of "", the variable name "${FAIL}" must not contain a dollar.
+make: "var-eval-short.mk" line 41: Malformed conditional (0 && ${:Uword:@${FAIL}@expr@})
+make: "var-eval-short.mk" line 79: Invalid time value: ${FAIL}}
+make: "var-eval-short.mk" line 79: Malformed conditional (0 && ${:Uword:gmtime=${FAIL}})
+make: "var-eval-short.mk" line 93: Invalid time value: ${FAIL}}
+make: "var-eval-short.mk" line 93: Malformed conditional (0 && ${:Uword:localtime=${FAIL}})
+CondParser_Eval: 0 && ${0:?${FAIL}then:${FAIL}else}
+Var_Parse: ${0:?${FAIL}then:${FAIL}else} (parse-only)
+Parsing modifier ${0:?...}
+Modifier part: "${FAIL}then"
+Modifier part: "${FAIL}else"
+Result of ${0:?${FAIL}then:${FAIL}else} is "" (parse-only, defined)
+ParseReadLine (158): 'DEFINED= defined'
+Global: DEFINED = defined
+CondParser_Eval: 0 && ${DEFINED:L:?${FAIL}then:${FAIL}else}
+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:?...}
+Modifier part: "${FAIL}then"
+Modifier part: "${FAIL}else"
+Result of ${DEFINED:?${FAIL}then:${FAIL}else} is "defined" (parse-only, regular)
+ParseReadLine (161): '.MAKEFLAGS: -d0'
+ParseDependency(.MAKEFLAGS: -d0)
+Global: .MAKEFLAGS = -r -k -d cpv -d
+Global: .MAKEFLAGS = -r -k -d cpv -d 0
+make: Fatal errors encountered -- cannot continue
+make: stopped in unit-tests
+exit status 1
diff --git a/unit-tests/var-eval-short.mk b/unit-tests/var-eval-short.mk
new file mode 100644
index 000000000000..41782f0d7823
--- /dev/null
+++ b/unit-tests/var-eval-short.mk
@@ -0,0 +1,163 @@
+# $NetBSD: var-eval-short.mk,v 1.5 2021/04/04 13:35:26 rillig Exp $
+#
+# Tests for each variable modifier to ensure that they only do the minimum
+# necessary computations. If the result of the expression is not needed, they
+# should only parse the modifier but not actually evaluate it.
+#
+# See also:
+# var.c, the comment starting with 'The ApplyModifier functions'
+# ApplyModifier, for the order of the modifiers
+# ParseModifierPart, for evaluating nested expressions
+# cond-short.mk
+
+FAIL= ${:!echo unexpected 1>&2!}
+
+# The following tests only ensure that nested expressions are not evaluated.
+# They cannot ensure that any unexpanded text returned from ParseModifierPart
+# is ignored as well. To do that, it is necessary to step through the code of
+# each modifier.
+
+.if 0 && ${FAIL}
+.endif
+
+.if 0 && ${VAR::=${FAIL}}
+.elif defined(VAR)
+. error
+.endif
+
+.if 0 && ${${FAIL}:?then:else}
+.endif
+
+.if 0 && ${1:?${FAIL}:${FAIL}}
+.endif
+
+.if 0 && ${0:?${FAIL}:${FAIL}}
+.endif
+
+# Before var.c 1.870 from 2021-03-14, the expression ${FAIL} was evaluated
+# after the loop, when undefining the temporary global loop variable.
+# Since var.c 1.907 from 2021-04-04, a '$' is no longer allowed in the
+# variable name.
+.if 0 && ${:Uword:@${FAIL}@expr@}
+.endif
+
+.if 0 && ${:Uword:@var@${FAIL}@}
+.endif
+
+# Before var.c,v 1.877 from 2021-03-14, the modifier ':[...]' did not expand
+# the nested expression ${FAIL} and then tried to parse the unexpanded text,
+# which failed since '$' is not a valid range character.
+.if 0 && ${:Uword:[${FAIL}]}
+.endif
+
+# Before var.c,v 1.867 from 2021-03-14, the modifier ':_' defined the variable
+# even though the whole expression should have only been parsed, not
+# evaluated.
+.if 0 && ${:Uword:_=VAR}
+.elif defined(VAR)
+. error
+.endif
+
+# Before var.c,v 1.856 from 2021-03-14, the modifier ':C' did not expand the
+# nested expression ${FAIL} and then tried to compile the unexpanded text as a
+# regular expression, which failed both because of the '{FAIL}', which is not
+# a valid repetition, and because of the '****', which are repeated
+# repetitions as well.
+# '${FAIL}'
+.if 0 && ${:Uword:C,${FAIL}****,,}
+.endif
+
+DEFINED= # defined
+.if 0 && ${DEFINED:D${FAIL}}
+.endif
+
+.if 0 && ${:Uword:E}
+.endif
+
+# As of 2021-03-14, the error 'Invalid time value: ${FAIL}}' is ok since
+# ':gmtime' does not expand its argument.
+.if 0 && ${:Uword:gmtime=${FAIL}}
+.endif
+
+.if 0 && ${:Uword:H}
+.endif
+
+.if 0 && ${:Uword:hash}
+.endif
+
+.if 0 && ${value:L}
+.endif
+
+# As of 2021-03-14, the error 'Invalid time value: ${FAIL}}' is ok since
+# ':localtime' does not expand its argument.
+.if 0 && ${:Uword:localtime=${FAIL}}
+.endif
+
+.if 0 && ${:Uword:M${FAIL}}
+.endif
+
+.if 0 && ${:Uword:N${FAIL}}
+.endif
+
+.if 0 && ${:Uword:O}
+.endif
+
+.if 0 && ${:Uword:Ox}
+.endif
+
+.if 0 && ${:Uword:P}
+.endif
+
+.if 0 && ${:Uword:Q}
+.endif
+
+.if 0 && ${:Uword:q}
+.endif
+
+.if 0 && ${:Uword:R}
+.endif
+
+.if 0 && ${:Uword:range}
+.endif
+
+.if 0 && ${:Uword:S,${FAIL},${FAIL},}
+.endif
+
+.if 0 && ${:Uword:sh}
+.endif
+
+.if 0 && ${:Uword:T}
+.endif
+
+.if 0 && ${:Uword:ts/}
+.endif
+
+.if 0 && ${:U${FAIL}}
+.endif
+
+.if 0 && ${:Uword:u}
+.endif
+
+.if 0 && ${:Uword:word=replacement}
+.endif
+
+# Before var.c 1.875 from 2021-03-14, Var_Parse returned "${FAIL}else" for the
+# irrelevant right-hand side of the condition, even though this was not
+# necessary. Since the return value from Var_Parse is supposed to be ignored
+# anyway, and since it is actually ignored in an overly complicated way,
+# an empty string suffices.
+.MAKEFLAGS: -dcpv
+.if 0 && ${0:?${FAIL}then:${FAIL}else}
+.endif
+
+# The ':L' is applied before the ':?' modifier, giving the expression a name
+# and a value, just to see whether this value gets passed through or whether
+# the parse-only mode results in an empty string (only visible in the debug
+# log). As of var.c 1.875 from 2021-03-14, the value of the variable gets
+# through, even though an empty string would suffice.
+DEFINED= defined
+.if 0 && ${DEFINED:L:?${FAIL}then:${FAIL}else}
+.endif
+.MAKEFLAGS: -d0
+
+all:
diff --git a/unit-tests/var-op-append.exp b/unit-tests/var-op-append.exp
index 424ad37ccf63..32134be75a3d 100644
--- a/unit-tests/var-op-append.exp
+++ b/unit-tests/var-op-append.exp
@@ -1,7 +1,7 @@
-Var_Parse: ${:U\$\$\$\$\$\$\$\$} with VARE_WANTRES
-Applying ${:U...} to "" (VARE_WANTRES, none, VES_UNDEF)
-Result of ${:U\$\$\$\$\$\$\$\$} is "$$$$$$$$" (VARE_WANTRES, none, VES_DEF)
-Global:VAR.$$$$$$$$ = dollars
-Global:.MAKEFLAGS = -r -k -d v -d
-Global:.MAKEFLAGS = -r -k -d v -d 0
+Var_Parse: ${:U\$\$\$\$\$\$\$\$} (eval)
+Evaluating modifier ${:U...} on value "" (eval, undefined)
+Result of ${:U\$\$\$\$\$\$\$\$} is "$$$$$$$$" (eval, defined)
+Global: VAR.$$$$$$$$ = dollars
+Global: .MAKEFLAGS = -r -k -d v -d
+Global: .MAKEFLAGS = -r -k -d v -d 0
exit status 0
diff --git a/unit-tests/var-op-append.mk b/unit-tests/var-op-append.mk
index deb4af6a7384..420ee376b75d 100644
--- a/unit-tests/var-op-append.mk
+++ b/unit-tests/var-op-append.mk
@@ -1,4 +1,4 @@
-# $NetBSD: var-op-append.mk,v 1.8 2021/02/03 08:40:47 rillig Exp $
+# $NetBSD: var-op-append.mk,v 1.9 2021/04/04 10:13:09 rillig Exp $
#
# Tests for the += variable assignment operator, which appends to a variable,
# creating it if necessary.
@@ -26,7 +26,7 @@ VAR+= # empty
# '+=' assignment operator. As far as possible, the '+' is interpreted as
# part of the assignment operator.
#
-# See Parse_DoVar
+# See Parse_Var
C++= value
.if ${C+} != "value" || defined(C++)
. error
diff --git a/unit-tests/var-op-assign.mk b/unit-tests/var-op-assign.mk
index 3bcc3de0ba0e..18ecf8d0d5ed 100644
--- a/unit-tests/var-op-assign.mk
+++ b/unit-tests/var-op-assign.mk
@@ -1,4 +1,4 @@
-# $NetBSD: var-op-assign.mk,v 1.7 2020/11/15 20:20:58 rillig Exp $
+# $NetBSD: var-op-assign.mk,v 1.8 2021/03/15 19:15:04 rillig Exp $
#
# Tests for the = variable assignment operator, which overwrites an existing
# variable or creates it.
@@ -42,7 +42,7 @@ VAR= new value and \# some $$ special characters # comment
# This alone would not produce any side-effects, therefore the variable has
# a :!...! modifier that executes a shell command. The :!...! modifier turns
# an undefined expression into a defined one, see ApplyModifier_ShellCommand,
-# the call to ApplyModifiersState_Define.
+# the call to Expr_Define.
#
# Since the right-hand side of a '=' assignment is not expanded at the time
# when the variable is defined, the first command is not run at all.
diff --git a/unit-tests/var-op-sunsh.mk b/unit-tests/var-op-sunsh.mk
index 0e16b2b42d34..0d15b8c88b92 100644
--- a/unit-tests/var-op-sunsh.mk
+++ b/unit-tests/var-op-sunsh.mk
@@ -1,4 +1,4 @@
-# $NetBSD: var-op-sunsh.mk,v 1.6 2020/11/15 20:20:58 rillig Exp $
+# $NetBSD: var-op-sunsh.mk,v 1.8 2021/04/04 10:13:09 rillig Exp $
#
# Tests for the :sh= variable assignment operator, which runs its right-hand
# side through the shell. It is a seldom-used alternative to the !=
@@ -50,7 +50,7 @@ VAR:shoe:shore= echo two-colons
# The variable modifier ':sh' and the assignment operator modifier ':sh'.
# Intuitively this variable name contains the variable modifier, but until
# 2020-10-04, the parser regarded it as an assignment operator modifier, in
-# Parse_DoVar.
+# Parse_Var.
VAR.${:Uecho 123:sh}= ok-123
.if ${VAR.123} != "ok-123"
. error
@@ -75,11 +75,11 @@ VAR.key:shift= Shift
# the ':sh' assignment operator modifier. Let's see what happens ...
#
# Well, the end result is correct but the way until there is rather
-# adventurous. This only works because the parser replaces each an every
-# whitespace character that is not nested with '\0' (see Parse_DoVar).
+# adventurous. This only works because the parser replaces each and every
+# whitespace character that is not nested with '\0' (see Parse_Var).
# The variable name therefore ends before the first ':sh', and the last
# ':sh' turns the assignment operator into the shell command evaluation.
-# Parse_DoVar completely trusts Parse_IsVar to properly verify the syntax.
+# Parse_Var completely trusts Parse_IsVar to properly verify the syntax.
#
# The ':sh' is the only word that may occur between the variable name and
# the assignment operator at nesting level 0. All other words would lead
@@ -102,7 +102,7 @@ VAR :sh(Put a comment here)= comment in parentheses
# The unintended comment can include multiple levels of nested braces and
# parentheses, they don't even need to be balanced since they are only
-# counted by Parse_IsVar and ignored by Parse_DoVar.
+# counted by Parse_IsVar and ignored by Parse_Var.
VAR :sh{Put}((((a}{comment}}}}{here}= comment in braces
.if ${VAR} != "comment in braces"
. error
diff --git a/unit-tests/varcmd.mk b/unit-tests/varcmd.mk
index 9ec4f4f9a21a..12739df30926 100644
--- a/unit-tests/varcmd.mk
+++ b/unit-tests/varcmd.mk
@@ -1,6 +1,17 @@
-# $NetBSD: varcmd.mk,v 1.5 2020/10/24 08:50:17 rillig Exp $
+# $NetBSD: varcmd.mk,v 1.6 2021/02/16 19:43:09 rillig Exp $
#
# Test behaviour of recursive make and vars set on command line.
+#
+# FIXME: The purpose of this test is unclear. The test uses six levels of
+# sub-makes, which makes it incredibly hard to understand. There must be at
+# least an introductory explanation about what _should_ happen here.
+# The variable names are terrible, as well as their values.
+#
+# This test produces different results if the large block with the condition
+# "scope == SCOPE_GLOBAL" in Var_SetWithFlags is removed. This test should
+# 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.
FU= fu
FOO?= foo
@@ -57,4 +68,3 @@ five: show show-v
six: show-v
@${.MAKE} -f ${MAKEFILE} V=override show-v
-
diff --git a/unit-tests/vardebug.exp b/unit-tests/vardebug.exp
index a9a00a11f5dd..6d00acc977af 100644
--- a/unit-tests/vardebug.exp
+++ b/unit-tests/vardebug.exp
@@ -1,86 +1,69 @@
Global:delete FROM_CMDLINE (not found)
-Command:FROM_CMDLINE =
-Global:.MAKEOVERRIDES = FROM_CMDLINE
-Global:VAR = added
-Global:VAR = overwritten
+Command: FROM_CMDLINE =
+Global: .MAKEOVERRIDES = FROM_CMDLINE
+Global: VAR = added
+Global: VAR = overwritten
Global:delete VAR
Global:delete VAR (not found)
-Var_Parse: ${:U} with VARE_WANTRES
-Applying ${:U} to "" (VARE_WANTRES, none, VES_UNDEF)
-Result of ${:U} is "" (VARE_WANTRES, none, VES_DEF)
-Var_Set("${:U}", "empty name", ...) name expands to empty string - ignored
-Var_Parse: ${:U} with VARE_WANTRES
-Applying ${:U} to "" (VARE_WANTRES, none, VES_UNDEF)
-Result of ${:U} is "" (VARE_WANTRES, none, VES_DEF)
-Var_Append("${:U}", "empty name", ...) name expands to empty string - ignored
-Global:FROM_CMDLINE = overwritten ignored!
-Global:VAR = 1
-Global:VAR = 1 2
-Global:VAR = 1 2 3
-Var_Parse: ${VAR:M[2]} with VARE_UNDEFERR|VARE_WANTRES
-Applying ${VAR:M...} to "1 2 3" (VARE_UNDEFERR|VARE_WANTRES, none, none)
-Pattern[VAR] for [1 2 3] is [[2]]
+Var_SetExpand: variable name "${:U}" expands to empty string, with value "empty name" - ignored
+Var_AppendExpand: variable name "${:U}" expands to empty string, with value "empty name" - ignored
+Global: FROM_CMDLINE = overwritten ignored!
+Global: VAR = 1
+Global: VAR = 1 2
+Global: VAR = 1 2 3
+Var_Parse: ${VAR:M[2]} (eval-defined)
+Evaluating modifier ${VAR:M...} on value "1 2 3"
+Pattern for ':M' is "[2]"
ModifyWords: split "1 2 3" into 3 words
-VarMatch [1] [[2]]
-VarMatch [2] [[2]]
-VarMatch [3] [[2]]
-Result of ${VAR:M[2]} is "2" (VARE_UNDEFERR|VARE_WANTRES, none, none)
-Var_Parse: ${VAR:N[2]} with VARE_UNDEFERR|VARE_WANTRES
-Applying ${VAR:N...} to "1 2 3" (VARE_UNDEFERR|VARE_WANTRES, none, none)
-Pattern[VAR] for [1 2 3] is [[2]]
+Result of ${VAR:M[2]} is "2"
+Var_Parse: ${VAR:N[2]} (eval-defined)
+Evaluating modifier ${VAR:N...} on value "1 2 3"
+Pattern for ':N' is "[2]"
ModifyWords: split "1 2 3" into 3 words
-Result of ${VAR:N[2]} is "1 3" (VARE_UNDEFERR|VARE_WANTRES, none, none)
-Var_Parse: ${VAR:S,2,two,} with VARE_UNDEFERR|VARE_WANTRES
-Applying ${VAR:S...} to "1 2 3" (VARE_UNDEFERR|VARE_WANTRES, none, none)
+Result of ${VAR:N[2]} is "1 3"
+Var_Parse: ${VAR:S,2,two,} (eval-defined)
+Evaluating modifier ${VAR:S...} on value "1 2 3"
Modifier part: "2"
Modifier part: "two"
ModifyWords: split "1 2 3" into 3 words
-Result of ${VAR:S,2,two,} is "1 two 3" (VARE_UNDEFERR|VARE_WANTRES, none, none)
-Var_Parse: ${VAR:Q} with VARE_UNDEFERR|VARE_WANTRES
-Applying ${VAR:Q} to "1 2 3" (VARE_UNDEFERR|VARE_WANTRES, none, none)
-Result of ${VAR:Q} is "1\ 2\ 3" (VARE_UNDEFERR|VARE_WANTRES, none, none)
-Var_Parse: ${VAR:tu:tl:Q} with VARE_UNDEFERR|VARE_WANTRES
-Applying ${VAR:t...} to "1 2 3" (VARE_UNDEFERR|VARE_WANTRES, none, none)
-Result of ${VAR:tu} is "1 2 3" (VARE_UNDEFERR|VARE_WANTRES, none, none)
-Applying ${VAR:t...} to "1 2 3" (VARE_UNDEFERR|VARE_WANTRES, none, none)
-Result of ${VAR:tl} is "1 2 3" (VARE_UNDEFERR|VARE_WANTRES, none, none)
-Applying ${VAR:Q} to "1 2 3" (VARE_UNDEFERR|VARE_WANTRES, none, none)
-Result of ${VAR:Q} is "1\ 2\ 3" (VARE_UNDEFERR|VARE_WANTRES, none, none)
-Var_Parse: ${:Uvalue:${:UM*e}:Mvalu[e]} with VARE_UNDEFERR|VARE_WANTRES
-Applying ${:U...} to "" (VARE_UNDEFERR|VARE_WANTRES, none, VES_UNDEF)
-Result of ${:Uvalue} is "value" (VARE_UNDEFERR|VARE_WANTRES, none, VES_DEF)
-Var_Parse: ${:UM*e}:Mvalu[e]} with VARE_UNDEFERR|VARE_WANTRES
-Applying ${:U...} to "" (VARE_UNDEFERR|VARE_WANTRES, none, VES_UNDEF)
-Result of ${:UM*e} is "M*e" (VARE_UNDEFERR|VARE_WANTRES, none, VES_DEF)
+Result of ${VAR:S,2,two,} is "1 two 3"
+Var_Parse: ${VAR:Q} (eval-defined)
+Evaluating modifier ${VAR:Q} on value "1 2 3"
+Result of ${VAR:Q} is "1\ 2\ 3"
+Var_Parse: ${VAR:tu:tl:Q} (eval-defined)
+Evaluating modifier ${VAR:t...} on value "1 2 3"
+Result of ${VAR:tu} is "1 2 3"
+Evaluating modifier ${VAR:t...} on value "1 2 3"
+Result of ${VAR:tl} is "1 2 3"
+Evaluating modifier ${VAR:Q} on value "1 2 3"
+Result of ${VAR:Q} is "1\ 2\ 3"
+Var_Parse: ${:Uvalue:${:UM*e}:Mvalu[e]} (eval-defined)
+Evaluating modifier ${:U...} on value "" (eval-defined, undefined)
+Result of ${:Uvalue} is "value" (eval-defined, defined)
Indirect modifier "M*e" from "${:UM*e}"
-Applying ${:M...} to "value" (VARE_UNDEFERR|VARE_WANTRES, none, VES_DEF)
-Pattern[] for [value] is [*e]
+Evaluating modifier ${:M...} on value "value" (eval-defined, defined)
+Pattern for ':M' is "*e"
ModifyWords: split "value" into 1 words
-VarMatch [value] [*e]
-Result of ${:M*e} is "value" (VARE_UNDEFERR|VARE_WANTRES, none, VES_DEF)
-Applying ${:M...} to "value" (VARE_UNDEFERR|VARE_WANTRES, none, VES_DEF)
-Pattern[] for [value] is [valu[e]]
+Result of ${:M*e} is "value" (eval-defined, defined)
+Evaluating modifier ${:M...} on value "value" (eval-defined, defined)
+Pattern for ':M' is "valu[e]"
ModifyWords: split "value" into 1 words
-VarMatch [value] [valu[e]]
-Result of ${:Mvalu[e]} is "value" (VARE_UNDEFERR|VARE_WANTRES, none, VES_DEF)
-Var_Parse: ${:UVAR} with VARE_WANTRES
-Applying ${:U...} to "" (VARE_WANTRES, none, VES_UNDEF)
-Result of ${:UVAR} is "VAR" (VARE_WANTRES, none, VES_DEF)
+Result of ${:Mvalu[e]} is "value" (eval-defined, defined)
Global:delete VAR
-Var_Parse: ${:Uvariable:unknown} with VARE_UNDEFERR|VARE_WANTRES
-Applying ${:U...} to "" (VARE_UNDEFERR|VARE_WANTRES, none, VES_UNDEF)
-Result of ${:Uvariable} is "variable" (VARE_UNDEFERR|VARE_WANTRES, none, VES_DEF)
-Applying ${:u...} to "variable" (VARE_UNDEFERR|VARE_WANTRES, none, VES_DEF)
-make: "vardebug.mk" line 44: Unknown modifier 'u'
-Result of ${:unknown} is error (VARE_UNDEFERR|VARE_WANTRES, none, VES_DEF)
+Var_Parse: ${:Uvariable:unknown} (eval-defined)
+Evaluating modifier ${:U...} on value "" (eval-defined, undefined)
+Result of ${:Uvariable} is "variable" (eval-defined, defined)
+Evaluating modifier ${:u...} on value "variable" (eval-defined, defined)
+make: "vardebug.mk" line 44: Unknown modifier "unknown"
+Result of ${:unknown} is error (eval-defined, defined)
make: "vardebug.mk" line 44: Malformed conditional (${:Uvariable:unknown})
-Var_Parse: ${UNDEFINED} with VARE_UNDEFERR|VARE_WANTRES
+Var_Parse: ${UNDEFINED} (eval-defined)
make: "vardebug.mk" line 53: Malformed conditional (${UNDEFINED})
Global:delete .SHELL (not found)
-Command:.SHELL = </path/to/shell>
-Command:.SHELL = overwritten ignored (read-only)
-Global:.MAKEFLAGS = -r -k -d v -d
-Global:.MAKEFLAGS = -r -k -d v -d 0
+Command: .SHELL = </path/to/shell>
+Command: .SHELL = overwritten ignored (read-only)
+Global: .MAKEFLAGS = -r -k -d v -d
+Global: .MAKEFLAGS = -r -k -d v -d 0
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/unit-tests/varmisc.exp b/unit-tests/varmisc.exp
index e8f88d9ca51f..f56f72d0ab9c 100644
--- a/unit-tests/varmisc.exp
+++ b/unit-tests/varmisc.exp
@@ -54,7 +54,7 @@ make: Unclosed variable "UNCLOSED"
make: Unclosed variable "UNCLOSED"
make: Unclosed variable "PATTERN"
-make: Unclosed variable specification (expecting '}') for "UNCLOSED" (value "") modifier M
+make: Unclosed variable expression, expecting '}' for modifier "M${PATTERN" of variable "UNCLOSED" with value ""
make: Unclosed variable "param"
make: Unclosed variable "UNCLOSED."
diff --git a/unit-tests/varmod-assign.exp b/unit-tests/varmod-assign.exp
index 743ef2fb4082..1e43714d500b 100644
--- a/unit-tests/varmod-assign.exp
+++ b/unit-tests/varmod-assign.exp
@@ -1,3 +1,17 @@
+Global: param = twice
+Global: VARNAME = VAR.$${param}
+Var_Parse: ${VARNAME} (eval)
+Global: VAR.${param} = initial-value
+Var_Parse: ${${VARNAME}::=assigned-value} (eval-defined)
+Var_Parse: ${VARNAME}::=assigned-value} (eval-defined)
+Evaluating modifier ${VAR.${param}::...} on value "initial-value"
+Modifier part: "assigned-value"
+Global: VAR.${param} = assigned-value
+Result of ${VAR.${param}::=assigned-value} is ""
+Var_Parse: ${${VARNAME}} != "assigned-value" (eval-defined)
+Var_Parse: ${VARNAME}} != "assigned-value" (eval-defined)
+Global: .MAKEFLAGS = -r -k -d v -d
+Global: .MAKEFLAGS = -r -k -d v -d 0
mod-assign: first=1.
mod-assign: last=3.
mod-assign: appended=1 2 3.
@@ -10,15 +24,15 @@ mod-assign-nested: then1t1
mod-assign-nested: else2e2
mod-assign-nested: then3t3
mod-assign-nested: else4e4
-make: Bad modifier `:' for
+make: Bad modifier ":" for variable ""
mod-assign-empty: value}
-make: Bad modifier `:' for
+make: Bad modifier ":" for variable ""
mod-assign-empty: overwritten}
mod-assign-empty: VAR=overwritten
-make: Unknown modifier ':'
+make: Unknown modifier ":x"
sysv:y
-make: Unfinished modifier for ASSIGN ('}' missing)
+make: Unfinished modifier for "ASSIGN" ('}' missing)
ok=word
make: " echo word; false " returned non-zero status
diff --git a/unit-tests/varmod-assign.mk b/unit-tests/varmod-assign.mk
index e4cbc249df88..f50c654f5bcf 100644
--- a/unit-tests/varmod-assign.mk
+++ b/unit-tests/varmod-assign.mk
@@ -1,4 +1,4 @@
-# $NetBSD: varmod-assign.mk,v 1.9 2021/01/22 22:54:53 rillig Exp $
+# $NetBSD: varmod-assign.mk,v 1.12 2021/03/15 18:56:38 rillig Exp $
#
# Tests for the obscure ::= variable modifiers, which perform variable
# assignments during evaluation, just like the = operator in C.
@@ -91,7 +91,7 @@ mod-assign-shell-error:
@${SH_ERR::=previous}
@${SH_ERR::!= echo word; false } echo err=${SH_ERR}
-# XXX: The ::= modifier expands its right-hand side, exactly once.
+# XXX: The ::= modifier expands its right-hand side exactly once.
# This differs subtly from normal assignments such as '+=' or '=', which copy
# their right-hand side literally.
APPEND.prev= previous
@@ -104,3 +104,38 @@ APPEND.dollar= $${APPEND.indirect}
.if ${APPEND.var} != "previous indirect \${:Unot expanded}"
. error
.endif
+
+
+# The assignment modifier can be used in a variable expression that is
+# enclosed in parentheses. In such a case, parsing stops at the first ')',
+# not at the first '}'.
+VAR= previous
+_:= $(VAR::=current})
+.if ${VAR} != "current}"
+. error
+.endif
+
+
+# Before var.c 1.888 from 2021-03-15, an expression using the modifier '::='
+# expanded its variable name once too often during evaluation. This was only
+# relevant for variable names containing a '$' sign in their actual name, not
+# the usual VAR.${param}.
+.MAKEFLAGS: -dv
+param= twice
+VARNAME= VAR.$${param} # Indirect variable name because of the '$',
+ # to avoid difficult escaping rules.
+
+${VARNAME}= initial-value # Sets 'VAR.${param}' to 'expanded'.
+.if defined(VAR.twice) # At this point, the '$$' is not expanded.
+. error
+.endif
+.if ${${VARNAME}::=assigned-value} # Here the variable name gets expanded once
+. error # too often.
+.endif
+.if defined(VAR.twice)
+. error The variable name in the '::=' modifier is expanded once too often.
+.endif
+.if ${${VARNAME}} != "assigned-value"
+. error
+.endif
+.MAKEFLAGS: -d0
diff --git a/unit-tests/varmod-defined.exp b/unit-tests/varmod-defined.exp
index 15f40226f1db..2f7d4dbf4baa 100644
--- a/unit-tests/varmod-defined.exp
+++ b/unit-tests/varmod-defined.exp
@@ -1,23 +1,23 @@
-Global:8_DOLLARS = $$$$$$$$
-Global:VAR =
-Var_Parse: ${8_DOLLARS} with VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF
-Global:VAR = $$$$$$$$
-Var_Parse: ${VAR:D${8_DOLLARS}} with VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF
-Applying ${VAR:D...} to "$$$$$$$$" (VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF, none, none)
-Var_Parse: ${8_DOLLARS}} with VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF
-Result of ${VAR:D${8_DOLLARS}} is "$$$$$$$$" (VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF, none, none)
-Global:VAR = $$$$$$$$
-Var_Parse: ${VAR:@var@${8_DOLLARS}@} with VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF
-Applying ${VAR:@...} to "$$$$$$$$" (VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF, none, none)
+Global: 8_DOLLARS = $$$$$$$$
+Global: VAR =
+Var_Parse: ${8_DOLLARS} (eval-keep-dollar-and-undefined)
+Global: VAR = $$$$$$$$
+Var_Parse: ${VAR:D${8_DOLLARS}} (eval-keep-dollar-and-undefined)
+Evaluating modifier ${VAR:D...} on value "$$$$$$$$" (eval-keep-dollar-and-undefined, regular)
+Var_Parse: ${8_DOLLARS}} (eval-keep-dollar-and-undefined)
+Result of ${VAR:D${8_DOLLARS}} is "$$$$$$$$" (eval-keep-dollar-and-undefined, regular)
+Global: VAR = $$$$$$$$
+Var_Parse: ${VAR:@var@${8_DOLLARS}@} (eval-keep-dollar-and-undefined)
+Evaluating modifier ${VAR:@...} on value "$$$$$$$$" (eval-keep-dollar-and-undefined, regular)
Modifier part: "var"
Modifier part: "${8_DOLLARS}"
ModifyWords: split "$$$$$$$$" into 1 words
-Global:var = $$$$$$$$
-Var_Parse: ${8_DOLLARS} with VARE_WANTRES|VARE_KEEP_UNDEF
+Global: var = $$$$$$$$
+Var_Parse: ${8_DOLLARS} (eval-keep-undefined)
ModifyWord_Loop: in "$$$$$$$$", replace "var" with "${8_DOLLARS}" to "$$$$"
Global:delete var
-Result of ${VAR:@var@${8_DOLLARS}@} is "$$$$" (VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF, none, none)
-Global:VAR = $$$$
-Global:.MAKEFLAGS = -r -k -d v -d
-Global:.MAKEFLAGS = -r -k -d v -d 0
+Result of ${VAR:@var@${8_DOLLARS}@} is "$$$$" (eval-keep-dollar-and-undefined, regular)
+Global: VAR = $$$$
+Global: .MAKEFLAGS = -r -k -d v -d
+Global: .MAKEFLAGS = -r -k -d v -d 0
exit status 0
diff --git a/unit-tests/varmod-defined.mk b/unit-tests/varmod-defined.mk
index 59b9d79d754b..a44b9f993146 100644
--- a/unit-tests/varmod-defined.mk
+++ b/unit-tests/varmod-defined.mk
@@ -1,4 +1,4 @@
-# $NetBSD: varmod-defined.mk,v 1.9 2020/11/12 00:40:55 rillig Exp $
+# $NetBSD: varmod-defined.mk,v 1.11 2021/04/11 13:35:56 rillig Exp $
#
# Tests for the :D variable modifier, which returns the given string
# if the variable is defined. It is closely related to the :U modifier.
@@ -89,9 +89,9 @@ DEF= defined
# The :D and :U modifiers behave differently from the :@var@ modifier in
# that they preserve dollars in a ':=' assignment. This is because
-# ApplyModifier_Defined passes the eflags unmodified to Var_Parse, unlike
+# ApplyModifier_Defined passes the emode unmodified to Var_Parse, unlike
# ApplyModifier_Loop, which uses ParseModifierPart, which in turn removes
-# VARE_KEEP_DOLLAR from eflags.
+# the keepDollar flag from emode.
#
# XXX: This inconsistency is documented nowhere.
.MAKEFLAGS: -dv
diff --git a/unit-tests/varmod-edge.exp b/unit-tests/varmod-edge.exp
index c90eef2756c6..d9db72b2e2ef 100644
--- a/unit-tests/varmod-edge.exp
+++ b/unit-tests/varmod-edge.exp
@@ -1,7 +1,7 @@
make: "varmod-edge.mk" line 166: ok M-paren
make: "varmod-edge.mk" line 166: ok M-mixed
make: "varmod-edge.mk" line 166: ok M-unescape
-make: Unclosed variable specification (expecting '}') for "" (value "*)") modifier U
+make: Unclosed variable expression, expecting '}' for modifier "U*)" of variable "" with value "*)"
make: "varmod-edge.mk" line 166: ok M-nest-mix
make: "varmod-edge.mk" line 166: ok M-nest-brk
make: "varmod-edge.mk" line 166: ok M-pat-err
@@ -12,12 +12,16 @@ make: "varmod-edge.mk" line 166: ok M-128
make: "varmod-edge.mk" line 166: ok eq-ext
make: "varmod-edge.mk" line 166: ok eq-q
make: "varmod-edge.mk" line 166: ok eq-bs
-make: Unfinished modifier for INP.eq-esc ('=' missing)
+make: Unfinished modifier for "INP.eq-esc" ('=' missing)
make: "varmod-edge.mk" line 166: ok eq-esc
make: "varmod-edge.mk" line 166: ok colon
-make: "varmod-edge.mk" line 165: Unknown modifier ':'
-make: "varmod-edge.mk" line 165: Unknown modifier ':'
+make: "varmod-edge.mk" line 165: Unknown modifier ":"
+make: "varmod-edge.mk" line 165: Unknown modifier ":"
make: "varmod-edge.mk" line 166: ok colons
+make: "varmod-edge.mk" line 175: Unknown modifier "Z"
+make: "varmod-edge.mk" line 175: Malformed conditional (${:Z})
+make: Unfinished modifier for "" (',' missing)
+make: "varmod-edge.mk" line 188: Malformed conditional (${:S,})
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/unit-tests/varmod-edge.mk b/unit-tests/varmod-edge.mk
index a0b6d9342ef6..762053d281a3 100644
--- a/unit-tests/varmod-edge.mk
+++ b/unit-tests/varmod-edge.mk
@@ -1,4 +1,4 @@
-# $NetBSD: varmod-edge.mk,v 1.13 2020/10/24 08:46:08 rillig Exp $
+# $NetBSD: varmod-edge.mk,v 1.16 2021/02/23 15:56:30 rillig Exp $
#
# Tests for edge cases in variable modifiers.
#
@@ -51,7 +51,7 @@ TESTS+= M-nest-mix
INP.M-nest-mix= (parentheses)
MOD.M-nest-mix= ${INP.M-nest-mix:M${:U*)}}
EXP.M-nest-mix= (parentheses)}
-# make: Unclosed variable specification (expecting '}') for "" (value "*)") modifier U
+# make: Unclosed variable expression, expecting '}' for modifier "U*)" of variable "" with value "*)"
# In contrast to parentheses and braces, the brackets are not counted
# when the :M modifier is parsed since Makefile variables only take the
@@ -169,5 +169,27 @@ EXP.colons= # empty
. endif
.endfor
+# Even in expressions based on an unnamed variable, there may be errors.
+# XXX: The error message should mention the variable name of the expression,
+# even though that name is empty in this case.
+.if ${:Z}
+. error
+.else
+. error
+.endif
+
+# Even in expressions based on an unnamed variable, there may be errors.
+#
+# Before var.c 1.842 from 2021-02-23, the error message did not surround the
+# variable name with quotes, leading to the rather confusing "Unfinished
+# modifier for (',' missing)", having two spaces in a row.
+#
+# XXX: The error message should report the filename:lineno.
+.if ${:S,}
+. error
+.else
+. error
+.endif
+
all:
@echo ok
diff --git a/unit-tests/varmod-hash.exp b/unit-tests/varmod-hash.exp
index f16f30903539..1286b456c6c2 100644
--- a/unit-tests/varmod-hash.exp
+++ b/unit-tests/varmod-hash.exp
@@ -1,9 +1,9 @@
-make: Unknown modifier 'h'
+make: Unknown modifier "has"
26bb0f5f
12345
-make: Unknown modifier 'h'
+make: Unknown modifier "hasX"
-make: Unknown modifier 'h'
+make: Unknown modifier "hashed"
exit status 0
diff --git a/unit-tests/varmod-ifelse.exp b/unit-tests/varmod-ifelse.exp
index 17d4d8afcbeb..e42e39525f1c 100644
--- a/unit-tests/varmod-ifelse.exp
+++ b/unit-tests/varmod-ifelse.exp
@@ -1,20 +1,32 @@
-make: Bad conditional expression `variable expression == "literal"' in variable expression == "literal"?bad:bad
+make: Bad conditional expression 'variable expression == "literal"' in 'variable expression == "literal"?bad:bad'
make: "varmod-ifelse.mk" line 27: Malformed conditional (${${:Uvariable expression} == "literal":?bad:bad})
-make: Bad conditional expression ` == ""' in == ""?bad-assign:bad-assign
-make: Bad conditional expression ` == ""' in == ""?bad-cond:bad-cond
+make: Bad conditional expression ' == ""' in ' == ""?bad-assign:bad-assign'
+make: Bad conditional expression ' == ""' in ' == ""?bad-cond:bad-cond'
make: "varmod-ifelse.mk" line 44: Malformed conditional (${${UNDEF} == "":?bad-cond:bad-cond})
-make: Bad conditional expression `1 == == 2' in 1 == == 2?yes:no
+make: Bad conditional expression '1 == == 2' in '1 == == 2?yes:no'
make: "varmod-ifelse.mk" line 66: Malformed conditional (${1 == == 2:?yes:no} != "")
CondParser_Eval: "${1 == == 2:?yes:no}" != ""
CondParser_Eval: 1 == == 2
lhs = 1.000000, rhs = 0.000000, op = ==
-make: Bad conditional expression `1 == == 2' in 1 == == 2?yes:no
+make: Bad conditional expression '1 == == 2' in '1 == == 2?yes:no'
lhs = "", rhs = "", op = !=
make: "varmod-ifelse.mk" line 92: warning: Oops, the parse error should have been propagated.
CondParser_Eval: ${ ${:U\$}{VAR} == value :?ok:bad} != "ok"
CondParser_Eval: ${VAR} == value
lhs = "value", rhs = "value", op = ==
lhs = "ok", rhs = "ok", op = !=
+make: "varmod-ifelse.mk" line 153: no.
+make: "varmod-ifelse.mk" line 154: String comparison operator must be either == or !=
+make: Bad conditional expression 'string == "literal" || no >= 10' in 'string == "literal" || no >= 10?yes:no'
+make: "varmod-ifelse.mk" line 154: .
+make: Bad conditional expression 'string == "literal" && >= 10' in 'string == "literal" && >= 10?yes:no'
+make: "varmod-ifelse.mk" line 159: .
+make: Bad conditional expression 'string == "literal" || >= 10' in 'string == "literal" || >= 10?yes:no'
+make: "varmod-ifelse.mk" line 160: .
+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:
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/unit-tests/varmod-ifelse.mk b/unit-tests/varmod-ifelse.mk
index ec6acdb2ee2f..0e16032a6543 100644
--- a/unit-tests/varmod-ifelse.mk
+++ b/unit-tests/varmod-ifelse.mk
@@ -1,4 +1,4 @@
-# $NetBSD: varmod-ifelse.mk,v 1.9 2021/01/25 19:05:39 rillig Exp $
+# $NetBSD: varmod-ifelse.mk,v 1.17 2021/06/11 13:01:28 rillig Exp $
#
# Tests for the ${cond:?then:else} variable modifier, which evaluates either
# the then-expression or the else-expression, depending on the condition.
@@ -74,7 +74,7 @@ COND:= ${${UNDEF} == "":?bad-assign:bad-assign}
# conditional expression".
#
# XXX: The left-hand side is enclosed in quotes. This results in Var_Parse
-# being called without VARE_UNDEFERR being set. When ApplyModifier_IfElse
+# being called without VARE_UNDEFERR. When ApplyModifier_IfElse
# returns AMR_CLEANUP as result, Var_Parse returns varUndefined since the
# value of the variable expression is still undefined. CondParser_String is
# then supposed to do proper error handling, but since varUndefined is local
@@ -111,5 +111,61 @@ VAR= value
.endif
.MAKEFLAGS: -d0
-all:
- @:;
+# On 2021-04-19, when building external/bsd/tmux with HAVE_LLVM=yes and
+# HAVE_GCC=no, the following conditional generated this error message:
+#
+# make: Bad conditional expression 'string == "literal" && no >= 10'
+# in 'string == "literal" && no >= 10?yes:no'
+#
+# Despite the error message (which was not clearly marked with "error:"),
+# the build continued, for historical reasons, see main_Exit.
+#
+# The tricky detail here is that the condition that looks so obvious in the
+# form written in the makefile becomes tricky when it is actually evaluated.
+# This is because the condition is written in the place of the variable name
+# of the expression, and in an expression, the variable name is always
+# expanded first, before even looking at the modifiers. This happens for the
+# modifier ':?' as well, so when CondEvalExpression gets to see the
+# expression, it already looks like this:
+#
+# string == "literal" && no >= 10
+#
+# When parsing such an expression, the parser used to be strict. It first
+# evaluated the left-hand side of the operator '&&' and then started parsing
+# the right-hand side 'no >= 10'. The word 'no' is obviously a string
+# literal, not enclosed in quotes, which is ok, even on the left-hand side of
+# the comparison operator, but only because this is a condition in the
+# modifier ':?'. In an ordinary directive '.if', this would be a parse error.
+# For strings, only the comparison operators '==' and '!=' are defined,
+# therefore parsing stopped at the '>', producing the 'Bad conditional
+# expression'.
+#
+# Ideally, the conditional expression would not be expanded before parsing
+# it. This would allow to write the conditions exactly as seen below. That
+# change has a high chance of breaking _some_ existing code and would need
+# to be thoroughly tested.
+#
+# Since cond.c 1.262 from 2021-04-20, make reports a more specific error
+# message in situations like these, pointing directly to the specific problem
+# instead of just saying that the whole condition is bad.
+STRING= string
+NUMBER= no # not really a number
+.info ${${STRING} == "literal" && ${NUMBER} >= 10:?yes:no}.
+.info ${${STRING} == "literal" || ${NUMBER} >= 10:?yes:no}.
+
+# The following situation occasionally occurs with MKINET6 or similar
+# variables.
+NUMBER= # empty, not really a number either
+.info ${${STRING} == "literal" && ${NUMBER} >= 10:?yes:no}.
+.info ${${STRING} == "literal" || ${NUMBER} >= 10:?yes:no}.
+
+# CondParser_LeafToken handles [0-9-+] specially, treating them as a number.
+PLUS= +
+ASTERISK= *
+EMPTY= # empty
+# "true" since "+" is not the empty string.
+.info ${${PLUS} :?true:false}
+# "false" since the variable named "*" is not defined.
+.info ${${ASTERISK} :?true:false}
+# syntax error since the condition is completely blank.
+.info ${${EMPTY} :?true:false}
diff --git a/unit-tests/varmod-indirect.exp b/unit-tests/varmod-indirect.exp
index 860da7781979..63ed988d0c0e 100644
--- a/unit-tests/varmod-indirect.exp
+++ b/unit-tests/varmod-indirect.exp
@@ -1,59 +1,43 @@
-make: "varmod-indirect.mk" line 13: Unknown modifier '$'
-make: "varmod-indirect.mk" line 108: before
-make: "varmod-indirect.mk" line 108: after
-make: "varmod-indirect.mk" line 114: before
-make: "varmod-indirect.mk" line 114: after
-make: "varmod-indirect.mk" line 120: before
-make: "varmod-indirect.mk" line 120: after
-make: "varmod-indirect.mk" line 124: Unknown modifier 'Z'
-make: "varmod-indirect.mk" line 125: before
-make: "varmod-indirect.mk" line 125: after
-ParseReadLine (134): '_:= before ${UNDEF} after'
-Global:_ =
-Var_Parse: ${UNDEF} after with VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF
-Global:_ = before ${UNDEF} after
-ParseReadLine (137): '_:= before ${UNDEF:${:US,a,a,}} after'
-Var_Parse: ${UNDEF:${:US,a,a,}} after with VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF
-Var_Parse: ${:US,a,a,}} after with VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF
-Applying ${:U...} to "" (VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF, none, VES_UNDEF)
-Result of ${:US,a,a,} is "S,a,a," (VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF, none, VES_DEF)
+make: "varmod-indirect.mk" line 19: Unknown modifier "${"
+make: "varmod-indirect.mk" line 52: Unknown modifier "${"
+make: "varmod-indirect.mk" line 55: warning: FIXME: this expression should have resulted in a parse error rather than returning the unparsed portion of the expression.
+make: "varmod-indirect.mk" line 140: before
+make: "varmod-indirect.mk" line 140: after
+make: "varmod-indirect.mk" line 146: before
+make: "varmod-indirect.mk" line 146: after
+make: "varmod-indirect.mk" line 152: before
+make: "varmod-indirect.mk" line 152: after
+make: "varmod-indirect.mk" line 156: Unknown modifier "Z"
+make: "varmod-indirect.mk" line 157: before
+make: "varmod-indirect.mk" line 157: after
+ParseReadLine (166): '_:= before ${UNDEF} after'
+Global: _ =
+Var_Parse: ${UNDEF} after (eval-keep-dollar-and-undefined)
+Global: _ = before ${UNDEF} after
+ParseReadLine (169): '_:= before ${UNDEF:${:US,a,a,}} after'
+Var_Parse: ${UNDEF:${:US,a,a,}} after (eval-keep-dollar-and-undefined)
Indirect modifier "S,a,a," from "${:US,a,a,}"
-Applying ${UNDEF:S...} to "" (VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF, none, VES_UNDEF)
+Evaluating modifier ${UNDEF:S...} on value "" (eval-keep-dollar-and-undefined, undefined)
Modifier part: "a"
Modifier part: "a"
ModifyWords: split "" into 1 words
-Result of ${UNDEF:S,a,a,} is "" (VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF, none, VES_UNDEF)
-Var_Parse: ${:US,a,a,}} after with VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF
-Applying ${:U...} to "" (VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF, none, VES_UNDEF)
-Result of ${:US,a,a,} is "S,a,a," (VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF, none, VES_DEF)
-Global:_ = before ${UNDEF:S,a,a,} after
-ParseReadLine (147): '_:= before ${UNDEF:${:U}} after'
-Var_Parse: ${UNDEF:${:U}} after with VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF
-Var_Parse: ${:U}} after with VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF
-Applying ${:U} to "" (VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF, none, VES_UNDEF)
-Result of ${:U} is "" (VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF, none, VES_DEF)
+Result of ${UNDEF:S,a,a,} is "" (eval-keep-dollar-and-undefined, undefined)
+Global: _ = before ${UNDEF:S,a,a,} after
+ParseReadLine (179): '_:= before ${UNDEF:${:U}} after'
+Var_Parse: ${UNDEF:${:U}} after (eval-keep-dollar-and-undefined)
Indirect modifier "" from "${:U}"
-Var_Parse: ${:U}} after with VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF
-Applying ${:U} to "" (VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF, none, VES_UNDEF)
-Result of ${:U} is "" (VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF, none, VES_DEF)
-Global:_ = before ${UNDEF:} after
-ParseReadLine (152): '_:= before ${UNDEF:${:UZ}} after'
-Var_Parse: ${UNDEF:${:UZ}} after with VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF
-Var_Parse: ${:UZ}} after with VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF
-Applying ${:U...} to "" (VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF, none, VES_UNDEF)
-Result of ${:UZ} is "Z" (VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF, none, VES_DEF)
+Global: _ = before ${UNDEF:} after
+ParseReadLine (184): '_:= before ${UNDEF:${:UZ}} after'
+Var_Parse: ${UNDEF:${:UZ}} after (eval-keep-dollar-and-undefined)
Indirect modifier "Z" from "${:UZ}"
-Applying ${UNDEF:Z} to "" (VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF, none, VES_UNDEF)
-make: "varmod-indirect.mk" line 152: Unknown modifier 'Z'
-Result of ${UNDEF:Z} is error (VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF, none, VES_UNDEF)
-Var_Parse: ${:UZ}} after with VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF
-Applying ${:U...} to "" (VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF, none, VES_UNDEF)
-Result of ${:UZ} is "Z" (VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF, none, VES_DEF)
-Global:_ = before ${UNDEF:Z} after
-ParseReadLine (154): '.MAKEFLAGS: -d0'
-ParseDoDependency(.MAKEFLAGS: -d0)
-Global:.MAKEFLAGS = -r -k -d 0 -d pv -d
-Global:.MAKEFLAGS = -r -k -d 0 -d pv -d 0
+Evaluating modifier ${UNDEF:Z} on value "" (eval-keep-dollar-and-undefined, undefined)
+make: "varmod-indirect.mk" line 184: Unknown modifier "Z"
+Result of ${UNDEF:Z} is error (eval-keep-dollar-and-undefined, undefined)
+Global: _ = before ${UNDEF:Z} after
+ParseReadLine (186): '.MAKEFLAGS: -d0'
+ParseDependency(.MAKEFLAGS: -d0)
+Global: .MAKEFLAGS = -r -k -d 0 -d pv -d
+Global: .MAKEFLAGS = -r -k -d 0 -d pv -d 0
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/unit-tests/varmod-indirect.mk b/unit-tests/varmod-indirect.mk
index d130c7cae76d..fa58997cc849 100644
--- a/unit-tests/varmod-indirect.mk
+++ b/unit-tests/varmod-indirect.mk
@@ -1,15 +1,21 @@
-# $NetBSD: varmod-indirect.mk,v 1.5 2020/12/27 17:32:25 rillig Exp $
+# $NetBSD: varmod-indirect.mk,v 1.9 2021/03/15 20:00:50 rillig Exp $
#
# Tests for indirect variable modifiers, such as in ${VAR:${M_modifiers}}.
# These can be used for very basic purposes like converting a string to either
# uppercase or lowercase, as well as for fairly advanced modifiers that first
# look like line noise and are hard to decipher.
#
-# TODO: Since when are indirect modifiers supported?
+# Initial support for indirect modifiers was added in var.c 1.101 from
+# 2006-02-18. Since var.c 1.108 from 2006-05-11 it is possible to use
+# indirect modifiers for all but the very first modifier as well.
# To apply a modifier indirectly via another variable, the whole
# modifier must be put into a single variable expression.
+# The following expression generates a parse error since its indirect
+# modifier contains more than a sole variable expression.
+#
+# expect+1: Unknown modifier '$'
.if ${value:L:${:US}${:U,value,replacement,}} != "S,value,replacement,}"
. warning unexpected
.endif
@@ -28,13 +34,39 @@
.endif
-# An indirect variable that evaluates to the empty string is allowed though.
+# An indirect variable that evaluates to the empty string is allowed.
+# It is even allowed to write another modifier directly afterwards.
+# There is no practical use case for this feature though, as demonstrated
+# in the test case directly below.
+.if ${value:L:${:Dempty}S,value,replaced,} != "replaced"
+. warning unexpected
+.endif
+
+# If an expression for an indirect modifier evaluates to anything else than an
+# empty string and is neither followed by a ':' nor '}', this produces a parse
+# error. Because of this parse error, this feature cannot be used reasonably
+# in practice.
+#
+# expect+1: Unknown modifier '$'
+#.MAKEFLAGS: -dvc
+.if ${value:L:${:UM*}S,value,replaced,} == "M*S,value,replaced,}"
+. warning FIXME: this expression should have resulted in a parse $\
+ error rather than returning the unparsed portion of the $\
+ expression.
+.else
+. error
+.endif
+#.MAKEFLAGS: -d0
+
+# An indirect modifier can be followed by other modifiers, no matter if the
+# indirect modifier evaluates to an empty string or not.
+#
# This makes it possible to define conditional modifiers, like this:
#
# M.little-endian= S,1234,4321,
# M.big-endian= # none
-.if ${value:L:${:Dempty}S,a,A,} != "vAlue"
-. warning unexpected
+.if ${value:L:${:D empty }:S,value,replaced,} != "replaced"
+. error
.endif
@@ -154,4 +186,62 @@ _:= before ${UNDEF:${:UZ}} after
.MAKEFLAGS: -d0
.undef _
+
+# When evaluating indirect modifiers, these modifiers may expand to ':tW',
+# which modifies the interpretation of the expression value. This modified
+# interpretation only lasts until the end of the indirect modifier, it does
+# not influence the outer variable expression.
+.if ${1 2 3:L:tW:[#]} != 1 # direct :tW applies to the :[#]
+. error
+.endif
+.if ${1 2 3:L:${:UtW}:[#]} != 3 # indirect :tW does not apply to :[#]
+. error
+.endif
+
+
+# When evaluating indirect modifiers, these modifiers may expand to ':ts*',
+# which modifies the interpretation of the expression value. This modified
+# interpretation only lasts until the end of the indirect modifier, it does
+# not influence the outer variable expression.
+#
+# In this first expression, the direct ':ts*' has no effect since ':U' does not
+# treat the expression value as a list of words but as a single word. It has
+# to be ':U', not ':D', since the "expression name" is "1 2 3" and there is no
+# variable of that name.
+#.MAKEFLAGS: -dcpv
+.if ${1 2 3:L:ts*:Ua b c} != "a b c"
+. error
+.endif
+# In this expression, the direct ':ts*' affects the ':M' at the end.
+.if ${1 2 3:L:ts*:Ua b c:M*} != "a*b*c"
+. error
+.endif
+# In this expression, the ':ts*' is indirect, therefore the changed separator
+# only applies to the modifiers from the indirect text. It does not affect
+# the ':M' since that is not part of the text from the indirect modifier.
+#
+# Implementation detail: when ApplyModifiersIndirect calls ApplyModifiers
+# (which creates a new ModChain containing a fresh separator),
+# the outer separator character is not passed by reference to the inner
+# evaluation, therefore the scope of the inner separator ends after applying
+# the modifier ':ts*'.
+.if ${1 2 3:L:${:Uts*}:Ua b c:M*} != "a b c"
+. error
+.endif
+
+# A direct modifier ':U' turns the expression from undefined to defined.
+# An indirect modifier ':U' has the same effect, unlike the separator from
+# ':ts*' or the single-word marker from ':tW'.
+#
+# This is because when ApplyModifiersIndirect calls ApplyModifiers, it passes
+# the definedness of the outer expression by reference. If that weren't the
+# case, the first condition below would result in a parse error because its
+# left-hand side would be undefined.
+.if ${UNDEF:${:UUindirect-fallback}} != "indirect-fallback"
+. error
+.endif
+.if ${UNDEF:${:UUindirect-fallback}:Uouter-fallback} != "outer-fallback"
+. error
+.endif
+
all:
diff --git a/unit-tests/varmod-loop-varname.exp b/unit-tests/varmod-loop-varname.exp
new file mode 100644
index 000000000000..9170307bd2a0
--- /dev/null
+++ b/unit-tests/varmod-loop-varname.exp
@@ -0,0 +1,11 @@
+make: "varmod-loop-varname.mk" line 13: In the :@ modifier of "", the variable name "${:Ubar:S,b,v,}" must not contain a dollar.
+make: "varmod-loop-varname.mk" line 13: Malformed conditional (${:Uone two three:@${:Ubar:S,b,v,}@+${var}+@} != "+one+ +two+ +three+")
+make: "varmod-loop-varname.mk" line 80: In the :@ modifier of "1 2 3", the variable name "v$" must not contain a dollar.
+make: "varmod-loop-varname.mk" line 80: Malformed conditional (${1 2 3:L:@v$@($v)@} != "(1) (2) (3)")
+make: "varmod-loop-varname.mk" line 85: In the :@ modifier of "1 2 3", the variable name "v$$" must not contain a dollar.
+make: "varmod-loop-varname.mk" line 85: Malformed conditional (${1 2 3:L:@v$$@($v)@} != "() () ()")
+make: "varmod-loop-varname.mk" line 90: In the :@ modifier of "1 2 3", the variable name "v$$$" must not contain a dollar.
+make: "varmod-loop-varname.mk" line 90: Malformed conditional (${1 2 3:L:@v$$$@($v)@} != "() () ()")
+make: Fatal errors encountered -- cannot continue
+make: stopped in unit-tests
+exit status 1
diff --git a/unit-tests/varmod-loop-varname.mk b/unit-tests/varmod-loop-varname.mk
new file mode 100644
index 000000000000..d51e2ba76a42
--- /dev/null
+++ b/unit-tests/varmod-loop-varname.mk
@@ -0,0 +1,127 @@
+# $NetBSD: varmod-loop-varname.mk,v 1.2 2021/04/04 13:35:26 rillig Exp $
+#
+# Tests for the first part of the variable modifier ':@var@...@', which
+# contains the variable name to use during the loop.
+
+.MAKE.SAVE_DOLLARS= yes
+
+
+# Before 2021-04-04, the name of the loop variable could be generated
+# dynamically. There was no practical use-case for this.
+# Since var.c 1.907 from 2021-04-04, a '$' is no longer allowed in the
+# variable name.
+.if ${:Uone two three:@${:Ubar:S,b,v,}@+${var}+@} != "+one+ +two+ +three+"
+. error
+.endif
+
+
+# ":::" is a very creative variable name, unlikely to occur in practice.
+# The expression ${\:\:\:} would not work since backslashes can only
+# be escaped in the modifiers, but not in the variable name, therefore
+# the extra indirection via the modifier ':U'.
+.if ${:U1 2 3:@:::@x${${:U\:\:\:}}y@} != "x1y x2y x3y"
+. error
+.endif
+
+
+# "@@" is another creative variable name.
+.if ${:U1 2 3:@\@\@@x${@@}y@} != "x1y x2y x3y"
+. error
+.endif
+
+
+# In extreme cases, even the backslash can be used as variable name.
+# It needs to be doubled though.
+.if ${:U1 2 3:@\\@x${${:Ux:S,x,\\,}}y@} != "x1y x2y x3y"
+. error
+.endif
+
+
+# The variable name can technically be empty, and in this situation
+# the variable value cannot be accessed since the empty "variable"
+# is protected to always return an empty string.
+.if ${:U1 2 3:@@x${}y@} != "xy xy xy"
+. error
+.endif
+
+
+# The :@ modifier resolves the variables from the replacement text once more
+# than expected. In particular, it resolves _all_ variables from the scope,
+# and not only the loop variable (in this case v).
+SRCS= source
+CFLAGS.source= before
+ALL_CFLAGS:= ${SRCS:@src@${CFLAGS.${src}}@} # note the ':='
+CFLAGS.source+= after
+.if ${ALL_CFLAGS} != "before"
+. error
+.endif
+
+
+# In the following example, the modifier ':@' expands the '$$' to '$'. This
+# means that when the resulting expression is evaluated, these resulting '$'
+# will be interpreted as starting a subexpression.
+#
+# The d means direct reference, the i means indirect reference.
+RESOLVE= ${RES1} $${RES1}
+RES1= 1d${RES2} 1i$${RES2}
+RES2= 2d${RES3} 2i$${RES3}
+RES3= 3
+
+.if ${RESOLVE:@v@w${v}w@} != "w1d2d3w w2i3w w1i2d3 2i\${RES3}w w1d2d3 2i\${RES3} 1i\${RES2}w"
+. error
+.endif
+
+
+# Until 2020-07-20, the variable name of the :@ modifier could end with one
+# or two dollar signs, which were silently ignored.
+# There's no point in allowing a dollar sign in that position.
+# Since var.c 1.907 from 2021-04-04, a '$' is no longer allowed in the
+# variable name.
+.if ${1 2 3:L:@v$@($v)@} != "(1) (2) (3)"
+. error
+.else
+. error
+.endif
+.if ${1 2 3:L:@v$$@($v)@} != "() () ()"
+. error
+.else
+. error
+.endif
+.if ${1 2 3:L:@v$$$@($v)@} != "() () ()"
+. error
+.else
+. error
+.endif
+
+
+# It may happen that there are nested :@ modifiers that use the same name for
+# for the loop variable. These modifiers influence each other.
+#
+# As of 2020-10-18, the :@ modifier is implemented by actually setting a
+# variable in the scope of the expression and deleting it again after the
+# loop. This is different from the .for loops, which substitute the variable
+# expression with ${:Uvalue}, leading to different unwanted side effects.
+#
+# To make the behavior more predictable, the :@ modifier should restore the
+# loop variable to the value it had before the loop. This would result in
+# the string "1a b c1 2a b c2 3a b c3", making the two loops independent.
+.if ${:U1 2 3:@i@$i${:Ua b c:@i@$i@}${i:Uu}@} != "1a b cu 2a b cu 3a b cu"
+. error
+.endif
+
+# During the loop, the variable is actually defined and nonempty.
+# If the loop were implemented in the same way as the .for loop, the variable
+# would be neither defined nor nonempty since all expressions of the form
+# ${var} would have been replaced with ${:Uword} before evaluating them.
+.if defined(var)
+. error
+.endif
+.if ${:Uword:@var@${defined(var):?def:undef} ${empty(var):?empty:nonempty}@} \
+ != "def nonempty"
+. error
+.endif
+.if defined(var)
+. error
+.endif
+
+all: .PHONY
diff --git a/unit-tests/varmod-loop.exp b/unit-tests/varmod-loop.exp
index 66cfd6f51e16..a4704973f6e2 100644
--- a/unit-tests/varmod-loop.exp
+++ b/unit-tests/varmod-loop.exp
@@ -1,21 +1,12 @@
-ParseReadLine (117): 'USE_8_DOLLARS= ${:U1:@var@${8_DOLLARS}@} ${8_DOLLARS} $$$$$$$$'
+ParseReadLine (75): 'USE_8_DOLLARS= ${:U1:@var@${8_DOLLARS}@} ${8_DOLLARS} $$$$$$$$'
CondParser_Eval: ${USE_8_DOLLARS} != "\$\$\$\$ \$\$\$\$ \$\$\$\$"
lhs = "$$$$ $$$$ $$$$", rhs = "$$$$ $$$$ $$$$", op = !=
-ParseReadLine (122): 'SUBST_CONTAINING_LOOP:= ${USE_8_DOLLARS}'
+ParseReadLine (80): 'SUBST_CONTAINING_LOOP:= ${USE_8_DOLLARS}'
CondParser_Eval: ${SUBST_CONTAINING_LOOP} != "\$\$ \$\$\$\$ \$\$\$\$"
lhs = "$$ $$$$ $$$$", rhs = "$$ $$$$ $$$$", op = !=
-ParseReadLine (147): '.MAKEFLAGS: -d0'
-ParseDoDependency(.MAKEFLAGS: -d0)
-:+one+ +two+ +three+:
-:x1y x2y x3y:
-:x1y x2y x3y:
-:mod-loop-varname: :x1y x2y x3y: ::
-:x1y x2y x3y:
-empty: :xy xy xy:
-mod-loop-resolve:w1d2d3w w2i3w w1i2d3 2i${RES3}w w1d2d3 2i${RES3} 1i${RES2}w:
-mod-loop-varname-dollar:(1) (2) (3).
-mod-loop-varname-dollar:() () ().
-mod-loop-varname-dollar:() () ().
+ParseReadLine (105): '.MAKEFLAGS: -d0'
+ParseDependency(.MAKEFLAGS: -d0)
+:varname-overwriting-target: :x1y x2y x3y: ::
mod-loop-dollar:1:
mod-loop-dollar:${word}$:
mod-loop-dollar:$3$:
diff --git a/unit-tests/varmod-loop.mk b/unit-tests/varmod-loop.mk
index c109c775a492..4fdaa3ff4e61 100644
--- a/unit-tests/varmod-loop.mk
+++ b/unit-tests/varmod-loop.mk
@@ -1,63 +1,21 @@
-# $NetBSD: varmod-loop.mk,v 1.9 2021/02/04 21:42:47 rillig Exp $
+# $NetBSD: varmod-loop.mk,v 1.15 2021/04/11 13:35:56 rillig Exp $
#
# Tests for the :@var@...${var}...@ variable modifier.
.MAKE.SAVE_DOLLARS= yes
-all: mod-loop-varname
-all: mod-loop-resolve
-all: mod-loop-varname-dollar
+all: varname-overwriting-target
all: mod-loop-dollar
-# In the :@ modifier, the name of the loop variable can even be generated
-# dynamically. There's no practical use-case for this, and hopefully nobody
-# will ever depend on this, but technically it's possible.
-# Therefore, in -dL mode, this is forbidden, see lint.mk.
-mod-loop-varname:
- @echo :${:Uone two three:@${:Ubar:S,b,v,}@+${var}+@:Q}:
-
- # ":::" is a very creative variable name, unlikely in practice.
- # The expression ${\:\:\:} would not work since backslashes can only
- # be escaped in the modifiers, but not in the variable name.
- @echo :${:U1 2 3:@:::@x${${:U\:\:\:}}y@}:
-
- # "@@" is another creative variable name.
- @echo :${:U1 2 3:@\@\@@x${@@}y@}:
-
+varname-overwriting-target:
# Even "@" works as a variable name since the variable is installed
# in the "current" scope, which in this case is the one from the
- # target.
+ # target. Because of this, after the loop has finished, '$@' is
+ # undefined. This is something that make doesn't expect, this may
+ # even trigger an assertion failure somewhere.
@echo :$@: :${:U1 2 3:@\@@x${@}y@}: :$@:
- # In extreme cases, even the backslash can be used as variable name.
- # It needs to be doubled though.
- @echo :${:U1 2 3:@\\@x${${:Ux:S,x,\\,}}y@}:
-
- # The variable name can technically be empty, and in this situation
- # the variable value cannot be accessed since the empty variable is
- # protected to always return an empty string.
- @echo empty: :${:U1 2 3:@@x${}y@}:
-# The :@ modifier resolves the variables a little more often than expected.
-# In particular, it resolves _all_ variables from the scope, and not only
-# the loop variable (in this case v).
-#
-# The d means direct reference, the i means indirect reference.
-RESOLVE= ${RES1} $${RES1}
-RES1= 1d${RES2} 1i$${RES2}
-RES2= 2d${RES3} 2i$${RES3}
-RES3= 3
-
-mod-loop-resolve:
- @echo $@:${RESOLVE:@v@w${v}w@:Q}:
-
-# Until 2020-07-20, the variable name of the :@ modifier could end with one
-# or two dollar signs, which were silently ignored.
-# There's no point in allowing a dollar sign in that position.
-mod-loop-varname-dollar:
- @echo $@:${1 2 3:L:@v$@($v)@:Q}.
- @echo $@:${1 2 3:L:@v$$@($v)@:Q}.
- @echo $@:${1 2 3:L:@v$$$@($v)@:Q}.
# Demonstrate that it is possible to generate dollar signs using the
# :@ modifier.
@@ -109,9 +67,9 @@ mod-loop-dollar:
# This string literal is written with 8 dollars, and this is saved as the
# variable value. But as soon as this value is evaluated, it goes through
# Var_Subst, which replaces each '$$' with a single '$'. This could be
-# prevented by VARE_KEEP_DOLLAR, but that flag is usually removed before
-# expanding subexpressions. See ApplyModifier_Loop and ParseModifierPart
-# for examples.
+# prevented by VARE_EVAL_KEEP_DOLLAR, but that flag is usually removed
+# before expanding subexpressions. See ApplyModifier_Loop and
+# ParseModifierPart for examples.
#
.MAKEFLAGS: -dcp
USE_8_DOLLARS= ${:U1:@var@${8_DOLLARS}@} ${8_DOLLARS} $$$$$$$$
@@ -120,20 +78,20 @@ USE_8_DOLLARS= ${:U1:@var@${8_DOLLARS}@} ${8_DOLLARS} $$$$$$$$
.endif
#
SUBST_CONTAINING_LOOP:= ${USE_8_DOLLARS}
-# The ':=' assignment operator evaluates the variable value using the flag
-# VARE_KEEP_DOLLAR, which means that some dollar signs are preserved, but not
-# all. The dollar signs in the top-level expression and in the indirect
-# ${8_DOLLARS} are preserved.
+# The ':=' assignment operator evaluates the variable value using the mode
+# VARE_KEEP_DOLLAR_UNDEF, which means that some dollar signs are preserved,
+# but not all. The dollar signs in the top-level expression and in the
+# indirect ${8_DOLLARS} are preserved.
#
# The variable modifier :@var@ does not preserve the dollar signs though, no
# matter in which context it is evaluated. What happens in detail is:
# First, the modifier part "${8_DOLLARS}" is parsed without expanding it.
# Next, each word of the value is expanded on its own, and at this moment
-# in ApplyModifier_Loop, the VARE_KEEP_DOLLAR flag is not passed down to
+# in ApplyModifier_Loop, the flag keepDollar is not passed down to
# ModifyWords, resulting in "$$$$" for the first word of USE_8_DOLLARS.
#
# The remaining words of USE_8_DOLLARS are not affected by any variable
-# modifier and are thus expanded with the flag VARE_KEEP_DOLLAR in action.
+# modifier and are thus expanded with the flag keepDollar in action.
# The variable SUBST_CONTAINING_LOOP therefore gets assigned the raw value
# "$$$$ $$$$$$$$ $$$$$$$$".
#
@@ -145,3 +103,87 @@ SUBST_CONTAINING_LOOP:= ${USE_8_DOLLARS}
. error
.endif
.MAKEFLAGS: -d0
+
+# After looping over the words of the expression, the loop variable gets
+# undefined. The modifier ':@' uses an ordinary global variable for this,
+# which is different from the '.for' loop, which replaces ${var} with
+# ${:Uvalue} in the body of the loop. This choice of implementation detail
+# can be used for a nasty side effect. The expression ${:U:@VAR@@} evaluates
+# to an empty string, plus it undefines the variable 'VAR'. This is the only
+# possibility to undefine a global variable during evaluation.
+GLOBAL= before-global
+RESULT:= ${:U${GLOBAL} ${:U:@GLOBAL@@} ${GLOBAL:Uundefined}}
+.if ${RESULT} != "before-global undefined"
+. error
+.endif
+
+# The above side effect of undefining a variable from a certain scope can be
+# further combined with the otherwise undocumented implementation detail that
+# the argument of an '.if' directive is evaluated in cmdline scope. Putting
+# these together makes it possible to undefine variables from the cmdline
+# scope, something that is not possible in a straight-forward way.
+.MAKEFLAGS: CMDLINE=cmdline
+.if ${:U${CMDLINE}${:U:@CMDLINE@@}} != "cmdline"
+. error
+.endif
+# Now the cmdline variable got undefined.
+.if ${CMDLINE} != "cmdline"
+. error
+.endif
+# At this point, it still looks as if the cmdline variable were defined,
+# since the value of CMDLINE is still "cmdline". That impression is only
+# superficial though, the cmdline variable is actually deleted. To
+# demonstrate this, it is now possible to override its value using a global
+# variable, something that was not possible before:
+CMDLINE= global
+.if ${CMDLINE} != "global"
+. error
+.endif
+# Now undefine that global variable again, to get back to the original value.
+.undef CMDLINE
+.if ${CMDLINE} != "cmdline"
+. error
+.endif
+# What actually happened is that when CMDLINE was set by the '.MAKEFLAGS'
+# target in the cmdline scope, that same variable was exported to the
+# environment, see Var_SetWithFlags.
+.unexport CMDLINE
+.if ${CMDLINE} != "cmdline"
+. error
+.endif
+# The above '.unexport' has no effect since UnexportVar requires a global
+# variable of the same name to be defined, otherwise nothing is unexported.
+CMDLINE= global
+.unexport CMDLINE
+.undef CMDLINE
+.if ${CMDLINE} != "cmdline"
+. error
+.endif
+# This still didn't work since there must not only be a global variable, the
+# variable must be marked as exported as well, which it wasn't before.
+CMDLINE= global
+.export CMDLINE
+.unexport CMDLINE
+.undef CMDLINE
+.if ${CMDLINE:Uundefined} != "undefined"
+. error
+.endif
+# Finally the variable 'CMDLINE' from the cmdline scope is gone, and all its
+# traces from the environment are gone as well. To do that, a global variable
+# had to be defined and exported, something that is far from obvious. To
+# recap, here is the essence of the above story:
+.MAKEFLAGS: CMDLINE=cmdline # have a cmdline + environment variable
+.if ${:U:@CMDLINE@@}} # undefine cmdline, keep environment
+.endif
+CMDLINE= global # needed for deleting the environment
+.export CMDLINE # needed for deleting the environment
+.unexport CMDLINE # delete the environment
+.undef CMDLINE # delete the global helper variable
+.if ${CMDLINE:Uundefined} != "undefined"
+. error # 'CMDLINE' is gone now from all scopes
+.endif
+
+
+# TODO: Actually trigger the undefined behavior (use after free) that was
+# already suspected in Var_Parse, in the comment 'the value of the variable
+# must not change'.
diff --git a/unit-tests/varmod-match-escape.exp b/unit-tests/varmod-match-escape.exp
index 30c148075524..42cdd7a87ac9 100755
--- a/unit-tests/varmod-match-escape.exp
+++ b/unit-tests/varmod-match-escape.exp
@@ -1,60 +1,38 @@
-Global:SPECIALS = \: : \\ * \*
+Global: SPECIALS = \: : \\ * \*
CondParser_Eval: ${SPECIALS:M${:U}\:} != ${SPECIALS:M\:${:U}}
-Var_Parse: ${SPECIALS:M${:U}\:} != ${SPECIALS:M\:${:U}} with VARE_UNDEFERR|VARE_WANTRES
-Applying ${SPECIALS:M...} to "\: : \\ * \*" (VARE_UNDEFERR|VARE_WANTRES, none, none)
-Var_Parse: ${:U}\: with VARE_UNDEFERR|VARE_WANTRES
-Applying ${:U} to "" (VARE_UNDEFERR|VARE_WANTRES, none, VES_UNDEF)
-Result of ${:U} is "" (VARE_UNDEFERR|VARE_WANTRES, none, VES_DEF)
-Pattern[SPECIALS] for [\: : \\ * \*] is [\:]
+Var_Parse: ${SPECIALS:M${:U}\:} != ${SPECIALS:M\:${:U}} (eval-defined)
+Evaluating modifier ${SPECIALS:M...} on value "\: : \\ * \*"
+Pattern for ':M' is "\:"
ModifyWords: split "\: : \\ * \*" into 5 words
-VarMatch [\:] [\:]
-VarMatch [:] [\:]
-VarMatch [\\] [\:]
-VarMatch [*] [\:]
-VarMatch [\*] [\:]
-Result of ${SPECIALS:M${:U}\:} is ":" (VARE_UNDEFERR|VARE_WANTRES, none, none)
-Var_Parse: ${SPECIALS:M\:${:U}} with VARE_UNDEFERR|VARE_WANTRES
-Applying ${SPECIALS:M...} to "\: : \\ * \*" (VARE_UNDEFERR|VARE_WANTRES, none, none)
-Var_Parse: ${:U} with VARE_UNDEFERR|VARE_WANTRES
-Applying ${:U} to "" (VARE_UNDEFERR|VARE_WANTRES, none, VES_UNDEF)
-Result of ${:U} is "" (VARE_UNDEFERR|VARE_WANTRES, none, VES_DEF)
-Pattern[SPECIALS] for [\: : \\ * \*] is [:]
+Result of ${SPECIALS:M${:U}\:} is ":"
+Var_Parse: ${SPECIALS:M\:${:U}} (eval-defined)
+Evaluating modifier ${SPECIALS:M...} on value "\: : \\ * \*"
+Pattern for ':M' is ":"
ModifyWords: split "\: : \\ * \*" into 5 words
-VarMatch [\:] [:]
-VarMatch [:] [:]
-VarMatch [\\] [:]
-VarMatch [*] [:]
-VarMatch [\*] [:]
-Result of ${SPECIALS:M\:${:U}} is ":" (VARE_UNDEFERR|VARE_WANTRES, none, none)
+Result of ${SPECIALS:M\:${:U}} is ":"
lhs = ":", rhs = ":", op = !=
-Global:VALUES = : :: :\:
+Global: VALUES = : :: :\:
CondParser_Eval: ${VALUES:M\:${:U\:}} != ${VALUES:M${:U\:}\:}
-Var_Parse: ${VALUES:M\:${:U\:}} != ${VALUES:M${:U\:}\:} with VARE_UNDEFERR|VARE_WANTRES
-Applying ${VALUES:M...} to ": :: :\:" (VARE_UNDEFERR|VARE_WANTRES, none, none)
-Var_Parse: ${:U:} with VARE_UNDEFERR|VARE_WANTRES
-Applying ${:U} to "" (VARE_UNDEFERR|VARE_WANTRES, none, VES_UNDEF)
-Result of ${:U} is "" (VARE_UNDEFERR|VARE_WANTRES, none, VES_DEF)
-Pattern[VALUES] for [: :: :\:] is [:]
+Var_Parse: ${VALUES:M\:${:U\:}} != ${VALUES:M${:U\:}\:} (eval-defined)
+Evaluating modifier ${VALUES:M...} on value ": :: :\:"
+Var_Parse: ${:U:} (eval-defined)
+Evaluating modifier ${:U} on value "" (eval-defined, undefined)
+Result of ${:U} is "" (eval-defined, defined)
+Pattern for ':M' is ":"
ModifyWords: split ": :: :\:" into 3 words
-VarMatch [:] [:]
-VarMatch [::] [:]
-VarMatch [:\:] [:]
-Result of ${VALUES:M\:${:U\:}} is ":" (VARE_UNDEFERR|VARE_WANTRES, none, none)
-Var_Parse: ${VALUES:M${:U\:}\:} with VARE_UNDEFERR|VARE_WANTRES
-Applying ${VALUES:M...} to ": :: :\:" (VARE_UNDEFERR|VARE_WANTRES, none, none)
-Var_Parse: ${:U\:}\: with VARE_UNDEFERR|VARE_WANTRES
-Applying ${:U...} to "" (VARE_UNDEFERR|VARE_WANTRES, none, VES_UNDEF)
-Result of ${:U\:} is ":" (VARE_UNDEFERR|VARE_WANTRES, none, VES_DEF)
-Pattern[VALUES] for [: :: :\:] is [:\:]
+Result of ${VALUES:M\:${:U\:}} is ":"
+Var_Parse: ${VALUES:M${:U\:}\:} (eval-defined)
+Evaluating modifier ${VALUES:M...} on value ": :: :\:"
+Var_Parse: ${:U\:}\: (eval-defined)
+Evaluating modifier ${:U...} on value "" (eval-defined, undefined)
+Result of ${:U\:} is ":" (eval-defined, defined)
+Pattern for ':M' is ":\:"
ModifyWords: split ": :: :\:" into 3 words
-VarMatch [:] [:\:]
-VarMatch [::] [:\:]
-VarMatch [:\:] [:\:]
-Result of ${VALUES:M${:U\:}\:} is "::" (VARE_UNDEFERR|VARE_WANTRES, none, none)
+Result of ${VALUES:M${:U\:}\:} is "::"
lhs = ":", rhs = "::", op = !=
make: "varmod-match-escape.mk" line 42: warning: XXX: Oops
-Global:.MAKEFLAGS = -r -k -d cv -d
-Global:.MAKEFLAGS = -r -k -d cv -d 0
+Global: .MAKEFLAGS = -r -k -d cv -d
+Global: .MAKEFLAGS = -r -k -d cv -d 0
make: "varmod-match-escape.mk" line 67: Dollar followed by nothing
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
diff --git a/unit-tests/varmod-match-escape.mk b/unit-tests/varmod-match-escape.mk
index e62fbe8352b7..5ac69f964a68 100755
--- a/unit-tests/varmod-match-escape.mk
+++ b/unit-tests/varmod-match-escape.mk
@@ -1,8 +1,8 @@
-# $NetBSD: varmod-match-escape.mk,v 1.6 2021/02/01 22:36:28 rillig Exp $
+# $NetBSD: varmod-match-escape.mk,v 1.7 2021/04/03 11:08:40 rillig Exp $
#
# As of 2020-08-01, the :M and :N modifiers interpret backslashes differently,
# depending on whether there was a variable expression somewhere before the
-# first backslash or not. See ApplyModifier_Match, "copy = TRUE".
+# first backslash or not. See ApplyModifier_Match, "copy = true".
#
# Apart from the different and possibly confusing debug output, there is no
# difference in behavior. When parsing the modifier text, only \{, \} and \:
@@ -29,8 +29,8 @@ SPECIALS= \: : \\ * \*
#
# XXX: As of 2020-11-01, the modifier on the right-hand side of the
# comparison is parsed differently though. First, the variable expression
-# is parsed, resulting in ':' and needSubst=TRUE. After that, the escaped
-# ':' is seen, and this time, copy=TRUE is not executed but stays copy=FALSE.
+# is parsed, resulting in ':' and needSubst=true. After that, the escaped
+# ':' is seen, and this time, copy=true is not executed but stays copy=false.
# Therefore the escaped ':' is kept as-is, and the final pattern becomes
# ':\:'.
#
diff --git a/unit-tests/varmod-order.exp b/unit-tests/varmod-order.exp
index 99d1d6ef164c..94c3cb694886 100644
--- a/unit-tests/varmod-order.exp
+++ b/unit-tests/varmod-order.exp
@@ -1,6 +1,6 @@
-make: Bad modifier `:OX' for NUMBERS
+make: Bad modifier ":OX" for variable "NUMBERS"
make: "varmod-order.mk" line 13: Undefined variable "${NUMBERS:OX"
-make: Bad modifier `:OxXX' for NUMBERS
+make: Bad modifier ":OxXX" for variable "NUMBERS"
make: "varmod-order.mk" line 16: Undefined variable "${NUMBERS:Ox"
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
diff --git a/unit-tests/varmod-range.exp b/unit-tests/varmod-range.exp
index 3a9d4d032c3a..f4ada11ebde6 100644
--- a/unit-tests/varmod-range.exp
+++ b/unit-tests/varmod-range.exp
@@ -1,12 +1,12 @@
-make: "varmod-range.mk" line 53: Invalid number: x}Rest" != "Rest"
+make: "varmod-range.mk" line 53: Invalid number "x}Rest" != "Rest"" for ':range' modifier
make: "varmod-range.mk" line 53: Malformed conditional ("${:U:range=x}Rest" != "Rest")
-make: "varmod-range.mk" line 62: Unknown modifier 'x'
+make: "varmod-range.mk" line 62: Unknown modifier "x0"
make: "varmod-range.mk" line 62: Malformed conditional ("${:U:range=0x0}Rest" != "Rest")
-make: "varmod-range.mk" line 78: Unknown modifier 'r'
+make: "varmod-range.mk" line 78: Unknown modifier "rang"
make: "varmod-range.mk" line 78: Malformed conditional ("${a b c:L:rang}Rest" != "Rest")
-make: "varmod-range.mk" line 85: Unknown modifier 'r'
+make: "varmod-range.mk" line 85: Unknown modifier "rango"
make: "varmod-range.mk" line 85: Malformed conditional ("${a b c:L:rango}Rest" != "Rest")
-make: "varmod-range.mk" line 92: Unknown modifier 'r'
+make: "varmod-range.mk" line 92: Unknown modifier "ranger"
make: "varmod-range.mk" line 92: Malformed conditional ("${a b c:L:ranger}Rest" != "Rest")
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
diff --git a/unit-tests/varmod-remember.exp b/unit-tests/varmod-remember.exp
index 448f817d8969..39a9383953dd 100644
--- a/unit-tests/varmod-remember.exp
+++ b/unit-tests/varmod-remember.exp
@@ -1,3 +1 @@
-1 2 3 1 2 3 1 2 3
-1 2 3, SAVED=3
exit status 0
diff --git a/unit-tests/varmod-remember.mk b/unit-tests/varmod-remember.mk
index 68eb96a122c4..403811759672 100644
--- a/unit-tests/varmod-remember.mk
+++ b/unit-tests/varmod-remember.mk
@@ -1,12 +1,35 @@
-# $NetBSD: varmod-remember.mk,v 1.3 2020/08/23 15:18:43 rillig Exp $
+# $NetBSD: varmod-remember.mk,v 1.6 2021/03/14 17:27:27 rillig Exp $
#
# Tests for the :_ modifier, which saves the current variable value
# in the _ variable or another, to be used later again.
+.if ${1 2 3:L:_:@var@${_}@} != "1 2 3 1 2 3 1 2 3"
+. error
+.endif
+
# In the parameterized form, having the variable name on the right side of
# the = assignment operator is confusing. In almost all other situations
# the variable name is on the left-hand side of the = operator. Luckily
# this modifier is only rarely needed.
+.if ${1 2 3:L:@var@${var:_=SAVED:}@} != "1 2 3"
+. error
+.elif ${SAVED} != "3"
+. error
+.endif
+
+# The ':_' modifier takes a variable name as optional argument. This variable
+# name can refer to other variables, though this was rather an implementation
+# oversight than an intended feature. The variable name stops at the first
+# '}' or ')' and thus cannot use the usual form ${VARNAME} of long variable
+# names.
+#
+# Because of all these edge-casey conditions, this "feature" has been removed
+# in var.c 1.867 from 2021-03-14.
+S= INDIRECT_VARNAME
+.if ${value:L:@var@${var:_=$S}@} != "value"
+. error
+.elif defined(INDIRECT_VARNAME)
+. error
+.endif
+
all:
- @echo ${1 2 3:L:_:@var@${_}@}
- @echo ${1 2 3:L:@var@${var:_=SAVED:}@}, SAVED=${SAVED}
diff --git a/unit-tests/varmod-shell.mk b/unit-tests/varmod-shell.mk
index db82e302f2a8..c736042f80a0 100644
--- a/unit-tests/varmod-shell.mk
+++ b/unit-tests/varmod-shell.mk
@@ -1,15 +1,13 @@
-# $NetBSD: varmod-shell.mk,v 1.5 2020/11/17 20:11:02 rillig Exp $
+# $NetBSD: varmod-shell.mk,v 1.6 2021/02/14 20:16:17 rillig Exp $
#
-# Tests for the :sh variable modifier, which runs the shell command
-# given by the variable value and returns its output.
+# Tests for the ':!cmd!' variable modifier, which runs the shell command
+# given by the variable modifier and returns its output.
#
# This modifier has been added on 2000-04-29.
#
# See also:
# ApplyModifier_ShellCommand
-# TODO: Implementation
-
# The command to be run is enclosed between exclamation marks.
# The previous value of the expression is irrelevant for this modifier.
# The :!cmd! modifier turns an undefined expression into a defined one.
@@ -32,4 +30,3 @@
.endif
all:
- @:;
diff --git a/unit-tests/varmod-subst-regex.exp b/unit-tests/varmod-subst-regex.exp
index 207a97fc25e8..a09046ef764c 100644
--- a/unit-tests/varmod-subst-regex.exp
+++ b/unit-tests/varmod-subst-regex.exp
@@ -20,6 +20,27 @@ mod-regex-limits:22-ok:1 33 556
mod-regex-limits:capture:ihgfedcbaabcdefghijABCDEFGHIJa0a1a2rest
make: Regex compilation error: (details omitted)
mod-regex-errors:
-make: Unknown modifier 'Z'
+make: Unknown modifier "Z"
mod-regex-errors: xy
-exit status 0
+unmatched-subexpression.ok: one one 2 3 5 8 one3 2one 34
+make: No match for subexpression \2
+unmatched-subexpression.1: ()()
+make: No match for subexpression \2
+unmatched-subexpression.1: ()()
+make: No match for subexpression \1
+unmatched-subexpression.2: ()()
+unmatched-subexpression.3: 3
+unmatched-subexpression.5: 5
+unmatched-subexpression.8: 8
+make: No match for subexpression \2
+unmatched-subexpression.13: (3)()
+make: No match for subexpression \1
+unmatched-subexpression.21: ()(1)
+unmatched-subexpression.34: 34
+make: No match for subexpression \2
+make: No match for subexpression \2
+make: No match for subexpression \1
+make: No match for subexpression \2
+make: No match for subexpression \1
+unmatched-subexpression.all: ()() ()() ()() 3 5 8 (3)() ()(1) 34
+exit status 2
diff --git a/unit-tests/varmod-subst-regex.mk b/unit-tests/varmod-subst-regex.mk
index 91b2f0e6a2f9..197691d73aad 100644
--- a/unit-tests/varmod-subst-regex.mk
+++ b/unit-tests/varmod-subst-regex.mk
@@ -1,10 +1,14 @@
-# $NetBSD: varmod-subst-regex.mk,v 1.6 2020/12/05 18:13:44 rillig Exp $
+# $NetBSD: varmod-subst-regex.mk,v 1.7 2021/06/21 08:17:39 rillig Exp $
#
# Tests for the :C,from,to, variable modifier.
+# report unmatched subexpressions
+.MAKEFLAGS: -dL
+
all: mod-regex-compile-error
all: mod-regex-limits
all: mod-regex-errors
+all: unmatched-subexpression
# The variable expression expands to 4 words. Of these words, none matches
# the regular expression "a b" since these words don't contain any
@@ -107,3 +111,51 @@ mod-regex-errors:
# unknown modifier, the parse error is ignored in ParseModifierPart
# and the faulty variable expression expands to "".
@echo $@: ${word:L:C,.*,x${:U:Z}y,W}
+
+# In regular expressions with alternatives, not all capturing groups are
+# always set; some may be missing. Make calls these "unmatched
+# subexpressions".
+#
+# Between var.c 1.16 from 1996-12-24 until before var.c 1.933 from 2021-06-21,
+# unmatched subexpressions produced an "error message" but did not have any
+# further effect since the "error handling" didn't influence the exit status.
+#
+# Before 2021-06-21 there was no way to turn off this warning, thus the
+# combination of alternative matches and capturing groups was seldom used, if
+# at all.
+#
+# Since var.c 1.933 from 2021-06-21, the error message is only printed in lint
+# mode (-dL), but not in default mode.
+#
+# As an alternative to the change from var.c 1.933 from 2021-06-21, a possible
+# mitigation would have been to add a new modifier 'U' to the already existing
+# '1Wg' modifiers of the ':C' modifier. That modifier could have been used in
+# the modifier ':C,(a.)|(b.),\1\2,U' to treat unmatched subexpressions as
+# empty. This approach would have created a syntactical ambiguity since the
+# modifiers ':S' and ':C' are open-ended (see mod-subst-chain), that is, they
+# do not need to be followed by a ':' to separate them from the next modifier.
+# Luckily the modifier :U does not make sense after :C, therefore this case
+# does not happen in practice.
+unmatched-subexpression:
+ # In each of the following cases, if the regular expression matches at
+ # all, the subexpression \1 matches as well.
+ @echo $@.ok: ${:U1 1 2 3 5 8 13 21 34:C,1(.*),one\1,}
+
+ # In the following cases:
+ # * The subexpression \1 is only defined for 1 and 13.
+ # * The subexpression \2 is only defined for 2 and 21.
+ # * If the regular expression does not match at all, the
+ # replacement string is not analyzed, thus no error messages.
+ # In total, there are 5 error messages about unmatched subexpressions.
+ @echo $@.1: ${:U 1:C,1(.*)|2(.*),(\1)(\2),:Q} # missing \2
+ @echo $@.1: ${:U 1:C,1(.*)|2(.*),(\1)(\2),:Q} # missing \2
+ @echo $@.2: ${:U 2:C,1(.*)|2(.*),(\1)(\2),:Q} # missing \1
+ @echo $@.3: ${:U 3:C,1(.*)|2(.*),(\1)(\2),:Q}
+ @echo $@.5: ${:U 5:C,1(.*)|2(.*),(\1)(\2),:Q}
+ @echo $@.8: ${:U 8:C,1(.*)|2(.*),(\1)(\2),:Q}
+ @echo $@.13: ${:U 13:C,1(.*)|2(.*),(\1)(\2),:Q} # missing \2
+ @echo $@.21: ${:U 21:C,1(.*)|2(.*),(\1)(\2),:Q} # missing \1
+ @echo $@.34: ${:U 34:C,1(.*)|2(.*),(\1)(\2),:Q}
+
+ # And now all together: 5 error messages for 1, 1, 2, 13, 21.
+ @echo $@.all: ${:U1 1 2 3 5 8 13 21 34:C,1(.*)|2(.*),(\1)(\2),:Q}
diff --git a/unit-tests/varmod-subst.exp b/unit-tests/varmod-subst.exp
index 3122c17b1ed3..97fa2e4f1491 100644
--- a/unit-tests/varmod-subst.exp
+++ b/unit-tests/varmod-subst.exp
@@ -45,7 +45,7 @@ mod-subst-delimiter:
1 two 3 tilde
mod-subst-chain:
A B c.
-make: Unknown modifier 'i'
+make: Unknown modifier "i"
.
mod-subst-dollar:$1:
mod-subst-dollar:$2:
diff --git a/unit-tests/varmod-subst.mk b/unit-tests/varmod-subst.mk
index 3c3ee673c07a..85f41e499ab7 100644
--- a/unit-tests/varmod-subst.mk
+++ b/unit-tests/varmod-subst.mk
@@ -1,4 +1,4 @@
-# $NetBSD: varmod-subst.mk,v 1.7 2020/11/15 20:20:58 rillig Exp $
+# $NetBSD: varmod-subst.mk,v 1.8 2021/05/14 19:37:16 rillig Exp $
#
# Tests for the :S,from,to, variable modifier.
@@ -78,6 +78,14 @@ WORDS= sequences of letters
. warning The :S modifier matches a too long suffix anchored at both ends.
.endif
+.if ${WORDS:S,*,replacement,} != ${WORDS}
+. error The '*' seems to be interpreted as a wildcard of some kind.
+.endif
+
+.if ${WORDS:S,.,replacement,} != ${WORDS}
+. error The '.' seems to be interpreted as a wildcard of some kind.
+.endif
+
mod-subst:
@echo $@:
@echo :${:Ua b b c:S,a b,,:Q}:
diff --git a/unit-tests/varmod-sun-shell.exp b/unit-tests/varmod-sun-shell.exp
new file mode 100644
index 000000000000..5087bc66d943
--- /dev/null
+++ b/unit-tests/varmod-sun-shell.exp
@@ -0,0 +1,2 @@
+make: "echo word; false" returned non-zero status
+exit status 0
diff --git a/unit-tests/varmod-sun-shell.mk b/unit-tests/varmod-sun-shell.mk
new file mode 100644
index 000000000000..712b36bc7030
--- /dev/null
+++ b/unit-tests/varmod-sun-shell.mk
@@ -0,0 +1,21 @@
+# $NetBSD: varmod-sun-shell.mk,v 1.1 2021/02/14 20:16:17 rillig Exp $
+#
+# Tests for the :sh variable modifier, which runs the shell command
+# given by the variable value and returns its output.
+#
+# This modifier has been added on 1996-05-29.
+#
+# See also:
+# ApplyModifier_SunShell
+
+.if ${echo word:L:sh} != "word"
+. error
+.endif
+
+# If the command exits with non-zero, an error message is printed.
+# XXX: Processing continues as usual though.
+.if ${echo word; false:L:sh} != "word"
+. error
+.endif
+
+all:
diff --git a/unit-tests/varmod-sysv.exp b/unit-tests/varmod-sysv.exp
index 57e69a667281..59275857f98a 100644
--- a/unit-tests/varmod-sysv.exp
+++ b/unit-tests/varmod-sysv.exp
@@ -1,5 +1,150 @@
-make: Unfinished modifier for word214 ('=' missing)
+make: Unfinished modifier for "word214" ('=' missing)
make: "varmod-sysv.mk" line 214: Malformed conditional (${word214:L:from${:D=}to})
+word modifier result
+'' = ""
+suffix = "suffix"
+prefix = "prefix"
+pre-middle-suffix = "pre-middle-suffix"
+'' =NS ""
+suffix =NS "suffixNS"
+prefix =NS "prefixNS"
+pre-middle-suffix =NS "pre-middle-suffixNS"
+'' =% ""
+suffix =% "suffix%"
+prefix =% "prefix%"
+pre-middle-suffix =% "pre-middle-suffix%"
+'' =%NS ""
+suffix =%NS "suffix%NS"
+prefix =%NS "prefix%NS"
+pre-middle-suffix =%NS "pre-middle-suffix%NS"
+'' =NPre% ""
+suffix =NPre% "suffixNPre%"
+prefix =NPre% "prefixNPre%"
+pre-middle-suffix =NPre% "pre-middle-suffixNPre%"
+'' =NPre%NS ""
+suffix =NPre%NS "suffixNPre%NS"
+prefix =NPre%NS "prefixNPre%NS"
+pre-middle-suffix =NPre%NS "pre-middle-suffixNPre%NS"
+'' ffix= ""
+suffix ffix= "su"
+prefix ffix= "prefix"
+pre-middle-suffix ffix= "pre-middle-su"
+'' ffix=NS ""
+suffix ffix=NS "suNS"
+prefix ffix=NS "prefix"
+pre-middle-suffix ffix=NS "pre-middle-suNS"
+'' ffix=% ""
+suffix ffix=% "su%"
+prefix ffix=% "prefix"
+pre-middle-suffix ffix=% "pre-middle-su%"
+'' ffix=%NS ""
+suffix ffix=%NS "su%NS"
+prefix ffix=%NS "prefix"
+pre-middle-suffix ffix=%NS "pre-middle-su%NS"
+'' ffix=NPre% ""
+suffix ffix=NPre% "suNPre%"
+prefix ffix=NPre% "prefix"
+pre-middle-suffix ffix=NPre% "pre-middle-suNPre%"
+'' ffix=NPre%NS ""
+suffix ffix=NPre%NS "suNPre%NS"
+prefix ffix=NPre%NS "prefix"
+pre-middle-suffix ffix=NPre%NS "pre-middle-suNPre%NS"
+'' %= ""
+suffix %= ""
+prefix %= ""
+pre-middle-suffix %= ""
+'' %=NS ""
+suffix %=NS "NS"
+prefix %=NS "NS"
+pre-middle-suffix %=NS "NS"
+'' %=% ""
+suffix %=% "suffix"
+prefix %=% "prefix"
+pre-middle-suffix %=% "pre-middle-suffix"
+'' %=%NS ""
+suffix %=%NS "suffixNS"
+prefix %=%NS "prefixNS"
+pre-middle-suffix %=%NS "pre-middle-suffixNS"
+'' %=NPre% ""
+suffix %=NPre% "NPresuffix"
+prefix %=NPre% "NPreprefix"
+pre-middle-suffix %=NPre% "NPrepre-middle-suffix"
+'' %=NPre%NS ""
+suffix %=NPre%NS "NPresuffixNS"
+prefix %=NPre%NS "NPreprefixNS"
+pre-middle-suffix %=NPre%NS "NPrepre-middle-suffixNS"
+'' pre%= ""
+suffix pre%= "suffix"
+prefix pre%= ""
+pre-middle-suffix pre%= ""
+'' pre%=NS ""
+suffix pre%=NS "suffix"
+prefix pre%=NS "NS"
+pre-middle-suffix pre%=NS "NS"
+'' pre%=% ""
+suffix pre%=% "suffix"
+prefix pre%=% "fix"
+pre-middle-suffix pre%=% "-middle-suffix"
+'' pre%=%NS ""
+suffix pre%=%NS "suffix"
+prefix pre%=%NS "fixNS"
+pre-middle-suffix pre%=%NS "-middle-suffixNS"
+'' pre%=NPre% ""
+suffix pre%=NPre% "suffix"
+prefix pre%=NPre% "NPrefix"
+pre-middle-suffix pre%=NPre% "NPre-middle-suffix"
+'' pre%=NPre%NS ""
+suffix pre%=NPre%NS "suffix"
+prefix pre%=NPre%NS "NPrefixNS"
+pre-middle-suffix pre%=NPre%NS "NPre-middle-suffixNS"
+'' %ffix= ""
+suffix %ffix= ""
+prefix %ffix= "prefix"
+pre-middle-suffix %ffix= ""
+'' %ffix=NS ""
+suffix %ffix=NS "NS"
+prefix %ffix=NS "prefix"
+pre-middle-suffix %ffix=NS "NS"
+'' %ffix=% ""
+suffix %ffix=% "su"
+prefix %ffix=% "prefix"
+pre-middle-suffix %ffix=% "pre-middle-su"
+'' %ffix=%NS ""
+suffix %ffix=%NS "suNS"
+prefix %ffix=%NS "prefix"
+pre-middle-suffix %ffix=%NS "pre-middle-suNS"
+'' %ffix=NPre% ""
+suffix %ffix=NPre% "NPresu"
+prefix %ffix=NPre% "prefix"
+pre-middle-suffix %ffix=NPre% "NPrepre-middle-su"
+'' %ffix=NPre%NS ""
+suffix %ffix=NPre%NS "NPresuNS"
+prefix %ffix=NPre%NS "prefix"
+pre-middle-suffix %ffix=NPre%NS "NPrepre-middle-suNS"
+'' pre%ffix= ""
+suffix pre%ffix= "suffix"
+prefix pre%ffix= "prefix"
+pre-middle-suffix pre%ffix= ""
+'' pre%ffix=NS ""
+suffix pre%ffix=NS "suffix"
+prefix pre%ffix=NS "prefix"
+pre-middle-suffix pre%ffix=NS "NS"
+'' pre%ffix=% ""
+suffix pre%ffix=% "suffix"
+prefix pre%ffix=% "prefix"
+pre-middle-suffix pre%ffix=% "-middle-su"
+'' pre%ffix=%NS ""
+suffix pre%ffix=%NS "suffix"
+prefix pre%ffix=%NS "prefix"
+pre-middle-suffix pre%ffix=%NS "-middle-suNS"
+'' pre%ffix=NPre% ""
+suffix pre%ffix=NPre% "suffix"
+prefix pre%ffix=NPre% "prefix"
+pre-middle-suffix pre%ffix=NPre% "NPre-middle-su"
+'' pre%ffix=NPre%NS ""
+suffix pre%ffix=NPre%NS "suffix"
+prefix pre%ffix=NPre%NS "prefix"
+pre-middle-suffix pre%ffix=NPre%NS "NPre-middle-suNS"
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/unit-tests/varmod-sysv.mk b/unit-tests/varmod-sysv.mk
index 751736ceaf74..712c1731717b 100644
--- a/unit-tests/varmod-sysv.mk
+++ b/unit-tests/varmod-sysv.mk
@@ -1,13 +1,13 @@
-# $NetBSD: varmod-sysv.mk,v 1.12 2020/12/05 13:01:33 rillig Exp $
+# $NetBSD: varmod-sysv.mk,v 1.14 2021/04/12 16:09:57 rillig Exp $
#
-# Tests for the ${VAR:from=to} variable modifier, which replaces the suffix
+# Tests for the variable modifier ':from=to', which replaces the suffix
# "from" with "to". It can also use '%' as a wildcard.
#
# This modifier is applied when the other modifiers don't match exactly.
#
# See ApplyModifier_SysV.
-# A typical use case for the :from=to modifier is conversion of filename
+# A typical use case for the modifier ':from=to' is conversion of filename
# extensions.
.if ${src.c:L:.c=.o} != "src.o"
. error
@@ -23,43 +23,43 @@
. error
.endif
-# The :from=to modifier is therefore often combined with the :M modifier.
+# The modifier ':from=to' is therefore often combined with the modifier ':M'.
.if ${src.c src.h:L:M*.c:.c=.o} != "src.o"
. error
.endif
-# Another use case for the :from=to modifier is to append a suffix to each
+# Another use case for the modifier ':from=to' is to append a suffix to each
# word. In this case, the "from" string is empty, therefore it always
-# matches. The same effect can be achieved with the :S,$,teen, modifier.
+# matches. The same effect can be achieved with the modifier ':S,$,teen,'.
.if ${four six seven nine:L:=teen} != "fourteen sixteen seventeen nineteen"
. error
.endif
-# The :from=to modifier can also be used to surround each word by strings.
+# The modifier ':from=to' can also be used to surround each word by strings.
# It might be tempting to use this for enclosing a string in quotes for the
-# shell, but that's the job of the :Q modifier.
+# shell, but that's the job of the modifier ':Q'.
.if ${one two three:L:%=(%)} != "(one) (two) (three)"
. error
.endif
-# When the :from=to modifier is parsed, it lasts until the closing brace
-# or parenthesis. The :Q in the below expression may look like a modifier
-# but isn't. It is part of the replacement string.
+# When the modifier ':from=to' is parsed, it lasts until the closing brace
+# or parenthesis. The ':Q' in the below expression may look like a modifier
+# but it isn't. It is part of the replacement string.
.if ${a b c d e:L:%a=x:Q} != "x:Q b c d e"
. error
.endif
-# In the :from=to modifier, both parts can contain variable expressions.
+# In the modifier ':from=to', both parts can contain variable expressions.
.if ${one two:L:${:Uone}=${:U1}} != "1 two"
. error
.endif
-# In the :from=to modifier, the "from" part is expanded exactly once.
+# In the modifier ':from=to', the "from" part is expanded exactly once.
.if ${:U\$ \$\$ \$\$\$\$:${:U\$\$\$\$}=4} != "\$ \$\$ 4"
. error
.endif
-# In the :from=to modifier, the "to" part is expanded exactly twice.
+# In the modifier ':from=to', the "to" part is expanded exactly twice.
# XXX: The right-hand side should be expanded only once.
# XXX: It's hard to get the escaping correct here, and to read that.
# XXX: It's not intuitive why the closing brace must be escaped but not
@@ -75,7 +75,7 @@
.endif
# If the variable value is empty, it is debatable whether it consists of a
-# single empty word, or no word at all. The :from=to modifier treats it as
+# single empty word, or no word at all. The modifier ':from=to' treats it as
# no word at all.
#
# See SysVMatch, which doesn't handle w_len == p_len specially.
@@ -93,10 +93,10 @@
# Before 2020-07-19, an ampersand could be used in the replacement part
# of a SysV substitution modifier, and it was replaced with the whole match,
-# just like in the :S modifier.
+# just like in the modifier ':S'.
#
# This was probably a copy-and-paste mistake since the code for the SysV
-# modifier looked a lot like the code for the :S and :C modifiers.
+# modifier looked a lot like the code for the modifiers ':S' and ':C'.
# The ampersand is not mentioned in the manual page.
.if ${a.bcd.e:L:a.%=%} != "bcd.e"
. error
@@ -109,14 +109,14 @@
# Before 2020-07-20, when a SysV modifier was parsed, a single dollar
# before the '=' was parsed (but not interpreted) as an anchor.
# Parsing something without then evaluating it accordingly doesn't make
-# sense.
+# sense, so this has been fixed.
.if ${value:L:e$=x} != "value"
. error
.endif
-# Before 2020-07-20, the modifier ":e$=x" was parsed as having a left-hand
-# side "e" and a right-hand side "x". The dollar was parsed (but not
+# Before 2020-07-20, the modifier ':e$=x' was parsed as having a left-hand
+# side 'e' and a right-hand side 'x'. The dollar was parsed (but not
# interpreted) as 'anchor at the end'. Therefore the modifier was equivalent
-# to ":e=x", which doesn't match the string "value$". Therefore the whole
+# to ':e=x', which doesn't match the string "value$". Therefore the whole
# expression evaluated to "value$".
.if ${${:Uvalue\$}:L:e$=x} != "valux"
. error
@@ -198,7 +198,7 @@
. error
.endif
-# The :from=to modifier can be used to replace both the prefix and a suffix
+# The modifier ':from=to' can be used to replace both the prefix and a suffix
# of a word with other strings. This is not possible with a single :S
# modifier, and using a :C modifier for the same task looks more complicated
# in many cases.
@@ -238,4 +238,17 @@ INDIRECT= 1:${VALUE} 2:$${VALUE} 4:$$$${VALUE}
. error
.endif
+# Test all relevant combinations of prefix, '%' and suffix in both the pattern
+# and the replacement.
+!=1>&2 printf '%-24s %-24s %-24s\n' 'word' 'modifier' 'result'
+.for from in '' ffix % pre% %ffix pre%ffix
+. for to in '' NS % %NS NPre% NPre%NS
+. for word in '' suffix prefix pre-middle-suffix
+. for mod in ${from:N''}=${to:N''}
+!=1>&2 printf '%-24s %-24s "%s"\n' ''${word:Q} ''${mod:Q} ''${word:N'':${mod}:Q}
+. endfor
+. endfor
+. endfor
+.endfor
+
all:
diff --git a/unit-tests/varmod-to-separator.exp b/unit-tests/varmod-to-separator.exp
index 44c9f0973ed9..c6e8ce98a21a 100644
--- a/unit-tests/varmod-to-separator.exp
+++ b/unit-tests/varmod-to-separator.exp
@@ -2,17 +2,17 @@ make: "varmod-to-separator.mk" line 107: Invalid character number: 400:tu}
make: "varmod-to-separator.mk" line 107: Malformed conditional (${WORDS:[1..3]:ts\400:tu})
make: "varmod-to-separator.mk" line 121: Invalid character number: 100:tu}
make: "varmod-to-separator.mk" line 121: Malformed conditional (${WORDS:[1..3]:ts\x100:tu})
-make: Bad modifier `:ts\-300' for WORDS
+make: Bad modifier ":ts\-300" for variable "WORDS"
make: "varmod-to-separator.mk" line 128: Malformed conditional (${WORDS:[1..3]:ts\-300:tu})
-make: Bad modifier `:ts\8' for 1 2 3
+make: Bad modifier ":ts\8" for variable "1 2 3"
make: "varmod-to-separator.mk" line 136: Malformed conditional (${1 2 3:L:ts\8:tu})
-make: Bad modifier `:ts\100L' for 1 2 3
+make: Bad modifier ":ts\100L" for variable "1 2 3"
make: "varmod-to-separator.mk" line 143: Malformed conditional (${1 2 3:L:ts\100L})
-make: Bad modifier `:ts\x40g' for 1 2 3
+make: Bad modifier ":ts\x40g" for variable "1 2 3"
make: "varmod-to-separator.mk" line 150: Malformed conditional (${1 2 3:L:ts\x40g})
-make: Bad modifier `:tx' for WORDS
+make: Bad modifier ":tx" for variable "WORDS"
make: "varmod-to-separator.mk" line 158: Malformed conditional (${WORDS:tx} != "anything")
-make: Bad modifier `:t\X' for WORDS
+make: Bad modifier ":t\X" for variable "WORDS"
make: "varmod-to-separator.mk" line 165: Malformed conditional (${WORDS:t\X} != "anything")
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
diff --git a/unit-tests/varmod-unique.mk b/unit-tests/varmod-unique.mk
index ea4698764947..04d04a575af1 100644
--- a/unit-tests/varmod-unique.mk
+++ b/unit-tests/varmod-unique.mk
@@ -1,4 +1,4 @@
-# $NetBSD: varmod-unique.mk,v 1.4 2020/08/31 17:41:38 rillig Exp $
+# $NetBSD: varmod-unique.mk,v 1.5 2021/05/30 20:26:41 rillig Exp $
#
# Tests for the :u variable modifier, which discards adjacent duplicate
# words.
@@ -15,10 +15,18 @@
. warning The :u modifier must do nothing with an empty word list.
.endif
-.if ${:U1:u} != "1"
+.if ${:U :u} != ""
+. warning The modifier ':u' must normalize the whitespace.
+.endif
+
+.if ${:Uword:u} != "word"
. warning The :u modifier must do nothing with a single-element word list.
.endif
+.if ${:U word :u} != "word"
+. warning The modifier ':u' must normalize the whitespace.
+.endif
+
.if ${:U1 1 1 1 1 1 1 1:u} != "1"
. warning The :u modifier must merge _all_ adjacent duplicate words.
.endif
diff --git a/unit-tests/varname-dot-shell.exp b/unit-tests/varname-dot-shell.exp
index 46a1b2127c98..bfbcfc960182 100755
--- a/unit-tests/varname-dot-shell.exp
+++ b/unit-tests/varname-dot-shell.exp
@@ -1,32 +1,32 @@
ParseReadLine (10): 'ORIG_SHELL:= ${.SHELL}'
-Global:ORIG_SHELL =
-Var_Parse: ${.SHELL} with VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF
+Global: ORIG_SHELL =
+Var_Parse: ${.SHELL} (eval-keep-dollar-and-undefined)
Global:delete .SHELL (not found)
-Command:.SHELL = (details omitted)
-Global:ORIG_SHELL = (details omitted)
+Command: .SHELL = (details omitted)
+Global: ORIG_SHELL = (details omitted)
ParseReadLine (12): '.SHELL= overwritten'
-Global:.SHELL = overwritten
+Global: .SHELL = overwritten
CondParser_Eval: ${.SHELL} != ${ORIG_SHELL}
-Var_Parse: ${.SHELL} != ${ORIG_SHELL} with VARE_UNDEFERR|VARE_WANTRES
-Var_Parse: ${ORIG_SHELL} with VARE_UNDEFERR|VARE_WANTRES
+Var_Parse: ${.SHELL} != ${ORIG_SHELL} (eval-defined)
+Var_Parse: ${ORIG_SHELL} (eval-defined)
lhs = "(details omitted)", rhs = "(details omitted)", op = !=
ParseReadLine (19): '.MAKEFLAGS: .SHELL+=appended'
-ParseDoDependency(.MAKEFLAGS: .SHELL+=appended)
+ParseDependency(.MAKEFLAGS: .SHELL+=appended)
Ignoring append to .SHELL since it is read-only
CondParser_Eval: ${.SHELL} != ${ORIG_SHELL}
-Var_Parse: ${.SHELL} != ${ORIG_SHELL} with VARE_UNDEFERR|VARE_WANTRES
-Var_Parse: ${ORIG_SHELL} with VARE_UNDEFERR|VARE_WANTRES
+Var_Parse: ${.SHELL} != ${ORIG_SHELL} (eval-defined)
+Var_Parse: ${ORIG_SHELL} (eval-defined)
lhs = "(details omitted)", rhs = "(details omitted)", op = !=
ParseReadLine (27): '.undef .SHELL'
Global:delete .SHELL
ParseReadLine (28): '.SHELL= newly overwritten'
-Global:.SHELL = newly overwritten
+Global: .SHELL = newly overwritten
CondParser_Eval: ${.SHELL} != ${ORIG_SHELL}
-Var_Parse: ${.SHELL} != ${ORIG_SHELL} with VARE_UNDEFERR|VARE_WANTRES
-Var_Parse: ${ORIG_SHELL} with VARE_UNDEFERR|VARE_WANTRES
+Var_Parse: ${.SHELL} != ${ORIG_SHELL} (eval-defined)
+Var_Parse: ${ORIG_SHELL} (eval-defined)
lhs = "(details omitted)", rhs = "(details omitted)", op = !=
ParseReadLine (33): '.MAKEFLAGS: -d0'
-ParseDoDependency(.MAKEFLAGS: -d0)
-Global:.MAKEFLAGS = -r -k -d cpv -d
-Global:.MAKEFLAGS = -r -k -d cpv -d 0
+ParseDependency(.MAKEFLAGS: -d0)
+Global: .MAKEFLAGS = -r -k -d cpv -d
+Global: .MAKEFLAGS = -r -k -d cpv -d 0
exit status 0
diff --git a/unit-tests/varname-empty.exp b/unit-tests/varname-empty.exp
index 28f55368fd19..ec225c6973c8 100644
--- a/unit-tests/varname-empty.exp
+++ b/unit-tests/varname-empty.exp
@@ -1,47 +1,27 @@
-Var_Parse: ${:U} with VARE_WANTRES
-Applying ${:U} to "" (VARE_WANTRES, none, VES_UNDEF)
-Result of ${:U} is "" (VARE_WANTRES, none, VES_DEF)
-Var_Set("${:U}", "cmdline-u", ...) name expands to empty string - ignored
-Var_Set("", "cmdline-plain", ...) name expands to empty string - ignored
-Global:.CURDIR = <curdir>
-Var_Parse: ${MAKE_OBJDIR_CHECK_WRITABLE:U} with VARE_WANTRES
-Applying ${MAKE_OBJDIR_CHECK_WRITABLE:U} to "" (VARE_WANTRES, none, VES_UNDEF)
-Result of ${MAKE_OBJDIR_CHECK_WRITABLE:U} is "" (VARE_WANTRES, none, VES_DEF)
-Global:.OBJDIR = <curdir>
+Var_SetExpand: variable name "${:U}" expands to empty string, with value "cmdline-u" - ignored
+Var_SetExpand: variable name "" expands to empty string, with value "cmdline-plain" - ignored
+Global: .CURDIR = <curdir>
+Var_Parse: ${MAKE_OBJDIR_CHECK_WRITABLE} (eval)
+Global: .OBJDIR = <curdir>
Global:delete .PATH (not found)
-Global:.PATH = .
-Global:.PATH = . <curdir>
-Global:.TARGETS =
-Internal:MAKEFILE = varname-empty.mk
-Global:.MAKE.MAKEFILES = varname-empty.mk
-Global:.PARSEFILE = varname-empty.mk
+Global: .PATH = .
+Global: .PATH = . <curdir>
+Global: .TARGETS =
+Internal: MAKEFILE = varname-empty.mk
+Global: .MAKE.MAKEFILES = varname-empty.mk
+Global: .PARSEFILE = varname-empty.mk
Global:delete .INCLUDEDFROMDIR (not found)
Global:delete .INCLUDEDFROMFILE (not found)
-Var_Set("", "default", ...) name expands to empty string - ignored
-Var_Set("", "assigned", ...) name expands to empty string - ignored
+Var_SetExpand: variable name "" expands to empty string, with value "default" - ignored
+Var_SetExpand: variable name "" expands to empty string, with value "assigned" - ignored
SetVar: variable name is empty - ignored
-Var_Set("", "", ...) name expands to empty string - ignored
-Var_Set("", "subst", ...) name expands to empty string - ignored
-Var_Set("", "shell-output", ...) name expands to empty string - ignored
-Var_Parse: ${:Ufallback} != "fallback" with VARE_UNDEFERR|VARE_WANTRES
-Applying ${:U...} to "" (VARE_UNDEFERR|VARE_WANTRES, none, VES_UNDEF)
-Result of ${:Ufallback} is "fallback" (VARE_UNDEFERR|VARE_WANTRES, none, VES_DEF)
-Var_Parse: ${:U} with VARE_WANTRES
-Applying ${:U} to "" (VARE_WANTRES, none, VES_UNDEF)
-Result of ${:U} is "" (VARE_WANTRES, none, VES_DEF)
-Var_Set("${:U}", "assigned indirectly", ...) name expands to empty string - ignored
-Var_Parse: ${:Ufallback} != "fallback" with VARE_UNDEFERR|VARE_WANTRES
-Applying ${:U...} to "" (VARE_UNDEFERR|VARE_WANTRES, none, VES_UNDEF)
-Result of ${:Ufallback} is "fallback" (VARE_UNDEFERR|VARE_WANTRES, none, VES_DEF)
-Var_Parse: ${:U} with VARE_WANTRES
-Applying ${:U} to "" (VARE_WANTRES, none, VES_UNDEF)
-Result of ${:U} is "" (VARE_WANTRES, none, VES_DEF)
-Var_Append("${:U}", "appended indirectly", ...) name expands to empty string - ignored
-Var_Parse: ${:Ufallback} != "fallback" with VARE_UNDEFERR|VARE_WANTRES
-Applying ${:U...} to "" (VARE_UNDEFERR|VARE_WANTRES, none, VES_UNDEF)
-Result of ${:Ufallback} is "fallback" (VARE_UNDEFERR|VARE_WANTRES, none, VES_DEF)
-Global:.MAKEFLAGS = -r -d v -d
-Global:.MAKEFLAGS = -r -d v -d 0
+Var_SetExpand: variable name "" expands to empty string, with value "" - ignored
+Var_SetExpand: variable name "" expands to empty string, with value "subst" - ignored
+Var_SetExpand: variable name "" expands to empty string, with value "shell-output" - ignored
+Var_SetExpand: variable name "${:U}" expands to empty string, with value "assigned indirectly" - ignored
+Var_AppendExpand: variable name "${:U}" expands to empty string, with value "appended indirectly" - ignored
+Global: .MAKEFLAGS = -r -d v -d
+Global: .MAKEFLAGS = -r -d v -d 0
out: fallback
out: 1 2 3
exit status 0
diff --git a/unit-tests/varname-empty.mk b/unit-tests/varname-empty.mk
index 492f9f2618ba..f077d2ec07b4 100755
--- a/unit-tests/varname-empty.mk
+++ b/unit-tests/varname-empty.mk
@@ -1,4 +1,4 @@
-# $NetBSD: varname-empty.mk,v 1.8 2021/02/03 08:34:15 rillig Exp $
+# $NetBSD: varname-empty.mk,v 1.9 2021/04/04 10:13:09 rillig Exp $
#
# Tests for the special variable with the empty name.
#
@@ -49,7 +49,7 @@ ${:U}+= appended indirectly
.MAKEFLAGS: -d0
# Before 2020-08-22, the simple assignment operator '=' after an empty
-# variable name had an off-by-one bug in Parse_DoVar. The code that was
+# variable name had an off-by-one bug in Parse_Var. The code that was
# supposed to "skip to operator character" started its search _after_ the
# assignment operator, assuming that the variable name would be at least
# one character long. It then looked for the next occurrence of a '=', which
diff --git a/unit-tests/varname.exp b/unit-tests/varname.exp
index 84f878a9f742..942532b654d5 100644
--- a/unit-tests/varname.exp
+++ b/unit-tests/varname.exp
@@ -1,24 +1,21 @@
-Global:VAR{{{}}} = 3 braces
-Var_Parse: ${VAR{{{}}}}" != "3 braces" with VARE_WANTRES
-Global:VARNAME = VAR(((
-Var_Parse: ${VARNAME} with VARE_WANTRES
-Global:VAR((( = 3 open parentheses
-Var_Parse: ${VAR(((}}}}" != "3 open parentheses}}}" with VARE_WANTRES
-Var_Parse: ${:UVAR(((}= try1 with VARE_UNDEFERR|VARE_WANTRES
-Applying ${:U...} to "" (VARE_UNDEFERR|VARE_WANTRES, none, VES_UNDEF)
-Result of ${:UVAR(((} is "VAR(((" (VARE_UNDEFERR|VARE_WANTRES, none, VES_DEF)
-Global:.ALLTARGETS = VAR(((=)
+Global: VAR{{{}}} = 3 braces
+Var_Parse: ${VAR{{{}}}}" != "3 braces" (eval)
+Global: VARNAME = VAR(((
+Var_Parse: ${VARNAME} (eval)
+Global: VAR((( = 3 open parentheses
+Var_Parse: ${VAR(((}}}}" != "3 open parentheses}}}" (eval)
+Global: .ALLTARGETS = VAR(((=)
make: "varname.mk" line 30: No closing parenthesis in archive specification
make: "varname.mk" line 30: Error in archive specification: "VAR"
-Var_Parse: ${:UVAR\(\(\(}= try2 with VARE_UNDEFERR|VARE_WANTRES
-Applying ${:U...} to "" (VARE_UNDEFERR|VARE_WANTRES, none, VES_UNDEF)
-Result of ${:UVAR\(\(\(} is "VAR\(\(\(" (VARE_UNDEFERR|VARE_WANTRES, none, VES_DEF)
-Global:.ALLTARGETS = VAR(((=) VAR\(\(\(=
+Var_Parse: ${:UVAR\(\(\(}= try2 (eval-defined)
+Evaluating modifier ${:U...} on value "" (eval-defined, undefined)
+Result of ${:UVAR\(\(\(} is "VAR\(\(\(" (eval-defined, defined)
+Global: .ALLTARGETS = VAR(((=) VAR\(\(\(=
make: "varname.mk" line 35: Invalid line type
-Var_Parse: ${VARNAME} with VARE_WANTRES
-Global:VAR((( = try3
-Global:.MAKEFLAGS = -r -k -d v -d
-Global:.MAKEFLAGS = -r -k -d v -d 0
+Var_Parse: ${VARNAME} (eval)
+Global: VAR((( = try3
+Global: .MAKEFLAGS = -r -k -d v -d
+Global: .MAKEFLAGS = -r -k -d v -d 0
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/unit-tests/varparse-dynamic.mk b/unit-tests/varparse-dynamic.mk
index c65ba12e6149..d4d165017a7f 100644
--- a/unit-tests/varparse-dynamic.mk
+++ b/unit-tests/varparse-dynamic.mk
@@ -1,4 +1,4 @@
-# $NetBSD: varparse-dynamic.mk,v 1.4 2021/02/04 21:42:47 rillig Exp $
+# $NetBSD: varparse-dynamic.mk,v 1.5 2021/02/22 20:38:55 rillig Exp $
# Before 2020-07-27, there was an off-by-one error in Var_Parse that skipped
# the last character in the variable name.
@@ -15,8 +15,8 @@
# expression is returned as the variable value, hoping that it can be
# resolved at a later point.
#
-# This test covers the code in Var_Parse that deals with VAR_JUNK but not
-# VAR_KEEP for dynamic variables.
+# This test covers the code in Var_Parse that deals with DEF_UNDEF but not
+# DEF_DEFINED for dynamic variables.
.if ${.TARGET:S,^,,} != "\${.TARGET:S,^,,}"
. error
.endif
diff --git a/unit-tests/varparse-errors.exp b/unit-tests/varparse-errors.exp
index 50a0766c7d70..27589e0b21af 100644
--- a/unit-tests/varparse-errors.exp
+++ b/unit-tests/varparse-errors.exp
@@ -1,5 +1,5 @@
-make: "varparse-errors.mk" line 38: Unknown modifier 'Z'
-make: "varparse-errors.mk" line 46: Unknown modifier 'Z'
+make: "varparse-errors.mk" line 38: Unknown modifier "Z"
+make: "varparse-errors.mk" line 46: Unknown modifier "Z"
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/unit-tests/varparse-errors.mk b/unit-tests/varparse-errors.mk
index 113c7a292a79..f0947bb9410a 100644
--- a/unit-tests/varparse-errors.mk
+++ b/unit-tests/varparse-errors.mk
@@ -1,4 +1,4 @@
-# $NetBSD: varparse-errors.mk,v 1.3 2020/12/20 19:47:34 rillig Exp $
+# $NetBSD: varparse-errors.mk,v 1.4 2021/03/15 12:15:03 rillig Exp $
# Tests for parsing and evaluating all kinds of variable expressions.
#
@@ -25,7 +25,7 @@ ERR_BAD_MOD= An ${:Uindirect:Z} expression with an unknown modifier.
ERR_EVAL= An evaluation error ${:Uvalue:C,.,\3,}.
# In a conditional, a variable expression that is not enclosed in quotes is
-# expanded using the flags VARE_UNDEFERR and VARE_WANTRES.
+# expanded using the mode VARE_UNDEFERR.
# The variable itself must be defined.
# It may refer to undefined variables though.
.if ${REF_UNDEF} != "A reference to an undefined variable."