summaryrefslogtreecommitdiff
path: root/unit-tests
diff options
context:
space:
mode:
Diffstat (limited to 'unit-tests')
-rw-r--r--unit-tests/Makefile79
-rwxr-xr-xunit-tests/archive-suffix.mk6
-rw-r--r--unit-tests/archive.mk30
-rw-r--r--unit-tests/cmd-errors-lint.exp9
-rw-r--r--unit-tests/cmd-errors-lint.mk32
-rw-r--r--unit-tests/cmd-errors.exp9
-rw-r--r--unit-tests/cmd-errors.mk30
-rwxr-xr-xunit-tests/cmd-interrupt.mk4
-rw-r--r--unit-tests/cmdline-undefined.exp17
-rw-r--r--unit-tests/cmdline-undefined.mk40
-rw-r--r--unit-tests/cmdline.mk11
-rw-r--r--unit-tests/comment.mk12
-rw-r--r--unit-tests/cond-cmp-numeric-eq.exp6
-rwxr-xr-xunit-tests/cond-cmp-numeric-eq.mk15
-rw-r--r--unit-tests/cond-cmp-numeric.exp4
-rw-r--r--unit-tests/cond-cmp-numeric.mk14
-rw-r--r--unit-tests/cond-cmp-string.exp8
-rw-r--r--unit-tests/cond-cmp-string.mk30
-rwxr-xr-xunit-tests/cond-cmp-unary.exp1
-rwxr-xr-xunit-tests/cond-cmp-unary.mk17
-rw-r--r--unit-tests/cond-func-commands.mk5
-rw-r--r--unit-tests/cond-func-defined.exp5
-rw-r--r--unit-tests/cond-func-defined.mk21
-rw-r--r--unit-tests/cond-func-empty.exp6
-rw-r--r--unit-tests/cond-func-empty.mk43
-rw-r--r--unit-tests/cond-func.exp18
-rw-r--r--unit-tests/cond-func.mk72
-rw-r--r--unit-tests/cond-late.mk12
-rw-r--r--unit-tests/cond-op-and-lint.exp4
-rw-r--r--unit-tests/cond-op-and-lint.mk13
-rw-r--r--unit-tests/cond-op-not.exp5
-rw-r--r--unit-tests/cond-op-not.mk41
-rw-r--r--unit-tests/cond-op-or-lint.exp4
-rw-r--r--unit-tests/cond-op-or-lint.mk13
-rw-r--r--unit-tests/cond-op-parentheses.exp1
-rw-r--r--unit-tests/cond-op-parentheses.mk13
-rw-r--r--unit-tests/cond-op.exp17
-rw-r--r--unit-tests/cond-op.mk35
-rw-r--r--unit-tests/cond-short.mk21
-rw-r--r--unit-tests/cond-token-number.exp10
-rw-r--r--unit-tests/cond-token-number.mk28
-rw-r--r--unit-tests/cond-token-plain.mk13
-rw-r--r--unit-tests/cond-token-string.exp9
-rw-r--r--unit-tests/cond-token-string.mk33
-rw-r--r--unit-tests/cond-token-var.exp8
-rw-r--r--unit-tests/cond-token-var.mk22
-rwxr-xr-xunit-tests/cond-undef-lint.exp2
-rwxr-xr-xunit-tests/cond-undef-lint.mk6
-rw-r--r--unit-tests/cond1.exp4
-rw-r--r--unit-tests/cond1.mk7
-rw-r--r--unit-tests/dep-double-colon.mk7
-rw-r--r--unit-tests/dep-exclam.mk9
-rw-r--r--unit-tests/depsrc-ignore.mk11
-rw-r--r--unit-tests/depsrc-make.mk4
-rw-r--r--unit-tests/depsrc-optional.exp20
-rw-r--r--unit-tests/depsrc-optional.mk15
-rw-r--r--unit-tests/depsrc-precious.mk12
-rw-r--r--unit-tests/depsrc-usebefore.mk6
-rw-r--r--unit-tests/depsrc.mk4
-rw-r--r--unit-tests/deptgt-begin.exp3
-rw-r--r--unit-tests/deptgt-begin.mk36
-rw-r--r--unit-tests/deptgt-error.mk5
-rw-r--r--unit-tests/deptgt-ignore.mk5
-rw-r--r--unit-tests/deptgt-interrupt.mk6
-rw-r--r--unit-tests/deptgt-main.mk6
-rw-r--r--unit-tests/deptgt-makeflags.exp1
-rw-r--r--unit-tests/deptgt-makeflags.mk54
-rw-r--r--unit-tests/deptgt-silent.exp2
-rw-r--r--unit-tests/deptgt-silent.mk9
-rw-r--r--unit-tests/deptgt.exp8
-rw-r--r--unit-tests/deptgt.mk12
-rw-r--r--unit-tests/dir.mk5
-rw-r--r--unit-tests/directive-elif.exp18
-rw-r--r--unit-tests/directive-elif.mk68
-rw-r--r--unit-tests/directive-else.exp13
-rw-r--r--unit-tests/directive-else.mk16
-rw-r--r--unit-tests/directive-endif.mk21
-rw-r--r--unit-tests/directive-export-env.mk6
-rw-r--r--unit-tests/directive-export-gmake.mk6
-rw-r--r--unit-tests/directive-export-literal.mk6
-rw-r--r--unit-tests/directive-export.exp5
-rw-r--r--unit-tests/directive-export.mk8
-rwxr-xr-xunit-tests/directive-for.exp36
-rwxr-xr-xunit-tests/directive-for.mk12
-rw-r--r--unit-tests/directive-if-nested.exp2
-rw-r--r--unit-tests/directive-if-nested.mk25
-rw-r--r--unit-tests/directive-if.exp16
-rw-r--r--unit-tests/directive-if.mk77
-rw-r--r--unit-tests/directive-ifdef.exp1
-rw-r--r--unit-tests/directive-ifdef.mk12
-rw-r--r--unit-tests/directive-ifmake.exp13
-rw-r--r--unit-tests/directive-ifmake.mk55
-rwxr-xr-xunit-tests/directive-include.exp5
-rwxr-xr-xunit-tests/directive-include.mk7
-rw-r--r--unit-tests/directive-info.exp15
-rw-r--r--unit-tests/directive-info.mk21
-rwxr-xr-xunit-tests/directive-sinclude.mk6
-rw-r--r--unit-tests/directive-undef.exp5
-rw-r--r--unit-tests/directive-undef.mk6
-rw-r--r--unit-tests/directive-unexport-env.mk6
-rw-r--r--unit-tests/directive-unexport.exp5
-rw-r--r--unit-tests/directive-unexport.mk6
-rw-r--r--unit-tests/directive-warning.exp12
-rw-r--r--unit-tests/directive-warning.mk11
-rw-r--r--unit-tests/directive.exp13
-rw-r--r--unit-tests/directive.mk29
-rw-r--r--unit-tests/directives.exp42
-rw-r--r--unit-tests/directives.mk163
-rw-r--r--unit-tests/dollar.exp2
-rw-r--r--unit-tests/dollar.mk6
-rw-r--r--unit-tests/envfirst.mk4
-rw-r--r--unit-tests/error.exp6
-rw-r--r--unit-tests/error.mk10
-rw-r--r--unit-tests/escape.mk6
-rw-r--r--unit-tests/forloop.exp28
-rw-r--r--unit-tests/forloop.mk16
-rw-r--r--unit-tests/forsubst.mk14
-rw-r--r--unit-tests/gnode-submake.exp11
-rw-r--r--unit-tests/gnode-submake.mk42
-rw-r--r--unit-tests/include-sub.mk4
-rw-r--r--unit-tests/job-flags.exp12
-rw-r--r--unit-tests/job-flags.mk32
-rw-r--r--unit-tests/moderrs.mk4
-rw-r--r--unit-tests/modmisc.mk12
-rw-r--r--unit-tests/modts.mk5
-rw-r--r--unit-tests/modword.mk3
-rw-r--r--unit-tests/objdir-writable.exp5
-rw-r--r--unit-tests/objdir-writable.mk31
-rw-r--r--unit-tests/opt-chdir.exp5
-rw-r--r--unit-tests/opt-chdir.mk29
-rw-r--r--unit-tests/opt-debug-jobs.exp4
-rw-r--r--unit-tests/opt-debug-jobs.mk9
-rw-r--r--unit-tests/opt-ignore.mk3
-rw-r--r--unit-tests/opt-keep-going.mk3
-rw-r--r--unit-tests/opt-no-action.mk4
-rw-r--r--unit-tests/opt-query.mk4
-rw-r--r--unit-tests/opt-touch-jobs.exp4
-rw-r--r--unit-tests/opt-touch-jobs.mk30
-rw-r--r--unit-tests/opt-touch.exp3
-rw-r--r--unit-tests/opt-touch.mk21
-rw-r--r--unit-tests/opt-var-expanded.mk4
-rw-r--r--unit-tests/opt-var-literal.mk4
-rw-r--r--unit-tests/opt-warnings-as-errors.exp4
-rw-r--r--unit-tests/opt-warnings-as-errors.mk4
-rw-r--r--unit-tests/opt.exp21
-rw-r--r--unit-tests/opt.mk28
-rw-r--r--unit-tests/order.mk4
-rw-r--r--unit-tests/recursive.exp4
-rw-r--r--unit-tests/recursive.mk4
-rw-r--r--unit-tests/sh-leading-at.exp1
-rw-r--r--unit-tests/sh-leading-at.mk10
-rw-r--r--unit-tests/sh-leading-hyphen.mk7
-rw-r--r--unit-tests/sh-leading-plus.mk4
-rw-r--r--unit-tests/sh-meta-chars.mk8
-rw-r--r--unit-tests/suff-self.exp3
-rw-r--r--unit-tests/suff-self.mk11
-rw-r--r--unit-tests/use-inference.mk5
-rw-r--r--unit-tests/var-class-local.exp3
-rw-r--r--unit-tests/var-class-local.mk16
-rw-r--r--unit-tests/var-op-assign.exp4
-rw-r--r--unit-tests/var-op-assign.mk19
-rw-r--r--unit-tests/var-op-expand.exp9
-rw-r--r--unit-tests/var-op-expand.mk20
-rw-r--r--unit-tests/var-op-shell.exp6
-rw-r--r--unit-tests/var-op-shell.mk81
-rw-r--r--unit-tests/var-op-sunsh.mk4
-rw-r--r--unit-tests/vardebug.exp2
-rw-r--r--unit-tests/varmisc.mk6
-rw-r--r--unit-tests/varmod-defined.exp22
-rw-r--r--unit-tests/varmod-defined.mk18
-rw-r--r--unit-tests/varmod-exclam-shell.mk23
-rw-r--r--unit-tests/varmod-ifelse.exp8
-rw-r--r--unit-tests/varmod-ifelse.mk38
-rw-r--r--unit-tests/varmod-loop.exp8
-rw-r--r--unit-tests/varmod-loop.mk49
-rw-r--r--unit-tests/varmod-match.mk7
-rw-r--r--unit-tests/varmod-order-shuffle.mk4
-rw-r--r--unit-tests/varmod-shell.exp2
-rw-r--r--unit-tests/varmod-shell.mk28
-rw-r--r--unit-tests/varmod-subst.exp45
-rw-r--r--unit-tests/varmod-subst.mk68
-rw-r--r--unit-tests/varmod-to-abs.exp4
-rw-r--r--unit-tests/varmod-to-abs.mk21
-rw-r--r--unit-tests/varmod-to-lower.mk4
-rw-r--r--unit-tests/varmod-to-separator.mk8
-rw-r--r--unit-tests/varmod-undefined.mk3
-rw-r--r--unit-tests/varmod.exp2
-rw-r--r--unit-tests/varmod.mk11
-rwxr-xr-xunit-tests/varname-dot-shell.exp2
-rw-r--r--unit-tests/varname-empty.exp2
-rwxr-xr-xunit-tests/varname-makefile.exp1
-rwxr-xr-xunit-tests/varname-makefile.mk9
-rw-r--r--unit-tests/varname-vpath.exp11
-rw-r--r--unit-tests/varname-vpath.mk42
-rw-r--r--unit-tests/varname.exp25
-rw-r--r--unit-tests/varname.mk42
-rw-r--r--unit-tests/varparse-errors.exp1
-rw-r--r--unit-tests/varparse-errors.mk35
-rw-r--r--unit-tests/varparse-undef-partial.mk12
-rw-r--r--unit-tests/varshell.exp10
-rw-r--r--unit-tests/varshell.mk20
201 files changed, 2452 insertions, 674 deletions
diff --git a/unit-tests/Makefile b/unit-tests/Makefile
index 0940d55671ee..cca63155e868 100644
--- a/unit-tests/Makefile
+++ b/unit-tests/Makefile
@@ -1,6 +1,6 @@
-# $Id: Makefile,v 1.107 2020/11/02 00:40:25 sjg Exp $
+# $Id: Makefile,v 1.115 2020/11/18 04:01:07 sjg Exp $
#
-# $NetBSD: Makefile,v 1.181 2020/11/01 19:02:22 rillig Exp $
+# $NetBSD: Makefile,v 1.206 2020/11/18 01:12:00 sjg Exp $
#
# Unit tests for make(1)
#
@@ -36,8 +36,11 @@
# src/tests/usr.bin/make/t_make.sh as well.
#TESTS+= archive
TESTS+= archive-suffix
+TESTS+= cmd-errors
+TESTS+= cmd-errors-lint
TESTS+= cmd-interrupt
TESTS+= cmdline
+TESTS+= cmdline-undefined
TESTS+= comment
TESTS+= cond-cmp-numeric
TESTS+= cond-cmp-numeric-eq
@@ -58,8 +61,10 @@ TESTS+= cond-func-target
TESTS+= cond-late
TESTS+= cond-op
TESTS+= cond-op-and
+TESTS+= cond-op-and-lint
TESTS+= cond-op-not
TESTS+= cond-op-or
+TESTS+= cond-op-or-lint
TESTS+= cond-op-parentheses
TESTS+= cond-short
TESTS+= cond-token-number
@@ -144,6 +149,7 @@ TESTS+= directive-for
TESTS+= directive-for-generating-endif
TESTS+= directive-hyphen-include
TESTS+= directive-if
+TESTS+= directive-if-nested
TESTS+= directive-ifdef
TESTS+= directive-ifmake
TESTS+= directive-ifndef
@@ -156,7 +162,6 @@ TESTS+= directive-undef
TESTS+= directive-unexport
TESTS+= directive-unexport-env
TESTS+= directive-warning
-TESTS+= directives
TESTS+= dollar
TESTS+= doterror
TESTS+= dotwait
@@ -169,9 +174,11 @@ TESTS+= export-env
TESTS+= export-variants
TESTS+= forloop
TESTS+= forsubst
+TESTS+= gnode-submake
TESTS+= hanoi-include
TESTS+= impsrc
TESTS+= include-main
+TESTS+= job-flags
#TESTS+= job-output-long-lines
TESTS+= lint
TESTS+= make-exported
@@ -180,6 +187,7 @@ TESTS+= modmatch
TESTS+= modmisc
TESTS+= modts
TESTS+= modword
+TESTS+= objdir-writable
TESTS+= opt
TESTS+= opt-backwards
TESTS+= opt-chdir
@@ -223,6 +231,7 @@ TESTS+= opt-query
TESTS+= opt-raw
TESTS+= opt-silent
TESTS+= opt-touch
+TESTS+= opt-touch-jobs
TESTS+= opt-tracefile
TESTS+= opt-var-expanded
TESTS+= opt-var-literal
@@ -248,7 +257,9 @@ TESTS+= sh-multi-line
TESTS+= sh-single-line
TESTS+= shell-csh
TESTS+= shell-custom
+.if exists(/bin/ksh)
TESTS+= shell-ksh
+.endif
TESTS+= shell-sh
TESTS+= suff-add-later
TESTS+= suff-clear-regular
@@ -256,6 +267,7 @@ TESTS+= suff-clear-single
TESTS+= suff-lookup
TESTS+= suff-main
TESTS+= suff-rebuild
+TESTS+= suff-self
TESTS+= suff-transform-endless
TESTS+= suff-transform-expand
TESTS+= suff-transform-select
@@ -366,38 +378,54 @@ TESTS+= varname-makeflags
TESTS+= varname-pwd
TESTS+= varname-vpath
TESTS+= varparse-dynamic
+TESTS+= varparse-errors
TESTS+= varparse-mod
TESTS+= varparse-undef-partial
TESTS+= varquote
-TESTS+= varshell
+# Ideas for more tests:
+# char-0020-space.mk
+# char-005C-backslash.mk
+# escape-cond-str.mk
+# escape-cond-func-arg.mk
+# escape-cond-func-arg.mk
+# escape-varmod.mk
+# escape-varmod-define.mk
+# escape-varmod-match.mk
+# escape-varname.mk
+# escape-varassign-varname.mk
+# escape-varassign-varname-cmdline.mk
+# escape-varassign-value.mk
+# escape-varassign-value-cmdline.mk
+# escape-dependency-source.mk
+# escape-dependency-target.mk
+# escape-for-varname.mk
+# escape-for-item.mk
+# posix-*.mk (see posix.mk and posix1.mk)
+
+.if ${.OBJDIR} != ${.CURDIR}
+RO_OBJDIR:= ${.OBJDIR}/roobj
+.else
+RO_OBJDIR:= ${TMPDIR:U/tmp}/roobj
+.endif
# Additional environment variables for some of the tests.
# The base environment is -i PATH="$PATH".
+ENV.depsrc-optional+= TZ=UTC
ENV.envfirst= FROM_ENV=value-from-env
+ENV.objdir-writable+= RO_OBJDIR=${RO_OBJDIR}
ENV.varmisc= FROM_ENV=env
ENV.varmisc+= FROM_ENV_BEFORE=env
ENV.varmisc+= FROM_ENV_AFTER=env
ENV.varmod-localtime+= TZ=Europe/Berlin
+ENV.varname-vpath+= VPATH=varname-vpath.dir:varname-vpath.dir2
# Override make flags for some of the tests; default is -k.
# If possible, write ".MAKEFLAGS: -dv" in the test .mk file instead of
# settings FLAGS.test=-dv here, since that is closer to the test code.
FLAGS.cond-func-make= via-cmdline
FLAGS.directive-ifmake= first second
-FLAGS.doterror= # none
-FLAGS.envfirst= -e
-FLAGS.export= # none
-FLAGS.opt-ignore= -i
-FLAGS.opt-keep-going= -k
-FLAGS.opt-no-action= -n
-FLAGS.opt-query= -q
-FLAGS.opt-var-expanded= -v VAR -v VALUE
-FLAGS.opt-var-literal= -V VAR -V VALUE
-FLAGS.opt-warnings-as-errors= -W
-FLAGS.order= -j1
-FLAGS.recursive= -dL
-FLAGS.sh-leading-plus= -n
-FLAGS.varname-empty= -dv '$${:U}=cmdline-u' '=cmline-plain'
+FLAGS.doterror= # none, especially not -k
+FLAGS.varname-empty= -dv '$${:U}=cmdline-u' '=cmdline-plain'
# Some tests need extra postprocessing.
SED_CMDS.export= \
@@ -406,6 +434,9 @@ SED_CMDS.export= \
.for t in export-all export-env
SED_CMDS.$t= ${SED_CMDS.export}
.endfor
+SED_CMDS.directive-export-gmake= \
+ ${:D dash is a pain } \
+ -e /non-zero/d
SED_CMDS.job-output-long-lines= \
${:D Job separators on their own line are ok. } \
-e '/^--- job-[ab] ---$$/d' \
@@ -417,6 +448,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.objdir-writable= -e 's,${RO_OBJDIR},OBJDIR/roobj,g'
SED_CMDS.opt-debug-graph1= \
-e 's,${.CURDIR},CURDIR,'
SED_CMDS.opt-debug-graph1+= \
@@ -428,11 +460,14 @@ SED_CMDS.opt-debug-jobs+= -e 's,Process [0-9][0-9]*,Process <pid>,'
SED_CMDS.opt-debug-jobs+= -e 's,JobFinish: [0-9][0-9]*,JobFinish: <pid>,'
# The "-q" may be there or not, see jobs.c, variable shells.
SED_CMDS.opt-debug-jobs+= -e 's,^\(.Command: sh\) -q,\1,'
+SED_CMDS.var-op-shell+= -e 's,^${.SHELL:T}: ,,'
+SED_CMDS.var-op-shell+= -e '/command/{ s,^[1-9]: ,,;s,No such.*,not found,; }'
+SED_CMDS.vardebug= \
+ ${:D canonicalize .SHELL } \
+ -e 's,${.SHELL},</path/to/shell>,'
SED_CMDS.varmod-subst-regex+= \
-e 's,\(Regex compilation error:\).*,\1 (details omitted),'
SED_CMDS.varmod-edge+= -e 's, line [0-9]*:, line omitted:,'
-SED_CMDS.varshell+= -e 's,^${.SHELL:T}: ,,'
-SED_CMDS.varshell+= -e '/command/s,No such.*,not found,'
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'
@@ -442,7 +477,7 @@ SED_CMDS.varname-dot-shell+= -e 's,\[/[^] ]*\],[(details omitted)],g'
# Some tests need an additional round of postprocessing.
POSTPROC.deptgt-suffixes= \
${TOOL_SED} -n -e '/^\#\*\*\* Suffixes/,/^\#\*/p'
-POSTPROC.varname= ${TOOL_SED} -n -e '/^MAGIC/p' -e '/^ORDER_/p'
+POSTPROC.gnode-submake= awk '/Input graph/, /^$$/'
POSTPROC.varname-empty= ${TOOL_SED} -n -e '/^Var_Set/p' -e '/^out:/p'
# Some tests reuse other tests, which makes them unnecessarily fragile.
@@ -519,6 +554,8 @@ MAKE_TEST_ENV?= MALLOC_OPTIONS="JA" # for jemalloc
# always pretend .MAKE was called 'make'
_SED_CMDS+= -e 's,^${TEST_MAKE:T:S,.,\\.,g}[][0-9]*:,make:,'
_SED_CMDS+= -e 's,${TEST_MAKE:S,.,\\.,g},make,'
+_SED_CMDS+= -e 's,${TEST_MAKE:T:S,.,\\.,g}[][0-9]* warning,make warning,'
+_SED_CMDS+= -e 's,^usage: ${TEST_MAKE:T:S,.,\\.,g} ,usage: make ,'
# replace anything after 'stopped in' with unit-tests
_SED_CMDS+= -e '/stopped/s, /.*, unit-tests,'
# strip ${.CURDIR}/ from the output
diff --git a/unit-tests/archive-suffix.mk b/unit-tests/archive-suffix.mk
index 9f7fa219c667..a216fd2d3c6a 100755
--- a/unit-tests/archive-suffix.mk
+++ b/unit-tests/archive-suffix.mk
@@ -1,11 +1,11 @@
-# $NetBSD: archive-suffix.mk,v 1.1 2020/08/29 14:47:26 rillig Exp $
+# $NetBSD: archive-suffix.mk,v 1.3 2020/11/15 14:07:53 rillig Exp $
#
# Between 2020-08-23 and 2020-08-30, the below code produced an assertion
-# failure in Var_Set_with_flags, triggered by Compat_Make, when setting the
+# failure in Var_SetWithFlags, triggered by Compat_Make, when setting the
# .IMPSRC of an archive node to its .TARGET.
#
# The code assumed that the .TARGET variable of every node would be set, but
-# but that is not guaranteed.
+# that is not guaranteed.
#
# Between 2016-03-15 and 2016-03-16 the behavior of the below code changed.
# Until 2016-03-15, it remade the target, starting with 2016-03-16 it says
diff --git a/unit-tests/archive.mk b/unit-tests/archive.mk
index 2f91005f988c..f8815cf40a40 100644
--- a/unit-tests/archive.mk
+++ b/unit-tests/archive.mk
@@ -1,18 +1,15 @@
-# $NetBSD: archive.mk,v 1.10 2020/10/09 06:44:42 rillig Exp $
+# $NetBSD: archive.mk,v 1.11 2020/11/15 14:07:53 rillig Exp $
#
# Very basic demonstration of handling archives, based on the description
# in PSD.doc/tutorial.ms.
#
# This test aims at covering the code, not at being an introduction to
-# archive handling. That's why it is more complicated and detailed than
-# strictly necessary.
+# archive handling. That's why it deviates from the tutorial style of
+# several other tests.
ARCHIVE= libprog.a
FILES= archive.mk modmisc.mk varmisc.mk
-MAKE_CMD= ${.MAKE} -f ${MAKEFILE}
-RUN?= @set -eu;
-
all:
.if ${.PARSEDIR:tA} != ${.CURDIR:tA}
@cd ${MAKEFILE:H} && cp ${FILES} [at]*.mk ${.CURDIR}
@@ -20,13 +17,13 @@ all:
# The following targets create and remove files. The filesystem cache in
# dir.c would probably not handle this correctly, therefore each of the
# targets is run in its separate sub-make.
- ${RUN} ${MAKE_CMD} remove-archive
- ${RUN} ${MAKE_CMD} create-archive
- ${RUN} ${MAKE_CMD} list-archive
- ${RUN} ${MAKE_CMD} list-archive-wildcard
- ${RUN} ${MAKE_CMD} depend-on-existing-member
- ${RUN} ${MAKE_CMD} depend-on-nonexistent-member
- ${RUN} ${MAKE_CMD} remove-archive
+ @${MAKE} -f ${MAKEFILE} remove-archive
+ @${MAKE} -f ${MAKEFILE} create-archive
+ @${MAKE} -f ${MAKEFILE} list-archive
+ @${MAKE} -f ${MAKEFILE} list-archive-wildcard
+ @${MAKE} -f ${MAKEFILE} depend-on-existing-member
+ @${MAKE} -f ${MAKEFILE} depend-on-nonexistent-member
+ @${MAKE} -f ${MAKEFILE} remove-archive
create-archive: ${ARCHIVE} pre post
@@ -43,15 +40,16 @@ list-archive: ${ARCHIVE} pre post
# XXX: I had expected that this dependency would select all *.mk files from
# the archive. Instead, the globbing is done in the current directory.
+#
# To prevent an overly long file list, the pattern is restricted to [at]*.mk.
list-archive-wildcard: ${ARCHIVE}([at]*.mk) pre post
- ${RUN} printf '%s\n' ${.ALLSRC:O:@member@${.TARGET:Q}': '${member:Q}@}
+ @printf '%s\n' ${.ALLSRC:O:@member@${.TARGET:Q}': '${member:Q}@}
depend-on-existing-member: ${ARCHIVE}(archive.mk) pre post
- ${RUN} echo $@
+ @echo $@
depend-on-nonexistent-member: ${ARCHIVE}(nonexistent.mk) pre post
- ${RUN} echo $@
+ @echo $@
remove-archive: pre post
rm -f ${ARCHIVE}
diff --git a/unit-tests/cmd-errors-lint.exp b/unit-tests/cmd-errors-lint.exp
new file mode 100644
index 000000000000..09924c538de0
--- /dev/null
+++ b/unit-tests/cmd-errors-lint.exp
@@ -0,0 +1,9 @@
+: undefined
+make: Unclosed variable "UNCLOSED"
+: unclosed-variable
+make: Unclosed variable expression (expecting '}') for "UNCLOSED"
+: unclosed-modifier
+make: Unknown modifier 'Z'
+: unknown-modifier
+: end
+exit status 2
diff --git a/unit-tests/cmd-errors-lint.mk b/unit-tests/cmd-errors-lint.mk
new file mode 100644
index 000000000000..371e12af0f4f
--- /dev/null
+++ b/unit-tests/cmd-errors-lint.mk
@@ -0,0 +1,32 @@
+# $NetBSD: cmd-errors-lint.mk,v 1.1 2020/11/02 20:43:27 rillig Exp $
+#
+# Demonstrate how errors in variable expansions affect whether the commands
+# are actually executed.
+
+.MAKEFLAGS: -dL
+
+all: undefined unclosed-variable unclosed-modifier unknown-modifier end
+
+# Undefined variables are not an error. They expand to empty strings.
+undefined:
+ : $@ ${UNDEFINED}
+
+# XXX: As of 2020-11-01, this obvious syntax error is not detected.
+# XXX: As of 2020-11-01, this command is executed even though it contains
+# parse errors.
+unclosed-variable:
+ : $@ ${UNCLOSED
+
+# XXX: As of 2020-11-01, this obvious syntax error is not detected.
+# XXX: As of 2020-11-01, this command is executed even though it contains
+# parse errors.
+unclosed-modifier:
+ : $@ ${UNCLOSED:
+
+# XXX: As of 2020-11-01, this command is executed even though it contains
+# parse errors.
+unknown-modifier:
+ : $@ ${UNKNOWN:Z}
+
+end:
+ : $@
diff --git a/unit-tests/cmd-errors.exp b/unit-tests/cmd-errors.exp
new file mode 100644
index 000000000000..6d9c6bb7f890
--- /dev/null
+++ b/unit-tests/cmd-errors.exp
@@ -0,0 +1,9 @@
+: undefined eol
+make: Unclosed variable "UNCLOSED"
+: unclosed-variable
+make: Unclosed variable expression (expecting '}') for "UNCLOSED"
+: unclosed-modifier
+make: Unknown modifier 'Z'
+: unknown-modifier eol
+: end eol
+exit status 0
diff --git a/unit-tests/cmd-errors.mk b/unit-tests/cmd-errors.mk
new file mode 100644
index 000000000000..5ad4be311873
--- /dev/null
+++ b/unit-tests/cmd-errors.mk
@@ -0,0 +1,30 @@
+# $NetBSD: cmd-errors.mk,v 1.3 2020/11/09 23:36:34 rillig Exp $
+#
+# Demonstrate how errors in variable expansions affect whether the commands
+# are actually executed.
+
+all: undefined unclosed-variable unclosed-modifier unknown-modifier end
+
+# Undefined variables are not an error. They expand to empty strings.
+undefined:
+ : $@ ${UNDEFINED} eol
+
+# XXX: As of 2020-11-01, this command is executed even though it contains
+# parse errors.
+unclosed-variable:
+ : $@ ${UNCLOSED
+
+# XXX: As of 2020-11-01, this command is executed even though it contains
+# parse errors.
+unclosed-modifier:
+ : $@ ${UNCLOSED:
+
+# XXX: As of 2020-11-01, this command is executed even though it contains
+# parse errors.
+unknown-modifier:
+ : $@ ${UNKNOWN:Z} eol
+
+end:
+ : $@ eol
+
+# XXX: As of 2020-11-02, despite the parse errors, the exit status is 0.
diff --git a/unit-tests/cmd-interrupt.mk b/unit-tests/cmd-interrupt.mk
index 033f3307bd2e..fa0d85fc9063 100755
--- a/unit-tests/cmd-interrupt.mk
+++ b/unit-tests/cmd-interrupt.mk
@@ -1,4 +1,4 @@
-# $NetBSD: cmd-interrupt.mk,v 1.2 2020/08/28 18:16:22 rillig Exp $
+# $NetBSD: cmd-interrupt.mk,v 1.3 2020/11/15 14:07:53 rillig Exp $
#
# Tests for interrupting a command.
#
@@ -22,7 +22,7 @@ all: clean-before interrupt-ordinary interrupt-phony interrupt-precious clean-af
clean-before clean-after: .PHONY
@rm -f cmd-interrupt-ordinary cmd-interrupt-phony cmd-interrupt-precious
-interrupt-ordinary: .PHONY
+interrupt-ordinary:
@${.MAKE} ${MAKEFLAGS} -f ${MAKEFILE} cmd-interrupt-ordinary || true
# The ././ is necessary to work around the file cache.
@echo ${.TARGET}: ${exists(././cmd-interrupt-ordinary) :? error : ok }
diff --git a/unit-tests/cmdline-undefined.exp b/unit-tests/cmdline-undefined.exp
new file mode 100644
index 000000000000..977ceee6dbf5
--- /dev/null
+++ b/unit-tests/cmdline-undefined.exp
@@ -0,0 +1,17 @@
+The = assignment operator
+make: "cmdline-undefined.mk" line 29: From the command line: Undefined is .
+make: "cmdline-undefined.mk" line 30: From .MAKEFLAGS '=': Undefined is .
+make: "cmdline-undefined.mk" line 31: From .MAKEFLAGS ':=': Undefined is .
+make: "cmdline-undefined.mk" line 35: From the command line: Undefined is now defined.
+make: "cmdline-undefined.mk" line 36: From .MAKEFLAGS '=': Undefined is now defined.
+make: "cmdline-undefined.mk" line 37: From .MAKEFLAGS ':=': Undefined is now defined.
+
+The := assignment operator
+make: "cmdline-undefined.mk" line 29: From the command line: Undefined is .
+make: "cmdline-undefined.mk" line 30: From .MAKEFLAGS '=': Undefined is .
+make: "cmdline-undefined.mk" line 31: From .MAKEFLAGS ':=': Undefined is .
+make: "cmdline-undefined.mk" line 35: From the command line: Undefined is now defined.
+make: "cmdline-undefined.mk" line 36: From .MAKEFLAGS '=': Undefined is now defined.
+make: "cmdline-undefined.mk" line 37: From .MAKEFLAGS ':=': Undefined is now defined.
+
+exit status 0
diff --git a/unit-tests/cmdline-undefined.mk b/unit-tests/cmdline-undefined.mk
new file mode 100644
index 000000000000..5a3375cbbfb8
--- /dev/null
+++ b/unit-tests/cmdline-undefined.mk
@@ -0,0 +1,40 @@
+# $NetBSD: cmdline-undefined.mk,v 1.2 2020/11/04 04:49:33 rillig Exp $
+#
+# Tests for undefined variable expressions in the command line.
+
+all:
+ # When the command line is parsed, variable assignments using the
+ # '=' assignment operator do get their variable name expanded
+ # (which probably occurs rarely in practice, if at all), but their
+ # variable value is not expanded, as usual.
+ #
+ @echo 'The = assignment operator'
+ @${.MAKE} -f ${MAKEFILE} print-undefined \
+ CMDLINE='Undefined is $${UNDEFINED}.'
+ @echo
+
+ # The interesting case is using the ':=' assignment operator, which
+ # expands its right-hand side. But only those variables that are
+ # defined.
+ @echo 'The := assignment operator'
+ @${.MAKE} -f ${MAKEFILE} print-undefined \
+ CMDLINE:='Undefined is $${UNDEFINED}.'
+ @echo
+
+.if make(print-undefined)
+
+.MAKEFLAGS: MAKEFLAGS_ASSIGN='Undefined is $${UNDEFINED}.'
+.MAKEFLAGS: MAKEFLAGS_SUBST:='Undefined is $${UNDEFINED}.'
+
+.info From the command line: ${CMDLINE}
+.info From .MAKEFLAGS '=': ${MAKEFLAGS_ASSIGN}
+.info From .MAKEFLAGS ':=': ${MAKEFLAGS_SUBST}
+
+UNDEFINED?= now defined
+
+.info From the command line: ${CMDLINE}
+.info From .MAKEFLAGS '=': ${MAKEFLAGS_ASSIGN}
+.info From .MAKEFLAGS ':=': ${MAKEFLAGS_SUBST}
+
+print-undefined:
+.endif
diff --git a/unit-tests/cmdline.mk b/unit-tests/cmdline.mk
index c12c31220cb5..cd88cead4558 100644
--- a/unit-tests/cmdline.mk
+++ b/unit-tests/cmdline.mk
@@ -1,8 +1,7 @@
-# $NetBSD: cmdline.mk,v 1.1 2020/07/28 22:44:44 rillig Exp $
+# $NetBSD: cmdline.mk,v 1.2 2020/11/15 14:07:53 rillig Exp $
#
# Tests for command line parsing and related special variables.
-RUN?= @set -eu;
TMPBASE?= /tmp
SUB1= a7b41170-53f8-4cc2-bc5c-e4c3dd93ec45 # just a random UUID
SUB2= 6a8899d2-d227-4b55-9b6b-f3c8eeb83fd5 # just a random UUID
@@ -14,14 +13,14 @@ all: prepare-dirs
all: makeobjdir-direct makeobjdir-indirect
prepare-dirs:
- ${RUN} rm -rf ${DIR2} ${DIR12}
- ${RUN} mkdir -p ${DIR2} ${DIR12}
+ @rm -rf ${DIR2} ${DIR12}
+ @mkdir -p ${DIR2} ${DIR12}
# The .OBJDIR can be set via the MAKEOBJDIR command line variable.
# It must be a command line variable; an environment variable would not work.
makeobjdir-direct:
@echo $@:
- ${RUN} ${MAKE_CMD} MAKEOBJDIR=${DIR2} show-objdir
+ @${MAKE_CMD} MAKEOBJDIR=${DIR2} show-objdir
# The .OBJDIR can be set via the MAKEOBJDIR command line variable,
# and that variable could even contain the usual modifiers.
@@ -31,7 +30,7 @@ makeobjdir-direct:
# see MAKE_CMD.
makeobjdir-indirect:
@echo $@:
- ${RUN} ${MAKE_CMD} MAKEOBJDIR='$${TMPBASE}/$${SUB2}' show-objdir
+ @${MAKE_CMD} MAKEOBJDIR='$${TMPBASE}/$${SUB2}' show-objdir
show-objdir:
@echo $@: ${.OBJDIR:Q}
diff --git a/unit-tests/comment.mk b/unit-tests/comment.mk
index 1cdcfcdd86ba..d4fb041104a7 100644
--- a/unit-tests/comment.mk
+++ b/unit-tests/comment.mk
@@ -1,4 +1,4 @@
-# $NetBSD: comment.mk,v 1.2 2020/09/07 19:17:36 rillig Exp $
+# $NetBSD: comment.mk,v 1.3 2020/11/15 14:07:53 rillig Exp $
#
# Demonstrate how comments are written in makefiles.
@@ -12,7 +12,7 @@ that \
goes \
on and on.
- # Comments can be indented, but that is rather unusual.
+ # Comments can be indented with spaces, but that is rather unusual.
# Comments can be indented with a tab.
# These are not shell commands, they are just makefile comments.
@@ -21,6 +21,8 @@ on and on.
.endif # And after the closing directive.
VAR= # This comment makes the variable value empty.
+ # ParseGetLine removes any whitespace before the
+ # comment.
.if ${VAR} != ""
. error
.endif
@@ -35,7 +37,9 @@ VAR= value
. error
.endif
-# This is NOT an escaped comment due to the double backslashes \\
+# This comment ends with 2 backslashes. An even number of backslashes does
+# not count as a line continuation, therefore the variable assignment that
+# follows is actively interpreted. \\
VAR= not part of the comment
.if ${VAR} != "not part of the comment"
. error
@@ -55,7 +59,7 @@ WORDS= ${VAR:[#]} [#
. error
.endif
-# An odd number of comment signs makes a line continuation, \\\
+# An odd number of backslashes makes a line continuation, \\\
no matter if it is 3 or 5 \\\\\
or 9 backslashes. \\\\\\\\\
This is the last line of the comment.
diff --git a/unit-tests/cond-cmp-numeric-eq.exp b/unit-tests/cond-cmp-numeric-eq.exp
index 1f12e858a66c..64e383ef32a2 100644
--- a/unit-tests/cond-cmp-numeric-eq.exp
+++ b/unit-tests/cond-cmp-numeric-eq.exp
@@ -1,6 +1,6 @@
-make: "cond-cmp-numeric-eq.mk" line 54: warning: Unknown operator
-make: "cond-cmp-numeric-eq.mk" line 54: Malformed conditional (!(12345 = 12345))
-make: "cond-cmp-numeric-eq.mk" line 61: Malformed conditional (!(12345 === 12345))
+make: "cond-cmp-numeric-eq.mk" line 67: warning: Unknown operator
+make: "cond-cmp-numeric-eq.mk" line 67: Malformed conditional (!(12345 = 12345))
+make: "cond-cmp-numeric-eq.mk" line 74: Malformed conditional (!(12345 === 12345))
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/unit-tests/cond-cmp-numeric-eq.mk b/unit-tests/cond-cmp-numeric-eq.mk
index 0e77d364ac94..c6b39876e75e 100755
--- a/unit-tests/cond-cmp-numeric-eq.mk
+++ b/unit-tests/cond-cmp-numeric-eq.mk
@@ -1,4 +1,4 @@
-# $NetBSD: cond-cmp-numeric-eq.mk,v 1.4 2020/10/24 08:46:08 rillig Exp $
+# $NetBSD: cond-cmp-numeric-eq.mk,v 1.5 2020/11/08 21:47:59 rillig Exp $
#
# Tests for numeric comparisons with the == operator in .if conditions.
@@ -49,6 +49,19 @@
. error
.endif
+# Because an IEEE 754 double can only hold integers with a mantissa of 53
+# bits, these two numbers are considered the same. The 993 is rounded down
+# to the 992.
+.if 9007199254740993 == 9007199254740992
+.else
+. error
+.endif
+# The 995 is rounded up, the 997 is rounded down.
+.if 9007199254740995 == 9007199254740997
+.else
+. error Probably a misconfiguration in the floating point environment, \
+ or maybe a machine without IEEE 754 floating point support.
+.endif
# There is no = operator for numbers.
.if !(12345 = 12345)
diff --git a/unit-tests/cond-cmp-numeric.exp b/unit-tests/cond-cmp-numeric.exp
index 67d882e0c628..578d53228f6e 100644
--- a/unit-tests/cond-cmp-numeric.exp
+++ b/unit-tests/cond-cmp-numeric.exp
@@ -6,6 +6,10 @@ make: "cond-cmp-numeric.mk" line 16: warning: String comparison operator must be
make: "cond-cmp-numeric.mk" line 16: Malformed conditional (${:UNaN} > NaN)
CondParser_Eval: !(${:UNaN} == NaN)
lhs = "NaN", rhs = "NaN", op = ==
+CondParser_Eval: 123 ! 123
+lhs = 123.000000, rhs = 123.000000, op = !
+make: "cond-cmp-numeric.mk" line 34: warning: Unknown operator
+make: "cond-cmp-numeric.mk" line 34: Malformed conditional (123 ! 123)
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 67358ddaf86b..b1ec3e719d47 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.3 2020/09/12 18:01:51 rillig Exp $
+# $NetBSD: cond-cmp-numeric.mk,v 1.4 2020/11/08 22:56:16 rillig Exp $
#
# Tests for numeric comparisons in .if conditions.
@@ -25,5 +25,17 @@
. error
.endif
+# The parsing code in CondParser_Comparison only performs a light check on
+# whether the operator is valid, leaving the rest of the work to the
+# evaluation functions EvalCompareNum and EvalCompareStr. Ensure that this
+# parse error is properly reported.
+#
+# XXX: The warning message does not mention the actual operator.
+.if 123 ! 123
+. error
+.else
+. error
+.endif
+
all:
@:;
diff --git a/unit-tests/cond-cmp-string.exp b/unit-tests/cond-cmp-string.exp
index 735b7cda4430..6e6c218267bc 100644
--- a/unit-tests/cond-cmp-string.exp
+++ b/unit-tests/cond-cmp-string.exp
@@ -1,8 +1,8 @@
make: "cond-cmp-string.mk" line 18: Malformed conditional (str != str)
-make: "cond-cmp-string.mk" line 37: Malformed conditional ("string" != "str""ing")
-make: "cond-cmp-string.mk" line 42: warning: String comparison operator must be either == or !=
-make: "cond-cmp-string.mk" line 42: Malformed conditional (!("value" = "value"))
-make: "cond-cmp-string.mk" line 49: Malformed conditional (!("value" === "value"))
+make: "cond-cmp-string.mk" line 42: Malformed conditional ("string" != "str""ing")
+make: "cond-cmp-string.mk" line 49: warning: String comparison operator must be either == or !=
+make: "cond-cmp-string.mk" line 49: Malformed conditional (!("value" = "value"))
+make: "cond-cmp-string.mk" line 56: Malformed conditional (!("value" === "value"))
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/unit-tests/cond-cmp-string.mk b/unit-tests/cond-cmp-string.mk
index 6af457925e97..6b5cba83bef7 100644
--- a/unit-tests/cond-cmp-string.mk
+++ b/unit-tests/cond-cmp-string.mk
@@ -1,4 +1,4 @@
-# $NetBSD: cond-cmp-string.mk,v 1.11 2020/10/30 14:53:31 rillig Exp $
+# $NetBSD: cond-cmp-string.mk,v 1.13 2020/11/15 14:07:53 rillig Exp $
#
# Tests for string comparisons in .if conditions.
@@ -19,9 +19,14 @@
. error
.endif
-# The left-hand side of the comparison requires a defined variable.
-# The variable named "" is not defined, but applying the :U modifier to it
-# makes it "kind of defined" (see VAR_KEEP). Therefore it is ok here.
+# The left-hand side of the comparison requires that any variable expression
+# is defined.
+#
+# The variable named "" is never defined, nevertheless it can be used as a
+# starting point for variable expressions. Applying the :U modifier to such
+# an undefined expression turns it into a defined expression.
+#
+# See ApplyModifier_Defined and VEF_DEF.
.if ${:Ustr} != "str"
. error
.endif
@@ -33,9 +38,11 @@
.endif
# It is not possible to concatenate two string literals to form a single
-# string.
+# string. In C, Python and the shell this is possible, but not in make.
.if "string" != "str""ing"
. error
+.else
+. error
.endif
# There is no = operator for strings.
@@ -88,3 +95,16 @@
.if ${:U word } != " ${:Uword} "
. error
.endif
+
+# If at least one side of the comparison is a string literal, the string
+# comparison is performed.
+.if 12345 != "12345"
+. error
+.endif
+
+# If at least one side of the comparison is a string literal, the string
+# comparison is performed. The ".0" in the left-hand side makes the two
+# sides of the equation unequal.
+.if 12345.0 == "12345"
+. error
+.endif
diff --git a/unit-tests/cond-cmp-unary.exp b/unit-tests/cond-cmp-unary.exp
index 39a9383953dd..89f90dc1651f 100755
--- a/unit-tests/cond-cmp-unary.exp
+++ b/unit-tests/cond-cmp-unary.exp
@@ -1 +1,2 @@
+make: "cond-cmp-unary.mk" line 53: This is only reached because of a bug in EvalNotEmpty.
exit status 0
diff --git a/unit-tests/cond-cmp-unary.mk b/unit-tests/cond-cmp-unary.mk
index 88ce79bf1a99..168de0f30e3f 100755
--- a/unit-tests/cond-cmp-unary.mk
+++ b/unit-tests/cond-cmp-unary.mk
@@ -1,4 +1,4 @@
-# $NetBSD: cond-cmp-unary.mk,v 1.1 2020/09/14 06:22:59 rillig Exp $
+# $NetBSD: cond-cmp-unary.mk,v 1.2 2020/11/11 07:30:11 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,
@@ -25,6 +25,9 @@
.endif
# The empty string may come from a variable expression.
+#
+# XXX: As of 2020-11-11, this empty string is interpreted "as a number" in
+# EvalNotEmpty, which is plain wrong. The bug is in TryParseNumber.
.if ${:U}
. error
.endif
@@ -40,4 +43,16 @@
. error
.endif
+# A string of whitespace should evaluate to false.
+#
+# XXX: As of 2020-11-11, the implementation in EvalNotEmpty does not skip
+# whitespace before testing for the end. This was probably an oversight in
+# a commit from 1992-04-15 saying "A variable is empty when it just contains
+# spaces".
+.if ${:U }
+. info This is only reached because of a bug in EvalNotEmpty.
+.else
+. error
+.endif
+
all: # nothing
diff --git a/unit-tests/cond-func-commands.mk b/unit-tests/cond-func-commands.mk
index c6e1724c72f3..e127a8ebdc03 100644
--- a/unit-tests/cond-func-commands.mk
+++ b/unit-tests/cond-func-commands.mk
@@ -1,10 +1,11 @@
-# $NetBSD: cond-func-commands.mk,v 1.4 2020/10/24 08:46:08 rillig Exp $
+# $NetBSD: cond-func-commands.mk,v 1.5 2020/11/15 14:07:53 rillig Exp $
#
# Tests for the commands() function in .if conditions.
.MAIN: all
-# The target "target" does not exist yet, therefore it cannot have commands.
+# At this point, the target 'target' does not exist yet, therefore it cannot
+# have commands. Sounds obvious, but good to know that it is really so.
.if commands(target)
. error
.endif
diff --git a/unit-tests/cond-func-defined.exp b/unit-tests/cond-func-defined.exp
index 70c6342a02c3..caf66e39938f 100644
--- a/unit-tests/cond-func-defined.exp
+++ b/unit-tests/cond-func-defined.exp
@@ -1,5 +1,10 @@
make: "cond-func-defined.mk" line 23: warning: Missing closing parenthesis for defined()
make: "cond-func-defined.mk" line 23: Malformed conditional (!defined(A B))
+make: "cond-func-defined.mk" line 33: warning: Missing closing parenthesis for defined()
+make: "cond-func-defined.mk" line 33: Malformed conditional (defined(DEF)
+make: "cond-func-defined.mk" line 45: In .for loops, variable expressions for the loop variables are
+make: "cond-func-defined.mk" line 46: substituted at evaluation time. There is no actual variable
+make: "cond-func-defined.mk" line 47: involved, even if it feels like it.
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/unit-tests/cond-func-defined.mk b/unit-tests/cond-func-defined.mk
index ec4feae05839..2aa49ccbf147 100644
--- a/unit-tests/cond-func-defined.mk
+++ b/unit-tests/cond-func-defined.mk
@@ -1,4 +1,4 @@
-# $NetBSD: cond-func-defined.mk,v 1.5 2020/10/24 08:46:08 rillig Exp $
+# $NetBSD: cond-func-defined.mk,v 1.7 2020/11/15 14:07:53 rillig Exp $
#
# Tests for the defined() function in .if conditions.
@@ -29,5 +29,24 @@ ${:UA B}= variable name with spaces
. error
.endif
+# Parse error: missing closing parenthesis; see ParseFuncArg.
+.if defined(DEF
+. error
+.else
+. error
+.endif
+
+# Variables from .for loops are not defined.
+# See directive-for.mk for more details.
+.for var in value
+. if defined(var)
+. error
+. else
+. info In .for loops, variable expressions for the loop variables are
+. info substituted at evaluation time. There is no actual variable
+. info involved, even if it feels like it.
+. endif
+.endfor
+
all:
@:;
diff --git a/unit-tests/cond-func-empty.exp b/unit-tests/cond-func-empty.exp
index 39a9383953dd..77a4edd47f49 100644
--- a/unit-tests/cond-func-empty.exp
+++ b/unit-tests/cond-func-empty.exp
@@ -1 +1,5 @@
-exit status 0
+make: "cond-func-empty.mk" line 152: Unclosed variable "WORD"
+make: "cond-func-empty.mk" line 152: Malformed conditional (empty(WORD)
+make: Fatal errors encountered -- cannot continue
+make: stopped in unit-tests
+exit status 1
diff --git a/unit-tests/cond-func-empty.mk b/unit-tests/cond-func-empty.mk
index f93b45895e6e..f43d99bf92c5 100644
--- a/unit-tests/cond-func-empty.mk
+++ b/unit-tests/cond-func-empty.mk
@@ -1,11 +1,10 @@
-# $NetBSD: cond-func-empty.mk,v 1.8 2020/09/23 08:11:28 rillig Exp $
+# $NetBSD: cond-func-empty.mk,v 1.10 2020/11/15 14:07:53 rillig Exp $
#
# Tests for the empty() function in .if conditions, which tests a variable
# expression for emptiness.
#
# Note that the argument in the parentheses is indeed a variable name,
-# optionally followed by variable modifiers. This is like the defined()
-# function.
+# optionally followed by variable modifiers.
#
.undef UNDEF
@@ -25,13 +24,15 @@ WORD= word
. error
.endif
-# The :S modifier replaces the empty value with an actual word, and
-# after that the expression is no longer empty. Because the variable
-# was undefined in the first place, the expression has the flag VAR_JUNK
-# but not VAR_KEEP, therefore it is still considered undefined.
-# Only very few variable modifiers turn an undefined variable expression
-# into a defined variable expression. The :U and :D modifiers belong to
-# that group, but :S doesn't (see VAR_KEEP).
+# The :S modifier replaces the empty value with an actual word. The
+# expression is now no longer empty, but it is still possible to see whether
+# the expression was based on an undefined variable. The expression has the
+# flag VEF_UNDEF.
+#
+# The expression does not have the flag VEF_DEF though, therefore it is still
+# considered undefined. Yes, indeed, undefined but not empty. There are a
+# few variable modifiers that turn an undefined expression into a defined
+# expression, among them :U and :D, but not :S.
#
# XXX: This is hard to explain to someone who doesn't know these
# implementation details.
@@ -49,13 +50,14 @@ WORD= word
.endif
# And now to the surprising part. Applying the following :S modifier to the
-# undefined variable makes it non-empty, but the marker VAR_JUNK is preserved
-# nevertheless. The :U modifier that follows only looks at VAR_JUNK to decide
-# whether the variable is defined or not. This kind of makes sense since the
-# :U modifier tests the _variable_, not the _expression_.
+# undefined expression makes it non-empty, but the marker VEF_UNDEF is
+# preserved nevertheless. The :U modifier that follows only looks at the
+# VEF_UNDEF flag to decide whether the variable is defined or not. This kind
+# of makes sense since the :U modifier tests the _variable_, not the
+# _expression_.
#
-# But since the variable was undefined to begin with, the fallback value is
-# used in this expression.
+# But since the variable was undefined to begin with, the fallback value from
+# the :U modifier is used in this expression.
#
.if ${UNDEF:S,^$,value,W:Ufallback} != "fallback"
. error
@@ -128,7 +130,7 @@ ${:U }= space
# If everything goes well, the argument expands to "WORD", and that variable
# is defined at the beginning of this file. The surrounding 'W' and 'D'
# ensure that the parser in ParseEmptyArg has the correct position, both
-# before and after the call to Var_ParsePP.
+# before and after the call to Var_Parse.
.if empty(W${:UOR}D)
. error
.endif
@@ -146,5 +148,12 @@ ${:U WORD }= variable name with spaces
. error
.endif
+# Parse error: missing closing parenthesis.
+.if empty(WORD
+. error
+.else
+. error
+.endif
+
all:
@:;
diff --git a/unit-tests/cond-func.exp b/unit-tests/cond-func.exp
index 0069ed75726c..73b6273d0b09 100644
--- a/unit-tests/cond-func.exp
+++ b/unit-tests/cond-func.exp
@@ -1,9 +1,15 @@
-make: "cond-func.mk" line 29: warning: Missing closing parenthesis for defined()
-make: "cond-func.mk" line 29: Malformed conditional (!defined(A B))
-make: "cond-func.mk" line 44: warning: Missing closing parenthesis for defined()
-make: "cond-func.mk" line 44: Malformed conditional (!defined(A&B))
-make: "cond-func.mk" line 47: warning: Missing closing parenthesis for defined()
-make: "cond-func.mk" line 47: Malformed conditional (!defined(A|B))
+make: "cond-func.mk" line 36: warning: Missing closing parenthesis for defined()
+make: "cond-func.mk" line 36: Malformed conditional (!defined(A B))
+make: "cond-func.mk" line 51: warning: Missing closing parenthesis for defined()
+make: "cond-func.mk" line 51: Malformed conditional (!defined(A&B))
+make: "cond-func.mk" line 54: warning: Missing closing parenthesis for defined()
+make: "cond-func.mk" line 54: Malformed conditional (!defined(A|B))
+make: "cond-func.mk" line 94: The empty variable is never defined.
+make: "cond-func.mk" line 102: A plain function name is parsed as !empty(...).
+make: "cond-func.mk" line 109: A plain function name is parsed as !empty(...).
+make: "cond-func.mk" line 119: Symbols may start with a function name.
+make: "cond-func.mk" line 124: Symbols may start with a function name.
+make: "cond-func.mk" line 130: Malformed conditional (defined()
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/unit-tests/cond-func.mk b/unit-tests/cond-func.mk
index 8e7362182429..4ff43b72ef88 100644
--- a/unit-tests/cond-func.mk
+++ b/unit-tests/cond-func.mk
@@ -1,15 +1,22 @@
-# $NetBSD: cond-func.mk,v 1.4 2020/10/24 08:46:08 rillig Exp $
+# $NetBSD: cond-func.mk,v 1.9 2020/11/15 14:07:53 rillig Exp $
#
# Tests for those parts of the functions in .if conditions that are common
# among several functions.
#
# The below test uses the function defined(...) since it has no side-effects,
-# the other functions (except empty(...)) would work equally well.
+# the other functions (except empty(...)) would work equally well. The
+# function empty is special because it uses a different parsing algorithm for
+# its argument.
DEF= defined
${:UA B}= variable name with spaces
${:UVAR(value)}= variable name with parentheses
-${:UVAR{value}}= variable name with braces
+${:UVAR{value}}= variable name with balanced braces
+
+# Really strange variable names must be given indirectly via another variable,
+# so that no unbalanced braces appear in the top-level expression.
+VARNAME_UNBALANCED_BRACES= VAR{{{value
+${VARNAME_UNBALANCED_BRACES}= variable name with unbalanced braces
.if !defined(DEF)
. error
@@ -59,6 +66,12 @@ ${:UVAR{value}}= variable name with braces
. error
.endif
+# Braces do not have any special meaning when parsing arguments.
+# They don't need to be balanced.
+.if !defined(VAR{{{value)
+. error
+.endif
+
# There may be spaces around the operators and parentheses, and even
# inside the parentheses. The spaces inside the parentheses are not
# allowed for the empty() function (see cond-func-empty.mk), therefore
@@ -67,5 +80,58 @@ ${:UVAR{value}}= variable name with braces
. error
.endif
+# The following condition is interpreted as defined(A) && defined(B).
+# In lack of a function call expression, each kind of .if directive has a
+# default function that is called when a bare word is parsed. For the plain
+# .if directive, this function is defined(); see "struct If ifs" in cond.c.
+.if A&B
+. error
+.endif
+
+.if defined()
+. error
+.else
+. info The empty variable is never defined.
+.endif
+
+# The plain word 'defined' is interpreted as '!empty(defined)'.
+# That variable is not defined (yet).
+.if defined
+. error
+.else
+. info A plain function name is parsed as !empty(...).
+.endif
+
+# If a variable named 'defined' is actually defined and not empty, the plain
+# symbol 'defined' evaluates to true.
+defined= non-empty
+.if defined
+. info A plain function name is parsed as !empty(...).
+.else
+. error
+.endif
+
+# A plain symbol name may start with one of the function names, in this case
+# 'defined'.
+.if defined-var
+. error
+.else
+. info Symbols may start with a function name.
+.endif
+
+defined-var= non-empty
+.if defined-var
+. info Symbols may start with a function name.
+.else
+. error
+.endif
+
+# Missing closing parenthesis when parsing the function argument.
+.if defined(
+. error
+.else
+. error
+.endif
+
all:
@:;
diff --git a/unit-tests/cond-late.mk b/unit-tests/cond-late.mk
index 397f5febd480..4df3df2cf1d4 100644
--- a/unit-tests/cond-late.mk
+++ b/unit-tests/cond-late.mk
@@ -1,7 +1,9 @@
-# $NetBSD: cond-late.mk,v 1.2 2020/07/25 20:37:46 rillig Exp $
+# $NetBSD: cond-late.mk,v 1.3 2020/11/15 14:07:53 rillig Exp $
#
# Using the :? modifier, variable expressions can contain conditional
-# expressions that are evaluated late. Any variables appearing in these
+# expressions that are evaluated late, at expansion time.
+#
+# Any variables appearing in these
# conditions are expanded before parsing the condition. This is
# different from many other places.
#
@@ -11,15 +13,15 @@
# They should also not contain operators like == or <, since these are
# actually interpreted as these operators. This is demonstrated below.
#
-# If the order of evaluation were to change to first parse the condition
-# and then expand the variables, the output would change from the
-# current "yes no" to "yes yes", since both variables are non-empty.
all: cond-literal
COND.true= "yes" == "yes"
COND.false= "yes" != "yes"
+# If the order of evaluation were to change to first parse the condition
+# and then expand the variables, the output would change from the
+# current "yes no" to "yes yes", since both variables are non-empty.
cond-literal:
@echo ${ ${COND.true} :?yes:no}
@echo ${ ${COND.false} :?yes:no}
diff --git a/unit-tests/cond-op-and-lint.exp b/unit-tests/cond-op-and-lint.exp
new file mode 100644
index 000000000000..8817fd0d658b
--- /dev/null
+++ b/unit-tests/cond-op-and-lint.exp
@@ -0,0 +1,4 @@
+make: "cond-op-and-lint.mk" line 9: Unknown operator '&'
+make: Fatal errors encountered -- cannot continue
+make: stopped in unit-tests
+exit status 1
diff --git a/unit-tests/cond-op-and-lint.mk b/unit-tests/cond-op-and-lint.mk
new file mode 100644
index 000000000000..6262339016f5
--- /dev/null
+++ b/unit-tests/cond-op-and-lint.mk
@@ -0,0 +1,13 @@
+# $NetBSD: cond-op-and-lint.mk,v 1.1 2020/11/08 23:54:28 rillig Exp $
+#
+# Tests for the && operator in .if conditions, in lint mode.
+
+.MAKEFLAGS: -dL
+
+# The '&' operator is not allowed in lint mode.
+# It is not used in practice anyway.
+.if 0 & 0
+. error
+.else
+. error
+.endif
diff --git a/unit-tests/cond-op-not.exp b/unit-tests/cond-op-not.exp
index 39a9383953dd..37f57b7fdfa7 100644
--- a/unit-tests/cond-op-not.exp
+++ b/unit-tests/cond-op-not.exp
@@ -1 +1,6 @@
+make: "cond-op-not.mk" line 29: Not empty evaluates to true.
+make: "cond-op-not.mk" line 37: Not space evaluates to false.
+make: "cond-op-not.mk" line 41: Not 0 evaluates to true.
+make: "cond-op-not.mk" line 49: Not 1 evaluates to false.
+make: "cond-op-not.mk" line 55: Not word evaluates to false.
exit status 0
diff --git a/unit-tests/cond-op-not.mk b/unit-tests/cond-op-not.mk
index d929318785a5..388c62d8898f 100644
--- a/unit-tests/cond-op-not.mk
+++ b/unit-tests/cond-op-not.mk
@@ -1,6 +1,6 @@
-# $NetBSD: cond-op-not.mk,v 1.4 2020/10/24 08:46:08 rillig Exp $
+# $NetBSD: cond-op-not.mk,v 1.6 2020/11/15 14:58:14 rillig Exp $
#
-# Tests for the ! operator in .if conditions.
+# Tests for the ! operator in .if conditions, which negates its argument.
# The exclamation mark negates its operand.
.if !1
@@ -18,5 +18,42 @@
. error
.endif
+# The operator '==' binds more tightly than '!'.
+# This is unusual since most other programming languages define the precedence
+# to be the other way round.
+.if !${:Uexpression} == "expression"
+. error
+.endif
+
+.if !${:U}
+. info Not empty evaluates to true.
+.else
+. info Not empty evaluates to false.
+.endif
+
+.if !${:U }
+. info Not space evaluates to true.
+.else
+. info Not space evaluates to false.
+.endif
+
+.if !${:U0}
+. info Not 0 evaluates to true.
+.else
+. info Not 0 evaluates to false.
+.endif
+
+.if !${:U1}
+. info Not 1 evaluates to true.
+.else
+. info Not 1 evaluates to false.
+.endif
+
+.if !${:Uword}
+. info Not word evaluates to true.
+.else
+. info Not word evaluates to false.
+.endif
+
all:
@:;
diff --git a/unit-tests/cond-op-or-lint.exp b/unit-tests/cond-op-or-lint.exp
new file mode 100644
index 000000000000..8abae99b6c4c
--- /dev/null
+++ b/unit-tests/cond-op-or-lint.exp
@@ -0,0 +1,4 @@
+make: "cond-op-or-lint.mk" line 9: Unknown operator '|'
+make: Fatal errors encountered -- cannot continue
+make: stopped in unit-tests
+exit status 1
diff --git a/unit-tests/cond-op-or-lint.mk b/unit-tests/cond-op-or-lint.mk
new file mode 100644
index 000000000000..aa29e9a6c2f2
--- /dev/null
+++ b/unit-tests/cond-op-or-lint.mk
@@ -0,0 +1,13 @@
+# $NetBSD: cond-op-or-lint.mk,v 1.1 2020/11/08 23:54:28 rillig Exp $
+#
+# Tests for the || operator in .if conditions, in lint mode.
+
+.MAKEFLAGS: -dL
+
+# The '|' operator is not allowed in lint mode.
+# It is not used in practice anyway.
+.if 0 | 0
+. error
+.else
+. error
+.endif
diff --git a/unit-tests/cond-op-parentheses.exp b/unit-tests/cond-op-parentheses.exp
index 39a9383953dd..a0fa137af4ca 100644
--- a/unit-tests/cond-op-parentheses.exp
+++ b/unit-tests/cond-op-parentheses.exp
@@ -1 +1,2 @@
+make: "cond-op-parentheses.mk" line 13: Parentheses can be nested at least to depth 112.
exit status 0
diff --git a/unit-tests/cond-op-parentheses.mk b/unit-tests/cond-op-parentheses.mk
index 6c48d83dd2be..39ebc1607455 100644
--- a/unit-tests/cond-op-parentheses.mk
+++ b/unit-tests/cond-op-parentheses.mk
@@ -1,8 +1,19 @@
-# $NetBSD: cond-op-parentheses.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+# $NetBSD: cond-op-parentheses.mk,v 1.3 2020/11/15 14:58:14 rillig Exp $
#
# Tests for parentheses in .if conditions.
# TODO: Implementation
+# Test for deeply nested conditions.
+.if (((((((((((((((((((((((((((((((((((((((((((((((((((((((( \
+ (((((((((((((((((((((((((((((((((((((((((((((((((((((((( \
+ 1 \
+ )))))))))))))))))))))))))))))))))))))))))))))))))))))))) \
+ ))))))))))))))))))))))))))))))))))))))))))))))))))))))))
+. info Parentheses can be nested at least to depth 112.
+.else
+. error
+.endif
+
all:
@:;
diff --git a/unit-tests/cond-op.exp b/unit-tests/cond-op.exp
index fd5bc20f673c..1a66e5ffeabd 100644
--- a/unit-tests/cond-op.exp
+++ b/unit-tests/cond-op.exp
@@ -1,7 +1,16 @@
-make: "cond-op.mk" line 45: Malformed conditional ("!word" == !word)
-make: "cond-op.mk" line 70: Malformed conditional (0 ${ERR::=evaluated})
-make: "cond-op.mk" line 74: warning: After detecting a parse error, the rest is evaluated.
-make: "cond-op.mk" line 78: Parsing continues until here.
+make: "cond-op.mk" line 50: Malformed conditional ("!word" == !word)
+make: "cond-op.mk" line 75: Malformed conditional (0 ${ERR::=evaluated})
+make: "cond-op.mk" line 79: After detecting a parse error, the rest is evaluated.
+make: "cond-op.mk" line 83: Parsing continues until here.
+make: "cond-op.mk" line 86: A B C => (A || B) && C A || B && C A || (B && C)
+make: "cond-op.mk" line 93: 0 0 0 => 0 0 0
+make: "cond-op.mk" line 93: 0 0 1 => 0 0 0
+make: "cond-op.mk" line 93: 0 1 0 => 0 0 0
+make: "cond-op.mk" line 93: 0 1 1 => 1 1 1
+make: "cond-op.mk" line 93: 1 0 0 => 0 1 1
+make: "cond-op.mk" line 93: 1 0 1 => 1 1 1
+make: "cond-op.mk" line 93: 1 1 0 => 0 1 1
+make: "cond-op.mk" line 93: 1 1 1 => 1 1 1
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/unit-tests/cond-op.mk b/unit-tests/cond-op.mk
index 71c4f7b66441..170355f6c0ad 100644
--- a/unit-tests/cond-op.mk
+++ b/unit-tests/cond-op.mk
@@ -1,4 +1,4 @@
-# $NetBSD: cond-op.mk,v 1.8 2020/10/24 08:46:08 rillig Exp $
+# $NetBSD: cond-op.mk,v 1.10 2020/11/15 14:58:14 rillig Exp $
#
# Tests for operators like &&, ||, ! in .if conditions.
#
@@ -9,8 +9,8 @@
# cond-op-parentheses.mk
# In make, && binds more tightly than ||, like in C.
-# If make had the same precedence for both && and ||, the result would be
-# different.
+# If make had the same precedence for both && and ||, like in the shell,
+# the result would be different.
# If || were to bind more tightly than &&, the result would be different
# as well.
.if !(1 || 1 && 0)
@@ -18,13 +18,17 @@
.endif
# If make were to interpret the && and || operators like the shell, the
-# implicit binding would be this:
+# previous condition would be interpreted as:
.if (1 || 1) && 0
. error
.endif
# The precedence of the ! operator is different from C though. It has a
-# lower precedence than the comparison operators.
+# lower precedence than the comparison operators. Negating a condition
+# does not need parentheses.
+#
+# This kind of condition looks so unfamiliar that it doesn't occur in
+# practice.
.if !"word" == "word"
. error
.endif
@@ -36,7 +40,8 @@
# TODO: Demonstrate that the precedence of the ! and == operators actually
# makes a difference. There is a simple example for sure, I just cannot
-# wrap my head around it.
+# wrap my head around it right now. See the truth table generator below
+# for an example that doesn't require much thought.
# This condition is malformed because the '!' on the right-hand side must not
# appear unquoted. If any, it must be enclosed in quotes.
@@ -71,11 +76,27 @@
. error
.endif
.if ${ERR:Uundefined} == evaluated
-. warning After detecting a parse error, the rest is evaluated.
+. info After detecting a parse error, the rest is evaluated.
.endif
# Just in case that parsing should ever stop on the first error.
.info Parsing continues until here.
+# Demonstration that '&&' has higher precedence than '||'.
+.info A B C => (A || B) && C A || B && C A || (B && C)
+.for a in 0 1
+. for b in 0 1
+. for c in 0 1
+. for r1 in ${ ($a || $b) && $c :?1:0}
+. for r2 in ${ $a || $b && $c :?1:0}
+. for r3 in ${ $a || ($b && $c) :?1:0}
+. info $a $b $c => ${r1} ${r2} ${r3}
+. endfor
+. endfor
+. endfor
+. endfor
+. endfor
+.endfor
+
all:
@:;
diff --git a/unit-tests/cond-short.mk b/unit-tests/cond-short.mk
index 9e3b0575eb78..077684be33fc 100644
--- a/unit-tests/cond-short.mk
+++ b/unit-tests/cond-short.mk
@@ -1,11 +1,14 @@
-# $NetBSD: cond-short.mk,v 1.11 2020/10/24 08:50:17 rillig Exp $
+# $NetBSD: cond-short.mk,v 1.12 2020/11/15 14:58:14 rillig Exp $
#
# Demonstrates that in conditions, the right-hand side of an && or ||
# is only evaluated if it can actually influence the result.
+# This is called 'short-circuit evaluation' and is the usual evaluation
+# mode in most programming languages. A notable exception is Ada, which
+# distinguishes between the operators 'And', 'And Then', 'Or', 'Or Else'.
#
# Between 2015-10-11 and 2020-06-28, the right-hand side of an && or ||
# operator was always evaluated, which was wrong.
-#
+# TODO: Had the evaluation been correct at some time before 2015-11-12?
# The && operator.
@@ -113,6 +116,9 @@ VAR= # empty again, for the following tests
# make sure these do not cause complaint
#.MAKEFLAGS: -dc
+# TODO: Rewrite this whole section and check all the conditions and variables.
+# Several of the assumptions are probably wrong here.
+# TODO: replace 'x=' with '.info' or '.error'.
V42= 42
iV1= ${V42}
iV2= ${V66}
@@ -167,5 +173,16 @@ x= Fail
.endif
x!= echo '0 || ${iV2:U2} < ${V42}: $x' >&2; echo
+# TODO: Has this always worked? There may have been a time, maybe around
+# 2000, when make would complain about the "Malformed conditional" because
+# UNDEF is not defined.
+.if defined(UNDEF) && ${UNDEF} != "undefined"
+. 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-number.exp b/unit-tests/cond-token-number.exp
index ea1f92797ff4..b5bfaf95d575 100644
--- a/unit-tests/cond-token-number.exp
+++ b/unit-tests/cond-token-number.exp
@@ -1,8 +1,8 @@
-make: "cond-token-number.mk" line 13: Malformed conditional (-0)
-make: "cond-token-number.mk" line 21: Malformed conditional (+0)
-make: "cond-token-number.mk" line 29: Malformed conditional (!-1)
-make: "cond-token-number.mk" line 37: Malformed conditional (!+1)
-make: "cond-token-number.mk" line 54: End of the tests.
+make: "cond-token-number.mk" line 15: Malformed conditional (-0)
+make: "cond-token-number.mk" line 25: Malformed conditional (+0)
+make: "cond-token-number.mk" line 35: Malformed conditional (!-1)
+make: "cond-token-number.mk" line 45: Malformed conditional (!+1)
+make: "cond-token-number.mk" line 80: End of the tests.
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/unit-tests/cond-token-number.mk b/unit-tests/cond-token-number.mk
index 4171a07d56c6..93e2646a60eb 100644
--- a/unit-tests/cond-token-number.mk
+++ b/unit-tests/cond-token-number.mk
@@ -1,6 +1,8 @@
-# $NetBSD: cond-token-number.mk,v 1.3 2020/09/14 06:22:59 rillig Exp $
+# $NetBSD: cond-token-number.mk,v 1.5 2020/11/15 14:58:14 rillig Exp $
#
# Tests for number tokens in .if conditions.
+#
+# TODO: Add introduction.
.if 0
. error
@@ -12,6 +14,8 @@
# See the ch_isdigit call in CondParser_String.
.if -0
. error
+.else
+. error
.endif
# Even though +0 is a number and would be accepted by strtod, it is not
@@ -20,6 +24,8 @@
# See the ch_isdigit call in CondParser_String.
.if +0
. error
+.else
+. error
.endif
# Even though -1 is a number and would be accepted by strtod, it is not
@@ -28,6 +34,8 @@
# See the ch_isdigit call in CondParser_String.
.if !-1
. error
+.else
+. error
.endif
# Even though +1 is a number and would be accepted by strtod, it is not
@@ -36,6 +44,8 @@
# See the ch_isdigit call in CondParser_String.
.if !+1
. error
+.else
+. error
.endif
# When the number comes from a variable expression though, it may be signed.
@@ -50,6 +60,22 @@
. error
.endif
+# Hexadecimal numbers are accepted.
+.if 0x0
+. error
+.endif
+.if 0x1
+.else
+. error
+.endif
+
+# This is not a hexadecimal number, even though it has an x.
+# It is interpreted as a string instead, effectively meaning defined(3x4).
+.if 3x4
+.else
+. error
+.endif
+
# Ensure that parsing continues until here.
.info End of the tests.
diff --git a/unit-tests/cond-token-plain.mk b/unit-tests/cond-token-plain.mk
index ba9934f5b882..a5ffa37a5c84 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.4 2020/09/12 17:47:24 rillig Exp $
+# $NetBSD: cond-token-plain.mk,v 1.6 2020/11/15 14:58:14 rillig Exp $
#
# Tests for plain tokens (that is, string literals without quotes)
# in .if conditions.
@@ -14,7 +14,7 @@
# parser gets to see it.
#
# XXX: The error message is missing for this malformed condition.
-# The right-hand side of the comparison is just a '"'.
+# The right-hand side of the comparison is just a '"', before unescaping.
.if ${:U} != "#hash"
. error
.endif
@@ -31,16 +31,19 @@
# comment handling anymore. The comments are supposed to be stripped off
# in a very early parsing phase.
#
+# See https://gnats.netbsd.org/19596 for example makefiles demonstrating the
+# original problems. This workaround is probably not needed anymore.
+#
# XXX: Missing error message for the malformed condition. The right-hand
-# side is double-quotes, backslash, backslash.
-# XXX: It is unexpected that the right-hand side evaluates to a single
-# backslash.
+# side before unescaping is double-quotes, backslash, backslash.
.if ${:U\\} != "\\#hash"
. error
.endif
# The right-hand side of a comparison is not parsed as a token, therefore
# the code from CondParser_Token does not apply to it.
+# TODO: Explain the consequences.
+# TODO: Does this mean that more syntactic variants are allowed here?
.if ${:U\#hash} != \#hash
. error
.endif
diff --git a/unit-tests/cond-token-string.exp b/unit-tests/cond-token-string.exp
index 39a9383953dd..5df4cc675bb1 100644
--- a/unit-tests/cond-token-string.exp
+++ b/unit-tests/cond-token-string.exp
@@ -1 +1,8 @@
-exit status 0
+make: Unknown modifier 'Z'
+make: "cond-token-string.mk" line 9: Malformed conditional ("" != "${:Uvalue:Z}")
+make: "cond-token-string.mk" line 18: xvalue is not defined.
+make: "cond-token-string.mk" line 24: Malformed conditional (x${:Uvalue} == "")
+make: "cond-token-string.mk" line 33: Expected.
+make: Fatal errors encountered -- cannot continue
+make: stopped in unit-tests
+exit status 1
diff --git a/unit-tests/cond-token-string.mk b/unit-tests/cond-token-string.mk
index 1a8019754824..7e110806d408 100644
--- a/unit-tests/cond-token-string.mk
+++ b/unit-tests/cond-token-string.mk
@@ -1,8 +1,39 @@
-# $NetBSD: cond-token-string.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+# $NetBSD: cond-token-string.mk,v 1.3 2020/11/10 22:23:37 rillig Exp $
#
# Tests for quoted and unquoted string literals in .if conditions.
# TODO: Implementation
+# Cover the code in CondParser_String that frees the memory after parsing
+# a variable expression based on an undefined variable.
+.if "" != "${:Uvalue:Z}"
+. error
+.else
+. error
+.endif
+
+.if x${:Uvalue}
+. error
+.else
+. info xvalue is not defined.
+.endif
+
+# The 'x' produces a "Malformed conditional" since the left-hand side of a
+# comparison in an .if directive must be either a variable expression, a
+# quoted string literal or a number that starts with a digit.
+.if x${:Uvalue} == ""
+. error
+.else
+. error
+.endif
+
+# In plain words, a '\' can be used to escape any character, just as in
+# double-quoted string literals. See CondParser_String.
+.if \x${:Uvalue} == "xvalue"
+. info Expected.
+.else
+. error
+.endif
+
all:
@:;
diff --git a/unit-tests/cond-token-var.exp b/unit-tests/cond-token-var.exp
index eb71a43c55f3..fcd92d12a3da 100644
--- a/unit-tests/cond-token-var.exp
+++ b/unit-tests/cond-token-var.exp
@@ -1,7 +1,7 @@
-make: "cond-token-var.mk" line 9: ok
-make: "cond-token-var.mk" line 15: Malformed conditional (${UNDEF} == ${DEF})
-make: "cond-token-var.mk" line 20: Malformed conditional (${DEF} == ${UNDEF})
-make: "cond-token-var.mk" line 29: Malformed conditional (${UNDEF})
+make: "cond-token-var.mk" line 20: ok
+make: "cond-token-var.mk" line 27: Malformed conditional (${UNDEF} == ${DEF})
+make: "cond-token-var.mk" line 33: Malformed conditional (${DEF} == ${UNDEF})
+make: "cond-token-var.mk" line 42: Malformed conditional (${UNDEF})
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/unit-tests/cond-token-var.mk b/unit-tests/cond-token-var.mk
index 6e55bd2d552d..30eba87ad4d2 100644
--- a/unit-tests/cond-token-var.mk
+++ b/unit-tests/cond-token-var.mk
@@ -1,6 +1,17 @@
-# $NetBSD: cond-token-var.mk,v 1.4 2020/10/24 08:46:08 rillig Exp $
+# $NetBSD: cond-token-var.mk,v 1.5 2020/11/15 14:58:14 rillig Exp $
#
-# Tests for variables in .if conditions.
+# Tests for variable expressions in .if conditions.
+#
+# Note the fine distinction between a variable and a variable expression.
+# A variable has a name and a value. To access the value, one writes a
+# variable expression of the form ${VAR}. This is a simple variable
+# expression. Variable expressions can get more complicated by adding
+# variable modifiers such as in ${VAR:Mpattern}.
+#
+# XXX: Strictly speaking, variable modifiers should be called expression
+# modifiers instead since they only modify the expression, not the variable.
+# Well, except for the assignment modifiers, these do indeed change the value
+# of the variable.
DEF= defined
@@ -12,11 +23,13 @@ DEF= defined
.endif
# A variable that appears on the left-hand side must be defined.
+# The following line thus generates a parse error.
.if ${UNDEF} == ${DEF}
. error
.endif
# A variable that appears on the right-hand side must be defined.
+# The following line thus generates a parse error.
.if ${DEF} == ${UNDEF}
. error
.endif
@@ -25,10 +38,11 @@ DEF= defined
.if ${DEF}
.endif
-# An undefined variable generates a warning.
+# An undefined variable on its own generates a parse error.
.if ${UNDEF}
.endif
-# The :U modifier turns an undefined variable into an ordinary expression.
+# The :U modifier turns an undefined expression into a defined expression.
+# Since the expression is defined now, it doesn't generate any parse error.
.if ${UNDEF:U}
.endif
diff --git a/unit-tests/cond-undef-lint.exp b/unit-tests/cond-undef-lint.exp
index 365edae61275..2c4feb0376ff 100755
--- a/unit-tests/cond-undef-lint.exp
+++ b/unit-tests/cond-undef-lint.exp
@@ -1,7 +1,7 @@
make: "cond-undef-lint.mk" line 23: Variable "UNDEF" is undefined
make: "cond-undef-lint.mk" line 38: Variable "UNDEF" is undefined
make: "cond-undef-lint.mk" line 38: Variable "VAR." is undefined
-make: "cond-undef-lint.mk" line 45: Variable "VAR.defined" is undefined
+make: "cond-undef-lint.mk" line 49: Variable "VAR.defined" is undefined
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/unit-tests/cond-undef-lint.mk b/unit-tests/cond-undef-lint.mk
index 334ceb594054..9dfd1bd53252 100755
--- a/unit-tests/cond-undef-lint.mk
+++ b/unit-tests/cond-undef-lint.mk
@@ -1,4 +1,4 @@
-# $NetBSD: cond-undef-lint.mk,v 1.2 2020/09/14 07:13:29 rillig Exp $
+# $NetBSD: cond-undef-lint.mk,v 1.3 2020/11/15 14:58:14 rillig Exp $
#
# Tests for defined and undefined variables in .if conditions, in lint mode.
#
@@ -42,6 +42,10 @@ DEF= defined
.endif
# The variable VAR.defined is not defined and thus generates an error message.
+#
+# TODO: This pattern looks a lot like CFLAGS.${OPSYS}, which is at least
+# debatable. Or would any practical use of CFLAGS.${OPSYS} be via an indirect
+# expression, as in the next example?
.if ${VAR.${DEF}}
. error
.else
diff --git a/unit-tests/cond1.exp b/unit-tests/cond1.exp
index 38d616babdfb..b9db035833be 100644
--- a/unit-tests/cond1.exp
+++ b/unit-tests/cond1.exp
@@ -1,5 +1,5 @@
-make: "cond1.mk" line 75: warning: extra else
-make: "cond1.mk" line 85: warning: extra else
+make: "cond1.mk" line 80: warning: extra else
+make: "cond1.mk" line 90: warning: extra else
2 is prime
A='other' B='unknown' C='clever' o='no,no'
Passed:
diff --git a/unit-tests/cond1.mk b/unit-tests/cond1.mk
index eedff8c1530b..53908c2dacf1 100644
--- a/unit-tests/cond1.mk
+++ b/unit-tests/cond1.mk
@@ -1,4 +1,9 @@
-# $NetBSD: cond1.mk,v 1.2 2020/10/24 08:34:59 rillig Exp $
+# $NetBSD: cond1.mk,v 1.3 2020/11/15 14:58:14 rillig Exp $
+
+# TODO: Convert these tests into tutorial form.
+# TODO: Split these tests by topic.
+# TODO: Use better variable names and expression values that actually express
+# the intended behavior. uname(1) has nothing to do with conditions.
# hard code these!
TEST_UNAME_S= NetBSD
diff --git a/unit-tests/dep-double-colon.mk b/unit-tests/dep-double-colon.mk
index 67a28a4315cd..70fddbcf21a4 100644
--- a/unit-tests/dep-double-colon.mk
+++ b/unit-tests/dep-double-colon.mk
@@ -1,6 +1,9 @@
-# $NetBSD: dep-double-colon.mk,v 1.4 2020/09/26 15:41:53 rillig Exp $
+# $NetBSD: dep-double-colon.mk,v 1.5 2020/11/15 20:20:58 rillig Exp $
#
-# Tests for the :: operator in dependency declarations.
+# Tests for the '::' operator in dependency declarations, which allows
+# several dependency groups for a single node, each having its own attributes
+# and dependencies. In the code, the additional dependency groups are called
+# cohorts.
all::
@echo 'command 1a'
diff --git a/unit-tests/dep-exclam.mk b/unit-tests/dep-exclam.mk
index 2779a66ea6e3..5f6b72da3fe0 100644
--- a/unit-tests/dep-exclam.mk
+++ b/unit-tests/dep-exclam.mk
@@ -1,6 +1,11 @@
-# $NetBSD: dep-exclam.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+# $NetBSD: dep-exclam.mk,v 1.3 2020/11/15 20:20:58 rillig Exp $
#
-# Tests for the ! operator in dependency declarations.
+# Tests for the ! operator in dependency declarations, which always re-creates
+# the target, whether or not it is out of date.
+#
+# TODO: Is this related to OP_PHONY?
+# TODO: Is this related to OP_EXEC?
+# TODO: Is this related to OP_MAKE?
# TODO: Implementation
diff --git a/unit-tests/depsrc-ignore.mk b/unit-tests/depsrc-ignore.mk
index 1be3eabe8806..aea7d24941ab 100644
--- a/unit-tests/depsrc-ignore.mk
+++ b/unit-tests/depsrc-ignore.mk
@@ -1,11 +1,10 @@
-# $NetBSD: depsrc-ignore.mk,v 1.4 2020/08/29 16:13:27 rillig Exp $
+# $NetBSD: depsrc-ignore.mk,v 1.5 2020/11/15 20:20:58 rillig Exp $
#
# Tests for the special source .IGNORE in dependency declarations,
# which ignores any command failures for that target.
#
-# Even though ignore-errors fails, the all target is still made.
-# Since the all target is not marked with .IGNORE, it stops at the
-# first failing command.
+# Even though 'ignore-errors' fails, 'all' is still made. Since 'all' is
+# not marked with .IGNORE, it stops at the first failing command.
#
# XXX: The ordering of the messages in the output is confusing.
# The "ignored" comes much too late to be related to the "false
@@ -24,8 +23,8 @@
# This is what actually happens, as of 2020-08-29. To verify it, set the
# following breakpoints in CompatRunCommand:
#
-# * the "!silent" line, to see all commands.
-# * the "fflush" line, to see stdout being flushed.
+# * the "!silent" line, to see all commands
+# * the "fflush" line, to see stdout being flushed
# * the "status = WEXITSTATUS" line
# * the "(continuing)" line
# * the "(ignored)" line
diff --git a/unit-tests/depsrc-make.mk b/unit-tests/depsrc-make.mk
index f7b5a9cca8ff..8069007e6e11 100644
--- a/unit-tests/depsrc-make.mk
+++ b/unit-tests/depsrc-make.mk
@@ -1,9 +1,11 @@
-# $NetBSD: depsrc-make.mk,v 1.3 2020/09/05 15:57:12 rillig Exp $
+# $NetBSD: depsrc-make.mk,v 1.4 2020/11/15 20:20:58 rillig Exp $
#
# Tests for the special source .MAKE in dependency declarations, which
# executes the commands of the target even if the -n or -t command line
# options are given.
+# TODO: Add a test for the -t command line option.
+
.MAKEFLAGS: -n
all: this-is-made
diff --git a/unit-tests/depsrc-optional.exp b/unit-tests/depsrc-optional.exp
index 05f2ac6624c4..fce85b3cb38e 100644
--- a/unit-tests/depsrc-optional.exp
+++ b/unit-tests/depsrc-optional.exp
@@ -1,2 +1,20 @@
-`all' is up to date.
+Make_ExpandUse: examine all
+ExamineLater: need to examine "important"
+Make_ExpandUse: examine important
+ExamineLater: need to examine "optional"
+ExamineLater: need to examine "optional-cohort"
+Make_ExpandUse: examine optional
+Make_ExpandUse: examine optional-cohort
+Examining optional...non-existent...up-to-date.
+Examining optional-cohort...non-existent...:: operator and no sources...out-of-date.
+: A leaf node using '::' is considered out-of-date.
+ recheck(optional-cohort): update time from 0:00:00 Jan 01, 1970 to now
+Examining important...non-existent...modified before source "optional-cohort"...out-of-date.
+: important is made.
+ recheck(important): update time from 0:00:00 Jan 01, 1970 to now
+Examining all...non-existent...modified before source "important"...out-of-date.
+: all is made.
+ recheck(all): update time from 0:00:00 Jan 01, 1970 to now
+Examining .END...non-existent...non-existent and no sources...out-of-date.
+ recheck(.END): update time from 0:00:00 Jan 01, 1970 to now
exit status 0
diff --git a/unit-tests/depsrc-optional.mk b/unit-tests/depsrc-optional.mk
index 75ae38bf3194..f12eeca2b3cb 100644
--- a/unit-tests/depsrc-optional.mk
+++ b/unit-tests/depsrc-optional.mk
@@ -1,18 +1,21 @@
-# $NetBSD: depsrc-optional.mk,v 1.3 2020/09/05 15:57:12 rillig Exp $
+# $NetBSD: depsrc-optional.mk,v 1.5 2020/11/08 10:33:47 rillig Exp $
#
# Tests for the special source .OPTIONAL in dependency declarations,
# which ignores the target if make cannot find out how to create it.
#
# TODO: Describe practical use cases for this feature.
-# TODO: Explain why the commands for "important" are not executed.
-# I had thought that only the "optional" commands were skipped.
-
all: important
: ${.TARGET} is made.
-important: optional
+important: optional optional-cohort
: ${.TARGET} is made.
optional: .OPTIONAL
- : This is not executed.
+ : An optional leaf node is not executed.
+
+# See IsOODateRegular.
+optional-cohort:: .OPTIONAL
+ : A leaf node using '::' is considered out-of-date.
+
+.MAKEFLAGS: -dm
diff --git a/unit-tests/depsrc-precious.mk b/unit-tests/depsrc-precious.mk
index 699b83d767b1..e8522a300790 100644
--- a/unit-tests/depsrc-precious.mk
+++ b/unit-tests/depsrc-precious.mk
@@ -1,6 +1,14 @@
-# $NetBSD: depsrc-precious.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+# $NetBSD: depsrc-precious.mk,v 1.3 2020/11/15 20:20:58 rillig Exp $
#
-# Tests for the special source .PRECIOUS in dependency declarations.
+# Tests for the special source .PRECIOUS in dependency declarations, which
+# is only relevant if the commands for the target fail or are interrupted.
+# In such a case, the target file is usually removed, to avoid having
+# half-finished files with a timestamp suggesting the file were up-to-date.
+#
+# For targets marked with .PRECIOUS, the target file is not removed.
+# The author of the makefile is then responsible for avoiding the above
+# situation, in which the target would be wrongly considered up-to-date,
+# just because its timestamp says so.
# TODO: Implementation
diff --git a/unit-tests/depsrc-usebefore.mk b/unit-tests/depsrc-usebefore.mk
index c6be2bae0a9c..001cfb0d71c1 100644
--- a/unit-tests/depsrc-usebefore.mk
+++ b/unit-tests/depsrc-usebefore.mk
@@ -1,7 +1,11 @@
-# $NetBSD: depsrc-usebefore.mk,v 1.5 2020/08/22 11:53:18 rillig Exp $
+# $NetBSD: depsrc-usebefore.mk,v 1.6 2020/11/15 20:20:58 rillig Exp $
#
# Tests for the special source .USEBEFORE in dependency declarations,
# which allows to prepend common commands to other targets.
+#
+# See also:
+# .USE
+# depsrc-use.mk
all: action directly
diff --git a/unit-tests/depsrc.mk b/unit-tests/depsrc.mk
index d461e1111d0f..15b27286de22 100644
--- a/unit-tests/depsrc.mk
+++ b/unit-tests/depsrc.mk
@@ -1,9 +1,11 @@
-# $NetBSD: depsrc.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+# $NetBSD: depsrc.mk,v 1.3 2020/11/15 20:20:58 rillig Exp $
#
# Tests for special sources (those starting with a dot, followed by
# uppercase letters) in dependency declarations, such as .PHONY.
# TODO: Implementation
+# TODO: Test 'target: ${:U.SILENT}'
+
all:
@:;
diff --git a/unit-tests/deptgt-begin.exp b/unit-tests/deptgt-begin.exp
index 527020f6818d..abc80afe9964 100644
--- a/unit-tests/deptgt-begin.exp
+++ b/unit-tests/deptgt-begin.exp
@@ -1,4 +1,7 @@
+make: "deptgt-begin.mk" line 17: warning: duplicate script for target ".BEGIN" ignored
+make: "deptgt-begin.mk" line 8: warning: using previous script for ".BEGIN" defined here
: parse time
+: Making before-begin before .BEGIN.
: .BEGIN
: all
exit status 0
diff --git a/unit-tests/deptgt-begin.mk b/unit-tests/deptgt-begin.mk
index c6ca2f4aa3c7..b71d78f371ed 100644
--- a/unit-tests/deptgt-begin.mk
+++ b/unit-tests/deptgt-begin.mk
@@ -1,4 +1,4 @@
-# $NetBSD: deptgt-begin.mk,v 1.3 2020/08/29 17:34:21 rillig Exp $
+# $NetBSD: deptgt-begin.mk,v 1.5 2020/11/15 22:28:08 rillig Exp $
#
# Tests for the special target .BEGIN in dependency declarations,
# which is a container for commands that are run before any other
@@ -7,6 +7,40 @@
.BEGIN:
: $@
+# To register a custom action to be run at the beginning, the simplest way is
+# to directly place some commands on the '.BEGIN' target. This doesn't scale
+# though, since the ':' dependency operator prevents that any other place may
+# add its commands after this.
+#
+# There are several ways to resolve this situation, which are detailed below.
+.BEGIN:
+ : Making another $@.
+
+# One way to run commands at the beginning is to define a custom target and
+# make the .BEGIN depend on that target. This way, the commands from the
+# custom target are run even before the .BEGIN target.
+.BEGIN: before-begin
+before-begin: .PHONY .NOTMAIN
+ : Making $@ before .BEGIN.
+
+# Another way is to define a custom target and make that a .USE dependency.
+# For the .BEGIN target, .USE dependencies do not work though, since in
+# Compat_Run, the .USE and .USEBEFORE nodes are expanded right after the
+# .BEGIN target has been run, which is too late.
+.BEGIN: use
+use: .USE .NOTMAIN
+ : Making $@ from a .USE dependency.
+
+# Same as with .USE, but run the commands before the main commands from the
+# .BEGIN target.
+#
+# For the .BEGIN target, .USEBEFORE dependencies do not work though, since in
+# Compat_Run, the .USE and .USEBEFORE nodes are expanded right after the
+# .BEGIN target has been run, which is too late.
+.BEGIN: use-before
+use-before: .USEBEFORE .NOTMAIN
+ : Making $@ from a .USEBEFORE dependency.
+
all:
: $@
diff --git a/unit-tests/deptgt-error.mk b/unit-tests/deptgt-error.mk
index 07bc1b5d3408..5d515b95afc3 100644
--- a/unit-tests/deptgt-error.mk
+++ b/unit-tests/deptgt-error.mk
@@ -1,6 +1,7 @@
-# $NetBSD: deptgt-error.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+# $NetBSD: deptgt-error.mk,v 1.3 2020/11/15 20:20:58 rillig Exp $
#
-# Tests for the special target .ERROR in dependency declarations.
+# Tests for the special target .ERROR in dependency declarations, which
+# collects commands that are run when another target fails.
# TODO: Implementation
diff --git a/unit-tests/deptgt-ignore.mk b/unit-tests/deptgt-ignore.mk
index 6ace0841f28b..49c14d2cfd43 100644
--- a/unit-tests/deptgt-ignore.mk
+++ b/unit-tests/deptgt-ignore.mk
@@ -1,6 +1,7 @@
-# $NetBSD: deptgt-ignore.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+# $NetBSD: deptgt-ignore.mk,v 1.3 2020/11/15 20:20:58 rillig Exp $
#
-# Tests for the special target .IGNORE in dependency declarations.
+# Tests for the special target .IGNORE in dependency declarations, which
+# does not stop if a command from this target exits with a non-zero status.
# TODO: Implementation
diff --git a/unit-tests/deptgt-interrupt.mk b/unit-tests/deptgt-interrupt.mk
index d555f864563e..d94009a52e05 100644
--- a/unit-tests/deptgt-interrupt.mk
+++ b/unit-tests/deptgt-interrupt.mk
@@ -1,6 +1,8 @@
-# $NetBSD: deptgt-interrupt.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+# $NetBSD: deptgt-interrupt.mk,v 1.3 2020/11/15 20:20:58 rillig Exp $
#
-# Tests for the special target .INTERRUPT in dependency declarations.
+# Tests for the special target .INTERRUPT in dependency declarations, which
+# collects commands to be run when make is interrupted while building another
+# target.
# TODO: Implementation
diff --git a/unit-tests/deptgt-main.mk b/unit-tests/deptgt-main.mk
index cf1b1b4fd340..84d05dc25ed6 100644
--- a/unit-tests/deptgt-main.mk
+++ b/unit-tests/deptgt-main.mk
@@ -1,6 +1,8 @@
-# $NetBSD: deptgt-main.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+# $NetBSD: deptgt-main.mk,v 1.3 2020/11/15 20:20:58 rillig Exp $
#
-# Tests for the special target .MAIN in dependency declarations.
+# Tests for the special target .MAIN in dependency declarations, which defines
+# the main target. This main target is built if no target has been specified
+# on the command line or via MAKEFLAGS.
# TODO: Implementation
diff --git a/unit-tests/deptgt-makeflags.exp b/unit-tests/deptgt-makeflags.exp
index a226cafc9ba1..7eb54eba7f30 100644
--- a/unit-tests/deptgt-makeflags.exp
+++ b/unit-tests/deptgt-makeflags.exp
@@ -6,4 +6,5 @@ Var_Parse: ${DOLLAR} != "\$\$" with VARE_UNDEFERR|VARE_WANTRES
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
+make: Unterminated quoted string [make VAR=initial UNBALANCED=']
exit status 0
diff --git a/unit-tests/deptgt-makeflags.mk b/unit-tests/deptgt-makeflags.mk
index 70a9cd0b590b..0a0f410e14c4 100644
--- a/unit-tests/deptgt-makeflags.mk
+++ b/unit-tests/deptgt-makeflags.mk
@@ -1,30 +1,35 @@
-# $NetBSD: deptgt-makeflags.mk,v 1.4 2020/10/23 14:48:49 rillig Exp $
+# $NetBSD: deptgt-makeflags.mk,v 1.6 2020/11/15 20:20:58 rillig Exp $
#
# Tests for the special target .MAKEFLAGS in dependency declarations,
# which adds command line options later, at parse time.
+#
+# In these unit tests, it is often used to temporarily toggle the debug log
+# during parsing.
# The -D option sets a variable in the "Global" scope and thus can be
# undefined later.
.MAKEFLAGS: -D VAR
-
.if ${VAR} != 1
. error
.endif
+# Variables that are set via the -D command line option are normal global
+# variables and can thus be undefined later.
.undef VAR
-
.if defined(VAR)
. error
.endif
+# The -D command line option can define a variable again, after it has been
+# undefined.
.MAKEFLAGS: -D VAR
-
.if ${VAR} != 1
. error
.endif
+# The "dependency" for .MAKEFLAGS is split into words, interpreting the usual
+# quotes and escape sequences from the backslash.
.MAKEFLAGS: VAR="value"' with'\ spaces
-
.if ${VAR} != "value with spaces"
. error
.endif
@@ -32,7 +37,6 @@
# Variables set on the command line as VAR=value are placed in the
# "Command" scope and thus cannot be undefined.
.undef VAR
-
.if ${VAR} != "value with spaces"
. error
.endif
@@ -47,5 +51,43 @@
.endif
.MAKEFLAGS: -d0
+# An empty command line is skipped.
+.MAKEFLAGS: # none
+
+# Escape sequences like \n are interpreted.
+# The following line looks as if it assigned a newline to nl, but it doesn't.
+# Instead, the \n ends up as a line that is then interpreted as a variable
+# assignment. At that point, the line is simply "nl=\n", and the \n is
+# skipped since it is whitespace (see Parse_IsVar).
+.MAKEFLAGS: nl="\n"
+.if ${nl} != ""
+. error
+.endif
+
+# Next try at defining another newline variable. Since whitespace around the
+# variable value is trimmed, two empty variable expressions surround the
+# literal newline now. This prevents the newline from being skipped during
+# parsing. The ':=' assignment operator expands the empty variable
+# expressions, leaving only the newline as the variable value.
+#
+# This is one of the very few ways (maybe even the only one) to inject literal
+# newlines into a line that is being parsed. This may confuse the parser.
+# For example, in cond.c the parser only expects horizontal whitespace (' '
+# and '\t'), but no newlines.
+#.MAKEFLAGS: -dcpv
+.MAKEFLAGS: nl:="$${:U}\n$${:U}"
+.if ${nl} != ${.newline}
+. error
+.endif
+#.MAKEFLAGS: -d0
+
+# Unbalanced quotes produce an error message. If they occur anywhere in the
+# command line, the whole command line is skipped.
+.MAKEFLAGS: VAR=previous
+.MAKEFLAGS: VAR=initial UNBALANCED='
+.if ${VAR} != "previous"
+. error
+.endif
+
all:
@:;
diff --git a/unit-tests/deptgt-silent.exp b/unit-tests/deptgt-silent.exp
index 6cab88f53144..c9b89da0ae5e 100644
--- a/unit-tests/deptgt-silent.exp
+++ b/unit-tests/deptgt-silent.exp
@@ -1,3 +1,5 @@
+echo 'This is a loud command.'
+This is a loud command.
This is not echoed because of the @.
This is not echoed because of the .SILENT.
exit status 0
diff --git a/unit-tests/deptgt-silent.mk b/unit-tests/deptgt-silent.mk
index 64d54c00e632..335a1e897295 100644
--- a/unit-tests/deptgt-silent.mk
+++ b/unit-tests/deptgt-silent.mk
@@ -1,10 +1,15 @@
-# $NetBSD: deptgt-silent.mk,v 1.3 2020/09/10 21:40:50 rillig Exp $
+# $NetBSD: deptgt-silent.mk,v 1.4 2020/11/15 20:49:20 rillig Exp $
#
# Tests for the special target .SILENT in dependency declarations.
.SILENT: all
-all:
+all: loud
@echo 'This is not echoed because of the @.'
# Without the .SILENT, the following command would be echoed.
echo 'This is not echoed because of the .SILENT.'
+
+# Demonstrate that the .SILENT only applies to the particular target.
+# This is unlike .DELETE_ON_ERROR, which is a global setting.
+loud: .PHONY
+ echo 'This is a loud command.'
diff --git a/unit-tests/deptgt.exp b/unit-tests/deptgt.exp
index 2b899313b938..fee0563f5b37 100644
--- a/unit-tests/deptgt.exp
+++ b/unit-tests/deptgt.exp
@@ -1,5 +1,13 @@
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)
+ParseReadLine (35): ' : command for empty targets list'
+ParseReadLine (36): ': empty-source'
+ParseDoDependency(: empty-source)
+ParseReadLine (37): ' : command for empty targets list'
+ParseReadLine (38): '.MAKEFLAGS: -d0'
+ParseDoDependency(.MAKEFLAGS: -d0)
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 517ed5aa2b75..83f81b6f58ed 100644
--- a/unit-tests/deptgt.mk
+++ b/unit-tests/deptgt.mk
@@ -1,4 +1,4 @@
-# $NetBSD: deptgt.mk,v 1.8 2020/10/18 13:02:10 rillig Exp $
+# $NetBSD: deptgt.mk,v 1.9 2020/11/15 11:57:00 rillig Exp $
#
# Tests for special targets like .BEGIN or .SUFFIXES in dependency
# declarations.
@@ -27,5 +27,15 @@ target1 target2: sources # targets := [target1, target2]
VAR=value # targets := NULL
: command3 # parse error, since targets == NULL
+# In a dependency declaration, the list of targets can be empty.
+# It doesn't matter whether the empty string is generated by a variable
+# expression or whether it is just omitted.
+.MAKEFLAGS: -dp
+${:U}: empty-source
+ : command for empty targets list
+: empty-source
+ : command for empty targets list
+.MAKEFLAGS: -d0
+
all:
@:;
diff --git a/unit-tests/dir.mk b/unit-tests/dir.mk
index 24ce47d959ed..55e22d88b803 100644
--- a/unit-tests/dir.mk
+++ b/unit-tests/dir.mk
@@ -1,4 +1,4 @@
-# $NetBSD: dir.mk,v 1.7 2020/10/31 21:30:03 rillig Exp $
+# $NetBSD: dir.mk,v 1.8 2020/11/03 18:42:33 rillig Exp $
#
# Tests for dir.c.
@@ -64,7 +64,8 @@ fetch fetch-post extract extract-post:
: $@
# The expansions may have duplicates.
-# These are merged together because of the dependency line.
+# When the source of the dependency line is expanded later, each of the
+# expanded words will be the same.
all: dup-{1,1,1,1,1,1,1}
dup-1:
diff --git a/unit-tests/directive-elif.exp b/unit-tests/directive-elif.exp
index 39a9383953dd..6219b4896795 100644
--- a/unit-tests/directive-elif.exp
+++ b/unit-tests/directive-elif.exp
@@ -1 +1,17 @@
-exit status 0
+make: "directive-elif.mk" line 7: begin .elif misspellings tests, part 1
+make: "directive-elif.mk" line 9: 1-then
+make: "directive-elif.mk" line 18: begin .elif misspellings tests, part 2
+make: "directive-elif.mk" line 29: begin .elif misspellings tests, part 3
+make: "directive-elif.mk" line 41: which branch is taken on misspelling after false?
+make: "directive-elif.mk" line 49: else
+make: "directive-elif.mk" line 52: which branch is taken on misspelling after true?
+make: "directive-elif.mk" line 54: 1-then
+make: "directive-elif.mk" line 55: Unknown directive "elsif"
+make: "directive-elif.mk" line 56: 1-elsif
+make: "directive-elif.mk" line 57: Unknown directive "elsif"
+make: "directive-elif.mk" line 58: 2-elsif
+make: "directive-elif.mk" line 64: if-less elif
+make: "directive-elif.mk" line 69: warning: extra elif
+make: Fatal errors encountered -- cannot continue
+make: stopped in unit-tests
+exit status 1
diff --git a/unit-tests/directive-elif.mk b/unit-tests/directive-elif.mk
index fc8bb7e7d197..f9a43abfffc1 100644
--- a/unit-tests/directive-elif.mk
+++ b/unit-tests/directive-elif.mk
@@ -1,8 +1,72 @@
-# $NetBSD: directive-elif.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+# $NetBSD: directive-elif.mk,v 1.6 2020/11/12 19:46:36 rillig Exp $
#
# Tests for the .elif directive.
# TODO: Implementation
+.info begin .elif misspellings tests, part 1
+.if 1
+. info 1-then
+.elif 1 # ok
+. info 1-elif
+.elsif 1 # oops: misspelled
+. info 1-elsif
+.elseif 1 # oops: misspelled
+. info 1-elseif
+.endif
+
+.info begin .elif misspellings tests, part 2
+.if 0
+. info 0-then
+.elif 0 # ok
+. info 0-elif
+.elsif 0 # oops: misspelled
+. info 0-elsif
+.elseif 0 # oops: misspelled
+. info 0-elseif
+.endif
+
+.info begin .elif misspellings tests, part 3
+.if 0
+. info 0-then
+.elsif 0 # oops: misspelled
+. info 0-elsif
+.endif
+.if 0
+. info 0-then
+.elseif 0 # oops: misspelled
+. info 0-elseif
+.endif
+
+.info which branch is taken on misspelling after false?
+.if 0
+. info 0-then
+.elsif 1
+. info 1-elsif
+.elsif 2
+. info 2-elsif
+.else
+. info else
+.endif
+
+.info which branch is taken on misspelling after true?
+.if 1
+. info 1-then
+.elsif 1
+. info 1-elsif
+.elsif 2
+. info 2-elsif
+.else
+. info else
+.endif
+
+# Expect: "if-less elif"
+.elif 0
+
+.if 1
+.else
+# Expect: "warning: if-less elif"
+.elif
+.endif
+
all:
- @:;
diff --git a/unit-tests/directive-else.exp b/unit-tests/directive-else.exp
index 387577099b81..ca60595745a9 100644
--- a/unit-tests/directive-else.exp
+++ b/unit-tests/directive-else.exp
@@ -1,8 +1,11 @@
-make: "directive-else.mk" line 10: ok
-make: "directive-else.mk" line 14: ok
-make: "directive-else.mk" line 20: if-less else
-make: "directive-else.mk" line 26: ok
-make: "directive-else.mk" line 27: warning: extra else
+make: "directive-else.mk" line 11: The .else directive does not take arguments.
+make: "directive-else.mk" line 12: ok
+make: "directive-else.mk" line 16: ok
+make: "directive-else.mk" line 17: The .else directive does not take arguments.
+make: "directive-else.mk" line 22: if-less else
+make: "directive-else.mk" line 28: ok
+make: "directive-else.mk" line 29: warning: extra else
+make: "directive-else.mk" line 42: The .else directive does not take arguments.
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/unit-tests/directive-else.mk b/unit-tests/directive-else.mk
index 8fbbb5189ad5..fdd94007a1fa 100644
--- a/unit-tests/directive-else.mk
+++ b/unit-tests/directive-else.mk
@@ -1,7 +1,9 @@
-# $NetBSD: directive-else.mk,v 1.4 2020/10/24 08:46:08 rillig Exp $
+# $NetBSD: directive-else.mk,v 1.6 2020/11/13 09:01:59 rillig Exp $
#
# Tests for the .else directive.
+.MAKEFLAGS: -dL # To enable the check for ".else <cond>"
+
# The .else directive does not take any arguments.
# As of 2020-08-29, make doesn't warn about this.
.if 0
@@ -28,5 +30,17 @@
. info After an extra .else, everything is skipped.
.endif
+# An .else may have a comment. This comment does not count as an argument,
+# therefore no parse error.
+.if 0
+.else # comment
+.endif
+
+# A variable expression does count as an argument, even if it is empty.
+# XXX: This should be a parse error.
+.if 0
+.else ${:U}
+.endif
+
all:
@:;
diff --git a/unit-tests/directive-endif.mk b/unit-tests/directive-endif.mk
index 12b608ffbeee..b0b531af2f06 100644
--- a/unit-tests/directive-endif.mk
+++ b/unit-tests/directive-endif.mk
@@ -1,8 +1,27 @@
-# $NetBSD: directive-endif.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+# $NetBSD: directive-endif.mk,v 1.3 2020/11/12 22:40:11 rillig Exp $
#
# Tests for the .endif directive.
+#
+# See also:
+# Cond_EvalLine
# TODO: Implementation
+.MAKEFLAGS: -dL
+
+# Error: .endif does not take arguments
+# XXX: Missing error message
+.if 0
+.endif 0
+
+# Error: .endif does not take arguments
+# XXX: Missing error message
+.if 1
+.endif 1
+
+# Comments are allowed after an '.endif'.
+.if 2
+.endif # comment
+
all:
@:;
diff --git a/unit-tests/directive-export-env.mk b/unit-tests/directive-export-env.mk
index 49d1edb9f6fe..82b5e8087c6d 100644
--- a/unit-tests/directive-export-env.mk
+++ b/unit-tests/directive-export-env.mk
@@ -1,8 +1,12 @@
-# $NetBSD: directive-export-env.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+# $NetBSD: directive-export-env.mk,v 1.3 2020/11/03 17:17:31 rillig Exp $
#
# Tests for the .export-env directive.
# TODO: Implementation
+.export-en # oops: misspelled
+.export-env
+.export-environment # oops: misspelled
+
all:
@:;
diff --git a/unit-tests/directive-export-gmake.mk b/unit-tests/directive-export-gmake.mk
index 51de8fe55f64..d94cd9debf64 100644
--- a/unit-tests/directive-export-gmake.mk
+++ b/unit-tests/directive-export-gmake.mk
@@ -1,4 +1,4 @@
-# $NetBSD: directive-export-gmake.mk,v 1.2 2020/10/19 18:59:53 rillig Exp $
+# $NetBSD: directive-export-gmake.mk,v 1.3 2020/11/17 20:16:44 rillig Exp $
#
# Tests for the export directive (without leading dot), as in GNU make.
@@ -39,8 +39,8 @@ export VAR= leading spaces
# may be silently discarded. One such shell is dash, which is the default
# shell on Ubuntu and Debian.
export VAR =trailing space in varname
-.if ${:!env | grep trailing!} != "VAR =trailing space in varname"
-. if ${:!env | grep trailing!} != "" # for dash
+.if ${:!env | grep trailing || true!} != "VAR =trailing space in varname"
+. if ${:!env | grep trailing || true!} != "" # for dash
. error
. endif
.endif
diff --git a/unit-tests/directive-export-literal.mk b/unit-tests/directive-export-literal.mk
index 817c836fd6c4..51e5b522a3b9 100644
--- a/unit-tests/directive-export-literal.mk
+++ b/unit-tests/directive-export-literal.mk
@@ -1,4 +1,4 @@
-# $NetBSD: directive-export-literal.mk,v 1.5 2020/10/05 19:27:48 rillig Exp $
+# $NetBSD: directive-export-literal.mk,v 1.6 2020/11/03 17:17:31 rillig Exp $
#
# Tests for the .export-literal directive, which exports a variable value
# without expanding it.
@@ -7,5 +7,9 @@ UT_VAR= value with ${UNEXPANDED} expression
.export-literal UT_VAR
+.export-litera # oops: misspelled
+.export-literal # oops: missing argument
+.export-literally # oops: misspelled
+
all:
@echo "$$UT_VAR"
diff --git a/unit-tests/directive-export.exp b/unit-tests/directive-export.exp
index 39a9383953dd..bd828b63c10c 100644
--- a/unit-tests/directive-export.exp
+++ b/unit-tests/directive-export.exp
@@ -1 +1,4 @@
-exit status 0
+make: "directive-export.mk" line 25: Unknown directive "expor"
+make: Fatal errors encountered -- cannot continue
+make: stopped in unit-tests
+exit status 1
diff --git a/unit-tests/directive-export.mk b/unit-tests/directive-export.mk
index 6fbf8f09d4dc..bae50aecbdaf 100644
--- a/unit-tests/directive-export.mk
+++ b/unit-tests/directive-export.mk
@@ -1,4 +1,4 @@
-# $NetBSD: directive-export.mk,v 1.3 2020/10/29 17:27:12 rillig Exp $
+# $NetBSD: directive-export.mk,v 1.4 2020/11/03 17:17:31 rillig Exp $
#
# Tests for the .export directive.
@@ -21,5 +21,11 @@ VAR= value $$ ${INDIRECT}
. error
.endif
+# Tests for parsing the .export directive.
+.expor # misspelled
+.export # oops: missing argument
+.export VARNAME
+.exporting works # oops: misspelled
+
all:
@:;
diff --git a/unit-tests/directive-for.exp b/unit-tests/directive-for.exp
index 88ab45529e3f..af610cc34edd 100755
--- a/unit-tests/directive-for.exp
+++ b/unit-tests/directive-for.exp
@@ -1,19 +1,19 @@
-make: "directive-for.mk" line 100: outer
-make: "directive-for.mk" line 125: a:\ a:\file.txt
-make: "directive-for.mk" line 125: d:\\
-make: "directive-for.mk" line 125: d:\\file.txt
-make: "directive-for.mk" line 132: ( ( (
-make: "directive-for.mk" line 132: [ [ [
-make: "directive-for.mk" line 132: { { {
-make: "directive-for.mk" line 132: ) ) )
-make: "directive-for.mk" line 132: ] ] ]
-make: "directive-for.mk" line 132: } } }
-make: "directive-for.mk" line 132: (()) (()) (())
-make: "directive-for.mk" line 132: [[]] [[]] [[]]
-make: "directive-for.mk" line 132: {{}} {{}} {{}}
-make: "directive-for.mk" line 132: )( )( )(
-make: "directive-for.mk" line 132: ][ ][ ][
-make: "directive-for.mk" line 132: }{ }{ }{
-make: "directive-for.mk" line 140: outer value value
-make: "directive-for.mk" line 140: outer "quoted" \"quoted\"
+make: "directive-for.mk" line 108: outer
+make: "directive-for.mk" line 133: a:\ a:\file.txt
+make: "directive-for.mk" line 133: d:\\
+make: "directive-for.mk" line 133: d:\\file.txt
+make: "directive-for.mk" line 140: ( ( (
+make: "directive-for.mk" line 140: [ [ [
+make: "directive-for.mk" line 140: { { {
+make: "directive-for.mk" line 140: ) ) )
+make: "directive-for.mk" line 140: ] ] ]
+make: "directive-for.mk" line 140: } } }
+make: "directive-for.mk" line 140: (()) (()) (())
+make: "directive-for.mk" line 140: [[]] [[]] [[]]
+make: "directive-for.mk" line 140: {{}} {{}} {{}}
+make: "directive-for.mk" line 140: )( )( )(
+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\"
exit status 0
diff --git a/unit-tests/directive-for.mk b/unit-tests/directive-for.mk
index e0987d331a14..93f0a14f5892 100755
--- a/unit-tests/directive-for.mk
+++ b/unit-tests/directive-for.mk
@@ -1,6 +1,13 @@
-# $NetBSD: directive-for.mk,v 1.8 2020/10/25 15:49:03 rillig Exp $
+# $NetBSD: directive-for.mk,v 1.9 2020/11/15 20:20:58 rillig Exp $
#
# Tests for the .for directive.
+#
+# TODO: Describe naming conventions for the loop variables.
+# .for f in values
+# .for file in values
+# .for _FILE_ in values
+# .for .FILE. in values
+# .for _f_ in values
# Using the .for loop, lists of values can be produced.
# In simple cases, the :@var@${var}@ variable modifier can be used to
@@ -15,6 +22,7 @@ NUMBERS+= ${num}
.endif
# The .for loop also works for multiple iteration variables.
+# This is something that the variable modifier :@ cannot do.
.for name value in VARNAME value NAME2 value2
${name}= ${value}
.endfor
@@ -26,7 +34,7 @@ ${name}= ${value}
# just like the :M or :S variable modifiers.
#
# Until 2012-06-03, it had split the items exactly at whitespace, without
-# taking the quotes into account.
+# taking the quotes into account. This had resulted in 10 words.
#
.undef WORDS
.for var in one t\ w\ o "three three" 'four four' `five six`
diff --git a/unit-tests/directive-if-nested.exp b/unit-tests/directive-if-nested.exp
new file mode 100644
index 000000000000..1a9ae02f07b2
--- /dev/null
+++ b/unit-tests/directive-if-nested.exp
@@ -0,0 +1,2 @@
+make: "directive-if-nested.inc" line 1001: deeply nested .if directives
+exit status 0
diff --git a/unit-tests/directive-if-nested.mk b/unit-tests/directive-if-nested.mk
new file mode 100644
index 000000000000..19c8e9452660
--- /dev/null
+++ b/unit-tests/directive-if-nested.mk
@@ -0,0 +1,25 @@
+# $NetBSD: directive-if-nested.mk,v 1.1 2020/11/10 22:23:37 rillig Exp $
+#
+# Tests for deeply nested .if directives. By default, memory for 128 nested
+# .if directives is pre-allocated, any deeper nesting is reallocated.
+#
+# See also:
+# Cond_EvalLine
+
+GEN= directive-if-nested.inc
+
+all: set-up test tear-down
+
+set-up: .PHONY
+ @{ printf '.if %s\n' ${:U:range=1000}; \
+ printf '.info deeply nested .if directives\n'; \
+ printf '.endif # %s\n' ${:U:range=1000}; \
+ printf '\n'; \
+ printf 'all:\n'; \
+ } > ${GEN}
+
+test: .PHONY
+ @${MAKE} -f ${GEN}
+
+tear-down: .PHONY
+ @rm -f ${GEN}
diff --git a/unit-tests/directive-if.exp b/unit-tests/directive-if.exp
index 39a9383953dd..21a33fe4cfd6 100644
--- a/unit-tests/directive-if.exp
+++ b/unit-tests/directive-if.exp
@@ -1 +1,15 @@
-exit status 0
+make: "directive-if.mk" line 13: 0 evaluates to false.
+make: "directive-if.mk" line 17: 1 evaluates to true.
+make: "directive-if.mk" line 40: Unknown directive "ifx"
+make: "directive-if.mk" line 41: Unknown directive "error"
+make: "directive-if.mk" line 42: if-less else
+make: "directive-if.mk" line 43: Unknown directive "error"
+make: "directive-if.mk" line 44: if-less endif
+make: "directive-if.mk" line 47: Malformed conditional ()
+make: "directive-if.mk" line 57: Quotes in plain words are probably a mistake.
+make: "directive-if.mk" line 66: Don't do this, always put a space after a directive.
+make: "directive-if.mk" line 70: Don't do this, always put a space after a directive.
+make: "directive-if.mk" line 76: Don't do this, always put a space around comparison operators.
+make: Fatal errors encountered -- cannot continue
+make: stopped in unit-tests
+exit status 1
diff --git a/unit-tests/directive-if.mk b/unit-tests/directive-if.mk
index 72d7d0e2d920..3b1d13c7a0c0 100644
--- a/unit-tests/directive-if.mk
+++ b/unit-tests/directive-if.mk
@@ -1,8 +1,81 @@
-# $NetBSD: directive-if.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+# $NetBSD: directive-if.mk,v 1.7 2020/11/15 20:20:58 rillig Exp $
#
# Tests for the .if directive.
+#
+# See also:
+# cond-*.mk
# TODO: Implementation
+.if 0
+. error
+.else
+. info 0 evaluates to false.
+.endif
+
+.if 1
+. info 1 evaluates to true.
+.else
+. error
+.endif
+
+# There is no '.ifx'.
+#
+# The commit from 2005-05-01 intended to detect this situation, but it failed
+# to do this since the call to is_token had its arguments switched. They were
+# expected as (str, token, token_len) but were actually passed as (token, str,
+# token_len). This made is_token return true even if the directive was
+# directly followed by alphanumerical characters, which was wrong. The
+# typical cases produced an error message such as "Malformed conditional
+# (x 123)", while the intended error message was "Unknown directive".
+#
+# Back at that time, the commits only modified the main code but did not add
+# the corresponding unit tests. This allowed the bug to hide for more than
+# 15 years.
+#
+# Since 2020-11-10, the correct error message is produced. The '.ifx' is no
+# longer interpreted as a variant of '.if', therefore the '.error' and '.else'
+# are interpreted as ordinary directives, producing the error messages
+# "if-less else" and "if-less endif".
+.ifx 123
+. error
+.else
+. error
+.endif
+
+# Missing condition.
+.if
+. error
+.else
+. error
+.endif
+
+# A plain word must not start with a '"'. It may contain a embedded quotes
+# though, which are kept. The quotes need not be balanced. The next space
+# ends the word, and the remaining " || 1" is parsed as "or true".
+.if ${:Uplain"""""} == plain""""" || 1
+. info Quotes in plain words are probably a mistake.
+# XXX: Accepting quotes in plain words is probably a mistake as well.
+.else
+. error
+.endif
+
+.if0
+. error
+.else
+. info Don't do this, always put a space after a directive.
+.endif
+
+.if${:U-3}
+. info Don't do this, always put a space after a directive.
+.else
+. error
+.endif
+
+.if${:U-3}>-4
+. info Don't do this, always put a space around comparison operators.
+.else
+. error
+.endif
+
all:
- @:;
diff --git a/unit-tests/directive-ifdef.exp b/unit-tests/directive-ifdef.exp
index 39a9383953dd..b8453ecc4124 100644
--- a/unit-tests/directive-ifdef.exp
+++ b/unit-tests/directive-ifdef.exp
@@ -1 +1,2 @@
+make: "directive-ifdef.mk" line 12: Function calls in .ifdef are possible.
exit status 0
diff --git a/unit-tests/directive-ifdef.mk b/unit-tests/directive-ifdef.mk
index 64f073fb5ae1..516e095c464f 100644
--- a/unit-tests/directive-ifdef.mk
+++ b/unit-tests/directive-ifdef.mk
@@ -1,8 +1,18 @@
-# $NetBSD: directive-ifdef.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+# $NetBSD: directive-ifdef.mk,v 1.3 2020/11/08 22:38:28 rillig Exp $
#
# Tests for the .ifdef directive.
# TODO: Implementation
+DEFINED= defined
+
+# It looks redundant to have a call to defined() in an .ifdef, but it's
+# possible. The .ifdef only affects plain symbols, not function calls.
+.ifdef defined(DEFINED)
+. info Function calls in .ifdef are possible.
+.else
+. error
+.endif
+
all:
@:;
diff --git a/unit-tests/directive-ifmake.exp b/unit-tests/directive-ifmake.exp
index 5fefeb252b48..fd4bcae151a0 100644
--- a/unit-tests/directive-ifmake.exp
+++ b/unit-tests/directive-ifmake.exp
@@ -1,9 +1,10 @@
-make: "directive-ifmake.mk" line 8: ok: positive condition works
-make: "directive-ifmake.mk" line 19: ok: negation works
-make: "directive-ifmake.mk" line 25: ok: double negation works
-make: "directive-ifmake.mk" line 32: ok: both mentioned
-make: "directive-ifmake.mk" line 39: ok: only those mentioned
-make: "directive-ifmake.mk" line 49: Targets can even be added at parse time.
+make: "directive-ifmake.mk" line 13: ok: positive condition works
+make: "directive-ifmake.mk" line 24: ok: negation works
+make: "directive-ifmake.mk" line 33: ok: double negation works
+make: "directive-ifmake.mk" line 40: ok: both mentioned
+make: "directive-ifmake.mk" line 47: ok: only those mentioned
+make: "directive-ifmake.mk" line 57: Targets can even be added at parse time.
+make: "directive-ifmake.mk" line 75: ok
: first
: second
: late-target
diff --git a/unit-tests/directive-ifmake.mk b/unit-tests/directive-ifmake.mk
index 20329bc5ce25..4d49add72626 100644
--- a/unit-tests/directive-ifmake.mk
+++ b/unit-tests/directive-ifmake.mk
@@ -1,44 +1,52 @@
-# $NetBSD: directive-ifmake.mk,v 1.4 2020/08/30 14:25:45 rillig Exp $
+# $NetBSD: directive-ifmake.mk,v 1.8 2020/11/15 20:20:58 rillig Exp $
#
# Tests for the .ifmake directive, which provides a shortcut for asking
# whether a certain target is requested to be made from the command line.
+#
+# TODO: Describe why the shortcut may be useful (if it's useful at all),
+# instead of sticking to the simple '.if' only.
+
+# The targets 'first' and 'second' are passed in on the command line.
# This is the most basic form.
.ifmake first
-.info ok: positive condition works
+. info ok: positive condition works
.else
-.warning positive condition fails
+. warning positive condition fails
.endif
# The not operator works as expected.
# An alternative interpretation were that this condition is asking whether
# the target "!first" was requested. To distinguish this, see the next test.
.ifmake !first
-.warning unexpected
+. warning unexpected
.else
-.info ok: negation works
+. info ok: negation works
.endif
# See if the exclamation mark really means "not", or if it is just part of
-# the target name.
+# the target name. Since it means 'not', the two exclamation marks are
+# effectively ignored, and 'first' is indeed a requested target. If the
+# exclamation mark were part of the name instead, the name would be '!!first',
+# and such a target was not requested to be made.
.ifmake !!first
-.info ok: double negation works
+. info ok: double negation works
.else
-.warning double negation fails
+. warning double negation fails
.endif
# Multiple targets can be combined using the && and || operators.
.ifmake first && second
-.info ok: both mentioned
+. info ok: both mentioned
.else
-.warning && does not work as expected
+. warning && does not work as expected
.endif
# Negation also works in complex conditions.
.ifmake first && !unmentioned
-.info ok: only those mentioned
+. info ok: only those mentioned
.else
-.warning && with ! does not work as expected
+. warning && with ! does not work as expected
.endif
# Using the .MAKEFLAGS special dependency target, arbitrary command
@@ -46,10 +54,29 @@
# possible to extend the targets to be made.
.MAKEFLAGS: late-target
.ifmake late-target
-.info Targets can even be added at parse time.
+. info Targets can even be added at parse time.
+.else
+. info No, targets cannot be added at parse time anymore.
+.endif
+
+# Numbers are interpreted as numbers, no matter whether the directive is
+# a plain .if or an .ifmake.
+.ifmake 0
+. error
+.endif
+.ifmake 1
.else
-.info No, targets cannot be added at parse time anymore.
+. error
.endif
+# A condition that consists of a variable expression only (without any
+# comparison operator) can be used with .if and the other .ifxxx directives.
+.ifmake ${:Ufirst}
+. info ok
+.else
+. error
+.endif
+
+
first second unmentioned late-target:
: $@
diff --git a/unit-tests/directive-include.exp b/unit-tests/directive-include.exp
index 3a75ce38728b..af56eefb2b88 100755
--- a/unit-tests/directive-include.exp
+++ b/unit-tests/directive-include.exp
@@ -2,4 +2,7 @@ CondParser_Eval: ${.MAKE.MAKEFILES:T} != "${.PARSEFILE} null"
lhs = "directive-include.mk null", rhs = "directive-include.mk null", op = !=
CondParser_Eval: ${.MAKE.MAKEFILES:T} != "${.PARSEFILE} null"
lhs = "directive-include.mk null", rhs = "directive-include.mk null", op = !=
-exit status 0
+make: "directive-include.mk" line 25: Could not find nonexistent.mk
+make: Fatal errors encountered -- cannot continue
+make: stopped in unit-tests
+exit status 1
diff --git a/unit-tests/directive-include.mk b/unit-tests/directive-include.mk
index d71b27ef2c63..120706cef8d7 100755
--- a/unit-tests/directive-include.mk
+++ b/unit-tests/directive-include.mk
@@ -1,4 +1,4 @@
-# $NetBSD: directive-include.mk,v 1.3 2020/10/31 23:01:23 rillig Exp $
+# $NetBSD: directive-include.mk,v 1.4 2020/11/03 17:17:31 rillig Exp $
#
# Tests for the .include directive, which includes another file.
@@ -22,5 +22,10 @@
. error
.endif
+.include "nonexistent.mk"
+.include "/dev/null" # size 0
+# including a directory technically succeeds, but shouldn't.
+#.include "." # directory
+
all:
@:;
diff --git a/unit-tests/directive-info.exp b/unit-tests/directive-info.exp
index 39a9383953dd..971f417b706a 100644
--- a/unit-tests/directive-info.exp
+++ b/unit-tests/directive-info.exp
@@ -1 +1,14 @@
-exit status 0
+make: "directive-info.mk" line 7: begin .info tests
+make: "directive-info.mk" line 8: Unknown directive "inf"
+make: "directive-info.mk" line 9: Unknown directive "info"
+make: "directive-info.mk" line 10: message
+make: "directive-info.mk" line 11: indented message
+make: "directive-info.mk" line 12: Unknown directive "information"
+make: "directive-info.mk" line 13: message
+make: "directive-info.mk" line 18: Unknown directive "info"
+make: "directive-info.mk" line 19: Unknown directive "info"
+make: "directive-info.mk" line 22: Unknown directive "info-message"
+make: "directive-info.mk" line 23: no-target: no-source
+make: Fatal errors encountered -- cannot continue
+make: stopped in unit-tests
+exit status 1
diff --git a/unit-tests/directive-info.mk b/unit-tests/directive-info.mk
index 3eb972ad7a0e..bbfc80ea0c9a 100644
--- a/unit-tests/directive-info.mk
+++ b/unit-tests/directive-info.mk
@@ -1,8 +1,27 @@
-# $NetBSD: directive-info.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+# $NetBSD: directive-info.mk,v 1.4 2020/11/15 11:57:00 rillig Exp $
#
# Tests for the .info directive.
# TODO: Implementation
+.info begin .info tests
+.inf # misspelled
+.info # oops: message should be "missing parameter"
+.info message
+.info indented message
+.information
+.information message # oops: misspelled
+.info.man: # not a message, but possibly a suffix rule
+
+# Even if lines would have trailing whitespace, this would be trimmed by
+# ParseGetLine.
+.info
+.info # comment
+
+.info: message # This is a dependency declaration.
+.info-message # This is an unknown directive.
+.info no-target: no-source # This is a .info directive, not a dependency.
+# See directive.mk for more tests of this kind.
+
all:
@:;
diff --git a/unit-tests/directive-sinclude.mk b/unit-tests/directive-sinclude.mk
index b1faa7d41e65..1932e7b3ba13 100755
--- a/unit-tests/directive-sinclude.mk
+++ b/unit-tests/directive-sinclude.mk
@@ -1,7 +1,11 @@
-# $NetBSD: directive-sinclude.mk,v 1.1 2020/09/13 09:20:23 rillig Exp $
+# $NetBSD: directive-sinclude.mk,v 1.2 2020/11/15 20:20:58 rillig Exp $
#
# Tests for the .sinclude directive, which includes another file,
# silently skipping it if it cannot be opened.
+#
+# The 'silently skipping' only applies to the case where the file cannot be
+# opened. Parse errors and other errors are handled the same way as in the
+# other .include directives.
# TODO: Implementation
diff --git a/unit-tests/directive-undef.exp b/unit-tests/directive-undef.exp
index 39a9383953dd..303d5a3e2a27 100644
--- a/unit-tests/directive-undef.exp
+++ b/unit-tests/directive-undef.exp
@@ -1 +1,4 @@
-exit status 0
+make: "directive-undef.mk" line 16: Unknown directive "unde"
+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 aac110437686..c72513a1bf5a 100644
--- a/unit-tests/directive-undef.mk
+++ b/unit-tests/directive-undef.mk
@@ -1,4 +1,4 @@
-# $NetBSD: directive-undef.mk,v 1.4 2020/10/24 08:46:08 rillig Exp $
+# $NetBSD: directive-undef.mk,v 1.5 2020/11/03 17:17:31 rillig Exp $
#
# Tests for the .undef directive.
@@ -13,5 +13,9 @@
. warning $1$2$3
.endif
+.unde # misspelled
+.undef # oops: missing argument
+.undefined # oops: misspelled
+
all:
@:;
diff --git a/unit-tests/directive-unexport-env.mk b/unit-tests/directive-unexport-env.mk
index 34d867d706ef..637286af0d6b 100644
--- a/unit-tests/directive-unexport-env.mk
+++ b/unit-tests/directive-unexport-env.mk
@@ -1,8 +1,12 @@
-# $NetBSD: directive-unexport-env.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+# $NetBSD: directive-unexport-env.mk,v 1.3 2020/11/03 17:17:31 rillig Exp $
#
# Tests for the .unexport-env directive.
# TODO: Implementation
+.unexport-en # oops: misspelled
+.unexport-env # ok
+.unexport-environment # oops: misspelled
+
all:
@:;
diff --git a/unit-tests/directive-unexport.exp b/unit-tests/directive-unexport.exp
index 263a2fedcc0b..72b24e7344fc 100644
--- a/unit-tests/directive-unexport.exp
+++ b/unit-tests/directive-unexport.exp
@@ -2,4 +2,7 @@ make: "directive-unexport.mk" line 14: UT_A=a UT_B=b UT_C=c
make: "directive-unexport.mk" line 15: UT_A UT_B UT_C
make: "directive-unexport.mk" line 23: UT_A=a UT_B=b UT_C=c
make: "directive-unexport.mk" line 24:
-exit status 0
+make: "directive-unexport.mk" line 26: Unknown directive "unexpor"
+make: Fatal errors encountered -- cannot continue
+make: stopped in unit-tests
+exit status 1
diff --git a/unit-tests/directive-unexport.mk b/unit-tests/directive-unexport.mk
index b44932904805..3ba4a1b1f307 100644
--- a/unit-tests/directive-unexport.mk
+++ b/unit-tests/directive-unexport.mk
@@ -1,4 +1,4 @@
-# $NetBSD: directive-unexport.mk,v 1.4 2020/10/30 23:54:42 sjg Exp $
+# $NetBSD: directive-unexport.mk,v 1.5 2020/11/03 17:17:31 rillig Exp $
#
# Tests for the .unexport directive.
@@ -23,5 +23,9 @@ UT_C= c
.info ${:!env|sort|grep '^UT_'!}
.info ${.MAKE.EXPORTED}
+.unexpor # misspelled
+.unexport # oops: missing argument
+.unexporting works # oops: misspelled
+
all:
@:;
diff --git a/unit-tests/directive-warning.exp b/unit-tests/directive-warning.exp
index 39a9383953dd..630285fd3612 100644
--- a/unit-tests/directive-warning.exp
+++ b/unit-tests/directive-warning.exp
@@ -1 +1,11 @@
-exit status 0
+make: "directive-warning.mk" line 7: Unknown directive "warn"
+make: "directive-warning.mk" line 8: Unknown directive "warn"
+make: "directive-warning.mk" line 9: Unknown directive "warnin"
+make: "directive-warning.mk" line 10: Unknown directive "warnin"
+make: "directive-warning.mk" line 11: Unknown directive "warning"
+make: "directive-warning.mk" line 12: warning: message
+make: "directive-warning.mk" line 13: Unknown directive "warnings"
+make: "directive-warning.mk" line 14: warning: messages
+make: Fatal errors encountered -- cannot continue
+make: stopped in unit-tests
+exit status 1
diff --git a/unit-tests/directive-warning.mk b/unit-tests/directive-warning.mk
index e1e636e3ec9f..75560aa9e4df 100644
--- a/unit-tests/directive-warning.mk
+++ b/unit-tests/directive-warning.mk
@@ -1,8 +1,17 @@
-# $NetBSD: directive-warning.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+# $NetBSD: directive-warning.mk,v 1.3 2020/11/03 17:17:31 rillig Exp $
#
# Tests for the .warning directive.
# TODO: Implementation
+.warn # misspelled
+.warn message # misspelled
+.warnin # misspelled
+.warnin message # misspelled
+.warning # oops: should be "missing argument"
+.warning message # ok
+.warnings # misspelled
+.warnings messages # oops
+
all:
@:;
diff --git a/unit-tests/directive.exp b/unit-tests/directive.exp
index 39a9383953dd..b93d768169ab 100644
--- a/unit-tests/directive.exp
+++ b/unit-tests/directive.exp
@@ -1 +1,12 @@
-exit status 0
+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
+make: "directive.mk" line 26: := value
+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.mk b/unit-tests/directive.mk
index 8d01a49a34cf..d463ce4f009a 100644
--- a/unit-tests/directive.mk
+++ b/unit-tests/directive.mk
@@ -1,8 +1,35 @@
-# $NetBSD: directive.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+# $NetBSD: directive.mk,v 1.4 2020/11/15 11:57:00 rillig Exp $
#
# Tests for the preprocessing directives, such as .if or .info.
# TODO: Implementation
+# Unknown directives are correctly named in the error messages,
+# even if they are indented.
+.indented none
+. indented 2 spaces
+. indented tab
+
+# Directives must be written directly, not indirectly via variable
+# expressions.
+.${:Uinfo} directives cannot be indirect
+
+# There is no directive called '.target', therefore this is parsed as a
+# dependency declaration with 2 targets and 1 source.
+.target target: source
+
+# This looks ambiguous. It could be either an .info message or a variable
+# assignment. It is a variable assignment.
+.MAKEFLAGS: -dv
+.info:= value
+.info?= value # This is a variable assignment as well.
+.info := value # The space after the '.info' makes this
+ # a directive.
+.MAKEFLAGS: -d0
+
+# This is a dependency since directives must be given directly.
+# Not even the space after the '.info' can change anything about this.
+.${:Uinfo} : source
+
all:
@:;
diff --git a/unit-tests/directives.exp b/unit-tests/directives.exp
deleted file mode 100644
index 15dd9aa9e962..000000000000
--- a/unit-tests/directives.exp
+++ /dev/null
@@ -1,42 +0,0 @@
-make: "directives.mk" line 10: begin .export tests
-make: "directives.mk" line 11: Unknown directive "expor"
-make: "directives.mk" line 20: begin .export-env tests
-make: "directives.mk" line 30: begin .export-literal tests
-make: "directives.mk" line 40: begin .info tests
-make: "directives.mk" line 41: Unknown directive "inf"
-make: "directives.mk" line 42: Unknown directive "info"
-make: "directives.mk" line 43: message
-make: "directives.mk" line 44: indented message
-make: "directives.mk" line 45: Unknown directive "information"
-make: "directives.mk" line 46: message
-make: "directives.mk" line 50: begin .undef tests
-make: "directives.mk" line 51: Unknown directive "unde"
-make: "directives.mk" line 60: begin .unexport tests
-make: "directives.mk" line 61: Unknown directive "unexpor"
-make: "directives.mk" line 70: begin .unexport-env tests
-make: "directives.mk" line 80: begin .warning tests
-make: "directives.mk" line 81: Unknown directive "warn"
-make: "directives.mk" line 82: Unknown directive "warnin"
-make: "directives.mk" line 83: Unknown directive "warning"
-make: "directives.mk" line 84: warning: message
-make: "directives.mk" line 85: Unknown directive "warnings"
-make: "directives.mk" line 86: warning: messages
-make: "directives.mk" line 90: begin .elif misspellings tests, part 1
-make: "directives.mk" line 100: begin .elif misspellings tests, part 2
-make: "directives.mk" line 110: begin .elif misspellings tests, part 3
-make: "directives.mk" line 120: which branch is taken on misspelling after false?
-make: "directives.mk" line 127: else taken
-make: "directives.mk" line 130: which branch is taken on misspelling after true?
-make: "directives.mk" line 132: Unknown directive "elsif"
-make: "directives.mk" line 133: 1 taken
-make: "directives.mk" line 134: Unknown directive "elsif"
-make: "directives.mk" line 135: 2 taken
-make: "directives.mk" line 140: Unknown directive "indented"
-make: "directives.mk" line 141: Unknown directive "indented"
-make: "directives.mk" line 142: Unknown directive "indented"
-make: "directives.mk" line 143: Unknown directive "info"
-make: "directives.mk" line 150: Could not find nonexistent.mk
-make: "directives.mk" line 160: end of the tests
-make: Fatal errors encountered -- cannot continue
-make: stopped in unit-tests
-exit status 1
diff --git a/unit-tests/directives.mk b/unit-tests/directives.mk
deleted file mode 100644
index 436a5d35e4ec..000000000000
--- a/unit-tests/directives.mk
+++ /dev/null
@@ -1,163 +0,0 @@
-# $NetBSD: directives.mk,v 1.6 2020/10/24 08:46:08 rillig Exp $
-#
-# Tests for parsing directives, in the same order as in the manual page.
-#
-# Each test group has 10 lines, to keep the line numbers in directives.exp
-# stable.
-#
-# no tests for .error since it exits immediately, see ParseMessage.
-
-.info begin .export tests
-.expor # misspelled
-.export # oops: missing argument
-.export VARNAME
-.exporting works # oops: misspelled
-
-
-
-
-
-.info begin .export-env tests
-.export-en # oops: misspelled
-.export-env
-.export-environment # oops: misspelled
-
-
-
-
-
-
-.info begin .export-literal tests
-.export-litera # oops: misspelled
-.export-literal # oops: missing argument
-.export-literal VARNAME
-.export-literally # oops: misspelled
-
-
-
-
-
-.info begin .info tests
-.inf # misspelled
-.info # oops: message should be "missing parameter"
-.info message
-.info indented message
-.information
-.information message # oops: misspelled
-.info.man: # not a message, but a suffix rule
-
-
-.info begin .undef tests
-.unde # misspelled
-.undef # oops: missing argument
-.undefined # oops: misspelled
-.undef VARNAME
-
-
-
-
-
-.info begin .unexport tests
-.unexpor # misspelled
-.unexport # oops: missing argument
-.unexport VARNAME # ok
-.unexporting works # oops: misspelled
-
-
-
-
-
-.info begin .unexport-env tests
-.unexport-en # misspelled
-.unexport-env # ok
-.unexport-environment # oops: misspelled
-
-
-
-
-
-
-.info begin .warning tests
-.warn # misspelled
-.warnin # misspelled
-.warning # oops: should be "missing argument"
-.warning message # ok
-.warnings # misspelled
-.warnings messages # oops
-
-
-
-.info begin .elif misspellings tests, part 1
-.if 1
-.elif 1 # ok
-.elsif 1 # oops: misspelled
-.elseif 1 # oops: misspelled
-.endif
-
-
-
-
-.info begin .elif misspellings tests, part 2
-.if 0
-.elif 0 # ok
-.elsif 0 # oops: misspelled
-.elseif 0 # oops: misspelled
-.endif
-
-
-
-
-.info begin .elif misspellings tests, part 3
-.if 0
-.elsif 0 # oops: misspelled
-.endif
-.if 0
-.elseif 0 # oops: misspelled
-.endif
-
-
-
-.info which branch is taken on misspelling after false?
-.if 0
-.elsif 1
-. info 1 taken
-.elsif 2
-. info 2 taken
-.else
-. info else taken
-.endif
-
-.info which branch is taken on misspelling after true?
-.if 1
-.elsif 1
-. info 1 taken
-.elsif 2
-. info 2 taken
-.else
-. info else taken
-.endif
-
-.indented none
-. indented 2 spaces
-. indented tab
-.${:Uinfo} directives cannot be indirect
-
-
-
-
-
-
-.include "nonexistent.mk"
-.include "/dev/null" # size 0
-# including a directory technically succeeds, but shouldn't.
-#.include "." # directory
-
-
-
-
-
-
-.info end of the tests
-
-all:
- @:
diff --git a/unit-tests/dollar.exp b/unit-tests/dollar.exp
index 496adc02f157..9b8c9d638a87 100644
--- a/unit-tests/dollar.exp
+++ b/unit-tests/dollar.exp
@@ -1,7 +1,7 @@
Printing dollar from literals and variables
-To survive the parser, a dollar character must be doubled.
+To survive the parser, a dollar sign must be doubled.
1 dollar literal => <single-quote-var-value>
1 dollar literal eol => <>
2 dollar literal => <$>
diff --git a/unit-tests/dollar.mk b/unit-tests/dollar.mk
index b7f7d9fa7c9e..8a06c5bf722b 100644
--- a/unit-tests/dollar.mk
+++ b/unit-tests/dollar.mk
@@ -1,6 +1,6 @@
-# $NetBSD: dollar.mk,v 1.3 2020/05/17 09:37:48 rillig Exp $
+# $NetBSD: dollar.mk,v 1.4 2020/11/03 18:21:36 rillig Exp $
#
-# Test the various places where a dollar character can appear and
+# Test the various places where a dollar sign can appear and
# see what happens. There are lots of surprises here.
#
@@ -30,7 +30,7 @@ ${:U'}= single-quote-var-value'
all:
$H 'Printing dollar from literals and variables'
- $C 'To survive the parser, a dollar character must be doubled.'
+ $C 'To survive the parser, a dollar sign must be doubled.'
$T '1 dollar literal' '$'
$T '1 dollar literal eol' ''$
$T '2 dollar literal' '$$'
diff --git a/unit-tests/envfirst.mk b/unit-tests/envfirst.mk
index fedcc0a75056..b5cfc4f5c578 100644
--- a/unit-tests/envfirst.mk
+++ b/unit-tests/envfirst.mk
@@ -1,7 +1,9 @@
-# $NetBSD: envfirst.mk,v 1.3 2020/10/24 08:46:08 rillig Exp $
+# $NetBSD: envfirst.mk,v 1.4 2020/11/09 20:50:56 rillig Exp $
#
# The -e option makes environment variables stronger than global variables.
+.MAKEFLAGS: -e
+
.if ${FROM_ENV} != value-from-env
. error ${FROM_ENV}
.endif
diff --git a/unit-tests/error.exp b/unit-tests/error.exp
index 8d0ebef89e8e..3adc099a4625 100644
--- a/unit-tests/error.exp
+++ b/unit-tests/error.exp
@@ -1,6 +1,6 @@
-make: "error.mk" line 3: just FYI
-make: "error.mk" line 4: warning: this could be serious
-make: "error.mk" line 5: this is fatal
+make: "error.mk" line 6: just FYI
+make: "error.mk" line 7: warning: this could be serious
+make: "error.mk" line 8: this is fatal
make: stopped in unit-tests
exit status 1
diff --git a/unit-tests/error.mk b/unit-tests/error.mk
index 6e2654dbea72..0029b3bc6aa9 100644
--- a/unit-tests/error.mk
+++ b/unit-tests/error.mk
@@ -1,10 +1,12 @@
-# $NetBSD: error.mk,v 1.2 2020/10/24 08:34:59 rillig Exp $
+# $NetBSD: error.mk,v 1.3 2020/11/03 17:38:45 rillig Exp $
+#
+# Demonstrate that the .error directive exits immediately, without
+# continuing parsing until the end of the file.
.info just FYI
.warning this could be serious
.error this is fatal
+.info this is not reached because of the .error above
all:
-
-.info.html:
- @echo this should be ignored
+ : this is not reached because of the .error
diff --git a/unit-tests/escape.mk b/unit-tests/escape.mk
index 264b124bf1ad..8bdd3ad2ab49 100644
--- a/unit-tests/escape.mk
+++ b/unit-tests/escape.mk
@@ -1,4 +1,4 @@
-# $NetBSD: escape.mk,v 1.13 2020/10/24 08:50:17 rillig Exp $
+# $NetBSD: escape.mk,v 1.14 2020/11/03 17:38:45 rillig Exp $
#
# Test backslash escaping.
@@ -187,7 +187,7 @@ var-1bsnl-space: .PHONY __printvars \
# Backslash-newline in a command is retained.
#
# The "#" in "# second line without space" makes it a comment instead
-# of a syntax error if the preceding line is parsed incorretly.
+# of a syntax error if the preceding line is parsed incorrectly.
# The ":" in "third line':" makes it look like the start of a
# target instead of a syntax error if the first line is parsed incorrectly.
#
@@ -220,7 +220,7 @@ cmd-1bsnl-eof:
# XXX: This may differ from POSIX, but matches gmake.
#
# When make passes two backslashes to the shell, the shell will pass one
-# backslash to the echo commant.
+# backslash to the echo command.
#
all: cmd-2bsnl
cmd-2bsnl: .PHONY
diff --git a/unit-tests/forloop.exp b/unit-tests/forloop.exp
index 63b67c15bcc6..422711b41247 100644
--- a/unit-tests/forloop.exp
+++ b/unit-tests/forloop.exp
@@ -1,17 +1,17 @@
-x=one
-x="two and three"
-x=four
-x="five"
-x=-I/this
-x=-I"This or that"
-x=-Ithat
-x="-DTHIS=\"this and that\""
-cfl=-I/this -I"This or that" -Ithat "-DTHIS=\"this and that\""
-newline-item=(a)
-a=one b="two and three"
-a=four b="five"
-a=ONE b="TWO AND THREE"
-a=FOUR b="FIVE"
+make: "forloop.mk" line 14: x=one
+make: "forloop.mk" line 14: x="two and three"
+make: "forloop.mk" line 14: x=four
+make: "forloop.mk" line 14: x="five"
+make: "forloop.mk" line 20: x=-I/this
+make: "forloop.mk" line 20: x=-I"This or that"
+make: "forloop.mk" line 20: x=-Ithat
+make: "forloop.mk" line 20: x="-DTHIS=\"this and that\""
+make: "forloop.mk" line 27: cfl=-I/this -I"This or that" -Ithat "-DTHIS=\"this and that\""
+make: "forloop.mk" line 41: newline-item=(a)
+make: "forloop.mk" line 47: a=one b="two and three"
+make: "forloop.mk" line 47: a=four b="five"
+make: "forloop.mk" line 47: a=ONE b="TWO AND THREE"
+make: "forloop.mk" line 47: a=FOUR b="FIVE"
We expect an error next:
make: "forloop.mk" line 46: Wrong number of words (9) in .for substitution list with 2 variables
make: Fatal errors encountered -- cannot continue
diff --git a/unit-tests/forloop.mk b/unit-tests/forloop.mk
index 0162ffa27a07..cef05cbe4c61 100644
--- a/unit-tests/forloop.mk
+++ b/unit-tests/forloop.mk
@@ -1,4 +1,4 @@
-# $NetBSD: forloop.mk,v 1.6 2020/10/24 08:50:17 rillig Exp $
+# $NetBSD: forloop.mk,v 1.7 2020/11/03 17:37:57 rillig Exp $
all: for-loop
@@ -11,40 +11,40 @@ XTRA_LIST= xtra
.else
. for x in ${LIST}
-X!= echo 'x=$x' >&2; echo
+. info x=$x
. endfor
CFL= -I/this -I"This or that" -Ithat "-DTHIS=\"this and that\""
cfl=
. for x in ${CFL}
-X!= echo 'x=$x' >&2; echo
+. info x=$x
. if empty(cfl)
cfl= $x
. else
cfl+= $x
. endif
. endfor
-X!= echo 'cfl=${cfl}' >&2; echo
+. info cfl=${cfl}
. if ${cfl} != ${CFL}
-. error ${.newline}'${cfl}' != ${.newline}'${CFL}'
+. error ${.newline}${cfl} != ${.newline}${CFL}
. endif
. for a b in ${EMPTY}
-X!= echo 'a=$a b=$b' >&2; echo
+. info a=$a b=$b
. endfor
# Since at least 1993, iteration stops at the first newline.
# Back then, the .newline variable didn't exist, therefore it was unlikely
# that a newline ever occurred.
. for var in a${.newline}b${.newline}c
-X!= echo 'newline-item=('${var:Q}')' 1>&2; echo
+. info newline-item=(${var})
. endfor
.endif # for-fail
.for a b in ${LIST} ${LIST:tu} ${XTRA_LIST}
-X!= echo 'a=$a b=$b' >&2; echo
+. info a=$a b=$b
.endfor
for-loop:
diff --git a/unit-tests/forsubst.mk b/unit-tests/forsubst.mk
index 79af3d9a45fb..9f293ab7f94e 100644
--- a/unit-tests/forsubst.mk
+++ b/unit-tests/forsubst.mk
@@ -1,4 +1,16 @@
-# $NetBSD: forsubst.mk,v 1.2 2020/10/24 08:34:59 rillig Exp $
+# $NetBSD: forsubst.mk,v 1.3 2020/11/03 17:59:27 rillig Exp $
+#
+# The parser used to break dependency lines at ';' without regard for
+# substitution patterns. Back then, the first ';' was interpreted as the
+# separator between the dependency and its commands. This (perhaps coupled
+# with the new handling of .for variables in ${:U<value>...) caused
+# interesting results for lines like:
+#
+# .for file in ${LIST}
+# for-subst: ${file:S;^;${here}/;g}
+# .endfor
+#
+# See the commit to unit-tests/forsubst (without the .mk) from 2009-10-07.
all: for-subst
diff --git a/unit-tests/gnode-submake.exp b/unit-tests/gnode-submake.exp
new file mode 100644
index 000000000000..dbce13efaefa
--- /dev/null
+++ b/unit-tests/gnode-submake.exp
@@ -0,0 +1,11 @@
+#*** Input graph:
+# all, made UNMADE, type OP_DEPENDS, flags none
+# makeinfo, made UNMADE, type OP_DEPENDS|OP_HAS_COMMANDS, flags none
+# make-index, made UNMADE, type OP_DEPENDS|OP_SUBMAKE|OP_HAS_COMMANDS, flags none
+# braces-dot, made UNMADE, type OP_DEPENDS|OP_SUBMAKE|OP_HAS_COMMANDS, flags none
+# braces-no-dot, made UNMADE, type OP_DEPENDS|OP_SUBMAKE|OP_HAS_COMMANDS, flags none
+# braces-no-dot-modifier, made UNMADE, type OP_DEPENDS|OP_HAS_COMMANDS, flags none
+# parentheses-dot, made UNMADE, type OP_DEPENDS|OP_SUBMAKE|OP_HAS_COMMANDS, flags none
+# parentheses-no-dot, made UNMADE, type OP_DEPENDS|OP_SUBMAKE|OP_HAS_COMMANDS, flags none
+
+exit status 0
diff --git a/unit-tests/gnode-submake.mk b/unit-tests/gnode-submake.mk
new file mode 100644
index 000000000000..40ff20276df9
--- /dev/null
+++ b/unit-tests/gnode-submake.mk
@@ -0,0 +1,42 @@
+# $NetBSD: gnode-submake.mk,v 1.1 2020/11/07 23:25:06 rillig Exp $
+#
+# Test whether OP_SUBMAKE is determined correctly. If it is, this node's
+# shell commands are connected to the make process via pipes, to coordinate
+# the number of running jobs.
+#
+# Determining whether a node is a sub-make node happens when the node is
+# parsed. This information is only used in parallel mode, but the result
+# from parsing is available in compat mode as well.
+
+.MAKEFLAGS: -n -dg1
+
+all: makeinfo make-index
+all: braces-dot braces-no-dot
+all: braces-no-dot-modifier
+all: parentheses-dot parentheses-no-dot
+
+makeinfo:
+ # The command contains the substring "make", but not as a whole word.
+ : makeinfo submake
+
+make-index:
+ # The command contains the word "make", therefore it is considered a
+ # possible sub-make. It isn't really, but that doesn't hurt.
+ : make-index
+
+braces-dot:
+ : ${.MAKE}
+
+braces-no-dot:
+ : ${MAKE}
+
+braces-no-dot-modifier:
+ # The command refers to MAKE, but not in its pure form. Therefore it
+ # is not considered a sub-make.
+ : ${MAKE:T}
+
+parentheses-dot:
+ : $(.MAKE)
+
+parentheses-no-dot:
+ : $(MAKE)
diff --git a/unit-tests/include-sub.mk b/unit-tests/include-sub.mk
index aeb7c3a69082..0b8dc77398ab 100644
--- a/unit-tests/include-sub.mk
+++ b/unit-tests/include-sub.mk
@@ -1,4 +1,4 @@
-# $NetBSD: include-sub.mk,v 1.6 2020/10/25 12:08:53 rillig Exp $
+# $NetBSD: include-sub.mk,v 1.7 2020/11/02 19:07:09 rillig Exp $
.if ${.INCLUDEDFROMFILE} == "include-main.mk"
. info sub-before-ok
@@ -20,7 +20,7 @@
# To see the variable 'includes' in action:
#
# Breakpoints:
-# Parse_File at "PtrVector_Push(&includes, curFile)"
+# Parse_File at "Vector_Push(&includes)"
# ParseMessage at entry
# Watches:
# ((const IFile *[10])(*includes.items))
diff --git a/unit-tests/job-flags.exp b/unit-tests/job-flags.exp
new file mode 100644
index 000000000000..b0c81d8f7094
--- /dev/null
+++ b/unit-tests/job-flags.exp
@@ -0,0 +1,12 @@
+.BEGIN
+silent
+ignore
+true in ignore
+false in ignore
+*** [ignore] Error code 1 (ignored)
+false without indentation
+false space
+false tab
+*** [ignore-cmds] Error code 1 (ignored)
+.END
+exit status 0
diff --git a/unit-tests/job-flags.mk b/unit-tests/job-flags.mk
new file mode 100644
index 000000000000..d4c3b5d43643
--- /dev/null
+++ b/unit-tests/job-flags.mk
@@ -0,0 +1,32 @@
+# $NetBSD: job-flags.mk,v 1.2 2020/11/14 13:17:47 rillig Exp $
+#
+# Tests for Job.flags, which are controlled by special source dependencies
+# like .SILENT or .IGNORE, as well as the command line options -s or -i.
+
+.MAKEFLAGS: -j1
+
+all: silent .WAIT ignore .WAIT ignore-cmds
+
+.BEGIN:
+ @echo $@
+
+silent: .SILENT .PHONY
+ echo $@
+
+ignore: .IGNORE .PHONY
+ @echo $@
+ true in $@
+ false in $@
+ @echo 'Still there in $@'
+
+ignore-cmds: .PHONY
+ # This node is not marked .IGNORE; individual commands can be switched
+ # to ignore mode by prefixing them with a '-'.
+ -false without indentation
+ # This also works if the '-' is indented by a space or a tab.
+ # Leading whitespace is stripped off by ParseLine_ShellCommand.
+ -false space
+ -false tab
+
+.END:
+ @echo $@
diff --git a/unit-tests/moderrs.mk b/unit-tests/moderrs.mk
index 77ba39a3d57d..8fdcb496ee29 100644
--- a/unit-tests/moderrs.mk
+++ b/unit-tests/moderrs.mk
@@ -1,4 +1,4 @@
-# $NetBSD: moderrs.mk,v 1.24 2020/11/01 14:36:25 rillig Exp $
+# $NetBSD: moderrs.mk,v 1.25 2020/11/15 20:20:58 rillig Exp $
#
# various modifier error tests
@@ -123,7 +123,7 @@ mod-regex-delimiter: print-header print-footer
# 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 not widely used.
+# 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
diff --git a/unit-tests/modmisc.mk b/unit-tests/modmisc.mk
index f57e679cd4da..64a84ce0dadd 100644
--- a/unit-tests/modmisc.mk
+++ b/unit-tests/modmisc.mk
@@ -1,4 +1,4 @@
-# $NetBSD: modmisc.mk,v 1.49 2020/10/24 08:50:17 rillig Exp $
+# $NetBSD: modmisc.mk,v 1.51 2020/11/15 20:20:58 rillig Exp $
#
# miscellaneous modifier tests
@@ -48,9 +48,9 @@ emptyvar:
@echo @:${:@var@${var}@}
# The :U modifier turns even the "" variable into something that has a value.
-# The resulting variable is empty, but is still considered to contain a
-# single empty word. This word can be accessed by the :S and :C modifiers,
-# but not by the :@ modifier since it explicitly skips empty words.
+# The value of the resulting expression is empty, but is still considered to
+# contain a single empty word. This word can be accessed by the :S and :C
+# modifiers, but not by the :@ modifier since it explicitly skips empty words.
undefvar:
@echo S:${:U:S,^$,empty,}
@echo C:${:U:C,^$,empty,}
@@ -60,12 +60,12 @@ undefvar:
mod-quote:
@echo $@: new${.newline:Q}${.newline:Q}line
-# Cover the bmake_realloc in brk_string.
+# Cover the bmake_realloc in Str_Words.
mod-break-many-words:
@echo $@: ${UNDEF:U:range=500:[#]}
# To apply a modifier indirectly via another variable, the whole
-# modifier must be put into a single variable.
+# modifier must be put into a single variable expression.
.if ${value:L:${:US}${:U,value,replacement,}} != "S,value,replacement,}"
. warning unexpected
.endif
diff --git a/unit-tests/modts.mk b/unit-tests/modts.mk
index 67ba7eb97078..4776c5818ea5 100644
--- a/unit-tests/modts.mk
+++ b/unit-tests/modts.mk
@@ -1,7 +1,6 @@
-# $NetBSD: modts.mk,v 1.7 2020/10/24 08:50:17 rillig Exp $
+# $NetBSD: modts.mk,v 1.8 2020/11/03 18:42:33 rillig Exp $
-LIST= one two three
-LIST+= four five six
+LIST= one two three four five six
FU_mod-ts= a / b / cool
diff --git a/unit-tests/modword.mk b/unit-tests/modword.mk
index 43edf6a32b77..383c9dca975b 100644
--- a/unit-tests/modword.mk
+++ b/unit-tests/modword.mk
@@ -1,6 +1,7 @@
-# $NetBSD: modword.mk,v 1.4 2020/11/01 13:55:31 rillig Exp $
+# $NetBSD: modword.mk,v 1.5 2020/11/15 20:20:58 rillig Exp $
#
# Test behaviour of new :[] modifier
+# TODO: When was this modifier new?
all: mod-squarebrackets mod-S-W mod-C-W mod-tW-tw
diff --git a/unit-tests/objdir-writable.exp b/unit-tests/objdir-writable.exp
new file mode 100644
index 000000000000..f15cf914a7f5
--- /dev/null
+++ b/unit-tests/objdir-writable.exp
@@ -0,0 +1,5 @@
+make warning: OBJDIR/roobj: Permission denied.
+/tmp
+OBJDIR/roobj
+OBJDIR/roobj
+exit status 0
diff --git a/unit-tests/objdir-writable.mk b/unit-tests/objdir-writable.mk
new file mode 100644
index 000000000000..9fc1c69afb56
--- /dev/null
+++ b/unit-tests/objdir-writable.mk
@@ -0,0 +1,31 @@
+# $NetBSD: objdir-writable.mk,v 1.4 2020/11/14 07:36:00 sjg Exp $
+
+# test checking for writable objdir
+
+RO_OBJDIR?= ${TMPDIR:U/tmp}/roobj
+
+.if make(do-objdir)
+# this should succeed
+.OBJDIR: ${RO_OBJDIR}
+
+do-objdir:
+.else
+all: no-objdir ro-objdir explicit-objdir
+
+# make it now
+x!= echo; mkdir -p ${RO_OBJDIR}; chmod 555 ${RO_OBJDIR}
+
+.END: rm-objdir
+rm-objdir:
+ @rmdir ${RO_OBJDIR}
+
+no-objdir:
+ @MAKEOBJDIR=${RO_OBJDIR} ${.MAKE} -r -f /dev/null -C /tmp -V .OBJDIR
+
+ro-objdir:
+ @MAKEOBJDIR=${RO_OBJDIR} ${.MAKE} -r -f /dev/null -C /tmp -V .OBJDIR MAKE_OBJDIR_CHECK_WRITABLE=no
+
+explicit-objdir:
+ @MAKEOBJDIR=/tmp ${.MAKE} -r -f ${MAKEFILE:tA} -C /tmp do-objdir -V .OBJDIR
+.endif
+
diff --git a/unit-tests/opt-chdir.exp b/unit-tests/opt-chdir.exp
index 39a9383953dd..d20f9eb2f07b 100644
--- a/unit-tests/opt-chdir.exp
+++ b/unit-tests/opt-chdir.exp
@@ -1 +1,6 @@
+make: chdir /./././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././: File name too long
+*** Error code 1 (ignored)
+cwd: /
+make: chdir /nonexistent: No such file or directory
+*** Error code 1 (ignored)
exit status 0
diff --git a/unit-tests/opt-chdir.mk b/unit-tests/opt-chdir.mk
index 8735fddbef9e..20241f02740e 100644
--- a/unit-tests/opt-chdir.mk
+++ b/unit-tests/opt-chdir.mk
@@ -1,8 +1,27 @@
-# $NetBSD: opt-chdir.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+# $NetBSD: opt-chdir.mk,v 1.5 2020/11/15 05:43:56 sjg Exp $
#
-# Tests for the -C command line option.
+# Tests for the -C command line option, which changes the directory at the
+# beginning.
+#
+# This option has been available since 2009-08-27.
+
+.MAKEFLAGS: -d0 # switch stdout to line-buffered
+
+all: chdir-filename-too-long
+all: chdir-root
+all: chdir-nonexistent
+
+# Try to overflow the internal buffer for .CURDIR, which is curdir.
+chdir-filename-too-long: .PHONY .IGNORE
+ # 5000 slashes, separated by dots: /./././.../././
+ @${MAKE} -C ${:U:range=5000:@@/@:ts.}
-# TODO: Implementation
+# Changing to another directory is possible via the command line.
+# In this test, it is the root directory since almost any other directory
+# is not guaranteed to exist on every platform.
+chdir-root: .PHONY .IGNORE
+ @MAKE_OBJDIR_CHECK_WRITABLE=no ${MAKE} -C / -V 'cwd: $${.CURDIR}'
-all:
- @:;
+# Trying to change to a nonexistent directory exits immediately.
+chdir-nonexistent: .PHONY .IGNORE
+ @${MAKE} -C /nonexistent
diff --git a/unit-tests/opt-debug-jobs.exp b/unit-tests/opt-debug-jobs.exp
index 1214dce781b5..0431867756a1 100644
--- a/unit-tests/opt-debug-jobs.exp
+++ b/unit-tests/opt-debug-jobs.exp
@@ -10,7 +10,9 @@ echo ": variable"
echo ": 'single' and \"double\" quotes"
{ : 'single' and "double" quotes
} || exit $?
-Running all locally
+{ sleep 1
+} || exit $?
+Running all
Command: sh
JobExec(all): pid <pid> added to jobs table
job table @ job started
diff --git a/unit-tests/opt-debug-jobs.mk b/unit-tests/opt-debug-jobs.mk
index fb65d4f3356c..f3732df7e25d 100644
--- a/unit-tests/opt-debug-jobs.mk
+++ b/unit-tests/opt-debug-jobs.mk
@@ -1,4 +1,4 @@
-# $NetBSD: opt-debug-jobs.mk,v 1.4 2020/10/05 19:27:48 rillig Exp $
+# $NetBSD: opt-debug-jobs.mk,v 1.5 2020/11/12 21:54:52 rillig Exp $
#
# Tests for the -dj command line option, which adds debug logging about
# running jobs in multiple shells.
@@ -24,3 +24,10 @@ all:
# This allows to copy and paste the whole command, without having
# to unescape anything.
: 'single' and "double" quotes
+
+ # Avoid a race condition in the debug output. Without sleeping,
+ # it is not guaranteed that the two lines "exited/stopped" and
+ # "JobFinish" are output earlier than the stdout of the actual shell
+ # commands. The '@' prefix avoids that this final command gets into
+ # another race condition with the "exited/stopped" line.
+ @sleep 1
diff --git a/unit-tests/opt-ignore.mk b/unit-tests/opt-ignore.mk
index ff11c1e15c31..f776e8a27229 100644
--- a/unit-tests/opt-ignore.mk
+++ b/unit-tests/opt-ignore.mk
@@ -1,4 +1,4 @@
-# $NetBSD: opt-ignore.mk,v 1.4 2020/10/18 18:12:42 rillig Exp $
+# $NetBSD: opt-ignore.mk,v 1.5 2020/11/09 20:50:56 rillig Exp $
#
# Tests for the -i command line option, which ignores the exit status of the
# shell commands, and just continues with the next command, even from the same
@@ -11,6 +11,7 @@
# failed?
.MAKEFLAGS: -d0 # switch stdout to being line-buffered
+.MAKEFLAGS: -i
all: dependency other
diff --git a/unit-tests/opt-keep-going.mk b/unit-tests/opt-keep-going.mk
index ec4adfa00e62..72f605246712 100644
--- a/unit-tests/opt-keep-going.mk
+++ b/unit-tests/opt-keep-going.mk
@@ -1,10 +1,11 @@
-# $NetBSD: opt-keep-going.mk,v 1.4 2020/10/18 18:12:42 rillig Exp $
+# $NetBSD: opt-keep-going.mk,v 1.5 2020/11/09 20:50:56 rillig Exp $
#
# Tests for the -k command line option, which stops building a target as soon
# as an error is detected, but continues building the other, independent
# targets, as far as possible.
.MAKEFLAGS: -d0 # switch stdout to being line-buffered
+.MAKEFLAGS: -k
all: dependency other
diff --git a/unit-tests/opt-no-action.mk b/unit-tests/opt-no-action.mk
index 32b3b1564acb..91c05b2dc00f 100644
--- a/unit-tests/opt-no-action.mk
+++ b/unit-tests/opt-no-action.mk
@@ -1,9 +1,11 @@
-# $NetBSD: opt-no-action.mk,v 1.3 2020/08/19 05:25:26 rillig Exp $
+# $NetBSD: opt-no-action.mk,v 1.4 2020/11/09 20:50:56 rillig Exp $
#
# Tests for the -n command line option, which runs almost no commands.
# It just outputs them, to be inspected by human readers.
# Only commands that are in a .MAKE target or prefixed by '+' are run.
+.MAKEFLAGS: -n
+
# 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.
!= echo 'command during parsing' 1>&2; echo
diff --git a/unit-tests/opt-query.mk b/unit-tests/opt-query.mk
index 04e605991140..0a7d5219a8fe 100644
--- a/unit-tests/opt-query.mk
+++ b/unit-tests/opt-query.mk
@@ -1,4 +1,4 @@
-# $NetBSD: opt-query.mk,v 1.3 2020/08/19 05:13:18 rillig Exp $
+# $NetBSD: opt-query.mk,v 1.4 2020/11/09 20:50:56 rillig Exp $
#
# Tests for the -q command line option.
#
@@ -6,6 +6,8 @@
# None of the commands in the targets are run, not even those that are
# prefixed with '+'.
+.MAKEFLAGS: -q
+
# 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.
!= echo 'command during parsing' 1>&2; echo
diff --git a/unit-tests/opt-touch-jobs.exp b/unit-tests/opt-touch-jobs.exp
new file mode 100644
index 000000000000..0cfde5135198
--- /dev/null
+++ b/unit-tests/opt-touch-jobs.exp
@@ -0,0 +1,4 @@
+: Making opt-touch-make.
+`opt-touch-join' is up to date.
+`opt-touch-use' is up to date.
+exit status 0
diff --git a/unit-tests/opt-touch-jobs.mk b/unit-tests/opt-touch-jobs.mk
new file mode 100644
index 000000000000..4402d76e9578
--- /dev/null
+++ b/unit-tests/opt-touch-jobs.mk
@@ -0,0 +1,30 @@
+# $NetBSD: opt-touch-jobs.mk,v 1.1 2020/11/14 15:35:20 rillig Exp $
+#
+# Tests for the -t command line option in jobs mode.
+
+.MAKEFLAGS: -j1
+.MAKEFLAGS: -t
+.MAKEFLAGS: opt-touch-phony
+.MAKEFLAGS: opt-touch-join
+.MAKEFLAGS: opt-touch-use
+.MAKEFLAGS: opt-touch-make
+
+opt-touch-phony: .PHONY
+ : Making $@.
+
+opt-touch-join: .JOIN
+ : Making $@.
+
+opt-touch-use: .USE
+ : Making use of $@.
+
+# Even though it is listed last, in the output it appears first.
+# This is because it is the only node that actually needs to be run.
+# The "is up to date" of the other nodes happens after all jobs have
+# finished, by Make_Run > MakePrintStatusList > MakePrintStatus.
+opt-touch-make: .MAKE
+ : Making $@.
+
+.END:
+ @files=$$(ls opt-touch-* 2>/dev/null | grep -v -e '\.' -e '\*'); \
+ [ -z "$$files" ] || { echo "created files: $$files" 1>&2; exit 1; }
diff --git a/unit-tests/opt-touch.exp b/unit-tests/opt-touch.exp
index 39a9383953dd..c9e6c7890f42 100644
--- a/unit-tests/opt-touch.exp
+++ b/unit-tests/opt-touch.exp
@@ -1 +1,4 @@
+`opt-touch-join' is up to date.
+`opt-touch-use' is up to date.
+: Making opt-touch-make.
exit status 0
diff --git a/unit-tests/opt-touch.mk b/unit-tests/opt-touch.mk
index 5093c5cad6ac..defb7c59e0ad 100644
--- a/unit-tests/opt-touch.mk
+++ b/unit-tests/opt-touch.mk
@@ -1,8 +1,21 @@
-# $NetBSD: opt-touch.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+# $NetBSD: opt-touch.mk,v 1.4 2020/11/14 14:13:09 rillig Exp $
#
# Tests for the -t command line option.
-# TODO: Implementation
+.MAKEFLAGS: -t opt-touch-phony opt-touch-join opt-touch-use opt-touch-make
-all:
- @:;
+opt-touch-phony: .PHONY
+ : Making $@.
+
+opt-touch-join: .JOIN
+ : Making $@.
+
+opt-touch-use: .USE
+ : Making use of $@.
+
+opt-touch-make: .MAKE
+ : Making $@.
+
+.END:
+ @files=$$(ls opt-touch-* 2>/dev/null | grep -v -e '\.' -e '\*'); \
+ [ -z "$$files" ] || { echo "created files: $$files" 1>&2; exit 1; }
diff --git a/unit-tests/opt-var-expanded.mk b/unit-tests/opt-var-expanded.mk
index 0b4088a82082..f7203374b8cc 100644
--- a/unit-tests/opt-var-expanded.mk
+++ b/unit-tests/opt-var-expanded.mk
@@ -1,6 +1,8 @@
-# $NetBSD: opt-var-expanded.mk,v 1.3 2020/08/23 14:28:04 rillig Exp $
+# $NetBSD: opt-var-expanded.mk,v 1.4 2020/11/09 20:50:56 rillig Exp $
#
# Tests for the -v command line option.
+.MAKEFLAGS: -v VAR -v VALUE
+
VAR= other ${VALUE} $$$$
VALUE= value
diff --git a/unit-tests/opt-var-literal.mk b/unit-tests/opt-var-literal.mk
index a819e7537105..d236e389d259 100644
--- a/unit-tests/opt-var-literal.mk
+++ b/unit-tests/opt-var-literal.mk
@@ -1,6 +1,8 @@
-# $NetBSD: opt-var-literal.mk,v 1.3 2020/08/23 14:28:04 rillig Exp $
+# $NetBSD: opt-var-literal.mk,v 1.4 2020/11/09 20:50:56 rillig Exp $
#
# Tests for the -V command line option.
+.MAKEFLAGS: -V VAR -V VALUE
+
VAR= other ${VALUE} $$$$
VALUE= value
diff --git a/unit-tests/opt-warnings-as-errors.exp b/unit-tests/opt-warnings-as-errors.exp
index bd54bb673f08..278c9469607f 100644
--- a/unit-tests/opt-warnings-as-errors.exp
+++ b/unit-tests/opt-warnings-as-errors.exp
@@ -1,6 +1,6 @@
-make: "opt-warnings-as-errors.mk" line 5: warning: message 1
+make: "opt-warnings-as-errors.mk" line 7: warning: message 1
make: parsing warnings being treated as errors
-make: "opt-warnings-as-errors.mk" line 6: warning: message 2
+make: "opt-warnings-as-errors.mk" line 8: warning: message 2
parsing continues
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
diff --git a/unit-tests/opt-warnings-as-errors.mk b/unit-tests/opt-warnings-as-errors.mk
index 905753410db0..2302dd4794b5 100644
--- a/unit-tests/opt-warnings-as-errors.mk
+++ b/unit-tests/opt-warnings-as-errors.mk
@@ -1,7 +1,9 @@
-# $NetBSD: opt-warnings-as-errors.mk,v 1.3 2020/08/23 14:28:04 rillig Exp $
+# $NetBSD: opt-warnings-as-errors.mk,v 1.4 2020/11/09 20:50:56 rillig Exp $
#
# Tests for the -W command line option, which turns warnings into errors.
+.MAKEFLAGS: -W
+
.warning message 1
.warning message 2
diff --git a/unit-tests/opt.exp b/unit-tests/opt.exp
index 39a9383953dd..11344ae0c359 100644
--- a/unit-tests/opt.exp
+++ b/unit-tests/opt.exp
@@ -1 +1,22 @@
+make -r -f /dev/null -V MAKEFLAGS
+ -r -k -d 0
+
+make -:
+usage: make [-BeikNnqrSstWwX]
+ [-C directory] [-D variable] [-d flags] [-f makefile]
+ [-I directory] [-J private] [-j max_jobs] [-m directory] [-T file]
+ [-V variable] [-v variable] [variable=value] [target ...]
+*** Error code 2 (ignored)
+
+make -r -f /dev/null -- -VAR=value -f /dev/null
+make: don't know how to make -f (continuing)
+`/dev/null' is up to date.
+
+make -?
+usage: make [-BeikNnqrSstWwX]
+ [-C directory] [-D variable] [-d flags] [-f makefile]
+ [-I directory] [-J private] [-j max_jobs] [-m directory] [-T file]
+ [-V variable] [-v variable] [variable=value] [target ...]
+*** Error code 2 (ignored)
+
exit status 0
diff --git a/unit-tests/opt.mk b/unit-tests/opt.mk
index eae430965df7..0931a66d3d15 100644
--- a/unit-tests/opt.mk
+++ b/unit-tests/opt.mk
@@ -1,8 +1,28 @@
-# $NetBSD: opt.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+# $NetBSD: opt.mk,v 1.6 2020/11/18 01:06:59 sjg Exp $
#
# Tests for the command line options.
-# TODO: Implementation
+.MAKEFLAGS: -d0 # make stdout line-buffered
-all:
- @:;
+all: .IGNORE
+ # The options from the top-level make are passed to the sub-makes via
+ # the environment variable MAKEFLAGS. This is where the " -r -k -d 0"
+ # comes from. See MainParseArg.
+ ${MAKE} -r -f /dev/null -V MAKEFLAGS
+ @echo
+
+ # Just to see how the custom argument parsing code reacts to a syntax
+ # error. The colon is used in the options string, marking an option
+ # that takes arguments. It is not an option by itself, though.
+ ${MAKE} -:
+ @echo
+
+ # See whether a '--' stops handling of command line options, like in
+ # standard getopt programs. Yes, it does, and it treats the
+ # second '-f' as a target to be created.
+ ${MAKE} -r -f /dev/null -- -VAR=value -f /dev/null
+ @echo
+
+ # This is the normal way to print the usage of a command.
+ ${MAKE} -?
+ @echo
diff --git a/unit-tests/order.mk b/unit-tests/order.mk
index f90b627d9e5f..9b94016c1841 100644
--- a/unit-tests/order.mk
+++ b/unit-tests/order.mk
@@ -1,10 +1,12 @@
-# $NetBSD: order.mk,v 1.1 2014/08/21 13:44:51 apb Exp $
+# $NetBSD: order.mk,v 1.2 2020/11/09 20:50:56 rillig Exp $
# Test that .ORDER is handled correctly.
# The explicit dependency the.o: the.h will make us examine the.h
# the .ORDER will prevent us building it immediately,
# we should then examine the.c rather than stop.
+.MAKEFLAGS: -j1
+
all: the.o
.ORDER: the.c the.h
diff --git a/unit-tests/recursive.exp b/unit-tests/recursive.exp
index bb5db75a474c..36cd1c989532 100644
--- a/unit-tests/recursive.exp
+++ b/unit-tests/recursive.exp
@@ -1,5 +1,5 @@
-make: "recursive.mk" line 34: Unclosed variable "MISSING_PAREN"
-make: "recursive.mk" line 35: Unclosed variable "MISSING_BRACE"
+make: "recursive.mk" line 36: Unclosed variable "MISSING_PAREN"
+make: "recursive.mk" line 37: Unclosed variable "MISSING_BRACE"
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/unit-tests/recursive.mk b/unit-tests/recursive.mk
index 6e5d8a2ca8b5..73a8409fe030 100644
--- a/unit-tests/recursive.mk
+++ b/unit-tests/recursive.mk
@@ -1,4 +1,4 @@
-# $NetBSD: recursive.mk,v 1.3 2020/10/24 08:50:17 rillig Exp $
+# $NetBSD: recursive.mk,v 1.4 2020/11/09 20:50:56 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
@@ -11,6 +11,8 @@
# Seen in pkgsrc/x11/libXfixes, and probably many more package that use
# GNU Automake.
+.MAKEFLAGS: -dL
+
AM_V_lt= ${am__v_lt_${V}}
am__v_lt_= ${am__v_lt_${AM_DEFAULT_VERBOSITY}}
am__v_lt_0= --silent
diff --git a/unit-tests/sh-leading-at.exp b/unit-tests/sh-leading-at.exp
index 5ffa84690a40..8347fda085f7 100644
--- a/unit-tests/sh-leading-at.exp
+++ b/unit-tests/sh-leading-at.exp
@@ -2,4 +2,5 @@ ok
space after @
echo 'echoed'
echoed
+3
exit status 0
diff --git a/unit-tests/sh-leading-at.mk b/unit-tests/sh-leading-at.mk
index 19a6e59e4e6a..9f98005ec088 100644
--- a/unit-tests/sh-leading-at.mk
+++ b/unit-tests/sh-leading-at.mk
@@ -1,10 +1,18 @@
-# $NetBSD: sh-leading-at.mk,v 1.3 2020/08/22 09:16:08 rillig Exp $
+# $NetBSD: sh-leading-at.mk,v 1.5 2020/11/15 20:20:58 rillig Exp $
#
# Tests for shell commands preceded by an '@', to suppress printing
# the command to stdout.
+#
+# See also:
+# .SILENT
+# depsrc-silent.mk
+# opt-silent.mk
all:
@
@echo 'ok'
@ echo 'space after @'
echo 'echoed'
+ # The leading '@' can be repeated.
+ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+ @@@echo '3'
diff --git a/unit-tests/sh-leading-hyphen.mk b/unit-tests/sh-leading-hyphen.mk
index 94be43495afb..d760abb9afdd 100644
--- a/unit-tests/sh-leading-hyphen.mk
+++ b/unit-tests/sh-leading-hyphen.mk
@@ -1,7 +1,12 @@
-# $NetBSD: sh-leading-hyphen.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+# $NetBSD: sh-leading-hyphen.mk,v 1.3 2020/11/15 20:20:58 rillig Exp $
#
# Tests for shell commands preceded by a '-', to ignore the exit status of
# the command line.
+#
+# See also:
+# .IGNORE
+# depsrc-ignore.mk
+# opt-ignore.mk
# TODO: Implementation
diff --git a/unit-tests/sh-leading-plus.mk b/unit-tests/sh-leading-plus.mk
index 75279d7d57fd..ff57b4a38a7d 100644
--- a/unit-tests/sh-leading-plus.mk
+++ b/unit-tests/sh-leading-plus.mk
@@ -1,8 +1,10 @@
-# $NetBSD: sh-leading-plus.mk,v 1.3 2020/08/23 14:46:33 rillig Exp $
+# $NetBSD: sh-leading-plus.mk,v 1.4 2020/11/09 20:50:56 rillig Exp $
#
# Tests for shell commands preceded by a '+', to run them even if
# the command line option -n is given.
+.MAKEFLAGS: -n
+
all:
@echo 'this command is not run'
@+echo 'this command is run'
diff --git a/unit-tests/sh-meta-chars.mk b/unit-tests/sh-meta-chars.mk
index 126ca2ceb118..a029c73a855c 100644
--- a/unit-tests/sh-meta-chars.mk
+++ b/unit-tests/sh-meta-chars.mk
@@ -1,9 +1,13 @@
-# $NetBSD: sh-meta-chars.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+# $NetBSD: sh-meta-chars.mk,v 1.3 2020/11/15 20:20:58 rillig Exp $
#
# Tests for running shell commands that contain meta-characters.
#
# These meta-characters decide whether the command is run by the shell
-# or executed directly via execv. See Cmd_Exec for details.
+# or executed directly via execv, but only in compatibility mode, not
+# in jobs mode, and only if MAKE_NATIVE is defined during compilation.
+#
+# See also:
+# Compat_RunCommand, useShell
# TODO: Implementation
diff --git a/unit-tests/suff-self.exp b/unit-tests/suff-self.exp
new file mode 100644
index 000000000000..4e70762209a2
--- /dev/null
+++ b/unit-tests/suff-self.exp
@@ -0,0 +1,3 @@
+make: Graph cycles through suff-self.suff
+`all' not remade because of errors.
+exit status 0
diff --git a/unit-tests/suff-self.mk b/unit-tests/suff-self.mk
new file mode 100644
index 000000000000..8874cc5157e8
--- /dev/null
+++ b/unit-tests/suff-self.mk
@@ -0,0 +1,11 @@
+# $NetBSD: suff-self.mk,v 1.1 2020/11/16 15:12:16 rillig Exp $
+#
+# See what happens if someone defines a self-referencing suffix
+# transformation rule.
+
+.SUFFIXES: .suff
+
+.suff.suff:
+ : Making ${.TARGET} out of ${.IMPSRC}.
+
+all: suff-self.suff
diff --git a/unit-tests/use-inference.mk b/unit-tests/use-inference.mk
index b0e5017bc6fb..cde3c772edaa 100644
--- a/unit-tests/use-inference.mk
+++ b/unit-tests/use-inference.mk
@@ -1,4 +1,4 @@
-# $NetBSD: use-inference.mk,v 1.1 2020/08/09 16:32:28 rillig Exp $
+# $NetBSD: use-inference.mk,v 1.2 2020/11/05 00:41:04 rillig Exp $
#
# Demonstrate that .USE rules do not have an effect on inference rules.
# At least not in the special case where the inference rule does not
@@ -33,3 +33,6 @@ use-inference.from: # assume it exists
# This is strange since make definitely knows about the .from.to suffix
# inference rule. But it seems to ignore it, maybe because it doesn't
# have any associated commands.
+
+# XXX: Despite the error message "don't know how to make", the exit status
+# is 0. This is inconsistent.
diff --git a/unit-tests/var-class-local.exp b/unit-tests/var-class-local.exp
index f1595c6810fb..db85b47cae06 100644
--- a/unit-tests/var-class-local.exp
+++ b/unit-tests/var-class-local.exp
@@ -1,2 +1,5 @@
+: Making var-class-local.c out of nothing.
+: Making var-class-local.o from var-class-local.c.
+: Making basename "var-class-local.o" in "." from "var-class-local.c" in ".".
: all overwritten
exit status 0
diff --git a/unit-tests/var-class-local.mk b/unit-tests/var-class-local.mk
index 68696ea854af..f9d56e539ff0 100644
--- a/unit-tests/var-class-local.mk
+++ b/unit-tests/var-class-local.mk
@@ -1,4 +1,4 @@
-# $NetBSD: var-class-local.mk,v 1.4 2020/10/25 09:46:25 rillig Exp $
+# $NetBSD: var-class-local.mk,v 1.5 2020/11/05 18:08:39 rillig Exp $
#
# Tests for target-local variables, such as ${.TARGET} or $@.
@@ -25,6 +25,20 @@
.endif
all:
+
+.SUFFIXES: .c .o
+
+var-class-local.c:
+ : Making ${.TARGET} out of nothing.
+
+.c.o:
+ : Making ${.TARGET} from ${.IMPSRC}.
+
+ # The local variables @F, @D, <F, <D are legacy forms.
+ # See the manual page for details.
+ : Making basename "${@F}" in "${@D}" from "${<F}" in "${<D}".
+
+all: var-class-local.o
# The ::= modifier overwrites the .TARGET variable in the node
# 'all', not in the global scope. This can be seen with the -dv
# option, looking for "all:@ = overwritten".
diff --git a/unit-tests/var-op-assign.exp b/unit-tests/var-op-assign.exp
index 0e9e2d211a5f..0562ffcda733 100644
--- a/unit-tests/var-op-assign.exp
+++ b/unit-tests/var-op-assign.exp
@@ -1,6 +1,6 @@
this will be evaluated later
-make: "var-op-assign.mk" line 52: Need an operator
-make: "var-op-assign.mk" line 86: Parsing still continues until here.
+make: "var-op-assign.mk" line 59: Need an operator
+make: "var-op-assign.mk" line 93: Parsing still continues until here.
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/unit-tests/var-op-assign.mk b/unit-tests/var-op-assign.mk
index c988d4141b83..3bcc3de0ba0e 100644
--- a/unit-tests/var-op-assign.mk
+++ b/unit-tests/var-op-assign.mk
@@ -1,11 +1,12 @@
-# $NetBSD: var-op-assign.mk,v 1.6 2020/10/24 08:50:17 rillig Exp $
+# $NetBSD: var-op-assign.mk,v 1.7 2020/11/15 20:20:58 rillig Exp $
#
# Tests for the = variable assignment operator, which overwrites an existing
# variable or creates it.
# This is a simple variable assignment.
# To the left of the assignment operator '=' there is the variable name,
-# and to the right is the variable value.
+# and to the right is the variable value. The variable value is stored as-is,
+# it is not expanded in any way.
#
VAR= value
@@ -36,9 +37,15 @@ VAR= new value and \# some $$ special characters # comment
# The variable value may contain references to other variables.
# In this example, the reference is to the variable with the empty name,
-# which always expands to an empty string. This alone would not produce
-# any side-effects, therefore the variable has a :!...! modifier that
-# executes a shell command.
+# which is never defined.
+#
+# 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.
+#
+# 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.
VAR= ${:! echo 'not yet evaluated' 1>&2 !}
VAR= ${:! echo 'this will be evaluated later' 1>&2 !}
@@ -48,7 +55,7 @@ VAR= ${:! echo 'this will be evaluated later' 1>&2 !}
.endif
# In a variable assignment, the variable name must consist of a single word.
-#
+# The following line therefore generates a parse error.
VARIABLE NAME= variable value
# But if the whitespace appears inside parentheses or braces, everything is
diff --git a/unit-tests/var-op-expand.exp b/unit-tests/var-op-expand.exp
index 39a9383953dd..8ccbbd5ae92d 100644
--- a/unit-tests/var-op-expand.exp
+++ b/unit-tests/var-op-expand.exp
@@ -1 +1,10 @@
+Var_Parse: ${UNDEF} with VARE_WANTRES
+Global:VAR_ASSIGN_ = undef value
+Var_Parse: ${UNDEF} with VARE_WANTRES
+Var_Parse: ${UNDEF} with VARE_WANTRES
+Global:VAR_SUBST_${UNDEF} =
+Var_Parse: ${UNDEF} with VARE_WANTRES
+Global:VAR_SUBST_ = undef value
+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-expand.mk b/unit-tests/var-op-expand.mk
index 07c5fb647759..0b5ddbbc0386 100644
--- a/unit-tests/var-op-expand.mk
+++ b/unit-tests/var-op-expand.mk
@@ -1,9 +1,27 @@
-# $NetBSD: var-op-expand.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+# $NetBSD: var-op-expand.mk,v 1.4 2020/11/08 14:00:52 rillig Exp $
#
# Tests for the := variable assignment operator, which expands its
# right-hand side.
# TODO: Implementation
+# XXX: edge case: When a variable name refers to an undefined variable, the
+# behavior differs between the '=' and the ':=' assignment operators.
+# This bug exists since var.c 1.42 from 2000-05-11.
+#
+# The '=' operator expands the undefined variable to an empty string, thus
+# assigning to VAR_ASSIGN_. In the name of variables to be set, it should
+# really be forbidden to refer to undefined variables.
+#
+# The ':=' operator expands the variable name twice. In one of these
+# expansions, the undefined variable expression is preserved (controlled by
+# preserveUndefined in VarAssign_EvalSubst), in the other expansion it expands
+# to an empty string. This way, 2 variables are created using a single
+# variable assignment. It's magic. :-/
+.MAKEFLAGS: -dv
+VAR_ASSIGN_${UNDEF}= undef value
+VAR_SUBST_${UNDEF}:= undef value
+.MAKEFLAGS: -d0
+
all:
@:;
diff --git a/unit-tests/var-op-shell.exp b/unit-tests/var-op-shell.exp
index 39a9383953dd..caea85ab5daa 100644
--- a/unit-tests/var-op-shell.exp
+++ b/unit-tests/var-op-shell.exp
@@ -1 +1,7 @@
+make: "var-op-shell.mk" line 28: warning: "echo "failed"; false" returned non-zero status
+make: "var-op-shell.mk" line 34: warning: "false" returned non-zero status
+make: "var-op-shell.mk" line 59: warning: "kill -14 $$" exited on a signal
+/bin/no/such/command: not found
+make: "var-op-shell.mk" line 65: warning: "/bin/no/such/command" returned non-zero status
+stderr
exit status 0
diff --git a/unit-tests/var-op-shell.mk b/unit-tests/var-op-shell.mk
index 83580a89e6c2..7b52513e1131 100644
--- a/unit-tests/var-op-shell.mk
+++ b/unit-tests/var-op-shell.mk
@@ -1,9 +1,84 @@
-# $NetBSD: var-op-shell.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+# $NetBSD: var-op-shell.mk,v 1.3 2020/11/09 20:39:46 rillig Exp $
#
# Tests for the != variable assignment operator, which runs its right-hand
# side through the shell.
-# TODO: Implementation
+# The variable OUTPUT gets the output from running the shell command.
+OUTPUT!= echo "success"'ful'
+.if ${OUTPUT} != "successful"
+. error
+.endif
+
+# Since 2014-08-20, the output of the shell command may be empty.
+#
+# On 1996-05-29, when the '!=' assignment operator and Cmd_Exec were added,
+# an empty output produced the error message "Couldn't read shell's output
+# for \"%s\"".
+#
+# The error message is still there but reserved for technical errors.
+# It may be possible to trigger the error message by killing the shell after
+# reading part of its output.
+OUTPUT!= true
+.if ${OUTPUT} != ""
+. error
+.endif
+
+# The output of a shell command that failed is processed nevertheless.
+# TODO: Make this an error in lint mode.
+OUTPUT!= echo "failed"; false
+.if ${OUTPUT} != "failed"
+. error
+.endif
+
+# A command with empty output may fail as well.
+OUTPUT!= false
+.if ${OUTPUT} != ""
+. error
+.endif
+
+# In the output of the command, each newline is replaced with a space.
+# Except for the very last one, which is discarded.
+OUTPUT!= echo "line 1"; echo "line 2"
+.if ${OUTPUT} != "line 1 line 2"
+. error
+.endif
+
+# A failing command in the middle results in the exit status 0, which in the
+# end means that the whole sequence of commands succeeded.
+OUTPUT!= echo "before"; false; echo "after"
+.if ${OUTPUT} != "before after"
+. error
+.endif
+
+# NB: The signal number must be numeric since some shells (which ones?) don't
+# accept symbolic signal names. 14 is typically SIGALRM.
+#
+# XXX: The number of the signal is not mentioned in the warning since that
+# would have been difficult to implement; currently the errfmt is a format
+# string containing a single %s conversion.
+OUTPUT!= kill -14 $$$$
+.if ${OUTPUT} != ""
+. error
+.endif
+
+# A nonexistent command produces a non-zero exit status.
+OUTPUT!= /bin/no/such/command
+.if ${OUTPUT} != ""
+. error
+.endif
+
+# The output from the shell's stderr is not captured, it just passes through.
+OUTPUT!= echo "stdout"; echo "stderr" 1>&2
+.if ${OUTPUT} != "stdout"
+. error
+.endif
+
+# The 8 dollar signs end up as 4 dollar signs when expanded. The shell sees
+# the command "echo '$$$$'". The 4 dollar signs are stored in OUTPUT, and
+# when that variable is expanded, they expand to 2 dollar signs.
+OUTPUT!= echo '$$$$$$$$'
+.if ${OUTPUT} != "\$\$"
+. error
+.endif
all:
- @:;
diff --git a/unit-tests/var-op-sunsh.mk b/unit-tests/var-op-sunsh.mk
index efef19bf1567..0e16b2b42d34 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.5 2020/10/04 08:32:52 rillig Exp $
+# $NetBSD: var-op-sunsh.mk,v 1.6 2020/11/15 20:20:58 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 !=
@@ -118,5 +118,7 @@ VAR :sh += echo two
. error ${VAR}
.endif
+# TODO: test VAR:sh!=command
+
all:
@:;
diff --git a/unit-tests/vardebug.exp b/unit-tests/vardebug.exp
index 474a46ee1b5c..06c8b590e1b1 100644
--- a/unit-tests/vardebug.exp
+++ b/unit-tests/vardebug.exp
@@ -77,7 +77,7 @@ make: "vardebug.mk" line 44: Malformed conditional (${:Uvariable:unknown})
Var_Parse: ${UNDEFINED} with VARE_UNDEFERR|VARE_WANTRES
make: "vardebug.mk" line 53: Malformed conditional (${UNDEFINED})
Global:delete .SHELL (not found)
-Command:.SHELL = /bin/sh
+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
diff --git a/unit-tests/varmisc.mk b/unit-tests/varmisc.mk
index e2d53129a932..aced1a0554d1 100644
--- a/unit-tests/varmisc.mk
+++ b/unit-tests/varmisc.mk
@@ -1,5 +1,5 @@
-# $Id: varmisc.mk,v 1.20 2020/10/26 17:43:57 sjg Exp $
-# $NetBSD: varmisc.mk,v 1.26 2020/10/24 08:50:17 rillig Exp $
+# $Id: varmisc.mk,v 1.21 2020/11/11 23:08:50 sjg Exp $
+# $NetBSD: varmisc.mk,v 1.28 2020/11/07 00:07:02 rillig Exp $
#
# Miscellaneous variable tests.
@@ -88,7 +88,7 @@ VARNAME= ${VARNAME${:U1}}
.if defined(VARNAME${:U2}) && !empty(VARNAME${:U2})
.endif
-# begin .MAKE.SAVE_DOLLARS; see Var_Set_with_flags and s2Boolean.
+# begin .MAKE.SAVE_DOLLARS; see Var_SetWithFlags and ParseBoolean.
SD_VALUES= 0 1 2 False True false true Yes No yes no On Off ON OFF on off
SD_4_DOLLARS= $$$$
diff --git a/unit-tests/varmod-defined.exp b/unit-tests/varmod-defined.exp
index 39a9383953dd..7f61cc426305 100644
--- a/unit-tests/varmod-defined.exp
+++ b/unit-tests/varmod-defined.exp
@@ -1 +1,23 @@
+Global:8_DOLLARS = $$$$$$$$
+Global:VAR =
+Var_Parse: ${8_DOLLARS} with VARE_WANTRES|VARE_KEEP_DOLLAR
+Global:VAR = $$$$$$$$
+Var_Parse: ${VAR:D${8_DOLLARS}} with VARE_WANTRES|VARE_KEEP_DOLLAR
+Applying ${VAR:D...} to "$$$$$$$$" (VARE_WANTRES|VARE_KEEP_DOLLAR, none, none)
+Var_Parse: ${8_DOLLARS}} with VARE_WANTRES|VARE_KEEP_DOLLAR
+Result of ${VAR:D${8_DOLLARS}} is "$$$$$$$$" (VARE_WANTRES|VARE_KEEP_DOLLAR, none, none)
+Global:VAR = $$$$$$$$
+Var_Parse: ${VAR:@var@${8_DOLLARS}@} with VARE_WANTRES|VARE_KEEP_DOLLAR
+Applying ${VAR:@...} to "$$$$$$$$" (VARE_WANTRES|VARE_KEEP_DOLLAR, none, none)
+Modifier part: "var"
+Modifier part: "${8_DOLLARS}"
+ModifyWords: split "$$$$$$$$" into 1 words
+Global:var = $$$$$$$$
+Var_Parse: ${8_DOLLARS} with VARE_WANTRES
+ModifyWord_Loop: in "$$$$$$$$", replace "var" with "${8_DOLLARS}" to "$$$$"
+Global:delete var
+Result of ${VAR:@var@${8_DOLLARS}@} is "$$$$" (VARE_WANTRES|VARE_KEEP_DOLLAR, none, none)
+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 a722ebf666e6..59b9d79d754b 100644
--- a/unit-tests/varmod-defined.mk
+++ b/unit-tests/varmod-defined.mk
@@ -1,8 +1,10 @@
-# $NetBSD: varmod-defined.mk,v 1.7 2020/10/24 08:46:08 rillig Exp $
+# $NetBSD: varmod-defined.mk,v 1.9 2020/11/12 00:40:55 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.
+.MAKE.SAVE_DOLLARS= yes
+
DEF= defined
.undef UNDEF
@@ -85,5 +87,19 @@ DEF= defined
# TODO: Add more tests for parsing the plain text part, to cover each branch
# of ApplyModifier_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_Loop, which uses ParseModifierPart, which in turn removes
+# VARE_KEEP_DOLLAR from eflags.
+#
+# XXX: This inconsistency is documented nowhere.
+.MAKEFLAGS: -dv
+8_DOLLARS= $$$$$$$$
+VAR:= ${8_DOLLARS}
+VAR:= ${VAR:D${8_DOLLARS}}
+VAR:= ${VAR:@var@${8_DOLLARS}@}
+.MAKEFLAGS: -d0
+
all:
@:;
diff --git a/unit-tests/varmod-exclam-shell.mk b/unit-tests/varmod-exclam-shell.mk
index eaad8275805f..14b3d2510b72 100644
--- a/unit-tests/varmod-exclam-shell.mk
+++ b/unit-tests/varmod-exclam-shell.mk
@@ -1,27 +1,36 @@
-# $NetBSD: varmod-exclam-shell.mk,v 1.3 2020/10/24 08:46:08 rillig Exp $
+# $NetBSD: varmod-exclam-shell.mk,v 1.4 2020/11/03 18:42:33 rillig Exp $
#
-# Tests for the :!cmd! variable modifier.
+# Tests for the :!cmd! variable modifier, which evaluates the modifier
+# argument, independent of the value or the name of the original variable.
.if ${:!echo hello | tr 'l' 'l'!} != "hello"
-. warning unexpected
+. error
.endif
# The output is truncated at the first null byte.
# Cmd_Exec returns only a string pointer without length information.
+# Truncating the output is not necessarily intended but may also be a side
+# effect from the implementation. Having null bytes in the output of a
+# shell command is so unusual that it doesn't matter in practice.
.if ${:!echo hello | tr 'l' '\0'!} != "he"
-. warning unexpected
+. error
.endif
+# The newline at the end of the output is stripped.
.if ${:!echo!} != ""
-. warning A newline at the end of the output must be stripped.
+. error
.endif
+# Only the final newline of the output is stripped. All other newlines are
+# converted to spaces.
.if ${:!echo;echo!} != " "
-. warning Only a single newline at the end of the output is stripped.
+. error
.endif
+# Each newline in the output is converted to a space, except for the newline
+# at the end of the output, which is stripped.
.if ${:!echo;echo;echo;echo!} != " "
-. warning Other newlines in the output are converted to spaces.
+. error
.endif
all:
diff --git a/unit-tests/varmod-ifelse.exp b/unit-tests/varmod-ifelse.exp
index ac1527a11c9e..75518c08117f 100644
--- a/unit-tests/varmod-ifelse.exp
+++ b/unit-tests/varmod-ifelse.exp
@@ -3,6 +3,14 @@ make: "varmod-ifelse.mk" line 27: Malformed conditional (${${:Uvariable expressi
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: "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
+lhs = "", rhs = "", op = !=
+make: "varmod-ifelse.mk" line 92: warning: Oops, the parse error should have been propagated.
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 8bd67195282c..ea94dc875e4d 100644
--- a/unit-tests/varmod-ifelse.mk
+++ b/unit-tests/varmod-ifelse.mk
@@ -1,4 +1,4 @@
-# $NetBSD: varmod-ifelse.mk,v 1.5 2020/10/23 14:24:51 rillig Exp $
+# $NetBSD: varmod-ifelse.mk,v 1.6 2020/11/12 00:29:55 rillig Exp $
#
# Tests for the ${cond:?then:else} variable modifier, which evaluates either
# the then-expression or the else-expression, depending on the condition.
@@ -57,5 +57,41 @@ COND:= ${${UNDEF} == "":?bad-assign:bad-assign}
. error
.endif
+# This line generates 2 error messages. The first comes from evaluating the
+# malformed conditional "1 == == 2", which is reported as "Bad conditional
+# expression" by ApplyModifier_IfElse. The variable expression containing that
+# conditional therefore returns a parse error from Var_Parse, and this parse
+# error propagates to CondEvalExpression, where the "Malformed conditional"
+# comes from.
+.if ${1 == == 2:?yes:no} != ""
+. error
+.else
+. error
+.endif
+
+# If the "Bad conditional expression" appears in a quoted string literal, the
+# error message "Malformed conditional" is not printed, leaving only the "Bad
+# 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
+# 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
+# to var.c, it cannot distinguish this return value from an ordinary empty
+# string. The left-hand side of the comparison is therefore just an empty
+# string, which is obviously equal to the empty string on the right-hand side.
+#
+# XXX: The debug log for -dc shows a comparison between 1.0 and 0.0. The
+# condition should be detected as being malformed before any comparison is
+# done since there is no well-formed comparison in the condition at all.
+.MAKEFLAGS: -dc
+.if "${1 == == 2:?yes:no}" != ""
+. error
+.else
+. warning Oops, the parse error should have been propagated.
+.endif
+.MAKEFLAGS: -d0
+
all:
@:;
diff --git a/unit-tests/varmod-loop.exp b/unit-tests/varmod-loop.exp
index 497de68df82c..66cfd6f51e16 100644
--- a/unit-tests/varmod-loop.exp
+++ b/unit-tests/varmod-loop.exp
@@ -1,3 +1,11 @@
+ParseReadLine (117): '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}'
+CondParser_Eval: ${SUBST_CONTAINING_LOOP} != "\$\$ \$\$\$\$ \$\$\$\$"
+lhs = "$$ $$$$ $$$$", rhs = "$$ $$$$ $$$$", op = !=
+ParseReadLine (147): '.MAKEFLAGS: -d0'
+ParseDoDependency(.MAKEFLAGS: -d0)
:+one+ +two+ +three+:
:x1y x2y x3y:
:x1y x2y x3y:
diff --git a/unit-tests/varmod-loop.mk b/unit-tests/varmod-loop.mk
index a3fbbfceae8d..654c449d7bfa 100644
--- a/unit-tests/varmod-loop.mk
+++ b/unit-tests/varmod-loop.mk
@@ -1,7 +1,9 @@
-# $NetBSD: varmod-loop.mk,v 1.5 2020/10/31 12:34:03 rillig Exp $
+# $NetBSD: varmod-loop.mk,v 1.8 2020/11/12 00:40:55 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
@@ -57,7 +59,7 @@ mod-loop-varname-dollar:
@echo $@:${1 2 3:L:@v$$@($v)@:Q}.
@echo $@:${1 2 3:L:@v$$$@($v)@:Q}.
-# Demonstrate that it is possible to generate dollar characters using the
+# Demonstrate that it is possible to generate dollar signs using the
# :@ modifier.
#
# These are edge cases that could have resulted in a parse error as well
@@ -100,3 +102,46 @@ mod-loop-dollar:
.if defined(var)
. error
.endif
+
+# Assignment using the ':=' operator, combined with the :@var@ modifier
+#
+8_DOLLARS= $$$$$$$$
+# 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.
+#
+.MAKEFLAGS: -dcp
+USE_8_DOLLARS= ${:U1:@var@${8_DOLLARS}@} ${8_DOLLARS} $$$$$$$$
+.if ${USE_8_DOLLARS} != "\$\$\$\$ \$\$\$\$ \$\$\$\$"
+. error
+.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 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
+# 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.
+# The variable SUBST_CONTAINING_LOOP therefore gets assigned the raw value
+# "$$$$ $$$$$$$$ $$$$$$$$".
+#
+# The variable expression in the condition then expands this raw stored value
+# once, resulting in "$$ $$$$ $$$$". The effects from VARE_KEEP_DOLLAR no
+# longer take place since they had only been active during the evaluation of
+# the variable assignment.
+.if ${SUBST_CONTAINING_LOOP} != "\$\$ \$\$\$\$ \$\$\$\$"
+. error
+.endif
+.MAKEFLAGS: -d0
diff --git a/unit-tests/varmod-match.mk b/unit-tests/varmod-match.mk
index 5e16a9cc8bf1..9b56fb451eda 100644
--- a/unit-tests/varmod-match.mk
+++ b/unit-tests/varmod-match.mk
@@ -1,4 +1,4 @@
-# $NetBSD: varmod-match.mk,v 1.5 2020/09/13 05:36:26 rillig Exp $
+# $NetBSD: varmod-match.mk,v 1.6 2020/11/15 18:33:41 rillig Exp $
#
# Tests for the :M variable modifier, which filters words that match the
# given pattern.
@@ -51,5 +51,10 @@ ${:U*}= asterisk
. error
.endif
+# TODO: ${VAR:M(((}}}}
+# TODO: ${VAR:M{{{)))}
+# TODO: ${VAR:M${UNBALANCED}}
+# TODO: ${VAR:M${:U(((\}\}\}}}
+
all:
@:;
diff --git a/unit-tests/varmod-order-shuffle.mk b/unit-tests/varmod-order-shuffle.mk
index 4ad057e0a810..185141b6c4a5 100644
--- a/unit-tests/varmod-order-shuffle.mk
+++ b/unit-tests/varmod-order-shuffle.mk
@@ -1,8 +1,10 @@
-# $NetBSD: varmod-order-shuffle.mk,v 1.5 2020/10/24 08:46:08 rillig Exp $
+# $NetBSD: varmod-order-shuffle.mk,v 1.6 2020/11/09 20:16:33 rillig Exp $
#
# Tests for the :Ox variable modifier, which returns the words of the
# variable, shuffled.
#
+# The variable modifier :Ox is available since 2005-06-01.
+#
# As of 2020-08-16, make uses random(3) seeded by the current time in seconds.
# This makes the random numbers completely predictable since there is no other
# part of make that uses random numbers.
diff --git a/unit-tests/varmod-shell.exp b/unit-tests/varmod-shell.exp
index 39a9383953dd..9aef0c9e5acc 100644
--- a/unit-tests/varmod-shell.exp
+++ b/unit-tests/varmod-shell.exp
@@ -1 +1,3 @@
+make: "echo word; false" returned non-zero status
+make: "echo word; false" returned non-zero status
exit status 0
diff --git a/unit-tests/varmod-shell.mk b/unit-tests/varmod-shell.mk
index 052968004f1b..db82e302f2a8 100644
--- a/unit-tests/varmod-shell.mk
+++ b/unit-tests/varmod-shell.mk
@@ -1,9 +1,35 @@
-# $NetBSD: varmod-shell.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+# $NetBSD: varmod-shell.mk,v 1.5 2020/11/17 20:11:02 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 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.
+.if ${:!echo word!} != "word"
+. error
+.endif
+
+# If the command exits with non-zero, an error message is printed.
+# XXX: Processing continues as usual though.
+#
+# Between 2000-04-29 and 2020-11-17, the error message mentioned the previous
+# value of the expression (which is usually an empty string) instead of the
+# command that was executed. It's strange that such a simple bug could
+# survive such a long time.
+.if ${:!echo word; false!} != "word"
+. error
+.endif
+.if ${:Uprevious value:!echo word; false!} != "word"
+. error
+.endif
+
all:
@:;
diff --git a/unit-tests/varmod-subst.exp b/unit-tests/varmod-subst.exp
index e752fb8058a8..3122c17b1ed3 100644
--- a/unit-tests/varmod-subst.exp
+++ b/unit-tests/varmod-subst.exp
@@ -9,28 +9,39 @@ mod-subst-delimiter:
1 two 3 horizontal tabulator
1 two 3 space
1 two 3 exclamation mark
-1 two 3 double quotes
-1 two 3 hash
-1 two 3 dollar
-1 two 3 percent
+1 two 3 quotation mark
+1 two 3 number sign
+1 two 3 dollar sign
+1 two 3 percent sign
+1 two 3 ampersand
1 two 3 apostrophe
-1 two 3 opening parenthesis
-1 two 3 closing parenthesis
+1 two 3 left parenthesis
+1 two 3 right parenthesis
+1 two 3 asterisk
+1 two 3 plus sign
+1 two 3 comma
+1 two 3 hyphen-minus
+1 two 3 full stop
+1 two 3 solidus
1 two 3 digit
1 two 3 colon
-1 two 3 less than sign
-1 two 3 equal sign
-1 two 3 greater than sign
+1 two 3 semicolon
+1 two 3 less-than sign
+1 two 3 equals sign
+1 two 3 greater-than sign
1 two 3 question mark
-1 two 3 at
-1 two 3 letter
-1 two 3 opening bracket
-1 two 3 backslash
-1 two 3 closing bracket
-1 two 3 caret
-1 two 3 opening brace
+1 two 3 commercial at
+1 two 3 capital letter
+1 two 3 left square bracket
+1 two 3 reverse solidus
+1 two 3 right square bracket
+1 two 3 circumflex accent
+1 two 3 low line
+1 two 3 grave accent
+1 two 3 small letter
+1 two 3 left curly bracket
1 two 3 vertical line
-1 two 3 closing brace
+1 two 3 right curly bracket
1 two 3 tilde
mod-subst-chain:
A B c.
diff --git a/unit-tests/varmod-subst.mk b/unit-tests/varmod-subst.mk
index 5fc657ea10ee..3c3ee673c07a 100644
--- a/unit-tests/varmod-subst.mk
+++ b/unit-tests/varmod-subst.mk
@@ -1,4 +1,4 @@
-# $NetBSD: varmod-subst.mk,v 1.4 2020/10/24 08:46:08 rillig Exp $
+# $NetBSD: varmod-subst.mk,v 1.7 2020/11/15 20:20:58 rillig Exp $
#
# Tests for the :S,from,to, variable modifier.
@@ -8,55 +8,72 @@ all: mod-subst-chain
all: mod-subst-dollar
WORDS= sequences of letters
+
.if ${WORDS:S,,,} != ${WORDS}
. warning The empty pattern matches something.
.endif
+
.if ${WORDS:S,e,*,1} != "s*quences of letters"
. warning The :S modifier flag '1' is not applied exactly once.
.endif
+
.if ${WORDS:S,f,*,1} != "sequences o* letters"
. warning The :S modifier flag '1' is only applied to the first word,\
not to the first occurrence.
.endif
+
.if ${WORDS:S,e,*,} != "s*quences of l*tters"
. warning The :S modifier does not replace every first match per word.
.endif
+
.if ${WORDS:S,e,*,g} != "s*qu*nc*s of l*tt*rs"
. warning The :S modifier flag 'g' does not replace every occurrence.
.endif
+
.if ${WORDS:S,^sequ,occurr,} != "occurrences of letters"
. warning The :S modifier fails for a short match anchored at the start.
.endif
+
.if ${WORDS:S,^of,with,} != "sequences with letters"
. warning The :S modifier fails for an exact match anchored at the start.
.endif
+
.if ${WORDS:S,^office,does not match,} != ${WORDS}
. warning The :S modifier matches a too long pattern anchored at the start.
.endif
+
.if ${WORDS:S,f$,r,} != "sequences or letters"
. warning The :S modifier fails for a short match anchored at the end.
.endif
+
.if ${WORDS:S,s$,,} != "sequence of letter"
. warning The :S modifier fails to replace one occurrence per word.
.endif
+
.if ${WORDS:S,of$,,} != "sequences letters"
. warning The :S modifier fails for an exact match anchored at the end.
.endif
+
.if ${WORDS:S,eof$,,} != ${WORDS}
. warning The :S modifier matches a too long pattern anchored at the end.
.endif
+
.if ${WORDS:S,^of$,,} != "sequences letters"
. warning The :S modifier does not match a word anchored at both ends.
.endif
+
.if ${WORDS:S,^o$,,} != ${WORDS}
. warning The :S modifier matches a prefix anchored at both ends.
.endif
+
.if ${WORDS:S,^f$,,} != ${WORDS}
. warning The :S modifier matches a suffix anchored at both ends.
.endif
+
.if ${WORDS:S,^eof$,,} != ${WORDS}
. warning The :S modifier matches a too long prefix anchored at both ends.
.endif
+
.if ${WORDS:S,^office$,,} != ${WORDS}
. warning The :S modifier matches a too long suffix anchored at both ends.
.endif
@@ -78,30 +95,41 @@ mod-subst-delimiter:
@echo ${:U1 2 3:S 2 two :Q} horizontal tabulator
@echo ${:U1 2 3:S 2 two :Q} space
@echo ${:U1 2 3:S!2!two!:Q} exclamation mark
- @echo ${:U1 2 3:S"2"two":Q} double quotes
+ @echo ${:U1 2 3:S"2"two":Q} quotation mark
# In shell command lines, the hash does not need to be escaped.
# It needs to be escaped in variable assignment lines though.
- @echo ${:U1 2 3:S#2#two#:Q} hash
- @echo ${:U1 2 3:S$2$two$:Q} dollar
- @echo ${:U1 2 3:S%2%two%:Q} percent
+ @echo ${:U1 2 3:S#2#two#:Q} number sign
+ @echo ${:U1 2 3:S$2$two$:Q} dollar sign
+ @echo ${:U1 2 3:S%2%two%:Q} percent sign
+ @echo ${:U1 2 3:S&2&two&:Q} ampersand
@echo ${:U1 2 3:S'2'two':Q} apostrophe
- @echo ${:U1 2 3:S(2(two(:Q} opening parenthesis
- @echo ${:U1 2 3:S)2)two):Q} closing parenthesis
+ @echo ${:U1 2 3:S(2(two(:Q} left parenthesis
+ @echo ${:U1 2 3:S)2)two):Q} right parenthesis
+ @echo ${:U1 2 3:S*2*two*:Q} asterisk
+ @echo ${:U1 2 3:S+2+two+:Q} plus sign
+ @echo ${:U1 2 3:S,2,two,:Q} comma
+ @echo ${:U1 2 3:S-2-two-:Q} hyphen-minus
+ @echo ${:U1 2 3:S.2.two.:Q} full stop
+ @echo ${:U1 2 3:S/2/two/:Q} solidus
@echo ${:U1 2 3:S121two1:Q} digit
@echo ${:U1 2 3:S:2:two::Q} colon
- @echo ${:U1 2 3:S<2<two<:Q} less than sign
- @echo ${:U1 2 3:S=2=two=:Q} equal sign
- @echo ${:U1 2 3:S>2>two>:Q} greater than sign
+ @echo ${:U1 2 3:S;2;two;:Q} semicolon
+ @echo ${:U1 2 3:S<2<two<:Q} less-than sign
+ @echo ${:U1 2 3:S=2=two=:Q} equals sign
+ @echo ${:U1 2 3:S>2>two>:Q} greater-than sign
@echo ${:U1 2 3:S?2?two?:Q} question mark
- @echo ${:U1 2 3:S@2@two@:Q} at
- @echo ${:U1 2 3:Sa2atwoa:Q} letter
- @echo ${:U1 2 3:S[2[two[:Q} opening bracket
- @echo ${:U1 2 3:S\2\two\:Q} backslash
- @echo ${:U1 2 3:S]2]two]:Q} closing bracket
- @echo ${:U1 2 3:S^2^two^:Q} caret
- @echo ${:U1 2 3:S{2{two{:Q} opening brace
+ @echo ${:U1 2 3:S@2@two@:Q} commercial at
+ @echo ${:U1 2 3:SA2AtwoA:Q} capital letter
+ @echo ${:U1 2 3:S[2[two[:Q} left square bracket
+ @echo ${:U1 2 3:S\2\two\:Q} reverse solidus
+ @echo ${:U1 2 3:S]2]two]:Q} right square bracket
+ @echo ${:U1 2 3:S^2^two^:Q} circumflex accent
+ @echo ${:U1 2 3:S_2_two_:Q} low line
+ @echo ${:U1 2 3:S`2`two`:Q} grave accent
+ @echo ${:U1 2 3:Sa2atwoa:Q} small letter
+ @echo ${:U1 2 3:S{2{two{:Q} left curly bracket
@echo ${:U1 2 3:S|2|two|:Q} vertical line
- @echo ${:U1 2 3:S}2}two}:Q} closing brace
+ @echo ${:U1 2 3:S}2}two}:Q} right curly bracket
@echo ${:U1 2 3:S~2~two~:Q} tilde
# The :S and :C modifiers can be chained without a separating ':'.
@@ -121,7 +149,7 @@ mod-subst-chain:
# modifiers with the matching modifiers.
@echo ${:Uvalue:S,a,x,i}.
-# No matter how many dollar characters there are, they all get merged
+# No matter how many dollar signs there are, they all get merged
# into a single dollar by the :S modifier.
#
# As of 2020-08-09, this is because ParseModifierPart sees a '$' and
@@ -145,7 +173,7 @@ mod-subst-dollar:
@echo $@:${:U40:S,^,$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$,:Q}:
# This generates no dollar at all:
@echo $@:${:UU8:S,^,${:U$$$$$$$$},:Q}:
-# Here is an alternative way to generate dollar characters.
+# Here is an alternative way to generate dollar signs.
# It's unexpectedly complicated though.
@echo $@:${:U:range=5:ts\x24:C,[0-9],,g:Q}:
# In modifiers, dollars are escaped using the backslash, not using another
diff --git a/unit-tests/varmod-to-abs.exp b/unit-tests/varmod-to-abs.exp
index 39a9383953dd..426b4d39744f 100644
--- a/unit-tests/varmod-to-abs.exp
+++ b/unit-tests/varmod-to-abs.exp
@@ -1 +1,5 @@
+make: "varmod-to-abs.mk" line 18: does-not-exist.c
+make: "varmod-to-abs.mk" line 19: does-not-exist.c
+cached_realpath: varmod-to-abs.mk -> varmod-to-abs.mk
+make: "varmod-to-abs.mk" line 23: varmod-to-abs.mk
exit status 0
diff --git a/unit-tests/varmod-to-abs.mk b/unit-tests/varmod-to-abs.mk
index 7a74e89088e5..7f23318487e3 100644
--- a/unit-tests/varmod-to-abs.mk
+++ b/unit-tests/varmod-to-abs.mk
@@ -1,9 +1,28 @@
-# $NetBSD: varmod-to-abs.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+# $NetBSD: varmod-to-abs.mk,v 1.5 2020/11/15 05:48:17 rillig Exp $
#
# Tests for the :tA variable modifier, which returns the absolute path for
# each of the words in the variable value.
# TODO: Implementation
+# Between 2016-06-03 and 2020-11-14, it was possible to trick the :tA modifier
+# into resolving completely unrelated absolute paths by defining a global
+# variable with the same name as the path that is to be resolved. There were
+# a few restrictions though: The "redirected" path had to start with a slash,
+# and it had to exist (see ModifyWord_Realpath).
+#
+# This unintended behavior was caused by cached_realpath using a GNode for
+# keeping the cache, just like the GNode for global variables.
+.MAKEFLAGS: -dd
+does-not-exist.c= /dev/null
+.info ${does-not-exist.c:L:tA}
+.info ${does-not-exist.c:L:tA}
+
+# The output of the following line is modified by the global _SED_CMDS in
+# unit-tests/Makefile. See the .rawout file for the truth.
+.info ${MAKEFILE:tA}
+
+.MAKEFLAGS: -d0
+
all:
@:;
diff --git a/unit-tests/varmod-to-lower.mk b/unit-tests/varmod-to-lower.mk
index 6ab4af740fae..19d3406054b7 100644
--- a/unit-tests/varmod-to-lower.mk
+++ b/unit-tests/varmod-to-lower.mk
@@ -1,7 +1,9 @@
-# $NetBSD: varmod-to-lower.mk,v 1.4 2020/10/24 08:46:08 rillig Exp $
+# $NetBSD: varmod-to-lower.mk,v 1.5 2020/11/15 20:20:58 rillig Exp $
#
# Tests for the :tl variable modifier, which returns the words in the
# variable value, converted to lowercase.
+#
+# TODO: What about non-ASCII characters? ISO-8859-1, UTF-8?
.if ${:UUPPER:tl} != "upper"
. error
diff --git a/unit-tests/varmod-to-separator.mk b/unit-tests/varmod-to-separator.mk
index 89aa3d978bee..08c6126ecc68 100644
--- a/unit-tests/varmod-to-separator.mk
+++ b/unit-tests/varmod-to-separator.mk
@@ -1,4 +1,4 @@
-# $NetBSD: varmod-to-separator.mk,v 1.6 2020/11/01 14:36:25 rillig Exp $
+# $NetBSD: varmod-to-separator.mk,v 1.7 2020/11/15 20:20:58 rillig Exp $
#
# Tests for the :ts variable modifier, which joins the words of the variable
# using an arbitrary character as word separator.
@@ -166,4 +166,10 @@ WORDS= one two three four five six
. info This line is not reached.
.endif
+# TODO: This modifier used to accept decimal numbers as well, in the form
+# ':ts\120'. When has this been changed to octal, and what happens now
+# for ':ts\90' ('Z' in decimal ASCII, undefined in octal)?
+
+# TODO: :ts\x1F600
+
all:
diff --git a/unit-tests/varmod-undefined.mk b/unit-tests/varmod-undefined.mk
index 1beaa763716b..e06fc73244ab 100644
--- a/unit-tests/varmod-undefined.mk
+++ b/unit-tests/varmod-undefined.mk
@@ -1,9 +1,10 @@
-# $NetBSD: varmod-undefined.mk,v 1.6 2020/10/24 08:46:08 rillig Exp $
+# $NetBSD: varmod-undefined.mk,v 1.7 2020/11/15 20:20:58 rillig Exp $
#
# Tests for the :U variable modifier, which returns the given string
# if the variable is undefined.
#
# See also:
+# directive-for.mk
# varmod-defined.mk
# The pattern ${:Uword} is heavily used when expanding .for loops.
diff --git a/unit-tests/varmod.exp b/unit-tests/varmod.exp
index e4257bb0b596..a80979e1410d 100644
--- a/unit-tests/varmod.exp
+++ b/unit-tests/varmod.exp
@@ -1,6 +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: Unknown directive "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 0a2a4c52c29b..b496bdd206a2 100644
--- a/unit-tests/varmod.mk
+++ b/unit-tests/varmod.mk
@@ -1,4 +1,4 @@
-# $NetBSD: varmod.mk,v 1.3 2020/09/13 07:42:20 rillig Exp $
+# $NetBSD: varmod.mk,v 1.4 2020/11/02 17:30:22 rillig Exp $
#
# Tests for variable modifiers, such as :Q, :S,from,to or :Ufallback.
@@ -48,4 +48,13 @@ DOLLAR2= ${:U\$}
. error
.endif
+# The variable modifier :P does not fall back to the SysV modifier.
+# Therefore the modifier :P=RE generates a parse error.
+# XXX: The .error should not be reached since the variable expression is
+# malformed.
+VAR= STOP
+.if ${VAR:P=RE} != "STORE"
+. error
+.endif
+
all: # nothing
diff --git a/unit-tests/varname-dot-shell.exp b/unit-tests/varname-dot-shell.exp
index 704d4863d4e9..f219515444cf 100755
--- a/unit-tests/varname-dot-shell.exp
+++ b/unit-tests/varname-dot-shell.exp
@@ -1,6 +1,6 @@
ParseReadLine (10): 'ORIG_SHELL:= ${.SHELL}'
Global:ORIG_SHELL =
-Var_Parse: ${.SHELL} with VARE_WANTRES|VARE_ASSIGN
+Var_Parse: ${.SHELL} with VARE_WANTRES|VARE_KEEP_DOLLAR
Global:delete .SHELL (not found)
Command:.SHELL = (details omitted)
Global:ORIG_SHELL = (details omitted)
diff --git a/unit-tests/varname-empty.exp b/unit-tests/varname-empty.exp
index ba465cc3eff2..24c596e1c568 100644
--- a/unit-tests/varname-empty.exp
+++ b/unit-tests/varname-empty.exp
@@ -1,5 +1,5 @@
Var_Set("${:U}", "cmdline-u", ...) name expands to empty string - ignored
-Var_Set("", "cmline-plain", ...) name expands to empty string - ignored
+Var_Set("", "cmdline-plain", ...) name expands to empty string - ignored
Var_Set("", "default", ...) name expands to empty string - ignored
Var_Set("", "assigned", ...) name expands to empty string - ignored
Var_Set("", "appended", ...) name expands to empty string - ignored
diff --git a/unit-tests/varname-makefile.exp b/unit-tests/varname-makefile.exp
index 39a9383953dd..67919c400193 100755
--- a/unit-tests/varname-makefile.exp
+++ b/unit-tests/varname-makefile.exp
@@ -1 +1,2 @@
+: In the end, MAKEFILE is /dev/null.
exit status 0
diff --git a/unit-tests/varname-makefile.mk b/unit-tests/varname-makefile.mk
index 785fe9301df2..641c53de9cf8 100755
--- a/unit-tests/varname-makefile.mk
+++ b/unit-tests/varname-makefile.mk
@@ -1,4 +1,4 @@
-# $NetBSD: varname-makefile.mk,v 1.2 2020/09/05 06:25:38 rillig Exp $
+# $NetBSD: varname-makefile.mk,v 1.3 2020/11/09 22:36:44 rillig Exp $
#
# Tests for the special MAKEFILE variable, which contains the current
# makefile from the -f command line option.
@@ -41,4 +41,9 @@ MAKEFILE= overwritten
.endif
all:
- @:;
+ # MAKEFILE is the file that appeared last in the command line.
+ : In the end, MAKEFILE is ${MAKEFILE}.
+
+# Additional makefiles can be added while reading a makefile. They will be
+# read in order.
+.MAKEFLAGS: -f /dev/null
diff --git a/unit-tests/varname-vpath.exp b/unit-tests/varname-vpath.exp
index 39a9383953dd..bf7a3036e99d 100644
--- a/unit-tests/varname-vpath.exp
+++ b/unit-tests/varname-vpath.exp
@@ -1 +1,12 @@
+CondParser_Eval: !defined(TEST_MAIN)
+CondParser_Eval: exists(file-in-subdirectory)
+exists(file-in-subdirectory) result is ""
+CondParser_Eval: exists(file2-in-subdirectory)
+exists(file2-in-subdirectory) result is ""
+CondParser_Eval: exists(file-in-subdirectory)
+exists(file-in-subdirectory) result is "varname-vpath.dir/file-in-subdirectory"
+: yes 1
+CondParser_Eval: exists(file2-in-subdirectory)
+exists(file2-in-subdirectory) result is "varname-vpath.dir2/file2-in-subdirectory"
+: yes 2
exit status 0
diff --git a/unit-tests/varname-vpath.mk b/unit-tests/varname-vpath.mk
index 8924647a5072..65fe9945c202 100644
--- a/unit-tests/varname-vpath.mk
+++ b/unit-tests/varname-vpath.mk
@@ -1,8 +1,42 @@
-# $NetBSD: varname-vpath.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+# $NetBSD: varname-vpath.mk,v 1.3 2020/11/10 00:19:19 rillig Exp $
#
-# Tests for the special VPATH variable.
+# Tests for the special VPATH variable, which is an obsolete way of
+# specifying a colon-separated search path. This search path is not active
+# when the makefiles are read, but only later when the shell commands are run.
+#
+# Instead of the VPATH, better use the -I option or the special target .PATH.
+
+.if !defined(TEST_MAIN)
+
+all: .SILENT
+ rm -rf varname-vpath.dir
+ mkdir varname-vpath.dir
+ touch varname-vpath.dir/file-in-subdirectory
+ rm -rf varname-vpath.dir2
+ mkdir varname-vpath.dir2
+ touch varname-vpath.dir2/file2-in-subdirectory
+
+ TEST_MAIN=yes VPATH=varname-vpath.dir:varname-vpath.dir2 \
+ ${MAKE} -f ${MAKEFILE} -dc
-# TODO: Implementation
+ rm -r varname-vpath.dir
+ rm -r varname-vpath.dir2
+
+.else
+
+# The VPATH variable does not take effect at parse time.
+# It is evaluated only once, between reading the makefiles and making the
+# targets. Therefore it could also be an ordinary variable, it doesn't need
+# to be an environment variable or a command line variable.
+. if exists(file-in-subdirectory)
+. error
+. endif
+. if exists(file2-in-subdirectory)
+. error
+. endif
all:
- @:;
+ : ${exists(file-in-subdirectory):L:?yes 1:no 1}
+ : ${exists(file2-in-subdirectory):L:?yes 2:no 2}
+
+.endif
diff --git a/unit-tests/varname.exp b/unit-tests/varname.exp
index 39a9383953dd..93962d7fb7b7 100644
--- a/unit-tests/varname.exp
+++ b/unit-tests/varname.exp
@@ -1 +1,24 @@
-exit status 0
+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, VEF_UNDEF)
+Result of ${:UVAR(((} is "VAR(((" (VARE_UNDEFERR|VARE_WANTRES, none, VEF_UNDEF|VEF_DEF)
+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, VEF_UNDEF)
+Result of ${:UVAR\(\(\(} is "VAR\(\(\(" (VARE_UNDEFERR|VARE_WANTRES, none, VEF_UNDEF|VEF_DEF)
+Global:.ALLTARGETS = VAR(((=) VAR\(\(\(=
+make: "varname.mk" line 35: Need an operator
+Var_Parse: ${VARNAME} with VARE_WANTRES
+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/varname.mk b/unit-tests/varname.mk
index e9fbc89873ca..f586c7602cb7 100644
--- a/unit-tests/varname.mk
+++ b/unit-tests/varname.mk
@@ -1,8 +1,44 @@
-# $NetBSD: varname.mk,v 1.4 2020/10/18 08:47:54 rillig Exp $
+# $NetBSD: varname.mk,v 1.8 2020/11/02 22:59:48 rillig Exp $
#
# Tests for special variables, such as .MAKE or .PARSEDIR.
+# And for variable names in general.
-# TODO: Implementation
+.MAKEFLAGS: -dv
+
+# In variable names, braces are allowed, but they must be balanced.
+# Parentheses and braces may be mixed.
+VAR{{{}}}= 3 braces
+.if "${VAR{{{}}}}" != "3 braces"
+. error
+.endif
+
+# In variable expressions, the parser works differently. It doesn't treat
+# braces and parentheses equally, therefore the first closing brace already
+# marks the end of the variable name.
+VARNAME= VAR(((
+${VARNAME}= 3 open parentheses
+.if "${VAR(((}}}}" != "3 open parentheses}}}"
+. error
+.endif
+
+# In the above test, the variable name is constructed indirectly. Neither
+# of the following expressions produces the intended effect.
+#
+# This is not a variable assignment since the parentheses and braces are not
+# balanced. At the end of the line, there are still 3 levels open, which
+# means the variable name is not finished.
+${:UVAR(((}= try1
+# On the left-hand side of a variable assignments, the backslash is not parsed
+# as an escape character, therefore the parentheses still count to the nesting
+# level, which at the end of the line is still 3. Therefore this is not a
+# variable assignment as well.
+${:UVAR\(\(\(}= try2
+# To assign to a variable with an arbitrary name, the variable name has to
+# come from an external source, not the text that is parsed in the assignment
+# itself. This is exactly the reason why further above, the indirect
+# ${VARNAME} works, while all other attempts fail.
+${VARNAME}= try3
+
+.MAKEFLAGS: -d0
all:
- @:;
diff --git a/unit-tests/varparse-errors.exp b/unit-tests/varparse-errors.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/unit-tests/varparse-errors.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/unit-tests/varparse-errors.mk b/unit-tests/varparse-errors.mk
new file mode 100644
index 000000000000..42f5b65a728e
--- /dev/null
+++ b/unit-tests/varparse-errors.mk
@@ -0,0 +1,35 @@
+# $NetBSD: varparse-errors.mk,v 1.1 2020/11/08 16:44:47 rillig Exp $
+
+# Tests for parsing and evaluating all kinds of variable expressions.
+#
+# This is the basis for redesigning the error handling in Var_Parse and
+# Var_Subst, collecting typical and not so typical use cases.
+#
+# See also:
+# VarParseResult
+# Var_Parse
+# Var_Subst
+
+PLAIN= plain value
+
+LITERAL_DOLLAR= To get a dollar, double $$ it.
+
+INDIRECT= An ${:Uindirect} value.
+
+REF_UNDEF= A reference to an ${UNDEF}undefined variable.
+
+ERR_UNCLOSED= An ${UNCLOSED variable expression.
+
+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.
+# The variable itself must be defined.
+# It may refer to undefined variables though.
+.if ${REF_UNDEF} != "A reference to an undefined variable."
+. error
+.endif
+
+all:
diff --git a/unit-tests/varparse-undef-partial.mk b/unit-tests/varparse-undef-partial.mk
index 4851b6d9d567..27f44d79b31a 100644
--- a/unit-tests/varparse-undef-partial.mk
+++ b/unit-tests/varparse-undef-partial.mk
@@ -1,4 +1,4 @@
-# $NetBSD: varparse-undef-partial.mk,v 1.2 2020/09/27 09:53:41 rillig Exp $
+# $NetBSD: varparse-undef-partial.mk,v 1.3 2020/11/04 05:10:01 rillig Exp $
# When an undefined variable is expanded in a ':=' assignment, only the
# initial '$' of the variable expression is skipped by the parser, while
@@ -9,7 +9,7 @@ LIST= ${DEF} ${UNDEF} ${VAR.${PARAM}} end
DEF= defined
PARAM= :Q
-# The expression ${VAR.{PARAM}} refers to the variable named "VAR.:Q",
+# The expression ${VAR.${PARAM}} refers to the variable named "VAR.:Q",
# with the ":Q" being part of the name. This variable is not defined,
# therefore the initial '$' of that whole expression is skipped by the
# parser (see Var_Subst, the Buf_AddByte in the else branch) and the rest
@@ -28,15 +28,15 @@ VAR.= var-dot without parameter
${:UVAR.\:Q}= var-dot with parameter :Q
# At this point, the variable "VAR." is defined, therefore the expression
-# ${VAR.:Q} is expanded as usual.
+# ${VAR.:Q} is expanded, consisting of the variable name "VAR." and the
+# modifier ":Q".
.if ${EVAL} != "defined var-dot\\ without\\ parameter end"
. error ${EVAL}
.endif
# In contrast to the previous line, evaluating the original LIST again now
-# produces a different result since the ":Q" has already been inserted
-# literally into the expression. The variable named "VAR.:Q" is defined,
-# therefore it is resolved as usual. The ":Q" is interpreted as part of the
+# produces a different result since the variable named "VAR.:Q" is now
+# defined. It is expanded as usual, interpreting the ":Q" as part of the
# variable name, as would be expected from reading the variable expression.
EVAL:= ${LIST}
.if ${EVAL} != "defined var-dot with parameter :Q end"
diff --git a/unit-tests/varshell.exp b/unit-tests/varshell.exp
deleted file mode 100644
index 54df3527ed1c..000000000000
--- a/unit-tests/varshell.exp
+++ /dev/null
@@ -1,10 +0,0 @@
-make: "varshell.mk" line 6: warning: "/bin/no/such/command 2> /dev/null" returned non-zero status
-make: "varshell.mk" line 9: warning: "false" returned non-zero status
-make: "varshell.mk" line 10: warning: "echo "output before the error"; false" returned non-zero status
-EXEC_FAILED=''
-TERMINATED_BY_SIGNAL=''
-ERROR_NO_OUTPUT=''
-ERROR_WITH_OUTPUT='output before the error'
-NO_ERROR_NO_OUTPUT=''
-NO_ERROR_WITH_OUTPUT='this is good'
-exit status 0
diff --git a/unit-tests/varshell.mk b/unit-tests/varshell.mk
deleted file mode 100644
index 113c265cce3c..000000000000
--- a/unit-tests/varshell.mk
+++ /dev/null
@@ -1,20 +0,0 @@
-# $Id: varshell.mk,v 1.6 2020/10/26 17:55:23 sjg Exp $
-# $NetBSD: varshell.mk,v 1.4 2020/10/24 08:50:17 rillig Exp $
-#
-# Test VAR != shell command
-
-EXEC_FAILED!= /bin/no/such/command 2> /dev/null
-# SunOS cannot handle this one
-#TERMINATED_BY_SIGNAL!= kill -14 $$$$
-ERROR_NO_OUTPUT!= false
-ERROR_WITH_OUTPUT!= echo "output before the error"; false
-NO_ERROR_NO_OUTPUT!= true
-NO_ERROR_WITH_OUTPUT!= echo "this is good"
-
-allvars= EXEC_FAILED TERMINATED_BY_SIGNAL ERROR_NO_OUTPUT ERROR_WITH_OUTPUT \
- NO_ERROR_NO_OUTPUT NO_ERROR_WITH_OUTPUT
-
-all:
-.for v in ${allvars}
- @echo ${v}=\'${${v}}\'
-.endfor