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