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) | |
download | src-ee914ef902ae018bd4f67192832120f9bf05651f.tar.gz src-ee914ef902ae018bd4f67192832120f9bf05651f.zip |
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." |