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