aboutsummaryrefslogtreecommitdiff
path: root/unit-tests
diff options
context:
space:
mode:
Diffstat (limited to 'unit-tests')
-rw-r--r--unit-tests/Makefile41
-rw-r--r--unit-tests/Makefile.config.in3
-rw-r--r--unit-tests/cmd-errors-jobs.exp10
-rw-r--r--unit-tests/cmd-errors-jobs.mk12
-rw-r--r--unit-tests/cmd-errors.exp10
-rw-r--r--unit-tests/cmd-errors.mk12
-rw-r--r--unit-tests/cond-cmp-numeric.exp6
-rw-r--r--unit-tests/cond-cmp-numeric.mk4
-rw-r--r--unit-tests/cond-cmp-string.exp8
-rwxr-xr-xunit-tests/cond-cmp-unary.mk22
-rw-r--r--unit-tests/cond-op-parentheses.exp8
-rw-r--r--unit-tests/cond-op-parentheses.mk7
-rw-r--r--unit-tests/cond-token-plain.exp8
-rw-r--r--unit-tests/cond-token-plain.mk6
-rw-r--r--unit-tests/deptgt.exp4
-rw-r--r--unit-tests/deptgt.mk4
-rw-r--r--unit-tests/dir.mk6
-rw-r--r--unit-tests/directive-for-break.exp4
-rw-r--r--unit-tests/directive-for-break.mk60
-rwxr-xr-xunit-tests/directive-for.mk18
-rw-r--r--unit-tests/export.mk4
-rw-r--r--unit-tests/hanoi-include.mk18
-rw-r--r--unit-tests/include-main.exp16
-rw-r--r--unit-tests/include-main.mk4
-rw-r--r--unit-tests/include-sub.inc (renamed from unit-tests/include-sub.mk)4
-rw-r--r--unit-tests/include-subsub.inc9
-rw-r--r--unit-tests/include-subsub.mk9
-rw-r--r--unit-tests/job-output-null.exp6
-rw-r--r--unit-tests/job-output-null.mk36
-rwxr-xr-xunit-tests/make-exported.mk4
-rw-r--r--unit-tests/opt-env.exp6
-rw-r--r--unit-tests/opt-query.exp24
-rw-r--r--unit-tests/opt-query.mk69
-rw-r--r--unit-tests/parse-var.exp6
-rw-r--r--unit-tests/parse-var.mk132
-rw-r--r--unit-tests/sh-leading-at.exp1
-rw-r--r--unit-tests/sh-leading-at.mk6
-rw-r--r--unit-tests/sh-leading-hyphen.exp10
-rw-r--r--unit-tests/sh-leading-hyphen.mk19
-rw-r--r--unit-tests/sh-leading-plus.exp2
-rw-r--r--unit-tests/sh-leading-plus.mk6
-rw-r--r--unit-tests/var-op-expand.exp8
-rw-r--r--unit-tests/var-op-expand.mk13
-rw-r--r--unit-tests/var-readonly.exp1
-rw-r--r--unit-tests/var-readonly.mk20
-rw-r--r--unit-tests/var-scope-local-legacy.exp5
-rw-r--r--unit-tests/var-scope-local-legacy.mk19
-rw-r--r--unit-tests/varmod-defined.mk14
-rw-r--r--unit-tests/varmod-ifelse.exp6
-rw-r--r--unit-tests/varmod-ifelse.mk4
-rw-r--r--unit-tests/varmod-loop.exp6
-rw-r--r--unit-tests/varmod-loop.mk47
-rw-r--r--unit-tests/varmod-match.mk12
-rw-r--r--unit-tests/varmod-order-numeric.mk8
-rw-r--r--unit-tests/varmod-to-lower.mk15
-rw-r--r--unit-tests/varmod-undefined.mk4
-rw-r--r--unit-tests/varmod.exp10
-rw-r--r--unit-tests/varmod.mk56
-rw-r--r--unit-tests/varname-dot-newline.exp3
-rw-r--r--unit-tests/varname-dot-newline.mk32
-rw-r--r--unit-tests/varparse-errors.exp14
-rw-r--r--unit-tests/varparse-errors.mk21
62 files changed, 780 insertions, 182 deletions
diff --git a/unit-tests/Makefile b/unit-tests/Makefile
index 10d8f32c61c7..f4d4425e204c 100644
--- a/unit-tests/Makefile
+++ b/unit-tests/Makefile
@@ -1,6 +1,6 @@
-# $Id: Makefile,v 1.182 2022/07/26 19:39:32 sjg Exp $
+# $Id: Makefile,v 1.191 2023/01/24 06:09:49 sjg Exp $
#
-# $NetBSD: Makefile,v 1.318 2022/06/10 21:28:50 rillig Exp $
+# $NetBSD: Makefile,v 1.331 2023/01/24 00:24:02 sjg Exp $
#
# Unit tests for make(1)
#
@@ -25,10 +25,6 @@
# named makefile (*.mk), with its own set of expected results (*.exp),
# and it should be added to the TESTS list.
#
-# A few *.mk files are helper files for other tests (such as include-sub.mk)
-# and are thus not added to TESTS. Such files must be ignored in
-# src/tests/usr.bin/make/t_make.sh.
-#
.MAIN: all
@@ -167,6 +163,7 @@ TESTS+= directive-export-impl
TESTS+= directive-export-gmake
TESTS+= directive-export-literal
TESTS+= directive-for
+TESTS+= directive-for-break
TESTS+= directive-for-empty
TESTS+= directive-for-errors
TESTS+= directive-for-escape
@@ -324,6 +321,7 @@ TESTS+= ternary
TESTS+= unexport
TESTS+= unexport-env
TESTS+= use-inference
+TESTS+= var-readonly
TESTS+= var-scope
TESTS+= var-scope-cmdline
TESTS+= var-scope-env
@@ -500,7 +498,7 @@ TESTS:= ${TESTS:${BROKEN_TESTS:S,^,N,:ts:}}
ENV.depsrc-optional+= TZ=UTC
ENV.deptgt-phony+= MAKESYSPATH=.
ENV.directive-undef= ENV_VAR=env-value
-ENV.envfirst= FROM_ENV=value-from-env
+ENV.opt-env= FROM_ENV=value-from-env
ENV.opt-m-include-dir= ${MAKEOBJDIR:DMAKEOBJDIR=${MAKEOBJDIR}}
ENV.varmisc= FROM_ENV=env
ENV.varmisc+= FROM_ENV_BEFORE=env
@@ -563,6 +561,7 @@ SED_CMDS.sh-dots+= -e 's,^\(\*\*\* Error code \)[1-9][0-9]*,\1<nonzero>,'
SED_CMDS.sh-errctl= ${STD_SED_CMDS.dj}
SED_CMDS.sh-flags= ${STD_SED_CMDS.hide-from-output}
SED_CMDS.shell-csh= ${STD_SED_CMDS.white-space}
+SED_CMDS.sh-leading-hyphen= ${STD_SED_CMDS.shell}
SED_CMDS.suff-main+= ${STD_SED_CMDS.dg1}
SED_CMDS.suff-main-several+= ${STD_SED_CMDS.dg1}
SED_CMDS.suff-transform-debug+= ${STD_SED_CMDS.dg1}
@@ -571,12 +570,13 @@ SED_CMDS.var-op-shell+= -e '/command/s,No such.*,not found,'
SED_CMDS.var-op-shell+= ${STD_SED_CMDS.white-space}
SED_CMDS.vardebug+= -e 's,${.SHELL},</path/to/shell>,'
SED_CMDS.varmod-subst-regex+= ${STD_SED_CMDS.regex}
+SED_CMDS.varparse-errors+= ${STD_SED_CMDS.timestamp}
SED_CMDS.varname-dot-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'
SED_CMDS.varname-dot-shell+= -e 's,"/[^" ]*","(details omitted)",g'
SED_CMDS.varname-dot-shell+= -e 's,\[/[^] ]*\],[(details omitted)],g'
-SED_CMDS.varname-empty= ${.OBJDIR .PARSEDIR .PATH .SHELL:L:@v@-e '/\\$v/d'@}
+SED_CMDS.varname-empty= ${.OBJDIR .PARSEDIR .PATH .SHELL .SYSPATH:L:@v@-e '/\\$v/d'@}
# Some tests need an additional round of postprocessing.
POSTPROC.depsrc-wait= sed -e '/^---/d' -e 's,^\(: Making 3[abc]\)[123]$$,\1,'
@@ -601,13 +601,14 @@ STD_SED_CMDS.dd+= -e '/^CachedDir /d'
# Omit details such as process IDs from the output of the -dg1 option.
STD_SED_CMDS.dg1= -e '/\#.* \.$$/d'
STD_SED_CMDS.dg1+= -e '/\.MAKE.PATH_FILEMON/d'
-STD_SED_CMDS.dg1+= -e '/^MAKE_VERSION/d;/^\#.*\/mk/d'
+STD_SED_CMDS.dg1+= -e '/^\#.*\/mk/d'
STD_SED_CMDS.dg1+= -e 's, ${DEFSYSPATH:U/usr/share/mk}$$, <defsyspath>,'
STD_SED_CMDS.dg1+= -e 's,^\(\.MAKE *=\) .*,\1 <details omitted>,'
STD_SED_CMDS.dg1+= -e 's,^\(\.MAKE\.[A-Z_]* *=\) .*,\1 <details omitted>,'
STD_SED_CMDS.dg1+= -e 's,^\(MACHINE[_ARCH]* *=\) .*,\1 <details omitted>,'
STD_SED_CMDS.dg1+= -e 's,^\(MAKE *=\) .*,\1 <details omitted>,'
STD_SED_CMDS.dg1+= -e 's,^\(\.SHELL *=\) .*,\1 <details omitted>,'
+STD_SED_CMDS.dg1+= -e '/\.SYSPATH/d'
STD_SED_CMDS.dg2= ${STD_SED_CMDS.dg1}
STD_SED_CMDS.dg2+= -e 's,\(last modified\) ..:..:.. ... ..\, ....,\1 <timestamp>,'
@@ -651,9 +652,11 @@ STD_SED_CMDS.hide-from-output= \
# bash 5.1.0 bash: line 1: /nonexistent: No such file or directory
# dash dash: 1: cannot open /nonexistent: No such file
#
+STD_SED_CMDS.shell+= -e 's,^${.SHELL},${.SHELL:T},'
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}: ,,'
+STD_SED_CMDS.shell+= -e 's,: command not found,: not found,'
STD_SED_CMDS.white-space= -e 's, *, ,g' -e 's, *$$,,'
@@ -662,6 +665,11 @@ STD_SED_CMDS.white-space= -e 's, *, ,g' -e 's, *$$,,'
STD_SED_CMDS.regex= \
-e 's,\(Regex compilation error:\).*,\1 (details omitted),'
+# Normalize timestamps from ':gmtime' or ':localtime' to '<timestamp>'.
+# See STD_SED_CMDS.dg2 for timestamps from the debug log.
+STD_SED_CMDS.timestamp= \
+ -e 's,[A-Z][a-z][a-z] [A-Z][a-z][a-z] [ 0-9][0-9] [0-9][0-9]:[0-9][0-9]:[0-9][0-9] [12][0-9][0-9][0-9],<timestamp>,'
+
# End of the configuration helpers section.
.-include "Makefile.inc"
@@ -721,7 +729,17 @@ TMPDIR:= /tmp/uid${.MAKE.UID}
_!= mkdir -p ${TMPDIR}
.endif
-MAKE_TEST_ENV= MALLOC_OPTIONS="JA" # for jemalloc 100
+# Some Linux systems such as Fedora have deprecated egrep in favor of grep -E.
+.if ${.MAKE.OS:NLinux} == ""
+EGREP= grep -E
+.endif
+# Keep the classical definition for all other systems. Just as the bmake code
+# is kept compatible with C90, the tests are kept compatible with systems that
+# are several decades old and don't follow modern POSIX standards.
+EGREP?= egrep
+
+MAKE_TEST_ENV= EGREP="${EGREP}"
+MAKE_TEST_ENV+= MALLOC_OPTIONS="JA" # for jemalloc 100
MAKE_TEST_ENV+= MALLOC_CONF="junk:true" # for jemalloc 510
MAKE_TEST_ENV+= TMPDIR=${TMPDIR}
@@ -769,6 +787,9 @@ _SED_CMDS+= -e 's,${.OBJDIR},<curdir>,g'
_SED_CMDS+= -e 's,${.CURDIR},<curdir>,g'
_SED_CMDS+= -e 's,<curdir>/,,g'
_SED_CMDS+= -e 's,${UNIT_TESTS:S,.,\\.,g}/,,g'
+_SED_CMDS+= -e '/MAKE_VERSION/d'
+_SED_CMDS+= -e '/EGREP=/d'
+
# on AT&T derived systems: false exits 255 not 1
.if ${.MAKE.OS:N*BSD} != ""
_SED_CMDS+= -e 's,\(Error code\) 255,\1 1,'
diff --git a/unit-tests/Makefile.config.in b/unit-tests/Makefile.config.in
index 3139a0d4d0b5..30049eaa7c26 100644
--- a/unit-tests/Makefile.config.in
+++ b/unit-tests/Makefile.config.in
@@ -1,6 +1,7 @@
-# $Id: Makefile.config.in,v 1.3 2021/10/22 07:48:57 sjg Exp $
+# $Id: Makefile.config.in,v 1.4 2022/09/09 18:44:56 sjg Exp $
srcdir= @srcdir@
+EGREP= @egrep@
TOOL_DIFF?= @diff@
DIFF_FLAGS?= @diff_u@
UTC_1= @UTC_1@
diff --git a/unit-tests/cmd-errors-jobs.exp b/unit-tests/cmd-errors-jobs.exp
index 9ed0557975b3..c6baacfe0ed7 100644
--- a/unit-tests/cmd-errors-jobs.exp
+++ b/unit-tests/cmd-errors-jobs.exp
@@ -1,9 +1,9 @@
-: undefined eol
+: undefined--eol
make: Unclosed variable "UNCLOSED"
-: unclosed-variable
+: unclosed-variable-
make: Unclosed variable expression (expecting '}') for "UNCLOSED"
-: unclosed-modifier
+: unclosed-modifier-
make: Unknown modifier "Z"
-: unknown-modifier eol
-: end eol
+: unknown-modifier--eol
+: end-eol
exit status 0
diff --git a/unit-tests/cmd-errors-jobs.mk b/unit-tests/cmd-errors-jobs.mk
index 8462a2e3497e..b3dc982de35f 100644
--- a/unit-tests/cmd-errors-jobs.mk
+++ b/unit-tests/cmd-errors-jobs.mk
@@ -1,4 +1,4 @@
-# $NetBSD: cmd-errors-jobs.mk,v 1.1 2020/12/27 05:11:40 rillig Exp $
+# $NetBSD: cmd-errors-jobs.mk,v 1.2 2022/09/25 12:51:37 rillig Exp $
#
# Demonstrate how errors in variable expansions affect whether the commands
# are actually executed in jobs mode.
@@ -9,24 +9,24 @@ all: undefined unclosed-variable unclosed-modifier unknown-modifier end
# Undefined variables are not an error. They expand to empty strings.
undefined:
- : $@ ${UNDEFINED} eol
+ : $@-${UNDEFINED}-eol
# XXX: As of 2020-11-01, this command is executed even though it contains
# parse errors.
unclosed-variable:
- : $@ ${UNCLOSED
+ : $@-${UNCLOSED
# XXX: As of 2020-11-01, this command is executed even though it contains
# parse errors.
unclosed-modifier:
- : $@ ${UNCLOSED:
+ : $@-${UNCLOSED:
# XXX: As of 2020-11-01, this command is executed even though it contains
# parse errors.
unknown-modifier:
- : $@ ${UNKNOWN:Z} eol
+ : $@-${UNKNOWN:Z}-eol
end:
- : $@ eol
+ : $@-eol
# XXX: As of 2020-11-02, despite the parse errors, the exit status is 0.
diff --git a/unit-tests/cmd-errors.exp b/unit-tests/cmd-errors.exp
index 9ed0557975b3..c6baacfe0ed7 100644
--- a/unit-tests/cmd-errors.exp
+++ b/unit-tests/cmd-errors.exp
@@ -1,9 +1,9 @@
-: undefined eol
+: undefined--eol
make: Unclosed variable "UNCLOSED"
-: unclosed-variable
+: unclosed-variable-
make: Unclosed variable expression (expecting '}') for "UNCLOSED"
-: unclosed-modifier
+: unclosed-modifier-
make: Unknown modifier "Z"
-: unknown-modifier eol
-: end eol
+: unknown-modifier--eol
+: end-eol
exit status 0
diff --git a/unit-tests/cmd-errors.mk b/unit-tests/cmd-errors.mk
index 356fe1a3e4a2..6d3880684bcf 100644
--- a/unit-tests/cmd-errors.mk
+++ b/unit-tests/cmd-errors.mk
@@ -1,4 +1,4 @@
-# $NetBSD: cmd-errors.mk,v 1.4 2020/12/27 05:11:40 rillig Exp $
+# $NetBSD: cmd-errors.mk,v 1.5 2022/09/25 12:51:37 rillig Exp $
#
# Demonstrate how errors in variable expansions affect whether the commands
# are actually executed in compat mode.
@@ -7,24 +7,24 @@ all: undefined unclosed-variable unclosed-modifier unknown-modifier end
# Undefined variables are not an error. They expand to empty strings.
undefined:
- : $@ ${UNDEFINED} eol
+ : $@-${UNDEFINED}-eol
# XXX: As of 2020-11-01, this command is executed even though it contains
# parse errors.
unclosed-variable:
- : $@ ${UNCLOSED
+ : $@-${UNCLOSED
# XXX: As of 2020-11-01, this command is executed even though it contains
# parse errors.
unclosed-modifier:
- : $@ ${UNCLOSED:
+ : $@-${UNCLOSED:
# XXX: As of 2020-11-01, this command is executed even though it contains
# parse errors.
unknown-modifier:
- : $@ ${UNKNOWN:Z} eol
+ : $@-${UNKNOWN:Z}-eol
end:
- : $@ eol
+ : $@-eol
# XXX: As of 2020-11-02, despite the parse errors, the exit status is 0.
diff --git a/unit-tests/cond-cmp-numeric.exp b/unit-tests/cond-cmp-numeric.exp
index d913a44ff889..c03ecd1b311e 100644
--- a/unit-tests/cond-cmp-numeric.exp
+++ b/unit-tests/cond-cmp-numeric.exp
@@ -1,7 +1,7 @@
CondParser_Eval: !(${:UINF} > 1e100)
-make: "cond-cmp-numeric.mk" line 11: String comparison operator must be either == or !=
+make: "cond-cmp-numeric.mk" line 11: Comparison with '>' requires both operands 'INF' and '1e100' to be numeric
CondParser_Eval: ${:UNaN} > NaN
-make: "cond-cmp-numeric.mk" line 16: String comparison operator must be either == or !=
+make: "cond-cmp-numeric.mk" line 16: Comparison with '>' requires both operands 'NaN' and 'NaN' to be numeric
CondParser_Eval: !(${:UNaN} == NaN)
Comparing "NaN" == "NaN"
CondParser_Eval: 123 ! 123
@@ -9,7 +9,7 @@ make: "cond-cmp-numeric.mk" line 34: Malformed conditional (123 ! 123)
CondParser_Eval: ${:U 123} < 124
Comparing 123.000000 < 124.000000
CondParser_Eval: ${:U123 } < 124
-make: "cond-cmp-numeric.mk" line 50: String comparison operator must be either == or !=
+make: "cond-cmp-numeric.mk" line 50: Comparison with '<' requires both operands '123 ' and '124' to be numeric
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/unit-tests/cond-cmp-numeric.mk b/unit-tests/cond-cmp-numeric.mk
index b34e5bfc0a06..5386e4a97297 100644
--- a/unit-tests/cond-cmp-numeric.mk
+++ b/unit-tests/cond-cmp-numeric.mk
@@ -1,4 +1,4 @@
-# $NetBSD: cond-cmp-numeric.mk,v 1.5 2021/07/29 06:31:18 rillig Exp $
+# $NetBSD: cond-cmp-numeric.mk,v 1.6 2022/09/04 22:55:00 rillig Exp $
#
# Tests for numeric comparisons in .if conditions.
@@ -46,7 +46,7 @@
# Trailing spaces are NOT allowed for numbers.
# See EvalCompare and TryParseNumber.
-# expect+1: String comparison operator must be either == or !=
+# expect+1: Comparison with '<' requires both operands '123 ' and '124' to be numeric
.if ${:U123 } < 124
. error
.else
diff --git a/unit-tests/cond-cmp-string.exp b/unit-tests/cond-cmp-string.exp
index a10341ed2121..c9c7a0777383 100644
--- a/unit-tests/cond-cmp-string.exp
+++ b/unit-tests/cond-cmp-string.exp
@@ -2,10 +2,10 @@ make: "cond-cmp-string.mk" line 18: Malformed conditional (str != str)
make: "cond-cmp-string.mk" line 42: Malformed conditional ("string" != "str""ing")
make: "cond-cmp-string.mk" line 49: Malformed conditional (!("value" = "value"))
make: "cond-cmp-string.mk" line 56: Malformed conditional (!("value" === "value"))
-make: "cond-cmp-string.mk" line 113: String comparison operator must be either == or !=
-make: "cond-cmp-string.mk" line 120: String comparison operator must be either == or !=
-make: "cond-cmp-string.mk" line 127: String comparison operator must be either == or !=
-make: "cond-cmp-string.mk" line 134: String comparison operator must be either == or !=
+make: "cond-cmp-string.mk" line 113: Comparison with '<' requires both operands 'string' and 'string' to be numeric
+make: "cond-cmp-string.mk" line 120: Comparison with '<=' requires both operands 'string' and 'string' to be numeric
+make: "cond-cmp-string.mk" line 127: Comparison with '>' requires both operands 'string' and 'string' to be numeric
+make: "cond-cmp-string.mk" line 134: Comparison with '>=' requires both operands 'string' and 'string' to be numeric
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/unit-tests/cond-cmp-unary.mk b/unit-tests/cond-cmp-unary.mk
index 168de0f30e3f..90cd04e5092b 100755
--- a/unit-tests/cond-cmp-unary.mk
+++ b/unit-tests/cond-cmp-unary.mk
@@ -1,11 +1,11 @@
-# $NetBSD: cond-cmp-unary.mk,v 1.2 2020/11/11 07:30:11 rillig Exp $
+# $NetBSD: cond-cmp-unary.mk,v 1.3 2022/09/08 05:43:20 rillig Exp $
#
# Tests for unary comparisons in .if conditions, that is, comparisons with
# a single operand. If the operand is a number, it is compared to zero,
# if it is a string, it is tested for emptiness.
-# The number 0 evaluates to false.
-.if 0
+# The number 0 in all its various representations evaluates to false.
+.if 0 || 0.0 || 0e0 || 0.0e0 || 0.0e10
. error
.endif
@@ -55,4 +55,20 @@
. error
.endif
+# The condition '${VAR:M*}' is almost equivalent to '${VAR:M*} != ""'. The
+# only case where they differ is for a single word whose numeric value is zero.
+.if ${:U0:M*}
+. error
+.endif
+.if ${:U0:M*} == ""
+. error
+.endif
+# Multiple words cannot be parsed as a single number, thus evaluating to true.
+.if !${:U0 0:M*}
+. error
+.endif
+.if ${:U0 0:M*} == ""
+. error
+.endif
+
all: # nothing
diff --git a/unit-tests/cond-op-parentheses.exp b/unit-tests/cond-op-parentheses.exp
index 63f7b19570b5..c2530f62de64 100644
--- a/unit-tests/cond-op-parentheses.exp
+++ b/unit-tests/cond-op-parentheses.exp
@@ -1,7 +1,7 @@
-make: "cond-op-parentheses.mk" line 19: String comparison operator must be either == or !=
-make: "cond-op-parentheses.mk" line 22: Malformed conditional ((3) > 2)
-make: "cond-op-parentheses.mk" line 40: Malformed conditional (()
-make: "cond-op-parentheses.mk" line 53: Malformed conditional ())
+make: "cond-op-parentheses.mk" line 22: Comparison with '>' requires both operands '3' and '(2' to be numeric
+make: "cond-op-parentheses.mk" line 25: Malformed conditional ((3) > 2)
+make: "cond-op-parentheses.mk" line 43: Malformed conditional (()
+make: "cond-op-parentheses.mk" line 56: Malformed conditional ())
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/unit-tests/cond-op-parentheses.mk b/unit-tests/cond-op-parentheses.mk
index b790f8bec330..8f8c3ee1e791 100644
--- a/unit-tests/cond-op-parentheses.mk
+++ b/unit-tests/cond-op-parentheses.mk
@@ -1,4 +1,4 @@
-# $NetBSD: cond-op-parentheses.mk,v 1.5 2022/01/22 21:50:41 rillig Exp $
+# $NetBSD: cond-op-parentheses.mk,v 1.6 2022/09/04 22:55:00 rillig Exp $
#
# Tests for parentheses in .if conditions, which group expressions to override
# the precedence of the operators '!', '&&' and '||'. Parentheses cannot be
@@ -15,7 +15,10 @@
# Parentheses cannot enclose numbers as there is no need for it. Make does
# not implement any arithmetic functions in its condition parser. If
# absolutely necessary, use expr(1).
-# expect+1: String comparison operator must be either == or !=
+#
+# XXX: It's inconsistent that the right operand has unbalanced parentheses.
+#
+# expect+1: Comparison with '>' requires both operands '3' and '(2' to be numeric
.if 3 > (2)
.endif
# expect+1: Malformed conditional ((3) > 2)
diff --git a/unit-tests/cond-token-plain.exp b/unit-tests/cond-token-plain.exp
index 90da7644bd9e..a508bf62a5ea 100644
--- a/unit-tests/cond-token-plain.exp
+++ b/unit-tests/cond-token-plain.exp
@@ -9,11 +9,11 @@ Comparing "\" != "\"
CondParser_Eval: ${:U#hash} != #hash
Comparing "#hash" != "#hash"
CondParser_Eval: 0 # This is treated as a comment, but why?
-CondParser_Eval: ${0 # comment :?yes:no} != no
-CondParser_Eval: 0 # comment
+CondParser_Eval: ${0 # comment:?yes:no} != no
+CondParser_Eval: 0 # comment
Comparing "no" != "no"
-CondParser_Eval: ${1 # comment :?yes:no} != yes
-CondParser_Eval: 1 # comment
+CondParser_Eval: ${1 # comment:?yes:no} != yes
+CondParser_Eval: 1 # comment
Comparing "yes" != "yes"
CondParser_Eval: ${UNDEF:Uundefined}!=undefined
Comparing "undefined" != "undefined"
diff --git a/unit-tests/cond-token-plain.mk b/unit-tests/cond-token-plain.mk
index 1e9f30be9153..5fb4a72b74a5 100644
--- a/unit-tests/cond-token-plain.mk
+++ b/unit-tests/cond-token-plain.mk
@@ -1,4 +1,4 @@
-# $NetBSD: cond-token-plain.mk,v 1.15 2021/12/30 02:14:55 rillig Exp $
+# $NetBSD: cond-token-plain.mk,v 1.16 2022/09/25 12:51:37 rillig Exp $
#
# Tests for plain tokens (that is, string literals without quotes)
# in .if conditions. These are also called bare words.
@@ -63,10 +63,10 @@
# anybody really use this? This is neither documented nor obvious since
# the '#' is escaped. It's much clearer to write a comment in the line
# above the condition.
-.if ${0 \# comment :?yes:no} != no
+.if ${0 \# comment:?yes:no} != no
. error
.endif
-.if ${1 \# comment :?yes:no} != yes
+.if ${1 \# comment:?yes:no} != yes
. error
.endif
diff --git a/unit-tests/deptgt.exp b/unit-tests/deptgt.exp
index 0a27f562293d..73d4a7f0c33f 100644
--- a/unit-tests/deptgt.exp
+++ b/unit-tests/deptgt.exp
@@ -1,4 +1,4 @@
-make: "deptgt.mk" line 10: warning: Extra target ignored
+make: "deptgt.mk" line 10: warning: Extra target '.PHONY' ignored
make: "deptgt.mk" line 28: Unassociated shell command ": command3 # parse error, since targets == NULL"
Parsing line 34: ${:U}: empty-source
ParseDependency(: empty-source)
@@ -9,7 +9,7 @@ Parsing line 37: : command for empty targets list
Parsing line 38: .MAKEFLAGS: -d0
ParseDependency(.MAKEFLAGS: -d0)
make: "deptgt.mk" line 46: Unknown modifier "Z"
-make: "deptgt.mk" line 49: warning: Extra target ignored
+make: "deptgt.mk" line 49: warning: Extra target 'ordinary' ignored
make: "deptgt.mk" line 52: warning: Extra target (ordinary) ignored
make: "deptgt.mk" line 55: warning: Special and mundane targets don't mix. Mundane ones ignored
make: Fatal errors encountered -- cannot continue
diff --git a/unit-tests/deptgt.mk b/unit-tests/deptgt.mk
index 044644dcbd66..7d43220e888a 100644
--- a/unit-tests/deptgt.mk
+++ b/unit-tests/deptgt.mk
@@ -1,4 +1,4 @@
-# $NetBSD: deptgt.mk,v 1.12 2021/12/13 23:38:54 rillig Exp $
+# $NetBSD: deptgt.mk,v 1.13 2023/01/03 00:00:45 rillig Exp $
#
# Tests for special targets like .BEGIN or .SUFFIXES in dependency
# declarations.
@@ -45,7 +45,7 @@ ${:U}: empty-source
# that nobody uses it.
$$$$$$$${:U:Z}:
-# expect+1: warning: Extra target ignored
+# expect+1: warning: Extra target 'ordinary' ignored
.END ordinary:
# expect+1: warning: Extra target (ordinary) ignored
diff --git a/unit-tests/dir.mk b/unit-tests/dir.mk
index 36fe2baf978c..d3c75c8cb11d 100644
--- a/unit-tests/dir.mk
+++ b/unit-tests/dir.mk
@@ -1,8 +1,10 @@
-# $NetBSD: dir.mk,v 1.9 2021/01/23 10:48:49 rillig Exp $
+# $NetBSD: dir.mk,v 1.10 2023/01/24 00:24:02 sjg Exp $
#
# Tests for dir.c.
-.MAKEFLAGS: -m / # hide /usr/share/mk from the debug log
+# hide /usr/share/mk from the debug log
+.SYSPATH:
+.SYSPATH: /
# Dependency lines may use braces for expansion.
# See DirExpandCurly for the implementation.
diff --git a/unit-tests/directive-for-break.exp b/unit-tests/directive-for-break.exp
new file mode 100644
index 000000000000..b036ebfeb661
--- /dev/null
+++ b/unit-tests/directive-for-break.exp
@@ -0,0 +1,4 @@
+make: "directive-for-break.mk" line 45: break outside of for loop
+make: Fatal errors encountered -- cannot continue
+make: stopped in unit-tests
+exit status 1
diff --git a/unit-tests/directive-for-break.mk b/unit-tests/directive-for-break.mk
new file mode 100644
index 000000000000..a86acfa8bde0
--- /dev/null
+++ b/unit-tests/directive-for-break.mk
@@ -0,0 +1,60 @@
+# $NetBSD: directive-for-break.mk,v 1.3 2022/09/24 10:52:05 rillig Exp $
+#
+# Tests for .break in .for loops, which immediately terminates processing of
+# the surrounding .for loop.
+
+
+# .break terminates the loop early.
+# This is usually done within a conditional.
+.for i in 1 2 3 4 5 6 7 8
+. if $i == 3
+I= $i
+. break
+I= unreached
+. endif
+.endfor
+.if $I != "3"
+. error
+.endif
+
+
+# The .break only breaks out of the immediately surrounding .for loop, any
+# other .for loops are continued normally.
+.for outer in o1 o2 o3
+. for inner in i1 i2 i3
+. if ${outer} == o2 && ${inner} == i2
+. break
+. endif
+COMBINED+= ${outer}-${inner}
+. endfor
+.endfor
+# Only o2-i2 and o2-i3 are missing.
+.if ${COMBINED} != "o1-i1 o1-i2 o1-i3 o2-i1 o3-i1 o3-i2 o3-i3"
+. error
+.endif
+
+
+# A .break outside the context of a .for loop is an error.
+.if $I == 0
+# No parse error, even though the .break occurs outside a .for loop, since
+# lines from inactive branches are only parsed as far as necessary to see
+# whether they belong to an .if/.elif/.else/.endif chain.
+. break
+.else
+# expect+1: break outside of for loop
+. break
+.endif
+
+
+# Since cond.c 1.335 from 2022-09-02 and before cond.c 1.338 from 2022-09-23,
+# the following paragraph generated the wrong error message '4294967294 open
+# conditionals'.
+.if 1
+. if 2
+. for var in value
+. if 3
+. break
+. endif
+. endfor
+. endif
+.endif
diff --git a/unit-tests/directive-for.mk b/unit-tests/directive-for.mk
index 572c4d6a5c92..95171c68031f 100755
--- a/unit-tests/directive-for.mk
+++ b/unit-tests/directive-for.mk
@@ -1,4 +1,4 @@
-# $NetBSD: directive-for.mk,v 1.13 2022/01/15 12:35:18 rillig Exp $
+# $NetBSD: directive-for.mk,v 1.15 2022/10/01 09:23:04 rillig Exp $
#
# Tests for the .for directive.
#
@@ -228,3 +228,19 @@ var= outer
endfor
.endfor
.MAKEFLAGS: -d0
+
+
+# When there is a variable definition 'scope=cmdline' from the command line
+# (which has higher precedence than global variables) and a .for loop iterates
+# over a variable of the same name, the expression '${scope}' expands to the
+# value from the .for loop. This is because when the body of the .for loop is
+# expanded, the expression '${scope}' is textually replaced with ${:Uloop}',
+# without resolving any other variable names (ForLoop_SubstBody). Later, when
+# the body of the .for loop is actually interpreted, the body text doesn't
+# contain the word 'scope' anymore.
+.MAKEFLAGS: scope=cmdline
+.for scope in loop
+. if ${scope} != "loop"
+. error
+. endif
+.endfor
diff --git a/unit-tests/export.mk b/unit-tests/export.mk
index bab08ee3ea23..38670eaaaf48 100644
--- a/unit-tests/export.mk
+++ b/unit-tests/export.mk
@@ -1,4 +1,4 @@
-# $NetBSD: export.mk,v 1.11 2021/12/05 14:57:36 rillig Exp $
+# $NetBSD: export.mk,v 1.12 2022/09/09 18:36:15 sjg Exp $
UT_TEST= export
UT_FOO= foo${BAR}
@@ -40,7 +40,7 @@ BAR= bar is ${UT_FU}
.MAKE.EXPORTED+= UT_ZOO UT_TEST
-FILTER_CMD?= egrep -v '^(MAKEFLAGS|MALLOC_.*|PATH|PWD|SHLVL|_|&)='
+FILTER_CMD?= ${EGREP} -v '^(MAKEFLAGS|MALLOC_.*|PATH|PWD|SHLVL|_|&)='
all:
@env | ${FILTER_CMD} | sort
diff --git a/unit-tests/hanoi-include.mk b/unit-tests/hanoi-include.mk
index ce443c506b9b..5e7d5c476dfc 100644
--- a/unit-tests/hanoi-include.mk
+++ b/unit-tests/hanoi-include.mk
@@ -1,4 +1,4 @@
-# $NetBSD: hanoi-include.mk,v 1.3 2022/05/08 07:27:50 rillig Exp $
+# $NetBSD: hanoi-include.mk,v 1.4 2023/01/19 22:48:42 rillig Exp $
#
# Implements the Towers of Hanoi puzzle, demonstrating a bunch of more or less
# useful programming techniques:
@@ -21,22 +21,28 @@ FROM?= A # ... from this stack ...
VIA?= B # ... via this stack ...
TO?= C # ... to this stack.
-.if $N == 1
+# Since make has no built-in arithmetic functions, convert N to a list of
+# words and use the built-in word counting instead.
+.if ${N:[#]} == 1
+N:= count ${:U:${:Urange=$N}} # 'count' + one word for every disk
+.endif
+
+.if ${N:[#]} == 2
. for from to in ${FROM} ${TO}
all::
@echo "Move the upper disk from stack ${from} to stack ${to}."
. endfor
.else
-_:= ${N::!=expr $N - 1} ${TMP::=${VIA}} ${VIA::=${TO}} ${TO::=${TMP}}
+_:= ${N::=${N:[1..-2]}} ${TMP::=${VIA}} ${VIA::=${TO}} ${TO::=${TMP}}
. include "${.PARSEDIR}/${.PARSEFILE}"
-_:= ${N::!=expr $N + 1} ${TMP::=${VIA}} ${VIA::=${TO}} ${TO::=${TMP}}
+_:= ${N::+=D} ${TMP::=${VIA}} ${VIA::=${TO}} ${TO::=${TMP}}
. for from to in ${FROM} ${TO}
all::
@echo "Move the upper disk from stack ${from} to stack ${to}."
. endfor
-_:= ${N::!=expr $N - 1} ${TMP::=${VIA}} ${VIA::=${FROM}} ${FROM::=${TMP}}
+_:= ${N::=${N:[1..-2]}} ${TMP::=${VIA}} ${VIA::=${FROM}} ${FROM::=${TMP}}
. include "${.PARSEDIR}/${.PARSEFILE}"
-_:= ${N::!=expr $N + 1} ${TMP::=${VIA}} ${VIA::=${FROM}} ${FROM::=${TMP}}
+_:= ${N::+=D} ${TMP::=${VIA}} ${VIA::=${FROM}} ${FROM::=${TMP}}
.endif
diff --git a/unit-tests/include-main.exp b/unit-tests/include-main.exp
index e677826373c1..f58870486f87 100644
--- a/unit-tests/include-main.exp
+++ b/unit-tests/include-main.exp
@@ -1,17 +1,17 @@
make: "include-main.mk" line 14: main-before-ok
make: "include-main.mk" line 21: main-before-for-ok
-make: "include-sub.mk" line 4: sub-before-ok
-make: "include-sub.mk" line 14: sub-before-for-ok
+make: "include-sub.inc" line 4: sub-before-ok
+make: "include-sub.inc" line 14: sub-before-for-ok
Parsing line 5: . info subsub-ok
-make: "include-subsub.mk" line 5: subsub-ok
- in .for loop from include-sub.mk:31 with i = include
- in .for loop from include-sub.mk:30 with i = nested
- in .for loop from include-sub.mk:29 with i = deeply
+make: "include-subsub.inc" line 5: subsub-ok
+ in .for loop from include-sub.inc:31 with i = include
+ in .for loop from include-sub.inc:30 with i = nested
+ in .for loop from include-sub.inc:29 with i = deeply
in include-main.mk:27
Parsing line 6: .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-sub.inc" line 38: sub-after-ok
+make: "include-sub.inc" line 45: sub-after-for-ok
make: "include-main.mk" line 30: main-after-ok
make: "include-main.mk" line 37: main-after-for-ok
exit status 0
diff --git a/unit-tests/include-main.mk b/unit-tests/include-main.mk
index 9a4c6630506b..50be0587348b 100644
--- a/unit-tests/include-main.mk
+++ b/unit-tests/include-main.mk
@@ -1,4 +1,4 @@
-# $NetBSD: include-main.mk,v 1.7 2022/01/08 23:41:43 rillig Exp $
+# $NetBSD: include-main.mk,v 1.8 2023/01/19 23:26:14 rillig Exp $
#
# Until 2020-09-05, the .INCLUDEDFROMFILE magic variable did not behave
# as described in the manual page.
@@ -24,7 +24,7 @@
. endif
.endfor
-.include "include-sub.mk"
+.include "include-sub.inc"
.if !defined(.INCLUDEDFROMFILE)
. info main-after-ok
diff --git a/unit-tests/include-sub.mk b/unit-tests/include-sub.inc
index 57d2aafe9d1d..f26f14c9d84f 100644
--- a/unit-tests/include-sub.mk
+++ b/unit-tests/include-sub.inc
@@ -1,4 +1,4 @@
-# $NetBSD: include-sub.mk,v 1.9 2022/01/08 23:41:43 rillig Exp $
+# $NetBSD: include-sub.inc,v 1.1 2023/01/19 23:26:14 rillig Exp $
.if ${.INCLUDEDFROMFILE} == "include-main.mk"
. info sub-before-ok
@@ -29,7 +29,7 @@
.for i in deeply
. for i in nested
. for i in include
-.include "include-subsub.mk"
+.include "include-subsub.inc"
. endfor
. endfor
.endfor
diff --git a/unit-tests/include-subsub.inc b/unit-tests/include-subsub.inc
new file mode 100644
index 000000000000..79a6a3770090
--- /dev/null
+++ b/unit-tests/include-subsub.inc
@@ -0,0 +1,9 @@
+# $NetBSD: include-subsub.inc,v 1.1 2023/01/19 23:26:14 rillig Exp $
+
+.if ${.INCLUDEDFROMFILE} == "include-sub.inc"
+.MAKEFLAGS: -dp
+. info subsub-ok
+.MAKEFLAGS: -d0
+.else
+. warning subsub-fail(${.INCLUDEDFROMFILE})
+.endif
diff --git a/unit-tests/include-subsub.mk b/unit-tests/include-subsub.mk
deleted file mode 100644
index 476d75f79556..000000000000
--- a/unit-tests/include-subsub.mk
+++ /dev/null
@@ -1,9 +0,0 @@
-# $NetBSD: include-subsub.mk,v 1.4 2021/01/26 23:44:56 rillig Exp $
-
-.if ${.INCLUDEDFROMFILE} == "include-sub.mk"
-.MAKEFLAGS: -dp
-. info subsub-ok
-.MAKEFLAGS: -d0
-.else
-. warning subsub-fail(${.INCLUDEDFROMFILE})
-.endif
diff --git a/unit-tests/job-output-null.exp b/unit-tests/job-output-null.exp
index 631d4862af44..628ec54a1a6b 100644
--- a/unit-tests/job-output-null.exp
+++ b/unit-tests/job-output-null.exp
@@ -1,4 +1,6 @@
-1
-2a
+1 trailing
+2a trailing
+2b trailing
+2c trailing
3a without newline, 3b without newline.
exit status 0
diff --git a/unit-tests/job-output-null.mk b/unit-tests/job-output-null.mk
index 1efd9c667980..04786dba4096 100644
--- a/unit-tests/job-output-null.mk
+++ b/unit-tests/job-output-null.mk
@@ -1,11 +1,11 @@
-# $NetBSD: job-output-null.mk,v 1.3 2021/09/12 10:26:49 rillig Exp $
+# $NetBSD: job-output-null.mk,v 1.4 2022/09/03 08:03:27 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.
+# Before job.c 1.454 from 2022-09-03, make handled null bytes in the output
+# from the child process inconsistently. It's an edge case though since
+# typically the child processes output text.
# Note: The printf commands used in this test must only use a single format
# string, without parameters. This is because it is implementation-dependent
@@ -16,30 +16,40 @@
# NetBSD /bin/ksh 3 x write("fmt") (via /bin/printf)
# Bash 5 3 x write("fmt")
#
-# In the latter case the output may arrive in parts, which in this test makes
-# a crucial difference since the outcome of the test depends on whether there
-# is a '\n' in each of the blocks from the output.
+# In the latter case the output may arrive in 1 to 3 parts, depending on the
+# exact timing, which in this test makes a crucial difference since before
+# job.c 1.454 from 2022-09-03, the outcome of the test depended on whether
+# there was a '\n' in each of the blocks from the output. Depending on the
+# exact timing, the output of that test varied, its possible values were '2a',
+# '2a 2b', '2a 2c', '2a 2b 2c'.
.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.
+ # The null byte from the command output is replaced with a single
+ # space by CollectOutput.
@printf '1\0trailing\n'
+ # expect: 1 trailing
# 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.
+ # Each null byte from the command output is replaced with a single
+ # space.
@printf '2a\0trailing\n''2b\0trailing\n''2c\0trailing\n'
+ # expect: 2a trailing
+ # expect: 2b trailing
+ # expect: 2c trailing
@sleep 1
- # The null bytes are replaced with spaces since they are not followed
- # by a newline.
+ # Each null byte from the command output is replaced with a single
+ # space. Because there is no trailing newline in the output, these
+ # null bytes were replaced with spaces even before job.c 1.454 from
+ # 2022-09-03, unlike in the cases above.
#
# The three null bytes in a row test whether this output is
# compressed to a single space like in DebugFailedTarget. It isn't.
@printf '3a\0without\0\0\0newline, 3b\0without\0\0\0newline.'
+ # expect: 3a without newline, 3b without newline.
diff --git a/unit-tests/make-exported.mk b/unit-tests/make-exported.mk
index 58cb15183b8d..363ea2733a47 100755
--- a/unit-tests/make-exported.mk
+++ b/unit-tests/make-exported.mk
@@ -1,4 +1,4 @@
-# $NetBSD: make-exported.mk,v 1.6 2020/10/05 19:27:48 rillig Exp $
+# $NetBSD: make-exported.mk,v 1.7 2022/09/09 18:36:15 sjg Exp $
#
# As of 2020-08-09, the code in Var_Export is shared between the .export
# directive and the .MAKE.EXPORTED variable. This leads to non-obvious
@@ -22,4 +22,4 @@ UT_VAR= ${UNEXPANDED}
.MAKE.EXPORTED= -literal UT_VAR
all:
- @env | sort | egrep '^UT_|make-exported-value' || true
+ @env | sort | ${EGREP} '^UT_|make-exported-value' || true
diff --git a/unit-tests/opt-env.exp b/unit-tests/opt-env.exp
index b2e9ea85bafd..39a9383953dd 100644
--- a/unit-tests/opt-env.exp
+++ b/unit-tests/opt-env.exp
@@ -1,5 +1 @@
-make: "opt-env.mk" line 13: Malformed conditional (${FROM_ENV} != value-from-env)
-make: "opt-env.mk" line 20: value-from-mk
-
-make: stopped in unit-tests
-exit status 1
+exit status 0
diff --git a/unit-tests/opt-query.exp b/unit-tests/opt-query.exp
index 38025dcf4d3a..0ba62780d844 100644
--- a/unit-tests/opt-query.exp
+++ b/unit-tests/opt-query.exp
@@ -1,2 +1,24 @@
+Making commands:
command during parsing
-exit status 1
+commands: query status 1
+
+Making opt-query-file.out-of-date in compat mode:
+opt-query-file.out-of-date in compat mode: query status 1
+
+Making opt-query-file.up-to-date in compat mode:
+`opt-query-file.up-to-date' is up to date.
+opt-query-file.up-to-date in compat mode: query status 0
+
+Making phony in compat mode:
+phony in compat mode: query status 1
+
+Making opt-query-file.out-of-date in jobs mode:
+opt-query-file.out-of-date in jobs mode: query status 1
+
+Making opt-query-file.up-to-date in jobs mode:
+opt-query-file.up-to-date in jobs mode: query status 0
+
+Making phony in jobs mode:
+phony in jobs mode: query status 1
+
+exit status 0
diff --git a/unit-tests/opt-query.mk b/unit-tests/opt-query.mk
index 0a7d5219a8fe..3554d69afad6 100644
--- a/unit-tests/opt-query.mk
+++ b/unit-tests/opt-query.mk
@@ -1,4 +1,4 @@
-# $NetBSD: opt-query.mk,v 1.4 2020/11/09 20:50:56 rillig Exp $
+# $NetBSD: opt-query.mk,v 1.7 2022/08/18 05:37:05 rillig Exp $
#
# Tests for the -q command line option.
#
@@ -6,7 +6,57 @@
# None of the commands in the targets are run, not even those that are
# prefixed with '+'.
-.MAKEFLAGS: -q
+# This test consists of several parts:
+#
+# main Delegates to the actual tests.
+#
+# commands Ensures that none of the targets is made.
+#
+# variants Ensures that the up-to-date status is correctly
+# reported in both compat and jobs mode, and for several
+# kinds of make targets.
+PART?= main
+
+.if ${PART} == "main"
+
+all: .PHONY variants cleanup
+
+_!= touch -f opt-query-file.up-to-date
+
+variants: .PHONY
+
+. for target in commands
+ @echo 'Making ${target}':
+ @${MAKE} -r -f ${MAKEFILE} -q ${mode:Mjobs:%=-j1} ${target} PART=commands \
+ && echo "${target}: query status $$?" \
+ || echo "${target}: query status $$?"
+ @echo
+. endfor
+
+. for mode in compat jobs
+. for target in opt-query-file.out-of-date opt-query-file.up-to-date phony
+ @echo 'Making ${target} in ${mode} mode':
+ @${MAKE} -r -f ${MAKEFILE} -q ${mode:Mjobs:%=-j1} ${target} PART=variants \
+ && echo "${target} in ${mode} mode: query status $$?" \
+ || echo "${target} in ${mode} mode: query status $$?"
+ @echo
+. endfor
+. endfor
+
+# Between 1994 and before 2022-08-17, the exit status for '-q' was always 1,
+# the cause for that exit code varied over time though.
+#
+# expect: opt-query-file.out-of-date in compat mode: query status 1
+# expect: opt-query-file.up-to-date in compat mode: query status 0
+# expect: phony in compat mode: query status 1
+# expect: opt-query-file.out-of-date in jobs mode: query status 1
+# expect: opt-query-file.up-to-date in jobs mode: query status 0
+# expect: phony in jobs mode: query status 1
+
+cleanup: .PHONY
+ @rm -f opt-query-file.up-to-date
+
+.elif ${PART} == "commands"
# This command cannot be prevented from being run since it is used at parse
# time, and any later variable assignments may depend on its result.
@@ -18,9 +68,18 @@
@+echo '$@: run always'
# None of these commands are run.
-all:
+commands:
@echo '$@: hidden command'
@+echo '$@: run always'
-
-# The exit status 1 is because the "all" target has to be made, that is,
+# The exit status 1 is because the "commands" target has to be made, that is,
# it is not up-to-date.
+
+.elif ${PART} == "variants"
+
+opt-query-file.out-of-date: ${MAKEFILE}
+opt-query-file.up-to-date: ${MAKEFILE}
+phony: .PHONY
+
+.else
+. error Invalid part '${PART}'
+.endif
diff --git a/unit-tests/parse-var.exp b/unit-tests/parse-var.exp
index 39a9383953dd..bae925e8c869 100644
--- a/unit-tests/parse-var.exp
+++ b/unit-tests/parse-var.exp
@@ -1 +1,5 @@
-exit status 0
+make: Unfinished modifier for "BRACE_GROUP" (',' missing)
+make: "parse-var.mk" line 130: Malformed conditional (0 && ${BRACE_GROUP:S,${BRACE_PAIR:S,{,{{,},<lbraces>,})
+make: Fatal errors encountered -- cannot continue
+make: stopped in unit-tests
+exit status 1
diff --git a/unit-tests/parse-var.mk b/unit-tests/parse-var.mk
index bd6c59f0e5cb..cca6931d14a1 100644
--- a/unit-tests/parse-var.mk
+++ b/unit-tests/parse-var.mk
@@ -1,13 +1,135 @@
-# $NetBSD: parse-var.mk,v 1.1 2020/10/04 06:53:15 rillig Exp $
+# $NetBSD: parse-var.mk,v 1.6 2022/09/25 21:26:23 rillig Exp $
+#
+# Tests for parsing variable expressions.
+#
+# TODO: Add systematic tests for all of the below combinations.
+#
+# Written form:
+# short form
+# long form with braces endc == '}'
+# long form with parentheses endc == ')'
+# indirect modifiers endc == '\0'
+#
+# Based on:
+# undefined variable
+# global variable
+# command-line variable
+# environment variable
+# target-local variable
+# legacy variable '@F'
+#
+# VarEvalMode:
+# parse
+# eval
+# eval-undeferr
+# eval-keep-dollar
+# eval-keep-undef
+# eval-keep-dollar-undef
+#
+# Global mode:
+# without -dL
+# with -dL
+#
+# Modifiers:
+# no
+# yes, stay undefined
+# convert to defined
+# indirect modifiers, involving changes to VarEvalMode
+#
+# Error conditions:
+# for the short form, EOF after the '$'
+# for the short form, each character
+# for the long forms, EOF right after '${'
+# for the long forms, EOF after the variable name
+# for the long forms, EOF after the ':'
+# for the long forms, EOF after parsing a modifier
+# for the long forms, ':}'
+# for each modifier: syntactic error
+# for each modifier: evaluation error
+#
+# Context:
+# in a condition, only operand, unquoted
+# in a condition, only operand, quoted
+# in a condition, left-hand side, unquoted
+# in a condition, left-hand side, quoted
+# in a condition, right-hand side, unquoted
+# in a condition, right-hand side, quoted
+# left-hand side of a variable assignment
+# right-hand side of a ':=' variable assignment
+# right-hand side of a '!=' variable assignment
+# shell command in a target
+# .info directive
+# dependency line
+# items in a .for loop
+# everywhere else Var_Parse is called
+#
+# Further influences:
+# multi-level evaluations like 'other=${OTHER}' with OTHER='$$ ${THIRD}'
+#
+# Effects:
+# How much does the parsing position advance (pp)?
+# What's the value of the expression (out_val)?
+# What's the status after parsing the expression (VarParseResult)?
+# What error messages are printed (Parse_Error)?
+# What no-effect error messages are printed (Error)?
+# What error messages should be printed but aren't?
+# What other side effects are there?
.MAKEFLAGS: -dL
-# In variable assignments, there may be spaces on the left-hand side of the
-# assignment, but only if they occur inside variable expressions.
+# In variable assignments, there may be spaces in the middle of the left-hand
+# side of the assignment, but only if they occur inside variable expressions.
+# Leading spaces (but not tabs) are possible but unusual.
+# Trailing spaces are common in some coding styles, others omit them.
VAR.${:U param }= value
.if ${VAR.${:U param }} != "value"
. error
.endif
-all:
- @:;
+# XXX: The following paragraph already uses past tense, in the hope that the
+# parsing behavior can be cleaned up soon.
+
+# Since var.c 1.323 from 2020-07-26 18:11 and except for var.c 1.1028 from
+# 2022-08-08, the exact way of parsing an expression depended on whether the
+# expression was actually evaluated or merely parsed.
+#
+# If it was evaluated, nested expressions were parsed correctly, parsing each
+# modifier according to its exact definition (see varmod.mk).
+#
+# If the expression was merely parsed but not evaluated (for example, because
+# its value would not influence the outcome of the condition, or during the
+# first pass of the ':@var@body@' modifier), and the expression contained a
+# modifier, and that modifier contained a nested expression, the nested
+# expression was not parsed correctly. Instead, make only counted the opening
+# and closing delimiters, which failed for nested modifiers with unbalanced
+# braces.
+#
+# This naive brace counting was implemented in ParseModifierPartDollar. As of
+# var.c 1.1029, there are still several other places that merely count braces
+# instead of properly parsing subexpressions.
+
+#.MAKEFLAGS: -dcpv
+# Keep these braces outside the conditions below, to keep them simple to
+# understand. If the BRACE_PAIR had been replaced with ':U{}', the '}' would
+# have to be escaped, but not the '{'. This asymmetry would have made the
+# example even more complicated to understand.
+BRACE_PAIR= {}
+# In this test word, the '{{}' in the middle will be replaced.
+BRACE_GROUP= {{{{}}}}
+
+# The inner ':S' modifier turns the word '{}' into '{{}'.
+# The outer ':S' modifier then replaces '{{}' with '<lbraces>'.
+# In the first case, the outer expression is relevant and is parsed correctly.
+.if 1 && ${BRACE_GROUP:S,${BRACE_PAIR:S,{,{{,},<lbraces>,}
+.endif
+# In the second case, the outer expression was irrelevant. In this case, in
+# the parts of the outer ':S' modifier, make only counted the braces, and since
+# the inner expression '${BRACE_PAIR:...}' contains more '{' than '}', parsing
+# failed with the error message 'Unfinished modifier for "BRACE_GROUP"'. Fixed
+# in var.c 1.1028 from 2022-08-08, reverted in var.c 1.1029 from 2022-08-23.
+.if 0 && ${BRACE_GROUP:S,${BRACE_PAIR:S,{,{{,},<lbraces>,}
+.endif
+#.MAKEFLAGS: -d0
+
+
+all: .PHONY
diff --git a/unit-tests/sh-leading-at.exp b/unit-tests/sh-leading-at.exp
index 8347fda085f7..8197a0d2b71a 100644
--- a/unit-tests/sh-leading-at.exp
+++ b/unit-tests/sh-leading-at.exp
@@ -3,4 +3,5 @@ space after @
echo 'echoed'
echoed
3
+whitespace in leading part
exit status 0
diff --git a/unit-tests/sh-leading-at.mk b/unit-tests/sh-leading-at.mk
index 9f98005ec088..cff3d4da1263 100644
--- a/unit-tests/sh-leading-at.mk
+++ b/unit-tests/sh-leading-at.mk
@@ -1,4 +1,4 @@
-# $NetBSD: sh-leading-at.mk,v 1.5 2020/11/15 20:20:58 rillig Exp $
+# $NetBSD: sh-leading-at.mk,v 1.6 2023/01/19 19:55:27 rillig Exp $
#
# Tests for shell commands preceded by an '@', to suppress printing
# the command to stdout.
@@ -16,3 +16,7 @@ all:
# The leading '@' can be repeated.
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@echo '3'
+
+ # Since 2023-01-17, the leading '@', '+' and '-' may contain
+ # whitespace, for compatibility with GNU make.
+ @ @ @ echo 'whitespace in leading part'
diff --git a/unit-tests/sh-leading-hyphen.exp b/unit-tests/sh-leading-hyphen.exp
index 39a9383953dd..d049757777cd 100644
--- a/unit-tests/sh-leading-hyphen.exp
+++ b/unit-tests/sh-leading-hyphen.exp
@@ -1 +1,11 @@
+true
+false
+*** Error code 1 (ignored)
+unknown-command 'needed for needshell in compat.c'
+unknown-command: not found
+*** Error code 127 (ignored)
+unknown-long-option 'needed for needshell in compat.c'
+unknown-long-option: not found
+whitespace in leading part
+*** Error code 127 (ignored)
exit status 0
diff --git a/unit-tests/sh-leading-hyphen.mk b/unit-tests/sh-leading-hyphen.mk
index d760abb9afdd..08b50a2ddc42 100644
--- a/unit-tests/sh-leading-hyphen.mk
+++ b/unit-tests/sh-leading-hyphen.mk
@@ -1,4 +1,4 @@
-# $NetBSD: sh-leading-hyphen.mk,v 1.3 2020/11/15 20:20:58 rillig Exp $
+# $NetBSD: sh-leading-hyphen.mk,v 1.4 2023/01/19 19:55:27 rillig Exp $
#
# Tests for shell commands preceded by a '-', to ignore the exit status of
# the command line.
@@ -11,4 +11,19 @@
# TODO: Implementation
all:
- @:;
+ -true
+ -false
+
+ # An undefined variable expands to an empty string, without warning.
+ # This is used in practice for prefixing tool names or for DESTDIR.
+ # The '-' before 'unknown' is interpreted by make as '.IGNORE' flag.
+ ${UNDEF}-unknown-command 'needed for needshell in compat.c'
+
+ # Expanding undefined variables may lead to strange error messages
+ # when the shell interprets single-character options as commands
+ # instead.
+ ${UNDEF} --unknown-long-option 'needed for needshell in compat.c'
+
+ # Since 2023-01-17, the leading '@', '+' and '-' may contain
+ # whitespace, for compatibility with GNU make.
+ - - - @echo 'whitespace in leading part'
diff --git a/unit-tests/sh-leading-plus.exp b/unit-tests/sh-leading-plus.exp
index eb586d29f1c2..8cc26deaacb5 100644
--- a/unit-tests/sh-leading-plus.exp
+++ b/unit-tests/sh-leading-plus.exp
@@ -1,4 +1,6 @@
echo 'this command is not run'
echo 'this command is run'
this command is run
+echo 'whitespace in leading part'
+whitespace in leading part
exit status 0
diff --git a/unit-tests/sh-leading-plus.mk b/unit-tests/sh-leading-plus.mk
index ff57b4a38a7d..83e7e7a15e24 100644
--- a/unit-tests/sh-leading-plus.mk
+++ b/unit-tests/sh-leading-plus.mk
@@ -1,4 +1,4 @@
-# $NetBSD: sh-leading-plus.mk,v 1.4 2020/11/09 20:50:56 rillig Exp $
+# $NetBSD: sh-leading-plus.mk,v 1.5 2023/01/19 19:55:27 rillig Exp $
#
# Tests for shell commands preceded by a '+', to run them even if
# the command line option -n is given.
@@ -8,3 +8,7 @@
all:
@echo 'this command is not run'
@+echo 'this command is run'
+
+ # Since 2023-01-17, the leading '@', '+' and '-' may contain
+ # whitespace, for compatibility with GNU make.
+ + + + @echo 'whitespace in leading part'
diff --git a/unit-tests/var-op-expand.exp b/unit-tests/var-op-expand.exp
index a4ba53942cf7..4916336c0400 100644
--- a/unit-tests/var-op-expand.exp
+++ b/unit-tests/var-op-expand.exp
@@ -1,7 +1,7 @@
-make: "var-op-expand.mk" line 265: Unknown modifier "s,value,replaced,"
-make: "var-op-expand.mk" line 268: warning: XXX Neither branch should be taken.
-make: "var-op-expand.mk" line 273: Unknown modifier "s,value,replaced,"
-make: "var-op-expand.mk" line 274: warning: XXX Neither branch should be taken.
+make: "var-op-expand.mk" line 274: Unknown modifier "s,value,replaced,"
+make: "var-op-expand.mk" line 277: warning: XXX Neither branch should be taken.
+make: "var-op-expand.mk" line 282: Unknown modifier "s,value,replaced,"
+make: "var-op-expand.mk" line 283: warning: XXX Neither branch should be taken.
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/unit-tests/var-op-expand.mk b/unit-tests/var-op-expand.mk
index 1d905aeb3757..f89aa6fa4b56 100644
--- a/unit-tests/var-op-expand.mk
+++ b/unit-tests/var-op-expand.mk
@@ -1,4 +1,4 @@
-# $NetBSD: var-op-expand.mk,v 1.16 2021/12/28 10:47:00 rillig Exp $
+# $NetBSD: var-op-expand.mk,v 1.17 2022/09/08 20:23:45 rillig Exp $
#
# Tests for the := variable assignment operator, which expands its
# right-hand side.
@@ -37,7 +37,7 @@ VAR:= $$ $$$$ $$$$$$$$
.endif
-# reference to a variable containing a literal dollar sign
+# reference to a variable containing literal dollar signs
REF= $$ $$$$ $$$$$$$$
VAR:= ${REF}
REF= too late
@@ -49,6 +49,9 @@ REF= too late
# reference to an undefined variable
.undef UNDEF
VAR:= <${UNDEF}>
+.if ${VAR} != "<>"
+. error
+.endif
UNDEF= after
.if ${VAR} != "<after>"
. error
@@ -68,6 +71,9 @@ REF= too late
# expression with an indirect modifier referring to an undefined variable
.undef UNDEF
VAR:= ${:${UNDEF}}
+.if ${VAR} != ""
+. error
+.endif
UNDEF= Uwas undefined
.if ${VAR} != "was undefined"
. error
@@ -99,6 +105,9 @@ UNDEF= Uwas undefined
REF2= <${REF3}>
REF= ${REF2}
VAR:= ${REF}
+.if ${VAR} != "<>"
+. error
+.endif
REF3= too late
.if ${VAR} != "<too late>"
. error
diff --git a/unit-tests/var-readonly.exp b/unit-tests/var-readonly.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/unit-tests/var-readonly.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/unit-tests/var-readonly.mk b/unit-tests/var-readonly.mk
new file mode 100644
index 000000000000..43799e4adb5c
--- /dev/null
+++ b/unit-tests/var-readonly.mk
@@ -0,0 +1,20 @@
+# $NetBSD: var-readonly.mk,v 1.1 2023/01/24 00:20:00 sjg Exp $
+
+# the answer
+N = 42
+.READONLY: N
+# this should be ignored
+N = 666
+.if ${N} != 42
+.error N ($N) should be 42
+.endif
+
+.NOREADONLY: N
+# now we can change it
+N = 69
+.if ${N} == 42
+.error N should not be 42
+.endif
+
+all:
+
diff --git a/unit-tests/var-scope-local-legacy.exp b/unit-tests/var-scope-local-legacy.exp
index 39a9383953dd..fb7e0863b2e6 100644
--- a/unit-tests/var-scope-local-legacy.exp
+++ b/unit-tests/var-scope-local-legacy.exp
@@ -1 +1,6 @@
+: LEN4=undef_
+: XY=undef_
+: AF=undef_
+: %D=undef_ %F=undef_
+: @D=._ @F=all_
exit status 0
diff --git a/unit-tests/var-scope-local-legacy.mk b/unit-tests/var-scope-local-legacy.mk
index e519d63e7c51..9b70e3f8f143 100644
--- a/unit-tests/var-scope-local-legacy.mk
+++ b/unit-tests/var-scope-local-legacy.mk
@@ -1,8 +1,17 @@
-# $NetBSD: var-scope-local-legacy.mk,v 1.1 2022/01/23 16:25:54 rillig Exp $
+# $NetBSD: var-scope-local-legacy.mk,v 1.2 2022/09/27 19:18:45 rillig Exp $
#
# Tests for legacy target-local variables, such as ${<F} or ${@D}.
-# TODO: Implementation
-
-all:
- @:;
+all: .PHONY
+ # Only variables of length 2 can be legacy, this one cannot.
+ : LEN4=${LEN4:Uundef}_
+ # The second character of the name must be 'D' or 'F'.
+ : XY=${XY:Uundef}_
+ # The first character must name one of the 7 predefined local
+ # variables, 'A' is not such a character.
+ : AF=${AF:Uundef}_
+ # The variable '.MEMBER' is undefined, therefore '%D' and '%F' are
+ # undefined as well.
+ : %D=${%D:Uundef}_ %F=${%F:Uundef}_
+ # The directory name of the target is '.', its basename is 'all'.
+ : @D=${@D:Uundef}_ @F=${@F:Uundef}_
diff --git a/unit-tests/varmod-defined.mk b/unit-tests/varmod-defined.mk
index ab5d708cf73f..8effec82620d 100644
--- a/unit-tests/varmod-defined.mk
+++ b/unit-tests/varmod-defined.mk
@@ -1,4 +1,4 @@
-# $NetBSD: varmod-defined.mk,v 1.12 2021/11/30 23:52:19 rillig Exp $
+# $NetBSD: varmod-defined.mk,v 1.13 2022/08/24 20:22:10 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.
@@ -104,5 +104,13 @@ VAR:= ${VAR:D${8_DOLLARS}}
VAR:= ${VAR:@var@${8_DOLLARS}@}
.MAKEFLAGS: -d0
-all:
- @:;
+
+# Before var.c 1.1030 from 2022-08-24, the following expression caused an
+# out-of-bounds read when parsing the indirect ':D' modifier.
+M_U_backslash:= ${:UU\\}
+.if ${:${M_U_backslash}} != "\\"
+. error
+.endif
+
+
+all: .PHONY
diff --git a/unit-tests/varmod-ifelse.exp b/unit-tests/varmod-ifelse.exp
index 7134c71b8d39..80361ebf6d61 100644
--- a/unit-tests/varmod-ifelse.exp
+++ b/unit-tests/varmod-ifelse.exp
@@ -11,12 +11,12 @@ Comparing 1.000000 == 0.000000
make: Bad conditional expression '1 == == 2' in '1 == == 2?yes:no'
Comparing "" != ""
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
+CondParser_Eval: ${ ${:U\$}{VAR} == value:?ok:bad} != "ok"
+CondParser_Eval: ${VAR} == value
Comparing "value" == "value"
Comparing "ok" != "ok"
make: "varmod-ifelse.mk" line 153: no.
-make: "varmod-ifelse.mk" line 154: String comparison operator must be either == or !=
+make: "varmod-ifelse.mk" line 154: Comparison with '>=' requires both operands 'no' and '10' to be numeric
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'
diff --git a/unit-tests/varmod-ifelse.mk b/unit-tests/varmod-ifelse.mk
index a823ae784935..2d1c54943ca1 100644
--- a/unit-tests/varmod-ifelse.mk
+++ b/unit-tests/varmod-ifelse.mk
@@ -1,4 +1,4 @@
-# $NetBSD: varmod-ifelse.mk,v 1.19 2022/05/08 06:51:27 rillig Exp $
+# $NetBSD: varmod-ifelse.mk,v 1.20 2022/09/25 12:51:37 rillig Exp $
#
# Tests for the ${cond:?then:else} variable modifier, which evaluates either
# the then-expression or the else-expression, depending on the condition.
@@ -106,7 +106,7 @@ COND:= ${${UNDEF} == "":?bad-assign:bad-assign}
# from the parser of the .for loop body. See ForLoop_SubstVarLong.
.MAKEFLAGS: -dc
VAR= value
-.if ${ ${:U\$}{VAR} == value :?ok:bad} != "ok"
+.if ${ ${:U\$}{VAR} == value:?ok:bad} != "ok"
. error
.endif
.MAKEFLAGS: -d0
diff --git a/unit-tests/varmod-loop.exp b/unit-tests/varmod-loop.exp
index bbe0037673b3..9b432c806885 100644
--- a/unit-tests/varmod-loop.exp
+++ b/unit-tests/varmod-loop.exp
@@ -13,4 +13,10 @@ mod-loop-dollar:$3$:
mod-loop-dollar:$${word}$$:
mod-loop-dollar:$$5$$:
mod-loop-dollar:$$${word}$$$:
+: t=$(( ${t:-0} + 1 ))
+: dollar=end
+: backslash=\ end
+: dollar=$ at=@ backslash=\ end
+: dollar=$$ at=@@ backslash=\\ end
+: dollar=$$ at=@@ backslash=\\ end
exit status 0
diff --git a/unit-tests/varmod-loop.mk b/unit-tests/varmod-loop.mk
index 82046ff95d79..d3cc0228efd9 100644
--- a/unit-tests/varmod-loop.mk
+++ b/unit-tests/varmod-loop.mk
@@ -1,4 +1,4 @@
-# $NetBSD: varmod-loop.mk,v 1.18 2021/12/05 15:20:13 rillig Exp $
+# $NetBSD: varmod-loop.mk,v 1.21 2022/08/23 21:13:46 rillig Exp $
#
# Tests for the :@var@...${var}...@ variable modifier.
@@ -186,4 +186,49 @@ CMDLINE= global # needed for deleting the environment
. error # 'CMDLINE' is gone now from all scopes
.endif
+
+# In the loop body text of the ':@' modifier, a literal '$' is written as '$$',
+# not '\$'. In the following example, each '$$' turns into a single '$',
+# except for '$i', which is replaced with the then-current value '1' of the
+# iteration variable.
+#
+# XXX: was broken in var.c 1.1028 from 2022-08-08, reverted in var.c 1.1029
+# from 2022-08-23; see parse-var.mk, keyword 'BRACE_GROUP'.
+all: varmod-loop-literal-dollar
+varmod-loop-literal-dollar: .PHONY
+ : ${:U1:@i@ t=$$(( $${t:-0} + $i ))@}
+
+
+# When parsing the loop body, each '\$', '\@' and '\\' is unescaped to '$',
+# '@' and '\'; all other backslashes are retained.
+#
+# In practice, the '$' is not escaped as '\$', as there is a second round of
+# unescaping '$$' to '$' later when the loop body is expanded after setting the
+# iteration variable.
+#
+# After the iteration variable has been set, the loop body is expanded with
+# this unescaping, regardless of whether .MAKE.SAVE_DOLLARS is set or not:
+# $$ a literal '$'
+# $x, ${var}, $(var) a nested expression
+# any other character itself
+all: escape-modifier
+escape-modifier: .PHONY
+ # In the first round, '\$ ' is unescaped to '$ ', and since the
+ # variable named ' ' is not defined, the expression '$ ' expands to an
+ # empty string.
+ # expect: : dollar=end
+ : ${:U1:@i@ dollar=\$ end@}
+
+ # Like in other modifiers, '\ ' is preserved, since ' ' is not one of
+ # the characters that _must_ be escaped.
+ # expect: : backslash=\ end
+ : ${:U1:@i@ backslash=\ end@}
+
+ # expect: : dollar=$ at=@ backslash=\ end
+ : ${:U1:@i@ dollar=\$\$ at=\@ backslash=\\ end@}
+ # expect: : dollar=$$ at=@@ backslash=\\ end
+ : ${:U1:@i@ dollar=\$\$\$\$ at=\@\@ backslash=\\\\ end@}
+ # expect: : dollar=$$ at=@@ backslash=\\ end
+ : ${:U1:@i@ dollar=$$$$ at=\@\@ backslash=\\\\ end@}
+
all: .PHONY
diff --git a/unit-tests/varmod-match.mk b/unit-tests/varmod-match.mk
index 8435c2a614e0..87928267d794 100644
--- a/unit-tests/varmod-match.mk
+++ b/unit-tests/varmod-match.mk
@@ -1,4 +1,4 @@
-# $NetBSD: varmod-match.mk,v 1.11 2022/06/11 09:15:49 rillig Exp $
+# $NetBSD: varmod-match.mk,v 1.12 2022/08/24 21:03:57 rillig Exp $
#
# Tests for the :M variable modifier, which filters words that match the
# given pattern.
@@ -280,3 +280,13 @@ n= 2
.if ${PRIMES:M${:U2}} != "2"
. error
.endif
+
+
+# Before var.c 1.1031 from 2022-08-24, the following expressions caused an
+# out-of-bounds read beyond the indirect ':M' modifiers.
+.if ${:U:${:UM\\}} # The ':M' pattern need not be unescaped, the
+. error # resulting pattern is '\', it never matches
+.endif # anything.
+.if ${:U:${:UM\\\:\\}} # The ':M' pattern must be unescaped, the
+. error # resulting pattern is ':\', it never matches
+.endif # anything.
diff --git a/unit-tests/varmod-order-numeric.mk b/unit-tests/varmod-order-numeric.mk
index 542894c53942..62212bd265ad 100644
--- a/unit-tests/varmod-order-numeric.mk
+++ b/unit-tests/varmod-order-numeric.mk
@@ -1,4 +1,4 @@
-# $NetBSD: varmod-order-numeric.mk,v 1.7 2022/02/09 21:09:24 rillig Exp $
+# $NetBSD: varmod-order-numeric.mk,v 1.8 2022/09/27 19:18:45 rillig Exp $
#
# Tests for the variable modifiers ':On', which returns the words, sorted in
# ascending numeric order, and for ':Orn' and ':Onr', which additionally
@@ -50,4 +50,10 @@ MIXED_BASE= 0 010 0x7 9
. error ${MIXED_BASE:On}
.endif
+# The measurement units for suffixes are k, M, G, but not T.
+# The string '3T' evaluates to 3, the string 'x' evaluates as '0'.
+.if ${4 3T 2M x:L:On} != "x 3T 4 2M"
+. error
+.endif
+
all:
diff --git a/unit-tests/varmod-to-lower.mk b/unit-tests/varmod-to-lower.mk
index 19d3406054b7..44116fd3eee2 100644
--- a/unit-tests/varmod-to-lower.mk
+++ b/unit-tests/varmod-to-lower.mk
@@ -1,7 +1,7 @@
-# $NetBSD: varmod-to-lower.mk,v 1.5 2020/11/15 20:20:58 rillig Exp $
+# $NetBSD: varmod-to-lower.mk,v 1.6 2022/11/29 23:54:55 rillig Exp $
#
-# Tests for the :tl variable modifier, which returns the words in the
-# variable value, converted to lowercase.
+# Tests for the :tl variable modifier, which converts the expression value
+# to lowercase.
#
# TODO: What about non-ASCII characters? ISO-8859-1, UTF-8?
@@ -17,5 +17,10 @@
. error
.endif
-all:
- @:;
+# The ':tl' modifier works on the whole string, without splitting it into
+# words.
+.if ${:Umultiple spaces:tl} != "multiple spaces"
+. error
+.endif
+
+all: .PHONY
diff --git a/unit-tests/varmod-undefined.mk b/unit-tests/varmod-undefined.mk
index e06fc73244ab..9fd41c7fdb60 100644
--- a/unit-tests/varmod-undefined.mk
+++ b/unit-tests/varmod-undefined.mk
@@ -1,4 +1,4 @@
-# $NetBSD: varmod-undefined.mk,v 1.7 2020/11/15 20:20:58 rillig Exp $
+# $NetBSD: varmod-undefined.mk,v 1.8 2022/08/06 21:26:05 rillig Exp $
#
# Tests for the :U variable modifier, which returns the given string
# if the variable is undefined.
@@ -29,7 +29,7 @@
# The nested variable expressions may contain braces, and these braces don't
# need to match pairwise. In the following example, the :S modifier uses '{'
# as delimiter, which confuses both editors and humans because the opening
-# and # closing braces don't match anymore. It's syntactically valid though.
+# and closing braces don't match anymore. It's syntactically valid though.
# For more similar examples, see varmod-subst.mk, mod-subst-delimiter.
.if ${:U${:Uvalue:S{a{X{}} != vXlue
diff --git a/unit-tests/varmod.exp b/unit-tests/varmod.exp
index e36c4ded9b47..3e588dc4d83f 100644
--- a/unit-tests/varmod.exp
+++ b/unit-tests/varmod.exp
@@ -1,8 +1,8 @@
-make: "varmod.mk" line 42: To escape a dollar, use \$, not $$, at "$$:L} != """
-make: "varmod.mk" line 42: Invalid variable name ':', at "$:L} != """
-make: "varmod.mk" line 47: Dollar followed by nothing
-make: "varmod.mk" line 56: Missing delimiter ':' after modifier "P"
-make: "varmod.mk" line 57: Missing argument for ".error"
+make: "varmod.mk" line 96: To escape a dollar, use \$, not $$, at "$$:L} != """
+make: "varmod.mk" line 96: Invalid variable name ':', at "$:L} != """
+make: "varmod.mk" line 101: Dollar followed by nothing
+make: "varmod.mk" line 110: Missing delimiter ':' after modifier "P"
+make: "varmod.mk" line 111: Missing argument for ".error"
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/unit-tests/varmod.mk b/unit-tests/varmod.mk
index 21ddf9103251..17bc1e6e3ae3 100644
--- a/unit-tests/varmod.mk
+++ b/unit-tests/varmod.mk
@@ -1,6 +1,60 @@
-# $NetBSD: varmod.mk,v 1.5 2020/12/19 22:33:11 rillig Exp $
+# $NetBSD: varmod.mk,v 1.7 2022/08/24 21:38:06 rillig Exp $
#
# Tests for variable modifiers, such as :Q, :S,from,to or :Ufallback.
+#
+# See also:
+# varparse-errors.mk
+
+# As of 2022-08-06, the possible behaviors during parsing are:
+#
+# * `strict`: the parsing style used by most modifiers:
+# * either uses `ParseModifierPart` or parses the modifier literal
+# * other modifiers may follow, separated by a ':'
+#
+# * `greedy`: calls `ParseModifierPart` with `ch->endc`; this means
+# that no further modifiers are parsed in that expression.
+#
+# * `no-colon`: after parsing this modifier, the following modifier
+# does not need to be separated by a colon.
+# Omitting this colon is bad style.
+#
+# * `individual`: parsing this modifier does not follow the common
+# pattern of calling `ParseModifierPart`.
+#
+# The SysV column says whether a parse error in the modifier falls back
+# trying the `:from=to` System V modifier.
+#
+# | **Operator** | **Behavior** | **Remarks** | **SysV** |
+# |--------------|--------------|--------------------|----------|
+# | `!` | no-colon | | no |
+# | `:=` | greedy | | yes |
+# | `?:` | greedy | | no |
+# | `@` | no-colon | | no |
+# | `C` | no-colon | | no |
+# | `D` | individual | custom parser | N/A |
+# | `E` | strict | | yes |
+# | `H` | strict | | yes |
+# | `L` | no-colon | | N/A |
+# | `M` | individual | custom parser | N/A |
+# | `N` | individual | custom parser | N/A |
+# | `O` | strict | only literal value | no |
+# | `P` | no-colon | | N/A |
+# | `Q` | strict | | yes |
+# | `R` | strict | | yes |
+# | `S` | no-colon | | N/A |
+# | `T` | strict | | N/A |
+# | `U` | individual | custom parser | N/A |
+# | `[` | strict | | no |
+# | `_` | individual | strcspn | yes |
+# | `gmtime` | strict | only literal value | yes |
+# | `hash` | strict | | N/A |
+# | `localtime` | strict | only literal value | yes |
+# | `q` | strict | | yes |
+# | `range` | strict | | N/A |
+# | `sh` | strict | | N/A |
+# | `t` | strict | | no |
+# | `u` | strict | | yes |
+# | `from=to` | greedy | SysV, fallback | N/A |
DOLLAR1= $$
DOLLAR2= ${:U\$}
diff --git a/unit-tests/varname-dot-newline.exp b/unit-tests/varname-dot-newline.exp
index 13943539bf3b..684fb5799752 100644
--- a/unit-tests/varname-dot-newline.exp
+++ b/unit-tests/varname-dot-newline.exp
@@ -1,4 +1,5 @@
-make: "varname-dot-newline.mk" line 16: The .newline variable can be overwritten. Just don't do that.
first
second
+backslash newline: <\
+>
exit status 0
diff --git a/unit-tests/varname-dot-newline.mk b/unit-tests/varname-dot-newline.mk
index 0565d244f298..1940dc2a990d 100644
--- a/unit-tests/varname-dot-newline.mk
+++ b/unit-tests/varname-dot-newline.mk
@@ -1,23 +1,31 @@
-# $NetBSD: varname-dot-newline.mk,v 1.4 2020/10/24 08:46:08 rillig Exp $
+# $NetBSD: varname-dot-newline.mk,v 1.6 2023/01/26 20:48:18 sjg Exp $
#
-# Tests for the special .newline variable.
+# Tests for the special .newline variable, which contains a single newline
+# character (U+000A).
+
+
+# https://austingroupbugs.net/view.php?id=1549 proposes:
+# > After all macro expansion is complete, when an escaped <newline> is
+# > found in a command line in a makefile, the command line that is executed
+# > shall contain the <backslash>, the <newline>, and the next line, except
+# > that the first character of the next line shall not be included if it is
+# > a <tab>.
#
-# Contrary to the special variable named "" that is used in expressions like
-# ${:Usome-value}, the variable ".newline" is not protected against
-# modification. Nobody exploits that though.
+# The above quote assumes that each resulting <newline> character has a "next
+# line", but that's not how the .newline variable works.
+BACKSLASH_NEWLINE:= \${.newline}
+
+
+# Check that .newline is read-only
NEWLINE:= ${.newline}
.newline= overwritten
-.if ${.newline} == ${NEWLINE}
-. info The .newline variable cannot be overwritten. Good.
-.else
-. info The .newline variable can be overwritten. Just don't do that.
+.if ${.newline} != ${NEWLINE}
+. error The .newline variable can be overwritten. It should be read-only.
.endif
-# Restore the original value.
-.newline= ${NEWLINE}
-
all:
@echo 'first${.newline}second'
+ @echo 'backslash newline: <${BACKSLASH_NEWLINE}>'
diff --git a/unit-tests/varparse-errors.exp b/unit-tests/varparse-errors.exp
index e47127447cda..2c3568e468ca 100644
--- a/unit-tests/varparse-errors.exp
+++ b/unit-tests/varparse-errors.exp
@@ -6,6 +6,20 @@ make: Bad modifier ":OX" for variable ""
make: Bad modifier ":OX" for variable ""
make: "varparse-errors.mk" line 68: Undefined variable "${:U:OX"
make: Bad modifier ":OX" for variable ""
+make: Unclosed variable expression, expecting '}' for modifier "Q" of variable "" with value ""
+make: Unclosed variable expression, expecting '}' for modifier "sh" of variable "" with value ""
+make: Unclosed variable expression, expecting '}' for modifier "tA" of variable "" with value ""
+make: Unclosed variable expression, expecting '}' for modifier "tsX" of variable "" with value ""
+make: Unclosed variable expression, expecting '}' for modifier "ts" of variable "" with value ""
+make: Unclosed variable expression, expecting '}' for modifier "ts\040" of variable "" with value ""
+make: Unclosed variable expression, expecting '}' for modifier "u" of variable "" with value ""
+make: Unclosed variable expression, expecting '}' for modifier "H" of variable "" with value "."
+make: Unclosed variable expression, expecting '}' for modifier "[1]" of variable "" with value ""
+make: Unclosed variable expression, expecting '}' for modifier "hash" of variable "" with value "b2af338b"
+make: Unclosed variable expression, expecting '}' for modifier "range" of variable "" with value "1"
+make: Unclosed variable expression, expecting '}' for modifier "_" of variable "" with value ""
+make: Unclosed variable expression, expecting '}' for modifier "gmtime" of variable "" with value "<timestamp>"
+make: Unclosed variable expression, expecting '}' for modifier "localtime" of variable "" with value "<timestamp>"
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 51a403fa898f..9174d264db0f 100644
--- a/unit-tests/varparse-errors.mk
+++ b/unit-tests/varparse-errors.mk
@@ -1,4 +1,4 @@
-# $NetBSD: varparse-errors.mk,v 1.5 2022/01/24 22:59:49 rillig Exp $
+# $NetBSD: varparse-errors.mk,v 1.7 2022/08/24 22:09:41 rillig Exp $
# Tests for parsing and evaluating all kinds of variable expressions.
#
@@ -68,4 +68,21 @@ IND= ${:OX}
_:= ${:U:OX:U${IND}} ${:U:OX:U${IND}}
#.MAKEFLAGS: -d0
-all:
+
+# Before var.c 1.032 from 2022-08-24, make complained about 'Unknown modifier'
+# or 'Bad modifier' when in fact the modifier was entirely correct, it was
+# just not delimited by either ':' or '}' but instead by '\0'.
+UNCLOSED:= ${:U:Q
+UNCLOSED:= ${:U:sh
+UNCLOSED:= ${:U:tA
+UNCLOSED:= ${:U:tsX
+UNCLOSED:= ${:U:ts
+UNCLOSED:= ${:U:ts\040
+UNCLOSED:= ${:U:u
+UNCLOSED:= ${:U:H
+UNCLOSED:= ${:U:[1]
+UNCLOSED:= ${:U:hash
+UNCLOSED:= ${:U:range
+UNCLOSED:= ${:U:_
+UNCLOSED:= ${:U:gmtime
+UNCLOSED:= ${:U:localtime