aboutsummaryrefslogtreecommitdiff
path: root/unit-tests
diff options
context:
space:
mode:
Diffstat (limited to 'unit-tests')
-rw-r--r--unit-tests/Makefile159
-rw-r--r--unit-tests/cmd-errors-jobs.exp9
-rw-r--r--unit-tests/cmd-errors-jobs.mk32
-rw-r--r--unit-tests/cmd-errors.mk4
-rw-r--r--unit-tests/cmdline.mk2
-rw-r--r--unit-tests/compat-error.exp15
-rw-r--r--unit-tests/compat-error.mk37
-rw-r--r--unit-tests/cond-eof.exp9
-rw-r--r--unit-tests/cond-eof.mk20
-rw-r--r--unit-tests/cond-func-empty.mk27
-rw-r--r--unit-tests/cond-func-exists.mk11
-rw-r--r--unit-tests/cond-func-make-main.exp3
-rw-r--r--unit-tests/cond-func-make-main.mk62
-rw-r--r--unit-tests/cond-short.exp12
-rw-r--r--unit-tests/cond-short.mk64
-rw-r--r--unit-tests/cond-token-string.exp2
-rw-r--r--unit-tests/dep-percent.exp5
-rw-r--r--unit-tests/depsrc-meta.exp4
-rw-r--r--unit-tests/depsrc-meta.mk27
-rw-r--r--unit-tests/depsrc-optional.exp18
-rw-r--r--unit-tests/depsrc.exp3
-rw-r--r--unit-tests/depsrc.mk17
-rw-r--r--unit-tests/deptgt-begin-fail-indirect.exp6
-rw-r--r--unit-tests/deptgt-begin-fail-indirect.mk16
-rw-r--r--unit-tests/deptgt-begin-fail.exp6
-rw-r--r--unit-tests/deptgt-begin-fail.mk10
-rw-r--r--unit-tests/deptgt-end-fail-all.exp7
-rw-r--r--unit-tests/deptgt-end-fail-all.mk19
-rw-r--r--unit-tests/deptgt-end-fail-indirect.exp7
-rw-r--r--unit-tests/deptgt-end-fail-indirect.mk16
-rw-r--r--unit-tests/deptgt-end-fail.exp163
-rw-r--r--unit-tests/deptgt-end-fail.mk69
-rw-r--r--unit-tests/deptgt-suffixes.exp26
-rw-r--r--unit-tests/deptgt-suffixes.mk23
-rw-r--r--unit-tests/deptgt.exp1
-rw-r--r--unit-tests/deptgt.mk10
-rw-r--r--unit-tests/directive-elif.exp32
-rw-r--r--unit-tests/directive-elif.mk126
-rw-r--r--unit-tests/directive-else.exp16
-rw-r--r--unit-tests/directive-else.mk15
-rw-r--r--unit-tests/directive-endfor.exp4
-rw-r--r--unit-tests/directive-endfor.mk9
-rw-r--r--unit-tests/directive-endif.exp9
-rw-r--r--unit-tests/directive-endif.mk28
-rw-r--r--unit-tests/directive-error.mk6
-rw-r--r--unit-tests/directive-export-env.mk4
-rw-r--r--unit-tests/directive-export-impl.exp56
-rw-r--r--unit-tests/directive-export-impl.mk62
-rw-r--r--unit-tests/directive-export-literal.mk4
-rw-r--r--unit-tests/directive-export.exp5
-rw-r--r--unit-tests/directive-export.mk16
-rw-r--r--unit-tests/directive-for-errors.exp22
-rw-r--r--unit-tests/directive-for-errors.mk75
-rw-r--r--unit-tests/directive-for-escape.exp74
-rw-r--r--unit-tests/directive-for-escape.mk96
-rw-r--r--unit-tests/directive-for-lines.exp10
-rw-r--r--unit-tests/directive-for-lines.mk32
-rw-r--r--unit-tests/directive-for-null.exp10
-rw-r--r--unit-tests/directive-for-null.mk19
-rwxr-xr-xunit-tests/directive-for.exp7
-rwxr-xr-xunit-tests/directive-for.mk9
-rw-r--r--unit-tests/directive-if.exp6
-rw-r--r--unit-tests/directive-if.mk14
-rwxr-xr-xunit-tests/directive-include.mk5
-rw-r--r--unit-tests/directive-info.exp23
-rw-r--r--unit-tests/directive-info.mk18
-rw-r--r--unit-tests/directive-misspellings.exp45
-rw-r--r--unit-tests/directive-misspellings.mk79
-rw-r--r--unit-tests/directive-undef.exp3
-rw-r--r--unit-tests/directive-undef.mk85
-rw-r--r--unit-tests/directive-unexport-env.exp19
-rw-r--r--unit-tests/directive-unexport-env.mk19
-rw-r--r--unit-tests/directive-unexport.exp13
-rw-r--r--unit-tests/directive-unexport.mk12
-rw-r--r--unit-tests/directive-warning.exp16
-rw-r--r--unit-tests/directive-warning.mk10
-rw-r--r--unit-tests/jobs-error-indirect.exp8
-rw-r--r--unit-tests/jobs-error-indirect.mk21
-rw-r--r--unit-tests/jobs-error-nested-make.exp11
-rw-r--r--unit-tests/jobs-error-nested-make.mk20
-rw-r--r--unit-tests/jobs-error-nested.exp15
-rw-r--r--unit-tests/jobs-error-nested.mk20
-rwxr-xr-xunit-tests/make-exported.mk2
-rw-r--r--unit-tests/meta-cmd-cmp.exp37
-rw-r--r--unit-tests/meta-cmd-cmp.mk52
-rw-r--r--unit-tests/modmisc.exp1
-rw-r--r--unit-tests/modmisc.mk29
-rw-r--r--unit-tests/opt-chdir.exp4
-rw-r--r--unit-tests/opt-debug-errors.exp5
-rw-r--r--unit-tests/opt-debug-graph1.exp36
-rw-r--r--unit-tests/opt-debug-jobs.exp4
-rw-r--r--unit-tests/opt-debug-lint.exp3
-rw-r--r--unit-tests/opt-debug-lint.mk15
-rw-r--r--unit-tests/opt-file.exp13
-rw-r--r--unit-tests/opt-file.mk101
-rw-r--r--unit-tests/opt-jobs-no-action.exp61
-rw-r--r--unit-tests/opt-jobs-no-action.mk102
-rw-r--r--unit-tests/opt-keep-going-multiple.exp9
-rw-r--r--unit-tests/opt-keep-going-multiple.mk21
-rw-r--r--unit-tests/opt-keep-going.exp5
-rw-r--r--unit-tests/opt-keep-going.mk6
-rw-r--r--unit-tests/opt-no-action-runflags.exp34
-rw-r--r--unit-tests/opt-no-action-runflags.mk32
-rw-r--r--unit-tests/opt.exp4
-rw-r--r--unit-tests/posix.exp5
-rw-r--r--unit-tests/qequals.exp2
-rw-r--r--unit-tests/qequals.mk8
-rwxr-xr-xunit-tests/sh-dots.exp12
-rw-r--r--unit-tests/sh-errctl.exp27
-rw-r--r--unit-tests/sh-errctl.mk26
-rw-r--r--unit-tests/sh-flags.exp4325
-rw-r--r--unit-tests/sh-flags.mk138
-rw-r--r--unit-tests/sh-jobs.exp7
-rw-r--r--unit-tests/sh-jobs.mk34
-rw-r--r--unit-tests/sh-meta-chars.mk17
-rw-r--r--unit-tests/shell-csh.mk6
-rw-r--r--unit-tests/suff-add-later.exp8
-rw-r--r--unit-tests/suff-clear-regular.exp5
-rw-r--r--unit-tests/suff-clear-regular.mk3
-rw-r--r--unit-tests/suff-clear-single.exp5
-rw-r--r--unit-tests/suff-incomplete.exp42
-rw-r--r--unit-tests/suff-incomplete.mk31
-rw-r--r--unit-tests/suff-lookup.exp21
-rw-r--r--unit-tests/suff-main-several.exp141
-rw-r--r--unit-tests/suff-main-several.mk42
-rw-r--r--unit-tests/suff-phony.exp13
-rw-r--r--unit-tests/suff-phony.mk21
-rw-r--r--unit-tests/suff-rebuild.exp76
-rw-r--r--unit-tests/suff-rebuild.mk23
-rw-r--r--unit-tests/suff-self.exp5
-rw-r--r--unit-tests/suff-transform-debug.exp62
-rw-r--r--unit-tests/suff-transform-debug.mk12
-rw-r--r--unit-tests/suff-transform-endless.exp44
-rw-r--r--unit-tests/suff-transform-endless.mk9
-rw-r--r--unit-tests/suff-transform-expand.exp5
-rw-r--r--unit-tests/suff-transform-select.exp45
-rw-r--r--unit-tests/suff-transform-select.mk7
-rw-r--r--unit-tests/use-inference.exp5
-rw-r--r--unit-tests/use-inference.mk6
-rw-r--r--unit-tests/var-op-default.mk72
-rw-r--r--unit-tests/var-op-expand.exp9
-rw-r--r--unit-tests/var-op-expand.mk191
-rw-r--r--unit-tests/vardebug.exp2
-rw-r--r--unit-tests/varmisc.mk14
-rw-r--r--unit-tests/varmod-defined.exp18
-rw-r--r--unit-tests/varmod-edge.exp41
-rw-r--r--unit-tests/varmod-extension.exp1
-rw-r--r--unit-tests/varmod-extension.mk4
-rw-r--r--unit-tests/varmod-gmtime.exp40
-rw-r--r--unit-tests/varmod-gmtime.mk197
-rw-r--r--unit-tests/varmod-head.exp1
-rw-r--r--unit-tests/varmod-head.mk4
-rw-r--r--unit-tests/varmod-ifelse.exp4
-rw-r--r--unit-tests/varmod-ifelse.mk20
-rw-r--r--unit-tests/varmod-indirect.exp59
-rw-r--r--unit-tests/varmod-indirect.mk157
-rw-r--r--unit-tests/varmod-localtime.exp40
-rw-r--r--unit-tests/varmod-localtime.mk196
-rw-r--r--unit-tests/varmod-range.exp9
-rw-r--r--unit-tests/varmod-root.exp1
-rw-r--r--unit-tests/varmod-root.mk4
-rw-r--r--unit-tests/varmod-subst-regex.exp2
-rw-r--r--unit-tests/varmod-subst-regex.mk7
-rw-r--r--unit-tests/varmod-sysv.exp4
-rw-r--r--unit-tests/varmod-sysv.mk16
-rw-r--r--unit-tests/varmod-tail.exp1
-rw-r--r--unit-tests/varmod-tail.mk4
-rw-r--r--unit-tests/varmod-to-many-words.mk16
-rw-r--r--unit-tests/varmod-to-one-word.mk16
-rw-r--r--unit-tests/varmod-to-separator.exp2
-rw-r--r--unit-tests/varmod.exp2
-rw-r--r--unit-tests/varmod.mk4
-rw-r--r--unit-tests/varname-dot-makeflags.exp3
-rw-r--r--unit-tests/varname-dot-makeflags.mk15
-rwxr-xr-xunit-tests/varname-dot-shell.exp2
-rw-r--r--unit-tests/varname-make_print_var_on_error-jobs.exp5
-rw-r--r--unit-tests/varname-make_print_var_on_error-jobs.mk15
-rw-r--r--unit-tests/varname-make_print_var_on_error.exp3
-rw-r--r--unit-tests/varname-make_print_var_on_error.mk15
-rw-r--r--unit-tests/varname-makeflags.mk24
-rw-r--r--unit-tests/varparse-dynamic.mk12
-rw-r--r--unit-tests/varparse-errors.exp6
-rw-r--r--unit-tests/varparse-errors.mk18
183 files changed, 8568 insertions, 651 deletions
diff --git a/unit-tests/Makefile b/unit-tests/Makefile
index cca63155e868..87ed4ef212d4 100644
--- a/unit-tests/Makefile
+++ b/unit-tests/Makefile
@@ -1,6 +1,6 @@
-# $Id: Makefile,v 1.115 2020/11/18 04:01:07 sjg Exp $
+# $Id: Makefile,v 1.138 2021/01/01 22:55:09 sjg Exp $
#
-# $NetBSD: Makefile,v 1.206 2020/11/18 01:12:00 sjg Exp $
+# $NetBSD: Makefile,v 1.260 2020/12/31 03:05:12 rillig Exp $
#
# Unit tests for make(1)
#
@@ -30,18 +30,24 @@
# src/tests/usr.bin/make/t_make.sh.
#
+# we use these below but we might be an older make
+.MAKE.OS?= ${uname -s:L:sh}
+.MAKE.UID?= ${id -u:L:sh}
+
# Each test is in a sub-makefile.
# Keep the list sorted.
# Any test that is commented out must be ignored in
# src/tests/usr.bin/make/t_make.sh as well.
#TESTS+= archive
-TESTS+= archive-suffix
+#TESTS+= archive-suffix
TESTS+= cmd-errors
+TESTS+= cmd-errors-jobs
TESTS+= cmd-errors-lint
TESTS+= cmd-interrupt
TESTS+= cmdline
TESTS+= cmdline-undefined
TESTS+= comment
+TESTS+= compat-error
TESTS+= cond-cmp-numeric
TESTS+= cond-cmp-numeric-eq
TESTS+= cond-cmp-numeric-ge
@@ -51,12 +57,14 @@ TESTS+= cond-cmp-numeric-lt
TESTS+= cond-cmp-numeric-ne
TESTS+= cond-cmp-string
TESTS+= cond-cmp-unary
+TESTS+= cond-eof
TESTS+= cond-func
TESTS+= cond-func-commands
TESTS+= cond-func-defined
TESTS+= cond-func-empty
TESTS+= cond-func-exists
TESTS+= cond-func-make
+TESTS+= cond-func-make-main
TESTS+= cond-func-target
TESTS+= cond-late
TESTS+= cond-op
@@ -107,9 +115,14 @@ TESTS+= depsrc-usebefore-double-colon
TESTS+= depsrc-wait
TESTS+= deptgt
TESTS+= deptgt-begin
+TESTS+= deptgt-begin-fail
+TESTS+= deptgt-begin-fail-indirect
TESTS+= deptgt-default
TESTS+= deptgt-delete_on_error
TESTS+= deptgt-end
+TESTS+= deptgt-end-fail
+TESTS+= deptgt-end-fail-all
+TESTS+= deptgt-end-fail-indirect
TESTS+= deptgt-end-jobs
TESTS+= deptgt-error
TESTS+= deptgt-ignore
@@ -139,14 +152,20 @@ TESTS+= directive-elifmake
TESTS+= directive-elifndef
TESTS+= directive-elifnmake
TESTS+= directive-else
+TESTS+= directive-endfor
TESTS+= directive-endif
TESTS+= directive-error
TESTS+= directive-export
TESTS+= directive-export-env
+TESTS+= directive-export-impl
TESTS+= directive-export-gmake
TESTS+= directive-export-literal
TESTS+= directive-for
+TESTS+= directive-for-errors
+TESTS+= directive-for-escape
TESTS+= directive-for-generating-endif
+TESTS+= directive-for-lines
+TESTS+= directive-for-null
TESTS+= directive-hyphen-include
TESTS+= directive-if
TESTS+= directive-if-nested
@@ -157,6 +176,7 @@ TESTS+= directive-ifnmake
TESTS+= directive-include
TESTS+= directive-include-fatal
TESTS+= directive-info
+TESTS+= directive-misspellings
TESTS+= directive-sinclude
TESTS+= directive-undef
TESTS+= directive-unexport
@@ -180,14 +200,20 @@ TESTS+= impsrc
TESTS+= include-main
TESTS+= job-flags
#TESTS+= job-output-long-lines
+TESTS+= jobs-error-indirect
+TESTS+= jobs-error-nested
+TESTS+= jobs-error-nested-make
TESTS+= lint
TESTS+= make-exported
+TESTS+= meta-cmd-cmp
TESTS+= moderrs
TESTS+= modmatch
TESTS+= modmisc
TESTS+= modts
TESTS+= modword
+.if ${.MAKE.UID} > 0
TESTS+= objdir-writable
+.endif
TESTS+= opt
TESTS+= opt-backwards
TESTS+= opt-chdir
@@ -223,10 +249,13 @@ TESTS+= opt-ignore
TESTS+= opt-include-dir
TESTS+= opt-jobs
TESTS+= opt-jobs-internal
+TESTS+= opt-jobs-no-action
TESTS+= opt-keep-going
+TESTS+= opt-keep-going-multiple
TESTS+= opt-m-include-dir
TESTS+= opt-no-action
TESTS+= opt-no-action-at-all
+TESTS+= opt-no-action-runflags
TESTS+= opt-query
TESTS+= opt-raw
TESTS+= opt-silent
@@ -243,10 +272,11 @@ TESTS+= parse-var
TESTS+= phony-end
TESTS+= posix
TESTS+= # posix1 # broken by reverting POSIX changes
-TESTS+= qequals
TESTS+= recursive
TESTS+= sh
TESTS+= sh-dots
+TESTS+= sh-errctl
+TESTS+= sh-flags
TESTS+= sh-jobs
TESTS+= sh-jobs-error
TESTS+= sh-leading-at
@@ -264,10 +294,14 @@ TESTS+= shell-sh
TESTS+= suff-add-later
TESTS+= suff-clear-regular
TESTS+= suff-clear-single
+TESTS+= suff-incomplete
TESTS+= suff-lookup
TESTS+= suff-main
+TESTS+= suff-main-several
+TESTS+= suff-phony
TESTS+= suff-rebuild
TESTS+= suff-self
+TESTS+= suff-transform-debug
TESTS+= suff-transform-endless
TESTS+= suff-transform-expand
TESTS+= suff-transform-select
@@ -304,6 +338,7 @@ TESTS+= varmod-gmtime
TESTS+= varmod-hash
TESTS+= varmod-head
TESTS+= varmod-ifelse
+TESTS+= varmod-indirect
TESTS+= varmod-l-name-to-value
TESTS+= varmod-localtime
TESTS+= varmod-loop
@@ -361,6 +396,7 @@ TESTS+= varname-dot-make-path_filemon
TESTS+= varname-dot-make-pid
TESTS+= varname-dot-make-ppid
TESTS+= varname-dot-make-save_dollars
+TESTS+= varname-dot-makeflags
TESTS+= varname-dot-makeoverrides
TESTS+= varname-dot-newline
TESTS+= varname-dot-objdir
@@ -422,21 +458,22 @@ 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, especially not -k
-FLAGS.varname-empty= -dv '$${:U}=cmdline-u' '=cmdline-plain'
+FLAGS.cond-func-make= via-cmdline
+FLAGS.directive-ifmake= first second
+FLAGS.doterror= # none, especially not -k
+FLAGS.jobs-error-indirect= # none, especially not -k
+FLAGS.jobs-error-nested= # none, especially not -k
+FLAGS.jobs-error-nested-make= # none, especially not -k
+FLAGS.varname-empty= -dv '$${:U}=cmdline-u' '=cmdline-plain'
# Some tests need extra postprocessing.
-SED_CMDS.export= \
- -e '/^[^=_A-Za-z0-9]*=/d'
-# these all share the same requirement
-.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.dir= ${:D remove output from -DCLEANUP mode }
+SED_CMDS.dir+= -e '/^OpenDirs_Done:/d'
+SED_CMDS.dir+= -e '/^CachedDir /d'
+SED_CMDS.export= -e '/^[^=_A-Za-z0-9]*=/d'
+SED_CMDS.export-all= ${SED_CMDS.export}
+SED_CMDS.export-env= ${SED_CMDS.export}
+SED_CMDS.cmdline= -e 's,uid${.MAKE.UID}/,,'
SED_CMDS.job-output-long-lines= \
${:D Job separators on their own line are ok. } \
-e '/^--- job-[ab] ---$$/d' \
@@ -448,26 +485,34 @@ 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+= \
- -e '/Global Variables:/,/Suffixes:/d'
-SED_CMDS.sh-dots= -e 's,^.*\.\.\.:.*,<normalized: ...: not found>,'
+SED_CMDS.objdir-writable= -e 's,${RO_OBJDIR},OBJDIR/roobj,g'
+SED_CMDS.opt-debug-graph1= ${STD_SED_CMDS.dg1}
SED_CMDS.opt-debug-jobs= -e 's,([0-9][0-9]*),(<pid>),'
SED_CMDS.opt-debug-jobs+= -e 's,pid [0-9][0-9]*,pid <pid>,'
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>,'
+SED_CMDS.opt-debug-jobs+= -e 's,Command: ${.SHELL:T},Command: <shell>,'
# The "-q" may be there or not, see jobs.c, variable shells.
-SED_CMDS.opt-debug-jobs+= -e 's,^\(.Command: 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.opt-debug-jobs+= -e 's,^\(.Command: <shell>\) -q,\1,'
+SED_CMDS.opt-jobs-no-action= ${STD_SED_CMDS.hide-from-output}
+SED_CMDS.opt-no-action-runflags= ${STD_SED_CMDS.hide-from-output}
+# For Compat_RunCommand, useShell == FALSE.
+SED_CMDS.sh-dots= -e 's,^.*\.\.\.:.*,<not found: ...>,'
+# For Compat_RunCommand, useShell == TRUE.
+SED_CMDS.sh-dots+= -e 's,^make: exec(\(.*\)) failed (.*)$$,<not found: \1>,'
+SED_CMDS.sh-dots+= -e 's,^\(\*\*\* Error code \)[1-9][0-9]*,\1<nonzero>,'
+SED_CMDS.sh-errctl= ${STD_SED_CMDS.dj}
+SED_CMDS.sh-flags= ${STD_SED_CMDS.hide-from-output}
+SED_CMDS.suff-main+= ${STD_SED_CMDS.dg1}
+SED_CMDS.suff-main-several+= ${STD_SED_CMDS.dg1}
+SED_CMDS.suff-transform-debug+= ${STD_SED_CMDS.dg1}
+SED_CMDS.var-op-shell+= \
+ -e 's,^${.SHELL:T}: [ 0-9:]*,,' \
+ -e 's,^${.SHELL:T}: ,,' \
+ -e '/command/s,No such.*,not found,'
+SED_CMDS.vardebug+= -e 's,${.SHELL},</path/to/shell>,'
SED_CMDS.varmod-subst-regex+= \
-e 's,\(Regex compilation error:\).*,\1 (details omitted),'
-SED_CMDS.varmod-edge+= -e 's, line [0-9]*:, line omitted:,'
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'
@@ -475,10 +520,9 @@ SED_CMDS.varname-dot-shell+= -e 's,"/[^" ]*","(details omitted)",g'
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.gnode-submake= awk '/Input graph/, /^$$/'
-POSTPROC.varname-empty= ${TOOL_SED} -n -e '/^Var_Set/p' -e '/^out:/p'
+POSTPROC.deptgt-suffixes= awk '/^\#\*\*\* Suffixes/,/^never-stop/'
+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.
export-all.rawout: export.mk
@@ -487,6 +531,35 @@ unexport-env.rawout: export.mk
# End of the configuration section.
+# Some standard sed commands, to be used in the SED_CMDS above.
+
+# Omit details such as process IDs from the output of the -dg1 option.
+STD_SED_CMDS.dg1= -e 's,${.CURDIR}$$,<curdir>,'
+STD_SED_CMDS.dg1+= -e '/\.MAKE.PATH_FILEMON/d'
+STD_SED_CMDS.dg1+= -e '/^MAKE_VERSION/d;/^\#.*\/mk/d'
+STD_SED_CMDS.dg1+= -e 's, ${DEFSYSPATH:U/usr/share/mk}$$, <defsyspath>,'
+STD_SED_CMDS.dg1+= -e 's,^\(\.MAKE *=\) .*,\1 <details omitted>,'
+STD_SED_CMDS.dg1+= -e 's,^\(\.MAKE\.[A-Z_]* *=\) .*,\1 <details omitted>,'
+STD_SED_CMDS.dg1+= -e 's,^\(MACHINE[_ARCH]* *=\) .*,\1 <details omitted>,'
+STD_SED_CMDS.dg1+= -e 's,^\(MAKE *=\) .*,\1 <details omitted>,'
+
+# Omit details such as process IDs from the output of the -dj option.
+STD_SED_CMDS.dj= \
+ -e '/Process/d;/JobFinish:/d' \
+ -e 's,^\(Job_TokenWithdraw\)([0-9]*),\1(<pid>),' \
+ -e 's,^([0-9][0-9]*) \(withdrew token\),(<pid>) \1,' \
+ -e 's, \(pid\) [0-9][0-9]*, \1 <pid>,' \
+ -e 's,^\( Command:\) .*,\1 <shell>,'
+
+# Reduce the noise for tests running with the -n option, since there is no
+# other way to suppress the echoing of the commands.
+STD_SED_CMDS.hide-from-output= \
+ -e '/^echo hide-from-output/d' \
+ -e 's,hide-from-output ,,' \
+ -e 's,hide-from-output,,'
+
+# End of the configuration helpers section.
+
.MAIN: all
.-include "Makefile.inc"
@@ -532,6 +605,11 @@ _MKMSG_TEST= :
MAKE_TEST_ENV?= MALLOC_OPTIONS="JA" # for jemalloc
+.if ${.MAKE.OS} == "NetBSD"
+LIMIT_RESOURCES?= ulimit -v 200000
+.endif
+LIMIT_RESOURCES?= :
+
# Each test is run in a sub-make, to keep the tests for interfering with
# each other, and because they use different environment variables and
# command line options.
@@ -539,6 +617,7 @@ MAKE_TEST_ENV?= MALLOC_OPTIONS="JA" # for jemalloc
.mk.rawout:
@${_MKMSG_TEST:Uecho '# test '} ${.PREFIX}
@set -eu; \
+ ${LIMIT_RESOURCES}; \
cd ${.OBJDIR}; \
env -i PATH="$$PATH" ${MAKE_TEST_ENV} ${ENV.${.PREFIX:T}} \
${TEST_MAKE} \
@@ -561,6 +640,10 @@ _SED_CMDS+= -e '/stopped/s, /.*, unit-tests,'
# strip ${.CURDIR}/ from the output
_SED_CMDS+= -e 's,${.CURDIR:S,.,\\.,g}/,,g'
_SED_CMDS+= -e 's,${UNIT_TESTS:S,.,\\.,g}/,,g'
+# on AT&T derrived systems; false exits 255 not 1
+.if ${.MAKE.OS:N*BSD} != ""
+_SED_CMDS+= -e 's,\(Error code\) 255,\1 1,'
+.endif
.rawout.out:
@${TOOL_SED} ${_SED_CMDS} ${SED_CMDS.${.PREFIX:T}} \
@@ -570,10 +653,18 @@ _SED_CMDS+= -e 's,${UNIT_TESTS:S,.,\\.,g}/,,g'
@echo "exit status `cat ${.TARGET:R}.status`" >> ${.TARGET}.tmp2
@mv ${.TARGET}.tmp2 ${.TARGET}
+.if empty(DIFF_FLAGS)
+DIFF_ECHO= echo
+.else
+DIFF_ECHO= :
+.endif
+
# Compare all output files
test: ${OUTFILES} .PHONY
@failed= ; \
for test in ${TESTS}; do \
+ cmp -s ${UNIT_TESTS}/$${test}.exp $${test}.out && continue || \
+ ${DIFF_ECHO} diff ${UNIT_TESTS}/$${test}.exp $${test}.out; \
${TOOL_DIFF} ${DIFF_FLAGS} ${UNIT_TESTS}/$${test}.exp $${test}.out \
|| failed="$${failed}$${failed:+ }$${test}" ; \
done ; \
diff --git a/unit-tests/cmd-errors-jobs.exp b/unit-tests/cmd-errors-jobs.exp
new file mode 100644
index 000000000000..6d9c6bb7f890
--- /dev/null
+++ b/unit-tests/cmd-errors-jobs.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-jobs.mk b/unit-tests/cmd-errors-jobs.mk
new file mode 100644
index 000000000000..8462a2e3497e
--- /dev/null
+++ b/unit-tests/cmd-errors-jobs.mk
@@ -0,0 +1,32 @@
+# $NetBSD: cmd-errors-jobs.mk,v 1.1 2020/12/27 05:11:40 rillig Exp $
+#
+# Demonstrate how errors in variable expansions affect whether the commands
+# are actually executed in jobs mode.
+
+.MAKEFLAGS: -j1
+
+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-errors.mk b/unit-tests/cmd-errors.mk
index 5ad4be311873..356fe1a3e4a2 100644
--- a/unit-tests/cmd-errors.mk
+++ b/unit-tests/cmd-errors.mk
@@ -1,7 +1,7 @@
-# $NetBSD: cmd-errors.mk,v 1.3 2020/11/09 23:36:34 rillig Exp $
+# $NetBSD: cmd-errors.mk,v 1.4 2020/12/27 05:11:40 rillig Exp $
#
# Demonstrate how errors in variable expansions affect whether the commands
-# are actually executed.
+# are actually executed in compat mode.
all: undefined unclosed-variable unclosed-modifier unknown-modifier end
diff --git a/unit-tests/cmdline.mk b/unit-tests/cmdline.mk
index cd88cead4558..a3e2de984927 100644
--- a/unit-tests/cmdline.mk
+++ b/unit-tests/cmdline.mk
@@ -2,7 +2,7 @@
#
# Tests for command line parsing and related special variables.
-TMPBASE?= /tmp
+TMPBASE?= /tmp/uid${.MAKE.UID}
SUB1= a7b41170-53f8-4cc2-bc5c-e4c3dd93ec45 # just a random UUID
SUB2= 6a8899d2-d227-4b55-9b6b-f3c8eeb83fd5 # just a random UUID
MAKE_CMD= env TMPBASE=${TMPBASE}/${SUB1} ${.MAKE} -f ${MAKEFILE} -r
diff --git a/unit-tests/compat-error.exp b/unit-tests/compat-error.exp
new file mode 100644
index 000000000000..256cb6d4361c
--- /dev/null
+++ b/unit-tests/compat-error.exp
@@ -0,0 +1,15 @@
+: Making success1 out of nothing.
+: Making fail1 out of nothing.
+false 'fail1' '${.TARGET}' '$${.TARGET}'
+*** Error code 1 (continuing)
+: Making success2 out of nothing.
+: Making fail2 out of nothing.
+false 'fail2' '${.TARGET}' '$${.TARGET}'
+*** Error code 1 (continuing)
+: Making success3 out of nothing.
+
+Stop.
+make: stopped in unit-tests
+.ERROR target: <fail1>
+.ERROR command: <>
+exit status 1
diff --git a/unit-tests/compat-error.mk b/unit-tests/compat-error.mk
new file mode 100644
index 000000000000..4cbc48d4b6bb
--- /dev/null
+++ b/unit-tests/compat-error.mk
@@ -0,0 +1,37 @@
+# $NetBSD: compat-error.mk,v 1.3 2020/12/13 19:33:53 rillig Exp $
+#
+# Test detailed error handling in compat mode.
+#
+# Until 2020-12-13, .ERROR_TARGET was success3, which was wrong.
+# Since compat.c 1.215 from 2020-12-13, it is 'fail1', which is the first
+# failed top-level target. XXX: Even better would be if .ERROR_TARGET were
+# the smallest target that caused the build to fail, even if it were a
+# sub-sub-sub-dependency of a top-level target.
+#
+# XXX: As of 2020-12-13, .ERROR_CMD is empty, which is wrong.
+#
+# See also:
+# Compat_Run
+#
+# The commit that added the NULL command to gn->commands:
+# CVS: 1994.06.06.22.45.??
+# Git: 26a8972fd7f982502c5fbfdabd34578b99d77ca5
+# 1994: Lst_Replace (cmdNode, (ClientData) NULL);
+# 2020: LstNode_SetNull(cmdNode);
+#
+# The commit that skipped NULL commands for .ERROR_CMD:
+# CVS: 2016.08.11.19.53.??
+# Git: 58b23478b7353d46457089e726b07a49197388e4
+
+.MAKEFLAGS: success1 fail1 success2 fail2 success3
+
+success1 success2 success3:
+ : Making ${.TARGET} out of nothing.
+
+fail1 fail2:
+ : Making ${.TARGET} out of nothing.
+ false '${.TARGET}' '$${.TARGET}' '$$$${.TARGET}'
+
+.ERROR:
+ @echo ${.TARGET} target: '<'${.ERROR_TARGET:Q}'>'
+ @echo ${.TARGET} command: '<'${.ERROR_CMD:Q}'>'
diff --git a/unit-tests/cond-eof.exp b/unit-tests/cond-eof.exp
new file mode 100644
index 000000000000..3b1e6eb1f056
--- /dev/null
+++ b/unit-tests/cond-eof.exp
@@ -0,0 +1,9 @@
+side effect
+make: "cond-eof.mk" line 15: Malformed conditional (0 ${SIDE_EFFECT} ${SIDE_EFFECT2})
+side effect
+make: "cond-eof.mk" line 17: Malformed conditional (1 ${SIDE_EFFECT} ${SIDE_EFFECT2})
+side effect
+make: "cond-eof.mk" line 19: Malformed conditional ((0) ${SIDE_EFFECT} ${SIDE_EFFECT2})
+make: Fatal errors encountered -- cannot continue
+make: stopped in unit-tests
+exit status 1
diff --git a/unit-tests/cond-eof.mk b/unit-tests/cond-eof.mk
new file mode 100644
index 000000000000..08f432bc4593
--- /dev/null
+++ b/unit-tests/cond-eof.mk
@@ -0,0 +1,20 @@
+# $NetBSD: cond-eof.mk,v 1.2 2020/12/14 20:28:09 rillig Exp $
+#
+# Tests for parsing conditions, especially the end of such conditions, which
+# are represented as the token TOK_EOF.
+
+SIDE_EFFECT= ${:!echo 'side effect' 1>&2!}
+SIDE_EFFECT2= ${:!echo 'side effect 2' 1>&2!}
+
+# In the following conditions, ${SIDE_EFFECT} is the position of the first
+# parse error. It is always fully evaluated, even if it were not necessary
+# to expand the variable expression. This is because these syntax errors are
+# an edge case that does not occur during normal operation, therefore there
+# is no need to optimize for this case, and it would slow down the common
+# case as well.
+.if 0 ${SIDE_EFFECT} ${SIDE_EFFECT2}
+.endif
+.if 1 ${SIDE_EFFECT} ${SIDE_EFFECT2}
+.endif
+.if (0) ${SIDE_EFFECT} ${SIDE_EFFECT2}
+.endif
diff --git a/unit-tests/cond-func-empty.mk b/unit-tests/cond-func-empty.mk
index f43d99bf92c5..5094924f1c8d 100644
--- a/unit-tests/cond-func-empty.mk
+++ b/unit-tests/cond-func-empty.mk
@@ -1,4 +1,4 @@
-# $NetBSD: cond-func-empty.mk,v 1.10 2020/11/15 14:07:53 rillig Exp $
+# $NetBSD: cond-func-empty.mk,v 1.11 2020/11/28 14:08:37 rillig Exp $
#
# Tests for the empty() function in .if conditions, which tests a variable
# expression for emptiness.
@@ -155,5 +155,30 @@ ${:U WORD }= variable name with spaces
. error
.endif
+# Between 2020-06-28 and var.c 1.226 from 2020-07-02, this paragraph generated
+# a wrong error message "Variable VARNAME is recursive".
+#
+# The bug was that the !empty() condition was evaluated, even though this was
+# not necessary since the defined() condition already evaluated to false.
+#
+# When evaluating the !empty condition, the variable name was parsed as
+# "VARNAME${:U2}", but without expanding any nested variable expression, in
+# this case the ${:U2}. Therefore, the variable name came out as simply
+# "VARNAME". Since this variable name should have been discarded quickly after
+# parsing it, this unrealistic variable name should have done no harm.
+#
+# The variable expression was expanded though, and this was wrong. The
+# expansion was done without the VARE_WANTRES flag (called VARF_WANTRES back
+# then) though. This had the effect that the ${:U1} from the value of VARNAME
+# expanded to an empty string. This in turn created the seemingly recursive
+# definition VARNAME=${VARNAME}, and that definition was never meant to be
+# expanded.
+#
+# This was fixed by expanding nested variable expressions in the variable name
+# only if the flag VARE_WANTRES is given.
+VARNAME= ${VARNAME${:U1}}
+.if defined(VARNAME${:U2}) && !empty(VARNAME${:U2})
+.endif
+
all:
@:;
diff --git a/unit-tests/cond-func-exists.mk b/unit-tests/cond-func-exists.mk
index 4a80fb1c393d..48d7e727dc3f 100644
--- a/unit-tests/cond-func-exists.mk
+++ b/unit-tests/cond-func-exists.mk
@@ -1,4 +1,4 @@
-# $NetBSD: cond-func-exists.mk,v 1.5 2020/10/24 08:46:08 rillig Exp $
+# $NetBSD: cond-func-exists.mk,v 1.6 2020/11/30 20:12:29 rillig Exp $
#
# Tests for the exists() function in .if conditions.
@@ -38,5 +38,14 @@
. error
.endif
+# The exists function does not really look up the file in the file system,
+# instead it uses a cache that is preloaded very early, before parsing the
+# first makefile. At that time, the file did not exist yet.
+_!= > cond-func-exists.just-created
+.if exists(cond-func-exists.just-created)
+. error
+.endif
+_!= rm cond-func-exists.just-created
+
all:
@:;
diff --git a/unit-tests/cond-func-make-main.exp b/unit-tests/cond-func-make-main.exp
new file mode 100644
index 000000000000..3266459f3bea
--- /dev/null
+++ b/unit-tests/cond-func-make-main.exp
@@ -0,0 +1,3 @@
+: Making dot-main-target-1a.
+: Making dot-main-target-1b.
+exit status 0
diff --git a/unit-tests/cond-func-make-main.mk b/unit-tests/cond-func-make-main.mk
new file mode 100644
index 000000000000..31b370afabde
--- /dev/null
+++ b/unit-tests/cond-func-make-main.mk
@@ -0,0 +1,62 @@
+# $NetBSD: cond-func-make-main.mk,v 1.1 2020/11/22 19:37:27 rillig Exp $
+#
+# Test how accurately the make() function in .if conditions reflects
+# what is actually made.
+#
+# There are several ways to specify what is being made:
+#
+# 1. The default main target is the first target in the given makefiles that
+# is not one of the special targets. For example, .PHONY is special when
+# it appears on the left-hand side of the ':'. It is not special on the
+# right-hand side though.
+#
+# 2. Command line arguments that are neither options (-ds or -k) nor variable
+# assignments (VAR=value) are interpreted as targets to be made. These
+# override the default main target from above.
+#
+# 3. All sources of the first '.MAIN: sources' line. Any further .MAIN line
+# is treated as if .MAIN were a regular name.
+#
+# This test only covers items 1 and 3. For item 2, see cond-func-make.mk.
+
+first-main-target:
+ : Making ${.TARGET}.
+
+# Even though the main-target would actually be made at this point, it is
+# ignored by the make() function.
+.if make(first-main-target)
+. error
+.endif
+
+# Declaring a target via the .MAIN dependency adds it to the targets to be
+# created (opts.create), but only that list was empty at the beginning of
+# the line. This implies that several main targets can be set at the name
+# time, but they have to be in the same dependency group.
+#
+# See ParseDoDependencyTargetSpecial, branch SP_MAIN.
+.MAIN: dot-main-target-1a dot-main-target-1b
+
+.if !make(dot-main-target-1a)
+. error
+.endif
+.if !make(dot-main-target-1b)
+. error
+.endif
+
+dot-main-target-{1,2}{a,b}:
+ : Making ${.TARGET}.
+
+# At this point, the list of targets to be made (opts.create) is not empty
+# anymore. ParseDoDependencyTargetSpecial therefore treats the .MAIN as if
+# it were an ordinary target. Since .MAIN is not listed as a dependency
+# anywhere, it is not made.
+.if target(.MAIN)
+. error
+.endif
+.MAIN: dot-main-target-2a dot-main-target-2b
+.if !target(.MAIN)
+. error
+.endif
+.if make(dot-main-target-2a)
+. error
+.endif
diff --git a/unit-tests/cond-short.exp b/unit-tests/cond-short.exp
index fdc38d98a8b7..2865dcb6ef33 100644
--- a/unit-tests/cond-short.exp
+++ b/unit-tests/cond-short.exp
@@ -7,10 +7,10 @@ expected M pattern
expected or
expected or exists
expected or empty
-defined(V42) && 42 > 0: Ok
-defined(V66) && ( "" < 42 ): Ok
-1 || 42 < 42: Ok
-1 || < 42: Ok
-0 || 42 <= 42: Ok
-0 || < 42: Ok
+defined(V42) && ${V42} > 0: Ok
+defined(V66) && ( "${iV2}" < ${V42} ): Ok
+1 || ${iV1} < ${V42}: Ok
+1 || ${iV2:U2} < ${V42}: Ok
+0 || ${iV1} <= ${V42}: Ok
+0 || ${iV2:U2} < ${V42}: Ok
exit status 0
diff --git a/unit-tests/cond-short.mk b/unit-tests/cond-short.mk
index 077684be33fc..46c7ea26a97b 100644
--- a/unit-tests/cond-short.mk
+++ b/unit-tests/cond-short.mk
@@ -1,4 +1,4 @@
-# $NetBSD: cond-short.mk,v 1.12 2020/11/15 14:58:14 rillig Exp $
+# $NetBSD: cond-short.mk,v 1.15 2020/12/01 19:37:23 rillig Exp $
#
# Demonstrates that in conditions, the right-hand side of an && or ||
# is only evaluated if it can actually influence the result.
@@ -6,9 +6,13 @@
# 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?
+# Before 2020-06-28, the right-hand side of an && or || operator was always
+# evaluated, which was wrong. In cond.c 1.69 and var.c 1.197 on 2015-10-11,
+# Var_Parse got a new parameter named 'wantit'. Since then it would have been
+# possible to skip evaluation of irrelevant variable expressions and only
+# parse them. They were still evaluated though, the only difference to
+# relevant variable expressions was that in the irrelevant variable
+# expressions, undefined variables were allowed.
# The && operator.
@@ -128,33 +132,56 @@ x= Ok
.else
x= Fail
.endif
-x!= echo 'defined(V42) && ${V42} > 0: $x' >&2; echo
+x!= echo 'defined(V42) && $${V42} > 0: $x' >&2; echo
-# this one throws both String comparison operator and
-# Malformed conditional with cond.c 1.78
-# indirect iV2 would expand to "" and treated as 0
+# With cond.c 1.76 from 2020-07-03, the following condition triggered a
+# warning: "String comparison operator should be either == or !=".
+# This was because the variable expression ${iV2} was defined, but the
+# contained variable V66 was undefined. The left-hand side of the comparison
+# therefore evaluated to the string "${V66}", which is obviously not a number.
+#
+# This was fixed in cond.c 1.79 from 2020-07-09 by not evaluating irrelevant
+# comparisons. Instead, they are only parsed and then discarded.
+#
+# At that time, there was not enough debug logging to see the details in the
+# -dA log. To actually see it, add debug logging at the beginning and end of
+# Var_Parse.
.if defined(V66) && ( ${iV2} < ${V42} )
x= Fail
.else
x= Ok
.endif
-x!= echo 'defined(V66) && ( "${iV2}" < ${V42} ): $x' >&2; echo
+# XXX: This condition doesn't match the one above. The quotes are missing
+# above. This is a crucial detail since without quotes, the variable
+# expression ${iV2} evaluates to "${V66}", and with quotes, it evaluates to ""
+# since undefined variables are allowed and expand to an empty string.
+x!= echo 'defined(V66) && ( "$${iV2}" < $${V42} ): $x' >&2; echo
-# next two thow String comparison operator with cond.c 1.78
-# indirect iV1 would expand to 42
.if 1 || ${iV1} < ${V42}
x= Ok
.else
x= Fail
.endif
-x!= echo '1 || ${iV1} < ${V42}: $x' >&2; echo
+x!= echo '1 || $${iV1} < $${V42}: $x' >&2; echo
+# With cond.c 1.76 from 2020-07-03, the following condition triggered a
+# warning: "String comparison operator should be either == or !=".
+# This was because the variable expression ${iV2} was defined, but the
+# contained variable V66 was undefined. The left-hand side of the comparison
+# therefore evaluated to the string "${V66}", which is obviously not a number.
+#
+# This was fixed in cond.c 1.79 from 2020-07-09 by not evaluating irrelevant
+# comparisons. Instead, they are only parsed and then discarded.
+#
+# At that time, there was not enough debug logging to see the details in the
+# -dA log. To actually see it, add debug logging at the beginning and end of
+# Var_Parse.
.if 1 || ${iV2:U2} < ${V42}
x= Ok
.else
x= Fail
.endif
-x!= echo '1 || ${iV2:U2} < ${V42}: $x' >&2; echo
+x!= echo '1 || $${iV2:U2} < $${V42}: $x' >&2; echo
# the same expressions are fine when the lhs is expanded
# ${iV1} expands to 42
@@ -163,7 +190,7 @@ x= Ok
.else
x= Fail
.endif
-x!= echo '0 || ${iV1} <= ${V42}: $x' >&2; echo
+x!= echo '0 || $${iV1} <= $${V42}: $x' >&2; echo
# ${iV2:U2} expands to 2
.if 0 || ${iV2:U2} < ${V42}
@@ -171,11 +198,12 @@ x= Ok
.else
x= Fail
.endif
-x!= echo '0 || ${iV2:U2} < ${V42}: $x' >&2; echo
+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.
+# The right-hand side of the '&&' is irrelevant since the left-hand side
+# already evaluates to false. Before cond.c 1.79 from 2020-07-09, it was
+# expanded nevertheless, although with a small modification: undefined
+# variables may be used in these expressions without generating an error.
.if defined(UNDEF) && ${UNDEF} != "undefined"
. error
.endif
diff --git a/unit-tests/cond-token-string.exp b/unit-tests/cond-token-string.exp
index 5df4cc675bb1..83009f420325 100644
--- a/unit-tests/cond-token-string.exp
+++ b/unit-tests/cond-token-string.exp
@@ -1,4 +1,4 @@
-make: Unknown modifier 'Z'
+make: "cond-token-string.mk" line 9: 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} == "")
diff --git a/unit-tests/dep-percent.exp b/unit-tests/dep-percent.exp
index 2b5b5bf1fa50..1e6c04d2e167 100644
--- a/unit-tests/dep-percent.exp
+++ b/unit-tests/dep-percent.exp
@@ -1,3 +1,6 @@
make: don't know how to make dep-percent.o (continuing)
`all' not remade because of errors.
-exit status 0
+
+Stop.
+make: stopped in unit-tests
+exit status 1
diff --git a/unit-tests/depsrc-meta.exp b/unit-tests/depsrc-meta.exp
index 39a9383953dd..77e27582f7da 100644
--- a/unit-tests/depsrc-meta.exp
+++ b/unit-tests/depsrc-meta.exp
@@ -1 +1,5 @@
+Skipping meta for actual-test: no commands
+Skipping meta for .END: .SPECIAL
+Targets from meta mode:
+| TARGET depsrc-meta-target
exit status 0
diff --git a/unit-tests/depsrc-meta.mk b/unit-tests/depsrc-meta.mk
index 6adef4baa5de..d41aad9a9c96 100644
--- a/unit-tests/depsrc-meta.mk
+++ b/unit-tests/depsrc-meta.mk
@@ -1,8 +1,31 @@
-# $NetBSD: depsrc-meta.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+# $NetBSD: depsrc-meta.mk,v 1.4 2020/11/27 08:39:07 rillig Exp $
#
# Tests for the special source .META in dependency declarations.
# TODO: Implementation
+# TODO: Explanation
+
+.if make(actual-test)
+
+.MAKEFLAGS: -dM
+.MAKE.MODE= meta curDirOk=true
+
+actual-test: depsrc-meta-target
+depsrc-meta-target: .META
+ @> ${.TARGET}-file
+ @rm -f ${.TARGET}-file
+
+.elif make(check-results)
+
+check-results:
+ @echo 'Targets from meta mode:'
+ @awk '/^TARGET/ { print "| " $$0 }' depsrc-meta-target.meta
+ @rm depsrc-meta-target.meta
+
+.else
all:
- @:;
+ @${MAKE} -f ${MAKEFILE} actual-test
+ @${MAKE} -f ${MAKEFILE} check-results
+
+.endif
diff --git a/unit-tests/depsrc-optional.exp b/unit-tests/depsrc-optional.exp
index fce85b3cb38e..8ecd28266b5e 100644
--- a/unit-tests/depsrc-optional.exp
+++ b/unit-tests/depsrc-optional.exp
@@ -5,16 +5,16 @@ 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.
+Examining optional...nonexistent...up-to-date.
+Examining optional-cohort...nonexistent...:: 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.
+ recheck(optional-cohort): update time from nonexistent to now
+Examining important...nonexistent...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.
+ recheck(important): update time from nonexistent to now
+Examining all...nonexistent...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
+ recheck(all): update time from nonexistent to now
+Examining .END...nonexistent...nonexistent and no sources...out-of-date.
+ recheck(.END): update time from nonexistent to now
exit status 0
diff --git a/unit-tests/depsrc.exp b/unit-tests/depsrc.exp
index 39a9383953dd..06165e6f9ac4 100644
--- a/unit-tests/depsrc.exp
+++ b/unit-tests/depsrc.exp
@@ -1 +1,4 @@
+: 'Undefined variables are expanded directly in the dependency'
+: 'declaration. They are not preserved and maybe expanded later.'
+: 'This is in contrast to local variables such as ${.TARGET}.'
exit status 0
diff --git a/unit-tests/depsrc.mk b/unit-tests/depsrc.mk
index 15b27286de22..ab9d04c1d3a4 100644
--- a/unit-tests/depsrc.mk
+++ b/unit-tests/depsrc.mk
@@ -1,4 +1,4 @@
-# $NetBSD: depsrc.mk,v 1.3 2020/11/15 20:20:58 rillig Exp $
+# $NetBSD: depsrc.mk,v 1.4 2020/12/22 19:38:44 rillig Exp $
#
# Tests for special sources (those starting with a dot, followed by
# uppercase letters) in dependency declarations, such as .PHONY.
@@ -7,5 +7,20 @@
# TODO: Test 'target: ${:U.SILENT}'
+# Demonstrate when exactly undefined variables are expanded in a dependency
+# declaration.
+target: .PHONY source-${DEFINED_LATER}
+#
+DEFINED_LATER= later
+#
+source-: .PHONY
+ : 'Undefined variables are expanded directly in the dependency'
+ : 'declaration. They are not preserved and maybe expanded later.'
+ : 'This is in contrast to local variables such as $${.TARGET}.'
+source-later: .PHONY
+ : 'Undefined variables are tried to be expanded in a dependency'
+ : 'declaration. If that fails because the variable is undefined,'
+ : 'the expression is preserved and tried to be expanded later.'
+
all:
@:;
diff --git a/unit-tests/deptgt-begin-fail-indirect.exp b/unit-tests/deptgt-begin-fail-indirect.exp
new file mode 100644
index 000000000000..59575e839a4a
--- /dev/null
+++ b/unit-tests/deptgt-begin-fail-indirect.exp
@@ -0,0 +1,6 @@
+false
+*** Error code 1 (continuing)
+
+Stop.
+make: stopped in unit-tests
+exit status 1
diff --git a/unit-tests/deptgt-begin-fail-indirect.mk b/unit-tests/deptgt-begin-fail-indirect.mk
new file mode 100644
index 000000000000..e50b6207b7a1
--- /dev/null
+++ b/unit-tests/deptgt-begin-fail-indirect.mk
@@ -0,0 +1,16 @@
+# $NetBSD: deptgt-begin-fail-indirect.mk,v 1.1 2020/11/24 19:02:59 rillig Exp $
+#
+# Test for a .BEGIN target whose dependency results in an error.
+# This stops make immediately and does not build the main targets.
+#
+# Between 2005-05-08 and 2020-11-24, a failing dependency of the .BEGIN node
+# would not stop make from running the main targets. In the end, the exit
+# status was even 0.
+
+.BEGIN: failing
+
+failing: .PHONY .NOTMAIN
+ false
+
+all:
+ : This is not made.
diff --git a/unit-tests/deptgt-begin-fail.exp b/unit-tests/deptgt-begin-fail.exp
new file mode 100644
index 000000000000..59575e839a4a
--- /dev/null
+++ b/unit-tests/deptgt-begin-fail.exp
@@ -0,0 +1,6 @@
+false
+*** Error code 1 (continuing)
+
+Stop.
+make: stopped in unit-tests
+exit status 1
diff --git a/unit-tests/deptgt-begin-fail.mk b/unit-tests/deptgt-begin-fail.mk
new file mode 100644
index 000000000000..80cea25664ec
--- /dev/null
+++ b/unit-tests/deptgt-begin-fail.mk
@@ -0,0 +1,10 @@
+# $NetBSD: deptgt-begin-fail.mk,v 1.1 2020/11/24 19:02:59 rillig Exp $
+#
+# Test for a .BEGIN target whose command results in an error.
+# This stops make immediately and does not build the main targets.
+
+.BEGIN:
+ false
+
+all:
+ : This is not made.
diff --git a/unit-tests/deptgt-end-fail-all.exp b/unit-tests/deptgt-end-fail-all.exp
new file mode 100644
index 000000000000..2e2ee11f481a
--- /dev/null
+++ b/unit-tests/deptgt-end-fail-all.exp
@@ -0,0 +1,7 @@
+: Making all out of nothing.
+false
+*** Error code 1 (continuing)
+
+Stop.
+make: stopped in unit-tests
+exit status 1
diff --git a/unit-tests/deptgt-end-fail-all.mk b/unit-tests/deptgt-end-fail-all.mk
new file mode 100644
index 000000000000..4bf160d480f6
--- /dev/null
+++ b/unit-tests/deptgt-end-fail-all.mk
@@ -0,0 +1,19 @@
+# $NetBSD: deptgt-end-fail-all.mk,v 1.2 2020/12/07 01:04:07 rillig Exp $
+#
+# Test whether the commands from the .END target are run even if there is
+# an error before. The manual page says "after everything else is done",
+# which leaves room for interpretation.
+#
+# Until 2020-12-07, the .END node was made even if the main nodes had failed.
+# This was not intended since the .END node had already been skipped if a
+# dependency of the main nodes had failed, just not if one of the main nodes
+# themselves had failed. This inconsistency was not worth keeping. To run
+# some commands on error, use the .ERROR target instead, see deptgt-error.mk.
+
+all: .PHONY
+ : Making ${.TARGET} out of nothing.
+ false
+
+.END:
+ : Making ${.TARGET} out of nothing.
+ false
diff --git a/unit-tests/deptgt-end-fail-indirect.exp b/unit-tests/deptgt-end-fail-indirect.exp
new file mode 100644
index 000000000000..17e509600617
--- /dev/null
+++ b/unit-tests/deptgt-end-fail-indirect.exp
@@ -0,0 +1,7 @@
+: all
+false
+*** Error code 1 (continuing)
+
+Stop.
+make: stopped in unit-tests
+exit status 1
diff --git a/unit-tests/deptgt-end-fail-indirect.mk b/unit-tests/deptgt-end-fail-indirect.mk
new file mode 100644
index 000000000000..29346b8321fe
--- /dev/null
+++ b/unit-tests/deptgt-end-fail-indirect.mk
@@ -0,0 +1,16 @@
+# $NetBSD: deptgt-end-fail-indirect.mk,v 1.2 2020/12/06 21:22:04 rillig Exp $
+#
+# Tests for an error in a dependency of the .END node.
+#
+# Before 2020-11-25, an error in the .END target did not print the "Stop."
+# and exited with status 0. The cause for this was a missing condition in
+# Compat_Run in the handling of the .END node.
+
+all:
+ : $@
+
+.END: failing
+ : Making ${.TARGET} from ${.ALLSRC}.
+
+failing: .PHONY
+ false
diff --git a/unit-tests/deptgt-end-fail.exp b/unit-tests/deptgt-end-fail.exp
new file mode 100644
index 000000000000..9db907c209d5
--- /dev/null
+++ b/unit-tests/deptgt-end-fail.exp
@@ -0,0 +1,163 @@
+Test case all=ok all-dep=ok end=ok end-dep=ok.
+: Making all-dep out of nothing.
+: Making all from all-dep.
+: Making end-dep out of nothing.
+: Making .END from end-dep.
+exit status 0
+
+
+Test case all=ok all-dep=ok end=ok end-dep=ERR.
+: Making all-dep out of nothing.
+: Making all from all-dep.
+: Making end-dep out of nothing.
+*** Error code 1 (continuing)
+
+Stop.
+make: stopped in unit-tests
+exit status 1
+
+
+Test case all=ok all-dep=ok end=ERR end-dep=ok.
+: Making all-dep out of nothing.
+: Making all from all-dep.
+: Making end-dep out of nothing.
+: Making .END from end-dep.
+*** Error code 1 (continuing)
+
+Stop.
+make: stopped in unit-tests
+exit status 1
+
+
+Test case all=ok all-dep=ok end=ERR end-dep=ERR.
+: Making all-dep out of nothing.
+: Making all from all-dep.
+: Making end-dep out of nothing.
+*** Error code 1 (continuing)
+
+Stop.
+make: stopped in unit-tests
+exit status 1
+
+
+Test case all=ok all-dep=ERR end=ok end-dep=ok.
+: Making all-dep out of nothing.
+*** Error code 1 (continuing)
+`all' not remade because of errors.
+
+Stop.
+make: stopped in unit-tests
+exit status 1
+
+
+Test case all=ok all-dep=ERR end=ok end-dep=ERR.
+: Making all-dep out of nothing.
+*** Error code 1 (continuing)
+`all' not remade because of errors.
+
+Stop.
+make: stopped in unit-tests
+exit status 1
+
+
+Test case all=ok all-dep=ERR end=ERR end-dep=ok.
+: Making all-dep out of nothing.
+*** Error code 1 (continuing)
+`all' not remade because of errors.
+
+Stop.
+make: stopped in unit-tests
+exit status 1
+
+
+Test case all=ok all-dep=ERR end=ERR end-dep=ERR.
+: Making all-dep out of nothing.
+*** Error code 1 (continuing)
+`all' not remade because of errors.
+
+Stop.
+make: stopped in unit-tests
+exit status 1
+
+
+Test case all=ERR all-dep=ok end=ok end-dep=ok.
+: Making all-dep out of nothing.
+: Making all from all-dep.
+*** Error code 1 (continuing)
+
+Stop.
+make: stopped in unit-tests
+exit status 1
+
+
+Test case all=ERR all-dep=ok end=ok end-dep=ERR.
+: Making all-dep out of nothing.
+: Making all from all-dep.
+*** Error code 1 (continuing)
+
+Stop.
+make: stopped in unit-tests
+exit status 1
+
+
+Test case all=ERR all-dep=ok end=ERR end-dep=ok.
+: Making all-dep out of nothing.
+: Making all from all-dep.
+*** Error code 1 (continuing)
+
+Stop.
+make: stopped in unit-tests
+exit status 1
+
+
+Test case all=ERR all-dep=ok end=ERR end-dep=ERR.
+: Making all-dep out of nothing.
+: Making all from all-dep.
+*** Error code 1 (continuing)
+
+Stop.
+make: stopped in unit-tests
+exit status 1
+
+
+Test case all=ERR all-dep=ERR end=ok end-dep=ok.
+: Making all-dep out of nothing.
+*** Error code 1 (continuing)
+`all' not remade because of errors.
+
+Stop.
+make: stopped in unit-tests
+exit status 1
+
+
+Test case all=ERR all-dep=ERR end=ok end-dep=ERR.
+: Making all-dep out of nothing.
+*** Error code 1 (continuing)
+`all' not remade because of errors.
+
+Stop.
+make: stopped in unit-tests
+exit status 1
+
+
+Test case all=ERR all-dep=ERR end=ERR end-dep=ok.
+: Making all-dep out of nothing.
+*** Error code 1 (continuing)
+`all' not remade because of errors.
+
+Stop.
+make: stopped in unit-tests
+exit status 1
+
+
+Test case all=ERR all-dep=ERR end=ERR end-dep=ERR.
+: Making all-dep out of nothing.
+*** Error code 1 (continuing)
+`all' not remade because of errors.
+
+Stop.
+make: stopped in unit-tests
+exit status 1
+
+
+exit status 0
diff --git a/unit-tests/deptgt-end-fail.mk b/unit-tests/deptgt-end-fail.mk
new file mode 100644
index 000000000000..57cdc7da8679
--- /dev/null
+++ b/unit-tests/deptgt-end-fail.mk
@@ -0,0 +1,69 @@
+# $NetBSD: deptgt-end-fail.mk,v 1.6 2020/12/07 01:04:07 rillig Exp $
+#
+# Tests for an errors in the main target, its dependencies,
+# the .END node and its dependencies.
+#
+# Before 2020-11-25, an error in the .END target did not print the "Stop.",
+# even though this was intended. The cause for this was a missing condition
+# in Compat_Run, in the code handling the .END node.
+
+test: .PHONY
+
+# The default stop-on-error mode is not as interesting to test since it
+# stops right after the first error.
+.MAKEFLAGS: -k
+
+.for all in ok ERR
+. for all-dep in ok ERR
+. for end in ok ERR
+. for end-dep in ok ERR
+. for target in ${all}-${all-dep}-${end}-${end-dep}
+test: ${target}
+${target}: .PHONY .SILENT
+ echo Test case all=${all} all-dep=${all-dep} end=${end} end-dep=${end-dep}.
+ ${MAKE} -r -f ${MAKEFILE} \
+ all=${all} all-dep=${all-dep} \
+ end=${end} end-dep=${end-dep} \
+ all; \
+ echo "exit status $$?"
+ echo
+ echo
+. endfor
+. endfor
+. endfor
+. endfor
+.endfor
+
+.if make(all)
+
+all all-dep end-dep: .PHONY
+
+CMD.ok= true
+CMD.ERR= false
+
+all: all-dep
+ : Making ${.TARGET} from ${.ALLSRC}.
+ @${CMD.${all}}
+
+all-dep:
+ : Making ${.TARGET} out of nothing.
+ @${CMD.${all-dep}}
+
+.END: end-dep
+ : Making ${.TARGET} from ${.ALLSRC}.
+ @${CMD.${end}}
+
+end-dep:
+ : Making ${.TARGET} out of nothing.
+ @${CMD.${end-dep}}
+
+.endif
+
+# Until 2020-12-07, several of the test cases printed "`all' not remade
+# because of errors.", followed by "exit status 0", which contradicted
+# each other.
+
+# Until 2020-12-07, '.END' was even made if 'all' failed, but if a dependency
+# of 'all' failed, it was skipped. This inconsistency was not needed for
+# anything and thus has been dropped. To run some commands on error, use the
+# .ERROR target instead, see deptgt-error.mk.
diff --git a/unit-tests/deptgt-suffixes.exp b/unit-tests/deptgt-suffixes.exp
index 65dc36cfe001..512e6d44a8be 100644
--- a/unit-tests/deptgt-suffixes.exp
+++ b/unit-tests/deptgt-suffixes.exp
@@ -3,5 +3,31 @@
# To:
# From:
# Search Path: . ..
+# ".src-left" (num 2, ref 2)
+# To: .tgt-right
+# From:
+# Search Path:
+# ".tgt-right" (num 3, ref 2)
+# To:
+# From: .src-left
+# Search Path:
+# ".tgt-left" (num 4, ref 2)
+# To:
+# From: .src-right
+# Search Path:
+# ".src-right" (num 5, ref 2)
+# To: .tgt-left
+# From:
+# Search Path:
#*** Transformations:
+.src-left.tgt-right:
+ : Making ${.TARGET} from ${.IMPSRC}.
+
+.src-right.tgt-left:
+ : Making ${.TARGET} from ${.IMPSRC}.
+
+: Making deptgt-suffixes.src-left out of nothing.
+: Making deptgt-suffixes.tgt-right from deptgt-suffixes.src-left.
+: Making deptgt-suffixes.src-right out of nothing.
+: Making deptgt-suffixes.tgt-left from deptgt-suffixes.src-right.
exit status 0
diff --git a/unit-tests/deptgt-suffixes.mk b/unit-tests/deptgt-suffixes.mk
index 791ff5eb5f03..15b4dc08161c 100644
--- a/unit-tests/deptgt-suffixes.mk
+++ b/unit-tests/deptgt-suffixes.mk
@@ -1,4 +1,4 @@
-# $NetBSD: deptgt-suffixes.mk,v 1.3 2020/08/28 04:05:35 rillig Exp $
+# $NetBSD: deptgt-suffixes.mk,v 1.4 2020/11/21 21:54:42 rillig Exp $
#
# Tests for the special target .SUFFIXES in dependency declarations.
#
@@ -8,11 +8,28 @@
.MAKEFLAGS: -dg1
+.MAIN: all
+
.SUFFIXES: .custom-null
# TODO: What is the effect of this? How is it useful?
.NULL: .custom-null
.PATH.custom-null: . ..
-all:
- @:;
+# The order in which the suffixes are listed doesn't matter.
+# Here, they are listed from source to target, just like in the transformation
+# rule below it.
+.SUFFIXES: .src-left .tgt-right
+deptgt-suffixes.src-left:
+ : Making ${.TARGET} out of nothing.
+.src-left.tgt-right:
+ : Making ${.TARGET} from ${.IMPSRC}.
+all: deptgt-suffixes.tgt-right
+
+# Here, the target is listed earlier than the source.
+.SUFFIXES: .tgt-left .src-right
+deptgt-suffixes.src-right:
+ : Making ${.TARGET} out of nothing.
+.src-right.tgt-left:
+ : Making ${.TARGET} from ${.IMPSRC}.
+all: deptgt-suffixes.tgt-left
diff --git a/unit-tests/deptgt.exp b/unit-tests/deptgt.exp
index fee0563f5b37..b2aeaa5a2850 100644
--- a/unit-tests/deptgt.exp
+++ b/unit-tests/deptgt.exp
@@ -8,6 +8,7 @@ ParseDoDependency(: empty-source)
ParseReadLine (37): ' : command for empty targets list'
ParseReadLine (38): '.MAKEFLAGS: -d0'
ParseDoDependency(.MAKEFLAGS: -d0)
+make: "deptgt.mk" line 46: Unknown modifier 'Z'
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/unit-tests/deptgt.mk b/unit-tests/deptgt.mk
index 83f81b6f58ed..09f381715e6d 100644
--- a/unit-tests/deptgt.mk
+++ b/unit-tests/deptgt.mk
@@ -1,4 +1,4 @@
-# $NetBSD: deptgt.mk,v 1.9 2020/11/15 11:57:00 rillig Exp $
+# $NetBSD: deptgt.mk,v 1.10 2020/12/27 18:20:26 rillig Exp $
#
# Tests for special targets like .BEGIN or .SUFFIXES in dependency
# declarations.
@@ -37,5 +37,13 @@ ${:U}: empty-source
: command for empty targets list
.MAKEFLAGS: -d0
+# Just to show that a malformed expression is only expanded once in
+# ParseDependencyTargetWord. The only way to produce an expression that
+# is well-formed on the first expansion and ill-formed on the second
+# expansion would be to use the variable modifier '::=' to modify the
+# targets. This in turn would be such an extreme and unreliable edge case
+# that nobody uses it.
+$$$$$$$${:U:Z}:
+
all:
@:;
diff --git a/unit-tests/directive-elif.exp b/unit-tests/directive-elif.exp
index 6219b4896795..6856494023d7 100644
--- a/unit-tests/directive-elif.exp
+++ b/unit-tests/directive-elif.exp
@@ -1,17 +1,21 @@
-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: "directive-elif.mk" line 47: Unknown directive "elsif"
+make: "directive-elif.mk" line 52: This branch is taken.
+make: "directive-elif.mk" line 60: Unknown directive "elsif"
+make: "directive-elif.mk" line 63: This branch is taken.
+make: "directive-elif.mk" line 69: This branch is taken.
+make: "directive-elif.mk" line 89: Unknown directive "elsif"
+make: "directive-elif.mk" line 90: This misspelling is detected.
+make: "directive-elif.mk" line 91: This branch is taken because of the .else.
+make: "directive-elif.mk" line 109: What happens on misspelling in a skipped branch?
+make: "directive-elif.mk" line 119: else
+make: "directive-elif.mk" line 122: What happens on misspelling in a taken branch?
+make: "directive-elif.mk" line 124: 1-then
+make: "directive-elif.mk" line 125: Unknown directive "elsif"
+make: "directive-elif.mk" line 126: 1-elsif
+make: "directive-elif.mk" line 127: Unknown directive "elsif"
+make: "directive-elif.mk" line 128: 2-elsif
+make: "directive-elif.mk" line 134: if-less elif
+make: "directive-elif.mk" line 139: 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 f9a43abfffc1..e7b89beec4e9 100644
--- a/unit-tests/directive-elif.mk
+++ b/unit-tests/directive-elif.mk
@@ -1,55 +1,125 @@
-# $NetBSD: directive-elif.mk,v 1.6 2020/11/12 19:46:36 rillig Exp $
+# $NetBSD: directive-elif.mk,v 1.7 2020/12/19 19:49:01 rillig Exp $
#
# Tests for the .elif directive.
+#
+# Misspellings of the .elif directive are not always detected. They are only
+# detected if the conditional branch directly above it is taken. In all other
+# cases, make skips over the skipped branch as fast as possible, looking only
+# at the initial '.' of the line and whether the directive is one of the known
+# conditional directives. All other directives are silently ignored, as they
+# could be variable assignments or dependency declarations as well, and
+# deciding this would cost time.
+
# 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
+
+# Misspelling '.elsif' below an .if branch that is not taken.
+.if 0
+. info This branch is not taken.
+# As of 2020-12-19, the misspelling is not recognized as a conditional
+# directive and is thus silently skipped.
+#
+# Since the .if condition evaluated to false, this whole branch is not taken.
+.elsif 0
+. info XXX: This misspelling is not detected.
+. info This branch is not taken.
+# Even if the misspelling were detected, the branch would not be taken
+# since the condition of the '.elsif' evaluates to false as well.
.endif
-.info begin .elif misspellings tests, part 2
+
+# Misspelling '.elsif' below an .if branch that is not taken.
.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
+. info This branch is not taken.
+# As of 2020-12-19, the misspelling is not recognized as a conditional
+# directive and is thus silently skipped. Since the .if condition evaluated
+# to false, this whole branch is not taken.
+.elsif 1
+. info XXX: This misspelling is not detected.
+# If the misspelling were detected, this branch would be taken.
+.endif
+
+
+# Misspelling '.elsif' below an .if branch that is taken.
+.if 1
+# This misspelling is in an active branch and is therefore detected.
+.elsif 0
+# The only thing that make detects here is a misspelled directive, make
+# doesn't recognize that it was meant to be a conditional directive.
+# Therefore the branch continues here, even though the '.elsif' condition
+# evaluates to false.
+. info This branch is taken.
+.endif
+
+
+# Misspelling '.elsif' below an .if branch that is taken.
+.if 1
+# As of 2020-12-19, the misspelling is in an active branch and is therefore
+# detected.
+.elsif 1
+# Since both conditions evaluate to true, this branch is taken no matter
+# whether make detects a misspelling or not.
+. info This branch is taken.
.endif
-.info begin .elif misspellings tests, part 3
+
+# Misspelling '.elsif' in a skipped branch below a branch that was taken.
+.if 1
+. info This branch is taken.
+.elif 0
+. info This branch is not taken.
+.elsif 1
+. info XXX: This misspelling is not detected.
+.endif
+
+
+# Misspelling '.elsif' in an .else branch that is not taken.
+.if 1
+.else
+. info This branch is not taken.
+.elsif 1
+. info XXX: This misspelling is not detected.
+.endif
+
+
+# Misspelling '.elsif' in an .else branch that is taken.
.if 0
-. info 0-then
-.elsif 0 # oops: misspelled
-. info 0-elsif
+.else
+.elsif 1
+. info This misspelling is detected.
+. info This branch is taken because of the .else.
.endif
+
+
+# Misspellings for .elif in a .elif branch that is not taken.
.if 0
-. info 0-then
-.elseif 0 # oops: misspelled
-. info 0-elseif
+. info This branch is not taken.
+.elif 0 # ok
+. info This branch is not taken.
+.elsif 0
+. info XXX: This misspelling is not detected.
+. info This branch is not taken.
+.elseif 0
+. info XXX: This misspelling is not detected.
+. info This branch is not taken.
.endif
-.info which branch is taken on misspelling after false?
+
+.info What happens on misspelling in a skipped branch?
.if 0
. info 0-then
.elsif 1
+. info XXX: This misspelling is not detected.
. info 1-elsif
.elsif 2
+. info XXX: This misspelling is not detected.
. info 2-elsif
.else
. info else
.endif
-.info which branch is taken on misspelling after true?
+.info What happens on misspelling in a taken branch?
.if 1
. info 1-then
.elsif 1
@@ -65,7 +135,7 @@
.if 1
.else
-# Expect: "warning: if-less elif"
+# Expect: "warning: extra elif"
.elif
.endif
diff --git a/unit-tests/directive-else.exp b/unit-tests/directive-else.exp
index ca60595745a9..138e893ffa88 100644
--- a/unit-tests/directive-else.exp
+++ b/unit-tests/directive-else.exp
@@ -1,11 +1,11 @@
-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: "directive-else.mk" line 14: The .else directive does not take arguments.
+make: "directive-else.mk" line 15: ok
+make: "directive-else.mk" line 19: ok
+make: "directive-else.mk" line 21: The .else directive does not take arguments.
+make: "directive-else.mk" line 26: if-less else
+make: "directive-else.mk" line 32: ok
+make: "directive-else.mk" line 33: warning: extra else
+make: "directive-else.mk" line 45: 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 fdd94007a1fa..794057110ef7 100644
--- a/unit-tests/directive-else.mk
+++ b/unit-tests/directive-else.mk
@@ -1,19 +1,23 @@
-# $NetBSD: directive-else.mk,v 1.6 2020/11/13 09:01:59 rillig Exp $
+# $NetBSD: directive-else.mk,v 1.7 2020/12/14 22:17:11 rillig Exp $
#
# Tests for the .else directive.
+#
+# Since 2020-11-13, an '.else' followed by extraneous text generates a parse
+# error in -dL (lint) mode.
+#
+# Since 2020-12-15, an '.else' followed by extraneous text always generates
+# a parse error.
-.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
. warning must not be reached
+# The .else directive does not take any arguments.
.else 123
. info ok
.endif
.if 1
. info ok
+# The .else directive does not take any arguments.
.else 123
. warning must not be reached
.endif
@@ -37,7 +41,6 @@
.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
diff --git a/unit-tests/directive-endfor.exp b/unit-tests/directive-endfor.exp
new file mode 100644
index 000000000000..7e243a8f67e6
--- /dev/null
+++ b/unit-tests/directive-endfor.exp
@@ -0,0 +1,4 @@
+make: "directive-endfor.mk" line 9: for-less endfor
+make: Fatal errors encountered -- cannot continue
+make: stopped in unit-tests
+exit status 1
diff --git a/unit-tests/directive-endfor.mk b/unit-tests/directive-endfor.mk
new file mode 100644
index 000000000000..b0c37f388504
--- /dev/null
+++ b/unit-tests/directive-endfor.mk
@@ -0,0 +1,9 @@
+# $NetBSD: directive-endfor.mk,v 1.1 2020/12/30 14:50:08 rillig Exp $
+#
+# Test for the directive .endfor, which ends a .for loop.
+#
+# See also:
+# directive-for.mk
+
+# An .endfor without a corresponding .for is a parse error.
+.endfor
diff --git a/unit-tests/directive-endif.exp b/unit-tests/directive-endif.exp
index 39a9383953dd..286d85244eae 100644
--- a/unit-tests/directive-endif.exp
+++ b/unit-tests/directive-endif.exp
@@ -1 +1,8 @@
-exit status 0
+make: "directive-endif.mk" line 18: The .endif directive does not take arguments.
+make: "directive-endif.mk" line 23: The .endif directive does not take arguments.
+make: "directive-endif.mk" line 33: The .endif directive does not take arguments.
+make: "directive-endif.mk" line 39: The .endif directive does not take arguments.
+make: "directive-endif.mk" line 45: Unknown directive "endifx"
+make: Fatal errors encountered -- cannot continue
+make: stopped in unit-tests
+exit status 1
diff --git a/unit-tests/directive-endif.mk b/unit-tests/directive-endif.mk
index b0b531af2f06..10dd6ce22ce8 100644
--- a/unit-tests/directive-endif.mk
+++ b/unit-tests/directive-endif.mk
@@ -1,7 +1,10 @@
-# $NetBSD: directive-endif.mk,v 1.3 2020/11/12 22:40:11 rillig Exp $
+# $NetBSD: directive-endif.mk,v 1.5 2020/12/14 21:56:17 rillig Exp $
#
# Tests for the .endif directive.
#
+# Since 2020-12-15, the .endif directive no longer accepts arguments.
+# The manual page had never allowed that, but the code didn't check it.
+#
# See also:
# Cond_EvalLine
@@ -10,18 +13,37 @@
.MAKEFLAGS: -dL
# Error: .endif does not take arguments
-# XXX: Missing error message
.if 0
+# Since 2020-12-15, complain about the extra text after the 'endif'.
.endif 0
# Error: .endif does not take arguments
-# XXX: Missing error message
.if 1
+# Since 2020-12-15, complain about the extra text after the 'endif'.
.endif 1
# Comments are allowed after an '.endif'.
.if 2
.endif # comment
+# Only whitespace and comments are allowed after an '.endif', but nothing
+# else.
+.if 1
+# Since 2020-12-15, complain about the extra text after the 'endif'.
+.endif0
+
+# Only whitespace and comments are allowed after an '.endif', but nothing
+# else.
+.if 1
+# Since 2020-12-15, complain about the extra text after the 'endif'.
+.endif/
+
+# After an '.endif', no other letter must occur. This 'endifx' is not
+# parsed as an 'endif', therefore another '.endif' must follow to balance
+# the directives.
+.if 1
+.endifx
+.endif # to close the preceding '.if'
+
all:
@:;
diff --git a/unit-tests/directive-error.mk b/unit-tests/directive-error.mk
index 3980016221ab..50a0c6c0e84c 100644
--- a/unit-tests/directive-error.mk
+++ b/unit-tests/directive-error.mk
@@ -1,6 +1,8 @@
-# $NetBSD: directive-error.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+# $NetBSD: directive-error.mk,v 1.3 2020/12/13 01:07:54 rillig Exp $
#
-# Tests for the .error directive.
+# Tests for the .error directive, which prints an error message and exits
+# immediately, unlike other "fatal" parse errors, which continue to parse
+# until the end of the current top-level makefile.
# TODO: Implementation
diff --git a/unit-tests/directive-export-env.mk b/unit-tests/directive-export-env.mk
index 82b5e8087c6d..2ef2ceaf788c 100644
--- a/unit-tests/directive-export-env.mk
+++ b/unit-tests/directive-export-env.mk
@@ -1,12 +1,10 @@
-# $NetBSD: directive-export-env.mk,v 1.3 2020/11/03 17:17:31 rillig Exp $
+# $NetBSD: directive-export-env.mk,v 1.4 2020/12/13 01:07:54 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-impl.exp b/unit-tests/directive-export-impl.exp
new file mode 100644
index 000000000000..c3ac940d2df0
--- /dev/null
+++ b/unit-tests/directive-export-impl.exp
@@ -0,0 +1,56 @@
+ParseReadLine (21): 'UT_VAR= <${REF}>'
+Global:UT_VAR = <${REF}>
+ParseReadLine (28): '.export UT_VAR'
+Global:.MAKE.EXPORTED = UT_VAR
+ParseReadLine (32): ': ${UT_VAR:N*}'
+Var_Parse: ${UT_VAR:N*} with VARE_UNDEFERR|VARE_WANTRES
+Var_Parse: ${REF}> with VARE_UNDEFERR|VARE_WANTRES
+Applying ${UT_VAR:N...} to "<>" (VARE_UNDEFERR|VARE_WANTRES, VAR_EXPORTED|VAR_REEXPORT, none)
+Pattern[UT_VAR] for [<>] is [*]
+ModifyWords: split "<>" into 1 words
+Result of ${UT_VAR:N*} is "" (VARE_UNDEFERR|VARE_WANTRES, VAR_EXPORTED|VAR_REEXPORT, none)
+ParseDoDependency(: )
+CondParser_Eval: ${:!echo "\$UT_VAR"!} != "<>"
+Var_Parse: ${:!echo "\$UT_VAR"!} != "<>" with VARE_UNDEFERR|VARE_WANTRES
+Applying ${:!...} to "" (VARE_UNDEFERR|VARE_WANTRES, none, VEF_UNDEF)
+Modifier part: "echo "$UT_VAR""
+Var_Parse: ${.MAKE.EXPORTED:O:u} with VARE_WANTRES
+Applying ${.MAKE.EXPORTED:O} to "UT_VAR" (VARE_WANTRES, none, none)
+Result of ${.MAKE.EXPORTED:O} is "UT_VAR" (VARE_WANTRES, none, none)
+Applying ${.MAKE.EXPORTED:u} to "UT_VAR" (VARE_WANTRES, none, none)
+Result of ${.MAKE.EXPORTED:u} is "UT_VAR" (VARE_WANTRES, none, none)
+Var_Parse: ${UT_VAR} with VARE_WANTRES
+Var_Parse: ${REF}> with VARE_WANTRES
+Result of ${:!echo "\$UT_VAR"!} is "<>" (VARE_UNDEFERR|VARE_WANTRES, none, VEF_UNDEF|VEF_DEF)
+lhs = "<>", rhs = "<>", op = !=
+ParseReadLine (49): ': ${UT_VAR:N*}'
+Var_Parse: ${UT_VAR:N*} with VARE_UNDEFERR|VARE_WANTRES
+Var_Parse: ${REF}> with VARE_UNDEFERR|VARE_WANTRES
+Applying ${UT_VAR:N...} to "<>" (VARE_UNDEFERR|VARE_WANTRES, VAR_EXPORTED|VAR_REEXPORT, none)
+Pattern[UT_VAR] for [<>] is [*]
+ModifyWords: split "<>" into 1 words
+Result of ${UT_VAR:N*} is "" (VARE_UNDEFERR|VARE_WANTRES, VAR_EXPORTED|VAR_REEXPORT, none)
+ParseDoDependency(: )
+ParseReadLine (53): 'REF= defined'
+Global:REF = defined
+CondParser_Eval: ${:!echo "\$UT_VAR"!} != "<defined>"
+Var_Parse: ${:!echo "\$UT_VAR"!} != "<defined>" with VARE_UNDEFERR|VARE_WANTRES
+Applying ${:!...} to "" (VARE_UNDEFERR|VARE_WANTRES, none, VEF_UNDEF)
+Modifier part: "echo "$UT_VAR""
+Var_Parse: ${.MAKE.EXPORTED:O:u} with VARE_WANTRES
+Applying ${.MAKE.EXPORTED:O} to "UT_VAR" (VARE_WANTRES, none, none)
+Result of ${.MAKE.EXPORTED:O} is "UT_VAR" (VARE_WANTRES, none, none)
+Applying ${.MAKE.EXPORTED:u} to "UT_VAR" (VARE_WANTRES, none, none)
+Result of ${.MAKE.EXPORTED:u} is "UT_VAR" (VARE_WANTRES, none, none)
+Var_Parse: ${UT_VAR} with VARE_WANTRES
+Var_Parse: ${REF}> with VARE_WANTRES
+Result of ${:!echo "\$UT_VAR"!} is "<defined>" (VARE_UNDEFERR|VARE_WANTRES, none, VEF_UNDEF|VEF_DEF)
+lhs = "<defined>", rhs = "<defined>", op = !=
+ParseReadLine (61): 'all:'
+ParseDoDependency(all:)
+Global:.ALLTARGETS = all
+ParseReadLine (62): '.MAKEFLAGS: -d0'
+ParseDoDependency(.MAKEFLAGS: -d0)
+Global:.MAKEFLAGS = -r -k -d cpv -d
+Global:.MAKEFLAGS = -r -k -d cpv -d 0
+exit status 0
diff --git a/unit-tests/directive-export-impl.mk b/unit-tests/directive-export-impl.mk
new file mode 100644
index 000000000000..556e5352d1c3
--- /dev/null
+++ b/unit-tests/directive-export-impl.mk
@@ -0,0 +1,62 @@
+# $NetBSD: directive-export-impl.mk,v 1.1 2020/12/29 01:45:06 rillig Exp $
+#
+# Test for the implementation of exporting variables to child processes.
+# This involves marking variables for export, actually exporting them,
+# or marking them for being re-exported.
+#
+# See also:
+# Var_Export
+# ExportVar
+# VarExportedMode (global)
+# VAR_EXPORTED (per variable)
+# VAR_REEXPORT (per variable)
+# VarExportMode (per call of Var_Export and ExportVar)
+
+: ${:U:sh} # side effect: initialize .SHELL
+
+.MAKEFLAGS: -dcpv
+
+# This is a variable that references another variable. At this point, the
+# other variable is still undefined.
+UT_VAR= <${REF}>
+
+# At this point, ExportVar("UT_VAR", VEM_PLAIN) is called. Since the
+# variable value refers to another variable, ExportVar does not actually
+# export the variable but only marks it as VAR_EXPORTED and VAR_REEXPORT.
+# After that, ExportVars registers the variable name in .MAKE.EXPORTED.
+# That's all for now.
+.export UT_VAR
+
+# Evaluating this expression shows the variable flags in the debug log,
+# which are VAR_EXPORTED|VAR_REEXPORT.
+: ${UT_VAR:N*}
+
+# At the last moment before actually forking off the child process for the
+# :!...! modifier, Cmd_Exec calls Var_ReexportVars to have all relevant
+# variables exported. Since this variable has both of the above-mentioned
+# flags set, it is actually exported to the environment. The variable flags
+# are not modified though, since the next time the :!...! modifier is
+# evaluated, the referenced variables could have changed, therefore the
+# variable will be exported anew for each ':sh' modifier, ':!...!' modifier,
+# '!=' variable assignment.
+.if ${:!echo "\$UT_VAR"!} != "<>"
+. error
+.endif
+
+# Evaluating this expression shows the variable flags in the debug log,
+# which are still VAR_EXPORTED|VAR_REEXPORT, which means that the variable
+# is still marked as being re-exported for each child process.
+: ${UT_VAR:N*}
+
+# Now the referenced variable gets defined. This does not influence anything
+# in the process of exporting the variable value, though.
+REF= defined
+
+# Nothing surprising here. The variable UT_VAR gets exported, and this time,
+# REF is defined and gets expanded into the exported environment variable.
+.if ${:!echo "\$UT_VAR"!} != "<defined>"
+. error
+.endif
+
+all:
+.MAKEFLAGS: -d0
diff --git a/unit-tests/directive-export-literal.mk b/unit-tests/directive-export-literal.mk
index 51e5b522a3b9..5fafa4a7282d 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.6 2020/11/03 17:17:31 rillig Exp $
+# $NetBSD: directive-export-literal.mk,v 1.7 2020/12/13 01:07:54 rillig Exp $
#
# Tests for the .export-literal directive, which exports a variable value
# without expanding it.
@@ -7,9 +7,7 @@ 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 bd828b63c10c..39a9383953dd 100644
--- a/unit-tests/directive-export.exp
+++ b/unit-tests/directive-export.exp
@@ -1,4 +1 @@
-make: "directive-export.mk" line 25: Unknown directive "expor"
-make: Fatal errors encountered -- cannot continue
-make: stopped in unit-tests
-exit status 1
+exit status 0
diff --git a/unit-tests/directive-export.mk b/unit-tests/directive-export.mk
index bae50aecbdaf..40fda0968cb0 100644
--- a/unit-tests/directive-export.mk
+++ b/unit-tests/directive-export.mk
@@ -1,12 +1,19 @@
-# $NetBSD: directive-export.mk,v 1.4 2020/11/03 17:17:31 rillig Exp $
+# $NetBSD: directive-export.mk,v 1.6 2020/12/13 01:07:54 rillig Exp $
#
# Tests for the .export directive.
+#
+# See also:
+# directive-misspellings.mk
# TODO: Implementation
INDIRECT= indirect
VAR= value $$ ${INDIRECT}
+# Before 2020-12-13, this unusual expression invoked undefined behavior since
+# it accessed out-of-bounds memory via Var_Export -> ExportVar -> MayExport.
+.export ${:U }
+
# A variable is exported using the .export directive.
# During that, its value is expanded, just like almost everywhere else.
.export VAR
@@ -21,11 +28,8 @@ VAR= value $$ ${INDIRECT}
. error
.endif
-# Tests for parsing the .export directive.
-.expor # misspelled
-.export # oops: missing argument
-.export VARNAME
-.exporting works # oops: misspelled
+# No argument means to export all variables.
+.export
all:
@:;
diff --git a/unit-tests/directive-for-errors.exp b/unit-tests/directive-for-errors.exp
new file mode 100644
index 000000000000..6088a93c9a4a
--- /dev/null
+++ b/unit-tests/directive-for-errors.exp
@@ -0,0 +1,22 @@
+make: "directive-for-errors.mk" line 7: Unknown directive "fori"
+make: "directive-for-errors.mk" line 8: warning:
+make: "directive-for-errors.mk" line 9: for-less endfor
+make: "directive-for-errors.mk" line 19: Unknown directive "for"
+make: "directive-for-errors.mk" line 20: warning:
+make: "directive-for-errors.mk" line 21: for-less endfor
+make: "directive-for-errors.mk" line 37: Dollar $ 1 1 and backslash 2 2 2.
+make: "directive-for-errors.mk" line 37: Dollar $ 3 3 and backslash 4 4 4.
+make: "directive-for-errors.mk" line 43: no iteration variables in for
+make: "directive-for-errors.mk" line 47: warning: Should not be reached.
+make: "directive-for-errors.mk" line 48: for-less endfor
+make: "directive-for-errors.mk" line 53: Wrong number of words (5) in .for substitution list with 3 variables
+make: "directive-for-errors.mk" line 64: missing `in' in for
+make: "directive-for-errors.mk" line 66: warning: Should not be reached.
+make: "directive-for-errors.mk" line 67: for-less endfor
+make: "directive-for-errors.mk" line 73: Unknown modifier 'Z'
+make: "directive-for-errors.mk" line 74: warning: Should not be reached.
+make: "directive-for-errors.mk" line 74: warning: Should not be reached.
+make: "directive-for-errors.mk" line 74: warning: Should not be reached.
+make: Fatal errors encountered -- cannot continue
+make: stopped in unit-tests
+exit status 1
diff --git a/unit-tests/directive-for-errors.mk b/unit-tests/directive-for-errors.mk
new file mode 100644
index 000000000000..7890e2375af4
--- /dev/null
+++ b/unit-tests/directive-for-errors.mk
@@ -0,0 +1,75 @@
+# $NetBSD: directive-for-errors.mk,v 1.1 2020/12/31 03:05:12 rillig Exp $
+#
+# Tests for error handling in .for loops.
+
+# A .for directive must be followed by whitespace, everything else results
+# in a parse error.
+.fori in 1 2 3
+. warning ${i}
+.endfor
+
+# A slash is not whitespace, therefore this is not parsed as a .for loop.
+#
+# XXX: The error message is misleading though. As of 2020-12-31, it says
+# "Unknown directive "for"", but that directive is actually known. This is
+# because ForEval does not detect the .for loop as such, so parsing
+# continues in ParseLine > ParseDependency > ParseDoDependency >
+# ParseDoDependencyTargets > ParseErrorNoDependency, and there the directive
+# name is parsed a bit differently.
+.for/i in 1 2 3
+. warning ${i}
+.endfor
+
+# As of 2020-12-31, the variable name can be an arbitrary word, it just needs
+# to be separated by whitespace. Even '$' and '\' are valid variable names,
+# which is not useful in practice.
+#
+# The '$$' is not replaced with the values '1' or '3' from the .for loop,
+# instead it is kept as-is, and when the .info directive expands its argument,
+# each '$$' gets replaced with a single '$'. The "long variable expression"
+# ${$} gets replaced though, even though this would be a parse error everywhere
+# outside a .for loop.
+#
+# The '\' on the other hand is treated as a normal variable name.
+${:U\$}= dollar # see whether the "variable" '$' is local
+${:U\\}= backslash # see whether the "variable" '\' is local
+.for $ \ in 1 2 3 4
+. info Dollar $$ ${$} $($) and backslash $\ ${\} $(\).
+.endfor
+
+# If there are no variables, there is no point in expanding the .for loop
+# since this would end up in an endless loop, each time consuming 0 of the
+# 3 values.
+.for in 1 2 3
+# XXX: This should not be reached. It should be skipped, as already done
+# when the number of values is not a multiple of the number of variables,
+# see below.
+. warning Should not be reached.
+.endfor
+
+# There are 3 variables and 5 values. These 5 values cannot be split evenly
+# among the variables, therefore the loop is not expanded at all, it is
+# rather skipped.
+.for a b c in 1 2 3 4 5
+. warning Should not be reached.
+.endfor
+
+# The list of values after the 'in' may be empty, no matter if this emptiness
+# comes from an empty expansion or even from a syntactically empty line.
+.for i in
+. info Would be reached if there were items to loop over.
+.endfor
+
+# A missing 'in' should parse the .for loop but skip the body.
+.for i : k
+# XXX: As of 2020-12-31, this line is reached once.
+. warning Should not be reached.
+.endfor
+
+# A malformed modifier should be detected and skip the body of the loop.
+#
+# XXX: As of 2020-12-31, Var_Subst doesn't report any errors, therefore
+# the loop body is expanded as if no error had happened.
+.for i in 1 2 ${:U3:Z} 4
+. warning Should not be reached.
+.endfor
diff --git a/unit-tests/directive-for-escape.exp b/unit-tests/directive-for-escape.exp
new file mode 100644
index 000000000000..3d2d2ec744aa
--- /dev/null
+++ b/unit-tests/directive-for-escape.exp
@@ -0,0 +1,74 @@
+For: end for 1
+For: loop body:
+. info ${:U!"#$%&'()*+,-./0-9\:;<=>?@A-Z[\\]_^a-z{|\}~}
+make: Unclosed variable specification (expecting '}') for "" (value "!"") modifier U
+make: "directive-for-escape.mk" line 19: !"
+For: end for 1
+For: loop body:
+. info ${:U!"\\\\#$%&'()*+,-./0-9\:;<=>?@A-Z[\\]_^a-z{|\}~}
+make: Unclosed variable specification (expecting '}') for "" (value "!"\\") modifier U
+make: "directive-for-escape.mk" line 29: !"\\
+For: end for 1
+For: loop body:
+. info ${:U\$}
+make: "directive-for-escape.mk" line 41: $
+For: loop body:
+. info ${:U${V}}
+make: "directive-for-escape.mk" line 41: value
+For: loop body:
+. info ${:U${V:=-with-modifier}}
+make: "directive-for-escape.mk" line 41: value-with-modifier
+For: loop body:
+. info ${:U$(V)}
+make: "directive-for-escape.mk" line 41: value
+For: loop body:
+. info ${:U$(V:=-with-modifier)}
+make: "directive-for-escape.mk" line 41: value-with-modifier
+For: end for 1
+For: loop body:
+. info ${:U\${UNDEF\:U\\$\\$}
+make: "directive-for-escape.mk" line 52: ${UNDEF:U\$
+For: loop body:
+. info ${:U{{\}\}}
+make: "directive-for-escape.mk" line 52: {{}}
+For: loop body:
+. info ${:Uend\}}
+make: "directive-for-escape.mk" line 52: end}
+For: end for 1
+For: loop body:
+. info ${:U\$}
+make: "directive-for-escape.mk" line 60: $
+For: end for 1
+For: loop body:
+. info ${NUMBERS} ${:Ureplaced}
+make: "directive-for-escape.mk" line 68: one two three replaced
+For: end for 1
+For: loop body:
+. info ${:Ureplaced}
+make: "directive-for-escape.mk" line 78: replaced
+For: end for 1
+For: loop body:
+. info . $$i: ${:Uinner}
+. info . $${i}: ${:Uinner}
+. info . $${i:M*}: ${:Uinner:M*}
+. info . $$(i): $(:Uinner)
+. info . $$(i:M*): $(:Uinner:M*)
+. info . $${i$${:U}}: ${i${:U}}
+. info . $${i\}}: ${:Uinner\}} # XXX: unclear why SubstVarLong needs this
+. info . $${i2}: ${i2}
+. info . $${i,}: ${i,}
+. info . adjacent: ${:Uinner}${:Uinner}${:Uinner:M*}${:Uinner}
+make: "directive-for-escape.mk" line 86: . $i: inner
+make: "directive-for-escape.mk" line 87: . ${i}: inner
+make: "directive-for-escape.mk" line 88: . ${i:M*}: inner
+make: "directive-for-escape.mk" line 89: . $(i): inner
+make: "directive-for-escape.mk" line 90: . $(i:M*): inner
+make: "directive-for-escape.mk" line 91: . ${i${:U}}: outer
+make: "directive-for-escape.mk" line 92: . ${i\}}: inner}
+make: "directive-for-escape.mk" line 93: . ${i2}: two
+make: "directive-for-escape.mk" line 94: . ${i,}: comma
+make: "directive-for-escape.mk" line 95: . adjacent: innerinnerinnerinner
+make: no target to make.
+
+make: stopped in unit-tests
+exit status 2
diff --git a/unit-tests/directive-for-escape.mk b/unit-tests/directive-for-escape.mk
new file mode 100644
index 000000000000..0bd2af68625a
--- /dev/null
+++ b/unit-tests/directive-for-escape.mk
@@ -0,0 +1,96 @@
+# $NetBSD: directive-for-escape.mk,v 1.3 2020/12/31 14:26:37 rillig Exp $
+#
+# Test escaping of special characters in the iteration values of a .for loop.
+# These values get expanded later using the :U variable modifier, and this
+# escaping and unescaping must pass all characters and strings effectively
+# unmodified.
+
+.MAKEFLAGS: -df
+
+# Even though the .for loops takes quotes into account when splitting the
+# string into words, the quotes don't need to be balances, as of 2020-12-31.
+# This could be considered a bug.
+ASCII= !"\#$$%&'()*+,-./0-9:;<=>?@A-Z[\]_^a-z{|}~
+
+# XXX: As of 2020-12-31, the '#' is not preserved in the expanded body of
+# the loop since it would not need only the escaping for the :U variable
+# modifier but also the escaping for the line-end comment.
+.for chars in ${ASCII}
+. info ${chars}
+.endfor
+
+# As of 2020-12-31, using 2 backslashes before be '#' would treat the '#'
+# as comment character. Using 3 backslashes doesn't help either since
+# then the situation is essentially the same as with 1 backslash.
+# This means that a '#' sign cannot be passed in the value of a .for loop
+# at all.
+ASCII.2020-12-31= !"\\\#$$%&'()*+,-./0-9:;<=>?@A-Z[\]_^a-z{|}~
+.for chars in ${ASCII.2020-12-31}
+. info ${chars}
+.endfor
+
+# Cover the code in for_var_len.
+#
+# XXX: It is unexpected that the variable V gets expanded in the loop body.
+# The double '$$' should prevent exactly this. Probably nobody was
+# adventurous enough to use literal dollar signs in the values for a .for
+# loop.
+V= value
+VALUES= $$ $${V} $${V:=-with-modifier} $$(V) $$(V:=-with-modifier)
+.for i in ${VALUES}
+. info $i
+.endfor
+
+# Cover the code for nested '{}' in for_var_len.
+#
+# The value of VALUES is not a variable expression. Instead, it is meant to
+# represent dollar, lbrace, "UNDEF:U", backslash, dollar, backslash, dollar,
+# space, nested braces, space, "end}".
+VALUES= $${UNDEF:U\$$\$$ {{}} end}
+# XXX: Where does the '\$$\$$' get converted into a single '\$'?
+.for i in ${VALUES}
+. info $i
+.endfor
+
+# A single trailing dollar doesn't happen in practice.
+# The dollar sign is correctly passed through to the body of the .for loop.
+# There, it is expanded by the .info directive, but even there a trailing
+# dollar sign is kept as-is.
+.for i in ${:U\$}
+. info ${i}
+.endfor
+
+# As of 2020-12-31, the name of the iteration variable can even contain
+# colons, which then affects variable expressions having this exact modifier.
+# This is clearly an unintended side effect of the implementation.
+NUMBERS= one two three
+.for NUMBERS:M*e in replaced
+. info ${NUMBERS} ${NUMBERS:M*e}
+.endfor
+
+# As of 2020-12-31, the name of the iteration variable can contain braces,
+# which gets even more surprising than colons, since it allows to replace
+# sequences of variable expressions. There is no practical use case for
+# this, though.
+BASENAME= one
+EXT= .c
+.for BASENAME}${EXT in replaced
+. info ${BASENAME}${EXT}
+.endfor
+
+# Demonstrate the various ways to refer to the iteration variable.
+i= outer
+i2= two
+i,= comma
+.for i in inner
+. info . $$i: $i
+. info . $${i}: ${i}
+. info . $${i:M*}: ${i:M*}
+. info . $$(i): $(i)
+. info . $$(i:M*): $(i:M*)
+. info . $${i$${:U}}: ${i${:U}}
+. info . $${i\}}: ${i\}} # XXX: unclear why SubstVarLong needs this
+. info . $${i2}: ${i2}
+. info . $${i,}: ${i,}
+. info . adjacent: $i${i}${i:M*}$i
+.endfor
diff --git a/unit-tests/directive-for-lines.exp b/unit-tests/directive-for-lines.exp
new file mode 100644
index 000000000000..7aeaaa4a7002
--- /dev/null
+++ b/unit-tests/directive-for-lines.exp
@@ -0,0 +1,10 @@
+make: "directive-for-lines.mk" line 23: expect 23
+make: "directive-for-lines.mk" line 23: expect 23
+make: "directive-for-lines.mk" line 30: expect 30
+make: "directive-for-lines.mk" line 23: expect 23
+make: "directive-for-lines.mk" line 23: expect 23
+make: "directive-for-lines.mk" line 30: expect 30
+make: no target to make.
+
+make: stopped in unit-tests
+exit status 2
diff --git a/unit-tests/directive-for-lines.mk b/unit-tests/directive-for-lines.mk
new file mode 100644
index 000000000000..96d659426882
--- /dev/null
+++ b/unit-tests/directive-for-lines.mk
@@ -0,0 +1,32 @@
+# $NetBSD: directive-for-lines.mk,v 1.3 2020/12/19 12:40:00 rillig Exp $
+#
+# Tests for the line numbers that are reported in .for loops.
+#
+# Between 2007-01-01 (git 4d3c468f96e1080e, parse.c 1.127) and 2020-12-19
+# (parse.c 1.494), the line numbers for the .info directives and error
+# messages inside .for loops had been wrong since ParseGetLine skipped empty
+# lines, even when collecting the lines for the .for loop body.
+
+.for outer in a b
+
+# comment \
+# continued comment
+
+.for inner in 1 2
+
+# comment \
+# continued comment
+
+VAR= \
+ multi-line
+
+.info expect 23
+
+.endfor
+
+# comment \
+# continued comment
+
+.info expect 30
+
+.endfor
diff --git a/unit-tests/directive-for-null.exp b/unit-tests/directive-for-null.exp
new file mode 100644
index 000000000000..37a7d68925ed
--- /dev/null
+++ b/unit-tests/directive-for-null.exp
@@ -0,0 +1,10 @@
+make: "(stdin)" line 2: Zero byte read from file
+make: "(stdin)" line 2: Unexpected end of file in for loop.
+make: "(stdin)" line 3: Zero byte read from file
+make: Fatal errors encountered -- cannot continue
+make: stopped in unit-tests
+*** Error code 1 (continuing)
+
+Stop.
+make: stopped in unit-tests
+exit status 1
diff --git a/unit-tests/directive-for-null.mk b/unit-tests/directive-for-null.mk
new file mode 100644
index 000000000000..a374f508dd55
--- /dev/null
+++ b/unit-tests/directive-for-null.mk
@@ -0,0 +1,19 @@
+# $NetBSD: directive-for-null.mk,v 1.1 2020/12/19 16:00:17 rillig Exp $
+#
+# Test for parsing a .for loop that accidentally contains a null byte.
+#
+# As of 2020-12-19, there are 3 error messages:
+#
+# make: "(stdin)" line 2: Zero byte read from file
+# make: "(stdin)" line 2: Unexpected end of file in for loop.
+# make: "(stdin)" line 3: Zero byte read from file
+#
+# The one about "end of file" might be misleading but is due to the
+# implementation. On both errors and EOF, ParseGetLine returns NULL.
+#
+# The one about the "zero byte" in line 3 is surprising since the only
+# line that contains a null byte is line 2.
+
+all: .PHONY
+ @printf '%s\n' '.for i in 1 2 3' 'VAR=value' '.endfor' | tr 'l' '\0' \
+ | ${MAKE} -f -
diff --git a/unit-tests/directive-for.exp b/unit-tests/directive-for.exp
index af610cc34edd..bdaf4492baf0 100755
--- a/unit-tests/directive-for.exp
+++ b/unit-tests/directive-for.exp
@@ -16,4 +16,9 @@ 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
+make: "directive-for.mk" line 154: Unknown modifier 'Z'
+make: "directive-for.mk" line 155: XXX: Not reached word1
+make: "directive-for.mk" line 155: XXX: Not reached word3
+make: Fatal errors encountered -- cannot continue
+make: stopped in unit-tests
+exit status 1
diff --git a/unit-tests/directive-for.mk b/unit-tests/directive-for.mk
index 93f0a14f5892..153762509b7a 100755
--- a/unit-tests/directive-for.mk
+++ b/unit-tests/directive-for.mk
@@ -1,4 +1,4 @@
-# $NetBSD: directive-for.mk,v 1.9 2020/11/15 20:20:58 rillig Exp $
+# $NetBSD: directive-for.mk,v 1.10 2020/12/27 09:58:35 rillig Exp $
#
# Tests for the .for directive.
#
@@ -148,5 +148,12 @@ var= outer
. info ${var} ${var:Q} ${var:Q:Q}
.endfor
+
+# XXX: A parse error or evaluation error in the items of the .for loop
+# should skip the whole loop. As of 2020-12-27, the loop is expanded twice.
+.for var in word1 ${:Uword2:Z} word3
+. info XXX: Not reached ${var}
+.endfor
+
all:
@:;
diff --git a/unit-tests/directive-if.exp b/unit-tests/directive-if.exp
index 21a33fe4cfd6..89a394fc0f22 100644
--- a/unit-tests/directive-if.exp
+++ b/unit-tests/directive-if.exp
@@ -1,15 +1,17 @@
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 41: This is not conditional.
make: "directive-if.mk" line 42: if-less else
-make: "directive-if.mk" line 43: Unknown directive "error"
+make: "directive-if.mk" line 43: This is not conditional.
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: "directive-if.mk" line 82: Don't do this, always put a space after a directive.
+make: "directive-if.mk" line 86: Don't do this, always put a space after a directive.
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 3b1d13c7a0c0..b1ad2396b398 100644
--- a/unit-tests/directive-if.mk
+++ b/unit-tests/directive-if.mk
@@ -1,4 +1,4 @@
-# $NetBSD: directive-if.mk,v 1.7 2020/11/15 20:20:58 rillig Exp $
+# $NetBSD: directive-if.mk,v 1.9 2020/12/19 22:33:11 rillig Exp $
#
# Tests for the .if directive.
#
@@ -38,9 +38,9 @@
# are interpreted as ordinary directives, producing the error messages
# "if-less else" and "if-less endif".
.ifx 123
-. error
+.info This is not conditional.
.else
-. error
+.info This is not conditional.
.endif
# Missing condition.
@@ -78,4 +78,12 @@
. error
.endif
+.if(1)
+. info Don't do this, always put a space after a directive.
+.endif
+
+.if!0
+. info Don't do this, always put a space after a directive.
+.endif
+
all:
diff --git a/unit-tests/directive-include.mk b/unit-tests/directive-include.mk
index 120706cef8d7..d36914b25a63 100755
--- a/unit-tests/directive-include.mk
+++ b/unit-tests/directive-include.mk
@@ -1,4 +1,4 @@
-# $NetBSD: directive-include.mk,v 1.4 2020/11/03 17:17:31 rillig Exp $
+# $NetBSD: directive-include.mk,v 1.5 2020/11/21 14:59:11 rillig Exp $
#
# Tests for the .include directive, which includes another file.
@@ -27,5 +27,8 @@
# including a directory technically succeeds, but shouldn't.
#.include "." # directory
+# As of 2020-11-21, anything after the delimiter '"' is ignored.
+.include "/dev/null" and ignore anything in the rest of the line.
+
all:
@:;
diff --git a/unit-tests/directive-info.exp b/unit-tests/directive-info.exp
index 971f417b706a..2652c191460c 100644
--- a/unit-tests/directive-info.exp
+++ b/unit-tests/directive-info.exp
@@ -1,14 +1,15 @@
-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: "directive-info.mk" line 11: begin .info tests
+make: "directive-info.mk" line 12: Unknown directive "inf"
+make: "directive-info.mk" line 13: Missing argument for ".info"
+make: "directive-info.mk" line 14: message
+make: "directive-info.mk" line 15: indented message
+make: "directive-info.mk" line 16: Unknown directive "information"
+make: "directive-info.mk" line 17: Unknown directive "information"
+make: "directive-info.mk" line 22: Missing argument for ".info"
+make: "directive-info.mk" line 23: Missing argument for ".info"
+make: "directive-info.mk" line 26: Unknown directive "info-message"
+make: "directive-info.mk" line 27: no-target: no-source
+make: "directive-info.mk" line 36: expect line 30 for multi-line message
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 bbfc80ea0c9a..5feea0cde565 100644
--- a/unit-tests/directive-info.mk
+++ b/unit-tests/directive-info.mk
@@ -1,16 +1,20 @@
-# $NetBSD: directive-info.mk,v 1.4 2020/11/15 11:57:00 rillig Exp $
+# $NetBSD: directive-info.mk,v 1.8 2020/12/19 22:33:11 rillig Exp $
#
# Tests for the .info directive.
+#
+# Until parse.c 1.502 from 2020-12-19, a missing argument to the directive
+# produced the wrong error message "Unknown directive". Since parse.c 1.503
+# from 2020-12-19, the correct "Missing argument" is produced.
# TODO: Implementation
.info begin .info tests
.inf # misspelled
-.info # oops: message should be "missing parameter"
+.info # "Missing argument"
.info message
.info indented message
.information
-.information message # oops: misspelled
+.information message # Accepted before 2020-12-13 01:07:54.
.info.man: # not a message, but possibly a suffix rule
# Even if lines would have trailing whitespace, this would be trimmed by
@@ -23,5 +27,13 @@
.info no-target: no-source # This is a .info directive, not a dependency.
# See directive.mk for more tests of this kind.
+# Since at least 2002-01-01, the line number that is used in error messages
+# and the .info directives is the number of completely read lines. For the
+# following multi-line directive, this means that the reported line number is
+# the one of the last line, not the first line.
+.info expect line 30 for\
+ multi$\
+ -line message
+
all:
@:;
diff --git a/unit-tests/directive-misspellings.exp b/unit-tests/directive-misspellings.exp
new file mode 100644
index 000000000000..e51d8473b305
--- /dev/null
+++ b/unit-tests/directive-misspellings.exp
@@ -0,0 +1,45 @@
+make: "directive-misspellings.mk" line 12: Unknown directive "dinclud"
+make: "directive-misspellings.mk" line 14: Unknown directive "dincludx"
+make: "directive-misspellings.mk" line 15: .include filename must be delimited by '"' or '<'
+make: "directive-misspellings.mk" line 17: Unknown directive "erro"
+make: "directive-misspellings.mk" line 18: Unknown directive "errox"
+make: "directive-misspellings.mk" line 22: Unknown directive "expor"
+make: "directive-misspellings.mk" line 24: Unknown directive "exporx"
+make: "directive-misspellings.mk" line 25: Unknown directive "exports"
+make: "directive-misspellings.mk" line 27: Unknown directive "export-en"
+make: "directive-misspellings.mk" line 30: Unknown directive "export-environment"
+make: "directive-misspellings.mk" line 32: Unknown directive "export-litera"
+make: "directive-misspellings.mk" line 34: Unknown directive "export-literax"
+make: "directive-misspellings.mk" line 35: Unknown directive "export-literally"
+make: "directive-misspellings.mk" line 37: Unknown directive "-includ"
+make: "directive-misspellings.mk" line 39: Unknown directive "-includx"
+make: "directive-misspellings.mk" line 40: .include filename must be delimited by '"' or '<'
+make: "directive-misspellings.mk" line 42: Unknown directive "includ"
+make: "directive-misspellings.mk" line 43: Could not find file
+make: "directive-misspellings.mk" line 44: Unknown directive "includx"
+make: "directive-misspellings.mk" line 45: .include filename must be delimited by '"' or '<'
+make: "directive-misspellings.mk" line 47: Unknown directive "inf"
+make: "directive-misspellings.mk" line 48: msg
+make: "directive-misspellings.mk" line 49: Unknown directive "infx"
+make: "directive-misspellings.mk" line 50: Unknown directive "infos"
+make: "directive-misspellings.mk" line 52: Unknown directive "sinclud"
+make: "directive-misspellings.mk" line 54: Unknown directive "sincludx"
+make: "directive-misspellings.mk" line 55: .include filename must be delimited by '"' or '<'
+make: "directive-misspellings.mk" line 57: Unknown directive "unde"
+make: "directive-misspellings.mk" line 59: Unknown directive "undex"
+make: "directive-misspellings.mk" line 60: Unknown directive "undefs"
+make: "directive-misspellings.mk" line 62: Unknown directive "unexpor"
+make: "directive-misspellings.mk" line 64: Unknown directive "unexporx"
+make: "directive-misspellings.mk" line 65: Unknown directive "unexports"
+make: "directive-misspellings.mk" line 67: Unknown directive "unexport-en"
+make: "directive-misspellings.mk" line 69: The directive .unexport-env does not take arguments
+make: "directive-misspellings.mk" line 70: Unknown directive "unexport-enx"
+make: "directive-misspellings.mk" line 71: Unknown directive "unexport-envs"
+make: "directive-misspellings.mk" line 73: Unknown directive "warn"
+make: "directive-misspellings.mk" line 74: Unknown directive "warnin"
+make: "directive-misspellings.mk" line 75: warning: msg
+make: "directive-misspellings.mk" line 76: Unknown directive "warninx"
+make: "directive-misspellings.mk" line 77: Unknown directive "warnings"
+make: Fatal errors encountered -- cannot continue
+make: stopped in unit-tests
+exit status 1
diff --git a/unit-tests/directive-misspellings.mk b/unit-tests/directive-misspellings.mk
new file mode 100644
index 000000000000..5f479f03b7f1
--- /dev/null
+++ b/unit-tests/directive-misspellings.mk
@@ -0,0 +1,79 @@
+# $NetBSD: directive-misspellings.mk,v 1.3 2020/12/13 01:10:22 rillig Exp $
+#
+# Tests for misspelled directives.
+#
+# Before 2020-12-12, make didn't catch most of these misspellings. For
+# example, the directive ".exporting" was interpreted as if it were spelled
+# ".export ing", which would export the variable named "ing" if that existed.
+# Another misspelling, as improbable as the others, was that both ".infos" and
+# ".information" were aliases to ".info" since the code for these diagnostic
+# directives just skipped any letters following the "error", "warn" or "info".
+
+.dinclud "file"
+.dinclude "file"
+.dincludx "file"
+.dincludes "file" # XXX: the 's' is not meant to be a filename
+
+.erro msg
+.errox msg
+# no .error since that would exit immediately
+# no .errors since that would exit immediately, even with the typo
+
+.expor varname
+.export varname
+.exporx varname
+.exports varname # Accepted before 2020-12-13 01:07:54.
+
+.export-en # Accepted before 2020-12-13 01:07:54.
+.export-env
+.export-env extra argument # XXX: undetected extra argument
+.export-environment # Accepted before 2020-12-13 01:07:54.
+
+.export-litera varname # Accepted before 2020-12-13 01:07:54.
+.export-literal varname
+.export-literax varname # Accepted before 2020-12-13 01:07:54.
+.export-literally varname # Accepted before 2020-12-13 01:07:54.
+
+.-includ "file"
+.-include "file"
+.-includx "file"
+.-includes "file" # XXX: the 's' is not meant to be a filename
+
+.includ "file"
+.include "file"
+.includx "file"
+.includex "file" # XXX: the 's' is not meant to be a filename
+
+.inf msg
+.info msg
+.infx msg
+.infos msg # Accepted before 2020-12-13 01:07:54.
+
+.sinclud "file"
+.sinclude "file"
+.sincludx "file"
+.sincludes "file" # XXX: the 's' is not meant to be a filename
+
+.unde varname
+.undef varname
+.undex varname
+.undefs varname # Accepted before 2020-12-13 01:07:54.
+
+.unexpor varname
+.unexport varname
+.unexporx varname
+.unexports varname # Accepted before 2020-12-12 18:00:18.
+
+.unexport-en # Accepted before 2020-12-12 18:11:42.
+.unexport-env
+.unexport-env extra argument # Accepted before 2020-12-12 18:00:18.
+.unexport-enx # Accepted before 2020-12-12 18:00:18.
+.unexport-envs # Accepted before 2020-12-12 18:00:18.
+
+.warn msg
+.warnin msg
+.warning msg
+.warninx msg
+.warnings msg # Accepted before 2020-12-13 01:07:54.
+
+all:
diff --git a/unit-tests/directive-undef.exp b/unit-tests/directive-undef.exp
index 303d5a3e2a27..d64cb8b5afe0 100644
--- a/unit-tests/directive-undef.exp
+++ b/unit-tests/directive-undef.exp
@@ -1,4 +1,5 @@
-make: "directive-undef.mk" line 16: Unknown directive "unde"
+make: "directive-undef.mk" line 29: The .undef directive requires an argument
+make: "directive-undef.mk" line 86: Unknown modifier 'Z'
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 c72513a1bf5a..b9a69f733517 100644
--- a/unit-tests/directive-undef.mk
+++ b/unit-tests/directive-undef.mk
@@ -1,21 +1,90 @@
-# $NetBSD: directive-undef.mk,v 1.5 2020/11/03 17:17:31 rillig Exp $
+# $NetBSD: directive-undef.mk,v 1.9 2020/12/22 20:10:21 rillig Exp $
#
# Tests for the .undef directive.
+#
+# See also:
+# directive-misspellings.mk
-# As of 2020-07-28, .undef only undefines the first variable.
-# All further variable names are silently ignored.
-# See parse.c, string literal "undef".
+# Before var.c 1.737 from 2020-12-19, .undef only undefined the first
+# variable, silently skipping all further variable names.
+#
+# Before var.c 1.761 from 2020-12-22, .undef complained about too many
+# arguments.
+#
+# Since var.c 1.761 from 2020-12-22, .undef handles multiple variable names
+# just like the .export directive.
1= 1
2= 2
3= 3
.undef 1 2 3
-.if ${1:U_}${2:U_}${3:U_} != _23
+.if ${1:U_}${2:U_}${3:U_} != ___
. warning $1$2$3
.endif
-.unde # misspelled
-.undef # oops: missing argument
-.undefined # oops: misspelled
+
+# Without any arguments, until var.c 1.736 from 2020-12-19, .undef tried
+# to delete the variable with the empty name, which never exists; see
+# varname-empty.mk. Since var.c 1.737 from 2020-12-19, .undef complains
+# about a missing argument.
+.undef
+
+
+# Trying to delete the variable with the empty name is ok, it just won't
+# ever do anything since that variable is never defined.
+.undef ${:U}
+
+
+# The argument of .undef is first expanded exactly once and then split into
+# words, just like everywhere else. This prevents variables whose names
+# contain spaces or unbalanced 'single' or "double" quotes from being
+# undefined, but these characters do not appear in variables names anyway.
+1= 1
+2= 2
+3= 3
+${:U1 2 3}= one two three
+VARNAMES= 1 2 3
+.undef ${VARNAMES} # undefines the variable "1 2 3"
+.if !defined(${:U1 2 3})
+. error
+.endif
+.if ${1:U_}${2:U_}${3:U_} != "___" # these are still defined
+. error
+.endif
+
+
+# A variable named " " cannot be undefined. There's no practical use case
+# for such variables anyway.
+SPACE= ${:U }
+${SPACE}= space
+.if !defined(${SPACE})
+. error
+.endif
+.undef ${SPACE}
+.if !defined(${SPACE})
+. error
+.endif
+
+
+# A variable named "$" can be undefined since the argument to .undef is
+# expanded exactly once, before being split into words.
+DOLLAR= $$
+${DOLLAR}= dollar
+.if !defined(${DOLLAR})
+. error
+.endif
+.undef ${DOLLAR}
+.if defined(${DOLLAR})
+. error
+.endif
+
+
+# Since var.c 1.762 from 2020-12-22, parse errors in the argument should be
+# properly detected and should stop the .undef directive from doing any work.
+#
+# As of var.c 1.762, this doesn't happen though because the error handling
+# in Var_Parse and Var_Subst is not done properly.
+.undef ${VARNAMES:L:Z}
+
all:
@:;
diff --git a/unit-tests/directive-unexport-env.exp b/unit-tests/directive-unexport-env.exp
index 39a9383953dd..677596ea4aa8 100644
--- a/unit-tests/directive-unexport-env.exp
+++ b/unit-tests/directive-unexport-env.exp
@@ -1 +1,18 @@
-exit status 0
+make: "directive-unexport-env.mk" line 13: Unknown directive "unexport-en"
+make: "directive-unexport-env.mk" line 15: Unknown directive "unexport-environment"
+Global:UT_EXPORTED = value
+Global:UT_UNEXPORTED = value
+Global:.MAKE.EXPORTED = UT_EXPORTED
+make: "directive-unexport-env.mk" line 21: The directive .unexport-env does not take arguments
+Var_Parse: ${.MAKE.EXPORTED:O:u} with VARE_WANTRES
+Applying ${.MAKE.EXPORTED:O} to "UT_EXPORTED" (VARE_WANTRES, none, none)
+Result of ${.MAKE.EXPORTED:O} is "UT_EXPORTED" (VARE_WANTRES, none, none)
+Applying ${.MAKE.EXPORTED:u} to "UT_EXPORTED" (VARE_WANTRES, none, none)
+Result of ${.MAKE.EXPORTED:u} is "UT_EXPORTED" (VARE_WANTRES, none, none)
+Unexporting "UT_EXPORTED"
+Global:delete .MAKE.EXPORTED
+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-unexport-env.mk b/unit-tests/directive-unexport-env.mk
index 637286af0d6b..ef58ae732e6d 100644
--- a/unit-tests/directive-unexport-env.mk
+++ b/unit-tests/directive-unexport-env.mk
@@ -1,12 +1,25 @@
-# $NetBSD: directive-unexport-env.mk,v 1.3 2020/11/03 17:17:31 rillig Exp $
+# $NetBSD: directive-unexport-env.mk,v 1.7 2020/12/12 18:11:42 rillig Exp $
#
# Tests for the .unexport-env directive.
+#
+# Before 2020-12-13, the directive unexport-env wrongly accepted arguments
+# and ignored them.
+#
+# Before 2020-12-13, misspelled directive names like "unexport-environment"
+# were not properly detected.
# TODO: Implementation
-.unexport-en # oops: misspelled
+.unexport-en # misspelled
.unexport-env # ok
-.unexport-environment # oops: misspelled
+.unexport-environment # misspelled
+
+.MAKEFLAGS: -dv
+UT_EXPORTED= value
+UT_UNEXPORTED= value
+.export UT_EXPORTED
+.unexport-env UT_EXPORTED UT_UNEXPORTED
+.MAKEFLAGS: -d0
all:
@:;
diff --git a/unit-tests/directive-unexport.exp b/unit-tests/directive-unexport.exp
index 72b24e7344fc..d59fb4713259 100644
--- a/unit-tests/directive-unexport.exp
+++ b/unit-tests/directive-unexport.exp
@@ -1,8 +1,5 @@
-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:
-make: "directive-unexport.mk" line 26: Unknown directive "unexpor"
-make: Fatal errors encountered -- cannot continue
-make: stopped in unit-tests
-exit status 1
+make: "directive-unexport.mk" line 18: UT_A=a UT_B=b UT_C=c
+make: "directive-unexport.mk" line 19: UT_A UT_B UT_C
+make: "directive-unexport.mk" line 27: UT_A=a UT_B=b UT_C=c
+make: "directive-unexport.mk" line 28:
+exit status 0
diff --git a/unit-tests/directive-unexport.mk b/unit-tests/directive-unexport.mk
index 3ba4a1b1f307..efc103efedf6 100644
--- a/unit-tests/directive-unexport.mk
+++ b/unit-tests/directive-unexport.mk
@@ -1,8 +1,12 @@
-# $NetBSD: directive-unexport.mk,v 1.5 2020/11/03 17:17:31 rillig Exp $
+# $NetBSD: directive-unexport.mk,v 1.7 2020/12/13 01:07:54 rillig Exp $
#
# Tests for the .unexport directive.
-
-# TODO: Implementation
+#
+# Before 2020-12-13, misspelled directives like ".unexporting" or
+# ".unexport-en" had not been detected properly.
+#
+# See also:
+# directive-misspellings.mk
# First, export 3 variables.
UT_A= a
@@ -23,9 +27,7 @@ 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 630285fd3612..b08b3207392c 100644
--- a/unit-tests/directive-warning.exp
+++ b/unit-tests/directive-warning.exp
@@ -1,11 +1,11 @@
-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: "directive-warning.mk" line 11: Unknown directive "warn"
+make: "directive-warning.mk" line 12: Unknown directive "warn"
+make: "directive-warning.mk" line 13: Unknown directive "warnin"
+make: "directive-warning.mk" line 14: Unknown directive "warnin"
+make: "directive-warning.mk" line 15: Missing argument for ".warning"
+make: "directive-warning.mk" line 16: warning: message
+make: "directive-warning.mk" line 17: Unknown directive "warnings"
+make: "directive-warning.mk" line 18: Unknown directive "warnings"
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 75560aa9e4df..d586c9fed170 100644
--- a/unit-tests/directive-warning.mk
+++ b/unit-tests/directive-warning.mk
@@ -1,6 +1,10 @@
-# $NetBSD: directive-warning.mk,v 1.3 2020/11/03 17:17:31 rillig Exp $
+# $NetBSD: directive-warning.mk,v 1.6 2020/12/19 22:33:11 rillig Exp $
#
# Tests for the .warning directive.
+#
+# Until parse.c 1.502 from 2020-12-19, a missing argument to the directive
+# produced the wrong error message "Unknown directive". Since parse.c 1.503
+# from 2020-12-19, the correct "Missing argument" is produced.
# TODO: Implementation
@@ -8,10 +12,10 @@
.warn message # misspelled
.warnin # misspelled
.warnin message # misspelled
-.warning # oops: should be "missing argument"
+.warning # "Missing argument"
.warning message # ok
.warnings # misspelled
-.warnings messages # oops
+.warnings messages # Accepted before 2020-12-13 01:07:54.
all:
@:;
diff --git a/unit-tests/jobs-error-indirect.exp b/unit-tests/jobs-error-indirect.exp
new file mode 100644
index 000000000000..5c5a3801f4f6
--- /dev/null
+++ b/unit-tests/jobs-error-indirect.exp
@@ -0,0 +1,8 @@
+false
+*** [indirect] Error code 1
+
+make: stopped in unit-tests
+1 error
+
+make: stopped in unit-tests
+exit status 2
diff --git a/unit-tests/jobs-error-indirect.mk b/unit-tests/jobs-error-indirect.mk
new file mode 100644
index 000000000000..55e193d9b18c
--- /dev/null
+++ b/unit-tests/jobs-error-indirect.mk
@@ -0,0 +1,21 @@
+# $NetBSD: jobs-error-indirect.mk,v 1.1 2020/12/01 17:50:04 rillig Exp $
+#
+# Ensure that in jobs mode, when a command fails, the current directory is
+# printed, to aid in debugging.
+#
+# XXX: This test is run without the -k flag, which prints "stopped in" twice.
+# Why?
+#
+# This particular case is not the cause for the PRs, but it is very close.
+#
+# https://gnats.netbsd.org/55578
+# https://gnats.netbsd.org/55832
+#
+#
+
+.MAKEFLAGS: -j1
+
+all: .PHONY indirect
+
+indirect: .PHONY
+ false
diff --git a/unit-tests/jobs-error-nested-make.exp b/unit-tests/jobs-error-nested-make.exp
new file mode 100644
index 000000000000..88c32ab8d1f6
--- /dev/null
+++ b/unit-tests/jobs-error-nested-make.exp
@@ -0,0 +1,11 @@
+make -f jobs-error-nested-make.mk nested
+false
+*** [nested] Error code 1
+
+make: stopped in unit-tests
+1 error
+
+make: stopped in unit-tests
+
+make: stopped in unit-tests
+exit status 2
diff --git a/unit-tests/jobs-error-nested-make.mk b/unit-tests/jobs-error-nested-make.mk
new file mode 100644
index 000000000000..8cccf7df6a52
--- /dev/null
+++ b/unit-tests/jobs-error-nested-make.mk
@@ -0,0 +1,20 @@
+# $NetBSD: jobs-error-nested-make.mk,v 1.2 2021/01/07 18:11:23 sjg Exp $
+#
+# Ensure that in jobs mode, when a command fails, the current directory is
+# printed, to aid in debugging, even if the target is marked as .MAKE.
+# This marker is typically used for targets like 'all' that descend into
+# subdirectories.
+#
+# XXX: In case of .MAKE targets, the "stopped if" output has been suppressed
+# since job.c 1.198 from 2020-06-19.
+#
+# https://gnats.netbsd.org/55578
+# https://gnats.netbsd.org/55832
+
+.MAKEFLAGS: -j1
+
+all: .PHONY .MAKE
+ ${MAKE} -f ${MAKEFILE} nested
+
+nested: .PHONY
+ false
diff --git a/unit-tests/jobs-error-nested.exp b/unit-tests/jobs-error-nested.exp
new file mode 100644
index 000000000000..f96b5d016777
--- /dev/null
+++ b/unit-tests/jobs-error-nested.exp
@@ -0,0 +1,15 @@
+make -f jobs-error-nested.mk nested
+false
+*** [nested] Error code 1
+
+make: stopped in unit-tests
+1 error
+
+make: stopped in unit-tests
+*** [all] Error code 2
+
+make: stopped in unit-tests
+1 error
+
+make: stopped in unit-tests
+exit status 2
diff --git a/unit-tests/jobs-error-nested.mk b/unit-tests/jobs-error-nested.mk
new file mode 100644
index 000000000000..879bfff89984
--- /dev/null
+++ b/unit-tests/jobs-error-nested.mk
@@ -0,0 +1,20 @@
+# $NetBSD: jobs-error-nested.mk,v 1.1 2020/12/01 17:50:04 rillig Exp $
+#
+# Ensure that in jobs mode, when a command fails, the current directory is
+# printed, to aid in debugging.
+#
+# XXX: This test is run without the -k flag, which prints "stopped in" 4
+# times. Why?
+#
+# This particular case is not the cause for the PRs, but it is very close.
+#
+# https://gnats.netbsd.org/55578
+# https://gnats.netbsd.org/55832
+
+.MAKEFLAGS: -j1
+
+all: .PHONY
+ ${MAKE} -f ${MAKEFILE} nested
+
+nested: .PHONY
+ false
diff --git a/unit-tests/make-exported.mk b/unit-tests/make-exported.mk
index db7f09dc490f..58cb15183b8d 100755
--- a/unit-tests/make-exported.mk
+++ b/unit-tests/make-exported.mk
@@ -22,4 +22,4 @@ UT_VAR= ${UNEXPANDED}
.MAKE.EXPORTED= -literal UT_VAR
all:
- @env | sort | grep -E '^UT_|make-exported-value' || true
+ @env | sort | egrep '^UT_|make-exported-value' || true
diff --git a/unit-tests/meta-cmd-cmp.exp b/unit-tests/meta-cmd-cmp.exp
new file mode 100644
index 000000000000..bfc52123e3b2
--- /dev/null
+++ b/unit-tests/meta-cmd-cmp.exp
@@ -0,0 +1,37 @@
+one:
+Building .meta-cmd-cmp.cmp
+Building .meta-cmd-cmp.nocmp
+Building .meta-cmd-cmp.cmp2
+This line not compared FLAGS=
+Skipping meta for .END: .SPECIAL
+two:
+`.meta-cmd-cmp.cmp' is up to date.
+`.meta-cmd-cmp.nocmp' is up to date.
+.meta-cmd-cmp.cmp2.meta: 3: cannot compare command using .OODATE
+`.meta-cmd-cmp.cmp2' is up to date.
+Skipping meta for .END: .SPECIAL
+change1:
+.meta-cmd-cmp.cmp.meta: 2: a build command has changed
+@echo FLAGS= > .meta-cmd-cmp.cmp
+vs
+@echo FLAGS=changed > .meta-cmd-cmp.cmp
+Building .meta-cmd-cmp.cmp
+`.meta-cmd-cmp.nocmp' is up to date.
+.meta-cmd-cmp.cmp2.meta: 3: cannot compare command using .OODATE
+`.meta-cmd-cmp.cmp2' is up to date.
+Skipping meta for .END: .SPECIAL
+change2:
+.meta-cmd-cmp.cmp.meta: 2: a build command has changed
+@echo FLAGS=changed > .meta-cmd-cmp.cmp
+vs
+@echo FLAGS= > .meta-cmd-cmp.cmp
+Building .meta-cmd-cmp.cmp
+`.meta-cmd-cmp.nocmp' is up to date.
+.meta-cmd-cmp.cmp2.meta: 2: a build command has changed
+@echo FLAGS2= > .meta-cmd-cmp.cmp2
+vs
+@echo FLAGS2=changed > .meta-cmd-cmp.cmp2
+Building .meta-cmd-cmp.cmp2
+This line not compared FLAGS=
+Skipping meta for .END: .SPECIAL
+exit status 0
diff --git a/unit-tests/meta-cmd-cmp.mk b/unit-tests/meta-cmd-cmp.mk
new file mode 100644
index 000000000000..a1c0f7c10063
--- /dev/null
+++ b/unit-tests/meta-cmd-cmp.mk
@@ -0,0 +1,52 @@
+# $NetBSD: meta-cmd-cmp.mk,v 1.2 2020/12/05 22:51:34 sjg Exp $
+#
+# Tests META_MODE command line comparison
+#
+
+.MAIN: all
+
+.MAKE.MODE= meta verbose silent=yes curdirok=yes
+tf:= .${.PARSEFILE:R}
+
+.if ${.TARGETS:Nall} == ""
+all: prep one two change1 change2 post
+
+CLEANFILES= ${tf}*
+
+prep post: .PHONY
+ @rm -f ${CLEANFILES}
+
+.endif
+
+FLAGS?=
+FLAGS2?=
+
+tests= ${tf}.cmp ${tf}.nocmp ${tf}.cmp2
+
+${tf}.cmp:
+ @echo FLAGS=${FLAGS:Uempty} > $@
+
+${tf}.nocmp: .NOMETA_CMP
+ @echo FLAGS=${FLAGS:Uempty} > $@
+
+# a line containing ${.OODATE} will not be compared
+# this allows the trick below
+${tf}.cmp2:
+ @echo FLAGS2=${FLAGS2:Uempty} > $@
+ @echo This line not compared FLAGS=${FLAGS:Uempty} ${.OODATE:MNOMETA_CMP}
+
+# these do the same
+one two: .PHONY
+ @echo $@:
+ @${.MAKE} -dM -r -C ${.CURDIR} -f ${MAKEFILE} ${tests}
+
+change1: .PHONY
+ @echo $@:
+ @${.MAKE} -dM -r -C ${.CURDIR} -f ${MAKEFILE} FLAGS=changed ${tests}
+
+change2: .PHONY
+ @echo $@:
+ @${.MAKE} -dM -r -C ${.CURDIR} -f ${MAKEFILE} FLAGS2=changed ${tests}
+
+# don't let gcov mess up the results
+.MAKE.META.IGNORE_PATTERNS+= *.gcda
diff --git a/unit-tests/modmisc.exp b/unit-tests/modmisc.exp
index 94f131052fdc..10475e65ee0f 100644
--- a/unit-tests/modmisc.exp
+++ b/unit-tests/modmisc.exp
@@ -1,4 +1,3 @@
-make: Unknown modifier '$'
path=':/bin:/tmp::/:.:/no/such/dir:.'
path='/bin:/tmp:/:/no/such/dir'
path='/bin:/tmp:/:/no/such/dir'
diff --git a/unit-tests/modmisc.mk b/unit-tests/modmisc.mk
index 64a84ce0dadd..9ace35c15162 100644
--- a/unit-tests/modmisc.mk
+++ b/unit-tests/modmisc.mk
@@ -1,4 +1,4 @@
-# $NetBSD: modmisc.mk,v 1.51 2020/11/15 20:20:58 rillig Exp $
+# $NetBSD: modmisc.mk,v 1.52 2020/12/20 19:29:06 rillig Exp $
#
# miscellaneous modifier tests
@@ -63,30 +63,3 @@ mod-quote:
# 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 expression.
-.if ${value:L:${:US}${:U,value,replacement,}} != "S,value,replacement,}"
-. warning unexpected
-.endif
-
-# Adding another level of indirection (the 2 nested :U expressions) helps.
-.if ${value:L:${:U${:US}${:U,value,replacement,}}} != "replacement"
-. warning unexpected
-.endif
-
-# Multiple indirect modifiers can be applied one after another as long as
-# they are separated with colons.
-.if ${value:L:${:US,a,A,}:${:US,e,E,}} != "vAluE"
-. warning unexpected
-.endif
-
-# An indirect variable that evaluates to the empty string is allowed though.
-# This makes it possible to define conditional modifiers, like this:
-#
-# M.little-endian= S,1234,4321,
-# M.big-endian= # none
-.if ${value:L:${:Dempty}S,a,A,} != "vAlue"
-. warning unexpected
-.endif
-
diff --git a/unit-tests/opt-chdir.exp b/unit-tests/opt-chdir.exp
index d20f9eb2f07b..d9759cf9ed8b 100644
--- a/unit-tests/opt-chdir.exp
+++ b/unit-tests/opt-chdir.exp
@@ -1,6 +1,6 @@
make: chdir /./././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././: File name too long
-*** Error code 1 (ignored)
+*** Error code 2 (ignored)
cwd: /
make: chdir /nonexistent: No such file or directory
-*** Error code 1 (ignored)
+*** Error code 2 (ignored)
exit status 0
diff --git a/unit-tests/opt-debug-errors.exp b/unit-tests/opt-debug-errors.exp
index dd13e66526b0..859a431f23bb 100644
--- a/unit-tests/opt-debug-errors.exp
+++ b/unit-tests/opt-debug-errors.exp
@@ -31,4 +31,7 @@ word1 word2
*** Failed command: echo 'word1' 'word2'; false
*** Error code 1 (continuing)
`all' not remade because of errors.
-exit status 0
+
+Stop.
+make: stopped in unit-tests
+exit status 1
diff --git a/unit-tests/opt-debug-graph1.exp b/unit-tests/opt-debug-graph1.exp
index db8367c6f250..45f403f75f1b 100644
--- a/unit-tests/opt-debug-graph1.exp
+++ b/unit-tests/opt-debug-graph1.exp
@@ -13,5 +13,41 @@
# Files that are only sources:
# unmade-sources [unmade-sources]
# unmade-silent-source [unmade-silent-source] .SILENT
+#*** Global Variables:
+.ALLTARGETS = all made-target made-target-no-sources made-source unmade-target unmade-sources unmade-silent-source unmade-target-no-sources
+.CURDIR = <curdir>
+.INCLUDES =
+.LIBS =
+.MAKE = <details omitted>
+.MAKE.DEPENDFILE = <details omitted>
+.MAKE.GID = <details omitted>
+.MAKE.LEVEL = <details omitted>
+.MAKE.MAKEFILES = <details omitted>
+.MAKE.MAKEFILE_PREFERENCE = <details omitted>
+.MAKE.OS = <details omitted>
+.MAKE.PID = <details omitted>
+.MAKE.PPID = <details omitted>
+.MAKE.UID = <details omitted>
+.MAKEFLAGS = -r -k -d g1
+.MAKEOVERRIDES =
+.OBJDIR = <curdir>
+.PATH = . <curdir>
+.TARGETS =
+.newline =
+
+MACHINE = <details omitted>
+MACHINE_ARCH = <details omitted>
+MAKE = <details omitted>
+MFLAGS = -r -k -d g1
+#*** Command-line Variables:
+.MAKE.LEVEL.ENV = MAKELEVEL
+
+#*** Directory Cache:
+# Stats: 0 hits 2 misses 0 near misses 0 losers (0%)
+# refs hits directory
+# 1 0 <curdir>
+# 1 0 .
+
+#*** Suffixes:
#*** Transformations:
exit status 0
diff --git a/unit-tests/opt-debug-jobs.exp b/unit-tests/opt-debug-jobs.exp
index 0431867756a1..e79d8e94a952 100644
--- a/unit-tests/opt-debug-jobs.exp
+++ b/unit-tests/opt-debug-jobs.exp
@@ -13,10 +13,10 @@ echo ": 'single' and \"double\" quotes"
{ sleep 1
} || exit $?
Running all
- Command: sh
+ Command: <shell>
JobExec(all): pid <pid> added to jobs table
job table @ job started
-job 0, status 3, flags 0, pid <pid>
+job 0, status 3, flags ---, pid <pid>
: expanded expression
: variable
: 'single' and "double" quotes
diff --git a/unit-tests/opt-debug-lint.exp b/unit-tests/opt-debug-lint.exp
index b0be460848fd..f2123f20e37f 100644
--- a/unit-tests/opt-debug-lint.exp
+++ b/unit-tests/opt-debug-lint.exp
@@ -2,8 +2,7 @@ make: "opt-debug-lint.mk" line 19: Variable "X" is undefined
make: "opt-debug-lint.mk" line 41: Variable "UNDEF" is undefined
make: "opt-debug-lint.mk" line 61: Missing delimiter ':' after modifier "L"
make: "opt-debug-lint.mk" line 61: Missing delimiter ':' after modifier "P"
-make: "opt-debug-lint.mk" line 67: Missing delimiter ':' after indirect modifier "${:UL}"
-make: Unknown modifier '$'
+make: "opt-debug-lint.mk" line 69: Unknown modifier '$'
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/unit-tests/opt-debug-lint.mk b/unit-tests/opt-debug-lint.mk
index 9075243208b1..bb1b38feb717 100644
--- a/unit-tests/opt-debug-lint.mk
+++ b/unit-tests/opt-debug-lint.mk
@@ -1,4 +1,4 @@
-# $NetBSD: opt-debug-lint.mk,v 1.11 2020/10/24 08:50:17 rillig Exp $
+# $NetBSD: opt-debug-lint.mk,v 1.12 2020/12/20 19:10:53 rillig Exp $
#
# Tests for the -dL command line option, which runs additional checks
# to catch common mistakes, such as unclosed variable expressions.
@@ -62,11 +62,20 @@ ${UNDEF}: ${UNDEF}
. error
.endif
-# Since 2020-10-03, in lint mode the variable modifier must be separated
-# by colons. See varparse-mod.mk.
+# Between 2020-10-03 and var.c 1.752 from 2020-12-20, in lint mode the
+# variable modifier had to be separated by colons. This was wrong though
+# since make always fell back trying to parse the indirect modifier as a
+# SysV modifier.
.if ${value:${:UL}PL} != "LPL}" # FIXME: "LPL}" is unexpected here.
. error ${value:${:UL}PL}
.endif
+# Typically, an indirect modifier is followed by a colon or the closing
+# brace. This one isn't, therefore make falls back to parsing it as the SysV
+# modifier ":lue=lid".
+.if ${value:L:${:Ulue}=${:Ulid}} != "valid"
+. error
+.endif
+
all:
@:;
diff --git a/unit-tests/opt-file.exp b/unit-tests/opt-file.exp
index 39a9383953dd..76a832949aca 100644
--- a/unit-tests/opt-file.exp
+++ b/unit-tests/opt-file.exp
@@ -1 +1,12 @@
-exit status 0
+value
+value
+line-with-trailing-whitespace
+make: "(stdin)" line 1: Zero byte read from file
+make: Fatal errors encountered -- cannot continue
+make: stopped in unit-tests
+*** Error code 1 (continuing)
+`all' not remade because of errors.
+
+Stop.
+make: stopped in unit-tests
+exit status 1
diff --git a/unit-tests/opt-file.mk b/unit-tests/opt-file.mk
index 86bc100bebc2..3ab8ef4e3c7d 100644
--- a/unit-tests/opt-file.mk
+++ b/unit-tests/opt-file.mk
@@ -1,8 +1,105 @@
-# $NetBSD: opt-file.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+# $NetBSD: opt-file.mk,v 1.11 2020/12/22 08:57:23 rillig Exp $
#
# Tests for the -f command line option.
# TODO: Implementation
+all: .PHONY
+all: file-ending-in-backslash
+all: file-ending-in-backslash-mmap
+all: line-with-trailing-whitespace
+all: file-containing-null-byte
+
+# Passing '-' as the filename reads from stdin. This is unusual but possible.
+#
+# In the unlikely case where a file ends in a backslash instead of a newline,
+# that backslash is trimmed. See ParseGetLine.
+#
+# make-2014.01.01.00.00.00 invoked undefined behavior, reading text from
+# outside of the file buffer.
+#
+# printf '%s' 'VAR=value\' \
+# | MALLOC_OPTIONS=JA make-2014.01.01.00.00.00 -r -f - -V VAR -dA 2>&1 \
+# | less
+#
+# The debug output shows how make happily uses freshly allocated memory (the
+# <A5>) and already freed memory ('Z').
+#
+# ParseReadLine (1): 'VAR=value\<A5><A5><A5><A5><A5><A5>'
+# Global:VAR = value\<A5><A5><A5><A5><A5><A5>value\<A5><A5><A5><A5><A5><A5>
+# ParseReadLine (2): 'alue\<A5><A5><A5><A5><A5><A5>'
+# ParseDoDependency(alue\<A5><A5><A5><A5><A5><A5>)
+# make-2014.01.01.00.00.00: "(stdin)" line 2: Need an operator
+# ParseReadLine (3): '<A5><A5><A5>ZZZZZZZZZZZZZZZZ'
+# ParseDoDependency(<A5><A5><A5>ZZZZZZZZZZZZZZZZ)
+#
+file-ending-in-backslash: .PHONY
+ @printf '%s' 'VAR=value\' \
+ | ${MAKE} -r -f - -V VAR
+
+# Between parse.c 1.170 from 2010-12-25 and parse.c 1.511 from 2020-12-22,
+# there was an out-of-bounds write in ParseGetLine, where line_end pointed at
+# the end of the allocated buffer, in the special case where loadedfile_mmap
+# had not added the final newline character.
+file-ending-in-backslash-mmap: .PHONY
+ @printf '%s' 'VAR=value\' > opt-file-backslash
+ @${MAKE} -r -f opt-file-backslash -V VAR
+ @rm opt-file-backslash
+
+# Since parse.c 1.511 from 2020-12-22, an assertion in ParseGetLine failed
+# for lines that contained trailing whitespace. Worked around in parse.c
+# 1.513, properly fixed in parse.c 1.514.
+line-with-trailing-whitespace: .PHONY
+ @printf '%s' 'VAR=$@ ' > opt-file-trailing-whitespace
+ @${MAKE} -r -f opt-file-trailing-whitespace -V VAR
+ @rm opt-file-trailing-whitespace
+
+# If a makefile contains null bytes, it is an error. Throughout the history
+# of make, the behavior has changed several times, sometimes intentionally,
+# sometimes by accident.
+#
+# echo 'VAR=value' | tr 'l' '\0' > zero-byte.in
+# printf '%s\n' 'all:' ': VAR=${VAR:Q}' >> zero-byte.in
+#
+# for year in $(seq 2003 2020); do
+# echo $year:
+# make-$year.01.01.00.00.00 -r -f zero-byte.in
+# echo "exit status $?"
+# echo
+# done 2>&1 \
+# | sed "s,$PWD/,.,"
+#
+# This program generated the following output:
+#
+# 2003 to 2007:
+# exit status 0
+#
+# 2008 to 2010:
+# make: "zero-byte.in" line 1: Zero byte read from file
+# make: Fatal errors encountered -- cannot continue
+#
+# make: stopped in .
+# exit status 1
+#
+# 2011 to 2013:
+# make: no target to make.
+#
+# make: stopped in .
+# exit status 2
+#
+# 2014 to 2020-12-06:
+# make: "zero-byte.in" line 1: warning: Zero byte read from file, skipping rest of line.
+# exit status 0
+#
+# Since 2020-12-07:
+# make: "zero-byte.in" line 1: Zero byte read from file
+# make: Fatal errors encountered -- cannot continue
+# make: stopped in .
+# exit status 1
+file-containing-null-byte: .PHONY
+ @printf '%s\n' 'VAR=value' 'VAR2=VALUE2' \
+ | tr 'l' '\0' \
+ | ${MAKE} -r -f - -V VAR -V VAR2
+
all:
- @:;
+ : Making ${.TARGET}
diff --git a/unit-tests/opt-jobs-no-action.exp b/unit-tests/opt-jobs-no-action.exp
new file mode 100644
index 000000000000..8556fa3bf943
--- /dev/null
+++ b/unit-tests/opt-jobs-no-action.exp
@@ -0,0 +1,61 @@
+begin explain
+# .echoOff
+# .echoTmpl
+echo "false regular"
+# .runChkTmpl
+{ false regular
+} || exit $?
+# .echoOn
+# .runChkTmpl
+{ : silent
+} || exit $?
+# .echoOn
+false ignore-errors
+echo run despite the -n option
+run despite the -n option
+end explain
+
+begin combined
+
+silent=no always=no ignerr=no
+# .echoOff
+# .echoTmpl
+echo "echo running"
+# .runChkTmpl
+{ echo running
+} || exit $?
+# .echoOn
+
+silent=no always=no ignerr=yes
+echo running; false
+
+silent=no always=yes ignerr=no
+echo running
+running
+
+silent=no always=yes ignerr=yes
+echo running; false
+running
+*** Error code 1 (ignored)
+
+silent=yes always=no ignerr=no
+# .runChkTmpl
+{ echo running
+} || exit $?
+# .echoOn
+
+silent=yes always=no ignerr=yes
+echo running; false
+# .echoOn
+
+silent=yes always=yes ignerr=no
+echo running
+running
+
+silent=yes always=yes ignerr=yes
+echo running; false
+running
+*** Error code 1 (ignored)
+
+end combined
+exit status 0
diff --git a/unit-tests/opt-jobs-no-action.mk b/unit-tests/opt-jobs-no-action.mk
new file mode 100644
index 000000000000..a75fc38cf2fa
--- /dev/null
+++ b/unit-tests/opt-jobs-no-action.mk
@@ -0,0 +1,102 @@
+# $NetBSD: opt-jobs-no-action.mk,v 1.8 2020/12/10 23:54:41 rillig Exp $
+#
+# Tests for the combination of the options -j and -n, which prints the
+# commands instead of actually running them.
+#
+# The format of the output differs from the output of only the -n option,
+# without the -j. This is because all this code is implemented twice, once
+# in compat.c and once in job.c.
+#
+# See also:
+# opt-jobs.mk
+# The corresponding tests without the -n option
+# opt-no-action-combined.mk
+# The corresponding tests without the -j option
+
+.MAKEFLAGS: -j1 -n
+
+# Change the templates for running the commands in jobs mode, to make it
+# easier to see what actually happens.
+#
+# The shell attributes are handled by Job_ParseShell.
+# The shell attributes 'quiet' and 'echo' don't need a trailing newline,
+# this is handled by the [0] != '\0' checks in Job_ParseShell.
+# The '\#' is handled by ParseGetLine.
+# The '\n' is handled by Str_Words in Job_ParseShell.
+# The '$$' is handled by Var_Subst in ParseDependency.
+.SHELL: \
+ name=sh \
+ path=${.SHELL} \
+ quiet="\# .echoOff" \
+ echo="\# .echoOn" \
+ filter="\# .noPrint\n" \
+ check="\# .echoTmpl\n""echo \"%s\"\n" \
+ ignore="\# .runIgnTmpl\n""%s\n" \
+ errout="\# .runChkTmpl\n""{ %s \n} || exit $$?\n"
+
+all: explained combined
+.ORDER: explained combined
+
+# Explain the most basic cases in detail.
+explained: .PHONY
+ @+echo hide-from-output 'begin explain'
+
+ # The following command is regular, it is printed twice:
+ # - first using the template shell.echoTmpl,
+ # - then using the template shell.runChkTmpl.
+ false regular
+
+ # The following command is silent, it is printed once, using the
+ # template shell.runChkTmpl.
+ @: silent
+
+ # The following command ignores errors, it is printed once, using
+ # the default template for cmdTemplate, which is "%s\n".
+ # XXX: Why is it not printed using shell.echoTmpl as well?
+ # XXX: The '-' should not influence the echoing of the command.
+ -false ignore-errors
+
+ # The following command ignores the -n command line option, it is
+ # not handled by the Job module but by the Compat module, see the
+ # '!silent' in Compat_RunCommand.
+ +echo run despite the -n option
+
+ @+echo hide-from-output 'end explain'
+ @+echo hide-from-output
+
+
+# Test all combinations of the 3 RunFlags.
+#
+# TODO: Closely inspect the output whether it makes sense.
+# XXX: silent=no always=no ignerr={no,yes} should be almost the same.
+#
+SILENT.no= # none
+SILENT.yes= @
+ALWAYS.no= # none
+ALWAYS.yes= +
+IGNERR.no= echo running
+IGNERR.yes= -echo running; false
+#
+combined: combined-begin
+
+combined-begin: .PHONY
+ @+echo hide-from-output 'begin combined'
+ @+echo hide-from-output
+
+.for silent in no yes
+. for always in no yes
+. for ignerr in no yes
+. for target in combined-silent-${silent}-always-${always}-ignerr-${ignerr}
+combined: .WAIT ${target} .WAIT
+${target}: .PHONY
+ @+echo hide-from-output silent=${silent} always=${always} ignerr=${ignerr}
+ ${SILENT.${silent}}${ALWAYS.${always}}${IGNERR.${ignerr}}
+ @+echo hide-from-output
+. endfor
+. endfor
+. endfor
+.endfor
+
+combined: combined-end
+combined-end: .PHONY
+ @+echo hide-from-output 'end combined'
diff --git a/unit-tests/opt-keep-going-multiple.exp b/unit-tests/opt-keep-going-multiple.exp
new file mode 100644
index 000000000000..6d1bec18977b
--- /dev/null
+++ b/unit-tests/opt-keep-going-multiple.exp
@@ -0,0 +1,9 @@
+false fail1
+*** Error code 1 (continuing)
+false fail2
+*** Error code 1 (continuing)
+true succeed
+
+Stop.
+make: stopped in unit-tests
+exit status 1
diff --git a/unit-tests/opt-keep-going-multiple.mk b/unit-tests/opt-keep-going-multiple.mk
new file mode 100644
index 000000000000..cc08ccfb82c3
--- /dev/null
+++ b/unit-tests/opt-keep-going-multiple.mk
@@ -0,0 +1,21 @@
+# $NetBSD: opt-keep-going-multiple.mk,v 1.1 2020/12/07 01:32:04 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.
+#
+# Until 2020-12-07, the exit status of make depended only on the last of the
+# main targets. Even if the first few targets could not be made, make
+# nevertheless exited with status 0.
+
+.MAKEFLAGS: -k
+.MAKEFLAGS: fail1 fail2 succeed
+
+fail1 fail2: .PHONY
+ false ${.TARGET}
+
+succeed: .PHONY
+ true ${.TARGET}
+
+.END:
+ : The end.
diff --git a/unit-tests/opt-keep-going.exp b/unit-tests/opt-keep-going.exp
index cdad54ac24f8..2dbeb9927a30 100644
--- a/unit-tests/opt-keep-going.exp
+++ b/unit-tests/opt-keep-going.exp
@@ -3,4 +3,7 @@ dependency 1
other 1
*** Error code 1 (continuing)
`all' not remade because of errors.
-exit status 0
+
+Stop.
+make: stopped in unit-tests
+exit status 1
diff --git a/unit-tests/opt-keep-going.mk b/unit-tests/opt-keep-going.mk
index 72f605246712..8a5e079ef406 100644
--- a/unit-tests/opt-keep-going.mk
+++ b/unit-tests/opt-keep-going.mk
@@ -1,8 +1,12 @@
-# $NetBSD: opt-keep-going.mk,v 1.5 2020/11/09 20:50:56 rillig Exp $
+# $NetBSD: opt-keep-going.mk,v 1.6 2020/12/07 00:53:30 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.
+#
+# Until 2020-12-07, if a dependency of the main target failed, the exit
+# status was nevertheless 0, which was wrong since the main targets could
+# not be made. This was only wrong in -k mode combined with compat mode.
.MAKEFLAGS: -d0 # switch stdout to being line-buffered
.MAKEFLAGS: -k
diff --git a/unit-tests/opt-no-action-runflags.exp b/unit-tests/opt-no-action-runflags.exp
new file mode 100644
index 000000000000..33b311228927
--- /dev/null
+++ b/unit-tests/opt-no-action-runflags.exp
@@ -0,0 +1,34 @@
+begin combined
+
+silent=no always=no ignerr=no
+echo running
+
+silent=no always=no ignerr=yes
+echo running; false
+
+silent=no always=yes ignerr=no
+echo running
+running
+
+silent=no always=yes ignerr=yes
+echo running; false
+running
+*** Error code 1 (ignored)
+
+silent=yes always=no ignerr=no
+echo running
+
+silent=yes always=no ignerr=yes
+echo running; false
+
+silent=yes always=yes ignerr=no
+echo running
+running
+
+silent=yes always=yes ignerr=yes
+echo running; false
+running
+*** Error code 1 (ignored)
+
+end combined
+exit status 0
diff --git a/unit-tests/opt-no-action-runflags.mk b/unit-tests/opt-no-action-runflags.mk
new file mode 100644
index 000000000000..61ae7b2bf319
--- /dev/null
+++ b/unit-tests/opt-no-action-runflags.mk
@@ -0,0 +1,32 @@
+# $NetBSD: opt-no-action-runflags.mk,v 1.1 2020/12/09 07:57:52 rillig Exp $
+#
+# Tests for the -n command line option, which runs almost no commands,
+# combined with the RunFlags '@', '-', '+' for individual commands.
+#
+# See also:
+# opt-jobs-no-action.mk
+# The corresponding test with the -j option
+
+.MAKEFLAGS: -n
+
+all: .PHONY combined
+
+SILENT.no= # none
+SILENT.yes= @
+ALWAYS.no= # none
+ALWAYS.yes= +
+IGNERR.no= echo running
+IGNERR.yes= -echo running; false
+#
+combined: .PHONY
+ @+echo hide-from-output 'begin $@'; echo
+.for silent in no yes
+. for always in no yes
+. for ignerr in no yes
+ @+echo hide-from-output silent=${silent} always=${always} ignerr=${ignerr}
+ ${SILENT.${silent}}${ALWAYS.${always}}${IGNERR.${ignerr}}
+ @+echo hide-from-output
+. endfor
+. endfor
+.endfor
+ @+echo hide-from-output 'end $@'
diff --git a/unit-tests/opt.exp b/unit-tests/opt.exp
index 11344ae0c359..3c96cf25025f 100644
--- a/unit-tests/opt.exp
+++ b/unit-tests/opt.exp
@@ -12,6 +12,10 @@ 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.
+Stop.
+make: stopped in unit-tests
+*** Error code 1 (ignored)
+
make -?
usage: make [-BeikNnqrSstWwX]
[-C directory] [-D variable] [-d flags] [-f makefile]
diff --git a/unit-tests/posix.exp b/unit-tests/posix.exp
index 7e74cabadfb5..01961f363f59 100644
--- a/unit-tests/posix.exp
+++ b/unit-tests/posix.exp
@@ -20,4 +20,7 @@ a command prefixed by '+' executes even with -n
Now we expect an error...
*** Error code 1 (continuing)
`all' not remade because of errors.
-exit status 0
+
+Stop.
+make: stopped in unit-tests
+exit status 1
diff --git a/unit-tests/qequals.exp b/unit-tests/qequals.exp
deleted file mode 100644
index 6b2f4dce6994..000000000000
--- a/unit-tests/qequals.exp
+++ /dev/null
@@ -1,2 +0,0 @@
-V.i386 ?= OK
-exit status 0
diff --git a/unit-tests/qequals.mk b/unit-tests/qequals.mk
deleted file mode 100644
index a964e99b2645..000000000000
--- a/unit-tests/qequals.mk
+++ /dev/null
@@ -1,8 +0,0 @@
-# $NetBSD: qequals.mk,v 1.3 2020/10/24 08:50:17 rillig Exp $
-
-M= i386
-V.i386= OK
-V.$M?= bug
-
-all:
- @echo 'V.$M ?= ${V.$M}'
diff --git a/unit-tests/sh-dots.exp b/unit-tests/sh-dots.exp
index 4d935096c48a..a7de932035f3 100755
--- a/unit-tests/sh-dots.exp
+++ b/unit-tests/sh-dots.exp
@@ -1,19 +1,19 @@
first first
hidden hidden
-make: exec(...) failed (No such file or directory)
-*** Error code 1 (ignored)
+<not found: ...>
+*** Error code <nonzero> (ignored)
hidden delayed hidden
repeated repeated
commented commented
... # Run the below commands later
-<normalized: ...: not found>
-*** Error code 127 (ignored)
+<not found: ...>
+*** Error code <nonzero> (ignored)
commented delayed commented
indirect regular
indirect-space regular
...
-make: exec(...) failed (No such file or directory)
-*** Error code 1 (ignored)
+<not found: ...>
+*** Error code <nonzero> (ignored)
indirect-space deferred
first delayed first
repeated delayed repeated
diff --git a/unit-tests/sh-errctl.exp b/unit-tests/sh-errctl.exp
new file mode 100644
index 000000000000..8e6bc3c82125
--- /dev/null
+++ b/unit-tests/sh-errctl.exp
@@ -0,0 +1,27 @@
+job_pipe -1 -1, maxjobs 1, tokens 1, compat 0
+Job_TokenWithdraw(<pid>): aborting 0, running 0
+(<pid>) withdrew token
+# echo off
+echo silent
+# echo on
+# echo off
+# error checking off
+set +e
+# echo on
+echo ignerr; false
+# echo off
+# error checking on
+set -e
+# echo on
+echo always
+Running all
+ Command: <shell>
+JobExec(all): pid <pid> added to jobs table
+job table @ job started
+job 0, status 3, flags ---, pid <pid>
+silent
+ignerr
+always
+Job_TokenWithdraw(<pid>): aborting 0, running 0
+(<pid>) withdrew token
+exit status 0
diff --git a/unit-tests/sh-errctl.mk b/unit-tests/sh-errctl.mk
new file mode 100644
index 000000000000..ecc2485e9b72
--- /dev/null
+++ b/unit-tests/sh-errctl.mk
@@ -0,0 +1,26 @@
+# $NetBSD: sh-errctl.mk,v 1.1 2020/12/12 15:06:11 rillig Exp $
+#
+# Test a shell with error control. This only works in jobs mode; in compat
+# mode, the default shell is always used, see InitShellNameAndPath.
+#
+# There is a subtle difference between error control and echo control.
+# With error control, each simple command is checked, whereas with echo
+# control, only the last command from each line is checked. A shell command
+# line that behaves differently in these two modes is "false; true". In
+# error control mode, this fails, while in echo control mode, it succeeds.
+
+.MAKEFLAGS: -j1 -dj
+
+.SHELL: \
+ name="sh" \
+ path="${.SHELL}" \
+ hasErrCtl="yes" \
+ check="\# error checking on\nset -e" \
+ ignore="\# error checking off\nset +e" \
+ echo="\# echo on" \
+ quiet="\# echo off"
+
+all:
+ @echo silent
+ -echo ignerr; false
+ +echo always
diff --git a/unit-tests/sh-flags.exp b/unit-tests/sh-flags.exp
new file mode 100644
index 000000000000..2fec7de2dd99
--- /dev/null
+++ b/unit-tests/sh-flags.exp
@@ -0,0 +1,4325 @@
+
+opt-______-tgt-___-cmd-___
+echo running
+running
+
+opt-______-tgt-___-cmd-__s
+running
+
+opt-______-tgt-___-cmd-_i_
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-______-tgt-___-cmd-_is
+running
+*** Error code 1 (ignored)
+
+opt-______-tgt-___-cmd-a__
+echo running
+running
+
+opt-______-tgt-___-cmd-a_s
+running
+
+opt-______-tgt-___-cmd-ai_
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-______-tgt-___-cmd-ais
+running
+*** Error code 1 (ignored)
+
+opt-______-tgt-__s-cmd-___
+running
+
+opt-______-tgt-__s-cmd-__s
+running
+
+opt-______-tgt-__s-cmd-_i_
+running
+*** Error code 1 (ignored)
+
+opt-______-tgt-__s-cmd-_is
+running
+*** Error code 1 (ignored)
+
+opt-______-tgt-__s-cmd-a__
+running
+
+opt-______-tgt-__s-cmd-a_s
+running
+
+opt-______-tgt-__s-cmd-ai_
+running
+*** Error code 1 (ignored)
+
+opt-______-tgt-__s-cmd-ais
+running
+*** Error code 1 (ignored)
+
+opt-______-tgt-_i_-cmd-___
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-______-tgt-_i_-cmd-__s
+running
+*** Error code 1 (ignored)
+
+opt-______-tgt-_i_-cmd-_i_
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-______-tgt-_i_-cmd-_is
+running
+*** Error code 1 (ignored)
+
+opt-______-tgt-_i_-cmd-a__
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-______-tgt-_i_-cmd-a_s
+running
+*** Error code 1 (ignored)
+
+opt-______-tgt-_i_-cmd-ai_
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-______-tgt-_i_-cmd-ais
+running
+*** Error code 1 (ignored)
+
+opt-______-tgt-_is-cmd-___
+running
+*** Error code 1 (ignored)
+
+opt-______-tgt-_is-cmd-__s
+running
+*** Error code 1 (ignored)
+
+opt-______-tgt-_is-cmd-_i_
+running
+*** Error code 1 (ignored)
+
+opt-______-tgt-_is-cmd-_is
+running
+*** Error code 1 (ignored)
+
+opt-______-tgt-_is-cmd-a__
+running
+*** Error code 1 (ignored)
+
+opt-______-tgt-_is-cmd-a_s
+running
+*** Error code 1 (ignored)
+
+opt-______-tgt-_is-cmd-ai_
+running
+*** Error code 1 (ignored)
+
+opt-______-tgt-_is-cmd-ais
+running
+*** Error code 1 (ignored)
+
+opt-______-tgt-a__-cmd-___
+echo running
+running
+
+opt-______-tgt-a__-cmd-__s
+running
+
+opt-______-tgt-a__-cmd-_i_
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-______-tgt-a__-cmd-_is
+running
+*** Error code 1 (ignored)
+
+opt-______-tgt-a__-cmd-a__
+echo running
+running
+
+opt-______-tgt-a__-cmd-a_s
+running
+
+opt-______-tgt-a__-cmd-ai_
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-______-tgt-a__-cmd-ais
+running
+*** Error code 1 (ignored)
+
+opt-______-tgt-a_s-cmd-___
+running
+
+opt-______-tgt-a_s-cmd-__s
+running
+
+opt-______-tgt-a_s-cmd-_i_
+running
+*** Error code 1 (ignored)
+
+opt-______-tgt-a_s-cmd-_is
+running
+*** Error code 1 (ignored)
+
+opt-______-tgt-a_s-cmd-a__
+running
+
+opt-______-tgt-a_s-cmd-a_s
+running
+
+opt-______-tgt-a_s-cmd-ai_
+running
+*** Error code 1 (ignored)
+
+opt-______-tgt-a_s-cmd-ais
+running
+*** Error code 1 (ignored)
+
+opt-______-tgt-ai_-cmd-___
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-______-tgt-ai_-cmd-__s
+running
+*** Error code 1 (ignored)
+
+opt-______-tgt-ai_-cmd-_i_
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-______-tgt-ai_-cmd-_is
+running
+*** Error code 1 (ignored)
+
+opt-______-tgt-ai_-cmd-a__
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-______-tgt-ai_-cmd-a_s
+running
+*** Error code 1 (ignored)
+
+opt-______-tgt-ai_-cmd-ai_
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-______-tgt-ai_-cmd-ais
+running
+*** Error code 1 (ignored)
+
+opt-______-tgt-ais-cmd-___
+running
+*** Error code 1 (ignored)
+
+opt-______-tgt-ais-cmd-__s
+running
+*** Error code 1 (ignored)
+
+opt-______-tgt-ais-cmd-_i_
+running
+*** Error code 1 (ignored)
+
+opt-______-tgt-ais-cmd-_is
+running
+*** Error code 1 (ignored)
+
+opt-______-tgt-ais-cmd-a__
+running
+*** Error code 1 (ignored)
+
+opt-______-tgt-ais-cmd-a_s
+running
+*** Error code 1 (ignored)
+
+opt-______-tgt-ais-cmd-ai_
+running
+*** Error code 1 (ignored)
+
+opt-______-tgt-ais-cmd-ais
+running
+*** Error code 1 (ignored)
+
+opt-___n__-tgt-___-cmd-___
+echo running
+
+opt-___n__-tgt-___-cmd-__s
+echo running
+
+opt-___n__-tgt-___-cmd-_i_
+echo running; false
+
+opt-___n__-tgt-___-cmd-_is
+echo running; false
+
+opt-___n__-tgt-___-cmd-a__
+echo running
+running
+
+opt-___n__-tgt-___-cmd-a_s
+echo running
+running
+
+opt-___n__-tgt-___-cmd-ai_
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-___n__-tgt-___-cmd-ais
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-___n__-tgt-__s-cmd-___
+echo running
+
+opt-___n__-tgt-__s-cmd-__s
+echo running
+
+opt-___n__-tgt-__s-cmd-_i_
+echo running; false
+
+opt-___n__-tgt-__s-cmd-_is
+echo running; false
+
+opt-___n__-tgt-__s-cmd-a__
+echo running
+running
+
+opt-___n__-tgt-__s-cmd-a_s
+echo running
+running
+
+opt-___n__-tgt-__s-cmd-ai_
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-___n__-tgt-__s-cmd-ais
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-___n__-tgt-_i_-cmd-___
+echo running; false
+
+opt-___n__-tgt-_i_-cmd-__s
+echo running; false
+
+opt-___n__-tgt-_i_-cmd-_i_
+echo running; false
+
+opt-___n__-tgt-_i_-cmd-_is
+echo running; false
+
+opt-___n__-tgt-_i_-cmd-a__
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-___n__-tgt-_i_-cmd-a_s
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-___n__-tgt-_i_-cmd-ai_
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-___n__-tgt-_i_-cmd-ais
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-___n__-tgt-_is-cmd-___
+echo running; false
+
+opt-___n__-tgt-_is-cmd-__s
+echo running; false
+
+opt-___n__-tgt-_is-cmd-_i_
+echo running; false
+
+opt-___n__-tgt-_is-cmd-_is
+echo running; false
+
+opt-___n__-tgt-_is-cmd-a__
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-___n__-tgt-_is-cmd-a_s
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-___n__-tgt-_is-cmd-ai_
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-___n__-tgt-_is-cmd-ais
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-___n__-tgt-a__-cmd-___
+echo running
+running
+
+opt-___n__-tgt-a__-cmd-__s
+running
+
+opt-___n__-tgt-a__-cmd-_i_
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-___n__-tgt-a__-cmd-_is
+running
+*** Error code 1 (ignored)
+
+opt-___n__-tgt-a__-cmd-a__
+echo running
+running
+
+opt-___n__-tgt-a__-cmd-a_s
+running
+
+opt-___n__-tgt-a__-cmd-ai_
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-___n__-tgt-a__-cmd-ais
+running
+*** Error code 1 (ignored)
+
+opt-___n__-tgt-a_s-cmd-___
+running
+
+opt-___n__-tgt-a_s-cmd-__s
+running
+
+opt-___n__-tgt-a_s-cmd-_i_
+running
+*** Error code 1 (ignored)
+
+opt-___n__-tgt-a_s-cmd-_is
+running
+*** Error code 1 (ignored)
+
+opt-___n__-tgt-a_s-cmd-a__
+running
+
+opt-___n__-tgt-a_s-cmd-a_s
+running
+
+opt-___n__-tgt-a_s-cmd-ai_
+running
+*** Error code 1 (ignored)
+
+opt-___n__-tgt-a_s-cmd-ais
+running
+*** Error code 1 (ignored)
+
+opt-___n__-tgt-ai_-cmd-___
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-___n__-tgt-ai_-cmd-__s
+running
+*** Error code 1 (ignored)
+
+opt-___n__-tgt-ai_-cmd-_i_
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-___n__-tgt-ai_-cmd-_is
+running
+*** Error code 1 (ignored)
+
+opt-___n__-tgt-ai_-cmd-a__
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-___n__-tgt-ai_-cmd-a_s
+running
+*** Error code 1 (ignored)
+
+opt-___n__-tgt-ai_-cmd-ai_
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-___n__-tgt-ai_-cmd-ais
+running
+*** Error code 1 (ignored)
+
+opt-___n__-tgt-ais-cmd-___
+running
+*** Error code 1 (ignored)
+
+opt-___n__-tgt-ais-cmd-__s
+running
+*** Error code 1 (ignored)
+
+opt-___n__-tgt-ais-cmd-_i_
+running
+*** Error code 1 (ignored)
+
+opt-___n__-tgt-ais-cmd-_is
+running
+*** Error code 1 (ignored)
+
+opt-___n__-tgt-ais-cmd-a__
+running
+*** Error code 1 (ignored)
+
+opt-___n__-tgt-ais-cmd-a_s
+running
+*** Error code 1 (ignored)
+
+opt-___n__-tgt-ais-cmd-ai_
+running
+*** Error code 1 (ignored)
+
+opt-___n__-tgt-ais-cmd-ais
+running
+*** Error code 1 (ignored)
+
+opt-__l___-tgt-___-cmd-___
+echo running
+running
+
+opt-__l___-tgt-___-cmd-__s
+echo running
+running
+
+opt-__l___-tgt-___-cmd-_i_
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-__l___-tgt-___-cmd-_is
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-__l___-tgt-___-cmd-a__
+echo running
+running
+
+opt-__l___-tgt-___-cmd-a_s
+echo running
+running
+
+opt-__l___-tgt-___-cmd-ai_
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-__l___-tgt-___-cmd-ais
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-__l___-tgt-__s-cmd-___
+running
+
+opt-__l___-tgt-__s-cmd-__s
+echo running
+running
+
+opt-__l___-tgt-__s-cmd-_i_
+running
+*** Error code 1 (ignored)
+
+opt-__l___-tgt-__s-cmd-_is
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-__l___-tgt-__s-cmd-a__
+running
+
+opt-__l___-tgt-__s-cmd-a_s
+echo running
+running
+
+opt-__l___-tgt-__s-cmd-ai_
+running
+*** Error code 1 (ignored)
+
+opt-__l___-tgt-__s-cmd-ais
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-__l___-tgt-_i_-cmd-___
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-__l___-tgt-_i_-cmd-__s
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-__l___-tgt-_i_-cmd-_i_
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-__l___-tgt-_i_-cmd-_is
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-__l___-tgt-_i_-cmd-a__
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-__l___-tgt-_i_-cmd-a_s
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-__l___-tgt-_i_-cmd-ai_
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-__l___-tgt-_i_-cmd-ais
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-__l___-tgt-_is-cmd-___
+running
+*** Error code 1 (ignored)
+
+opt-__l___-tgt-_is-cmd-__s
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-__l___-tgt-_is-cmd-_i_
+running
+*** Error code 1 (ignored)
+
+opt-__l___-tgt-_is-cmd-_is
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-__l___-tgt-_is-cmd-a__
+running
+*** Error code 1 (ignored)
+
+opt-__l___-tgt-_is-cmd-a_s
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-__l___-tgt-_is-cmd-ai_
+running
+*** Error code 1 (ignored)
+
+opt-__l___-tgt-_is-cmd-ais
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-__l___-tgt-a__-cmd-___
+echo running
+running
+
+opt-__l___-tgt-a__-cmd-__s
+echo running
+running
+
+opt-__l___-tgt-a__-cmd-_i_
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-__l___-tgt-a__-cmd-_is
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-__l___-tgt-a__-cmd-a__
+echo running
+running
+
+opt-__l___-tgt-a__-cmd-a_s
+echo running
+running
+
+opt-__l___-tgt-a__-cmd-ai_
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-__l___-tgt-a__-cmd-ais
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-__l___-tgt-a_s-cmd-___
+running
+
+opt-__l___-tgt-a_s-cmd-__s
+echo running
+running
+
+opt-__l___-tgt-a_s-cmd-_i_
+running
+*** Error code 1 (ignored)
+
+opt-__l___-tgt-a_s-cmd-_is
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-__l___-tgt-a_s-cmd-a__
+running
+
+opt-__l___-tgt-a_s-cmd-a_s
+echo running
+running
+
+opt-__l___-tgt-a_s-cmd-ai_
+running
+*** Error code 1 (ignored)
+
+opt-__l___-tgt-a_s-cmd-ais
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-__l___-tgt-ai_-cmd-___
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-__l___-tgt-ai_-cmd-__s
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-__l___-tgt-ai_-cmd-_i_
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-__l___-tgt-ai_-cmd-_is
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-__l___-tgt-ai_-cmd-a__
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-__l___-tgt-ai_-cmd-a_s
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-__l___-tgt-ai_-cmd-ai_
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-__l___-tgt-ai_-cmd-ais
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-__l___-tgt-ais-cmd-___
+running
+*** Error code 1 (ignored)
+
+opt-__l___-tgt-ais-cmd-__s
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-__l___-tgt-ais-cmd-_i_
+running
+*** Error code 1 (ignored)
+
+opt-__l___-tgt-ais-cmd-_is
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-__l___-tgt-ais-cmd-a__
+running
+*** Error code 1 (ignored)
+
+opt-__l___-tgt-ais-cmd-a_s
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-__l___-tgt-ais-cmd-ai_
+running
+*** Error code 1 (ignored)
+
+opt-__l___-tgt-ais-cmd-ais
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-__ln__-tgt-___-cmd-___
+echo running
+
+opt-__ln__-tgt-___-cmd-__s
+echo running
+
+opt-__ln__-tgt-___-cmd-_i_
+echo running; false
+
+opt-__ln__-tgt-___-cmd-_is
+echo running; false
+
+opt-__ln__-tgt-___-cmd-a__
+echo running
+running
+
+opt-__ln__-tgt-___-cmd-a_s
+echo running
+running
+
+opt-__ln__-tgt-___-cmd-ai_
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-__ln__-tgt-___-cmd-ais
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-__ln__-tgt-__s-cmd-___
+echo running
+
+opt-__ln__-tgt-__s-cmd-__s
+echo running
+
+opt-__ln__-tgt-__s-cmd-_i_
+echo running; false
+
+opt-__ln__-tgt-__s-cmd-_is
+echo running; false
+
+opt-__ln__-tgt-__s-cmd-a__
+echo running
+running
+
+opt-__ln__-tgt-__s-cmd-a_s
+echo running
+running
+
+opt-__ln__-tgt-__s-cmd-ai_
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-__ln__-tgt-__s-cmd-ais
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-__ln__-tgt-_i_-cmd-___
+echo running; false
+
+opt-__ln__-tgt-_i_-cmd-__s
+echo running; false
+
+opt-__ln__-tgt-_i_-cmd-_i_
+echo running; false
+
+opt-__ln__-tgt-_i_-cmd-_is
+echo running; false
+
+opt-__ln__-tgt-_i_-cmd-a__
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-__ln__-tgt-_i_-cmd-a_s
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-__ln__-tgt-_i_-cmd-ai_
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-__ln__-tgt-_i_-cmd-ais
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-__ln__-tgt-_is-cmd-___
+echo running; false
+
+opt-__ln__-tgt-_is-cmd-__s
+echo running; false
+
+opt-__ln__-tgt-_is-cmd-_i_
+echo running; false
+
+opt-__ln__-tgt-_is-cmd-_is
+echo running; false
+
+opt-__ln__-tgt-_is-cmd-a__
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-__ln__-tgt-_is-cmd-a_s
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-__ln__-tgt-_is-cmd-ai_
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-__ln__-tgt-_is-cmd-ais
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-__ln__-tgt-a__-cmd-___
+echo running
+running
+
+opt-__ln__-tgt-a__-cmd-__s
+echo running
+running
+
+opt-__ln__-tgt-a__-cmd-_i_
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-__ln__-tgt-a__-cmd-_is
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-__ln__-tgt-a__-cmd-a__
+echo running
+running
+
+opt-__ln__-tgt-a__-cmd-a_s
+echo running
+running
+
+opt-__ln__-tgt-a__-cmd-ai_
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-__ln__-tgt-a__-cmd-ais
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-__ln__-tgt-a_s-cmd-___
+running
+
+opt-__ln__-tgt-a_s-cmd-__s
+echo running
+running
+
+opt-__ln__-tgt-a_s-cmd-_i_
+running
+*** Error code 1 (ignored)
+
+opt-__ln__-tgt-a_s-cmd-_is
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-__ln__-tgt-a_s-cmd-a__
+running
+
+opt-__ln__-tgt-a_s-cmd-a_s
+echo running
+running
+
+opt-__ln__-tgt-a_s-cmd-ai_
+running
+*** Error code 1 (ignored)
+
+opt-__ln__-tgt-a_s-cmd-ais
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-__ln__-tgt-ai_-cmd-___
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-__ln__-tgt-ai_-cmd-__s
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-__ln__-tgt-ai_-cmd-_i_
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-__ln__-tgt-ai_-cmd-_is
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-__ln__-tgt-ai_-cmd-a__
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-__ln__-tgt-ai_-cmd-a_s
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-__ln__-tgt-ai_-cmd-ai_
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-__ln__-tgt-ai_-cmd-ais
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-__ln__-tgt-ais-cmd-___
+running
+*** Error code 1 (ignored)
+
+opt-__ln__-tgt-ais-cmd-__s
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-__ln__-tgt-ais-cmd-_i_
+running
+*** Error code 1 (ignored)
+
+opt-__ln__-tgt-ais-cmd-_is
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-__ln__-tgt-ais-cmd-a__
+running
+*** Error code 1 (ignored)
+
+opt-__ln__-tgt-ais-cmd-a_s
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-__ln__-tgt-ais-cmd-ai_
+running
+*** Error code 1 (ignored)
+
+opt-__ln__-tgt-ais-cmd-ais
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-_j____-tgt-___-cmd-___
+echo running
+running
+
+opt-_j____-tgt-___-cmd-__s
+running
+
+opt-_j____-tgt-___-cmd-_i_
+echo running; false
+running
+*** [opt-_j____-tgt-___-cmd-_i_] Error code 1 (ignored)
+
+opt-_j____-tgt-___-cmd-_is
+running
+*** [opt-_j____-tgt-___-cmd-_is] Error code 1 (ignored)
+
+opt-_j____-tgt-___-cmd-a__
+echo running
+running
+
+opt-_j____-tgt-___-cmd-a_s
+running
+
+opt-_j____-tgt-___-cmd-ai_
+echo running; false
+running
+*** [opt-_j____-tgt-___-cmd-ai_] Error code 1 (ignored)
+
+opt-_j____-tgt-___-cmd-ais
+running
+*** [opt-_j____-tgt-___-cmd-ais] Error code 1 (ignored)
+
+opt-_j____-tgt-__s-cmd-___
+running
+
+opt-_j____-tgt-__s-cmd-__s
+running
+
+opt-_j____-tgt-__s-cmd-_i_
+echo running; false
+running
+*** [opt-_j____-tgt-__s-cmd-_i_] Error code 1 (ignored)
+
+opt-_j____-tgt-__s-cmd-_is
+running
+*** [opt-_j____-tgt-__s-cmd-_is] Error code 1 (ignored)
+
+opt-_j____-tgt-__s-cmd-a__
+running
+
+opt-_j____-tgt-__s-cmd-a_s
+running
+
+opt-_j____-tgt-__s-cmd-ai_
+echo running; false
+running
+*** [opt-_j____-tgt-__s-cmd-ai_] Error code 1 (ignored)
+
+opt-_j____-tgt-__s-cmd-ais
+running
+*** [opt-_j____-tgt-__s-cmd-ais] Error code 1 (ignored)
+
+opt-_j____-tgt-_i_-cmd-___
+echo running; false
+running
+*** [opt-_j____-tgt-_i_-cmd-___] Error code 1 (ignored)
+
+opt-_j____-tgt-_i_-cmd-__s
+running
+*** [opt-_j____-tgt-_i_-cmd-__s] Error code 1 (ignored)
+
+opt-_j____-tgt-_i_-cmd-_i_
+echo running; false
+running
+*** [opt-_j____-tgt-_i_-cmd-_i_] Error code 1 (ignored)
+
+opt-_j____-tgt-_i_-cmd-_is
+running
+*** [opt-_j____-tgt-_i_-cmd-_is] Error code 1 (ignored)
+
+opt-_j____-tgt-_i_-cmd-a__
+echo running; false
+running
+*** [opt-_j____-tgt-_i_-cmd-a__] Error code 1 (ignored)
+
+opt-_j____-tgt-_i_-cmd-a_s
+running
+*** [opt-_j____-tgt-_i_-cmd-a_s] Error code 1 (ignored)
+
+opt-_j____-tgt-_i_-cmd-ai_
+echo running; false
+running
+*** [opt-_j____-tgt-_i_-cmd-ai_] Error code 1 (ignored)
+
+opt-_j____-tgt-_i_-cmd-ais
+running
+*** [opt-_j____-tgt-_i_-cmd-ais] Error code 1 (ignored)
+
+opt-_j____-tgt-_is-cmd-___
+running
+*** [opt-_j____-tgt-_is-cmd-___] Error code 1 (ignored)
+
+opt-_j____-tgt-_is-cmd-__s
+running
+*** [opt-_j____-tgt-_is-cmd-__s] Error code 1 (ignored)
+
+opt-_j____-tgt-_is-cmd-_i_
+echo running; false
+running
+*** [opt-_j____-tgt-_is-cmd-_i_] Error code 1 (ignored)
+
+opt-_j____-tgt-_is-cmd-_is
+running
+*** [opt-_j____-tgt-_is-cmd-_is] Error code 1 (ignored)
+
+opt-_j____-tgt-_is-cmd-a__
+running
+*** [opt-_j____-tgt-_is-cmd-a__] Error code 1 (ignored)
+
+opt-_j____-tgt-_is-cmd-a_s
+running
+*** [opt-_j____-tgt-_is-cmd-a_s] Error code 1 (ignored)
+
+opt-_j____-tgt-_is-cmd-ai_
+echo running; false
+running
+*** [opt-_j____-tgt-_is-cmd-ai_] Error code 1 (ignored)
+
+opt-_j____-tgt-_is-cmd-ais
+running
+*** [opt-_j____-tgt-_is-cmd-ais] Error code 1 (ignored)
+
+opt-_j____-tgt-a__-cmd-___
+echo running
+running
+
+opt-_j____-tgt-a__-cmd-__s
+running
+
+opt-_j____-tgt-a__-cmd-_i_
+echo running; false
+running
+*** [opt-_j____-tgt-a__-cmd-_i_] Error code 1 (ignored)
+
+opt-_j____-tgt-a__-cmd-_is
+running
+*** [opt-_j____-tgt-a__-cmd-_is] Error code 1 (ignored)
+
+opt-_j____-tgt-a__-cmd-a__
+echo running
+running
+
+opt-_j____-tgt-a__-cmd-a_s
+running
+
+opt-_j____-tgt-a__-cmd-ai_
+echo running; false
+running
+*** [opt-_j____-tgt-a__-cmd-ai_] Error code 1 (ignored)
+
+opt-_j____-tgt-a__-cmd-ais
+running
+*** [opt-_j____-tgt-a__-cmd-ais] Error code 1 (ignored)
+
+opt-_j____-tgt-a_s-cmd-___
+running
+
+opt-_j____-tgt-a_s-cmd-__s
+running
+
+opt-_j____-tgt-a_s-cmd-_i_
+echo running; false
+running
+*** [opt-_j____-tgt-a_s-cmd-_i_] Error code 1 (ignored)
+
+opt-_j____-tgt-a_s-cmd-_is
+running
+*** [opt-_j____-tgt-a_s-cmd-_is] Error code 1 (ignored)
+
+opt-_j____-tgt-a_s-cmd-a__
+running
+
+opt-_j____-tgt-a_s-cmd-a_s
+running
+
+opt-_j____-tgt-a_s-cmd-ai_
+echo running; false
+running
+*** [opt-_j____-tgt-a_s-cmd-ai_] Error code 1 (ignored)
+
+opt-_j____-tgt-a_s-cmd-ais
+running
+*** [opt-_j____-tgt-a_s-cmd-ais] Error code 1 (ignored)
+
+opt-_j____-tgt-ai_-cmd-___
+echo running; false
+running
+*** [opt-_j____-tgt-ai_-cmd-___] Error code 1 (ignored)
+
+opt-_j____-tgt-ai_-cmd-__s
+running
+*** [opt-_j____-tgt-ai_-cmd-__s] Error code 1 (ignored)
+
+opt-_j____-tgt-ai_-cmd-_i_
+echo running; false
+running
+*** [opt-_j____-tgt-ai_-cmd-_i_] Error code 1 (ignored)
+
+opt-_j____-tgt-ai_-cmd-_is
+running
+*** [opt-_j____-tgt-ai_-cmd-_is] Error code 1 (ignored)
+
+opt-_j____-tgt-ai_-cmd-a__
+echo running; false
+running
+*** [opt-_j____-tgt-ai_-cmd-a__] Error code 1 (ignored)
+
+opt-_j____-tgt-ai_-cmd-a_s
+running
+*** [opt-_j____-tgt-ai_-cmd-a_s] Error code 1 (ignored)
+
+opt-_j____-tgt-ai_-cmd-ai_
+echo running; false
+running
+*** [opt-_j____-tgt-ai_-cmd-ai_] Error code 1 (ignored)
+
+opt-_j____-tgt-ai_-cmd-ais
+running
+*** [opt-_j____-tgt-ai_-cmd-ais] Error code 1 (ignored)
+
+opt-_j____-tgt-ais-cmd-___
+running
+*** [opt-_j____-tgt-ais-cmd-___] Error code 1 (ignored)
+
+opt-_j____-tgt-ais-cmd-__s
+running
+*** [opt-_j____-tgt-ais-cmd-__s] Error code 1 (ignored)
+
+opt-_j____-tgt-ais-cmd-_i_
+echo running; false
+running
+*** [opt-_j____-tgt-ais-cmd-_i_] Error code 1 (ignored)
+
+opt-_j____-tgt-ais-cmd-_is
+running
+*** [opt-_j____-tgt-ais-cmd-_is] Error code 1 (ignored)
+
+opt-_j____-tgt-ais-cmd-a__
+running
+*** [opt-_j____-tgt-ais-cmd-a__] Error code 1 (ignored)
+
+opt-_j____-tgt-ais-cmd-a_s
+running
+*** [opt-_j____-tgt-ais-cmd-a_s] Error code 1 (ignored)
+
+opt-_j____-tgt-ais-cmd-ai_
+echo running; false
+running
+*** [opt-_j____-tgt-ais-cmd-ai_] Error code 1 (ignored)
+
+opt-_j____-tgt-ais-cmd-ais
+running
+*** [opt-_j____-tgt-ais-cmd-ais] Error code 1 (ignored)
+
+opt-_j_n__-tgt-___-cmd-___
+echo "echo running"
+{ echo running
+} || exit $?
+
+opt-_j_n__-tgt-___-cmd-__s
+{ echo running
+} || exit $?
+
+opt-_j_n__-tgt-___-cmd-_i_
+echo running; false
+
+opt-_j_n__-tgt-___-cmd-_is
+echo running; false
+
+opt-_j_n__-tgt-___-cmd-a__
+echo running
+running
+
+opt-_j_n__-tgt-___-cmd-a_s
+echo running
+running
+
+opt-_j_n__-tgt-___-cmd-ai_
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-_j_n__-tgt-___-cmd-ais
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-_j_n__-tgt-__s-cmd-___
+{ echo running
+} || exit $?
+
+opt-_j_n__-tgt-__s-cmd-__s
+{ echo running
+} || exit $?
+
+opt-_j_n__-tgt-__s-cmd-_i_
+echo running; false
+
+opt-_j_n__-tgt-__s-cmd-_is
+echo running; false
+
+opt-_j_n__-tgt-__s-cmd-a__
+echo running
+running
+
+opt-_j_n__-tgt-__s-cmd-a_s
+echo running
+running
+
+opt-_j_n__-tgt-__s-cmd-ai_
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-_j_n__-tgt-__s-cmd-ais
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-_j_n__-tgt-_i_-cmd-___
+echo "echo running; false"
+{ echo running; false
+} || exit $?
+
+opt-_j_n__-tgt-_i_-cmd-__s
+{ echo running; false
+} || exit $?
+
+opt-_j_n__-tgt-_i_-cmd-_i_
+echo running; false
+
+opt-_j_n__-tgt-_i_-cmd-_is
+echo running; false
+
+opt-_j_n__-tgt-_i_-cmd-a__
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-_j_n__-tgt-_i_-cmd-a_s
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-_j_n__-tgt-_i_-cmd-ai_
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-_j_n__-tgt-_i_-cmd-ais
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-_j_n__-tgt-_is-cmd-___
+{ echo running; false
+} || exit $?
+
+opt-_j_n__-tgt-_is-cmd-__s
+{ echo running; false
+} || exit $?
+
+opt-_j_n__-tgt-_is-cmd-_i_
+echo running; false
+
+opt-_j_n__-tgt-_is-cmd-_is
+echo running; false
+
+opt-_j_n__-tgt-_is-cmd-a__
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-_j_n__-tgt-_is-cmd-a_s
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-_j_n__-tgt-_is-cmd-ai_
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-_j_n__-tgt-_is-cmd-ais
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-_j_n__-tgt-a__-cmd-___
+echo running
+running
+
+opt-_j_n__-tgt-a__-cmd-__s
+running
+
+opt-_j_n__-tgt-a__-cmd-_i_
+echo running; false
+running
+
+opt-_j_n__-tgt-a__-cmd-_is
+running
+
+opt-_j_n__-tgt-a__-cmd-a__
+echo running
+running
+
+opt-_j_n__-tgt-a__-cmd-a_s
+running
+
+opt-_j_n__-tgt-a__-cmd-ai_
+echo running; false
+running
+
+opt-_j_n__-tgt-a__-cmd-ais
+running
+
+opt-_j_n__-tgt-a_s-cmd-___
+running
+
+opt-_j_n__-tgt-a_s-cmd-__s
+running
+
+opt-_j_n__-tgt-a_s-cmd-_i_
+echo running; false
+running
+
+opt-_j_n__-tgt-a_s-cmd-_is
+running
+
+opt-_j_n__-tgt-a_s-cmd-a__
+running
+
+opt-_j_n__-tgt-a_s-cmd-a_s
+running
+
+opt-_j_n__-tgt-a_s-cmd-ai_
+echo running; false
+running
+
+opt-_j_n__-tgt-a_s-cmd-ais
+running
+
+opt-_j_n__-tgt-ai_-cmd-___
+echo running; false
+running
+
+opt-_j_n__-tgt-ai_-cmd-__s
+running
+
+opt-_j_n__-tgt-ai_-cmd-_i_
+echo running; false
+running
+
+opt-_j_n__-tgt-ai_-cmd-_is
+running
+
+opt-_j_n__-tgt-ai_-cmd-a__
+echo running; false
+running
+
+opt-_j_n__-tgt-ai_-cmd-a_s
+running
+
+opt-_j_n__-tgt-ai_-cmd-ai_
+echo running; false
+running
+
+opt-_j_n__-tgt-ai_-cmd-ais
+running
+
+opt-_j_n__-tgt-ais-cmd-___
+running
+
+opt-_j_n__-tgt-ais-cmd-__s
+running
+
+opt-_j_n__-tgt-ais-cmd-_i_
+echo running; false
+running
+
+opt-_j_n__-tgt-ais-cmd-_is
+running
+
+opt-_j_n__-tgt-ais-cmd-a__
+running
+
+opt-_j_n__-tgt-ais-cmd-a_s
+running
+
+opt-_j_n__-tgt-ais-cmd-ai_
+echo running; false
+running
+
+opt-_j_n__-tgt-ais-cmd-ais
+running
+
+opt-_jl___-tgt-___-cmd-___
+echo running
+running
+
+opt-_jl___-tgt-___-cmd-__s
+echo running
+running
+
+opt-_jl___-tgt-___-cmd-_i_
+echo running; false
+running
+*** [opt-_jl___-tgt-___-cmd-_i_] Error code 1 (ignored)
+
+opt-_jl___-tgt-___-cmd-_is
+echo running; false
+running
+*** [opt-_jl___-tgt-___-cmd-_is] Error code 1 (ignored)
+
+opt-_jl___-tgt-___-cmd-a__
+echo running
+running
+
+opt-_jl___-tgt-___-cmd-a_s
+echo running
+running
+
+opt-_jl___-tgt-___-cmd-ai_
+echo running; false
+running
+*** [opt-_jl___-tgt-___-cmd-ai_] Error code 1 (ignored)
+
+opt-_jl___-tgt-___-cmd-ais
+echo running; false
+running
+*** [opt-_jl___-tgt-___-cmd-ais] Error code 1 (ignored)
+
+opt-_jl___-tgt-__s-cmd-___
+running
+
+opt-_jl___-tgt-__s-cmd-__s
+running
+
+opt-_jl___-tgt-__s-cmd-_i_
+echo running; false
+running
+*** [opt-_jl___-tgt-__s-cmd-_i_] Error code 1 (ignored)
+
+opt-_jl___-tgt-__s-cmd-_is
+echo running; false
+running
+*** [opt-_jl___-tgt-__s-cmd-_is] Error code 1 (ignored)
+
+opt-_jl___-tgt-__s-cmd-a__
+running
+
+opt-_jl___-tgt-__s-cmd-a_s
+running
+
+opt-_jl___-tgt-__s-cmd-ai_
+echo running; false
+running
+*** [opt-_jl___-tgt-__s-cmd-ai_] Error code 1 (ignored)
+
+opt-_jl___-tgt-__s-cmd-ais
+echo running; false
+running
+*** [opt-_jl___-tgt-__s-cmd-ais] Error code 1 (ignored)
+
+opt-_jl___-tgt-_i_-cmd-___
+echo running; false
+running
+*** [opt-_jl___-tgt-_i_-cmd-___] Error code 1 (ignored)
+
+opt-_jl___-tgt-_i_-cmd-__s
+echo running; false
+running
+*** [opt-_jl___-tgt-_i_-cmd-__s] Error code 1 (ignored)
+
+opt-_jl___-tgt-_i_-cmd-_i_
+echo running; false
+running
+*** [opt-_jl___-tgt-_i_-cmd-_i_] Error code 1 (ignored)
+
+opt-_jl___-tgt-_i_-cmd-_is
+echo running; false
+running
+*** [opt-_jl___-tgt-_i_-cmd-_is] Error code 1 (ignored)
+
+opt-_jl___-tgt-_i_-cmd-a__
+echo running; false
+running
+*** [opt-_jl___-tgt-_i_-cmd-a__] Error code 1 (ignored)
+
+opt-_jl___-tgt-_i_-cmd-a_s
+echo running; false
+running
+*** [opt-_jl___-tgt-_i_-cmd-a_s] Error code 1 (ignored)
+
+opt-_jl___-tgt-_i_-cmd-ai_
+echo running; false
+running
+*** [opt-_jl___-tgt-_i_-cmd-ai_] Error code 1 (ignored)
+
+opt-_jl___-tgt-_i_-cmd-ais
+echo running; false
+running
+*** [opt-_jl___-tgt-_i_-cmd-ais] Error code 1 (ignored)
+
+opt-_jl___-tgt-_is-cmd-___
+running
+*** [opt-_jl___-tgt-_is-cmd-___] Error code 1 (ignored)
+
+opt-_jl___-tgt-_is-cmd-__s
+running
+*** [opt-_jl___-tgt-_is-cmd-__s] Error code 1 (ignored)
+
+opt-_jl___-tgt-_is-cmd-_i_
+echo running; false
+running
+*** [opt-_jl___-tgt-_is-cmd-_i_] Error code 1 (ignored)
+
+opt-_jl___-tgt-_is-cmd-_is
+echo running; false
+running
+*** [opt-_jl___-tgt-_is-cmd-_is] Error code 1 (ignored)
+
+opt-_jl___-tgt-_is-cmd-a__
+running
+*** [opt-_jl___-tgt-_is-cmd-a__] Error code 1 (ignored)
+
+opt-_jl___-tgt-_is-cmd-a_s
+running
+*** [opt-_jl___-tgt-_is-cmd-a_s] Error code 1 (ignored)
+
+opt-_jl___-tgt-_is-cmd-ai_
+echo running; false
+running
+*** [opt-_jl___-tgt-_is-cmd-ai_] Error code 1 (ignored)
+
+opt-_jl___-tgt-_is-cmd-ais
+echo running; false
+running
+*** [opt-_jl___-tgt-_is-cmd-ais] Error code 1 (ignored)
+
+opt-_jl___-tgt-a__-cmd-___
+echo running
+running
+
+opt-_jl___-tgt-a__-cmd-__s
+echo running
+running
+
+opt-_jl___-tgt-a__-cmd-_i_
+echo running; false
+running
+*** [opt-_jl___-tgt-a__-cmd-_i_] Error code 1 (ignored)
+
+opt-_jl___-tgt-a__-cmd-_is
+echo running; false
+running
+*** [opt-_jl___-tgt-a__-cmd-_is] Error code 1 (ignored)
+
+opt-_jl___-tgt-a__-cmd-a__
+echo running
+running
+
+opt-_jl___-tgt-a__-cmd-a_s
+echo running
+running
+
+opt-_jl___-tgt-a__-cmd-ai_
+echo running; false
+running
+*** [opt-_jl___-tgt-a__-cmd-ai_] Error code 1 (ignored)
+
+opt-_jl___-tgt-a__-cmd-ais
+echo running; false
+running
+*** [opt-_jl___-tgt-a__-cmd-ais] Error code 1 (ignored)
+
+opt-_jl___-tgt-a_s-cmd-___
+running
+
+opt-_jl___-tgt-a_s-cmd-__s
+running
+
+opt-_jl___-tgt-a_s-cmd-_i_
+echo running; false
+running
+*** [opt-_jl___-tgt-a_s-cmd-_i_] Error code 1 (ignored)
+
+opt-_jl___-tgt-a_s-cmd-_is
+echo running; false
+running
+*** [opt-_jl___-tgt-a_s-cmd-_is] Error code 1 (ignored)
+
+opt-_jl___-tgt-a_s-cmd-a__
+running
+
+opt-_jl___-tgt-a_s-cmd-a_s
+running
+
+opt-_jl___-tgt-a_s-cmd-ai_
+echo running; false
+running
+*** [opt-_jl___-tgt-a_s-cmd-ai_] Error code 1 (ignored)
+
+opt-_jl___-tgt-a_s-cmd-ais
+echo running; false
+running
+*** [opt-_jl___-tgt-a_s-cmd-ais] Error code 1 (ignored)
+
+opt-_jl___-tgt-ai_-cmd-___
+echo running; false
+running
+*** [opt-_jl___-tgt-ai_-cmd-___] Error code 1 (ignored)
+
+opt-_jl___-tgt-ai_-cmd-__s
+echo running; false
+running
+*** [opt-_jl___-tgt-ai_-cmd-__s] Error code 1 (ignored)
+
+opt-_jl___-tgt-ai_-cmd-_i_
+echo running; false
+running
+*** [opt-_jl___-tgt-ai_-cmd-_i_] Error code 1 (ignored)
+
+opt-_jl___-tgt-ai_-cmd-_is
+echo running; false
+running
+*** [opt-_jl___-tgt-ai_-cmd-_is] Error code 1 (ignored)
+
+opt-_jl___-tgt-ai_-cmd-a__
+echo running; false
+running
+*** [opt-_jl___-tgt-ai_-cmd-a__] Error code 1 (ignored)
+
+opt-_jl___-tgt-ai_-cmd-a_s
+echo running; false
+running
+*** [opt-_jl___-tgt-ai_-cmd-a_s] Error code 1 (ignored)
+
+opt-_jl___-tgt-ai_-cmd-ai_
+echo running; false
+running
+*** [opt-_jl___-tgt-ai_-cmd-ai_] Error code 1 (ignored)
+
+opt-_jl___-tgt-ai_-cmd-ais
+echo running; false
+running
+*** [opt-_jl___-tgt-ai_-cmd-ais] Error code 1 (ignored)
+
+opt-_jl___-tgt-ais-cmd-___
+running
+*** [opt-_jl___-tgt-ais-cmd-___] Error code 1 (ignored)
+
+opt-_jl___-tgt-ais-cmd-__s
+running
+*** [opt-_jl___-tgt-ais-cmd-__s] Error code 1 (ignored)
+
+opt-_jl___-tgt-ais-cmd-_i_
+echo running; false
+running
+*** [opt-_jl___-tgt-ais-cmd-_i_] Error code 1 (ignored)
+
+opt-_jl___-tgt-ais-cmd-_is
+echo running; false
+running
+*** [opt-_jl___-tgt-ais-cmd-_is] Error code 1 (ignored)
+
+opt-_jl___-tgt-ais-cmd-a__
+running
+*** [opt-_jl___-tgt-ais-cmd-a__] Error code 1 (ignored)
+
+opt-_jl___-tgt-ais-cmd-a_s
+running
+*** [opt-_jl___-tgt-ais-cmd-a_s] Error code 1 (ignored)
+
+opt-_jl___-tgt-ais-cmd-ai_
+echo running; false
+running
+*** [opt-_jl___-tgt-ais-cmd-ai_] Error code 1 (ignored)
+
+opt-_jl___-tgt-ais-cmd-ais
+echo running; false
+running
+*** [opt-_jl___-tgt-ais-cmd-ais] Error code 1 (ignored)
+
+opt-_jln__-tgt-___-cmd-___
+echo "echo running"
+{ echo running
+} || exit $?
+
+opt-_jln__-tgt-___-cmd-__s
+echo "echo running"
+{ echo running
+} || exit $?
+
+opt-_jln__-tgt-___-cmd-_i_
+echo running; false
+
+opt-_jln__-tgt-___-cmd-_is
+echo running; false
+
+opt-_jln__-tgt-___-cmd-a__
+echo running
+running
+
+opt-_jln__-tgt-___-cmd-a_s
+echo running
+running
+
+opt-_jln__-tgt-___-cmd-ai_
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-_jln__-tgt-___-cmd-ais
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-_jln__-tgt-__s-cmd-___
+{ echo running
+} || exit $?
+
+opt-_jln__-tgt-__s-cmd-__s
+{ echo running
+} || exit $?
+
+opt-_jln__-tgt-__s-cmd-_i_
+echo running; false
+
+opt-_jln__-tgt-__s-cmd-_is
+echo running; false
+
+opt-_jln__-tgt-__s-cmd-a__
+echo running
+running
+
+opt-_jln__-tgt-__s-cmd-a_s
+echo running
+running
+
+opt-_jln__-tgt-__s-cmd-ai_
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-_jln__-tgt-__s-cmd-ais
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-_jln__-tgt-_i_-cmd-___
+echo "echo running; false"
+{ echo running; false
+} || exit $?
+
+opt-_jln__-tgt-_i_-cmd-__s
+echo "echo running; false"
+{ echo running; false
+} || exit $?
+
+opt-_jln__-tgt-_i_-cmd-_i_
+echo running; false
+
+opt-_jln__-tgt-_i_-cmd-_is
+echo running; false
+
+opt-_jln__-tgt-_i_-cmd-a__
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-_jln__-tgt-_i_-cmd-a_s
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-_jln__-tgt-_i_-cmd-ai_
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-_jln__-tgt-_i_-cmd-ais
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-_jln__-tgt-_is-cmd-___
+{ echo running; false
+} || exit $?
+
+opt-_jln__-tgt-_is-cmd-__s
+{ echo running; false
+} || exit $?
+
+opt-_jln__-tgt-_is-cmd-_i_
+echo running; false
+
+opt-_jln__-tgt-_is-cmd-_is
+echo running; false
+
+opt-_jln__-tgt-_is-cmd-a__
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-_jln__-tgt-_is-cmd-a_s
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-_jln__-tgt-_is-cmd-ai_
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-_jln__-tgt-_is-cmd-ais
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-_jln__-tgt-a__-cmd-___
+echo running
+running
+
+opt-_jln__-tgt-a__-cmd-__s
+echo running
+running
+
+opt-_jln__-tgt-a__-cmd-_i_
+echo running; false
+running
+
+opt-_jln__-tgt-a__-cmd-_is
+echo running; false
+running
+
+opt-_jln__-tgt-a__-cmd-a__
+echo running
+running
+
+opt-_jln__-tgt-a__-cmd-a_s
+echo running
+running
+
+opt-_jln__-tgt-a__-cmd-ai_
+echo running; false
+running
+
+opt-_jln__-tgt-a__-cmd-ais
+echo running; false
+running
+
+opt-_jln__-tgt-a_s-cmd-___
+running
+
+opt-_jln__-tgt-a_s-cmd-__s
+running
+
+opt-_jln__-tgt-a_s-cmd-_i_
+echo running; false
+running
+
+opt-_jln__-tgt-a_s-cmd-_is
+echo running; false
+running
+
+opt-_jln__-tgt-a_s-cmd-a__
+running
+
+opt-_jln__-tgt-a_s-cmd-a_s
+running
+
+opt-_jln__-tgt-a_s-cmd-ai_
+echo running; false
+running
+
+opt-_jln__-tgt-a_s-cmd-ais
+echo running; false
+running
+
+opt-_jln__-tgt-ai_-cmd-___
+echo running; false
+running
+
+opt-_jln__-tgt-ai_-cmd-__s
+echo running; false
+running
+
+opt-_jln__-tgt-ai_-cmd-_i_
+echo running; false
+running
+
+opt-_jln__-tgt-ai_-cmd-_is
+echo running; false
+running
+
+opt-_jln__-tgt-ai_-cmd-a__
+echo running; false
+running
+
+opt-_jln__-tgt-ai_-cmd-a_s
+echo running; false
+running
+
+opt-_jln__-tgt-ai_-cmd-ai_
+echo running; false
+running
+
+opt-_jln__-tgt-ai_-cmd-ais
+echo running; false
+running
+
+opt-_jln__-tgt-ais-cmd-___
+running
+
+opt-_jln__-tgt-ais-cmd-__s
+running
+
+opt-_jln__-tgt-ais-cmd-_i_
+echo running; false
+running
+
+opt-_jln__-tgt-ais-cmd-_is
+echo running; false
+running
+
+opt-_jln__-tgt-ais-cmd-a__
+running
+
+opt-_jln__-tgt-ais-cmd-a_s
+running
+
+opt-_jln__-tgt-ais-cmd-ai_
+echo running; false
+running
+
+opt-_jln__-tgt-ais-cmd-ais
+echo running; false
+running
+
+opt-i_____-tgt-___-cmd-___
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_____-tgt-___-cmd-__s
+running
+*** Error code 1 (ignored)
+
+opt-i_____-tgt-___-cmd-_i_
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_____-tgt-___-cmd-_is
+running
+*** Error code 1 (ignored)
+
+opt-i_____-tgt-___-cmd-a__
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_____-tgt-___-cmd-a_s
+running
+*** Error code 1 (ignored)
+
+opt-i_____-tgt-___-cmd-ai_
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_____-tgt-___-cmd-ais
+running
+*** Error code 1 (ignored)
+
+opt-i_____-tgt-__s-cmd-___
+running
+*** Error code 1 (ignored)
+
+opt-i_____-tgt-__s-cmd-__s
+running
+*** Error code 1 (ignored)
+
+opt-i_____-tgt-__s-cmd-_i_
+running
+*** Error code 1 (ignored)
+
+opt-i_____-tgt-__s-cmd-_is
+running
+*** Error code 1 (ignored)
+
+opt-i_____-tgt-__s-cmd-a__
+running
+*** Error code 1 (ignored)
+
+opt-i_____-tgt-__s-cmd-a_s
+running
+*** Error code 1 (ignored)
+
+opt-i_____-tgt-__s-cmd-ai_
+running
+*** Error code 1 (ignored)
+
+opt-i_____-tgt-__s-cmd-ais
+running
+*** Error code 1 (ignored)
+
+opt-i_____-tgt-_i_-cmd-___
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_____-tgt-_i_-cmd-__s
+running
+*** Error code 1 (ignored)
+
+opt-i_____-tgt-_i_-cmd-_i_
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_____-tgt-_i_-cmd-_is
+running
+*** Error code 1 (ignored)
+
+opt-i_____-tgt-_i_-cmd-a__
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_____-tgt-_i_-cmd-a_s
+running
+*** Error code 1 (ignored)
+
+opt-i_____-tgt-_i_-cmd-ai_
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_____-tgt-_i_-cmd-ais
+running
+*** Error code 1 (ignored)
+
+opt-i_____-tgt-_is-cmd-___
+running
+*** Error code 1 (ignored)
+
+opt-i_____-tgt-_is-cmd-__s
+running
+*** Error code 1 (ignored)
+
+opt-i_____-tgt-_is-cmd-_i_
+running
+*** Error code 1 (ignored)
+
+opt-i_____-tgt-_is-cmd-_is
+running
+*** Error code 1 (ignored)
+
+opt-i_____-tgt-_is-cmd-a__
+running
+*** Error code 1 (ignored)
+
+opt-i_____-tgt-_is-cmd-a_s
+running
+*** Error code 1 (ignored)
+
+opt-i_____-tgt-_is-cmd-ai_
+running
+*** Error code 1 (ignored)
+
+opt-i_____-tgt-_is-cmd-ais
+running
+*** Error code 1 (ignored)
+
+opt-i_____-tgt-a__-cmd-___
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_____-tgt-a__-cmd-__s
+running
+*** Error code 1 (ignored)
+
+opt-i_____-tgt-a__-cmd-_i_
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_____-tgt-a__-cmd-_is
+running
+*** Error code 1 (ignored)
+
+opt-i_____-tgt-a__-cmd-a__
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_____-tgt-a__-cmd-a_s
+running
+*** Error code 1 (ignored)
+
+opt-i_____-tgt-a__-cmd-ai_
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_____-tgt-a__-cmd-ais
+running
+*** Error code 1 (ignored)
+
+opt-i_____-tgt-a_s-cmd-___
+running
+*** Error code 1 (ignored)
+
+opt-i_____-tgt-a_s-cmd-__s
+running
+*** Error code 1 (ignored)
+
+opt-i_____-tgt-a_s-cmd-_i_
+running
+*** Error code 1 (ignored)
+
+opt-i_____-tgt-a_s-cmd-_is
+running
+*** Error code 1 (ignored)
+
+opt-i_____-tgt-a_s-cmd-a__
+running
+*** Error code 1 (ignored)
+
+opt-i_____-tgt-a_s-cmd-a_s
+running
+*** Error code 1 (ignored)
+
+opt-i_____-tgt-a_s-cmd-ai_
+running
+*** Error code 1 (ignored)
+
+opt-i_____-tgt-a_s-cmd-ais
+running
+*** Error code 1 (ignored)
+
+opt-i_____-tgt-ai_-cmd-___
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_____-tgt-ai_-cmd-__s
+running
+*** Error code 1 (ignored)
+
+opt-i_____-tgt-ai_-cmd-_i_
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_____-tgt-ai_-cmd-_is
+running
+*** Error code 1 (ignored)
+
+opt-i_____-tgt-ai_-cmd-a__
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_____-tgt-ai_-cmd-a_s
+running
+*** Error code 1 (ignored)
+
+opt-i_____-tgt-ai_-cmd-ai_
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_____-tgt-ai_-cmd-ais
+running
+*** Error code 1 (ignored)
+
+opt-i_____-tgt-ais-cmd-___
+running
+*** Error code 1 (ignored)
+
+opt-i_____-tgt-ais-cmd-__s
+running
+*** Error code 1 (ignored)
+
+opt-i_____-tgt-ais-cmd-_i_
+running
+*** Error code 1 (ignored)
+
+opt-i_____-tgt-ais-cmd-_is
+running
+*** Error code 1 (ignored)
+
+opt-i_____-tgt-ais-cmd-a__
+running
+*** Error code 1 (ignored)
+
+opt-i_____-tgt-ais-cmd-a_s
+running
+*** Error code 1 (ignored)
+
+opt-i_____-tgt-ais-cmd-ai_
+running
+*** Error code 1 (ignored)
+
+opt-i_____-tgt-ais-cmd-ais
+running
+*** Error code 1 (ignored)
+
+opt-i__n__-tgt-___-cmd-___
+echo running; false
+
+opt-i__n__-tgt-___-cmd-__s
+echo running; false
+
+opt-i__n__-tgt-___-cmd-_i_
+echo running; false
+
+opt-i__n__-tgt-___-cmd-_is
+echo running; false
+
+opt-i__n__-tgt-___-cmd-a__
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i__n__-tgt-___-cmd-a_s
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i__n__-tgt-___-cmd-ai_
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i__n__-tgt-___-cmd-ais
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i__n__-tgt-__s-cmd-___
+echo running; false
+
+opt-i__n__-tgt-__s-cmd-__s
+echo running; false
+
+opt-i__n__-tgt-__s-cmd-_i_
+echo running; false
+
+opt-i__n__-tgt-__s-cmd-_is
+echo running; false
+
+opt-i__n__-tgt-__s-cmd-a__
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i__n__-tgt-__s-cmd-a_s
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i__n__-tgt-__s-cmd-ai_
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i__n__-tgt-__s-cmd-ais
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i__n__-tgt-_i_-cmd-___
+echo running; false
+
+opt-i__n__-tgt-_i_-cmd-__s
+echo running; false
+
+opt-i__n__-tgt-_i_-cmd-_i_
+echo running; false
+
+opt-i__n__-tgt-_i_-cmd-_is
+echo running; false
+
+opt-i__n__-tgt-_i_-cmd-a__
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i__n__-tgt-_i_-cmd-a_s
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i__n__-tgt-_i_-cmd-ai_
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i__n__-tgt-_i_-cmd-ais
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i__n__-tgt-_is-cmd-___
+echo running; false
+
+opt-i__n__-tgt-_is-cmd-__s
+echo running; false
+
+opt-i__n__-tgt-_is-cmd-_i_
+echo running; false
+
+opt-i__n__-tgt-_is-cmd-_is
+echo running; false
+
+opt-i__n__-tgt-_is-cmd-a__
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i__n__-tgt-_is-cmd-a_s
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i__n__-tgt-_is-cmd-ai_
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i__n__-tgt-_is-cmd-ais
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i__n__-tgt-a__-cmd-___
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i__n__-tgt-a__-cmd-__s
+running
+*** Error code 1 (ignored)
+
+opt-i__n__-tgt-a__-cmd-_i_
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i__n__-tgt-a__-cmd-_is
+running
+*** Error code 1 (ignored)
+
+opt-i__n__-tgt-a__-cmd-a__
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i__n__-tgt-a__-cmd-a_s
+running
+*** Error code 1 (ignored)
+
+opt-i__n__-tgt-a__-cmd-ai_
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i__n__-tgt-a__-cmd-ais
+running
+*** Error code 1 (ignored)
+
+opt-i__n__-tgt-a_s-cmd-___
+running
+*** Error code 1 (ignored)
+
+opt-i__n__-tgt-a_s-cmd-__s
+running
+*** Error code 1 (ignored)
+
+opt-i__n__-tgt-a_s-cmd-_i_
+running
+*** Error code 1 (ignored)
+
+opt-i__n__-tgt-a_s-cmd-_is
+running
+*** Error code 1 (ignored)
+
+opt-i__n__-tgt-a_s-cmd-a__
+running
+*** Error code 1 (ignored)
+
+opt-i__n__-tgt-a_s-cmd-a_s
+running
+*** Error code 1 (ignored)
+
+opt-i__n__-tgt-a_s-cmd-ai_
+running
+*** Error code 1 (ignored)
+
+opt-i__n__-tgt-a_s-cmd-ais
+running
+*** Error code 1 (ignored)
+
+opt-i__n__-tgt-ai_-cmd-___
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i__n__-tgt-ai_-cmd-__s
+running
+*** Error code 1 (ignored)
+
+opt-i__n__-tgt-ai_-cmd-_i_
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i__n__-tgt-ai_-cmd-_is
+running
+*** Error code 1 (ignored)
+
+opt-i__n__-tgt-ai_-cmd-a__
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i__n__-tgt-ai_-cmd-a_s
+running
+*** Error code 1 (ignored)
+
+opt-i__n__-tgt-ai_-cmd-ai_
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i__n__-tgt-ai_-cmd-ais
+running
+*** Error code 1 (ignored)
+
+opt-i__n__-tgt-ais-cmd-___
+running
+*** Error code 1 (ignored)
+
+opt-i__n__-tgt-ais-cmd-__s
+running
+*** Error code 1 (ignored)
+
+opt-i__n__-tgt-ais-cmd-_i_
+running
+*** Error code 1 (ignored)
+
+opt-i__n__-tgt-ais-cmd-_is
+running
+*** Error code 1 (ignored)
+
+opt-i__n__-tgt-ais-cmd-a__
+running
+*** Error code 1 (ignored)
+
+opt-i__n__-tgt-ais-cmd-a_s
+running
+*** Error code 1 (ignored)
+
+opt-i__n__-tgt-ais-cmd-ai_
+running
+*** Error code 1 (ignored)
+
+opt-i__n__-tgt-ais-cmd-ais
+running
+*** Error code 1 (ignored)
+
+opt-i_l___-tgt-___-cmd-___
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_l___-tgt-___-cmd-__s
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_l___-tgt-___-cmd-_i_
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_l___-tgt-___-cmd-_is
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_l___-tgt-___-cmd-a__
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_l___-tgt-___-cmd-a_s
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_l___-tgt-___-cmd-ai_
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_l___-tgt-___-cmd-ais
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_l___-tgt-__s-cmd-___
+running
+*** Error code 1 (ignored)
+
+opt-i_l___-tgt-__s-cmd-__s
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_l___-tgt-__s-cmd-_i_
+running
+*** Error code 1 (ignored)
+
+opt-i_l___-tgt-__s-cmd-_is
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_l___-tgt-__s-cmd-a__
+running
+*** Error code 1 (ignored)
+
+opt-i_l___-tgt-__s-cmd-a_s
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_l___-tgt-__s-cmd-ai_
+running
+*** Error code 1 (ignored)
+
+opt-i_l___-tgt-__s-cmd-ais
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_l___-tgt-_i_-cmd-___
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_l___-tgt-_i_-cmd-__s
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_l___-tgt-_i_-cmd-_i_
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_l___-tgt-_i_-cmd-_is
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_l___-tgt-_i_-cmd-a__
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_l___-tgt-_i_-cmd-a_s
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_l___-tgt-_i_-cmd-ai_
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_l___-tgt-_i_-cmd-ais
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_l___-tgt-_is-cmd-___
+running
+*** Error code 1 (ignored)
+
+opt-i_l___-tgt-_is-cmd-__s
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_l___-tgt-_is-cmd-_i_
+running
+*** Error code 1 (ignored)
+
+opt-i_l___-tgt-_is-cmd-_is
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_l___-tgt-_is-cmd-a__
+running
+*** Error code 1 (ignored)
+
+opt-i_l___-tgt-_is-cmd-a_s
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_l___-tgt-_is-cmd-ai_
+running
+*** Error code 1 (ignored)
+
+opt-i_l___-tgt-_is-cmd-ais
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_l___-tgt-a__-cmd-___
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_l___-tgt-a__-cmd-__s
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_l___-tgt-a__-cmd-_i_
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_l___-tgt-a__-cmd-_is
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_l___-tgt-a__-cmd-a__
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_l___-tgt-a__-cmd-a_s
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_l___-tgt-a__-cmd-ai_
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_l___-tgt-a__-cmd-ais
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_l___-tgt-a_s-cmd-___
+running
+*** Error code 1 (ignored)
+
+opt-i_l___-tgt-a_s-cmd-__s
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_l___-tgt-a_s-cmd-_i_
+running
+*** Error code 1 (ignored)
+
+opt-i_l___-tgt-a_s-cmd-_is
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_l___-tgt-a_s-cmd-a__
+running
+*** Error code 1 (ignored)
+
+opt-i_l___-tgt-a_s-cmd-a_s
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_l___-tgt-a_s-cmd-ai_
+running
+*** Error code 1 (ignored)
+
+opt-i_l___-tgt-a_s-cmd-ais
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_l___-tgt-ai_-cmd-___
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_l___-tgt-ai_-cmd-__s
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_l___-tgt-ai_-cmd-_i_
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_l___-tgt-ai_-cmd-_is
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_l___-tgt-ai_-cmd-a__
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_l___-tgt-ai_-cmd-a_s
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_l___-tgt-ai_-cmd-ai_
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_l___-tgt-ai_-cmd-ais
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_l___-tgt-ais-cmd-___
+running
+*** Error code 1 (ignored)
+
+opt-i_l___-tgt-ais-cmd-__s
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_l___-tgt-ais-cmd-_i_
+running
+*** Error code 1 (ignored)
+
+opt-i_l___-tgt-ais-cmd-_is
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_l___-tgt-ais-cmd-a__
+running
+*** Error code 1 (ignored)
+
+opt-i_l___-tgt-ais-cmd-a_s
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_l___-tgt-ais-cmd-ai_
+running
+*** Error code 1 (ignored)
+
+opt-i_l___-tgt-ais-cmd-ais
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_ln__-tgt-___-cmd-___
+echo running; false
+
+opt-i_ln__-tgt-___-cmd-__s
+echo running; false
+
+opt-i_ln__-tgt-___-cmd-_i_
+echo running; false
+
+opt-i_ln__-tgt-___-cmd-_is
+echo running; false
+
+opt-i_ln__-tgt-___-cmd-a__
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_ln__-tgt-___-cmd-a_s
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_ln__-tgt-___-cmd-ai_
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_ln__-tgt-___-cmd-ais
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_ln__-tgt-__s-cmd-___
+echo running; false
+
+opt-i_ln__-tgt-__s-cmd-__s
+echo running; false
+
+opt-i_ln__-tgt-__s-cmd-_i_
+echo running; false
+
+opt-i_ln__-tgt-__s-cmd-_is
+echo running; false
+
+opt-i_ln__-tgt-__s-cmd-a__
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_ln__-tgt-__s-cmd-a_s
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_ln__-tgt-__s-cmd-ai_
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_ln__-tgt-__s-cmd-ais
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_ln__-tgt-_i_-cmd-___
+echo running; false
+
+opt-i_ln__-tgt-_i_-cmd-__s
+echo running; false
+
+opt-i_ln__-tgt-_i_-cmd-_i_
+echo running; false
+
+opt-i_ln__-tgt-_i_-cmd-_is
+echo running; false
+
+opt-i_ln__-tgt-_i_-cmd-a__
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_ln__-tgt-_i_-cmd-a_s
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_ln__-tgt-_i_-cmd-ai_
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_ln__-tgt-_i_-cmd-ais
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_ln__-tgt-_is-cmd-___
+echo running; false
+
+opt-i_ln__-tgt-_is-cmd-__s
+echo running; false
+
+opt-i_ln__-tgt-_is-cmd-_i_
+echo running; false
+
+opt-i_ln__-tgt-_is-cmd-_is
+echo running; false
+
+opt-i_ln__-tgt-_is-cmd-a__
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_ln__-tgt-_is-cmd-a_s
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_ln__-tgt-_is-cmd-ai_
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_ln__-tgt-_is-cmd-ais
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_ln__-tgt-a__-cmd-___
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_ln__-tgt-a__-cmd-__s
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_ln__-tgt-a__-cmd-_i_
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_ln__-tgt-a__-cmd-_is
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_ln__-tgt-a__-cmd-a__
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_ln__-tgt-a__-cmd-a_s
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_ln__-tgt-a__-cmd-ai_
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_ln__-tgt-a__-cmd-ais
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_ln__-tgt-a_s-cmd-___
+running
+*** Error code 1 (ignored)
+
+opt-i_ln__-tgt-a_s-cmd-__s
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_ln__-tgt-a_s-cmd-_i_
+running
+*** Error code 1 (ignored)
+
+opt-i_ln__-tgt-a_s-cmd-_is
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_ln__-tgt-a_s-cmd-a__
+running
+*** Error code 1 (ignored)
+
+opt-i_ln__-tgt-a_s-cmd-a_s
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_ln__-tgt-a_s-cmd-ai_
+running
+*** Error code 1 (ignored)
+
+opt-i_ln__-tgt-a_s-cmd-ais
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_ln__-tgt-ai_-cmd-___
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_ln__-tgt-ai_-cmd-__s
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_ln__-tgt-ai_-cmd-_i_
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_ln__-tgt-ai_-cmd-_is
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_ln__-tgt-ai_-cmd-a__
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_ln__-tgt-ai_-cmd-a_s
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_ln__-tgt-ai_-cmd-ai_
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_ln__-tgt-ai_-cmd-ais
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_ln__-tgt-ais-cmd-___
+running
+*** Error code 1 (ignored)
+
+opt-i_ln__-tgt-ais-cmd-__s
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_ln__-tgt-ais-cmd-_i_
+running
+*** Error code 1 (ignored)
+
+opt-i_ln__-tgt-ais-cmd-_is
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_ln__-tgt-ais-cmd-a__
+running
+*** Error code 1 (ignored)
+
+opt-i_ln__-tgt-ais-cmd-a_s
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-i_ln__-tgt-ais-cmd-ai_
+running
+*** Error code 1 (ignored)
+
+opt-i_ln__-tgt-ais-cmd-ais
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-ij____-tgt-___-cmd-___
+echo running; false
+running
+*** [opt-ij____-tgt-___-cmd-___] Error code 1 (ignored)
+
+opt-ij____-tgt-___-cmd-__s
+running
+*** [opt-ij____-tgt-___-cmd-__s] Error code 1 (ignored)
+
+opt-ij____-tgt-___-cmd-_i_
+echo running; false
+running
+*** [opt-ij____-tgt-___-cmd-_i_] Error code 1 (ignored)
+
+opt-ij____-tgt-___-cmd-_is
+running
+*** [opt-ij____-tgt-___-cmd-_is] Error code 1 (ignored)
+
+opt-ij____-tgt-___-cmd-a__
+echo running; false
+running
+*** [opt-ij____-tgt-___-cmd-a__] Error code 1 (ignored)
+
+opt-ij____-tgt-___-cmd-a_s
+running
+*** [opt-ij____-tgt-___-cmd-a_s] Error code 1 (ignored)
+
+opt-ij____-tgt-___-cmd-ai_
+echo running; false
+running
+*** [opt-ij____-tgt-___-cmd-ai_] Error code 1 (ignored)
+
+opt-ij____-tgt-___-cmd-ais
+running
+*** [opt-ij____-tgt-___-cmd-ais] Error code 1 (ignored)
+
+opt-ij____-tgt-__s-cmd-___
+running
+*** [opt-ij____-tgt-__s-cmd-___] Error code 1 (ignored)
+
+opt-ij____-tgt-__s-cmd-__s
+running
+*** [opt-ij____-tgt-__s-cmd-__s] Error code 1 (ignored)
+
+opt-ij____-tgt-__s-cmd-_i_
+echo running; false
+running
+*** [opt-ij____-tgt-__s-cmd-_i_] Error code 1 (ignored)
+
+opt-ij____-tgt-__s-cmd-_is
+running
+*** [opt-ij____-tgt-__s-cmd-_is] Error code 1 (ignored)
+
+opt-ij____-tgt-__s-cmd-a__
+running
+*** [opt-ij____-tgt-__s-cmd-a__] Error code 1 (ignored)
+
+opt-ij____-tgt-__s-cmd-a_s
+running
+*** [opt-ij____-tgt-__s-cmd-a_s] Error code 1 (ignored)
+
+opt-ij____-tgt-__s-cmd-ai_
+echo running; false
+running
+*** [opt-ij____-tgt-__s-cmd-ai_] Error code 1 (ignored)
+
+opt-ij____-tgt-__s-cmd-ais
+running
+*** [opt-ij____-tgt-__s-cmd-ais] Error code 1 (ignored)
+
+opt-ij____-tgt-_i_-cmd-___
+echo running; false
+running
+*** [opt-ij____-tgt-_i_-cmd-___] Error code 1 (ignored)
+
+opt-ij____-tgt-_i_-cmd-__s
+running
+*** [opt-ij____-tgt-_i_-cmd-__s] Error code 1 (ignored)
+
+opt-ij____-tgt-_i_-cmd-_i_
+echo running; false
+running
+*** [opt-ij____-tgt-_i_-cmd-_i_] Error code 1 (ignored)
+
+opt-ij____-tgt-_i_-cmd-_is
+running
+*** [opt-ij____-tgt-_i_-cmd-_is] Error code 1 (ignored)
+
+opt-ij____-tgt-_i_-cmd-a__
+echo running; false
+running
+*** [opt-ij____-tgt-_i_-cmd-a__] Error code 1 (ignored)
+
+opt-ij____-tgt-_i_-cmd-a_s
+running
+*** [opt-ij____-tgt-_i_-cmd-a_s] Error code 1 (ignored)
+
+opt-ij____-tgt-_i_-cmd-ai_
+echo running; false
+running
+*** [opt-ij____-tgt-_i_-cmd-ai_] Error code 1 (ignored)
+
+opt-ij____-tgt-_i_-cmd-ais
+running
+*** [opt-ij____-tgt-_i_-cmd-ais] Error code 1 (ignored)
+
+opt-ij____-tgt-_is-cmd-___
+running
+*** [opt-ij____-tgt-_is-cmd-___] Error code 1 (ignored)
+
+opt-ij____-tgt-_is-cmd-__s
+running
+*** [opt-ij____-tgt-_is-cmd-__s] Error code 1 (ignored)
+
+opt-ij____-tgt-_is-cmd-_i_
+echo running; false
+running
+*** [opt-ij____-tgt-_is-cmd-_i_] Error code 1 (ignored)
+
+opt-ij____-tgt-_is-cmd-_is
+running
+*** [opt-ij____-tgt-_is-cmd-_is] Error code 1 (ignored)
+
+opt-ij____-tgt-_is-cmd-a__
+running
+*** [opt-ij____-tgt-_is-cmd-a__] Error code 1 (ignored)
+
+opt-ij____-tgt-_is-cmd-a_s
+running
+*** [opt-ij____-tgt-_is-cmd-a_s] Error code 1 (ignored)
+
+opt-ij____-tgt-_is-cmd-ai_
+echo running; false
+running
+*** [opt-ij____-tgt-_is-cmd-ai_] Error code 1 (ignored)
+
+opt-ij____-tgt-_is-cmd-ais
+running
+*** [opt-ij____-tgt-_is-cmd-ais] Error code 1 (ignored)
+
+opt-ij____-tgt-a__-cmd-___
+echo running; false
+running
+*** [opt-ij____-tgt-a__-cmd-___] Error code 1 (ignored)
+
+opt-ij____-tgt-a__-cmd-__s
+running
+*** [opt-ij____-tgt-a__-cmd-__s] Error code 1 (ignored)
+
+opt-ij____-tgt-a__-cmd-_i_
+echo running; false
+running
+*** [opt-ij____-tgt-a__-cmd-_i_] Error code 1 (ignored)
+
+opt-ij____-tgt-a__-cmd-_is
+running
+*** [opt-ij____-tgt-a__-cmd-_is] Error code 1 (ignored)
+
+opt-ij____-tgt-a__-cmd-a__
+echo running; false
+running
+*** [opt-ij____-tgt-a__-cmd-a__] Error code 1 (ignored)
+
+opt-ij____-tgt-a__-cmd-a_s
+running
+*** [opt-ij____-tgt-a__-cmd-a_s] Error code 1 (ignored)
+
+opt-ij____-tgt-a__-cmd-ai_
+echo running; false
+running
+*** [opt-ij____-tgt-a__-cmd-ai_] Error code 1 (ignored)
+
+opt-ij____-tgt-a__-cmd-ais
+running
+*** [opt-ij____-tgt-a__-cmd-ais] Error code 1 (ignored)
+
+opt-ij____-tgt-a_s-cmd-___
+running
+*** [opt-ij____-tgt-a_s-cmd-___] Error code 1 (ignored)
+
+opt-ij____-tgt-a_s-cmd-__s
+running
+*** [opt-ij____-tgt-a_s-cmd-__s] Error code 1 (ignored)
+
+opt-ij____-tgt-a_s-cmd-_i_
+echo running; false
+running
+*** [opt-ij____-tgt-a_s-cmd-_i_] Error code 1 (ignored)
+
+opt-ij____-tgt-a_s-cmd-_is
+running
+*** [opt-ij____-tgt-a_s-cmd-_is] Error code 1 (ignored)
+
+opt-ij____-tgt-a_s-cmd-a__
+running
+*** [opt-ij____-tgt-a_s-cmd-a__] Error code 1 (ignored)
+
+opt-ij____-tgt-a_s-cmd-a_s
+running
+*** [opt-ij____-tgt-a_s-cmd-a_s] Error code 1 (ignored)
+
+opt-ij____-tgt-a_s-cmd-ai_
+echo running; false
+running
+*** [opt-ij____-tgt-a_s-cmd-ai_] Error code 1 (ignored)
+
+opt-ij____-tgt-a_s-cmd-ais
+running
+*** [opt-ij____-tgt-a_s-cmd-ais] Error code 1 (ignored)
+
+opt-ij____-tgt-ai_-cmd-___
+echo running; false
+running
+*** [opt-ij____-tgt-ai_-cmd-___] Error code 1 (ignored)
+
+opt-ij____-tgt-ai_-cmd-__s
+running
+*** [opt-ij____-tgt-ai_-cmd-__s] Error code 1 (ignored)
+
+opt-ij____-tgt-ai_-cmd-_i_
+echo running; false
+running
+*** [opt-ij____-tgt-ai_-cmd-_i_] Error code 1 (ignored)
+
+opt-ij____-tgt-ai_-cmd-_is
+running
+*** [opt-ij____-tgt-ai_-cmd-_is] Error code 1 (ignored)
+
+opt-ij____-tgt-ai_-cmd-a__
+echo running; false
+running
+*** [opt-ij____-tgt-ai_-cmd-a__] Error code 1 (ignored)
+
+opt-ij____-tgt-ai_-cmd-a_s
+running
+*** [opt-ij____-tgt-ai_-cmd-a_s] Error code 1 (ignored)
+
+opt-ij____-tgt-ai_-cmd-ai_
+echo running; false
+running
+*** [opt-ij____-tgt-ai_-cmd-ai_] Error code 1 (ignored)
+
+opt-ij____-tgt-ai_-cmd-ais
+running
+*** [opt-ij____-tgt-ai_-cmd-ais] Error code 1 (ignored)
+
+opt-ij____-tgt-ais-cmd-___
+running
+*** [opt-ij____-tgt-ais-cmd-___] Error code 1 (ignored)
+
+opt-ij____-tgt-ais-cmd-__s
+running
+*** [opt-ij____-tgt-ais-cmd-__s] Error code 1 (ignored)
+
+opt-ij____-tgt-ais-cmd-_i_
+echo running; false
+running
+*** [opt-ij____-tgt-ais-cmd-_i_] Error code 1 (ignored)
+
+opt-ij____-tgt-ais-cmd-_is
+running
+*** [opt-ij____-tgt-ais-cmd-_is] Error code 1 (ignored)
+
+opt-ij____-tgt-ais-cmd-a__
+running
+*** [opt-ij____-tgt-ais-cmd-a__] Error code 1 (ignored)
+
+opt-ij____-tgt-ais-cmd-a_s
+running
+*** [opt-ij____-tgt-ais-cmd-a_s] Error code 1 (ignored)
+
+opt-ij____-tgt-ais-cmd-ai_
+echo running; false
+running
+*** [opt-ij____-tgt-ais-cmd-ai_] Error code 1 (ignored)
+
+opt-ij____-tgt-ais-cmd-ais
+running
+*** [opt-ij____-tgt-ais-cmd-ais] Error code 1 (ignored)
+
+opt-ij_n__-tgt-___-cmd-___
+echo "echo running; false"
+{ echo running; false
+} || exit $?
+
+opt-ij_n__-tgt-___-cmd-__s
+{ echo running; false
+} || exit $?
+
+opt-ij_n__-tgt-___-cmd-_i_
+echo running; false
+
+opt-ij_n__-tgt-___-cmd-_is
+echo running; false
+
+opt-ij_n__-tgt-___-cmd-a__
+echo running; false
+running
+*** Error code 1 (continuing)
+
+opt-ij_n__-tgt-___-cmd-a_s
+echo running; false
+running
+*** Error code 1 (continuing)
+
+opt-ij_n__-tgt-___-cmd-ai_
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-ij_n__-tgt-___-cmd-ais
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-ij_n__-tgt-__s-cmd-___
+{ echo running; false
+} || exit $?
+
+opt-ij_n__-tgt-__s-cmd-__s
+{ echo running; false
+} || exit $?
+
+opt-ij_n__-tgt-__s-cmd-_i_
+echo running; false
+
+opt-ij_n__-tgt-__s-cmd-_is
+echo running; false
+
+opt-ij_n__-tgt-__s-cmd-a__
+echo running; false
+running
+*** Error code 1 (continuing)
+
+opt-ij_n__-tgt-__s-cmd-a_s
+echo running; false
+running
+*** Error code 1 (continuing)
+
+opt-ij_n__-tgt-__s-cmd-ai_
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-ij_n__-tgt-__s-cmd-ais
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-ij_n__-tgt-_i_-cmd-___
+echo "echo running; false"
+{ echo running; false
+} || exit $?
+
+opt-ij_n__-tgt-_i_-cmd-__s
+{ echo running; false
+} || exit $?
+
+opt-ij_n__-tgt-_i_-cmd-_i_
+echo running; false
+
+opt-ij_n__-tgt-_i_-cmd-_is
+echo running; false
+
+opt-ij_n__-tgt-_i_-cmd-a__
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-ij_n__-tgt-_i_-cmd-a_s
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-ij_n__-tgt-_i_-cmd-ai_
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-ij_n__-tgt-_i_-cmd-ais
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-ij_n__-tgt-_is-cmd-___
+{ echo running; false
+} || exit $?
+
+opt-ij_n__-tgt-_is-cmd-__s
+{ echo running; false
+} || exit $?
+
+opt-ij_n__-tgt-_is-cmd-_i_
+echo running; false
+
+opt-ij_n__-tgt-_is-cmd-_is
+echo running; false
+
+opt-ij_n__-tgt-_is-cmd-a__
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-ij_n__-tgt-_is-cmd-a_s
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-ij_n__-tgt-_is-cmd-ai_
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-ij_n__-tgt-_is-cmd-ais
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-ij_n__-tgt-a__-cmd-___
+echo running; false
+running
+
+opt-ij_n__-tgt-a__-cmd-__s
+running
+
+opt-ij_n__-tgt-a__-cmd-_i_
+echo running; false
+running
+
+opt-ij_n__-tgt-a__-cmd-_is
+running
+
+opt-ij_n__-tgt-a__-cmd-a__
+echo running; false
+running
+
+opt-ij_n__-tgt-a__-cmd-a_s
+running
+
+opt-ij_n__-tgt-a__-cmd-ai_
+echo running; false
+running
+
+opt-ij_n__-tgt-a__-cmd-ais
+running
+
+opt-ij_n__-tgt-a_s-cmd-___
+running
+
+opt-ij_n__-tgt-a_s-cmd-__s
+running
+
+opt-ij_n__-tgt-a_s-cmd-_i_
+echo running; false
+running
+
+opt-ij_n__-tgt-a_s-cmd-_is
+running
+
+opt-ij_n__-tgt-a_s-cmd-a__
+running
+
+opt-ij_n__-tgt-a_s-cmd-a_s
+running
+
+opt-ij_n__-tgt-a_s-cmd-ai_
+echo running; false
+running
+
+opt-ij_n__-tgt-a_s-cmd-ais
+running
+
+opt-ij_n__-tgt-ai_-cmd-___
+echo running; false
+running
+
+opt-ij_n__-tgt-ai_-cmd-__s
+running
+
+opt-ij_n__-tgt-ai_-cmd-_i_
+echo running; false
+running
+
+opt-ij_n__-tgt-ai_-cmd-_is
+running
+
+opt-ij_n__-tgt-ai_-cmd-a__
+echo running; false
+running
+
+opt-ij_n__-tgt-ai_-cmd-a_s
+running
+
+opt-ij_n__-tgt-ai_-cmd-ai_
+echo running; false
+running
+
+opt-ij_n__-tgt-ai_-cmd-ais
+running
+
+opt-ij_n__-tgt-ais-cmd-___
+running
+
+opt-ij_n__-tgt-ais-cmd-__s
+running
+
+opt-ij_n__-tgt-ais-cmd-_i_
+echo running; false
+running
+
+opt-ij_n__-tgt-ais-cmd-_is
+running
+
+opt-ij_n__-tgt-ais-cmd-a__
+running
+
+opt-ij_n__-tgt-ais-cmd-a_s
+running
+
+opt-ij_n__-tgt-ais-cmd-ai_
+echo running; false
+running
+
+opt-ij_n__-tgt-ais-cmd-ais
+running
+
+opt-ijl___-tgt-___-cmd-___
+echo running; false
+running
+*** [opt-ijl___-tgt-___-cmd-___] Error code 1 (ignored)
+
+opt-ijl___-tgt-___-cmd-__s
+echo running; false
+running
+*** [opt-ijl___-tgt-___-cmd-__s] Error code 1 (ignored)
+
+opt-ijl___-tgt-___-cmd-_i_
+echo running; false
+running
+*** [opt-ijl___-tgt-___-cmd-_i_] Error code 1 (ignored)
+
+opt-ijl___-tgt-___-cmd-_is
+echo running; false
+running
+*** [opt-ijl___-tgt-___-cmd-_is] Error code 1 (ignored)
+
+opt-ijl___-tgt-___-cmd-a__
+echo running; false
+running
+*** [opt-ijl___-tgt-___-cmd-a__] Error code 1 (ignored)
+
+opt-ijl___-tgt-___-cmd-a_s
+echo running; false
+running
+*** [opt-ijl___-tgt-___-cmd-a_s] Error code 1 (ignored)
+
+opt-ijl___-tgt-___-cmd-ai_
+echo running; false
+running
+*** [opt-ijl___-tgt-___-cmd-ai_] Error code 1 (ignored)
+
+opt-ijl___-tgt-___-cmd-ais
+echo running; false
+running
+*** [opt-ijl___-tgt-___-cmd-ais] Error code 1 (ignored)
+
+opt-ijl___-tgt-__s-cmd-___
+running
+*** [opt-ijl___-tgt-__s-cmd-___] Error code 1 (ignored)
+
+opt-ijl___-tgt-__s-cmd-__s
+running
+*** [opt-ijl___-tgt-__s-cmd-__s] Error code 1 (ignored)
+
+opt-ijl___-tgt-__s-cmd-_i_
+echo running; false
+running
+*** [opt-ijl___-tgt-__s-cmd-_i_] Error code 1 (ignored)
+
+opt-ijl___-tgt-__s-cmd-_is
+echo running; false
+running
+*** [opt-ijl___-tgt-__s-cmd-_is] Error code 1 (ignored)
+
+opt-ijl___-tgt-__s-cmd-a__
+running
+*** [opt-ijl___-tgt-__s-cmd-a__] Error code 1 (ignored)
+
+opt-ijl___-tgt-__s-cmd-a_s
+running
+*** [opt-ijl___-tgt-__s-cmd-a_s] Error code 1 (ignored)
+
+opt-ijl___-tgt-__s-cmd-ai_
+echo running; false
+running
+*** [opt-ijl___-tgt-__s-cmd-ai_] Error code 1 (ignored)
+
+opt-ijl___-tgt-__s-cmd-ais
+echo running; false
+running
+*** [opt-ijl___-tgt-__s-cmd-ais] Error code 1 (ignored)
+
+opt-ijl___-tgt-_i_-cmd-___
+echo running; false
+running
+*** [opt-ijl___-tgt-_i_-cmd-___] Error code 1 (ignored)
+
+opt-ijl___-tgt-_i_-cmd-__s
+echo running; false
+running
+*** [opt-ijl___-tgt-_i_-cmd-__s] Error code 1 (ignored)
+
+opt-ijl___-tgt-_i_-cmd-_i_
+echo running; false
+running
+*** [opt-ijl___-tgt-_i_-cmd-_i_] Error code 1 (ignored)
+
+opt-ijl___-tgt-_i_-cmd-_is
+echo running; false
+running
+*** [opt-ijl___-tgt-_i_-cmd-_is] Error code 1 (ignored)
+
+opt-ijl___-tgt-_i_-cmd-a__
+echo running; false
+running
+*** [opt-ijl___-tgt-_i_-cmd-a__] Error code 1 (ignored)
+
+opt-ijl___-tgt-_i_-cmd-a_s
+echo running; false
+running
+*** [opt-ijl___-tgt-_i_-cmd-a_s] Error code 1 (ignored)
+
+opt-ijl___-tgt-_i_-cmd-ai_
+echo running; false
+running
+*** [opt-ijl___-tgt-_i_-cmd-ai_] Error code 1 (ignored)
+
+opt-ijl___-tgt-_i_-cmd-ais
+echo running; false
+running
+*** [opt-ijl___-tgt-_i_-cmd-ais] Error code 1 (ignored)
+
+opt-ijl___-tgt-_is-cmd-___
+running
+*** [opt-ijl___-tgt-_is-cmd-___] Error code 1 (ignored)
+
+opt-ijl___-tgt-_is-cmd-__s
+running
+*** [opt-ijl___-tgt-_is-cmd-__s] Error code 1 (ignored)
+
+opt-ijl___-tgt-_is-cmd-_i_
+echo running; false
+running
+*** [opt-ijl___-tgt-_is-cmd-_i_] Error code 1 (ignored)
+
+opt-ijl___-tgt-_is-cmd-_is
+echo running; false
+running
+*** [opt-ijl___-tgt-_is-cmd-_is] Error code 1 (ignored)
+
+opt-ijl___-tgt-_is-cmd-a__
+running
+*** [opt-ijl___-tgt-_is-cmd-a__] Error code 1 (ignored)
+
+opt-ijl___-tgt-_is-cmd-a_s
+running
+*** [opt-ijl___-tgt-_is-cmd-a_s] Error code 1 (ignored)
+
+opt-ijl___-tgt-_is-cmd-ai_
+echo running; false
+running
+*** [opt-ijl___-tgt-_is-cmd-ai_] Error code 1 (ignored)
+
+opt-ijl___-tgt-_is-cmd-ais
+echo running; false
+running
+*** [opt-ijl___-tgt-_is-cmd-ais] Error code 1 (ignored)
+
+opt-ijl___-tgt-a__-cmd-___
+echo running; false
+running
+*** [opt-ijl___-tgt-a__-cmd-___] Error code 1 (ignored)
+
+opt-ijl___-tgt-a__-cmd-__s
+echo running; false
+running
+*** [opt-ijl___-tgt-a__-cmd-__s] Error code 1 (ignored)
+
+opt-ijl___-tgt-a__-cmd-_i_
+echo running; false
+running
+*** [opt-ijl___-tgt-a__-cmd-_i_] Error code 1 (ignored)
+
+opt-ijl___-tgt-a__-cmd-_is
+echo running; false
+running
+*** [opt-ijl___-tgt-a__-cmd-_is] Error code 1 (ignored)
+
+opt-ijl___-tgt-a__-cmd-a__
+echo running; false
+running
+*** [opt-ijl___-tgt-a__-cmd-a__] Error code 1 (ignored)
+
+opt-ijl___-tgt-a__-cmd-a_s
+echo running; false
+running
+*** [opt-ijl___-tgt-a__-cmd-a_s] Error code 1 (ignored)
+
+opt-ijl___-tgt-a__-cmd-ai_
+echo running; false
+running
+*** [opt-ijl___-tgt-a__-cmd-ai_] Error code 1 (ignored)
+
+opt-ijl___-tgt-a__-cmd-ais
+echo running; false
+running
+*** [opt-ijl___-tgt-a__-cmd-ais] Error code 1 (ignored)
+
+opt-ijl___-tgt-a_s-cmd-___
+running
+*** [opt-ijl___-tgt-a_s-cmd-___] Error code 1 (ignored)
+
+opt-ijl___-tgt-a_s-cmd-__s
+running
+*** [opt-ijl___-tgt-a_s-cmd-__s] Error code 1 (ignored)
+
+opt-ijl___-tgt-a_s-cmd-_i_
+echo running; false
+running
+*** [opt-ijl___-tgt-a_s-cmd-_i_] Error code 1 (ignored)
+
+opt-ijl___-tgt-a_s-cmd-_is
+echo running; false
+running
+*** [opt-ijl___-tgt-a_s-cmd-_is] Error code 1 (ignored)
+
+opt-ijl___-tgt-a_s-cmd-a__
+running
+*** [opt-ijl___-tgt-a_s-cmd-a__] Error code 1 (ignored)
+
+opt-ijl___-tgt-a_s-cmd-a_s
+running
+*** [opt-ijl___-tgt-a_s-cmd-a_s] Error code 1 (ignored)
+
+opt-ijl___-tgt-a_s-cmd-ai_
+echo running; false
+running
+*** [opt-ijl___-tgt-a_s-cmd-ai_] Error code 1 (ignored)
+
+opt-ijl___-tgt-a_s-cmd-ais
+echo running; false
+running
+*** [opt-ijl___-tgt-a_s-cmd-ais] Error code 1 (ignored)
+
+opt-ijl___-tgt-ai_-cmd-___
+echo running; false
+running
+*** [opt-ijl___-tgt-ai_-cmd-___] Error code 1 (ignored)
+
+opt-ijl___-tgt-ai_-cmd-__s
+echo running; false
+running
+*** [opt-ijl___-tgt-ai_-cmd-__s] Error code 1 (ignored)
+
+opt-ijl___-tgt-ai_-cmd-_i_
+echo running; false
+running
+*** [opt-ijl___-tgt-ai_-cmd-_i_] Error code 1 (ignored)
+
+opt-ijl___-tgt-ai_-cmd-_is
+echo running; false
+running
+*** [opt-ijl___-tgt-ai_-cmd-_is] Error code 1 (ignored)
+
+opt-ijl___-tgt-ai_-cmd-a__
+echo running; false
+running
+*** [opt-ijl___-tgt-ai_-cmd-a__] Error code 1 (ignored)
+
+opt-ijl___-tgt-ai_-cmd-a_s
+echo running; false
+running
+*** [opt-ijl___-tgt-ai_-cmd-a_s] Error code 1 (ignored)
+
+opt-ijl___-tgt-ai_-cmd-ai_
+echo running; false
+running
+*** [opt-ijl___-tgt-ai_-cmd-ai_] Error code 1 (ignored)
+
+opt-ijl___-tgt-ai_-cmd-ais
+echo running; false
+running
+*** [opt-ijl___-tgt-ai_-cmd-ais] Error code 1 (ignored)
+
+opt-ijl___-tgt-ais-cmd-___
+running
+*** [opt-ijl___-tgt-ais-cmd-___] Error code 1 (ignored)
+
+opt-ijl___-tgt-ais-cmd-__s
+running
+*** [opt-ijl___-tgt-ais-cmd-__s] Error code 1 (ignored)
+
+opt-ijl___-tgt-ais-cmd-_i_
+echo running; false
+running
+*** [opt-ijl___-tgt-ais-cmd-_i_] Error code 1 (ignored)
+
+opt-ijl___-tgt-ais-cmd-_is
+echo running; false
+running
+*** [opt-ijl___-tgt-ais-cmd-_is] Error code 1 (ignored)
+
+opt-ijl___-tgt-ais-cmd-a__
+running
+*** [opt-ijl___-tgt-ais-cmd-a__] Error code 1 (ignored)
+
+opt-ijl___-tgt-ais-cmd-a_s
+running
+*** [opt-ijl___-tgt-ais-cmd-a_s] Error code 1 (ignored)
+
+opt-ijl___-tgt-ais-cmd-ai_
+echo running; false
+running
+*** [opt-ijl___-tgt-ais-cmd-ai_] Error code 1 (ignored)
+
+opt-ijl___-tgt-ais-cmd-ais
+echo running; false
+running
+*** [opt-ijl___-tgt-ais-cmd-ais] Error code 1 (ignored)
+
+opt-ijln__-tgt-___-cmd-___
+echo "echo running; false"
+{ echo running; false
+} || exit $?
+
+opt-ijln__-tgt-___-cmd-__s
+echo "echo running; false"
+{ echo running; false
+} || exit $?
+
+opt-ijln__-tgt-___-cmd-_i_
+echo running; false
+
+opt-ijln__-tgt-___-cmd-_is
+echo running; false
+
+opt-ijln__-tgt-___-cmd-a__
+echo running; false
+running
+*** Error code 1 (continuing)
+
+opt-ijln__-tgt-___-cmd-a_s
+echo running; false
+running
+*** Error code 1 (continuing)
+
+opt-ijln__-tgt-___-cmd-ai_
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-ijln__-tgt-___-cmd-ais
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-ijln__-tgt-__s-cmd-___
+{ echo running; false
+} || exit $?
+
+opt-ijln__-tgt-__s-cmd-__s
+{ echo running; false
+} || exit $?
+
+opt-ijln__-tgt-__s-cmd-_i_
+echo running; false
+
+opt-ijln__-tgt-__s-cmd-_is
+echo running; false
+
+opt-ijln__-tgt-__s-cmd-a__
+echo running; false
+running
+*** Error code 1 (continuing)
+
+opt-ijln__-tgt-__s-cmd-a_s
+echo running; false
+running
+*** Error code 1 (continuing)
+
+opt-ijln__-tgt-__s-cmd-ai_
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-ijln__-tgt-__s-cmd-ais
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-ijln__-tgt-_i_-cmd-___
+echo "echo running; false"
+{ echo running; false
+} || exit $?
+
+opt-ijln__-tgt-_i_-cmd-__s
+echo "echo running; false"
+{ echo running; false
+} || exit $?
+
+opt-ijln__-tgt-_i_-cmd-_i_
+echo running; false
+
+opt-ijln__-tgt-_i_-cmd-_is
+echo running; false
+
+opt-ijln__-tgt-_i_-cmd-a__
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-ijln__-tgt-_i_-cmd-a_s
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-ijln__-tgt-_i_-cmd-ai_
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-ijln__-tgt-_i_-cmd-ais
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-ijln__-tgt-_is-cmd-___
+{ echo running; false
+} || exit $?
+
+opt-ijln__-tgt-_is-cmd-__s
+{ echo running; false
+} || exit $?
+
+opt-ijln__-tgt-_is-cmd-_i_
+echo running; false
+
+opt-ijln__-tgt-_is-cmd-_is
+echo running; false
+
+opt-ijln__-tgt-_is-cmd-a__
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-ijln__-tgt-_is-cmd-a_s
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-ijln__-tgt-_is-cmd-ai_
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-ijln__-tgt-_is-cmd-ais
+echo running; false
+running
+*** Error code 1 (ignored)
+
+opt-ijln__-tgt-a__-cmd-___
+echo running; false
+running
+
+opt-ijln__-tgt-a__-cmd-__s
+echo running; false
+running
+
+opt-ijln__-tgt-a__-cmd-_i_
+echo running; false
+running
+
+opt-ijln__-tgt-a__-cmd-_is
+echo running; false
+running
+
+opt-ijln__-tgt-a__-cmd-a__
+echo running; false
+running
+
+opt-ijln__-tgt-a__-cmd-a_s
+echo running; false
+running
+
+opt-ijln__-tgt-a__-cmd-ai_
+echo running; false
+running
+
+opt-ijln__-tgt-a__-cmd-ais
+echo running; false
+running
+
+opt-ijln__-tgt-a_s-cmd-___
+running
+
+opt-ijln__-tgt-a_s-cmd-__s
+running
+
+opt-ijln__-tgt-a_s-cmd-_i_
+echo running; false
+running
+
+opt-ijln__-tgt-a_s-cmd-_is
+echo running; false
+running
+
+opt-ijln__-tgt-a_s-cmd-a__
+running
+
+opt-ijln__-tgt-a_s-cmd-a_s
+running
+
+opt-ijln__-tgt-a_s-cmd-ai_
+echo running; false
+running
+
+opt-ijln__-tgt-a_s-cmd-ais
+echo running; false
+running
+
+opt-ijln__-tgt-ai_-cmd-___
+echo running; false
+running
+
+opt-ijln__-tgt-ai_-cmd-__s
+echo running; false
+running
+
+opt-ijln__-tgt-ai_-cmd-_i_
+echo running; false
+running
+
+opt-ijln__-tgt-ai_-cmd-_is
+echo running; false
+running
+
+opt-ijln__-tgt-ai_-cmd-a__
+echo running; false
+running
+
+opt-ijln__-tgt-ai_-cmd-a_s
+echo running; false
+running
+
+opt-ijln__-tgt-ai_-cmd-ai_
+echo running; false
+running
+
+opt-ijln__-tgt-ai_-cmd-ais
+echo running; false
+running
+
+opt-ijln__-tgt-ais-cmd-___
+running
+
+opt-ijln__-tgt-ais-cmd-__s
+running
+
+opt-ijln__-tgt-ais-cmd-_i_
+echo running; false
+running
+
+opt-ijln__-tgt-ais-cmd-_is
+echo running; false
+running
+
+opt-ijln__-tgt-ais-cmd-a__
+running
+
+opt-ijln__-tgt-ais-cmd-a_s
+running
+
+opt-ijln__-tgt-ais-cmd-ai_
+echo running; false
+running
+
+opt-ijln__-tgt-ais-cmd-ais
+echo running; false
+running
+exit status 0
diff --git a/unit-tests/sh-flags.mk b/unit-tests/sh-flags.mk
new file mode 100644
index 000000000000..a4e17ca88384
--- /dev/null
+++ b/unit-tests/sh-flags.mk
@@ -0,0 +1,138 @@
+# $NetBSD: sh-flags.mk,v 1.4 2020/12/12 12:19:18 rillig Exp $
+#
+# Tests for the effective RunFlags of a shell command (run/skip, echo/silent,
+# error check, trace), which are controlled by 12 different switches. These
+# switches interact in various non-obvious ways. To analyze the interactions,
+# this test runs each possible combination of these switches, for now.
+#
+# As soon as an interaction of switches is identified as obvious and expected,
+# this particular interaction may be removed from the test, to focus on the
+# remaining ones.
+#
+# See also:
+# Compat_RunCommand
+# JobPrintSpecials
+
+all: .PHONY
+
+opt-ignerr.yes= -i
+opt-jobs.yes= -j1
+opt-loud.no= -d0 # side effect: make stdout unbuffered
+opt-loud.yes= -dl # side effect: make stdout unbuffered
+opt-no-action.yes= -n
+opt-silent.yes= -s
+opt-xtrace.yes= -dx
+tgt-always.yes= .MAKE
+tgt-ignerr.yes= .IGNORE
+tgt-silent.yes= .SILENT
+cmd-always.yes= +
+cmd-ignerr.yes= -
+cmd-silent.yes= @
+
+letter.always.yes= a
+letter.ignerr.yes= i
+letter.jobs.yes= j
+letter.loud.yes= l
+letter.no-action.yes= n
+letter.silent.yes= s
+letter.xtrace.yes= x
+
+.if !defined(OPT_TARGET)
+.for opt-ignerr in no yes
+.for opt-jobs in no yes
+.for opt-loud in no yes
+.for opt-no-action in no yes
+# Only 'no', not 'yes', since job->echo is based trivially on opts.silent.
+.for opt-silent in no
+# Only 'no', not 'yes', since that would add uncontrollable output from
+# reading /etc/profile or similar files.
+.for opt-xtrace in no
+
+target= opt-
+target+= ${letter.ignerr.${opt-ignerr}:U_}
+target+= ${letter.jobs.${opt-jobs}:U_}
+target+= ${letter.loud.${opt-loud}:U_}
+target+= ${letter.no-action.${opt-no-action}:U_}
+target+= ${letter.silent.${opt-silent}:U_}
+target+= ${letter.xtrace.${opt-xtrace}:U_}
+
+.for target in ${target:ts}
+
+MAKE_CMD.${target}= ${MAKE}
+MAKE_CMD.${target}+= ${opt-ignerr.${opt-ignerr}}
+MAKE_CMD.${target}+= ${opt-jobs.${opt-jobs}}
+MAKE_CMD.${target}+= ${opt-loud.${opt-loud}}
+MAKE_CMD.${target}+= ${opt-no-action.${opt-no-action}}
+MAKE_CMD.${target}+= ${opt-silent.${opt-silent}}
+MAKE_CMD.${target}+= ${opt-xtrace.${opt-xtrace}}
+MAKE_CMD.${target}+= -f ${MAKEFILE}
+MAKE_CMD.${target}+= OPT_TARGET=${target}
+MAKE_CMD.${target}+= ${target}
+
+all: ${target}
+${target}: .PHONY
+ @${MAKE_CMD.${target}:M*}
+
+.endfor
+.endfor
+.endfor
+.endfor
+.endfor
+.endfor
+.endfor
+.endif
+
+SILENT.yes= @
+ALWAYS.yes= +
+IGNERR.yes= -
+
+.if defined(OPT_TARGET)
+.for tgt-always in no yes
+.for tgt-ignerr in no yes
+.for tgt-silent in no yes
+.for cmd-always in no yes
+.for cmd-ignerr in no yes
+.for cmd-silent in no yes
+
+target= ${OPT_TARGET}-tgt-
+target+= ${letter.always.${tgt-always}:U_}
+target+= ${letter.ignerr.${tgt-ignerr}:U_}
+target+= ${letter.silent.${tgt-silent}:U_}
+target+= -cmd-
+target+= ${letter.always.${cmd-always}:U_}
+target+= ${letter.ignerr.${cmd-ignerr}:U_}
+target+= ${letter.silent.${cmd-silent}:U_}
+
+.for target in ${target:ts}
+
+${OPT_TARGET}: .WAIT ${target} .WAIT
+.if ${tgt-always} == yes
+${target}: .MAKE
+.endif
+.if ${tgt-ignerr} == yes
+${target}: .IGNORE
+.endif
+.if ${tgt-silent} == yes
+${target}: .SILENT
+.endif
+
+RUNFLAGS.${target}= ${SILENT.${cmd-silent}}${ALWAYS.${cmd-always}}${IGNERR.${cmd-ignerr}}
+.if ${OPT_TARGET:M*i*} || ${tgt-ignerr} == yes || ${cmd-ignerr} == yes
+CMD.${target}= echo running; false
+.else
+CMD.${target}= echo running
+.endif
+
+${target}: .PHONY
+ @+echo hide-from-output
+ @+echo hide-from-output ${target}
+ ${RUNFLAGS.${target}} ${CMD.${target}}
+.endfor
+
+.endfor
+.endfor
+.endfor
+.endfor
+.endfor
+.endfor
+.endif
diff --git a/unit-tests/sh-jobs.exp b/unit-tests/sh-jobs.exp
index 39a9383953dd..ef0c574fceed 100644
--- a/unit-tests/sh-jobs.exp
+++ b/unit-tests/sh-jobs.exp
@@ -1 +1,6 @@
-exit status 0
+comment-with-followup-line: This is printed.
+no-comment: This is printed.
+*** [no-comment] Error code 1
+
+make: stopped in unit-tests
+exit status 1
diff --git a/unit-tests/sh-jobs.mk b/unit-tests/sh-jobs.mk
index 62172c2a0c86..e8d4f976109a 100644
--- a/unit-tests/sh-jobs.mk
+++ b/unit-tests/sh-jobs.mk
@@ -1,9 +1,35 @@
-# $NetBSD: sh-jobs.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+# $NetBSD: sh-jobs.mk,v 1.3 2020/12/11 01:06:10 rillig Exp $
#
# Tests for the "run in jobs mode" part of the "Shell Commands" section
# from the manual page.
-# TODO: Implementation
+# TODO: Tutorial
-all:
- @:;
+.MAKEFLAGS: -j1
+
+all: .PHONY comment .WAIT comment-with-followup-line .WAIT no-comment
+
+# If a shell command starts with a comment character after stripping the
+# leading '@', it is run in ignore-errors mode since the default runChkTmpl
+# would lead to a syntax error in the generated shell file, at least for
+# bash and dash, but not for NetBSD sh and ksh.
+#
+# See JobPrintCommand, cmdTemplate, runIgnTmpl
+comment: .PHONY
+ @# comment
+
+# If a shell command starts with a comment character after stripping the
+# leading '@', it is run in ignore-errors mode.
+#
+# See JobPrintCommand, cmdTemplate, runIgnTmpl
+comment-with-followup-line: .PHONY
+ @# comment${.newline}echo '$@: This is printed.'; false
+ @true
+
+# Without the comment, the commands are run in the default mode, which checks
+# the exit status of every makefile line.
+#
+# See JobPrintCommand, cmdTemplate, runChkTmpl
+no-comment: .PHONY
+ @echo '$@: This is printed.'; false
+ @true
diff --git a/unit-tests/sh-meta-chars.mk b/unit-tests/sh-meta-chars.mk
index a029c73a855c..10e29b4b117b 100644
--- a/unit-tests/sh-meta-chars.mk
+++ b/unit-tests/sh-meta-chars.mk
@@ -1,4 +1,4 @@
-# $NetBSD: sh-meta-chars.mk,v 1.3 2020/11/15 20:20:58 rillig Exp $
+# $NetBSD: sh-meta-chars.mk,v 1.4 2020/12/07 22:27:56 rillig Exp $
#
# Tests for running shell commands that contain meta-characters.
#
@@ -9,7 +9,16 @@
# See also:
# Compat_RunCommand, useShell
-# TODO: Implementation
-
all:
- @:;
+
+# The command "exit 0" contains no special characters, therefore it is
+# run directly via execv, but only if MAKE_NATIVE is defined.
+USING_EXEC!= { echo 'all:; exit 0' | ${MAKE} -r -f - 1>/dev/null 2>&1; } \
+ && echo yes || echo no
+
+# It's hard to do any useful tests that result in the same output.
+# See SED_CMDS.sh-dots, which normalizes the test output for the specific
+# case of the special command '...'.
+.if ${USING_EXEC} != "yes" && ${USING_EXEC} != "no"
+. error
+.endif
diff --git a/unit-tests/shell-csh.mk b/unit-tests/shell-csh.mk
index 590775dbcb5a..99852e33ce16 100644
--- a/unit-tests/shell-csh.mk
+++ b/unit-tests/shell-csh.mk
@@ -1,8 +1,8 @@
-# $NetBSD: shell-csh.mk,v 1.5 2020/10/19 19:14:11 rillig Exp $
+# $NetBSD: shell-csh.mk,v 1.7 2020/12/13 02:09:55 sjg Exp $
#
# Tests for using a C shell for running the commands.
-CSH!= which csh || true
+CSH!= which csh 2> /dev/null || true
# The shell path must be an absolute path.
# This is only obvious in parallel mode since in compat mode,
@@ -11,7 +11,7 @@ CSH!= which csh || true
.SHELL: name="csh" path="${CSH}"
.endif
-# In parallel mode, the commandShell->noPrint command is filtered from
+# In parallel mode, the shell->noPrint command is filtered from
# the output, rather naively (in JobOutput).
#
# Until 2020-10-03, the output in parallel mode was garbled because
diff --git a/unit-tests/suff-add-later.exp b/unit-tests/suff-add-later.exp
index 0556529457f7..663016a672c1 100644
--- a/unit-tests/suff-add-later.exp
+++ b/unit-tests/suff-add-later.exp
@@ -1,6 +1,9 @@
+Adding suffix ".c"
+Adding suffix ".d"
defining transformation from `.c' to `.d'
inserting ".c" (1) at end of list
inserting ".d" (2) at end of list
+Adding suffix ".e"
defining transformation from `.d' to `.e'
inserting ".d" (2) at end of list
inserting ".e" (3) at end of list
@@ -12,4 +15,7 @@ make: don't know how to make issue5c (continuing)
make: don't know how to make issue5d.e (continuing)
make: don't know how to make issue5e.d (continuing)
`all' not remade because of errors.
-exit status 0
+
+Stop.
+make: stopped in unit-tests
+exit status 1
diff --git a/unit-tests/suff-clear-regular.exp b/unit-tests/suff-clear-regular.exp
index f3d73f6e4a2f..75db9b47a55b 100644
--- a/unit-tests/suff-clear-regular.exp
+++ b/unit-tests/suff-clear-regular.exp
@@ -2,4 +2,7 @@ make: don't know how to make .a (continuing)
make: don't know how to make .a.b (continuing)
make: don't know how to make .b.a (continuing)
`all' not remade because of errors.
-exit status 0
+
+Stop.
+make: stopped in unit-tests
+exit status 1
diff --git a/unit-tests/suff-clear-regular.mk b/unit-tests/suff-clear-regular.mk
index 4f98aa374818..227492bae082 100644
--- a/unit-tests/suff-clear-regular.mk
+++ b/unit-tests/suff-clear-regular.mk
@@ -1,4 +1,4 @@
-# $NetBSD: suff-clear-regular.mk,v 1.1 2020/10/20 20:36:53 rillig Exp $
+# $NetBSD: suff-clear-regular.mk,v 1.2 2020/12/07 00:53:30 rillig Exp $
#
# https://gnats.netbsd.org/49086, issue 4:
# Suffix rules do not become regular rules when .SUFFIXES is cleared.
@@ -27,5 +27,4 @@ all: .a .a.b .b.a
# XXX: don't know how to make .a
# XXX: don't know how to make .a.b
# XXX: don't know how to make .b.a
-# XXX: exit status 0
#.MAKEFLAGS: -dg1
diff --git a/unit-tests/suff-clear-single.exp b/unit-tests/suff-clear-single.exp
index f8abe6348b34..aa46ac75f6da 100644
--- a/unit-tests/suff-clear-single.exp
+++ b/unit-tests/suff-clear-single.exp
@@ -1,3 +1,6 @@
make: don't know how to make issue3 (continuing)
`all' not remade because of errors.
-exit status 0
+
+Stop.
+make: stopped in unit-tests
+exit status 1
diff --git a/unit-tests/suff-incomplete.exp b/unit-tests/suff-incomplete.exp
new file mode 100644
index 000000000000..721f20eb33db
--- /dev/null
+++ b/unit-tests/suff-incomplete.exp
@@ -0,0 +1,42 @@
+ParseReadLine (9): '.SUFFIXES:'
+ParseDoDependency(.SUFFIXES:)
+Clearing all suffixes
+ParseReadLine (11): '.SUFFIXES: .a .b .c'
+ParseDoDependency(.SUFFIXES: .a .b .c)
+Adding suffix ".a"
+Adding suffix ".b"
+Adding suffix ".c"
+ParseReadLine (17): '.a.b:'
+ParseDoDependency(.a.b:)
+defining transformation from `.a' to `.b'
+inserting ".a" (1) at end of list
+inserting ".b" (2) at end of list
+ParseReadLine (21): '.a.c: ${.PREFIX}.dependency'
+deleting incomplete transformation from `.a' to `.b'
+ParseDoDependency(.a.c: ${.PREFIX}.dependency)
+defining transformation from `.a' to `.c'
+inserting ".a" (1) at end of list
+inserting ".c" (3) at end of list
+# LinkSource: added child .a.c - ${.PREFIX}.dependency
+# .a.c, made UNMADE, type OP_DEPENDS|OP_TRANSFORM, flags none
+# ${.PREFIX}.dependency, made UNMADE, type none, flags none
+ParseReadLine (23): '.DEFAULT:'
+transformation .a.c complete
+ParseDoDependency(.DEFAULT:)
+ParseReadLine (24): ' : Making ${.TARGET} from ${.IMPSRC} all ${.ALLSRC} by default.'
+transformation .DEFAULT complete
+Wildcard expanding "all"...
+SuffFindDeps "all"
+ No known suffix on all. Using .NULL suffix
+adding suffix rules
+Wildcard expanding "suff-incomplete.c"...suffix is ".c"...
+SuffFindDeps "suff-incomplete.c"
+ trying suff-incomplete.a...not there
+Wildcard expanding "suff-incomplete.c"...suffix is ".c"...
+: Making suff-incomplete.c from suff-incomplete.c all by default.
+Wildcard expanding "all"...
+SuffFindDeps ".END"
+ No known suffix on .END. Using .NULL suffix
+adding suffix rules
+Wildcard expanding ".END"...
+exit status 0
diff --git a/unit-tests/suff-incomplete.mk b/unit-tests/suff-incomplete.mk
new file mode 100644
index 000000000000..474c7aaab664
--- /dev/null
+++ b/unit-tests/suff-incomplete.mk
@@ -0,0 +1,31 @@
+# $NetBSD: suff-incomplete.mk,v 1.2 2020/11/22 11:05:49 rillig Exp $
+#
+# Tests incomplete transformation rules, which are ignored.
+
+all: suff-incomplete.c
+
+.MAKEFLAGS: -dps
+
+.SUFFIXES:
+
+.SUFFIXES: .a .b .c
+
+# This rule has no commands and no dependencies, therefore it is incomplete
+# and not added to the transformation rules.
+#
+# See Suff_EndTransform.
+.a.b:
+
+# This rule has a dependency, therefore it is a complete transformation.
+# Its commands are taken from a .DEFAULT target, if there is any.
+.a.c: ${.PREFIX}.dependency
+
+.DEFAULT:
+ : Making ${.TARGET} from ${.IMPSRC} all ${.ALLSRC} by default.
+
+# The debug log says "transformation .DEFAULT complete", which seems wrong
+# on the first sight. It is intentionally done though, in the call to
+# GNode_New(".DEFAULT").
+
+# XXX: The output of this test says "Making suff-incomplete.c from
+# suff-incomplete.c". It doesn't make sense to make something out of itself.
diff --git a/unit-tests/suff-lookup.exp b/unit-tests/suff-lookup.exp
index 6714e7975e18..a8f893fa9492 100644
--- a/unit-tests/suff-lookup.exp
+++ b/unit-tests/suff-lookup.exp
@@ -1,3 +1,9 @@
+Adding suffix ".c"
+Adding suffix ".cc"
+Adding suffix ".ccc"
+Adding suffix ".short"
+Adding suffix ".sho"
+Adding suffix ".dead-end"
defining transformation from `.ccc' to `.cc'
inserting ".ccc" (3) at end of list
inserting ".cc" (2) at end of list
@@ -18,22 +24,29 @@ defining transformation from `.dead-end' to `.short'
inserting ".dead-end" (6) at end of list
inserting ".short" (4) at end of list
transformation .dead-end.short complete
+Clearing all suffixes
+Adding suffix ".c"
+Adding suffix ".cc"
+Adding suffix ".ccc"
inserting ".ccc" (3) at end of list
inserting ".cc" (2) at end of list
inserting ".c" (1) at end of list
inserting ".ccc" (3) at end of list
+Adding suffix ".short"
inserting ".short" (4) at end of list
inserting ".c" (1) at end of list
+Adding suffix ".sho"
inserting ".sho" (5) at end of list
inserting ".c" (1) at end of list
+Adding suffix ".dead-end"
inserting ".dead-end" (6) at end of list
inserting ".short" (4) at end of list
Wildcard expanding "all"...
-SuffFindDeps (all)
+SuffFindDeps "all"
No known suffix on all. Using .NULL suffix
adding suffix rules
Wildcard expanding "suff-lookup.cc"...suffix is ".cc"...
-SuffFindDeps (suff-lookup.cc)
+SuffFindDeps "suff-lookup.cc"
trying suff-lookup.ccc...not there
trying suff-lookup.c...not there
trying suff-lookup.short...not there
@@ -44,14 +57,14 @@ SuffFindDeps (suff-lookup.cc)
suffix is ".ccc"...
suffix is ".c"...
suffix is ".sho"...
-SuffFindDeps (suff-lookup.sho)
+SuffFindDeps "suff-lookup.sho"
suffix is ".sho"...
: 'Making suff-lookup.sho out of nothing.'
: 'Making suff-lookup.c from suff-lookup.sho.'
: 'Making suff-lookup.ccc from suff-lookup.c.'
: 'Making suff-lookup.cc from suff-lookup.ccc.'
Wildcard expanding "all"...
-SuffFindDeps (.END)
+SuffFindDeps ".END"
No known suffix on .END. Using .NULL suffix
adding suffix rules
Wildcard expanding ".END"...
diff --git a/unit-tests/suff-main-several.exp b/unit-tests/suff-main-several.exp
new file mode 100644
index 000000000000..c938820c1eaf
--- /dev/null
+++ b/unit-tests/suff-main-several.exp
@@ -0,0 +1,141 @@
+ParseReadLine (8): '.1.2 .1.3 .1.4:'
+ParseDoDependency(.1.2 .1.3 .1.4:)
+Setting main node to ".1.2"
+ParseReadLine (9): ' : Making ${.TARGET} from ${.IMPSRC}.'
+ParseReadLine (14): 'next-main:'
+ParseDoDependency(next-main:)
+ParseReadLine (15): ' : Making ${.TARGET}'
+ParseReadLine (19): '.SUFFIXES: .1 .2 .3 .4'
+ParseDoDependency(.SUFFIXES: .1 .2 .3 .4)
+Adding suffix ".1"
+Adding suffix ".2"
+Setting main node from ".1.2" back to null
+defining transformation from `.1' to `.2'
+inserting ".1" (1) at end of list
+inserting ".2" (2) at end of list
+Setting main node to ".1.3"
+Adding suffix ".3"
+Setting main node from ".1.3" back to null
+defining transformation from `.1' to `.3'
+inserting ".1" (1) at end of list
+inserting ".3" (3) at end of list
+Setting main node to ".1.4"
+Adding suffix ".4"
+Setting main node from ".1.4" back to null
+defining transformation from `.1' to `.4'
+inserting ".1" (1) at end of list
+inserting ".4" (4) at end of list
+Setting main node to "next-main"
+ParseReadLine (24): '.SUFFIXES:'
+ParseDoDependency(.SUFFIXES:)
+Clearing all suffixes
+ParseReadLine (32): '.SUFFIXES: .4 .3 .2 .1'
+ParseDoDependency(.SUFFIXES: .4 .3 .2 .1)
+Adding suffix ".4"
+Adding suffix ".3"
+Adding suffix ".2"
+Adding suffix ".1"
+ParseReadLine (33): '.SUFFIXES:'
+ParseDoDependency(.SUFFIXES:)
+Clearing all suffixes
+ParseReadLine (34): '.SUFFIXES: .1 .2 .3 .4'
+ParseDoDependency(.SUFFIXES: .1 .2 .3 .4)
+Adding suffix ".1"
+Adding suffix ".2"
+Adding suffix ".3"
+Adding suffix ".4"
+ParseReadLine (35): '.SUFFIXES:'
+ParseDoDependency(.SUFFIXES:)
+Clearing all suffixes
+ParseReadLine (36): '.SUFFIXES: .4 .3 .2 .1'
+ParseDoDependency(.SUFFIXES: .4 .3 .2 .1)
+Adding suffix ".4"
+Adding suffix ".3"
+Adding suffix ".2"
+Adding suffix ".1"
+ParseReadLine (38): 'suff-main-several.1:'
+ParseDoDependency(suff-main-several.1:)
+ParseReadLine (39): ' : Making ${.TARGET} out of nothing.'
+ParseReadLine (40): 'next-main: suff-main-several.{2,3,4}'
+ParseDoDependency(next-main: suff-main-several.{2,3,4})
+# LinkSource: added child next-main - suff-main-several.{2,3,4}
+# next-main, made UNMADE, type OP_DEPENDS|OP_HAS_COMMANDS, flags none
+# suff-main-several.{2,3,4}, made UNMADE, type none, flags none
+ParseReadLine (42): '.MAKEFLAGS: -d0 -dg1'
+ParseDoDependency(.MAKEFLAGS: -d0 -dg1)
+#*** Input graph:
+# .1.2, made UNMADE, type OP_TRANSFORM, flags none
+# .1.3, made UNMADE, type OP_TRANSFORM, flags none
+# .1.4, made UNMADE, type OP_TRANSFORM, flags none
+# next-main, made UNMADE, type OP_DEPENDS|OP_HAS_COMMANDS, flags none
+# suff-main-several.1, made UNMADE, type OP_DEPENDS|OP_HAS_COMMANDS, flags none
+# suff-main-several.{2,3,4}, made UNMADE, type none, flags none
+
+
+#
+# Files that are only sources:
+# .1.2 [.1.2]
+# .1.3 [.1.3]
+# .1.4 [.1.4]
+# suff-main-several.{2,3,4} [suff-main-several.{2,3,4}]
+#*** Global Variables:
+.ALLTARGETS = .1.2 .1.3 .1.4 next-main suff-main-several.1 suff-main-several.{2,3,4}
+.CURDIR = <curdir>
+.INCLUDES =
+.LIBS =
+.MAKE = <details omitted>
+.MAKE.DEPENDFILE = <details omitted>
+.MAKE.GID = <details omitted>
+.MAKE.LEVEL = <details omitted>
+.MAKE.MAKEFILES = <details omitted>
+.MAKE.MAKEFILE_PREFERENCE = <details omitted>
+.MAKE.OS = <details omitted>
+.MAKE.PID = <details omitted>
+.MAKE.PPID = <details omitted>
+.MAKE.UID = <details omitted>
+.MAKEFLAGS = -r -k -d mps -d 0 -d g1
+.MAKEOVERRIDES =
+.OBJDIR = <curdir>
+.PATH = . <curdir>
+.TARGETS =
+.newline =
+
+MACHINE = <details omitted>
+MACHINE_ARCH = <details omitted>
+MAKE = <details omitted>
+MFLAGS = -r -k -d mps -d 0 -d g1
+#*** Command-line Variables:
+.MAKE.LEVEL.ENV = MAKELEVEL
+
+#*** Directory Cache:
+# Stats: 0 hits 2 misses 0 near misses 0 losers (0%)
+# refs hits directory
+# 1 0 <curdir>
+# 1 0 .
+
+#*** Suffixes:
+# ".4" (num 1, ref 1)
+# To:
+# From:
+# Search Path:
+# ".3" (num 2, ref 1)
+# To:
+# From:
+# Search Path:
+# ".2" (num 3, ref 1)
+# To:
+# From:
+# Search Path:
+# ".1" (num 4, ref 1)
+# To:
+# From:
+# Search Path:
+#*** Transformations:
+make: don't know how to make suff-main-several.2 (continuing)
+make: don't know how to make suff-main-several.3 (continuing)
+make: don't know how to make suff-main-several.4 (continuing)
+`next-main' not remade because of errors.
+
+Stop.
+make: stopped in unit-tests
+exit status 1
diff --git a/unit-tests/suff-main-several.mk b/unit-tests/suff-main-several.mk
new file mode 100644
index 000000000000..bcc21db5e128
--- /dev/null
+++ b/unit-tests/suff-main-several.mk
@@ -0,0 +1,42 @@
+# $NetBSD: suff-main-several.mk,v 1.1 2020/11/22 20:36:17 rillig Exp $
+#
+# Demonstrate that an inference rule is considered the main target if its
+# suffixes are not known at the point of declaration.
+
+.MAKEFLAGS: -dmps
+
+.1.2 .1.3 .1.4:
+ : Making ${.TARGET} from ${.IMPSRC}.
+
+# At this point, the above targets are normal targets.
+# The target '.1.2' is now the default main target.
+
+next-main:
+ : Making ${.TARGET}
+
+# At this point, 'next-main' is just a regular target.
+
+.SUFFIXES: .1 .2 .3 .4
+
+# Since the targets '.1.2', '.1.3' and '.1.4' have now been turned into
+# transformation rules, 'next-main' is the default main target now.
+
+.SUFFIXES: # clear all
+
+# At this point, 'next-main' is still the default main target, even though
+# it is not the first regular target anymore.
+
+# Define and undefine the suffixes, changing their order.
+# XXX: This should have no effect, but as of 2020-11-22, it does.
+# For some reason, mentioning the suffixes in reverse order disables them.
+.SUFFIXES: .4 .3 .2 .1
+.SUFFIXES: # none
+.SUFFIXES: .1 .2 .3 .4
+.SUFFIXES: # none
+.SUFFIXES: .4 .3 .2 .1
+
+suff-main-several.1:
+ : Making ${.TARGET} out of nothing.
+next-main: suff-main-several.{2,3,4}
+
+.MAKEFLAGS: -d0 -dg1
diff --git a/unit-tests/suff-phony.exp b/unit-tests/suff-phony.exp
new file mode 100644
index 000000000000..13f57f8ec2c6
--- /dev/null
+++ b/unit-tests/suff-phony.exp
@@ -0,0 +1,13 @@
+Adding suffix ".c"
+defining transformation from `.c' to `'
+inserting ".c" (1) at end of list
+inserting "" (0) at end of list
+transformation .c complete
+SuffFindDeps "all"
+ No valid suffix on all
+SuffFindDeps ".END"
+ No known suffix on .END. Using .NULL suffix
+adding suffix rules
+ trying .END.c...not there
+Wildcard expanding ".END"...
+exit status 0
diff --git a/unit-tests/suff-phony.mk b/unit-tests/suff-phony.mk
new file mode 100644
index 000000000000..f206187c7e85
--- /dev/null
+++ b/unit-tests/suff-phony.mk
@@ -0,0 +1,21 @@
+# $NetBSD: suff-phony.mk,v 1.1 2020/11/23 15:00:32 rillig Exp $
+#
+# Test that .PHONY targets are not resolved using suffix rules.
+#
+# The purpose of the .PHONY attribute is to mark them as independent from the
+# file system.
+#
+# See also:
+# FindDepsRegular, Ctrl+F OP_PHONY
+
+.MAKEFLAGS: -ds
+
+all: .PHONY
+
+.SUFFIXES: .c
+
+.c:
+ : Making ${.TARGET} from ${.IMPSRC}.
+
+all.c:
+ : Making ${.TARGET} out of nothing.
diff --git a/unit-tests/suff-rebuild.exp b/unit-tests/suff-rebuild.exp
index b5bb60c23477..ccb423a6086a 100644
--- a/unit-tests/suff-rebuild.exp
+++ b/unit-tests/suff-rebuild.exp
@@ -1,5 +1,73 @@
-: from nothing to a
-: from a to b
-: from b to c
-: from c to nothing
+ParseReadLine (10): '.SUFFIXES:'
+ParseDoDependency(.SUFFIXES:)
+Clearing all suffixes
+ParseReadLine (12): '.SUFFIXES: .a .b .c'
+ParseDoDependency(.SUFFIXES: .a .b .c)
+Adding suffix ".a"
+Adding suffix ".b"
+Adding suffix ".c"
+ParseReadLine (14): 'suff-rebuild-example.a:'
+ParseDoDependency(suff-rebuild-example.a:)
+Adding "suff-rebuild-example.a" to all targets.
+ParseReadLine (15): ' : Making ${.TARGET} out of nothing.'
+ParseReadLine (17): '.a.b:'
+ParseDoDependency(.a.b:)
+defining transformation from `.a' to `.b'
+inserting ".a" (1) at end of list
+inserting ".b" (2) at end of list
+ParseReadLine (18): ' : Making ${.TARGET} from ${.IMPSRC}.'
+ParseReadLine (19): '.b.c:'
+transformation .a.b complete
+ParseDoDependency(.b.c:)
+defining transformation from `.b' to `.c'
+inserting ".b" (2) at end of list
+inserting ".c" (3) at end of list
+ParseReadLine (20): ' : Making ${.TARGET} from ${.IMPSRC}.'
+ParseReadLine (21): '.c:'
+transformation .b.c complete
+ParseDoDependency(.c:)
+defining transformation from `.c' to `'
+inserting ".c" (3) at end of list
+inserting "" (0) at end of list
+ParseReadLine (22): ' : Making ${.TARGET} from ${.IMPSRC}.'
+ParseReadLine (44): '.SUFFIXES: .c .b .a'
+transformation .c complete
+ParseDoDependency(.SUFFIXES: .c .b .a)
+Adding ".END" to all targets.
+Wildcard expanding "all"...
+SuffFindDeps "all"
+ No known suffix on all. Using .NULL suffix
+adding suffix rules
+ trying all.c...not there
+ trying all.b...not there
+ trying all.a...not there
+Wildcard expanding "suff-rebuild-example"...
+SuffFindDeps "suff-rebuild-example"
+ No known suffix on suff-rebuild-example. Using .NULL suffix
+adding suffix rules
+ trying suff-rebuild-example.c...not there
+ trying suff-rebuild-example.b...not there
+ trying suff-rebuild-example.a...got it
+Adding "suff-rebuild-example.b" to all targets.
+ applying .a -> .b to "suff-rebuild-example.b"
+Adding "suff-rebuild-example.c" to all targets.
+ applying .b -> .c to "suff-rebuild-example.c"
+ applying .c -> to "suff-rebuild-example"
+suffix is ".c"...
+suffix is ".b"...
+suffix is ".a"...
+SuffFindDeps "suff-rebuild-example.a"
+suffix is ".a"...
+: Making suff-rebuild-example.a out of nothing.
+: Making suff-rebuild-example.b from suff-rebuild-example.a.
+: Making suff-rebuild-example.c from suff-rebuild-example.b.
+: Making suff-rebuild-example from suff-rebuild-example.c.
+Wildcard expanding "all"...
+SuffFindDeps ".END"
+ No known suffix on .END. Using .NULL suffix
+adding suffix rules
+ trying .END.c...not there
+ trying .END.b...not there
+ trying .END.a...not there
+Wildcard expanding ".END"...
exit status 0
diff --git a/unit-tests/suff-rebuild.mk b/unit-tests/suff-rebuild.mk
index d190eaf4c6e8..a1ce89402a01 100644
--- a/unit-tests/suff-rebuild.mk
+++ b/unit-tests/suff-rebuild.mk
@@ -1,31 +1,42 @@
-# $NetBSD: suff-rebuild.mk,v 1.2 2020/10/18 16:12:39 rillig Exp $
+# $NetBSD: suff-rebuild.mk,v 1.6 2020/11/21 12:01:16 rillig Exp $
#
# Demonstrates what happens to transformation rules (called inference rules
# by POSIX) when all suffixes are deleted.
all: suff-rebuild-example
+.MAKEFLAGS: -dpst
+
.SUFFIXES:
.SUFFIXES: .a .b .c
suff-rebuild-example.a:
- : from nothing to a
+ : Making ${.TARGET} out of nothing.
.a.b:
- : from a to b
+ : Making ${.TARGET} from ${.IMPSRC}.
.b.c:
- : from b to c
+ : Making ${.TARGET} from ${.IMPSRC}.
.c:
- : from c to nothing
+ : Making ${.TARGET} from ${.IMPSRC}.
-# XXX: At a quick glance, the code in SuffScanTargets looks as if it were
+# XXX: At a quick glance, the code in SuffUpdateTarget looks as if it were
# possible to delete the suffixes in the middle of the makefile, add back
# the suffixes from before, and have the transformation rules preserved.
#
# As of 2020-09-25, uncommenting the following line results in the error
# message "don't know how to make suff-rebuild-example" though.
#
+# If this is a bug, the actual cause is probably that when a suffix
+# transformation rule is defined, it is not added to the global list of
+# targets, see Suff_EndTransform. Later, UpdateTargets iterates over exactly
+# this global list of targets though.
+#
+# If UpdateTargets were to iterate over 'transforms' as well, it still
+# wouldn't work because the condition 'ptr == target->name' skips these
+# transformation rules.
+
#.SUFFIXES:
# Add the suffixes back. It should not matter that the order of the suffixes
diff --git a/unit-tests/suff-self.exp b/unit-tests/suff-self.exp
index 4e70762209a2..6192c508ab96 100644
--- a/unit-tests/suff-self.exp
+++ b/unit-tests/suff-self.exp
@@ -1,3 +1,6 @@
make: Graph cycles through suff-self.suff
`all' not remade because of errors.
-exit status 0
+
+Stop.
+make: stopped in unit-tests
+exit status 1
diff --git a/unit-tests/suff-transform-debug.exp b/unit-tests/suff-transform-debug.exp
new file mode 100644
index 000000000000..70181be4b97f
--- /dev/null
+++ b/unit-tests/suff-transform-debug.exp
@@ -0,0 +1,62 @@
+#*** Input graph:
+# all, made UNMADE, type OP_DEPENDS, flags none
+
+
+#
+# Files that are only sources:
+#*** Global Variables:
+.ALLTARGETS = all
+.CURDIR = <curdir>
+.INCLUDES =
+.LIBS =
+.MAKE = <details omitted>
+.MAKE.DEPENDFILE = <details omitted>
+.MAKE.GID = <details omitted>
+.MAKE.LEVEL = <details omitted>
+.MAKE.MAKEFILES = <details omitted>
+.MAKE.MAKEFILE_PREFERENCE = <details omitted>
+.MAKE.OS = <details omitted>
+.MAKE.PID = <details omitted>
+.MAKE.PPID = <details omitted>
+.MAKE.UID = <details omitted>
+.MAKEFLAGS = -r -k -d g1
+.MAKEOVERRIDES =
+.OBJDIR = <curdir>
+.PATH = . <curdir>
+.TARGETS =
+.newline =
+
+MACHINE = <details omitted>
+MACHINE_ARCH = <details omitted>
+MAKE = <details omitted>
+MFLAGS = -r -k -d g1
+#*** Command-line Variables:
+.MAKE.LEVEL.ENV = MAKELEVEL
+
+#*** Directory Cache:
+# Stats: 0 hits 2 misses 0 near misses 0 losers (0%)
+# refs hits directory
+# 1 0 <curdir>
+# 1 0 .
+
+#*** Suffixes:
+# ".a" (num 1, ref 2)
+# To:
+# From: .cpp
+# Search Path:
+# ".c" (num 2, ref 2)
+# To: .cpp
+# From:
+# Search Path:
+# ".cpp" (num 3, ref 3)
+# To: .a
+# From: .c
+# Search Path:
+#*** Transformations:
+.c.cpp :
+ : Making ${.TARGET} from impsrc ${.IMPSRC} allsrc ${.ALLSRC}.
+
+.cpp.a :
+ : Making ${.TARGET} from impsrc ${.IMPSRC} allsrc ${.ALLSRC}.
+
+exit status 0
diff --git a/unit-tests/suff-transform-debug.mk b/unit-tests/suff-transform-debug.mk
new file mode 100644
index 000000000000..d206a0f691a2
--- /dev/null
+++ b/unit-tests/suff-transform-debug.mk
@@ -0,0 +1,12 @@
+# $NetBSD: suff-transform-debug.mk,v 1.1 2020/11/22 23:45:20 rillig Exp $
+#
+# Test how the debug output of transformation rules looks.
+
+.MAKEFLAGS: -dg1
+
+.SUFFIXES: .a .c .cpp
+
+.c.cpp .cpp.a:
+ : Making ${.TARGET} from impsrc ${.IMPSRC} allsrc ${.ALLSRC}.
+
+all:
diff --git a/unit-tests/suff-transform-endless.exp b/unit-tests/suff-transform-endless.exp
index 5cd267306b92..620c46626e22 100644
--- a/unit-tests/suff-transform-endless.exp
+++ b/unit-tests/suff-transform-endless.exp
@@ -1,4 +1,46 @@
-make: "suff-transform-endless.mk" line 36: prevent endless loop
+Adding suffix ".c"
+Adding suffix ".d"
+defining transformation from `.c' to `.d'
+inserting ".c" (1) at end of list
+inserting ".d" (2) at end of list
+Adding suffix ".e"
+defining transformation from `.d' to `.e'
+inserting ".d" (2) at end of list
+inserting ".e" (3) at end of list
+Adding suffix ".f"
+defining transformation from `.e' to `'
+inserting ".e" (3) at end of list
+inserting "" (0) at end of list
+defining transformation from `.e' to `.f'
+inserting ".e" (3) at end of list
+inserting ".f" (4) at end of list
+defining transformation from `.f' to `.e'
+inserting ".f" (4) at end of list
+inserting ".e" (3) at end of list
+transformation .e complete
+transformation .e.f complete
+transformation .f.e complete
+Wildcard expanding "all"...
+SuffFindDeps "all"
+ No known suffix on all. Using .NULL suffix
+adding suffix rules
+ trying all.e...not there
+ trying all.d...not there
+ trying all.f...not there
+ trying all.c...not there
+ trying all.e...not there
+FindThem: skipping duplicate "all.e"
+Wildcard expanding "issue6.f"...suffix is ".f"...
+SuffFindDeps "issue6.f"
+ trying issue6.e...not there
+ trying issue6.d...not there
+ trying issue6.f...got it
+ applying .f -> .e to "issue6.e"
+ applying .e -> .f to "issue6.f"
+suffix is ".e"...
+make: Graph cycles through issue6.f
+`all' not remade because of errors.
+Stop.
make: stopped in unit-tests
exit status 1
diff --git a/unit-tests/suff-transform-endless.mk b/unit-tests/suff-transform-endless.mk
index 546e24f398ad..d3b1cf24d8aa 100644
--- a/unit-tests/suff-transform-endless.mk
+++ b/unit-tests/suff-transform-endless.mk
@@ -1,4 +1,4 @@
-# $NetBSD: suff-transform-endless.mk,v 1.1 2020/10/20 20:36:53 rillig Exp $
+# $NetBSD: suff-transform-endless.mk,v 1.4 2020/11/23 14:47:12 rillig Exp $
# https://gnats.netbsd.org/49086, issue 6:
# Transformation search can end up in an infinite loop.
@@ -8,6 +8,8 @@
# exist, make would try to make .f from .e and then infinitely try
# to do .e from .d and vice versa.
+.MAKEFLAGS: -ds
+
all: issue6.f
.c.d .d.c .d .d.e .e.d:
@@ -16,7 +18,7 @@ all: issue6.f
.SUFFIXES: .c .d .e .f
.e .e.f .f.e:
- : 'Making ${.TARGET} out of nothing.'
+ : 'Making ${.TARGET} from ${.IMPSRC}.'
# XXX: As of 2020-10-20, the result is unexpected.
# XXX: .d.c is not a transformation rule.
@@ -33,4 +35,5 @@ all: issue6.f
# XXX: Found 'all' as '(not found)'
# XXX: trying all.e, all.e, all.f, all.e, all.e, repeatedly.
#.MAKEFLAGS: -dg1
-.error prevent endless loop
+
+# Before 24-11-2020, resolving all.e ran into an endless loop.
diff --git a/unit-tests/suff-transform-expand.exp b/unit-tests/suff-transform-expand.exp
index 178e264769af..c1821852707d 100644
--- a/unit-tests/suff-transform-expand.exp
+++ b/unit-tests/suff-transform-expand.exp
@@ -2,4 +2,7 @@
make: don't know how to make .first (continuing)
: 'Making issue11.second out of nothing.'
`all' not remade because of errors.
-exit status 0
+
+Stop.
+make: stopped in unit-tests
+exit status 1
diff --git a/unit-tests/suff-transform-select.exp b/unit-tests/suff-transform-select.exp
index 8470ba3853dc..29065154c891 100644
--- a/unit-tests/suff-transform-select.exp
+++ b/unit-tests/suff-transform-select.exp
@@ -1,4 +1,47 @@
-make: "suff-transform-select.mk" line 28: prevent endless loop
+Adding suffix ".c"
+Adding suffix ".d"
+defining transformation from `.c' to `.d'
+inserting ".c" (1) at end of list
+inserting ".d" (2) at end of list
+Adding suffix ".e"
+defining transformation from `.d' to `.e'
+inserting ".d" (2) at end of list
+inserting ".e" (3) at end of list
+Adding suffix ".f"
+Adding suffix ".g"
+defining transformation from `.e' to `'
+inserting ".e" (3) at end of list
+inserting "" (0) at end of list
+defining transformation from `.e' to `.f'
+inserting ".e" (3) at end of list
+inserting ".f" (4) at end of list
+defining transformation from `.f' to `.e'
+inserting ".f" (4) at end of list
+inserting ".e" (3) at end of list
+transformation .e complete
+transformation .e.f complete
+transformation .f.e complete
+Wildcard expanding "all"...
+SuffFindDeps "all"
+ No known suffix on all. Using .NULL suffix
+adding suffix rules
+ trying all.e...not there
+ trying all.d...not there
+ trying all.f...not there
+ trying all.c...not there
+ trying all.e...not there
+FindThem: skipping duplicate "all.e"
+Wildcard expanding "issue10.e"...suffix is ".e"...
+SuffFindDeps "issue10.e"
+ trying issue10.d...got it
+suffix is ".d"...
+SuffFindDeps "issue10.d"
+ trying issue10.c...not there
+suffix is ".d"...
+: 'Making issue10.d out of nothing.'
+make: don't know how to make issue10.e (continuing)
+`all' not remade because of errors.
+Stop.
make: stopped in unit-tests
exit status 1
diff --git a/unit-tests/suff-transform-select.mk b/unit-tests/suff-transform-select.mk
index d4ae37086e84..a5af089d7714 100644
--- a/unit-tests/suff-transform-select.mk
+++ b/unit-tests/suff-transform-select.mk
@@ -1,4 +1,4 @@
-# $NetBSD: suff-transform-select.mk,v 1.1 2020/10/20 20:36:53 rillig Exp $
+# $NetBSD: suff-transform-select.mk,v 1.3 2020/11/23 14:47:12 rillig Exp $
#
# https://gnats.netbsd.org/49086, issue 10:
# Explicit dependencies affect transformation rule selection.
@@ -8,6 +8,8 @@
# The bug was that if issue10.d had an explicit dependency on issue10.f,
# it would choose .f.e instead.
+.MAKEFLAGS: -ds
+
_!= rm -f issue10.*
all: issue10.e
@@ -25,4 +27,5 @@ issue10.d issue10.f:
# XXX: see suff-bug-endless, which must be fixed first.
#.MAKEFLAGS: -dg1
-.error prevent endless loop
+
+# Before 24-11-2020, resolving all.e ran into an endless loop.
diff --git a/unit-tests/use-inference.exp b/unit-tests/use-inference.exp
index 14ecf0550574..135deabc918e 100644
--- a/unit-tests/use-inference.exp
+++ b/unit-tests/use-inference.exp
@@ -1,4 +1,7 @@
Building use-inference.from from nothing
make: don't know how to make use-inference.to (continuing)
`all' not remade because of errors.
-exit status 0
+
+Stop.
+make: stopped in unit-tests
+exit status 1
diff --git a/unit-tests/use-inference.mk b/unit-tests/use-inference.mk
index cde3c772edaa..550331a54d97 100644
--- a/unit-tests/use-inference.mk
+++ b/unit-tests/use-inference.mk
@@ -1,4 +1,4 @@
-# $NetBSD: use-inference.mk,v 1.2 2020/11/05 00:41:04 rillig Exp $
+# $NetBSD: use-inference.mk,v 1.3 2020/12/07 00:53:30 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
@@ -34,5 +34,5 @@ use-inference.from: # assume it exists
# 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.
+# Until 2020-12-07, despite the error message "don't know how to make",
+# the exit status was 0. This was inconsistent.
diff --git a/unit-tests/var-op-default.mk b/unit-tests/var-op-default.mk
index afb0c55f827c..ca4fbcc27c88 100644
--- a/unit-tests/var-op-default.mk
+++ b/unit-tests/var-op-default.mk
@@ -1,9 +1,77 @@
-# $NetBSD: var-op-default.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+# $NetBSD: var-op-default.mk,v 1.3 2020/12/07 21:35:43 rillig Exp $
#
# Tests for the ?= variable assignment operator, which only assigns
# if the variable is still undefined.
-# TODO: Implementation
+# The variable VAR is not defined yet. Therefore it gets the default value
+# from the variable assignment.
+VAR?= default value
+.if ${VAR} != "default value"
+. error
+.endif
+
+# At this point, the variable 'VAR' is already defined. The '?=' therefore
+# ignores the new variable value, preserving the previous "default value".
+VAR?= ignored
+.if ${VAR} != "default value"
+. error
+.endif
+
+# The '?=' operator only checks whether the variable is defined or not.
+# An empty variable is defined, therefore the '?=' operator does nothing.
+EMPTY= # empty
+EMPTY?= ignored
+.if ${EMPTY} != ""
+. error
+.endif
+
+# The .for loop is described in the manual page as if it would operate on
+# variables. This is not entirely true. Instead, each occurrence of an
+# expression $i or ${i} or ${i:...} is substituted with ${:Uloop-value}.
+# This comes very close to the description, the only difference is that
+# there is never an actual variable named 'i' involved.
+#
+# Because there is not really a variable named 'i', the '?=' operator
+# performs the variable assignment, resulting in $i == "default".
+.for i in loop-value
+i?= default
+.endfor
+.if ${i} != "default"
+. error
+.endif
+
+# At the point where the '?=' operator checks whether the variable exists,
+# it expands the variable name exactly once. Therefore both 'VAR.param'
+# and 'VAR.${param}' expand to 'VAR.param', and the second '?=' assignment
+# has no effect.
+#
+# Since 2000.05.11.07.43.42 it has been possible to use nested variable
+# expressions in variable names, which made make much more versatile.
+# On 2008.03.31.00.12.21, this particular case of the '?=' operator has been
+# fixed. Before, the '?=' operator had not expanded the variable name
+# 'VAR.${:Uparam}' to see whether the variable already existed. Since that
+# variable didn't exist (and variables with '$' in their name are particularly
+# fragile), the variable assignment with "not used" was performed, and only
+# during that, the variable name was expanded.
+VAR.param= already defined
+VAR.${:Uparam}?= not used
+.if ${VAR.param} != "already defined"
+. error
+.endif
+
+# Now demonstrate that the variable name is indeed expanded exactly once.
+# This is tricky to measure correctly since there are many inconsistencies
+# in and around the code that expands variable expressions in the various
+# places where variable expressions can occur. If in doubt, enable the
+# following debug flags to see what happens:
+#.MAKEFLAGS: -dcpv
+EXPAND_NAME= EXPAND.$$$$ # The full variable name is EXPAND.$$
+PARAM= $$$$
+EXPAND.${PARAM}?= value with param
+.if ${${EXPAND_NAME}} != "value with param"
+. error
+.endif
+.MAKEFLAGS: -d0
all:
@:;
diff --git a/unit-tests/var-op-expand.exp b/unit-tests/var-op-expand.exp
index 8ccbbd5ae92d..39a9383953dd 100644
--- a/unit-tests/var-op-expand.exp
+++ b/unit-tests/var-op-expand.exp
@@ -1,10 +1 @@
-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 0b5ddbbc0386..ff62668a8ada 100644
--- a/unit-tests/var-op-expand.mk
+++ b/unit-tests/var-op-expand.mk
@@ -1,27 +1,178 @@
-# $NetBSD: var-op-expand.mk,v 1.4 2020/11/08 14:00:52 rillig Exp $
+# $NetBSD: var-op-expand.mk,v 1.11 2021/01/01 23:07:48 sjg 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
+.MAKE.SAVE_DOLLARS:= yes
+
+# If the right-hand side does not contain a dollar sign, the ':=' assignment
+# operator has the same effect as the '=' assignment operator.
+VAR:= value
+.if ${VAR} != "value"
+. error
+.endif
+
+# When a ':=' assignment is performed, its right-hand side is evaluated and
+# expanded as far as possible. Contrary to other situations, '$$' and
+# variable expressions based on undefined variables are preserved though.
+#
+# Whether a variable expression is undefined or not is determined at the end
+# of evaluating the expression. The consequence is that ${:Ufallback} expands
+# to "fallback"; initially this expression is undefined since it is based on
+# the variable named "", which is guaranteed to be never defined, but at the
+# end of evaluating the expression ${:Ufallback}, the modifier ':U' has turned
+# the expression into a defined expression.
+
+
+# literal dollar signs
+VAR:= $$ $$$$ $$$$$$$$
+.if ${VAR} != "\$ \$\$ \$\$\$\$"
+. error
+.endif
+
+
+# reference to a variable containing a literal dollar sign
+REF= $$ $$$$ $$$$$$$$
+VAR:= ${REF}
+REF= too late
+.if ${VAR} != "\$ \$\$ \$\$\$\$"
+. error
+.endif
+
+
+# reference to an undefined variable
+.undef UNDEF
+VAR:= <${UNDEF}>
+UNDEF= after
+.if ${VAR} != "<after>"
+. error
+.endif
+
+
+# reference to a variable whose name is computed from another variable
+REF2= referred to
+REF= REF2
+VAR:= ${${REF}}
+REF= too late
+.if ${VAR} != "referred to"
+. error
+.endif
+
+
+# expression with an indirect modifier referring to an undefined variable
+.undef UNDEF
+VAR:= ${:${UNDEF}}
+UNDEF= Uwas undefined
+.if ${VAR} != "was undefined"
+. error
+.endif
+
+
+# expression with an indirect modifier referring to another variable that
+# in turn refers to an undefined variable
+#
+# XXX: Even though this is a ':=' assignment, the '${UNDEF}' in the part of
+# the variable modifier is not preserved. To preserve it, ParseModifierPart
+# would have to call VarSubstExpr somehow since this is the only piece of
+# code that takes care of this global variable.
+.undef UNDEF
+REF= U${UNDEF}
+#.MAKEFLAGS: -dv
+VAR:= ${:${REF}}
+#.MAKEFLAGS: -d0
+REF= too late
+UNDEF= Uwas undefined
+.if ${VAR} != ""
+. error
+.endif
+
+
+# In variable assignments using the ':=' operator, undefined variables are
+# preserved, no matter how indirectly they are referenced.
+.undef REF3
+REF2= <${REF3}>
+REF= ${REF2}
+VAR:= ${REF}
+REF3= too late
+.if ${VAR} != "<too late>"
+. error
+.endif
+
+
+# In variable assignments using the ':=' operator, '$$' are preserved, no
+# matter how indirectly they are referenced.
+REF2= REF2:$$ $$$$
+REF= REF:$$ $$$$ ${REF2}
+VAR:= VAR:$$ $$$$ ${REF}
+.if ${VAR} != "VAR:\$ \$\$ REF:\$ \$\$ REF2:\$ \$\$"
+. error
+.endif
+
+
+# In variable assignments using the ':=' operator, '$$' are preserved in the
+# expressions of the top level, but not in expressions that are nested.
+VAR:= top:$$ ${:Unest1\:\$\$} ${:Unest2${:U\:\$\$}}
+.if ${VAR} != "top:\$ nest1:\$ nest2:\$"
+. error
+.endif
+
+
+# In variable assignments using the ':=' operator, there may be expressions
+# containing variable modifiers, and these modifiers may refer to other
+# variables. These referred-to variables are expanded at the time of
+# assignment. The undefined variables are kept as-is and are later expanded
+# when evaluating the condition.
+#
+# Contrary to the assignment operator '=', the assignment operator ':='
+# consumes the '$' from modifier parts.
+REF.word= 1:$$ 2:$$$$ 4:$$$$$$$$
+.undef REF.undef
+VAR:= ${:Uword undef:@word@${REF.${word}}@}, direct: ${REF.word} ${REF.undef}
+REF.word= word.after
+REF.undef= undef.after
+.if ${VAR} != "1:2:\$ 4:\$\$ undef.after, direct: 1:\$ 2:\$\$ 4:\$\$\$\$ undef.after"
+. error
+.endif
+
+# Just for comparison, the previous example using the assignment operator '='
+# instead of ':='. The right-hand side of the assignment is not evaluated at
+# the time of assignment but only later, when ${VAR} appears in the condition.
+#
+# At that point, both REF.word and REF.undef are defined.
+REF.word= 1:$$ 2:$$$$ 4:$$$$$$$$
+.undef REF.undef
+VAR= ${:Uword undef:@word@${REF.${word}}@}, direct: ${REF.word} ${REF.undef}
+REF.word= word.after
+REF.undef= undef.after
+.if ${VAR} != "word.after undef.after, direct: word.after undef.after"
+. error
+.endif
+
+
+# Between var.c 1.42 from 2000-05-11 and before parse.c 1.520 from 2020-12-27,
+# if the variable name in a ':=' assignment referred to an undefined variable,
+# there were actually 2 assignments to different variables:
+#
+# Global["VAR_SUBST_${UNDEF}"] = ""
+# Global["VAR_SUBST_"] = ""
+#
+# The variable name with the empty value actually included a dollar sign.
+# Variable names with dollars are not used in practice.
+#
+# It might be a good idea to forbid undefined variables on the left-hand side
+# of a variable assignment.
+.undef UNDEF
+VAR_ASSIGN_${UNDEF}= assigned by '='
+VAR_SUBST_${UNDEF}:= assigned by ':='
+.if ${VAR_ASSIGN_} != "assigned by '='"
+. error
+.endif
+.if defined(${:UVAR_SUBST_\${UNDEF\}})
+. error
+.endif
+.if ${VAR_SUBST_} != "assigned by ':='"
+. error
+.endif
all:
@:;
diff --git a/unit-tests/vardebug.exp b/unit-tests/vardebug.exp
index 06c8b590e1b1..cd89e81b3923 100644
--- a/unit-tests/vardebug.exp
+++ b/unit-tests/vardebug.exp
@@ -71,7 +71,7 @@ Var_Parse: ${:Uvariable:unknown} with VARE_UNDEFERR|VARE_WANTRES
Applying ${:U...} to "" (VARE_UNDEFERR|VARE_WANTRES, none, VEF_UNDEF)
Result of ${:Uvariable} is "variable" (VARE_UNDEFERR|VARE_WANTRES, none, VEF_UNDEF|VEF_DEF)
Applying ${:u...} to "variable" (VARE_UNDEFERR|VARE_WANTRES, none, VEF_UNDEF|VEF_DEF)
-make: Unknown modifier 'u'
+make: "vardebug.mk" line 44: Unknown modifier 'u'
Result of ${:unknown} is error (VARE_UNDEFERR|VARE_WANTRES, none, VEF_UNDEF|VEF_DEF)
make: "vardebug.mk" line 44: Malformed conditional (${:Uvariable:unknown})
Var_Parse: ${UNDEFINED} with VARE_UNDEFERR|VARE_WANTRES
diff --git a/unit-tests/varmisc.mk b/unit-tests/varmisc.mk
index aced1a0554d1..607c8d7e0ed3 100644
--- a/unit-tests/varmisc.mk
+++ b/unit-tests/varmisc.mk
@@ -1,5 +1,5 @@
-# $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 $
+# $Id: varmisc.mk,v 1.22 2020/11/30 19:27:41 sjg Exp $
+# $NetBSD: varmisc.mk,v 1.29 2020/11/28 14:08:37 rillig Exp $
#
# Miscellaneous variable tests.
@@ -78,16 +78,6 @@ MAN+= ${MAN$s}
manok:
@echo MAN=${MAN}
-# This is an expanded variant of the above .for loop.
-# Between 2020-06-28 and 2020-07-02 this paragraph generated a wrong
-# error message "Variable VARNAME is recursive".
-# When evaluating the !empty expression, the ${:U1} was not expanded and
-# thus resulted in the seeming definition VARNAME=${VARNAME}, which is
-# obviously recursive.
-VARNAME= ${VARNAME${:U1}}
-.if defined(VARNAME${:U2}) && !empty(VARNAME${:U2})
-.endif
-
# 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 7f61cc426305..15f40226f1db 100644
--- a/unit-tests/varmod-defined.exp
+++ b/unit-tests/varmod-defined.exp
@@ -1,22 +1,22 @@
Global:8_DOLLARS = $$$$$$$$
Global:VAR =
-Var_Parse: ${8_DOLLARS} with VARE_WANTRES|VARE_KEEP_DOLLAR
+Var_Parse: ${8_DOLLARS} with VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF
Global:VAR = $$$$$$$$
-Var_Parse: ${VAR:D${8_DOLLARS}} with VARE_WANTRES|VARE_KEEP_DOLLAR
-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)
+Var_Parse: ${VAR:D${8_DOLLARS}} with VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF
+Applying ${VAR:D...} to "$$$$$$$$" (VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF, none, none)
+Var_Parse: ${8_DOLLARS}} with VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF
+Result of ${VAR:D${8_DOLLARS}} is "$$$$$$$$" (VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF, none, none)
Global:VAR = $$$$$$$$
-Var_Parse: ${VAR:@var@${8_DOLLARS}@} with VARE_WANTRES|VARE_KEEP_DOLLAR
-Applying ${VAR:@...} to "$$$$$$$$" (VARE_WANTRES|VARE_KEEP_DOLLAR, none, none)
+Var_Parse: ${VAR:@var@${8_DOLLARS}@} with VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF
+Applying ${VAR:@...} to "$$$$$$$$" (VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF, none, none)
Modifier part: "var"
Modifier part: "${8_DOLLARS}"
ModifyWords: split "$$$$$$$$" into 1 words
Global:var = $$$$$$$$
-Var_Parse: ${8_DOLLARS} with VARE_WANTRES
+Var_Parse: ${8_DOLLARS} with VARE_WANTRES|VARE_KEEP_UNDEF
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)
+Result of ${VAR:@var@${8_DOLLARS}@} is "$$$$" (VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF, none, none)
Global:VAR = $$$$
Global:.MAKEFLAGS = -r -k -d v -d
Global:.MAKEFLAGS = -r -k -d v -d 0
diff --git a/unit-tests/varmod-edge.exp b/unit-tests/varmod-edge.exp
index 94ba81e2e4f0..c90eef2756c6 100644
--- a/unit-tests/varmod-edge.exp
+++ b/unit-tests/varmod-edge.exp
@@ -1,22 +1,23 @@
-make: "varmod-edge.mk" line omitted: ok M-paren
-make: "varmod-edge.mk" line omitted: ok M-mixed
-make: "varmod-edge.mk" line omitted: ok M-unescape
+make: "varmod-edge.mk" line 166: ok M-paren
+make: "varmod-edge.mk" line 166: ok M-mixed
+make: "varmod-edge.mk" line 166: ok M-unescape
make: Unclosed variable specification (expecting '}') for "" (value "*)") modifier U
-make: "varmod-edge.mk" line omitted: ok M-nest-mix
-make: "varmod-edge.mk" line omitted: ok M-nest-brk
-make: "varmod-edge.mk" line omitted: ok M-pat-err
-make: "varmod-edge.mk" line omitted: ok M-bsbs
-make: "varmod-edge.mk" line omitted: ok M-bs1-par
-make: "varmod-edge.mk" line omitted: ok M-bs2-par
-make: "varmod-edge.mk" line omitted: ok M-128
-make: "varmod-edge.mk" line omitted: ok eq-ext
-make: "varmod-edge.mk" line omitted: ok eq-q
-make: "varmod-edge.mk" line omitted: ok eq-bs
+make: "varmod-edge.mk" line 166: ok M-nest-mix
+make: "varmod-edge.mk" line 166: ok M-nest-brk
+make: "varmod-edge.mk" line 166: ok M-pat-err
+make: "varmod-edge.mk" line 166: ok M-bsbs
+make: "varmod-edge.mk" line 166: ok M-bs1-par
+make: "varmod-edge.mk" line 166: ok M-bs2-par
+make: "varmod-edge.mk" line 166: ok M-128
+make: "varmod-edge.mk" line 166: ok eq-ext
+make: "varmod-edge.mk" line 166: ok eq-q
+make: "varmod-edge.mk" line 166: ok eq-bs
make: Unfinished modifier for INP.eq-esc ('=' missing)
-make: "varmod-edge.mk" line omitted: ok eq-esc
-make: "varmod-edge.mk" line omitted: ok colon
-make: Unknown modifier ':'
-make: Unknown modifier ':'
-make: "varmod-edge.mk" line omitted: ok colons
-ok
-exit status 0
+make: "varmod-edge.mk" line 166: ok eq-esc
+make: "varmod-edge.mk" line 166: ok colon
+make: "varmod-edge.mk" line 165: Unknown modifier ':'
+make: "varmod-edge.mk" line 165: Unknown modifier ':'
+make: "varmod-edge.mk" line 166: ok colons
+make: Fatal errors encountered -- cannot continue
+make: stopped in unit-tests
+exit status 1
diff --git a/unit-tests/varmod-extension.exp b/unit-tests/varmod-extension.exp
index 24f7403c7f3f..1ea2b2f0e995 100644
--- a/unit-tests/varmod-extension.exp
+++ b/unit-tests/varmod-extension.exp
@@ -7,4 +7,5 @@ extension of 'a.a' is 'a'
extension of '.gitignore' is 'gitignore'
extension of 'a' is ''
extension of 'a.a' is 'a'
+extension of 'trailing/' is ''
exit status 0
diff --git a/unit-tests/varmod-extension.mk b/unit-tests/varmod-extension.mk
index db501f7234c7..14ebed0debc1 100644
--- a/unit-tests/varmod-extension.mk
+++ b/unit-tests/varmod-extension.mk
@@ -1,9 +1,9 @@
-# $NetBSD: varmod-extension.mk,v 1.3 2020/08/23 15:09:15 rillig Exp $
+# $NetBSD: varmod-extension.mk,v 1.4 2020/12/20 22:57:40 rillig Exp $
#
# Tests for the :E variable modifier, which returns the filename extension
# of each word in the variable.
all:
-.for path in a/b/c def a.b.c a.b/c a a.a .gitignore a a.a
+.for path in a/b/c def a.b.c a.b/c a a.a .gitignore a a.a trailing/
@echo "extension of '"${path:Q}"' is '"${path:E:Q}"'"
.endfor
diff --git a/unit-tests/varmod-gmtime.exp b/unit-tests/varmod-gmtime.exp
index 06e6314c7bfb..1baa0215b6e6 100644
--- a/unit-tests/varmod-gmtime.exp
+++ b/unit-tests/varmod-gmtime.exp
@@ -1,27 +1,13 @@
-mod-gmtime:
-%Y
-2020
-%Y
-localtime == localtime
-mod-gmtime-indirect:
-make: Invalid time value: ${:U1593536400}}
-
-mtime=1593536400}
-parse-errors:
-make: Invalid time value: -1}.
-
-: -1 becomes mtime=-1}.
-make: Invalid time value: 1}.
-
-: space 1 becomes mtime= 1}.
-: 0 becomes ok.
-: 1 becomes Thu Jan 1 00:00:01 1970.
-: INT32_MAX becomes Tue Jan 19 03:14:07 2038.
-: INT32_MAX + 1 becomes Tue Jan 19 03:14:08 2038.
-make: Invalid time value: 10000000000000000000000000000000}.
-
-: overflow becomes mtime=10000000000000000000000000000000}.
-make: Invalid time value: error}.
-
-: letter becomes mtime=error}.
-exit status 0
+make: "varmod-gmtime.mk" line 60: Invalid time value: ${:U1593536400}} != "mtime=11593536400}"
+make: "varmod-gmtime.mk" line 60: Malformed conditional (${%Y:L:gmtime=${:U1593536400}} != "mtime=11593536400}")
+make: "varmod-gmtime.mk" line 70: Invalid time value: -1} != ""
+make: "varmod-gmtime.mk" line 70: Malformed conditional (${:L:gmtime=-1} != "")
+make: "varmod-gmtime.mk" line 79: Invalid time value: 1} != ""
+make: "varmod-gmtime.mk" line 79: Malformed conditional (${:L:gmtime= 1} != "")
+make: "varmod-gmtime.mk" line 118: Invalid time value: 10000000000000000000000000000000} != ""
+make: "varmod-gmtime.mk" line 118: Malformed conditional (${:L:gmtime=10000000000000000000000000000000} != "")
+make: "varmod-gmtime.mk" line 129: Invalid time value: error} != ""
+make: "varmod-gmtime.mk" line 129: Malformed conditional (${:L:gmtime=error} != "")
+make: Fatal errors encountered -- cannot continue
+make: stopped in unit-tests
+exit status 1
diff --git a/unit-tests/varmod-gmtime.mk b/unit-tests/varmod-gmtime.mk
index b404d1c2768e..3c5f04991c48 100644
--- a/unit-tests/varmod-gmtime.mk
+++ b/unit-tests/varmod-gmtime.mk
@@ -1,11 +1,11 @@
-# $NetBSD: varmod-gmtime.mk,v 1.6 2020/10/31 20:30:06 rillig Exp $
+# $NetBSD: varmod-gmtime.mk,v 1.9 2020/12/22 07:22:39 rillig Exp $
#
# Tests for the :gmtime variable modifier, which formats a timestamp
# using strftime(3) in UTC.
-all: mod-gmtime
-all: mod-gmtime-indirect
-all: parse-errors
+.if ${TZ:Uundefined} != "undefined" # see unit-tests/Makefile
+. error
+.endif
# Test for the default time format, %c. Since the time always varies, it's
# only possible to check for the general format here. The names of the
@@ -15,73 +15,122 @@ all: parse-errors
. error
.endif
-mod-gmtime:
- @echo $@:
-
- # modifier name too short
- @echo ${%Y:L:gmtim=1593536400}
-
- # 2020-07-01T00:00:00Z
- @echo ${%Y:L:gmtime=1593536400}
-
- # modifier name too long
- @echo ${%Y:L:gmtimer=1593536400}
-
- # If the modifier name is not matched exactly, fall back to the
- # :from=to modifier.
- @echo ${gmtime:L:gm%=local%} == localtime
-
-mod-gmtime-indirect:
- @echo $@:
-
- # As of 2020-08-16, it is not possible to pass the seconds via a
- # variable expression. This is because parsing of the :gmtime
- # modifier stops at the '$' and returns to ApplyModifiers.
- #
- # There, a colon would be skipped but not a dollar.
- # Parsing therefore continues at the '$' of the ${:U159...}, looking
- # for an ordinary variable modifier.
- #
- # At this point, the ${:U} is expanded and interpreted as a variable
- # modifier, which results in the error message "Unknown modifier '1'".
- #
- # If ApplyModifier_Gmtime were to pass its argument through
- # ParseModifierPart, this would work.
- @echo ${%Y:L:gmtime=${:U1593536400}}
-
-parse-errors:
- @echo $@:
-
- # As of 2020-10-31, it is possible to pass negative time stamps
- # to the :gmtime modifier, resulting in dates before 1970.
- # Going back 50 years in the past is not a practical use case for
- # make.
- : -1 becomes ${:L:gmtime=-1}.
-
- # Spaces are allowed, not because it would make sense but just as
- # a side-effect from using strtoul.
- : space 1 becomes ${:L:gmtime= 1}.
-
- # 0 means now; to get consistent test results, the actual value has
- # to be normalized.
- : 0 becomes ${:L:gmtime=0:C,^... ... .. ..:..:.. 20..$,ok,W}.
-
- : 1 becomes ${:L:gmtime=1}.
-
- : INT32_MAX becomes ${:L:gmtime=2147483647}.
-
- # This may be different if time_t is still a 32-bit signed integer.
- : INT32_MAX + 1 becomes ${:L:gmtime=2147483648}.
-
- # Integer overflow.
- # Because this modifier is implemented using strtoul, the parsed
- # time is ULONG_MAX, which gets converted to -1. This results
- # in a time stamp of the second before 1970.
- : overflow becomes ${:L:gmtime=10000000000000000000000000000000}.
-
- # As of 2020-10-31, there is no error handling while parsing the
- # :gmtime modifier, thus no error message is printed. Parsing
- # stops after the '=', and the remaining string is parsed for
- # more variable modifiers. Because of the unknown modifier 'e',
- # the whole variable value is discarded and thus not printed.
- : letter becomes ${:L:gmtime=error}.
+
+# modifier name too short, falling back to the SysV modifier.
+.if ${%Y:L:gmtim=1593536400} != "%Y"
+. error
+.endif
+
+
+# 2020-07-01T00:00:00Z
+.if ${%Y:L:gmtime=1593536400} != "2020"
+. error
+.endif
+
+
+# modifier name too long, falling back to the SysV modifier.
+.if ${%Y:L:gmtimer=1593536400} != "%Y"
+. error
+.endif
+
+
+# If the modifier name is not matched exactly, fall back to the
+# :from=to modifier.
+.if ${gmtime:L:gm%=local%} != "localtime"
+. error
+.endif
+
+
+# As of 2020-08-16, it is not possible to pass the seconds via a
+# variable expression. This is because parsing of the :gmtime
+# modifier stops at the '$' and returns to ApplyModifiers.
+#
+# There, a colon would be skipped but not a dollar.
+# Parsing therefore continues at the '$' of the ${:U159...}, looking
+# for an ordinary variable modifier.
+#
+# At this point, the ${:U} is expanded and interpreted as a variable
+# modifier, which results in the error message "Unknown modifier '1'".
+#
+# If ApplyModifier_Gmtime were to pass its argument through
+# ParseModifierPart, this would work.
+#
+# XXX: Where does the empty line 4 in varmod-gmtime.exp come from?
+# TODO: Remove the \n from "Invalid time value: %s\n" in var.c.
+.if ${%Y:L:gmtime=${:U1593536400}} != "mtime=11593536400}"
+. error
+.endif
+
+
+# Before var.c 1.631 from 2020-10-31 21:40:20, it was possible to pass
+# negative time stamps to the :gmtime modifier, resulting in dates before
+# 1970. Going back 50 years in the past is not a practical use case for
+# make. Therefore, since var.c 1.631, negative time stamps produce a
+# parse error.
+.if ${:L:gmtime=-1} != ""
+. error
+.else
+. error
+.endif
+
+
+# Spaces were allowed before var.c 1.631, not because it would make sense
+# but just as a side-effect from using strtoul.
+.if ${:L:gmtime= 1} != ""
+. error
+.endif
+
+
+# 0 means now; this differs from GNode.mtime, where a 0 means nonexistent.
+# Since "now" constantly changes, the strongest possible test is to match the
+# resulting pattern.
+.if !${:L:gmtime=0:tW:M??? ??? ?? ??\:??\:?? 20??}
+. error
+.endif
+
+
+.if ${:L:gmtime=1} != "Thu Jan 1 00:00:01 1970"
+. error
+.endif
+
+
+# INT32_MAX
+.if ${:L:gmtime=2147483647} != "Tue Jan 19 03:14:07 2038"
+. error
+.endif
+
+
+.if ${:L:gmtime=2147483648} == "Tue Jan 19 03:14:08 2038"
+# All systems that have unsigned time_t or 64-bit time_t.
+.elif ${:L:gmtime=2147483648} != "Fri Dec 13 20:45:52 1901"
+# FreeBSD-12.0-i386 still has 32-bit signed time_t.
+.else
+. error
+.endif
+
+
+# Integer overflow, at least before var.c 1.631 from 2020-10-31.
+# Because this modifier is implemented using strtoul, the parsed time was
+# ULONG_MAX, which got converted to -1. This resulted in a time stamp of
+# the second before 1970.
+#
+# Since var.c 1.631, the overflow is detected and produces a parse error.
+.if ${:L:gmtime=10000000000000000000000000000000} != ""
+. error
+.else
+. error
+.endif
+
+# Before var.c 1.631 from 2020-10-31, there was no error handling while
+# parsing the :gmtime modifier, thus no error message is printed. Parsing
+# stopped after the '=', and the remaining string was parsed for more variable
+# modifiers. Because of the unknown modifier 'e' from the 'error', the whole
+# variable value was discarded and thus not printed.
+.if ${:L:gmtime=error} != ""
+. error
+.else
+. error
+.endif
+
+
+all:
diff --git a/unit-tests/varmod-head.exp b/unit-tests/varmod-head.exp
index f0bf87f03012..651844439f5f 100644
--- a/unit-tests/varmod-head.exp
+++ b/unit-tests/varmod-head.exp
@@ -7,4 +7,5 @@ head (dirname) of 'a.a' is '.'
head (dirname) of '.gitignore' is '.'
head (dirname) of 'a' is '.'
head (dirname) of 'a.a' is '.'
+head (dirname) of 'trailing/' is 'trailing'
exit status 0
diff --git a/unit-tests/varmod-head.mk b/unit-tests/varmod-head.mk
index eda4820c5f14..66347b4bce61 100644
--- a/unit-tests/varmod-head.mk
+++ b/unit-tests/varmod-head.mk
@@ -1,9 +1,9 @@
-# $NetBSD: varmod-head.mk,v 1.3 2020/08/23 15:09:15 rillig Exp $
+# $NetBSD: varmod-head.mk,v 1.4 2020/12/20 22:57:40 rillig Exp $
#
# Tests for the :H variable modifier, which returns the dirname of
# each of the words in the variable value.
all:
-.for path in a/b/c def a.b.c a.b/c a a.a .gitignore a a.a
+.for path in a/b/c def a.b.c a.b/c a a.a .gitignore a a.a trailing/
@echo "head (dirname) of '"${path:Q}"' is '"${path:H:Q}"'"
.endfor
diff --git a/unit-tests/varmod-ifelse.exp b/unit-tests/varmod-ifelse.exp
index 75518c08117f..17d4d8afcbeb 100644
--- a/unit-tests/varmod-ifelse.exp
+++ b/unit-tests/varmod-ifelse.exp
@@ -11,6 +11,10 @@ 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.
+CondParser_Eval: ${ ${:U\$}{VAR} == value :?ok:bad} != "ok"
+CondParser_Eval: ${VAR} == value
+lhs = "value", rhs = "value", op = ==
+lhs = "ok", rhs = "ok", op = !=
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 ea94dc875e4d..5e0ad04584be 100644
--- a/unit-tests/varmod-ifelse.mk
+++ b/unit-tests/varmod-ifelse.mk
@@ -1,4 +1,4 @@
-# $NetBSD: varmod-ifelse.mk,v 1.6 2020/11/12 00:29:55 rillig Exp $
+# $NetBSD: varmod-ifelse.mk,v 1.8 2020/12/10 16:47:42 rillig Exp $
#
# Tests for the ${cond:?then:else} variable modifier, which evaluates either
# the then-expression or the else-expression, depending on the condition.
@@ -93,5 +93,23 @@ COND:= ${${UNDEF} == "":?bad-assign:bad-assign}
.endif
.MAKEFLAGS: -d0
+# As of 2020-12-10, the variable "name" is first expanded, and the result of
+# this expansion is then taken as the condition. To force the variable
+# expression in the condition to be evaluated at exactly the right point,
+# the '$' of the intended '${VAR}' escapes from the parser in form of the
+# expression ${:U\$}. Because of this escaping, the variable "name" and thus
+# the condition ends up as "${VAR} == value", just as intended.
+#
+# This hack does not work for variables from .for loops since these are
+# expanded at parse time to their corresponding ${:Uvalue} expressions.
+# Making the '$' of the '${VAR}' expression indirect hides this expression
+# from the parser of the .for loop body. See SubstVarLong.
+.MAKEFLAGS: -dc
+VAR= value
+.if ${ ${:U\$}{VAR} == value :?ok:bad} != "ok"
+. error
+.endif
+.MAKEFLAGS: -d0
+
all:
@:;
diff --git a/unit-tests/varmod-indirect.exp b/unit-tests/varmod-indirect.exp
new file mode 100644
index 000000000000..8cdd49ad4f03
--- /dev/null
+++ b/unit-tests/varmod-indirect.exp
@@ -0,0 +1,59 @@
+make: "varmod-indirect.mk" line 13: Unknown modifier '$'
+make: "varmod-indirect.mk" line 108: before
+make: "varmod-indirect.mk" line 108: after
+make: "varmod-indirect.mk" line 114: before
+make: "varmod-indirect.mk" line 114: after
+make: "varmod-indirect.mk" line 120: before
+make: "varmod-indirect.mk" line 120: after
+make: "varmod-indirect.mk" line 124: Unknown modifier 'Z'
+make: "varmod-indirect.mk" line 125: before
+make: "varmod-indirect.mk" line 125: after
+ParseReadLine (134): '_:= before ${UNDEF} after'
+Global:_ =
+Var_Parse: ${UNDEF} after with VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF
+Global:_ = before ${UNDEF} after
+ParseReadLine (137): '_:= before ${UNDEF:${:US,a,a,}} after'
+Var_Parse: ${UNDEF:${:US,a,a,}} after with VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF
+Var_Parse: ${:US,a,a,}} after with VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF
+Applying ${:U...} to "" (VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF, none, VEF_UNDEF)
+Result of ${:US,a,a,} is "S,a,a," (VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF, none, VEF_UNDEF|VEF_DEF)
+Indirect modifier "S,a,a," from "${:US,a,a,}"
+Applying ${UNDEF:S...} to "" (VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF, none, VEF_UNDEF)
+Modifier part: "a"
+Modifier part: "a"
+ModifyWords: split "" into 1 words
+Result of ${UNDEF:S,a,a,} is "" (VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF, none, VEF_UNDEF)
+Var_Parse: ${:US,a,a,}} after with VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF
+Applying ${:U...} to "" (VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF, none, VEF_UNDEF)
+Result of ${:US,a,a,} is "S,a,a," (VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF, none, VEF_UNDEF|VEF_DEF)
+Global:_ = before ${UNDEF:S,a,a,} after
+ParseReadLine (147): '_:= before ${UNDEF:${:U}} after'
+Var_Parse: ${UNDEF:${:U}} after with VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF
+Var_Parse: ${:U}} after with VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF
+Applying ${:U} to "" (VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF, none, VEF_UNDEF)
+Result of ${:U} is "" (VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF, none, VEF_UNDEF|VEF_DEF)
+Indirect modifier "" from "${:U}"
+Var_Parse: ${:U}} after with VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF
+Applying ${:U} to "" (VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF, none, VEF_UNDEF)
+Result of ${:U} is "" (VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF, none, VEF_UNDEF|VEF_DEF)
+Global:_ = before ${UNDEF:} after
+ParseReadLine (152): '_:= before ${UNDEF:${:UZ}} after'
+Var_Parse: ${UNDEF:${:UZ}} after with VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF
+Var_Parse: ${:UZ}} after with VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF
+Applying ${:U...} to "" (VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF, none, VEF_UNDEF)
+Result of ${:UZ} is "Z" (VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF, none, VEF_UNDEF|VEF_DEF)
+Indirect modifier "Z" from "${:UZ}"
+Applying ${UNDEF:Z} to "" (VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF, none, VEF_UNDEF)
+make: "varmod-indirect.mk" line 152: Unknown modifier 'Z'
+Result of ${UNDEF:Z} is error (VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF, none, VEF_UNDEF)
+Var_Parse: ${:UZ}} after with VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF
+Applying ${:U...} to "" (VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF, none, VEF_UNDEF)
+Result of ${:UZ} is "Z" (VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF, none, VEF_UNDEF|VEF_DEF)
+Global:_ = before ${UNDEF:Z} after
+ParseReadLine (154): '.MAKEFLAGS: -d0'
+ParseDoDependency(.MAKEFLAGS: -d0)
+Global:.MAKEFLAGS = -r -k -d 0 -d pv -d
+Global:.MAKEFLAGS = -r -k -d 0 -d pv -d 0
+make: Fatal errors encountered -- cannot continue
+make: stopped in unit-tests
+exit status 1
diff --git a/unit-tests/varmod-indirect.mk b/unit-tests/varmod-indirect.mk
new file mode 100644
index 000000000000..d130c7cae76d
--- /dev/null
+++ b/unit-tests/varmod-indirect.mk
@@ -0,0 +1,157 @@
+# $NetBSD: varmod-indirect.mk,v 1.5 2020/12/27 17:32:25 rillig Exp $
+#
+# Tests for indirect variable modifiers, such as in ${VAR:${M_modifiers}}.
+# These can be used for very basic purposes like converting a string to either
+# uppercase or lowercase, as well as for fairly advanced modifiers that first
+# look like line noise and are hard to decipher.
+#
+# TODO: Since when are indirect modifiers supported?
+
+
+# To apply a modifier indirectly via another variable, the whole
+# modifier must be put into a single variable expression.
+.if ${value:L:${:US}${:U,value,replacement,}} != "S,value,replacement,}"
+. warning unexpected
+.endif
+
+
+# Adding another level of indirection (the 2 nested :U expressions) helps.
+.if ${value:L:${:U${:US}${:U,value,replacement,}}} != "replacement"
+. warning unexpected
+.endif
+
+
+# Multiple indirect modifiers can be applied one after another as long as
+# they are separated with colons.
+.if ${value:L:${:US,a,A,}:${:US,e,E,}} != "vAluE"
+. warning unexpected
+.endif
+
+
+# An indirect variable that evaluates to the empty string is allowed though.
+# This makes it possible to define conditional modifiers, like this:
+#
+# M.little-endian= S,1234,4321,
+# M.big-endian= # none
+.if ${value:L:${:Dempty}S,a,A,} != "vAlue"
+. warning unexpected
+.endif
+
+
+# The nested variable expression expands to "tu", and this is interpreted as
+# a variable modifier for the value "Upper", resulting in "UPPER".
+.if ${Upper:L:${:Utu}} != "UPPER"
+. error
+.endif
+
+# The nested variable expression expands to "tl", and this is interpreted as
+# a variable modifier for the value "Lower", resulting in "lower".
+.if ${Lower:L:${:Utl}} != "lower"
+. error
+.endif
+
+
+# The nested variable expression is ${1 != 1:?Z:tl}, consisting of the
+# condition "1 != 1", the then-branch "Z" and the else-branch "tl". Since
+# the condition evaluates to false, the then-branch is ignored (it would
+# have been an unknown modifier anyway) and the ":tl" modifier is applied.
+.if ${Mixed:L:${1 != 1:?Z:tl}} != "mixed"
+. error
+.endif
+
+
+# The indirect modifier can also replace an ':L' modifier, which allows for
+# brain twisters since by reading the expression alone, it is not possible
+# to say whether the variable name will be evaluated as a variable name or
+# as the immediate value of the expression.
+VAR= value
+M_ExpandVar= # an empty modifier
+M_VarAsValue= L
+#
+.if ${VAR:${M_ExpandVar}} != "value"
+. error
+.endif
+.if ${VAR:${M_VarAsValue}} != "VAR"
+. error
+.endif
+
+# The indirect modifier M_ListToSkip, when applied to a list of patterns,
+# expands to a sequence of ':N' modifiers, each of which filters one of the
+# patterns. This list of patterns can then be applied to another variable
+# to actually filter that variable.
+#
+M_ListToSkip= @pat@N$${pat}@:ts:
+#
+# The dollar signs need to be doubled in the above modifier expression,
+# otherwise they would be expanded too early, that is, when parsing the
+# modifier itself.
+#
+# In the following example, M_NoPrimes expands to 'N2:N3:N5:N7:N1[1379]'.
+# The 'N' comes from the expression 'N${pat}', the separating colons come
+# from the modifier ':ts:'.
+#
+#.MAKEFLAGS: -dcv # Uncomment this line to see the details
+#
+PRIMES= 2 3 5 7 1[1379]
+M_NoPrimes= ${PRIMES:${M_ListToSkip}}
+.if ${:U:range=20:${M_NoPrimes}} != "1 4 6 8 9 10 12 14 15 16 18 20"
+. error
+.endif
+.MAKEFLAGS: -d0
+
+
+# In contrast to the .if conditions, the .for loop allows undefined variable
+# expressions. These expressions expand to empty strings.
+
+# An undefined expression without any modifiers expands to an empty string.
+.for var in before ${UNDEF} after
+. info ${var}
+.endfor
+
+# An undefined expression with only modifiers that keep the expression
+# undefined expands to an empty string.
+.for var in before ${UNDEF:${:US,a,a,}} after
+. info ${var}
+.endfor
+
+# Even in an indirect modifier based on an undefined variable, the value of
+# the expression in Var_Parse is a simple empty string.
+.for var in before ${UNDEF:${:U}} after
+. info ${var}
+.endfor
+
+# An error in an indirect modifier.
+.for var in before ${UNDEF:${:UZ}} after
+. info ${var}
+.endfor
+
+
+# Another slightly different evaluation context is the right-hand side of
+# a variable assignment using ':='.
+.MAKEFLAGS: -dpv
+
+# The undefined variable expression is kept as-is.
+_:= before ${UNDEF} after
+
+# The undefined variable expression is kept as-is.
+_:= before ${UNDEF:${:US,a,a,}} after
+
+# XXX: The subexpression ${:U} is fully defined, therefore it is expanded.
+# This results in ${UNDEF:}, which can lead to tricky parse errors later,
+# when the variable '_' is expanded further.
+#
+# XXX: What should be the correct strategy here? One possibility is to
+# expand the defined subexpression and replace it with ${:U...}, just like
+# in .for loops. This would preserve the structure of the expression while
+# at the same time expanding the expression as far as possible.
+_:= before ${UNDEF:${:U}} after
+
+# XXX: This expands to ${UNDEF:Z}, which will behave differently if the
+# variable '_' is used in a context where the variable expression ${_} is
+# parsed but not evaluated.
+_:= before ${UNDEF:${:UZ}} after
+
+.MAKEFLAGS: -d0
+.undef _
+
+all:
diff --git a/unit-tests/varmod-localtime.exp b/unit-tests/varmod-localtime.exp
index e89a03b40765..b58de7700466 100644
--- a/unit-tests/varmod-localtime.exp
+++ b/unit-tests/varmod-localtime.exp
@@ -1,27 +1,13 @@
-mod-localtime
-%Y
-2020
-%Y
-gmtime == gmtime
-mod-localtime-indirect:
-make: Invalid time value: ${:U1593536400}}
-
-ocaltime=1593536400}
-parse-errors:
-make: Invalid time value: -1}.
-
-: -1 becomes ocaltime=-1}.
-make: Invalid time value: 1}.
-
-: space 1 becomes ocaltime= 1}.
-: 0 becomes ok.
-: 1 becomes Thu Jan 1 01:00:01 1970.
-: INT32_MAX becomes Tue Jan 19 04:14:07 2038.
-: INT32_MAX + 1 becomes Tue Jan 19 04:14:08 2038.
-make: Invalid time value: 10000000000000000000000000000000}.
-
-: overflow becomes ocaltime=10000000000000000000000000000000}.
-make: Invalid time value: error}.
-
-: letter becomes ocaltime=error}.
-exit status 0
+make: "varmod-localtime.mk" line 60: Invalid time value: ${:U1593536400}} != "mtime=11593536400}"
+make: "varmod-localtime.mk" line 60: Malformed conditional (${%Y:L:localtime=${:U1593536400}} != "mtime=11593536400}")
+make: "varmod-localtime.mk" line 70: Invalid time value: -1} != ""
+make: "varmod-localtime.mk" line 70: Malformed conditional (${:L:localtime=-1} != "")
+make: "varmod-localtime.mk" line 79: Invalid time value: 1} != ""
+make: "varmod-localtime.mk" line 79: Malformed conditional (${:L:localtime= 1} != "")
+make: "varmod-localtime.mk" line 118: Invalid time value: 10000000000000000000000000000000} != ""
+make: "varmod-localtime.mk" line 118: Malformed conditional (${:L:localtime=10000000000000000000000000000000} != "")
+make: "varmod-localtime.mk" line 129: Invalid time value: error} != ""
+make: "varmod-localtime.mk" line 129: Malformed conditional (${:L:localtime=error} != "")
+make: Fatal errors encountered -- cannot continue
+make: stopped in unit-tests
+exit status 1
diff --git a/unit-tests/varmod-localtime.mk b/unit-tests/varmod-localtime.mk
index f7358e309046..3ee2f0ac93fb 100644
--- a/unit-tests/varmod-localtime.mk
+++ b/unit-tests/varmod-localtime.mk
@@ -1,4 +1,4 @@
-# $NetBSD: varmod-localtime.mk,v 1.5 2020/10/31 20:30:06 rillig Exp $
+# $NetBSD: varmod-localtime.mk,v 1.7 2020/12/22 07:22:39 rillig Exp $
#
# Tests for the :localtime variable modifier, which formats a timestamp
# using strftime(3) in local time.
@@ -7,10 +7,6 @@
. error
.endif
-all: mod-localtime
-all: mod-localtime-indirect
-all: parse-errors
-
# Test for the default time format, %c. Since the time always varies, it's
# only possible to check for the general format here. The names of the
# month and weekday are always in English, independent from the locale.
@@ -19,74 +15,122 @@ all: parse-errors
. error
.endif
-mod-localtime:
- @echo $@
-
- # modifier name too short
- @echo ${%Y:L:localtim=1593536400}
-
- # 2020-07-01T00:00:00Z
- @echo ${%Y:L:localtime=1593536400}
-
- # modifier name too long
- @echo ${%Y:L:localtimer=1593536400}
-
- # If the modifier name is not matched exactly, fall back to the
- # :from=to modifier.
- @echo ${localtime:L:local%=gm%} == gmtime
-
-mod-localtime-indirect:
- @echo $@:
-
- # As of 2020-08-16, it is not possible to pass the seconds via a
- # variable expression. This is because parsing of the :localtime
- # modifier stops at the '$' and returns to ApplyModifiers.
- #
- # There, a colon would be skipped but not a dollar.
- # Parsing therefore continues at the '$' of the ${:U159...}, looking
- # for an ordinary variable modifier.
- #
- # At this point, the ${:U} is expanded and interpreted as a variable
- # modifier, which results in the error message "Unknown modifier '1'".
- #
- # If ApplyModifier_Localtime were to pass its argument through
- # ParseModifierPart, this would work.
- @echo ${%Y:L:localtime=${:U1593536400}}
-
-parse-errors:
- @echo $@:
-
- # As of 2020-10-31, it is possible to pass negative time stamps
- # to the :localtime modifier, resulting in dates before 1970.
- # Going back 50 years in the past is not a practical use case for
- # make.
- : -1 becomes ${:L:localtime=-1}.
-
- # Spaces are allowed, not because it would make sense but just as
- # a side-effect from using strtoul.
- : space 1 becomes ${:L:localtime= 1}.
-
- # 0 means now; to get consistent test results, the actual value has
- # to be normalized.
- : 0 becomes ${:L:localtime=0:C,^... ... .. ..:..:.. 20..$,ok,W}.
-
- : 1 becomes ${:L:localtime=1}.
-
- : INT32_MAX becomes ${:L:localtime=2147483647}.
-
- # This may be different if time_t is still a 32-bit signed integer.
- : INT32_MAX + 1 becomes ${:L:localtime=2147483648}.
-
- # Integer overflow.
- # Because this modifier is implemented using strtoul, the parsed
- # time is ULONG_MAX, which gets converted to -1. This results
- # in a time stamp of the second before 1970 (in UTC) or 3599 seconds
- # after New Year's Day in Europe/Berlin.
- : overflow becomes ${:L:localtime=10000000000000000000000000000000}.
-
- # As of 2020-10-31, there is no error handling while parsing the
- # :localtime modifier, thus no error message is printed. Parsing
- # stops after the '=', and the remaining string is parsed for
- # more variable modifiers. Because of the unknown modifier 'e',
- # the whole variable value is discarded and thus not printed.
- : letter becomes ${:L:localtime=error}.
+
+# modifier name too short, falling back to the SysV modifier.
+.if ${%Y:L:localtim=1593536400} != "%Y"
+. error
+.endif
+
+
+# 2020-07-01T00:00:00Z
+.if ${%Y:L:localtime=1593536400} != "2020"
+. error
+.endif
+
+
+# modifier name too long, falling back to the SysV modifier.
+.if ${%Y:L:localtimer=1593536400} != "%Y"
+. error
+.endif
+
+
+# If the modifier name is not matched exactly, fall back to the
+# :from=to modifier.
+.if ${gmtime:L:gm%=local%} != "localtime"
+. error
+.endif
+
+
+# As of 2020-08-16, it is not possible to pass the seconds via a
+# variable expression. This is because parsing of the :localtime
+# modifier stops at the '$' and returns to ApplyModifiers.
+#
+# There, a colon would be skipped but not a dollar.
+# Parsing therefore continues at the '$' of the ${:U159...}, looking
+# for an ordinary variable modifier.
+#
+# At this point, the ${:U} is expanded and interpreted as a variable
+# modifier, which results in the error message "Unknown modifier '1'".
+#
+# If ApplyModifier_Localtime were to pass its argument through
+# ParseModifierPart, this would work.
+#
+# XXX: Where does the empty line 4 in varmod-localtime.exp come from?
+# TODO: Remove the \n from "Invalid time value: %s\n" in var.c.
+.if ${%Y:L:localtime=${:U1593536400}} != "mtime=11593536400}"
+. error
+.endif
+
+
+# Before var.c 1.631 from 2020-10-31 21:40:20, it was possible to pass
+# negative time stamps to the :localtime modifier, resulting in dates before
+# 1970. Going back 50 years in the past is not a practical use case for
+# make. Therefore, since var.c 1.631, negative time stamps produce a
+# parse error.
+.if ${:L:localtime=-1} != ""
+. error
+.else
+. error
+.endif
+
+
+# Spaces were allowed before var.c 1.631, not because it would make sense
+# but just as a side-effect from using strtoul.
+.if ${:L:localtime= 1} != ""
+. error
+.endif
+
+
+# 0 means now; this differs from GNode.mtime, where a 0 means nonexistent.
+# Since "now" constantly changes, the strongest possible test is to match the
+# resulting pattern.
+.if !${:L:localtime=0:tW:M??? ??? ?? ??\:??\:?? 20??}
+. error
+.endif
+
+
+.if ${:L:localtime=1} != "Thu Jan 1 01:00:01 1970"
+. error
+.endif
+
+
+# INT32_MAX
+.if ${:L:localtime=2147483647} != "Tue Jan 19 04:14:07 2038"
+. error
+.endif
+
+
+.if ${:L:localtime=2147483648} == "Tue Jan 19 04:14:08 2038"
+# All systems that have unsigned time_t or 64-bit time_t.
+.elif ${:L:localtime=2147483648} != "Fri Dec 13 21:45:52 1901"
+# FreeBSD-12.0-i386 still has 32-bit signed time_t.
+.else
+. error
+.endif
+
+
+# Integer overflow, at least before var.c 1.631 from 2020-10-31.
+# Because this modifier is implemented using strtoul, the parsed time was
+# ULONG_MAX, which got converted to -1. This resulted in a time stamp of
+# the second before 1970.
+#
+# Since var.c 1.631, the overflow is detected and produces a parse error.
+.if ${:L:localtime=10000000000000000000000000000000} != ""
+. error
+.else
+. error
+.endif
+
+# Before var.c 1.631 from 2020-10-31, there was no error handling while
+# parsing the :localtime modifier, thus no error message is printed. Parsing
+# stopped after the '=', and the remaining string was parsed for more variable
+# modifiers. Because of the unknown modifier 'e' from the 'error', the whole
+# variable value was discarded and thus not printed.
+.if ${:L:localtime=error} != ""
+. error
+.else
+. error
+.endif
+
+
+all:
diff --git a/unit-tests/varmod-range.exp b/unit-tests/varmod-range.exp
index eeeceb72b83f..3a9d4d032c3a 100644
--- a/unit-tests/varmod-range.exp
+++ b/unit-tests/varmod-range.exp
@@ -1,13 +1,12 @@
make: "varmod-range.mk" line 53: Invalid number: x}Rest" != "Rest"
-
make: "varmod-range.mk" line 53: Malformed conditional ("${:U:range=x}Rest" != "Rest")
-make: Unknown modifier 'x'
+make: "varmod-range.mk" line 62: Unknown modifier 'x'
make: "varmod-range.mk" line 62: Malformed conditional ("${:U:range=0x0}Rest" != "Rest")
-make: Unknown modifier 'r'
+make: "varmod-range.mk" line 78: Unknown modifier 'r'
make: "varmod-range.mk" line 78: Malformed conditional ("${a b c:L:rang}Rest" != "Rest")
-make: Unknown modifier 'r'
+make: "varmod-range.mk" line 85: Unknown modifier 'r'
make: "varmod-range.mk" line 85: Malformed conditional ("${a b c:L:rango}Rest" != "Rest")
-make: Unknown modifier 'r'
+make: "varmod-range.mk" line 92: Unknown modifier 'r'
make: "varmod-range.mk" line 92: Malformed conditional ("${a b c:L:ranger}Rest" != "Rest")
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
diff --git a/unit-tests/varmod-root.exp b/unit-tests/varmod-root.exp
index 24ecbb875f77..2c99cd3ef4c7 100644
--- a/unit-tests/varmod-root.exp
+++ b/unit-tests/varmod-root.exp
@@ -7,4 +7,5 @@ root of 'a.a' is 'a'
root of '.gitignore' is ''
root of 'a' is 'a'
root of 'a.a' is 'a'
+root of 'trailing/' is 'trailing/'
exit status 0
diff --git a/unit-tests/varmod-root.mk b/unit-tests/varmod-root.mk
index 88af42d82510..1e3159733df0 100644
--- a/unit-tests/varmod-root.mk
+++ b/unit-tests/varmod-root.mk
@@ -1,9 +1,9 @@
-# $NetBSD: varmod-root.mk,v 1.3 2020/08/23 15:09:15 rillig Exp $
+# $NetBSD: varmod-root.mk,v 1.4 2020/12/20 22:57:40 rillig Exp $
#
# Tests for the :R variable modifier, which returns the filename root
# without the extension.
all:
-.for path in a/b/c def a.b.c a.b/c a a.a .gitignore a a.a
+.for path in a/b/c def a.b.c a.b/c a a.a .gitignore a a.a trailing/
@echo "root of '"${path:Q}"' is '"${path:R:Q}"'"
.endfor
diff --git a/unit-tests/varmod-subst-regex.exp b/unit-tests/varmod-subst-regex.exp
index eb9ae7f41fb9..207a97fc25e8 100644
--- a/unit-tests/varmod-subst-regex.exp
+++ b/unit-tests/varmod-subst-regex.exp
@@ -20,4 +20,6 @@ mod-regex-limits:22-ok:1 33 556
mod-regex-limits:capture:ihgfedcbaabcdefghijABCDEFGHIJa0a1a2rest
make: Regex compilation error: (details omitted)
mod-regex-errors:
+make: Unknown modifier 'Z'
+mod-regex-errors: xy
exit status 0
diff --git a/unit-tests/varmod-subst-regex.mk b/unit-tests/varmod-subst-regex.mk
index f558ae1134e8..91b2f0e6a2f9 100644
--- a/unit-tests/varmod-subst-regex.mk
+++ b/unit-tests/varmod-subst-regex.mk
@@ -1,4 +1,4 @@
-# $NetBSD: varmod-subst-regex.mk,v 1.5 2020/10/31 12:20:36 rillig Exp $
+# $NetBSD: varmod-subst-regex.mk,v 1.6 2020/12/05 18:13:44 rillig Exp $
#
# Tests for the :C,from,to, variable modifier.
@@ -102,3 +102,8 @@ mod-regex-limits:
mod-regex-errors:
@echo $@: ${UNDEF:Uvalue:C,[,,}
+
+ # If the replacement pattern produces a parse error because of an
+ # unknown modifier, the parse error is ignored in ParseModifierPart
+ # and the faulty variable expression expands to "".
+ @echo $@: ${word:L:C,.*,x${:U:Z}y,W}
diff --git a/unit-tests/varmod-sysv.exp b/unit-tests/varmod-sysv.exp
index 301519ecc747..57e69a667281 100644
--- a/unit-tests/varmod-sysv.exp
+++ b/unit-tests/varmod-sysv.exp
@@ -1,5 +1,5 @@
-make: Unfinished modifier for word203 ('=' missing)
-make: "varmod-sysv.mk" line 210: Malformed conditional (${word203:L:from${:D=}to})
+make: Unfinished modifier for word214 ('=' missing)
+make: "varmod-sysv.mk" line 214: Malformed conditional (${word214:L:from${:D=}to})
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/unit-tests/varmod-sysv.mk b/unit-tests/varmod-sysv.mk
index 10643495fef5..751736ceaf74 100644
--- a/unit-tests/varmod-sysv.mk
+++ b/unit-tests/varmod-sysv.mk
@@ -1,4 +1,4 @@
-# $NetBSD: varmod-sysv.mk,v 1.11 2020/11/01 22:28:52 rillig Exp $
+# $NetBSD: varmod-sysv.mk,v 1.12 2020/12/05 13:01:33 rillig Exp $
#
# Tests for the ${VAR:from=to} variable modifier, which replaces the suffix
# "from" with "to". It can also use '%' as a wildcard.
@@ -77,13 +77,16 @@
# If the variable value is empty, it is debatable whether it consists of a
# single empty word, or no word at all. The :from=to modifier treats it as
# no word at all.
+#
+# See SysVMatch, which doesn't handle w_len == p_len specially.
.if ${:L:=suffix} != ""
. error
.endif
# If the variable value is empty, it is debatable whether it consists of a
-# single empty word, or no word at all. The :from=to modifier treats it as
-# no word at all.
+# single empty word (before 2020-05-06), or no word at all (since 2020-05-06).
+#
+# See SysVMatch, percent != NULL && w[0] == '\0'.
.if ${:L:%=suffix} != ""
. error
.endif
@@ -205,9 +208,10 @@
# This is not a SysV modifier since the nested variable expression expands
# to an empty string. The '=' in it should be irrelevant during parsing.
-# As of 2020-11-01, this seemingly correct modifier leads to a parse error.
-# XXX
-.if ${word203:L:from${:D=}to}
+# XXX: As of 2020-12-05, this expression generates an "Unfinished modifier"
+# error, while the correct error message would be "Unknown modifier" since
+# there is no modifier named "fromto".
+.if ${word214:L:from${:D=}to}
. error
.endif
diff --git a/unit-tests/varmod-tail.exp b/unit-tests/varmod-tail.exp
index e25c1cc4b914..26c87f3b5c1b 100644
--- a/unit-tests/varmod-tail.exp
+++ b/unit-tests/varmod-tail.exp
@@ -7,4 +7,5 @@ tail (basename) of 'a.a' is 'a.a'
tail (basename) of '.gitignore' is '.gitignore'
tail (basename) of 'a' is 'a'
tail (basename) of 'a.a' is 'a.a'
+tail (basename) of 'trailing/' is ''
exit status 0
diff --git a/unit-tests/varmod-tail.mk b/unit-tests/varmod-tail.mk
index a8078cc67335..05eae481fe3e 100644
--- a/unit-tests/varmod-tail.mk
+++ b/unit-tests/varmod-tail.mk
@@ -1,9 +1,9 @@
-# $NetBSD: varmod-tail.mk,v 1.3 2020/08/23 15:09:15 rillig Exp $
+# $NetBSD: varmod-tail.mk,v 1.4 2020/12/20 22:57:40 rillig Exp $
#
# Tests for the :T variable modifier, which returns the basename of each of
# the words in the variable value.
all:
-.for path in a/b/c def a.b.c a.b/c a a.a .gitignore a a.a
+.for path in a/b/c def a.b.c a.b/c a a.a .gitignore a a.a trailing/
@echo "tail (basename) of '"${path:Q}"' is '"${path:T:Q}"'"
.endfor
diff --git a/unit-tests/varmod-to-many-words.mk b/unit-tests/varmod-to-many-words.mk
index 10cddb00c5e4..e96962ce4136 100644
--- a/unit-tests/varmod-to-many-words.mk
+++ b/unit-tests/varmod-to-many-words.mk
@@ -1,9 +1,21 @@
-# $NetBSD: varmod-to-many-words.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+# $NetBSD: varmod-to-many-words.mk,v 1.3 2020/12/20 23:29:50 rillig Exp $
#
# Tests for the :tw modifier, which treats the variable as many words,
# to undo a previous :tW modifier.
-# TODO: Implementation
+SENTENCE= The quick brown fox jumps over the lazy brown dog.
+
+.if ${SENTENCE:tw:[#]} != 10
+. error
+.endif
+.if ${SENTENCE:tW:[#]} != 1
+. error
+.endif
+
+# Protect against accidental freeing of the variable value.
+.if ${SENTENCE} != "The quick brown fox jumps over the lazy brown dog."
+. error
+.endif
all:
@:;
diff --git a/unit-tests/varmod-to-one-word.mk b/unit-tests/varmod-to-one-word.mk
index 0865ce8fb41f..e4e2e99781f2 100644
--- a/unit-tests/varmod-to-one-word.mk
+++ b/unit-tests/varmod-to-one-word.mk
@@ -1,9 +1,21 @@
-# $NetBSD: varmod-to-one-word.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+# $NetBSD: varmod-to-one-word.mk,v 1.3 2020/12/20 23:29:50 rillig Exp $
#
# Tests for the :tW variable modifier, which treats the variable value
# as a single word, for all following modifiers.
-# TODO: Implementation
+SENTENCE= The quick brown fox jumps over the lazy brown dog.
+
+.if ${SENTENCE:tW:[#]} != 1
+. error
+.endif
+.if ${SENTENCE:tw:[#]} != 10
+. error
+.endif
+
+# Protect against accidental freeing of the variable value.
+.if ${SENTENCE} != "The quick brown fox jumps over the lazy brown dog."
+. error
+.endif
all:
@:;
diff --git a/unit-tests/varmod-to-separator.exp b/unit-tests/varmod-to-separator.exp
index a3c323ac123a..44c9f0973ed9 100644
--- a/unit-tests/varmod-to-separator.exp
+++ b/unit-tests/varmod-to-separator.exp
@@ -1,8 +1,6 @@
make: "varmod-to-separator.mk" line 107: Invalid character number: 400:tu}
-
make: "varmod-to-separator.mk" line 107: Malformed conditional (${WORDS:[1..3]:ts\400:tu})
make: "varmod-to-separator.mk" line 121: Invalid character number: 100:tu}
-
make: "varmod-to-separator.mk" line 121: Malformed conditional (${WORDS:[1..3]:ts\x100:tu})
make: Bad modifier `:ts\-300' for WORDS
make: "varmod-to-separator.mk" line 128: Malformed conditional (${WORDS:[1..3]:ts\-300:tu})
diff --git a/unit-tests/varmod.exp b/unit-tests/varmod.exp
index a80979e1410d..e36c4ded9b47 100644
--- a/unit-tests/varmod.exp
+++ b/unit-tests/varmod.exp
@@ -2,7 +2,7 @@ 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: "varmod.mk" line 57: Missing argument for ".error"
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/unit-tests/varmod.mk b/unit-tests/varmod.mk
index b496bdd206a2..21ddf9103251 100644
--- a/unit-tests/varmod.mk
+++ b/unit-tests/varmod.mk
@@ -1,4 +1,4 @@
-# $NetBSD: varmod.mk,v 1.4 2020/11/02 17:30:22 rillig Exp $
+# $NetBSD: varmod.mk,v 1.5 2020/12/19 22:33:11 rillig Exp $
#
# Tests for variable modifiers, such as :Q, :S,from,to or :Ufallback.
@@ -51,7 +51,7 @@ DOLLAR2= ${:U\$}
# 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.
+# malformed, and this error should be propagated up to Cond_EvalLine.
VAR= STOP
.if ${VAR:P=RE} != "STORE"
. error
diff --git a/unit-tests/varname-dot-makeflags.exp b/unit-tests/varname-dot-makeflags.exp
new file mode 100644
index 000000000000..dbf96469f86b
--- /dev/null
+++ b/unit-tests/varname-dot-makeflags.exp
@@ -0,0 +1,3 @@
+echo "$MAKEFLAGS"
+ -r -k -d 00000 -D VARNAME WITH SPACES
+exit status 0
diff --git a/unit-tests/varname-dot-makeflags.mk b/unit-tests/varname-dot-makeflags.mk
new file mode 100644
index 000000000000..10d1903022cb
--- /dev/null
+++ b/unit-tests/varname-dot-makeflags.mk
@@ -0,0 +1,15 @@
+# $NetBSD: varname-dot-makeflags.mk,v 1.1 2020/12/01 20:37:30 rillig Exp $
+#
+# Tests for the special .MAKEFLAGS variable, which collects almost all
+# command line arguments and passes them on to any child processes via
+# the environment variable MAKEFLAGS (without leading '.').
+
+# When options are parsed, the option and its argument are appended as
+# separate words to .MAKEFLAGS. Special characters in the option argument
+# are not quoted though. It seems to have not been necessary at least from
+# 1993 until 2020.
+.MAKEFLAGS: -d00000 -D"VARNAME WITH SPACES"
+
+all:
+ echo "$$MAKEFLAGS"
+ @:;
diff --git a/unit-tests/varname-dot-shell.exp b/unit-tests/varname-dot-shell.exp
index f219515444cf..46a1b2127c98 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_KEEP_DOLLAR
+Var_Parse: ${.SHELL} with VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF
Global:delete .SHELL (not found)
Command:.SHELL = (details omitted)
Global:ORIG_SHELL = (details omitted)
diff --git a/unit-tests/varname-make_print_var_on_error-jobs.exp b/unit-tests/varname-make_print_var_on_error-jobs.exp
index 057c09dcbcf6..81bea0e99ae9 100644
--- a/unit-tests/varname-make_print_var_on_error-jobs.exp
+++ b/unit-tests/varname-make_print_var_on_error-jobs.exp
@@ -1,7 +1,8 @@
-fail
+echo fail all; false 'all' '${.TARGET}' '$${.TARGET}'
+fail all
*** [all] Error code 1
make: stopped in unit-tests
.ERROR_TARGET='all'
-.ERROR_CMD='@: command before @echo fail; false @: command after, with variable expressions expanded'
+.ERROR_CMD='@: before '${.TARGET}' '${.TARGET}' '$${.TARGET}' echo fail ${.TARGET}; false '${.TARGET}' '${.TARGET}' '$${.TARGET}' @: after '${.TARGET}' '${.TARGET}' '$${.TARGET}''
exit status 1
diff --git a/unit-tests/varname-make_print_var_on_error-jobs.mk b/unit-tests/varname-make_print_var_on_error-jobs.mk
index 7e611d092f23..d4ab4c8bb711 100644
--- a/unit-tests/varname-make_print_var_on_error-jobs.mk
+++ b/unit-tests/varname-make_print_var_on_error-jobs.mk
@@ -1,4 +1,4 @@
-# $NetBSD: varname-make_print_var_on_error-jobs.mk,v 1.1 2020/10/23 06:18:23 rillig Exp $
+# $NetBSD: varname-make_print_var_on_error-jobs.mk,v 1.2 2020/12/13 19:08:20 rillig Exp $
#
# Tests for the special MAKE_PRINT_VAR_ON_ERROR variable, which prints the
# values of selected variables on error.
@@ -9,6 +9,13 @@
# The commands in .ERROR_CMD are space-separated. Since each command usually
# contains spaces as well, this value is only intended as a first hint to what
# happened. For more details, use the debug options -de, -dj, -dl, -dn, -dx.
+#
+# See also:
+# compat-error.mk
+
+# XXX: As of 2020-12-13, PrintOnError calls Var_Subst with VAR_GLOBAL, which
+# does not expand the node-local variables like .TARGET. This results in the
+# double '${.TARGET}' in the output.
# As of 2020-10-23, .ERROR_CMD only works in parallel mode.
.MAKEFLAGS: -j1
@@ -16,6 +23,6 @@
MAKE_PRINT_VAR_ON_ERROR= .ERROR_TARGET .ERROR_CMD
all:
- @: command before
- @echo fail; false
- @: command after${:U, with variable expressions expanded}
+ @: before '${.TARGET}' '$${.TARGET}' '$$$${.TARGET}'
+ echo fail ${.TARGET}; false '${.TARGET}' '$${.TARGET}' '$$$${.TARGET}'
+ @: after '${.TARGET}' '$${.TARGET}' '$$$${.TARGET}'
diff --git a/unit-tests/varname-make_print_var_on_error.exp b/unit-tests/varname-make_print_var_on_error.exp
index e2f6a429a9fc..f23deb3568d6 100644
--- a/unit-tests/varname-make_print_var_on_error.exp
+++ b/unit-tests/varname-make_print_var_on_error.exp
@@ -1,4 +1,5 @@
-fail
+echo fail all; false 'all' '${.TARGET}' '$${.TARGET}'
+fail all
*** Error code 1 (continuing)
Stop.
diff --git a/unit-tests/varname-make_print_var_on_error.mk b/unit-tests/varname-make_print_var_on_error.mk
index 9ea78cb2cb4a..3c498febc386 100644
--- a/unit-tests/varname-make_print_var_on_error.mk
+++ b/unit-tests/varname-make_print_var_on_error.mk
@@ -1,4 +1,4 @@
-# $NetBSD: varname-make_print_var_on_error.mk,v 1.4 2020/10/23 06:18:23 rillig Exp $
+# $NetBSD: varname-make_print_var_on_error.mk,v 1.5 2020/12/13 19:08:20 rillig Exp $
#
# Tests for the special MAKE_PRINT_VAR_ON_ERROR variable, which prints the
# values of selected variables on error.
@@ -7,10 +7,17 @@
# since at the point where it is filled in PrintOnError, the first command in
# gn->commands has been set to NULL already. This leaves .ERROR_CMD an empty
# list.
+#
+# See also:
+# compat-error.mk
+
+# XXX: As of 2020-12-13, PrintOnError calls Var_Subst with VAR_GLOBAL, which
+# does not expand the node-local variables like .TARGET. This results in the
+# double '${.TARGET}' in the output.
MAKE_PRINT_VAR_ON_ERROR= .ERROR_TARGET .ERROR_CMD
all:
- @: command before
- @echo fail; false
- @: command after
+ @: before '${.TARGET}' '$${.TARGET}' '$$$${.TARGET}'
+ echo fail ${.TARGET}; false '${.TARGET}' '$${.TARGET}' '$$$${.TARGET}'
+ @: after '${.TARGET}' '$${.TARGET}' '$$$${.TARGET}'
diff --git a/unit-tests/varname-makeflags.mk b/unit-tests/varname-makeflags.mk
index b2e5f68b4e08..3b4fd91c3f57 100644
--- a/unit-tests/varname-makeflags.mk
+++ b/unit-tests/varname-makeflags.mk
@@ -1,8 +1,26 @@
-# $NetBSD: varname-makeflags.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+# $NetBSD: varname-makeflags.mk,v 1.3 2020/12/01 20:37:30 rillig Exp $
#
-# Tests for the special MAKEFLAGS variable.
+# Tests for the special MAKEFLAGS variable, which is basically just a normal
+# environment variable. It is closely related to .MAKEFLAGS but captures the
+# state of .MAKEFLAGS at the very beginning of make, before any makefiles are
+# read.
# TODO: Implementation
+.MAKEFLAGS: -d0
+
+# The unit tests are run with an almost empty environment. In particular,
+# the variable MAKEFLAGS is not set. The '.MAKEFLAGS:' above also doesn't
+# influence the environment variable MAKEFLAGS, therefore it is still
+# undefined at this point.
+.if ${MAKEFLAGS:Uundefined} != "undefined"
+. error
+.endif
+
+# The special variable .MAKEFLAGS is influenced though.
+# See varname-dot-makeflags.mk for more details.
+.if ${.MAKEFLAGS} != " -r -k -d 0"
+. error
+.endif
+
all:
- @:;
diff --git a/unit-tests/varparse-dynamic.mk b/unit-tests/varparse-dynamic.mk
index 228eb17475b0..29051d31eeea 100644
--- a/unit-tests/varparse-dynamic.mk
+++ b/unit-tests/varparse-dynamic.mk
@@ -1,4 +1,4 @@
-# $NetBSD: varparse-dynamic.mk,v 1.2 2020/09/13 21:00:34 rillig Exp $
+# $NetBSD: varparse-dynamic.mk,v 1.3 2020/11/21 15:48:05 rillig Exp $
# Before 2020-07-27, there was an off-by-one error in Var_Parse that skipped
# the last character in the variable name.
@@ -21,5 +21,15 @@
. error
.endif
+# If a dynamic variable is expanded in a non-local context, the expression
+# based on this variable is not expanded. But there may be nested variable
+# expressions in the modifiers, and these are kept unexpanded as well.
+.if ${.TARGET:M${:Ufallback}} != "\${.TARGET:M\${:Ufallback}}"
+. error
+.endif
+.if ${.TARGET:M${UNDEF}} != "\${.TARGET:M\${UNDEF}}"
+. error
+.endif
+
all:
@:
diff --git a/unit-tests/varparse-errors.exp b/unit-tests/varparse-errors.exp
index 39a9383953dd..50a0766c7d70 100644
--- a/unit-tests/varparse-errors.exp
+++ b/unit-tests/varparse-errors.exp
@@ -1 +1,5 @@
-exit status 0
+make: "varparse-errors.mk" line 38: Unknown modifier 'Z'
+make: "varparse-errors.mk" line 46: Unknown modifier 'Z'
+make: Fatal errors encountered -- cannot continue
+make: stopped in unit-tests
+exit status 1
diff --git a/unit-tests/varparse-errors.mk b/unit-tests/varparse-errors.mk
index 42f5b65a728e..113c7a292a79 100644
--- a/unit-tests/varparse-errors.mk
+++ b/unit-tests/varparse-errors.mk
@@ -1,4 +1,4 @@
-# $NetBSD: varparse-errors.mk,v 1.1 2020/11/08 16:44:47 rillig Exp $
+# $NetBSD: varparse-errors.mk,v 1.3 2020/12/20 19:47:34 rillig Exp $
# Tests for parsing and evaluating all kinds of variable expressions.
#
@@ -32,4 +32,20 @@ ERR_EVAL= An evaluation error ${:Uvalue:C,.,\3,}.
. error
.endif
+# As of 2020-12-01, errors in the variable name are silently ignored.
+# Since var.c 1.754 from 2020-12-20, unknown modifiers at parse time result
+# in an error message and a non-zero exit status.
+VAR.${:U:Z}= unknown modifier in the variable name
+.if ${VAR.} != "unknown modifier in the variable name"
+. error
+.endif
+
+# As of 2020-12-01, errors in the variable name are silently ignored.
+# Since var.c 1.754 from 2020-12-20, unknown modifiers at parse time result
+# in an error message and a non-zero exit status.
+VAR.${:U:Z}post= unknown modifier with text in the variable name
+.if ${VAR.post} != "unknown modifier with text in the variable name"
+. error
+.endif
+
all: