diff options
Diffstat (limited to 'unit-tests')
87 files changed, 1505 insertions, 386 deletions
diff --git a/unit-tests/Makefile b/unit-tests/Makefile index 784223a56652..98ed3907cd5a 100644 --- a/unit-tests/Makefile +++ b/unit-tests/Makefile @@ -1,6 +1,6 @@ -# $Id: Makefile,v 1.148 2021/06/16 19:18:56 sjg Exp $ +# $Id: Makefile,v 1.164 2021/12/12 22:50:00 sjg Exp $ # -# $NetBSD: Makefile,v 1.279 2021/06/16 09:39:48 rillig Exp $ +# $NetBSD: Makefile,v 1.288 2021/12/12 22:16:48 rillig Exp $ # # Unit tests for make(1) # @@ -167,6 +167,7 @@ TESTS+= directive-for TESTS+= directive-for-errors TESTS+= directive-for-escape TESTS+= directive-for-generating-endif +TESTS+= directive-for-if TESTS+= directive-for-lines TESTS+= directive-for-null TESTS+= directive-hyphen-include @@ -351,13 +352,16 @@ TESTS+= varmod-indirect TESTS+= varmod-l-name-to-value TESTS+= varmod-localtime TESTS+= varmod-loop +TESTS+= varmod-loop-delete TESTS+= varmod-loop-varname TESTS+= varmod-match TESTS+= varmod-match-escape TESTS+= varmod-no-match TESTS+= varmod-order +TESTS+= varmod-order-numeric TESTS+= varmod-order-reverse TESTS+= varmod-order-shuffle +TESTS+= varmod-order-string TESTS+= varmod-path TESTS+= varmod-quote TESTS+= varmod-quote-dollar @@ -415,6 +419,7 @@ TESTS+= varname-dot-parsedir TESTS+= varname-dot-parsefile TESTS+= varname-dot-path TESTS+= varname-dot-shell +TESTS+= varname-dot-suffixes TESTS+= varname-dot-targets TESTS+= varname-empty TESTS+= varname-make @@ -430,12 +435,41 @@ TESTS+= varparse-mod TESTS+= varparse-undef-partial TESTS+= varquote +# for now at least +.if ${.SHELL:T} == "ksh" +BROKEN_TESTS+= sh-flags +.endif +.if ${.MAKE.OS:NDarwin} == "" +BROKEN_TESTS+= shell-ksh +.endif +.if ${.MAKE.OS} == "SCO_SV" +BROKEN_TESTS+= \ + opt-debug-graph[23] \ + varmod-localtime \ + varmod-to-separator \ + +.if ${.SHELL:T} == "bash" +BROKEN_TESTS+= job-output-null +.else +BROKEN_TESTS+= \ + cmd-interrupt \ + job-flags \ + +.endif +.endif + +# Some tests just do not work on some platforms or environments +# so allow for some filtering. +.if !empty(BROKEN_TESTS) +.warning Skipping broken tests: ${BROKEN_TESTS:O:u} +TESTS:= ${TESTS:${BROKEN_TESTS:S,^,N,:ts:}} +.endif + # Ideas for more tests: # char-0020-space.mk # char-005C-backslash.mk # escape-cond-str.mk # escape-cond-func-arg.mk -# escape-cond-func-arg.mk # escape-varmod.mk # escape-varmod-define.mk # escape-varmod-match.mk @@ -457,7 +491,7 @@ ENV.envfirst= FROM_ENV=value-from-env ENV.varmisc= FROM_ENV=env ENV.varmisc+= FROM_ENV_BEFORE=env ENV.varmisc+= FROM_ENV_AFTER=env -ENV.varmod-localtime+= TZ=Europe/Berlin +ENV.varmod-localtime+= TZ=${UTC_1:UEurope/Berlin} ENV.varname-vpath+= VPATH=varname-vpath.dir:varname-vpath.dir2 # Override make flags for some of the tests; default is -k. @@ -490,7 +524,10 @@ 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-chdir= -e 's,\(nonexistent\).[1-9][0-9]*,\1,' \ + -e '/name/s,file,File,' \ + -e 's,no such,No such,' \ + -e 's,Filename,File name,' 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} @@ -511,11 +548,13 @@ SED_CMDS.sh-dots+= -e 's,^make: exec(\(.*\)) failed (.*)$$,<not found: \1>,' SED_CMDS.sh-dots+= -e 's,^\(\*\*\* Error code \)[1-9][0-9]*,\1<nonzero>,' SED_CMDS.sh-errctl= ${STD_SED_CMDS.dj} SED_CMDS.sh-flags= ${STD_SED_CMDS.hide-from-output} +SED_CMDS.shell-csh= ${STD_SED_CMDS.white-space} SED_CMDS.suff-main+= ${STD_SED_CMDS.dg1} SED_CMDS.suff-main-several+= ${STD_SED_CMDS.dg1} SED_CMDS.suff-transform-debug+= ${STD_SED_CMDS.dg1} SED_CMDS.var-op-shell+= ${STD_SED_CMDS.shell} 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.varname-dot-parsedir= -e '/in some cases/ s,^make: "[^"]*,make: "<normalized>,' @@ -523,9 +562,7 @@ SED_CMDS.varname-dot-parsefile= -e '/in some cases/ s,^make: "[^"]*,make: "<norm 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= -e 's,${.CURDIR},<curdir>,g' -SED_CMDS.varname-empty+= -e '/\.PARSEDIR/d' -SED_CMDS.varname-empty+= -e '/\.SHELL/d' +SED_CMDS.varname-empty= ${.OBJDIR .PARSEDIR .PATH .SHELL:L:@v@-e '/\\$v/d'@} # Some tests need an additional round of postprocessing. POSTPROC.deptgt-suffixes= awk '/^\#\*\*\* Suffixes/,/^never-stop/' @@ -541,7 +578,7 @@ unexport-env.rawout: export.mk # Some standard sed commands, to be used in the SED_CMDS above. # Omit details such as process IDs from the output of the -dg1 option. -STD_SED_CMDS.dg1= -e 's,${.CURDIR}$$,<curdir>,' +STD_SED_CMDS.dg1= -e '/\#.* \.$$/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 's, ${DEFSYSPATH:U/usr/share/mk}$$, <defsyspath>,' @@ -597,6 +634,8 @@ 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.white-space= -e 's, *, ,g' -e 's, *$$,,' + # The actual error messages for a failed regcomp or regexec differ between the # implementations. STD_SED_CMDS.regex= \ @@ -661,7 +700,8 @@ TMPDIR:= /tmp/uid${.MAKE.UID} x!= echo; mkdir -p ${TMPDIR} .endif -MAKE_TEST_ENV?= MALLOC_OPTIONS="JA" # for jemalloc +MAKE_TEST_ENV= MALLOC_OPTIONS="JA" # for jemalloc 100 +MAKE_TEST_ENV+= MALLOC_CONF="junk:true" # for jemalloc 510 MAKE_TEST_ENV+= TMPDIR=${TMPDIR} .if ${.MAKE.OS} == "NetBSD" @@ -697,13 +737,22 @@ _SED_CMDS+= -e 's,^usage: ${TEST_MAKE:T:S,.,\\.,g} ,usage: make ,' # replace anything after 'stopped in' with unit-tests _SED_CMDS+= -e '/stopped/s, /.*, unit-tests,' _SED_CMDS+= -e 's,${TMPDIR},TMPDIR,g' -# strip ${.CURDIR}/ from the output -_SED_CMDS+= -e 's,${.CURDIR:S,.,\\.,g}/,,g' +# canonicalize ${.OBJDIR} and ${.CURDIR} +.if ${.OBJDIR} != ${.CURDIR} +# yes this is inaccurate but none of the tests expect <objdir> anywhere +# which we get depending on how MAKEOBJDIR is set. +_SED_CMDS+= -e 's,${.OBJDIR},<curdir>,g' +.endif +_SED_CMDS+= -e 's,${.CURDIR},<curdir>,g' +_SED_CMDS+= -e 's,<curdir>/,,g' _SED_CMDS+= -e 's,${UNIT_TESTS:S,.,\\.,g}/,,g' -# on AT&T derrived systems; false exits 255 not 1 +# on AT&T derived systems: false exits 255 not 1 .if ${.MAKE.OS:N*BSD} != "" _SED_CMDS+= -e 's,\(Error code\) 255,\1 1,' .endif +.if ${.SHELL:T} == "ksh" +_SED_CMDS+= -e '/^set [+-]v/d' +.endif .rawout.out: @${TOOL_SED} ${_SED_CMDS} ${SED_CMDS.${.PREFIX:T}} \ diff --git a/unit-tests/Makefile.config.in b/unit-tests/Makefile.config.in index 0fe24f08d2f9..3139a0d4d0b5 100644 --- a/unit-tests/Makefile.config.in +++ b/unit-tests/Makefile.config.in @@ -1,4 +1,6 @@ -# $Id: Makefile.config.in,v 1.1 2018/12/30 17:14:24 sjg Exp $ +# $Id: Makefile.config.in,v 1.3 2021/10/22 07:48:57 sjg Exp $ srcdir= @srcdir@ +TOOL_DIFF?= @diff@ DIFF_FLAGS?= @diff_u@ +UTC_1= @UTC_1@ diff --git a/unit-tests/cond-cmp-numeric.exp b/unit-tests/cond-cmp-numeric.exp index 4a97b6879e7a..d10262aa8823 100644 --- a/unit-tests/cond-cmp-numeric.exp +++ b/unit-tests/cond-cmp-numeric.exp @@ -6,6 +6,10 @@ CondParser_Eval: !(${:UNaN} == NaN) lhs = "NaN", rhs = "NaN", op = == CondParser_Eval: 123 ! 123 make: "cond-cmp-numeric.mk" line 34: Malformed conditional (123 ! 123) +CondParser_Eval: ${:U 123} < 124 +lhs = 123.000000, rhs = 124.000000, op = < +CondParser_Eval: ${:U123 } < 124 +make: "cond-cmp-numeric.mk" line 50: String comparison operator must be either == or != 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 b1ec3e719d47..b34e5bfc0a06 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.4 2020/11/08 22:56:16 rillig Exp $ +# $NetBSD: cond-cmp-numeric.mk,v 1.5 2021/07/29 06:31:18 rillig Exp $ # # Tests for numeric comparisons in .if conditions. @@ -37,5 +37,21 @@ . error .endif +# Leading spaces are allowed for numbers. +# See EvalCompare and TryParseNumber. +.if ${:U 123} < 124 +.else +. error +.endif + +# Trailing spaces are NOT allowed for numbers. +# See EvalCompare and TryParseNumber. +# expect+1: String comparison operator must be either == or != +.if ${:U123 } < 124 +. error +.else +. error +.endif + all: @:; diff --git a/unit-tests/cond-cmp-string.mk b/unit-tests/cond-cmp-string.mk index 9f3e731b2eb0..305a41099b98 100644 --- a/unit-tests/cond-cmp-string.mk +++ b/unit-tests/cond-cmp-string.mk @@ -1,4 +1,4 @@ -# $NetBSD: cond-cmp-string.mk,v 1.14 2021/01/19 19:54:57 rillig Exp $ +# $NetBSD: cond-cmp-string.mk,v 1.15 2021/12/11 09:53:53 rillig Exp $ # # Tests for string comparisons in .if conditions. @@ -26,7 +26,7 @@ # starting point for variable expressions. Applying the :U modifier to such # an undefined expression turns it into a defined expression. # -# See ApplyModifier_Defined and VEF_DEF. +# See ApplyModifier_Defined and DEF_DEFINED. .if ${:Ustr} != "str" . error .endif diff --git a/unit-tests/cond-eof.exp b/unit-tests/cond-eof.exp index 3b1e6eb1f056..3016a9b27805 100644 --- a/unit-tests/cond-eof.exp +++ b/unit-tests/cond-eof.exp @@ -1,8 +1,5 @@ -side effect make: "cond-eof.mk" line 15: Malformed conditional (0 ${SIDE_EFFECT} ${SIDE_EFFECT2}) -side effect make: "cond-eof.mk" line 17: Malformed conditional (1 ${SIDE_EFFECT} ${SIDE_EFFECT2}) -side effect make: "cond-eof.mk" line 19: Malformed conditional ((0) ${SIDE_EFFECT} ${SIDE_EFFECT2}) make: Fatal errors encountered -- cannot continue make: stopped in unit-tests diff --git a/unit-tests/cond-eof.mk b/unit-tests/cond-eof.mk index 08f432bc4593..ddf4a4cd20c8 100644 --- a/unit-tests/cond-eof.mk +++ b/unit-tests/cond-eof.mk @@ -1,4 +1,4 @@ -# $NetBSD: cond-eof.mk,v 1.2 2020/12/14 20:28:09 rillig Exp $ +# $NetBSD: cond-eof.mk,v 1.3 2021/12/10 23:12:44 rillig Exp $ # # Tests for parsing conditions, especially the end of such conditions, which # are represented as the token TOK_EOF. @@ -7,11 +7,11 @@ SIDE_EFFECT= ${:!echo 'side effect' 1>&2!} SIDE_EFFECT2= ${:!echo 'side effect 2' 1>&2!} # In the following conditions, ${SIDE_EFFECT} is the position of the first -# parse error. It is always fully evaluated, even if it were not necessary -# to expand the variable expression. This is because these syntax errors are -# an edge case that does not occur during normal operation, therefore there -# is no need to optimize for this case, and it would slow down the common -# case as well. +# parse error. Before cond.c 1.286 from 2021-12-10, it was always fully +# evaluated, even if it was not necessary to expand the variable expression. +# These syntax errors are an edge case that does not occur during normal +# operation. Still, it is easy to avoid evaluating these expressions, just in +# case they have side effects. .if 0 ${SIDE_EFFECT} ${SIDE_EFFECT2} .endif .if 1 ${SIDE_EFFECT} ${SIDE_EFFECT2} diff --git a/unit-tests/cond-func-defined.mk b/unit-tests/cond-func-defined.mk index 2aa49ccbf147..43db548a572b 100644 --- a/unit-tests/cond-func-defined.mk +++ b/unit-tests/cond-func-defined.mk @@ -1,4 +1,4 @@ -# $NetBSD: cond-func-defined.mk,v 1.7 2020/11/15 14:07:53 rillig Exp $ +# $NetBSD: cond-func-defined.mk,v 1.8 2021/12/12 08:55:28 rillig Exp $ # # Tests for the defined() function in .if conditions. @@ -29,7 +29,7 @@ ${:UA B}= variable name with spaces . error .endif -# Parse error: missing closing parenthesis; see ParseFuncArg. +# Parse error: missing closing parenthesis; see ParseWord. .if defined(DEF . error .else diff --git a/unit-tests/cond-func-empty.exp b/unit-tests/cond-func-empty.exp index 77a4edd47f49..d1dfda7c03ee 100644 --- a/unit-tests/cond-func-empty.exp +++ b/unit-tests/cond-func-empty.exp @@ -1,5 +1,5 @@ -make: "cond-func-empty.mk" line 152: Unclosed variable "WORD" -make: "cond-func-empty.mk" line 152: Malformed conditional (empty(WORD) +make: "cond-func-empty.mk" line 149: Unclosed variable "WORD" +make: "cond-func-empty.mk" line 149: Malformed conditional (empty(WORD) make: Fatal errors encountered -- cannot continue make: stopped in unit-tests exit status 1 diff --git a/unit-tests/cond-func-empty.mk b/unit-tests/cond-func-empty.mk index 11a990cbbce1..25c850d23d93 100644 --- a/unit-tests/cond-func-empty.mk +++ b/unit-tests/cond-func-empty.mk @@ -1,10 +1,10 @@ -# $NetBSD: cond-func-empty.mk,v 1.14 2021/04/11 13:35:56 rillig Exp $ +# $NetBSD: cond-func-empty.mk,v 1.16 2021/12/11 10:41:31 rillig Exp $ # # Tests for the empty() function in .if conditions, which tests a variable # expression for emptiness. # -# Note that the argument in the parentheses is indeed a variable name, -# optionally followed by variable modifiers. +# Note that the argument in the parentheses is a variable name, not a variable +# expression, optionally followed by variable modifiers. # .undef UNDEF @@ -25,14 +25,10 @@ WORD= word .endif # The :S modifier replaces the empty value with an actual word. The -# expression is now no longer empty, but it is still possible to see whether -# the expression was based on an undefined variable. The expression has the -# flag VEF_UNDEF. -# -# The expression does not have the flag VEF_DEF though, therefore it is still -# considered undefined. Yes, indeed, undefined but not empty. There are a -# few variable modifiers that turn an undefined expression into a defined -# expression, among them :U and :D, but not :S. +# expression is now no longer empty, but it is still based on an undefined +# variable (DEF_UNDEF). There are a few variable modifiers that turn an +# undefined expression into a defined expression, among them :U and :D, but +# not :S. # # XXX: This is hard to explain to someone who doesn't know these # implementation details. @@ -41,19 +37,19 @@ WORD= word . error .endif -# The :U modifier modifies expressions based on undefined variables -# (DEF_UNDEF) by adding the DEF_DEFINED flag, which marks the expression -# as "being interesting enough to be further processed". +# The :U modifier changes the state of a previously undefined expression from +# DEF_UNDEF to DEF_DEFINED. This marks the expression as "being interesting +# enough to be further processed". # .if empty(UNDEF:S,^$,value,W:Ufallback) . error .endif # And now to the surprising part. Applying the following :S modifier to the -# undefined expression makes it non-empty, but the marker VEF_UNDEF is -# preserved nevertheless. The :U modifier that follows only looks at the -# VEF_UNDEF flag to decide whether the variable is defined or not. This kind -# of makes sense since the :U modifier tests the _variable_, not the +# undefined expression makes it non-empty, but the expression is still in +# state DEF_UNDEF. The :U modifier that follows only looks at the state +# DEF_UNDEF to decide whether the variable is defined or not. This kind of +# makes sense since the :U modifier tests the _variable_, not the # _expression_. # # But since the variable was undefined to begin with, the fallback value from @@ -78,12 +74,13 @@ WORD= word . error .endif -# The empty variable named "" gets a fallback value of " ", which counts as -# empty. +# The following example constructs an expression with the variable name "" +# and the value " ". This expression counts as empty since the value contains +# only whitespace. # # Contrary to the other functions in conditionals, the trailing space is not # stripped off, as can be seen in the -dv debug log. If the space had been -# stripped, it wouldn't make a difference in this case. +# stripped, it wouldn't make a difference in this case, but in other cases. # .if !empty(:U ) . error @@ -92,8 +89,8 @@ WORD= word # Now the variable named " " gets a non-empty value, which demonstrates that # 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 +# that variable is indeed undefined. Since CondParser_FuncCallEmpty calls +# Var_Parse without VARE_UNDEFERR, the value of the undefined variable is # returned as an empty string. ${:U }= space .if empty( ) @@ -129,8 +126,8 @@ ${:U }= space # # If everything goes well, the argument expands to "WORD", and that variable # is defined at the beginning of this file. The surrounding 'W' and 'D' -# ensure that the parser in ParseEmptyArg has the correct position, both -# before and after the call to Var_Parse. +# ensure that CondParser_FuncCallEmpty keeps track of the parsing position, +# both before and after the call to Var_Parse. .if empty(W${:UOR}D) . error .endif @@ -155,17 +152,29 @@ ${:U WORD }= variable name with spaces . error .endif -# Between 2020-06-28 and var.c 1.226 from 2020-07-02, this paragraph generated -# a wrong error message "Variable VARNAME is recursive". +# Since cond.c 1.76 from 2020-06-28 and before var.c 1.226 from 2020-07-02, +# the following example generated a wrong error message "Variable VARNAME is +# recursive". +# +# Since at least 1993, the manual page claimed that irrelevant parts of +# conditions were not evaluated, but that was wrong for a long time. The +# expressions in irrelevant parts of the condition were actually evaluated, +# they just allowed undefined variables to be used in the conditions, and the +# result of evaluating them was not used further. These unnecessary +# evaluations were fixed in several commits, starting with var.c 1.226 from +# 2020-07-02. # -# The bug was that the !empty() condition was evaluated, even though this was -# not necessary since the defined() condition already evaluated to false. +# In this example, the variable "VARNAME2" is not defined, so evaluation of +# the condition should have stopped at this point, and the rest of the +# condition should have been processed in parse-only mode. The right-hand +# side containing the '!empty' was evaluated though, as it had always been. # # When evaluating the !empty condition, the variable name was parsed as # "VARNAME${:U2}", but without expanding any nested variable expression, in -# this case the ${:U2}. Therefore, the variable name came out as simply -# "VARNAME". Since this variable name should have been discarded quickly after -# parsing it, this unrealistic variable name should have done no harm. +# this case the ${:U2}. The expression '${:U2}' was replaced with an empty +# string, the resulting variable name was thus "VARNAME". This conceptually +# wrong variable name should have been discarded quickly after parsing it, to +# prevent it from doing any harm. # # The variable expression was expanded though, and this was wrong. The # expansion was done without VARE_WANTRES (called VARF_WANTRES back diff --git a/unit-tests/cond-func.exp b/unit-tests/cond-func.exp index 855b9e5210fd..8dc0f821a255 100644 --- a/unit-tests/cond-func.exp +++ b/unit-tests/cond-func.exp @@ -6,7 +6,7 @@ make: "cond-func.mk" line 102: A plain function name is parsed as !empty(...). make: "cond-func.mk" line 109: A plain function name is parsed as !empty(...). make: "cond-func.mk" line 119: Symbols may start with a function name. make: "cond-func.mk" line 124: Symbols may start with a function name. -make: "cond-func.mk" line 130: Malformed conditional (defined() +make: "cond-func.mk" line 130: Missing closing parenthesis for defined() make: Fatal errors encountered -- cannot continue make: stopped in unit-tests exit status 1 diff --git a/unit-tests/cond-op-and.exp b/unit-tests/cond-op-and.exp index 173b6861a98b..cd6b03a8359e 100644 --- a/unit-tests/cond-op-and.exp +++ b/unit-tests/cond-op-and.exp @@ -1,4 +1,7 @@ -make: "cond-op-and.mk" line 43: Malformed conditional (0 &&& 0) +make: "cond-op-and.mk" line 36: Malformed conditional (0 || (${DEF} && ${UNDEF})) +make: "cond-op-and.mk" line 40: Malformed conditional (0 || (${UNDEF} && ${UNDEF})) +make: "cond-op-and.mk" line 42: Malformed conditional (0 || (!${UNDEF} && ${UNDEF})) +make: "cond-op-and.mk" line 71: Malformed conditional (0 &&& 0) make: Fatal errors encountered -- cannot continue make: stopped in unit-tests exit status 1 diff --git a/unit-tests/cond-op-and.mk b/unit-tests/cond-op-and.mk index 83c694f15723..83386ed77de4 100644 --- a/unit-tests/cond-op-and.mk +++ b/unit-tests/cond-op-and.mk @@ -1,4 +1,4 @@ -# $NetBSD: cond-op-and.mk,v 1.5 2020/10/24 08:46:08 rillig Exp $ +# $NetBSD: cond-op-and.mk,v 1.6 2021/12/10 19:14:35 rillig Exp $ # # Tests for the && operator in .if conditions. @@ -18,11 +18,39 @@ . error .endif + # The right-hand side is not evaluated since the left-hand side is already # false. .if 0 && ${UNDEF} .endif +# When an outer condition makes the inner '&&' condition irrelevant, neither +# of its operands must be evaluated. +# +.if 1 || (${UNDEF} && ${UNDEF}) +.endif + +# Test combinations of outer '||' with inner '&&', to ensure that the operands +# of the inner '&&' are only evaluated if necessary. +DEF= defined +.if 0 || (${DEF} && ${UNDEF}) +.endif +.if 0 || (!${DEF} && ${UNDEF}) +.endif +.if 0 || (${UNDEF} && ${UNDEF}) +.endif +.if 0 || (!${UNDEF} && ${UNDEF}) +.endif +.if 1 || (${DEF} && ${UNDEF}) +.endif +.if 1 || (!${DEF} && ${UNDEF}) +.endif +.if 1 || (${UNDEF} && ${UNDEF}) +.endif +.if 1 || (!${UNDEF} && ${UNDEF}) +.endif + + # The && operator may be abbreviated as &. This is not widely known though # and is also not documented in the manual page. diff --git a/unit-tests/cond-op-or.exp b/unit-tests/cond-op-or.exp index 7888a475e3e4..43b9a5438a31 100644 --- a/unit-tests/cond-op-or.exp +++ b/unit-tests/cond-op-or.exp @@ -1,4 +1,7 @@ -make: "cond-op-or.mk" line 43: Malformed conditional (0 ||| 0) +make: "cond-op-or.mk" line 46: Malformed conditional (1 && (!${DEF} || ${UNDEF})) +make: "cond-op-or.mk" line 48: Malformed conditional (1 && (${UNDEF} || ${UNDEF})) +make: "cond-op-or.mk" line 50: Malformed conditional (1 && (!${UNDEF} || ${UNDEF})) +make: "cond-op-or.mk" line 71: Malformed conditional (0 ||| 0) make: Fatal errors encountered -- cannot continue make: stopped in unit-tests exit status 1 diff --git a/unit-tests/cond-op-or.mk b/unit-tests/cond-op-or.mk index c6993e7c277e..0b7ac55e6c35 100644 --- a/unit-tests/cond-op-or.mk +++ b/unit-tests/cond-op-or.mk @@ -1,4 +1,4 @@ -# $NetBSD: cond-op-or.mk,v 1.6 2020/10/24 08:46:08 rillig Exp $ +# $NetBSD: cond-op-or.mk,v 1.8 2021/12/10 19:14:35 rillig Exp $ # # Tests for the || operator in .if conditions. @@ -18,11 +18,39 @@ . error .endif + # The right-hand side is not evaluated since the left-hand side is already # true. .if 1 || ${UNDEF} .endif +# When an outer condition makes the inner '||' condition irrelevant, neither +# of its operands must be evaluated. This had been wrong in cond.c 1.283 from +# 2021-12-09 and was reverted in cond.c 1.284 an hour later. +.if 0 && (!defined(UNDEF) || ${UNDEF}) +.endif + +# Test combinations of outer '&&' with inner '||', to ensure that the operands +# of the inner '||' is only evaluated if necessary. +DEF= defined +.if 0 && (${DEF} || ${UNDEF}) +.endif +.if 0 && (!${DEF} || ${UNDEF}) +.endif +.if 0 && (${UNDEF} || ${UNDEF}) +.endif +.if 0 && (!${UNDEF} || ${UNDEF}) +.endif +.if 1 && (${DEF} || ${UNDEF}) +.endif +.if 1 && (!${DEF} || ${UNDEF}) +.endif +.if 1 && (${UNDEF} || ${UNDEF}) +.endif +.if 1 && (!${UNDEF} || ${UNDEF}) +.endif + + # The || operator may be abbreviated as |. This is not widely known though # and is also not documented in the manual page. diff --git a/unit-tests/cond-op.exp b/unit-tests/cond-op.exp index 28e8d48e2697..b8f6a4301819 100644 --- a/unit-tests/cond-op.exp +++ b/unit-tests/cond-op.exp @@ -1,20 +1,22 @@ make: "cond-op.mk" line 50: Malformed conditional ("!word" == !word) -make: "cond-op.mk" line 75: Malformed conditional (0 ${ERR::=evaluated}) -make: "cond-op.mk" line 79: After detecting a parse error, the rest is evaluated. -make: "cond-op.mk" line 83: Parsing continues until here. -make: "cond-op.mk" line 86: A B C => (A || B) && C A || B && C A || (B && C) -make: "cond-op.mk" line 93: 0 0 0 => 0 0 0 -make: "cond-op.mk" line 93: 0 0 1 => 0 0 0 -make: "cond-op.mk" line 93: 0 1 0 => 0 0 0 -make: "cond-op.mk" line 93: 0 1 1 => 1 1 1 -make: "cond-op.mk" line 93: 1 0 0 => 0 1 1 -make: "cond-op.mk" line 93: 1 0 1 => 1 1 1 -make: "cond-op.mk" line 93: 1 1 0 => 0 1 1 -make: "cond-op.mk" line 93: 1 1 1 => 1 1 1 -make: "cond-op.mk" line 104: Malformed conditional (1 &&) -make: "cond-op.mk" line 112: Malformed conditional (0 &&) -make: "cond-op.mk" line 120: Malformed conditional (1 ||) -make: "cond-op.mk" line 129: Malformed conditional (0 ||) +make: "cond-op.mk" line 76: Malformed conditional (0 ${ERR::=evaluated}) +make: "cond-op.mk" line 80: A misplaced expression after 0 is not evaluated. +make: "cond-op.mk" line 84: Malformed conditional (1 ${ERR::=evaluated}) +make: "cond-op.mk" line 88: A misplaced expression after 1 is not evaluated. +make: "cond-op.mk" line 92: Parsing continues until here. +make: "cond-op.mk" line 95: A B C => (A || B) && C A || B && C A || (B && C) +make: "cond-op.mk" line 102: 0 0 0 => 0 0 0 +make: "cond-op.mk" line 102: 0 0 1 => 0 0 0 +make: "cond-op.mk" line 102: 0 1 0 => 0 0 0 +make: "cond-op.mk" line 102: 0 1 1 => 1 1 1 +make: "cond-op.mk" line 102: 1 0 0 => 0 1 1 +make: "cond-op.mk" line 102: 1 0 1 => 1 1 1 +make: "cond-op.mk" line 102: 1 1 0 => 0 1 1 +make: "cond-op.mk" line 102: 1 1 1 => 1 1 1 +make: "cond-op.mk" line 113: Malformed conditional (1 &&) +make: "cond-op.mk" line 121: Malformed conditional (0 &&) +make: "cond-op.mk" line 129: Malformed conditional (1 ||) +make: "cond-op.mk" line 138: Malformed conditional (0 ||) make: Fatal errors encountered -- cannot continue make: stopped in unit-tests exit status 1 diff --git a/unit-tests/cond-op.mk b/unit-tests/cond-op.mk index 2ed451c90391..c3ab09f7709a 100644 --- a/unit-tests/cond-op.mk +++ b/unit-tests/cond-op.mk @@ -1,4 +1,4 @@ -# $NetBSD: cond-op.mk,v 1.13 2021/01/19 18:20:30 rillig Exp $ +# $NetBSD: cond-op.mk,v 1.15 2021/12/10 23:12:44 rillig Exp $ # # Tests for operators like &&, ||, ! in .if conditions. # @@ -58,25 +58,34 @@ . error .endif -# As soon as the parser sees the '$', it knows that the condition will -# be malformed. Therefore there is no point in evaluating it. +# In the following malformed conditions, as soon as the parser sees the '$' +# after the '0' or the '1', it knows that the condition will be malformed. +# Therefore there is no point in evaluating the misplaced expression. # -# As of 2021-01-20, that part of the condition is evaluated nevertheless, -# since CondParser_Or just requests the next token, without restricting -# the token to the expected tokens. If the parser were to restrict the -# valid follow tokens for the token "0" to those that can actually produce -# a correct condition (which in this case would be comparison operators, -# TOK_AND, TOK_OR or TOK_RPAREN), the variable expression would not have -# to be evaluated. +# Before cond.c 1.286 from 2021-12-10, the extra expression was evaluated +# nevertheless, since CondParser_Or and CondParser_And asked for the expanded +# next token, even though in this position of the condition, only comparison +# operators, TOK_AND, TOK_OR or TOK_RPAREN are allowed. # -# This would add a good deal of complexity to the code though, for almost -# no benefit, especially since most expressions and conditions are side -# effect free. +# +# +# +# +# +.undef ERR .if 0 ${ERR::=evaluated} . error .endif -.if ${ERR:Uundefined} == evaluated -. info After detecting a parse error, the rest is evaluated. +.if ${ERR:Uundefined} == undefined +. info A misplaced expression after 0 is not evaluated. +.endif + +.undef ERR +.if 1 ${ERR::=evaluated} +. error +.endif +.if ${ERR:Uundefined} == undefined +. info A misplaced expression after 1 is not evaluated. .endif # Just in case that parsing should ever stop on the first error. diff --git a/unit-tests/cond-short.mk b/unit-tests/cond-short.mk index 113c3fd08fed..d41c38488fd6 100644 --- a/unit-tests/cond-short.mk +++ b/unit-tests/cond-short.mk @@ -1,4 +1,4 @@ -# $NetBSD: cond-short.mk,v 1.16 2021/03/14 11:49:37 rillig Exp $ +# $NetBSD: cond-short.mk,v 1.18 2021/12/12 09:49:09 rillig Exp $ # # Demonstrates that in conditions, the right-hand side of an && or || # is only evaluated if it can actually influence the result. @@ -12,7 +12,20 @@ # possible to skip evaluation of irrelevant variable expressions and only # parse them. They were still evaluated though, the only difference to # relevant variable expressions was that in the irrelevant variable -# expressions, undefined variables were allowed. +# expressions, undefined variables were allowed. This allowed for conditions +# like 'defined(VAR) && ${VAR:S,from,to,} != ""', which no longer produced an +# error message 'Malformed conditional', but it still evaluated the +# expression, even though the expression was irrelevant. +# +# Since the initial commit on 1993-03-21, the manual page has been saying that +# make 'will only evaluate a conditional as far as is necessary to determine', +# but that was wrong. The code in cond.c 1.1 from 1993-03-21 looks good since +# it calls Var_Parse(condExpr, VAR_CMD, doEval,&varSpecLen,&doFree), but the +# definition of Var_Parse does not call the third parameter 'doEval', as would +# be expected, but instead 'err', accompanied by the comment 'TRUE if +# undefined variables are an error'. This subtle difference between 'do not +# evaluate at all' and 'allow undefined variables' led to the unexpected +# evaluation. # # See also: # var-eval-short.mk, for short-circuited variable modifiers @@ -211,4 +224,56 @@ x!= echo '0 || $${iV2:U2} < $${V42}: $x' >&2; echo . error .endif + +# Ensure that irrelevant conditions do not influence the result of the whole +# condition. As of cond.c 1.302 from 2021-12-11, an irrelevant function call +# evaluates to true (see CondParser_FuncCall and CondParser_FuncCallEmpty), an +# irrelevant comparison evaluates to false (see CondParser_Comparison). +# +# An irrelevant true bubbles up to the outermost CondParser_And, where it is +# ignored. An irrelevant false bubbles up to the outermost CondParser_Or, +# where it is ignored. +# +# If the condition parser should ever be restructured, the bubbling up of the +# irrelevant evaluation results might show up accidentally. Prevent this. +DEF= defined +.undef UNDEF + +.if 0 && defined(DEF) +. error +.endif + +.if 1 && defined(DEF) +.else +. error +.endif + +.if 0 && defined(UNDEF) +. error +.endif + +.if 1 && defined(UNDEF) +. error +.endif + +.if 0 || defined(DEF) +.else +. error +.endif + +.if 1 || defined(DEF) +.else +. error +.endif + +.if 0 || defined(UNDEF) +. error +.endif + +.if 1 || defined(UNDEF) +.else +. error +.endif + + all: diff --git a/unit-tests/cond-token-plain.exp b/unit-tests/cond-token-plain.exp index 24cfa6bcbc82..8afde2d41788 100644 --- a/unit-tests/cond-token-plain.exp +++ b/unit-tests/cond-token-plain.exp @@ -27,28 +27,35 @@ lhs = "var&&name", rhs = "var&&name", op = != CondParser_Eval: ${:Uvar}||name != "var||name" lhs = "var||name", rhs = "var||name", op = != CondParser_Eval: bare -make: "cond-token-plain.mk" line 102: A bare word is treated like defined(...), and the variable 'bare' is not defined. +make: "cond-token-plain.mk" line 106: A bare word is treated like defined(...), and the variable 'bare' is not defined. CondParser_Eval: VAR -make: "cond-token-plain.mk" line 107: A bare word is treated like defined(...). +make: "cond-token-plain.mk" line 111: A bare word is treated like defined(...). CondParser_Eval: V${:UA}R -make: "cond-token-plain.mk" line 114: ok +make: "cond-token-plain.mk" line 118: ok CondParser_Eval: V${UNDEF}AR -make: "cond-token-plain.mk" line 122: Undefined variables in bare words expand to an empty string. +make: "cond-token-plain.mk" line 126: Undefined variables in bare words expand to an empty string. CondParser_Eval: 0${:Ux00} -make: "cond-token-plain.mk" line 130: Numbers can be composed from literals and variable expressions. -CondParser_Eval: 0${:Ux01} make: "cond-token-plain.mk" line 134: Numbers can be composed from literals and variable expressions. +CondParser_Eval: 0${:Ux01} +make: "cond-token-plain.mk" line 138: Numbers can be composed from literals and variable expressions. CondParser_Eval: "" == -make: "cond-token-plain.mk" line 140: Missing right-hand-side of operator '==' +make: "cond-token-plain.mk" line 144: Missing right-hand side of operator '==' CondParser_Eval: == "" -make: "cond-token-plain.mk" line 148: Malformed conditional (== "") +make: "cond-token-plain.mk" line 152: Malformed conditional (== "") CondParser_Eval: \\ -make: "cond-token-plain.mk" line 163: The variable '\\' is not defined. +make: "cond-token-plain.mk" line 167: The variable '\\' is not defined. CondParser_Eval: \\ -make: "cond-token-plain.mk" line 168: Now the variable '\\' is defined. +make: "cond-token-plain.mk" line 172: Now the variable '\\' is defined. CondParser_Eval: "unquoted\"quoted" != unquoted"quoted lhs = "unquoted"quoted", rhs = "unquoted"quoted", op = != CondParser_Eval: $$$$$$$$ != "" +CondParser_Eval: left == right +make: "cond-token-plain.mk" line 195: Malformed conditional (left == right) +CondParser_Eval: ${0:?:} || left == right +CondParser_Eval: 0 +make: "cond-token-plain.mk" line 201: Malformed conditional (${0:?:} || left == right) +CondParser_Eval: left == right || ${0:?:} +make: "cond-token-plain.mk" line 206: Malformed conditional (left == right || ${0:?:}) make: Fatal errors encountered -- cannot continue make: stopped in unit-tests exit status 1 diff --git a/unit-tests/cond-token-plain.mk b/unit-tests/cond-token-plain.mk index 89f2247e077c..3e59f48bc3c7 100644 --- a/unit-tests/cond-token-plain.mk +++ b/unit-tests/cond-token-plain.mk @@ -1,17 +1,18 @@ -# $NetBSD: cond-token-plain.mk,v 1.10 2021/01/21 14:08:09 rillig Exp $ +# $NetBSD: cond-token-plain.mk,v 1.14 2021/12/12 09:36:00 rillig Exp $ # # Tests for plain tokens (that is, string literals without quotes) -# in .if conditions. +# in .if conditions. These are also called bare words. .MAKEFLAGS: -dc +# The word 'value' after the '!=' is a bare word. .if ${:Uvalue} != value . error .endif -# Malformed condition since comment parsing is done in an early phase -# and removes the '#' and everything behind it long before the condition -# parser gets to see it. +# Using a '#' in a string literal in a condition leads to a malformed +# condition since comment parsing is done in an early phase and removes the +# '#' and everything after it long before the condition parser gets to see it. # # XXX: The error message is missing for this malformed condition. # The right-hand side of the comparison is just a '"', before unescaping. @@ -32,7 +33,10 @@ # in a very early parsing phase. # # See https://gnats.netbsd.org/19596 for example makefiles demonstrating the -# original problems. This workaround is probably not needed anymore. +# original problems. At that time, the parser didn't recognize the comment in +# the line '.else # comment3'. This workaround is not needed anymore since +# comments are stripped in an earlier phase. See "case '#'" in +# CondParser_Token. # # XXX: Missing error message for the malformed condition. The right-hand # side before unescaping is double-quotes, backslash, backslash. @@ -152,7 +156,7 @@ VAR= defined .endif # The '\\' is not a line continuation. Neither is it an unquoted string -# literal. Instead, it is parsed as a function argument (ParseFuncArg), +# literal. Instead, it is parsed as a bare word (ParseWord), # and in that context, the backslash is just an ordinary character. The # function argument thus stays '\\' (2 backslashes). This string is passed # to FuncDefined, and since there is no variable named '\\', the condition @@ -185,6 +189,23 @@ ${:U\\\\}= backslash . error .endif +# In a condition in an .if directive, the left-hand side must not be an +# unquoted string literal. +# expect+1: Malformed conditional (left == right) +.if left == right +.endif +# Before cond.c 1.276 from 2021-09-21, a variable expression containing the +# modifier ':?:' allowed unquoted string literals for the rest of the +# condition. This was an unintended implementation mistake. +# expect+1: Malformed conditional (${0:?:} || left == right) +.if ${0:?:} || left == right +.endif +# This affected only the comparisons after the expression, so the following +# was still a syntax error. +# expect+1: Malformed conditional (left == right || ${0:?:}) +.if left == right || ${0:?:} +.endif + # See cond-token-string.mk for similar tests where the condition is enclosed # in "quotes". diff --git a/unit-tests/deptgt-default.exp b/unit-tests/deptgt-default.exp index 39a9383953dd..09fca899f063 100644 --- a/unit-tests/deptgt-default.exp +++ b/unit-tests/deptgt-default.exp @@ -1 +1,2 @@ +Default command is making 'not-a-target' from 'not-a-target'. exit status 0 diff --git a/unit-tests/deptgt-default.mk b/unit-tests/deptgt-default.mk index 814eaf72aed3..bf5f16536561 100644 --- a/unit-tests/deptgt-default.mk +++ b/unit-tests/deptgt-default.mk @@ -1,8 +1,17 @@ -# $NetBSD: deptgt-default.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# $NetBSD: deptgt-default.mk,v 1.3 2021/12/01 23:56:29 rillig Exp $ # -# Tests for the special target .DEFAULT in dependency declarations. +# Tests for the special target .DEFAULT in dependency declarations, which +# attaches its associated commands to all targets that don't specify any way +# to create them. -# TODO: Implementation +all: test-default not-a-target + +test-default: .PHONY + +has-commands: .PHONY + @echo 'Making ${.TARGET} from ${.IMPSRC}.' + +.DEFAULT: dependency-is-ignored + @echo "Default command is making '${.TARGET}' from '${.IMPSRC}'." all: - @:; diff --git a/unit-tests/deptgt-makeflags.mk b/unit-tests/deptgt-makeflags.mk index 0a0f410e14c4..26f3f5794354 100644 --- a/unit-tests/deptgt-makeflags.mk +++ b/unit-tests/deptgt-makeflags.mk @@ -1,4 +1,4 @@ -# $NetBSD: deptgt-makeflags.mk,v 1.6 2020/11/15 20:20:58 rillig Exp $ +# $NetBSD: deptgt-makeflags.mk,v 1.7 2021/11/29 00:17:10 rillig Exp $ # # Tests for the special target .MAKEFLAGS in dependency declarations, # which adds command line options later, at parse time. @@ -65,7 +65,7 @@ .endif # Next try at defining another newline variable. Since whitespace around the -# variable value is trimmed, two empty variable expressions surround the +# variable value is trimmed, two empty variable expressions ${:U} surround the # literal newline now. This prevents the newline from being skipped during # parsing. The ':=' assignment operator expands the empty variable # expressions, leaving only the newline as the variable value. @@ -81,6 +81,31 @@ .endif #.MAKEFLAGS: -d0 +# Now do the same for the other escape sequences; see Substring_Words. +.MAKEFLAGS: CHAR_BS:="$${:U}\b$${:U}" +.MAKEFLAGS: CHAR_FF:="$${:U}\f$${:U}" +.MAKEFLAGS: CHAR_NL:="$${:U}\n$${:U}" +.MAKEFLAGS: CHAR_CR:="$${:U}\r$${:U}" +.MAKEFLAGS: CHAR_TAB:="$${:U}\t$${:U}" + +# Note: backspace is not whitespace, it is a control character. +.if ${CHAR_BS:C,^[[:cntrl:]]$,found,W} != "found" +. error +.endif +.if ${CHAR_FF:C,^[[:space:]]$,found,W} != "found" +. error +.endif +.if ${CHAR_NL:C,^[[:space:]]$,found,W} != "found" +. error +.endif +.if ${CHAR_CR:C,^[[:space:]]$,found,W} != "found" +. error +.endif +.if ${CHAR_TAB:C,^[[:space:]]$,found,W} != "found" +. error +.endif + + # Unbalanced quotes produce an error message. If they occur anywhere in the # command line, the whole command line is skipped. .MAKEFLAGS: VAR=previous diff --git a/unit-tests/directive-else.exp b/unit-tests/directive-else.exp index 138e893ffa88..17d5571ba74b 100644 --- a/unit-tests/directive-else.exp +++ b/unit-tests/directive-else.exp @@ -1,11 +1,11 @@ -make: "directive-else.mk" line 14: The .else directive does not take arguments. +make: "directive-else.mk" line 14: The .else directive does not take arguments make: "directive-else.mk" line 15: ok make: "directive-else.mk" line 19: ok -make: "directive-else.mk" line 21: The .else directive does not take arguments. +make: "directive-else.mk" line 21: The .else directive does not take arguments make: "directive-else.mk" line 26: if-less else make: "directive-else.mk" line 32: ok make: "directive-else.mk" line 33: warning: extra else -make: "directive-else.mk" line 45: The .else directive does not take arguments. +make: "directive-else.mk" line 45: The .else directive does not take arguments make: Fatal errors encountered -- cannot continue make: stopped in unit-tests exit status 1 diff --git a/unit-tests/directive-endif.exp b/unit-tests/directive-endif.exp index 286d85244eae..0de1ecf0bf25 100644 --- a/unit-tests/directive-endif.exp +++ b/unit-tests/directive-endif.exp @@ -1,7 +1,7 @@ -make: "directive-endif.mk" line 18: The .endif directive does not take arguments. -make: "directive-endif.mk" line 23: The .endif directive does not take arguments. -make: "directive-endif.mk" line 33: The .endif directive does not take arguments. -make: "directive-endif.mk" line 39: The .endif directive does not take arguments. +make: "directive-endif.mk" line 18: The .endif directive does not take arguments +make: "directive-endif.mk" line 23: The .endif directive does not take arguments +make: "directive-endif.mk" line 33: The .endif directive does not take arguments +make: "directive-endif.mk" line 39: The .endif directive does not take arguments make: "directive-endif.mk" line 45: Unknown directive "endifx" make: Fatal errors encountered -- cannot continue make: stopped in unit-tests diff --git a/unit-tests/directive-export-impl.exp b/unit-tests/directive-export-impl.exp index 740daa605129..152fc0de4063 100644 --- a/unit-tests/directive-export-impl.exp +++ b/unit-tests/directive-export-impl.exp @@ -7,7 +7,7 @@ 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 +ModifyWords: split "<>" into 1 word Result of ${UT_VAR:N*} is "" ParseDependency(: ) CondParser_Eval: ${:!echo "\$UT_VAR"!} != "<>" @@ -28,7 +28,7 @@ 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 +ModifyWords: split "<>" into 1 word Result of ${UT_VAR:N*} is "" ParseDependency(: ) ParseReadLine (54): 'REF= defined' diff --git a/unit-tests/directive-for-escape.exp b/unit-tests/directive-for-escape.exp index 59d4c2324f15..492f82e16d1f 100644 --- a/unit-tests/directive-for-escape.exp +++ b/unit-tests/directive-for-escape.exp @@ -11,45 +11,45 @@ make: "directive-for-escape.mk" line 29: !"\\ For: end for 1 For: loop body: . info ${:U\$} -make: "directive-for-escape.mk" line 41: $ +make: "directive-for-escape.mk" line 43: $ For: loop body: . info ${:U${V}} -make: "directive-for-escape.mk" line 41: value +make: "directive-for-escape.mk" line 43: value For: loop body: . info ${:U${V:=-with-modifier}} -make: "directive-for-escape.mk" line 41: value-with-modifier +make: "directive-for-escape.mk" line 43: value-with-modifier For: loop body: . info ${:U$(V)} -make: "directive-for-escape.mk" line 41: value +make: "directive-for-escape.mk" line 43: value For: loop body: . info ${:U$(V:=-with-modifier)} -make: "directive-for-escape.mk" line 41: value-with-modifier +make: "directive-for-escape.mk" line 43: value-with-modifier For: end for 1 For: loop body: . info ${:U\${UNDEF\:U\\$\\$} -make: "directive-for-escape.mk" line 55: ${UNDEF:U\$ +make: "directive-for-escape.mk" line 57: ${UNDEF:U\$ For: loop body: . info ${:U{{\}\}} -make: "directive-for-escape.mk" line 55: {{}} +make: "directive-for-escape.mk" line 57: {{}} For: loop body: . info ${:Uend\}} -make: "directive-for-escape.mk" line 55: end} +make: "directive-for-escape.mk" line 57: end} For: end for 1 For: loop body: . info ${:Ubegin<${UNDEF:Ufallback:N{{{}}}}>end} -make: "directive-for-escape.mk" line 67: begin<fallback>end +make: "directive-for-escape.mk" line 69: begin<fallback>end For: end for 1 For: loop body: . info ${:U\$} -make: "directive-for-escape.mk" line 75: $ +make: "directive-for-escape.mk" line 77: $ For: end for 1 For: loop body: . info ${NUMBERS} ${:Ureplaced} -make: "directive-for-escape.mk" line 83: one two three replaced +make: "directive-for-escape.mk" line 85: one two three replaced For: end for 1 For: loop body: . info ${:Ureplaced} -make: "directive-for-escape.mk" line 93: replaced +make: "directive-for-escape.mk" line 95: replaced For: end for 1 For: loop body: . info . $$i: ${:Uinner} @@ -62,14 +62,31 @@ For: loop body: . info . $${i2}: ${i2} . info . $${i,}: ${i,} . info . adjacent: ${:Uinner}${:Uinner}${:Uinner:M*}${:Uinner} -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 +make: "directive-for-escape.mk" line 103: . $i: 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): inner +make: "directive-for-escape.mk" line 107: . $(i:M*): inner +make: "directive-for-escape.mk" line 108: . ${i${:U}}: outer +make: "directive-for-escape.mk" line 109: . ${i\}}: inner} +make: "directive-for-escape.mk" line 110: . ${i2}: two +make: "directive-for-escape.mk" line 111: . ${i,}: comma +make: "directive-for-escape.mk" line 112: . adjacent: innerinnerinnerinner +For: end for 1 +For: loop body: +. info eight $$$$$$$$ and no cents. +. info eight ${:Udollar}${:Udollar}${:Udollar}${:Udollar} and no cents. +make: "directive-for-escape.mk" line 120: eight $$$$ and no cents. +make: "directive-for-escape.mk" line 121: eight dollardollardollardollar and no cents. +make: "directive-for-escape.mk" line 130: eight and no cents. +For: end for 1 +make: "directive-for-escape.mk" line 137: newline in .for value +make: "directive-for-escape.mk" line 137: newline in .for value +For: loop body: +. info short: ${:U" "} +. info long: ${:U" "} +make: "directive-for-escape.mk" line 138: short: " " +make: "directive-for-escape.mk" line 139: long: " " +make: Fatal errors encountered -- cannot continue +make: stopped in unit-tests +exit status 1 diff --git a/unit-tests/directive-for-escape.mk b/unit-tests/directive-for-escape.mk index babc4b8c6e88..725fa85d68c3 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.7 2021/02/15 07:58:19 rillig Exp $ +# $NetBSD: directive-for-escape.mk,v 1.12 2021/12/05 11:40:03 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 @@ -13,8 +13,8 @@ ASCII= !"\#$$%&'()*+,-./0-9:;<=>?@A-Z[\]_^a-z{|}~ # XXX: As of 2020-12-31, the '#' is not preserved in the expanded body of -# the loop since it would not need only the escaping for the :U variable -# modifier but also the escaping for the line-end comment. +# the loop. Not only would it need the escaping for the variable modifier +# ':U' but also the escaping for the line-end comment. .for chars in ${ASCII} . info ${chars} .endfor @@ -29,19 +29,21 @@ ASCII.2020-12-31= !"\\\#$$%&'()*+,-./0-9:;<=>?@A-Z[\]_^a-z{|}~ . info ${chars} .endfor -# Cover the code in for_var_len. +# Cover the code in ExprLen. # # 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 of a .for -# loop. +# The double '$$' should intuitively prevent exactly this. Probably nobody +# was adventurous enough to use literal dollar signs in the values of a .for +# loop, allowing this edge case to go unnoticed for years. +# +# See for.c, function ExprLen. V= value VALUES= $$ $${V} $${V:=-with-modifier} $$(V) $$(V:=-with-modifier) .for i in ${VALUES} . info $i .endfor -# Try to cover the code for nested '{}' in for_var_len, without success. +# Try to cover the code for nested '{}' in ExprLen, without success. # # 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 @@ -55,9 +57,9 @@ VALUES= $${UNDEF:U\$$\$$ {{}} end} . info $i .endfor -# Second try to cover the code for nested '{}' in for_var_len. +# Second try to cover the code for nested '{}' in ExprLen. # -# XXX: It is wrong that for_var_len requires the braces to be balanced. +# XXX: It is wrong that ExprLen requires the braces to be balanced. # Each variable modifier has its own inconsistent way of parsing nested # variable expressions, braces and parentheses. (Compare ':M', ':S', and # ':D' for details.) The only sensible thing to do is therefore to let @@ -110,4 +112,31 @@ i,= comma . info . adjacent: $i${i}${i:M*}$i .endfor +# The variable name can be a single '$' since there is no check on valid +# variable names. ForLoop_SubstVarShort skips "stupid" variable names though, +# but ForLoop_SubstVarLong naively parses the body of the loop, substituting +# each '${$}' with an actual 'dollar'. +.for $ in dollar +. info eight $$$$$$$$ and no cents. +. info eight ${$}${$}${$}${$} and no cents. +.endfor +# Outside a .for loop, '${$}' is interpreted differently. The outer '$' starts +# a variable expression. The inner '$' is followed by a '}' and is thus a +# silent syntax error, the '$' is skipped. The variable name is thus '', and +# since since there is never a variable named '', the whole expression '${$}' +# evaluates to an empty string. +closing-brace= } # guard against an +${closing-brace}= <closing-brace> # alternative interpretation +.info eight ${$}${$}${$}${$} and no cents. + +# What happens if the values from the .for loop contain a literal newline? +# Before for.c 1.144 from 2021-06-25, the newline was passed verbatim to the +# body of the .for loop, where it was then interpreted as a literal newline, +# leading to syntax errors such as "Unclosed variable expression" in the upper +# line and "Invalid line type" in the lower line. +.for i in "${.newline}" +. info short: $i +. info long: ${i} +.endfor + all: diff --git a/unit-tests/directive-for-if.exp b/unit-tests/directive-for-if.exp new file mode 100644 index 000000000000..85bfc484856b --- /dev/null +++ b/unit-tests/directive-for-if.exp @@ -0,0 +1,8 @@ +make: "directive-for-if.mk" line 48: if-less endif +make: "directive-for-if.mk" line 48: if-less endif +make: "directive-for-if.mk" line 48: if-less endif +VAR1 +VAR3 +make: Fatal errors encountered -- cannot continue +make: stopped in unit-tests +exit status 1 diff --git a/unit-tests/directive-for-if.mk b/unit-tests/directive-for-if.mk new file mode 100644 index 000000000000..8d73e8ae8c4d --- /dev/null +++ b/unit-tests/directive-for-if.mk @@ -0,0 +1,86 @@ +# $NetBSD: directive-for-if.mk,v 1.1 2021/08/30 17:08:13 rillig Exp $ +# +# Test for a .for directive that contains an .if directive. +# +# Before for.c 1.39 from 2008-12-21, when expanding the variables of a .for +# loop, their values were placed verbatim in the expanded body. Since then, +# each variable value expands to an expression of the form ${:Uvalue}. +# +# Before that change, the following adventurous code was possible: +# +# .for directive in if ifdef ifndef +# . ${directive} "1" != "0" +# . endif +# .endfor +# +# A more practical usage of the .for loop that often led to surprises was the +# following: +# +# .for var in VAR1 VAR2 VAR3 +# . if ${var} != "VAR2" +# . endif +# .endfor +# +# The .for loop body expanded to this string: +# +# . if VAR1 != "VAR2" +# . endif +# +# Since bare words were not allowed at the left-hand side of a condition, +# make complained about a "Malformed conditional", which was surprising since +# the code before expanding the .for loop body looked quite well. +# +# In cond.c 1.48 from 2008-11-29, just a month before the expansion of .for +# loops changed from plain textual value to using expressions of the form +# ${:Uvalue}, this surprising behavior was documented in the code, and a +# workaround was implemented that allowed bare words when they are followed +# by either '!' or '=', as part of the operators '!=' or '=='. +# +# Since cond.c 1.68 from 2015-05-05, bare words are allowed on the left-hand +# side of a condition, but that applies only to expression of the form +# ${${cond} :? then : else}, it does not apply to conditions in ordinary .if +# directives. + +# The following snippet worked in 2005, when the variables from the .for loop +# expanded to their bare textual value. +.for directive in if ifdef ifndef +. ${directive} "1" != "0" +. endif +.endfor +# In 2021, the above code does not generate an error message, even though the +# code looks clearly malformed. This is due to the '!', which is interpreted +# as a dependency operator, similar to ':' and '::'. The parser turns this +# line into a dependency with the 3 targets '.', 'if', '"1"' and the 2 sources +# '=' and '"0"'. Since that line is not interpreted as an '.if' directive, +# the error message 'if-less endif' makes sense. + +# In 2005, make complained: +# +# .if line: Malformed conditional (VAR1 != "VAR2") +# .endif line: if-less endif +# .endif line: Need an operator +# +# 2008.11.30.22.37.55 does not complain about the left-hand side ${var}. +.for var in VAR1 VAR2 VAR3 +. if ${var} != "VAR2" +_!= echo "${var}" 1>&2; echo # In 2005, '.info' was not invented yet. +. endif +.endfor + +# Before for.c 1.39 from 2008-12-21, a common workaround was to surround the +# variable expression from the .for loop with '"'. Such a string literal +# has been allowed since cond.c 1.23 from 2004-04-13. Between that commit and +# the one from 2008, the parser would still get confused if the value from the +# .for loop contained '"', which was effectively a code injection. +# +# Surrounding ${var} with quotes disabled the check for typos though. For +# ordinary variables, referring to an undefined variable on the left-hand side +# of the comparison resulted in a "Malformed conditional". Since the .for +# loop was usually close to the .if clause, this was not a problem in +# practice. +.for var in VAR1 VAR2 VAR3 +. if "${var}" != "VAR2" +. endif +.endfor + +all: diff --git a/unit-tests/directive-for-null.exp b/unit-tests/directive-for-null.exp index 37a7d68925ed..dee26de25e63 100644 --- a/unit-tests/directive-for-null.exp +++ b/unit-tests/directive-for-null.exp @@ -1,5 +1,5 @@ make: "(stdin)" line 2: Zero byte read from file -make: "(stdin)" line 2: Unexpected end of file in for loop. +make: "(stdin)" line 2: Unexpected end of file in .for loop make: "(stdin)" line 3: Zero byte read from file make: Fatal errors encountered -- cannot continue make: stopped in unit-tests diff --git a/unit-tests/directive-include.exp b/unit-tests/directive-include.exp index af56eefb2b88..b339f393e7e8 100755 --- a/unit-tests/directive-include.exp +++ b/unit-tests/directive-include.exp @@ -3,6 +3,9 @@ lhs = "directive-include.mk null", rhs = "directive-include.mk null", op = != CondParser_Eval: ${.MAKE.MAKEFILES:T} != "${.PARSEFILE} null" lhs = "directive-include.mk null", rhs = "directive-include.mk null", op = != make: "directive-include.mk" line 25: Could not find nonexistent.mk +make: "directive-include.mk" line 47: Could not find " +make: "directive-include.mk" line 52: Unknown modifier "Z" +make: "directive-include.mk" line 52: Could not find nonexistent.mk make: Fatal errors encountered -- cannot continue make: stopped in unit-tests exit status 1 diff --git a/unit-tests/directive-include.mk b/unit-tests/directive-include.mk index d36914b25a63..a6b300b3d273 100755 --- a/unit-tests/directive-include.mk +++ b/unit-tests/directive-include.mk @@ -1,4 +1,4 @@ -# $NetBSD: directive-include.mk,v 1.5 2020/11/21 14:59:11 rillig Exp $ +# $NetBSD: directive-include.mk,v 1.7 2021/12/03 22:48:07 rillig Exp $ # # Tests for the .include directive, which includes another file. @@ -30,5 +30,25 @@ # As of 2020-11-21, anything after the delimiter '"' is ignored. .include "/dev/null" and ignore anything in the rest of the line. +# The filename to be included can contain expressions. +DEV= null +.include "/dev/${DEV}" + +# Expressions in double quotes or angle quotes are first parsed naively, to +# find the closing '"'. In a second step, the expressions are expanded. This +# means that the expressions cannot include the characters '"' or '>'. This +# restriction is not practically relevant since the expressions inside +# '.include' directives are typically kept as simple as possible. +# +# If the whole line were expanded before parsing, the filename to be included +# would be empty, and the closing '"' would be in the trailing part of the +# line, which is ignored as of 2021-12-03. +DQUOT= " +.include "${DQUOT}" + +# When the expression in a filename cannot be evaluated, the failing +# expression is skipped and the file is included nevertheless. +# FIXME: Add proper error handling, no file must be included here. +.include "nonexistent${:U123:Z}.mk" + all: - @:; diff --git a/unit-tests/export.mk b/unit-tests/export.mk index 94e3a862dce1..bab08ee3ea23 100644 --- a/unit-tests/export.mk +++ b/unit-tests/export.mk @@ -1,4 +1,4 @@ -# $NetBSD: export.mk,v 1.10 2020/10/24 08:50:17 rillig Exp $ +# $NetBSD: export.mk,v 1.11 2021/12/05 14:57:36 rillig 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_OPTIONS|PATH|PWD|SHLVL|_|&)=' +FILTER_CMD?= egrep -v '^(MAKEFLAGS|MALLOC_.*|PATH|PWD|SHLVL|_|&)=' all: @env | ${FILTER_CMD} | sort diff --git a/unit-tests/job-output-null.exp b/unit-tests/job-output-null.exp index af9b4e64dba3..631d4862af44 100644 --- a/unit-tests/job-output-null.exp +++ b/unit-tests/job-output-null.exp @@ -1,4 +1,4 @@ -hello -hello -hello world without newline, hello world without newline, hello world without newline. +1 +2a +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 7620bdf6a7ba..1efd9c667980 100644 --- a/unit-tests/job-output-null.mk +++ b/unit-tests/job-output-null.mk @@ -1,4 +1,4 @@ -# $NetBSD: job-output-null.mk,v 1.1 2021/04/15 19:02:29 rillig Exp $ +# $NetBSD: job-output-null.mk,v 1.3 2021/09/12 10:26:49 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. @@ -7,20 +7,33 @@ # 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 +# how many times the command 'printf "fmt%s" "" "" ""' calls write(2). +# +# NetBSD /bin/sh 1 x write("fmtfmtfmt") +# Dash 1 x write("fmtfmtfmt") +# 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. + .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' '' + @printf '1\0trailing\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' '' '' '' '' '' '' + @printf '2a\0trailing\n''2b\0trailing\n''2c\0trailing\n' @sleep 1 @@ -29,4 +42,4 @@ all: .PHONY # # 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' ', ' ', ' '.' + @printf '3a\0without\0\0\0newline, 3b\0without\0\0\0newline.' diff --git a/unit-tests/lint.exp b/unit-tests/lint.exp index d7068b5e006a..db2290c040cd 100755 --- a/unit-tests/lint.exp +++ b/unit-tests/lint.exp @@ -1,4 +1,4 @@ -make: In the :@ modifier of "VAR", the variable name "${:Ubar:S,b,v,}" must not contain a dollar. +make: In the :@ modifier of "VAR", the variable name "${:Ubar:S,b,v,}" must not contain a dollar y@:Q} xvaluey exit status 2 diff --git a/unit-tests/objdir-writable.exp b/unit-tests/objdir-writable.exp index 9c507f647f8c..e7298a66d369 100644 --- a/unit-tests/objdir-writable.exp +++ b/unit-tests/objdir-writable.exp @@ -1,5 +1,5 @@ make warning: TMPDIR/roobj: Permission denied. -/tmp +TMPDIR TMPDIR/roobj TMPDIR/roobj exit status 0 diff --git a/unit-tests/objdir-writable.mk b/unit-tests/objdir-writable.mk index 9fc1c69afb56..b09baa3c32b2 100644 --- a/unit-tests/objdir-writable.mk +++ b/unit-tests/objdir-writable.mk @@ -1,8 +1,9 @@ -# $NetBSD: objdir-writable.mk,v 1.4 2020/11/14 07:36:00 sjg Exp $ +# $NetBSD: objdir-writable.mk,v 1.5 2021/07/04 01:28:54 sjg Exp $ # test checking for writable objdir -RO_OBJDIR?= ${TMPDIR:U/tmp}/roobj +TMPDIR?= /tmp +RO_OBJDIR?= ${TMPDIR}/roobj .if make(do-objdir) # this should succeed @@ -20,12 +21,12 @@ rm-objdir: @rmdir ${RO_OBJDIR} no-objdir: - @MAKEOBJDIR=${RO_OBJDIR} ${.MAKE} -r -f /dev/null -C /tmp -V .OBJDIR + @MAKEOBJDIR=${RO_OBJDIR} ${.MAKE} -r -f /dev/null -C ${TMPDIR} -V .OBJDIR ro-objdir: - @MAKEOBJDIR=${RO_OBJDIR} ${.MAKE} -r -f /dev/null -C /tmp -V .OBJDIR MAKE_OBJDIR_CHECK_WRITABLE=no + @MAKEOBJDIR=${RO_OBJDIR} ${.MAKE} -r -f /dev/null -C ${TMPDIR} -V .OBJDIR MAKE_OBJDIR_CHECK_WRITABLE=no explicit-objdir: - @MAKEOBJDIR=/tmp ${.MAKE} -r -f ${MAKEFILE:tA} -C /tmp do-objdir -V .OBJDIR + @MAKEOBJDIR=${TMPDIR} ${.MAKE} -r -f ${MAKEFILE:tA} -C ${TMPDIR} do-objdir -V .OBJDIR .endif diff --git a/unit-tests/opt-debug-errors-jobs.exp b/unit-tests/opt-debug-errors-jobs.exp index 25eb2b470b72..c957c7736b32 100644 --- a/unit-tests/opt-debug-errors-jobs.exp +++ b/unit-tests/opt-debug-errors-jobs.exp @@ -24,6 +24,8 @@ line2 *** Failed target: fail-newline *** Failed commands: echo 'line1${.newline}line2'; false + => echo 'line1 +line2'; false *** [fail-newline] Error code 1 make: stopped in unit-tests @@ -45,4 +47,12 @@ word1 word2 *** [fail-multiline-intention] Error code 1 make: stopped in unit-tests + +*** Failed target: fail-vars +*** Failed commands: + @${COMPILE_C} ${COMPILE_C_FLAGS} + => @false c-compiler flag1 -macro="several words" +*** [fail-vars] 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 index 83b50987a752..007a5f37e08a 100644 --- a/unit-tests/opt-debug-errors-jobs.mk +++ b/unit-tests/opt-debug-errors-jobs.mk @@ -1,4 +1,4 @@ -# $NetBSD: opt-debug-errors-jobs.mk,v 1.1 2021/04/27 16:20:06 rillig Exp $ +# $NetBSD: opt-debug-errors-jobs.mk,v 1.2 2021/11/27 23:56:11 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. @@ -10,6 +10,7 @@ all: fail-escaped-space all: fail-newline all: fail-multiline all: fail-multiline-intention +all: fail-vars fail-spaces: echo '3 spaces'; false @@ -34,3 +35,14 @@ fail-multiline: fail-multiline-intention: echo 'word1' \ 'word2'; false + +# In makefiles that rely heavily on abstracted variables, it is not possible +# to determine the actual command from the unexpanded command alone. To help +# debugging these issues (for example in NetBSD's build.sh), output the +# expanded command as well whenever it differs from the unexpanded command. +# Since 2021-11-28. +COMPILE_C= false c-compiler +COMPILE_C_DEFS= macro="several words" +COMPILE_C_FLAGS=flag1 ${COMPILE_C_DEFS:@def@-${def}@} +fail-vars: + @${COMPILE_C} ${COMPILE_C_FLAGS} diff --git a/unit-tests/opt-debug-graph1.exp b/unit-tests/opt-debug-graph1.exp index 4d4aa0c3faea..4049900fee75 100644 --- a/unit-tests/opt-debug-graph1.exp +++ b/unit-tests/opt-debug-graph1.exp @@ -46,7 +46,6 @@ MFLAGS = -r -k -d g1 # Stats: 0 hits 2 misses 0 near misses 0 losers (0%) # refs hits directory # 1 0 <curdir> -# 1 0 . #*** Suffixes: #*** Transformations: diff --git a/unit-tests/opt-debug-graph2.exp b/unit-tests/opt-debug-graph2.exp index 03f02719618e..675e5e8cac18 100644 --- a/unit-tests/opt-debug-graph2.exp +++ b/unit-tests/opt-debug-graph2.exp @@ -81,7 +81,6 @@ MFLAGS = -r -k -d g2 # Stats: 0 hits 4 misses 0 near misses 0 losers (0%) # refs hits directory # 1 0 <curdir> -# 1 0 . #*** Suffixes: #*** Transformations: diff --git a/unit-tests/opt-debug-graph3.exp b/unit-tests/opt-debug-graph3.exp index f2966442eb26..78edb59e4e02 100644 --- a/unit-tests/opt-debug-graph3.exp +++ b/unit-tests/opt-debug-graph3.exp @@ -81,7 +81,6 @@ MFLAGS = -r -k -d g3 # Stats: 0 hits 4 misses 0 near misses 0 losers (0%) # refs hits directory # 1 0 <curdir> -# 1 0 . #*** Suffixes: #*** Transformations: diff --git a/unit-tests/opt-file.mk b/unit-tests/opt-file.mk index b7a1c09e6d16..edeff4b9ab11 100644 --- a/unit-tests/opt-file.mk +++ b/unit-tests/opt-file.mk @@ -1,6 +1,7 @@ -# $NetBSD: opt-file.mk,v 1.12 2021/04/04 10:13:09 rillig Exp $ +# $NetBSD: opt-file.mk,v 1.14 2021/12/09 20:47:33 rillig Exp $ # -# Tests for the -f command line option. +# Tests for the -f command line option, which adds a makefile to the list of +# files that are parsed. # TODO: Implementation @@ -10,7 +11,8 @@ all: file-ending-in-backslash-mmap all: line-with-trailing-whitespace all: file-containing-null-byte -# Passing '-' as the filename reads from stdin. This is unusual but possible. +# When the filename is '-', the input comes from stdin. This is unusual but +# possible. # # In the unlikely case where a file ends in a backslash instead of a newline, # that backslash is trimmed. See ParseGetLine. @@ -19,7 +21,9 @@ all: file-containing-null-byte # outside of the file buffer. # # printf '%s' 'VAR=value\' \ -# | MALLOC_OPTIONS=JA make-2014.01.01.00.00.00 -r -f - -V VAR -dA 2>&1 \ +# | MALLOC_OPTIONS="JA" \ +# MALLOC_CONF="junk:true" \ +# make-2014.01.01.00.00.00 -r -f - -V VAR -dA 2>&1 \ # | less # # The debug output shows how make happily uses freshly allocated memory (the diff --git a/unit-tests/opt-tracefile.exp b/unit-tests/opt-tracefile.exp index 39a9383953dd..0e815606d34f 100644 --- a/unit-tests/opt-tracefile.exp +++ b/unit-tests/opt-tracefile.exp @@ -1 +1,12 @@ +Making dependency1 from <nothing>. +Making dependency2 from <nothing>. +Making trace from dependency1 dependency2. +0 BEG +1 JOB +1 DON +1 JOB +1 DON +1 JOB +1 DON +0 END exit status 0 diff --git a/unit-tests/opt-tracefile.mk b/unit-tests/opt-tracefile.mk index b62392ca913c..291824680606 100644 --- a/unit-tests/opt-tracefile.mk +++ b/unit-tests/opt-tracefile.mk @@ -1,8 +1,16 @@ -# $NetBSD: opt-tracefile.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# $NetBSD: opt-tracefile.mk,v 1.5 2021/12/06 22:35:20 rillig Exp $ # -# Tests for the -T command line option. +# Tests for the command line option '-T', which in jobs mode appends a trace +# record to a trace log whenever a job is started or completed. -# TODO: Implementation +all: .PHONY + @rm -f opt-tracefile.log + @${MAKE} -f ${MAKEFILE} -j1 -Topt-tracefile.log trace + # Remove timestamps, process IDs and directory paths. + @awk '{ print $$2, $$3 }' opt-tracefile.log + @rm opt-tracefile.log -all: - @:; +trace dependency1 dependency2: .PHONY + @echo 'Making ${.TARGET} from ${.ALLSRC:S,^$,<nothing>,W}.' + +trace: dependency1 dependency2 diff --git a/unit-tests/suff-main-several.exp b/unit-tests/suff-main-several.exp index 09fa6d63bffa..b20f5ecf1143 100644 --- a/unit-tests/suff-main-several.exp +++ b/unit-tests/suff-main-several.exp @@ -111,7 +111,6 @@ MFLAGS = -r -k -d mps -d 0 -d g1 # Stats: 0 hits 2 misses 0 near misses 0 losers (0%) # refs hits directory # 1 0 <curdir> -# 1 0 . #*** Suffixes: # ".4" (num 1, ref 1) diff --git a/unit-tests/suff-transform-debug.exp b/unit-tests/suff-transform-debug.exp index 0634ff616d0d..7fec51a1de9d 100644 --- a/unit-tests/suff-transform-debug.exp +++ b/unit-tests/suff-transform-debug.exp @@ -37,7 +37,6 @@ MFLAGS = -r -k -d g1 # Stats: 0 hits 2 misses 0 near misses 0 losers (0%) # refs hits directory # 1 0 <curdir> -# 1 0 . #*** Suffixes: # ".a" (num 1, ref 2) diff --git a/unit-tests/var-eval-short.exp b/unit-tests/var-eval-short.exp index ae0aff7d7c2c..c476c93125e3 100644 --- a/unit-tests/var-eval-short.exp +++ b/unit-tests/var-eval-short.exp @@ -1,16 +1,16 @@ -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: 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}}) +make: "var-eval-short.mk" line 81: Invalid time value at "${FAIL}}" +make: "var-eval-short.mk" line 81: Malformed conditional (0 && ${:Uword:gmtime=${FAIL}}) +make: "var-eval-short.mk" line 95: Invalid time value at "${FAIL}}" +make: "var-eval-short.mk" line 95: 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' +ParseReadLine (160): 'DEFINED= defined' Global: DEFINED = defined CondParser_Eval: 0 && ${DEFINED:L:?${FAIL}then:${FAIL}else} Var_Parse: ${DEFINED:L:?${FAIL}then:${FAIL}else} (parse-only) @@ -20,7 +20,7 @@ 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' +ParseReadLine (163): '.MAKEFLAGS: -d0' ParseDependency(.MAKEFLAGS: -d0) Global: .MAKEFLAGS = -r -k -d cpv -d Global: .MAKEFLAGS = -r -k -d cpv -d 0 diff --git a/unit-tests/var-eval-short.mk b/unit-tests/var-eval-short.mk index 41782f0d7823..1b9ccc714736 100644 --- a/unit-tests/var-eval-short.mk +++ b/unit-tests/var-eval-short.mk @@ -1,4 +1,4 @@ -# $NetBSD: var-eval-short.mk,v 1.5 2021/04/04 13:35:26 rillig Exp $ +# $NetBSD: var-eval-short.mk,v 1.7 2021/09/07 20:41:58 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 @@ -44,13 +44,13 @@ FAIL= ${:!echo unexpected 1>&2!} .if 0 && ${:Uword:@var@${FAIL}@} .endif -# Before var.c,v 1.877 from 2021-03-14, the modifier ':[...]' did not expand +# Before var.c 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 +# Before var.c 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} @@ -58,11 +58,13 @@ FAIL= ${:!echo unexpected 1>&2!} . 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. +# Before var.c 1.856 from 2021-03-14, the modifier ':C' did not expand the +# nested expression ${FAIL}, which is correct, and then tried to compile the +# unexpanded text as a regular expression, which is unnecessary since the +# right-hand side of the '&&' cannot influence the outcome of the condition. +# Compiling the regular expression then failed both because of the '{FAIL}', +# which is not a valid repetition of the form '{1,5}', and because of the +# '****', which are repeated repetitions as well. # '${FAIL}' .if 0 && ${:Uword:C,${FAIL}****,,} .endif diff --git a/unit-tests/var-op-expand.exp b/unit-tests/var-op-expand.exp index 39a9383953dd..a4ba53942cf7 100644 --- a/unit-tests/var-op-expand.exp +++ b/unit-tests/var-op-expand.exp @@ -1 +1,7 @@ -exit status 0 +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: 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 ff62668a8ada..237f7baf1c62 100644 --- a/unit-tests/var-op-expand.mk +++ b/unit-tests/var-op-expand.mk @@ -1,8 +1,14 @@ -# $NetBSD: var-op-expand.mk,v 1.11 2021/01/01 23:07:48 sjg Exp $ +# $NetBSD: var-op-expand.mk,v 1.15 2021/11/30 23:52:19 rillig Exp $ # # Tests for the := variable assignment operator, which expands its # right-hand side. +# +# See also: +# varname-dot-make-save_dollars.mk +# Force the test results to be independent of the default value of this +# setting, which is 'yes' for NetBSD's usr.bin/make but 'no' for the bmake +# distribution and pkgsrc/devel/bmake. .MAKE.SAVE_DOLLARS:= yes # If the right-hand side does not contain a dollar sign, the ':=' assignment @@ -174,5 +180,102 @@ VAR_SUBST_${UNDEF}:= assigned by ':=' . error .endif + +# The following test case demonstrates that the variable 'LATER' is preserved +# in the ':=' assignment since the variable 'LATER' is not yet defined. +# After the assignment to 'LATER', evaluating the variable 'INDIRECT' +# evaluates 'LATER' as well. +# +.undef LATER +INDIRECT:= ${LATER:S,value,replaced,} +.if ${INDIRECT} != "" +. error +.endif +LATER= late-value +.if ${INDIRECT} != "late-replaced" +. error +.endif + + +# Same as the test case above, except for the additional modifier ':tl' when +# evaluating the variable 'INDIRECT'. Nothing surprising here. +.undef LATER +.undef later +INDIRECT:= ${LATER:S,value,replaced,} +.if ${INDIRECT:tl} != "" +. error +.endif +LATER= uppercase-value +later= lowercase-value +.if ${INDIRECT:tl} != "uppercase-replaced" +. error +.endif + + +# Similar to the two test cases above, the situation gets a bit more involved +# here, due to the double indirection. The variable 'indirect' is supposed to +# be the lowercase version of the variable 'INDIRECT'. +# +# The assignment operator ':=' for the variable 'INDIRECT' could be a '=' as +# well, it wouldn't make a difference in this case. The crucial detail is the +# assignment operator ':=' for the variable 'indirect'. During this +# assignment, the variable modifier ':S,value,replaced,' is converted to +# lowercase, which turns 'S' into 's', thus producing an unknown modifier. +# In this case, make issues a warning, but in cases where the modifier +# includes a '=', the modifier would be interpreted as a SysV-style +# substitution like '.c=.o', and make would not issue a warning, leading to +# silent unexpected behavior. +# +# As of 2021-11-20, the actual behavior is unexpected. Fixing it is not +# trivial. When the assignment to 'indirect' takes place, the expressions +# from the nested expression could be preserved, like this: +# +# Start with: +# +# indirect:= ${INDIRECT:tl} +# +# Since INDIRECT is defined, expand it, remembering that the modifier +# ':tl' must still be applied to the final result. +# +# indirect:= ${LATER:S,value,replaced,} \ +# OK \ +# ${LATER:value=sysv} +# +# The variable 'LATER' is not defined. An idea may be to append the +# remaining modifier ':tl' to each expression that is starting with an +# undefined variable, resulting in: +# +# indirect:= ${LATER:S,value,replaced,:tl} \ +# OK \ +# ${LATER:value=sysv:tl} +# +# This would work for the first expression. The second expression ends +# with the SysV modifier ':from=to', and when this modifier is parsed, +# it consumes all characters until the end of the expression, which in +# this case would replace the suffix 'value' with the literal 'sysv:tl', +# ignoring that the ':tl' was intended to be an additional modifier. +# +# Due to all of this, this surprising behavior is not easy to fix. +# +.undef LATER +.undef later +INDIRECT:= ${LATER:S,value,replaced,} OK ${LATER:value=sysv} +indirect:= ${INDIRECT:tl} +# expect+1: Unknown modifier "s,value,replaced," +.if ${indirect} != " ok " +. error +.else +. warning XXX Neither branch should be taken. +.endif +LATER= uppercase-value +later= lowercase-value +# expect+1: Unknown modifier "s,value,replaced," +.if ${indirect} != "uppercase-replaced ok uppercase-sysv" +. warning XXX Neither branch should be taken. +.else +. error +.endif + + all: @:; diff --git a/unit-tests/vardebug.exp b/unit-tests/vardebug.exp index 6d00acc977af..3519bbd0ba1b 100644 --- a/unit-tests/vardebug.exp +++ b/unit-tests/vardebug.exp @@ -43,11 +43,11 @@ Result of ${:Uvalue} is "value" (eval-defined, defined) Indirect modifier "M*e" from "${:UM*e}" Evaluating modifier ${:M...} on value "value" (eval-defined, defined) Pattern for ':M' is "*e" -ModifyWords: split "value" into 1 words +ModifyWords: split "value" into 1 word 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 +ModifyWords: split "value" into 1 word Result of ${:Mvalu[e]} is "value" (eval-defined, defined) Global:delete VAR Var_Parse: ${:Uvariable:unknown} (eval-defined) diff --git a/unit-tests/varmisc.mk b/unit-tests/varmisc.mk index e5ab375d8f39..81818f3fb8bb 100644 --- a/unit-tests/varmisc.mk +++ b/unit-tests/varmisc.mk @@ -1,5 +1,5 @@ -# $Id: varmisc.mk,v 1.23 2021/02/05 20:02:30 sjg Exp $ -# $NetBSD: varmisc.mk,v 1.30 2021/02/04 21:42:47 rillig Exp $ +# $Id: varmisc.mk,v 1.25 2021/12/07 00:03:11 sjg Exp $ +# $NetBSD: varmisc.mk,v 1.32 2021/12/05 10:02:51 rillig Exp $ # # Miscellaneous variable tests. @@ -66,7 +66,7 @@ cmpv: @echo Literal=3.4.5 == ${3.4.5:L:${M_cmpv}} @echo We have ${${.TARGET:T}.only} -# catch misshandling of nested vars in .for loop +# catch mishandling of nested variables in .for loop MAN= MAN1= make.1 .for s in 1 2 @@ -78,12 +78,15 @@ MAN+= ${MAN$s} manok: @echo MAN=${MAN} +# Test parsing of boolean values. # begin .MAKE.SAVE_DOLLARS; see Var_SetWithFlags and ParseBoolean. SD_VALUES= 0 1 2 False True false true Yes No yes no On Off ON OFF on off SD_4_DOLLARS= $$$$ .for val in ${SD_VALUES} -.MAKE.SAVE_DOLLARS:= ${val} # Must be := since a simple = has no effect. +# The assignment must be done using ':=' since a simple '=' would be +# interpreted as 'yes', due to the leading '$'; see ParseBoolean. +.MAKE.SAVE_DOLLARS:= ${val} SD.${val}:= ${SD_4_DOLLARS} .endfor .MAKE.SAVE_DOLLARS:= yes @@ -92,6 +95,7 @@ save-dollars: .for val in ${SD_VALUES} @printf '%s: %-8s = %s\n' $@ ${val} ${SD.${val}:Q} .endfor +# end .MAKE.SAVE_DOLLARS # Appending to an undefined variable does not add a space in front. .undef APPENDED diff --git a/unit-tests/varmod-assign.exp b/unit-tests/varmod-assign.exp index 1e43714d500b..1ad388418ab5 100644 --- a/unit-tests/varmod-assign.exp +++ b/unit-tests/varmod-assign.exp @@ -12,18 +12,6 @@ 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. -1 -2 -3 -mod-assign: ran:3. -mod-assign: global: 1, 3, 1 2 3, 3. -mod-assign-nested: then1t1 -mod-assign-nested: else2e2 -mod-assign-nested: then3t3 -mod-assign-nested: else4e4 make: Bad modifier ":" for variable "" mod-assign-empty: value} make: Bad modifier ":" for variable "" diff --git a/unit-tests/varmod-assign.mk b/unit-tests/varmod-assign.mk index f50c654f5bcf..b8559025fbfd 100644 --- a/unit-tests/varmod-assign.mk +++ b/unit-tests/varmod-assign.mk @@ -1,70 +1,79 @@ -# $NetBSD: varmod-assign.mk,v 1.12 2021/03/15 18:56:38 rillig Exp $ +# $NetBSD: varmod-assign.mk,v 1.14 2021/12/05 10:13:44 rillig Exp $ # # Tests for the obscure ::= variable modifiers, which perform variable # assignments during evaluation, just like the = operator in C. -all: mod-assign -all: mod-assign-nested all: mod-assign-empty all: mod-assign-parse all: mod-assign-shell-error -mod-assign: - # The ::?= modifier applies the ?= assignment operator 3 times. - # The ?= operator only has an effect for the first time, therefore - # the variable FIRST ends up with the value 1. - @echo $@: ${1 2 3:L:@i@${FIRST::?=$i}@} first=${FIRST}. - - # The ::= modifier applies the = assignment operator 3 times. - # The = operator overwrites the previous value, therefore the - # variable LAST ends up with the value 3. - @echo $@: ${1 2 3:L:@i@${LAST::=$i}@} last=${LAST}. - - # The ::+= modifier applies the += assignment operator 3 times. - # The += operator appends 3 times to the variable, therefore - # the variable APPENDED ends up with the value "1 2 3". - @echo $@: ${1 2 3:L:@i@${APPENDED::+=$i}@} appended=${APPENDED}. - - # The ::!= modifier applies the != assignment operator 3 times. - # The side effects of the shell commands are visible in the output. - # Just as with the ::= modifier, the last value is stored in the - # RAN variable. - @echo $@: ${echo.1 echo.2 echo.3:L:@i@${RAN::!=${i:C,.*,&; & 1>\&2,:S,., ,g}}@} ran:${RAN}. - - # The assignments happen in the global scope and thus are - # preserved even after the shell command has been run. - @echo $@: global: ${FIRST:Q}, ${LAST:Q}, ${APPENDED:Q}, ${RAN:Q}. - -mod-assign-nested: - # The condition "1" is true, therefore THEN1 gets assigned a value, - # and IT1 as well. Nothing surprising here. - @echo $@: ${1:?${THEN1::=then1${IT1::=t1}}:${ELSE1::=else1${IE1::=e1}}}${THEN1}${ELSE1}${IT1}${IE1} - - # The condition "0" is false, therefore ELSE1 gets assigned a value, - # and IE1 as well. Nothing surprising here as well. - @echo $@: ${0:?${THEN2::=then2${IT2::=t2}}:${ELSE2::=else2${IE2::=e2}}}${THEN2}${ELSE2}${IT2}${IE2} - - # The same effects happen when the variables are defined elsewhere. - @echo $@: ${SINK3:Q} - @echo $@: ${SINK4:Q} -SINK3:= ${1:?${THEN3::=then3${IT3::=t3}}:${ELSE3::=else3${IE3::=e3}}}${THEN3}${ELSE3}${IT3}${IE3} -SINK4:= ${0:?${THEN4::=then4${IT4::=t4}}:${ELSE4::=else4${IE4::=e4}}}${THEN4}${ELSE4}${IT4}${IE4} +# The modifier '::?=' applies the assignment operator '?=' 3 times. The +# operator '?=' only has an effect for the first time, therefore the variable +# FIRST ends up with the value 1. +.if "${1 2 3:L:@i@${FIRST::?=$i}@} first=${FIRST}" != " first=1" +. error +.endif + +# The modifier '::=' applies the assignment operator '=' 3 times. The +# operator '=' overwrites the previous value, therefore the variable LAST ends +# up with the value 3. +.if "${1 2 3:L:@i@${LAST::=$i}@} last=${LAST}" != " last=3" +. error +.endif + +# The modifier '::+=' applies the assignment operator '+=' 3 times. The +# operator '+=' appends 3 times to the variable, therefore the variable +# APPENDED ends up with the value "1 2 3". +.if "${1 2 3:L:@i@${APPENDED::+=$i}@} appended=${APPENDED}" != " appended=1 2 3" +. error +.endif + +# The modifier '::!=' applies the assignment operator '!=' 3 times. Just as +# with the modifier '::=', the last value is stored in the RAN variable. +.if "${1 2 3:L:@i@${RAN::!=${i:%=echo '<%>';}}@} ran=${RAN}" != " ran=<3>" +. error +.endif + +# The assignments happen in the global scope and thus are preserved even after +# the shell command has been run and the condition has been evaluated. +.if "${FIRST}, ${LAST}, ${APPENDED}, ${RAN}" != "1, 3, 1 2 3, <3>" +. error +.endif + +# Tests for nested assignments, which are hard to read and therefore seldom +# used in practice. + +# The condition "1" is true, therefore THEN1 gets assigned a value, +# and the inner IT1 as well. Nothing surprising here. +.if "${1:?${THEN1::=then1${IT1::=t1}}:${ELSE1::=else1${IE1::=e1}}} ${THEN1}${ELSE1}${IT1}${IE1}" != " then1t1" +. error +.endif + +# The condition "0" is false, therefore ELSE2 gets assigned a value, +# and the inner IE2 as well. Nothing surprising here as well. +.if "${0:?${THEN2::=then2${IT2::=t2}}:${ELSE2::=else2${IE2::=e2}}} ${THEN2}${ELSE2}${IT2}${IE2}" != " else2e2" +. error +.endif + +# The same effects happen when the variables are defined elsewhere. +SINK3:= ${1:?${THEN3::=then3${IT3::=t3}}:${ELSE3::=else3${IE3::=e3}}} ${THEN3}${ELSE3}${IT3}${IE3} +SINK4:= ${0:?${THEN4::=then4${IT4::=t4}}:${ELSE4::=else4${IE4::=e4}}} ${THEN4}${ELSE4}${IT4}${IE4} +.if ${SINK3} != " then3t3" +. error +.endif +.if ${SINK4} != " else4e4" +. error +.endif mod-assign-empty: # Assigning to the empty variable would obviously not work since that # variable is write-protected. Therefore it is rejected early with a # "Bad modifier" message. - # - # XXX: The error message is hard to read since the variable name is - # empty. This leads to a trailing space in the error message. @echo $@: ${::=value} # In this variant, it is not as obvious that the name of the # expression is empty. Assigning to it is rejected as well, with the # same "Bad modifier" message. - # - # XXX: The error message is hard to read since the variable name is - # empty. This leads to a trailing space in the error message. @echo $@: ${:Uvalue::=overwritten} # The :L modifier sets the value of the expression to its variable diff --git a/unit-tests/varmod-defined.exp b/unit-tests/varmod-defined.exp index 2f7d4dbf4baa..e2ae8d29808c 100644 --- a/unit-tests/varmod-defined.exp +++ b/unit-tests/varmod-defined.exp @@ -11,7 +11,7 @@ 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 +ModifyWords: split "$$$$$$$$" into 1 word Global: var = $$$$$$$$ Var_Parse: ${8_DOLLARS} (eval-keep-undefined) ModifyWord_Loop: in "$$$$$$$$", replace "var" with "${8_DOLLARS}" to "$$$$" diff --git a/unit-tests/varmod-defined.mk b/unit-tests/varmod-defined.mk index a44b9f993146..ab5d708cf73f 100644 --- a/unit-tests/varmod-defined.mk +++ b/unit-tests/varmod-defined.mk @@ -1,8 +1,11 @@ -# $NetBSD: varmod-defined.mk,v 1.11 2021/04/11 13:35:56 rillig Exp $ +# $NetBSD: varmod-defined.mk,v 1.12 2021/11/30 23:52:19 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. +# Force the test results to be independent of the default value of this +# setting, which is 'yes' for NetBSD's usr.bin/make but 'no' for the bmake +# distribution and pkgsrc/devel/bmake. .MAKE.SAVE_DOLLARS= yes DEF= defined diff --git a/unit-tests/varmod-gmtime.exp b/unit-tests/varmod-gmtime.exp index 5d5806b92d26..fdc9a2170e2f 100644 --- a/unit-tests/varmod-gmtime.exp +++ b/unit-tests/varmod-gmtime.exp @@ -1,12 +1,12 @@ -make: "varmod-gmtime.mk" line 57: Invalid time value: ${:U1593536400}} != "mtime=11593536400}" +make: "varmod-gmtime.mk" line 57: Invalid time value at "${:U1593536400}} != "mtime=11593536400}"" make: "varmod-gmtime.mk" line 57: Malformed conditional (${%Y:L:gmtime=${:U1593536400}} != "mtime=11593536400}") -make: "varmod-gmtime.mk" line 67: Invalid time value: -1} != "" +make: "varmod-gmtime.mk" line 67: Invalid time value at "-1} != """ make: "varmod-gmtime.mk" line 67: Malformed conditional (${:L:gmtime=-1} != "") -make: "varmod-gmtime.mk" line 76: Invalid time value: 1} != "" +make: "varmod-gmtime.mk" line 76: Invalid time value at " 1} != """ make: "varmod-gmtime.mk" line 76: Malformed conditional (${:L:gmtime= 1} != "") -make: "varmod-gmtime.mk" line 119: Invalid time value: 10000000000000000000000000000000} != "" +make: "varmod-gmtime.mk" line 119: Invalid time value at "10000000000000000000000000000000} != """ make: "varmod-gmtime.mk" line 119: Malformed conditional (${:L:gmtime=10000000000000000000000000000000} != "") -make: "varmod-gmtime.mk" line 130: Invalid time value: error} != "" +make: "varmod-gmtime.mk" line 130: Invalid time value at "error} != """ make: "varmod-gmtime.mk" line 130: Malformed conditional (${:L:gmtime=error} != "") make: Fatal errors encountered -- cannot continue make: stopped in unit-tests diff --git a/unit-tests/varmod-indirect.exp b/unit-tests/varmod-indirect.exp index 63ed988d0c0e..9cff88eb760a 100644 --- a/unit-tests/varmod-indirect.exp +++ b/unit-tests/varmod-indirect.exp @@ -20,7 +20,7 @@ Indirect modifier "S,a,a," from "${:US,a,a,}" Evaluating modifier ${UNDEF:S...} on value "" (eval-keep-dollar-and-undefined, undefined) Modifier part: "a" Modifier part: "a" -ModifyWords: split "" into 1 words +ModifyWords: split "" into 1 word 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' diff --git a/unit-tests/varmod-localtime.exp b/unit-tests/varmod-localtime.exp index ed4d4f053c61..494f160b766e 100644 --- a/unit-tests/varmod-localtime.exp +++ b/unit-tests/varmod-localtime.exp @@ -1,12 +1,12 @@ -make: "varmod-localtime.mk" line 57: Invalid time value: ${:U1593536400}} != "mtime=11593536400}" +make: "varmod-localtime.mk" line 57: Invalid time value at "${:U1593536400}} != "mtime=11593536400}"" make: "varmod-localtime.mk" line 57: Malformed conditional (${%Y:L:localtime=${:U1593536400}} != "mtime=11593536400}") -make: "varmod-localtime.mk" line 67: Invalid time value: -1} != "" +make: "varmod-localtime.mk" line 67: Invalid time value at "-1} != """ make: "varmod-localtime.mk" line 67: Malformed conditional (${:L:localtime=-1} != "") -make: "varmod-localtime.mk" line 76: Invalid time value: 1} != "" +make: "varmod-localtime.mk" line 76: Invalid time value at " 1} != """ make: "varmod-localtime.mk" line 76: Malformed conditional (${:L:localtime= 1} != "") -make: "varmod-localtime.mk" line 119: Invalid time value: 10000000000000000000000000000000} != "" +make: "varmod-localtime.mk" line 119: Invalid time value at "10000000000000000000000000000000} != """ make: "varmod-localtime.mk" line 119: Malformed conditional (${:L:localtime=10000000000000000000000000000000} != "") -make: "varmod-localtime.mk" line 130: Invalid time value: error} != "" +make: "varmod-localtime.mk" line 130: Invalid time value at "error} != """ make: "varmod-localtime.mk" line 130: Malformed conditional (${:L:localtime=error} != "") make: Fatal errors encountered -- cannot continue make: stopped in unit-tests diff --git a/unit-tests/varmod-localtime.mk b/unit-tests/varmod-localtime.mk index f2867b61f8e9..ffa09a0bc5fc 100644 --- a/unit-tests/varmod-localtime.mk +++ b/unit-tests/varmod-localtime.mk @@ -3,7 +3,7 @@ # Tests for the :localtime variable modifier, which formats a timestamp # using strftime(3) in local time. -.if ${TZ} != "Europe/Berlin" # see unit-tests/Makefile +.if ${TZ:Uno:NEurope/Berlin:NUTC-1} != "" # see unit-tests/Makefile . error .endif diff --git a/unit-tests/varmod-loop-delete.exp b/unit-tests/varmod-loop-delete.exp new file mode 100644 index 000000000000..aac86ee39061 --- /dev/null +++ b/unit-tests/varmod-loop-delete.exp @@ -0,0 +1,4 @@ +make: "varmod-loop-delete.mk" line 19: Cannot delete variable "VAR" while it is used +make: Fatal errors encountered -- cannot continue +make: stopped in unit-tests +exit status 1 diff --git a/unit-tests/varmod-loop-delete.mk b/unit-tests/varmod-loop-delete.mk new file mode 100644 index 000000000000..ed145b59ba0f --- /dev/null +++ b/unit-tests/varmod-loop-delete.mk @@ -0,0 +1,33 @@ +# $NetBSD: varmod-loop-delete.mk,v 1.2 2021/12/05 15:51:33 rillig Exp $ +# +# Tests for the variable modifier ':@', which as a side effect allows to +# delete an arbitrary variable. + +# A side effect of the modifier ':@' is that the loop variable is created as +# an actual variable in the current evaluation scope (Command/Global/target), +# and at the end of the loop, this variable is deleted. Since var.c 1.204 +# from 2016-02-18 and before var.c 1.963 from 2021-12-05, a variable could be +# deleted while it was in use, leading to a use-after-free bug. +# +# See Var_Parse, comment 'the value of the variable must not change'. + +# Set up the variable that deletes itself when it is evaluated. +VAR= ${:U:@VAR@@} rest of the value + +# In an assignment, the scope is 'Global'. Since the variable 'VAR' is +# defined in the global scope, it deletes itself. +EVAL:= ${VAR} +.if ${EVAL} != " rest of the value" +. error +.endif + +VAR= ${:U:@VAR@@} rest of the value +all: .PHONY + # In the command that is associated with a target, the scope is the + # one from the target. That scope only contains a few variables like + # '.TARGET', '.ALLSRC', '.IMPSRC'. Make does not expect that these + # variables get modified from the outside. + # + # There is no variable named 'VAR' in the local scope, so nothing + # happens. + : $@: '${VAR}' diff --git a/unit-tests/varmod-loop-varname.exp b/unit-tests/varmod-loop-varname.exp index 9170307bd2a0..4f0379d5ea0a 100644 --- a/unit-tests/varmod-loop-varname.exp +++ b/unit-tests/varmod-loop-varname.exp @@ -1,11 +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: "varmod-loop-varname.mk" line 16: In the :@ modifier of "", the variable name "${:Ubar:S,b,v,}" must not contain a dollar +make: "varmod-loop-varname.mk" line 16: Malformed conditional (${:Uone two three:@${:Ubar:S,b,v,}@+${var}+@} != "+one+ +two+ +three+") +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)@} != "(1) (2) (3)") +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: "varmod-loop-varname.mk" line 95: In the :@ modifier of "1 2 3", the variable name "v$$$" must not contain a dollar +make: "varmod-loop-varname.mk" line 95: 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 index d51e2ba76a42..91f8a4876466 100644 --- a/unit-tests/varmod-loop-varname.mk +++ b/unit-tests/varmod-loop-varname.mk @@ -1,8 +1,11 @@ -# $NetBSD: varmod-loop-varname.mk,v 1.2 2021/04/04 13:35:26 rillig Exp $ +# $NetBSD: varmod-loop-varname.mk,v 1.4 2021/12/05 15:01:04 rillig Exp $ # # Tests for the first part of the variable modifier ':@var@...@', which # contains the variable name to use during the loop. +# Force the test results to be independent of the default value of this +# setting, which is 'yes' for NetBSD's usr.bin/make but 'no' for the bmake +# distribution and pkgsrc/devel/bmake. .MAKE.SAVE_DOLLARS= yes @@ -12,6 +15,8 @@ # variable name. .if ${:Uone two three:@${:Ubar:S,b,v,}@+${var}+@} != "+one+ +two+ +three+" . error +.else +. error .endif diff --git a/unit-tests/varmod-loop.exp b/unit-tests/varmod-loop.exp index a4704973f6e2..d05b870d5b5e 100644 --- a/unit-tests/varmod-loop.exp +++ b/unit-tests/varmod-loop.exp @@ -1,10 +1,10 @@ -ParseReadLine (75): 'USE_8_DOLLARS= ${:U1:@var@${8_DOLLARS}@} ${8_DOLLARS} $$$$$$$$' +ParseReadLine (78): 'USE_8_DOLLARS= ${:U1:@var@${8_DOLLARS}@} ${8_DOLLARS} $$$$$$$$' CondParser_Eval: ${USE_8_DOLLARS} != "\$\$\$\$ \$\$\$\$ \$\$\$\$" lhs = "$$$$ $$$$ $$$$", rhs = "$$$$ $$$$ $$$$", op = != -ParseReadLine (80): 'SUBST_CONTAINING_LOOP:= ${USE_8_DOLLARS}' +ParseReadLine (83): 'SUBST_CONTAINING_LOOP:= ${USE_8_DOLLARS}' CondParser_Eval: ${SUBST_CONTAINING_LOOP} != "\$\$ \$\$\$\$ \$\$\$\$" lhs = "$$ $$$$ $$$$", rhs = "$$ $$$$ $$$$", op = != -ParseReadLine (105): '.MAKEFLAGS: -d0' +ParseReadLine (108): '.MAKEFLAGS: -d0' ParseDependency(.MAKEFLAGS: -d0) :varname-overwriting-target: :x1y x2y x3y: :: mod-loop-dollar:1: diff --git a/unit-tests/varmod-loop.mk b/unit-tests/varmod-loop.mk index 4fdaa3ff4e61..82046ff95d79 100644 --- a/unit-tests/varmod-loop.mk +++ b/unit-tests/varmod-loop.mk @@ -1,7 +1,10 @@ -# $NetBSD: varmod-loop.mk,v 1.15 2021/04/11 13:35:56 rillig Exp $ +# $NetBSD: varmod-loop.mk,v 1.18 2021/12/05 15:20:13 rillig Exp $ # # Tests for the :@var@...${var}...@ variable modifier. +# Force the test results to be independent of the default value of this +# setting, which is 'yes' for NetBSD's usr.bin/make but 'no' for the bmake +# distribution and pkgsrc/devel/bmake. .MAKE.SAVE_DOLLARS= yes all: varname-overwriting-target @@ -183,7 +186,4 @@ CMDLINE= global # needed for deleting the environment . 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'. +all: .PHONY diff --git a/unit-tests/varmod-order-numeric.exp b/unit-tests/varmod-order-numeric.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/varmod-order-numeric.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/varmod-order-numeric.mk b/unit-tests/varmod-order-numeric.mk new file mode 100644 index 000000000000..40a1d408e9ae --- /dev/null +++ b/unit-tests/varmod-order-numeric.mk @@ -0,0 +1,54 @@ +# $NetBSD: varmod-order-numeric.mk,v 1.5 2021/08/03 04:46:49 rillig Exp $ +# +# Tests for the variable modifiers ':On', which returns the words, sorted in +# ascending numeric order, and for ':Orn' and ':Onr', which additionally +# reverse the order. +# +# The variable modifiers ':On', ':Onr' and ':Orn' were added in var.c 1.939 +# from 2021-07-30. + +# This list contains only 32-bit numbers since the make code needs to conform +# to C90, which does not provide integer types larger than 32 bit. It uses +# 'long long' by default, but that type is overridable if necessary to support +# older environments. +# +# To get 53-bit integers even in C90, it would be possible to switch to +# 'double' instead, but that would allow floating-point numbers as well, which +# is out of scope for this variable modifier. +NUMBERS= 3 5 7 1 42 -42 5K -3m 1M 1k -2G + +.if ${NUMBERS:On} != "-2G -3m -42 1 3 5 7 42 1k 5K 1M" +. error ${NUMBERS:On} +.endif + +.if ${NUMBERS:Orn} != "1M 5K 1k 42 7 5 3 1 -42 -3m -2G" +. error ${NUMBERS:Orn} +.endif + +# Both ':Onr' and ':Orn' have the same effect. +.if ${NUMBERS:Onr} != "1M 5K 1k 42 7 5 3 1 -42 -3m -2G" +. error ${NUMBERS:Onr} +.endif + +# Duplicate numbers are preserved in the output. In this case the +# equal-valued numbers are spelled the same, so they are indistinguishable in +# the output. +DUPLICATES= 3 1 2 2 1 1 # https://oeis.org/A034002 +.if ${DUPLICATES:On} != "1 1 1 2 2 3" +. error ${DUPLICATES:On} +.endif + +# If there are several numbers that have the same integer value, they are +# returned in unspecified order. +SAME_VALUE:= ${:U 79 80 0x0050 81 :On} +.if ${SAME_VALUE} != "79 80 0x0050 81" && ${SAME_VALUE} != "79 0x0050 80 81" +. error ${SAME_VALUE} +.endif + +# Hexadecimal and octal numbers are supported as well. +MIXED_BASE= 0 010 0x7 9 +.if ${MIXED_BASE:On} != "0 0x7 010 9" +. error ${MIXED_BASE:On} +.endif + +all: diff --git a/unit-tests/varmod-order-reverse.mk b/unit-tests/varmod-order-reverse.mk index 1a6d2d766f76..c3be8d0f7817 100644 --- a/unit-tests/varmod-order-reverse.mk +++ b/unit-tests/varmod-order-reverse.mk @@ -1,13 +1,12 @@ -# $NetBSD: varmod-order-reverse.mk,v 1.4 2020/10/24 08:46:08 rillig Exp $ +# $NetBSD: varmod-order-reverse.mk,v 1.5 2021/08/03 04:46:49 rillig Exp $ # # Tests for the :Or variable modifier, which returns the words, sorted in # descending order. -NUMBERS= one two three four five six seven eight nine ten +WORDS= one two three four five six seven eight nine ten -.if ${NUMBERS:Or} != "two three ten six seven one nine four five eight" -. error ${NUMBERS:Or} +.if ${WORDS:Or} != "two three ten six seven one nine four five eight" +. error ${WORDS:Or} .endif all: - @:; diff --git a/unit-tests/varmod-order-shuffle.mk b/unit-tests/varmod-order-shuffle.mk index 185141b6c4a5..16121d7e498f 100644 --- a/unit-tests/varmod-order-shuffle.mk +++ b/unit-tests/varmod-order-shuffle.mk @@ -1,4 +1,4 @@ -# $NetBSD: varmod-order-shuffle.mk,v 1.6 2020/11/09 20:16:33 rillig Exp $ +# $NetBSD: varmod-order-shuffle.mk,v 1.7 2021/08/03 04:46:49 rillig Exp $ # # Tests for the :Ox variable modifier, which returns the words of the # variable, shuffled. @@ -11,7 +11,7 @@ # # Tags: probabilistic -NUMBERS= one two three four five six seven eight nine ten +WORDS= one two three four five six seven eight nine ten # Note that 1 in every 10! trials two independently generated # randomized orderings will be the same. The test framework doesn't @@ -20,24 +20,23 @@ NUMBERS= one two three four five six seven eight nine ten # lets the whole test fail once in 1.209.600 runs, on average. # Create two shuffles using the := assignment operator. -shuffled1:= ${NUMBERS:Ox} -shuffled2:= ${NUMBERS:Ox} +shuffled1:= ${WORDS:Ox} +shuffled2:= ${WORDS:Ox} .if ${shuffled1} == ${shuffled2} . error ${shuffled1} == ${shuffled2} .endif # Sorting the list before shuffling it has no effect. -shuffled1:= ${NUMBERS:O:Ox} -shuffled2:= ${NUMBERS:O:Ox} +shuffled1:= ${WORDS:O:Ox} +shuffled2:= ${WORDS:O:Ox} .if ${shuffled1} == ${shuffled2} . error ${shuffled1} == ${shuffled2} .endif # Sorting after shuffling must produce the original numbers. -sorted:= ${NUMBERS:Ox:O} -.if ${sorted} != ${NUMBERS:O} -. error ${sorted} != ${NUMBERS:O} +sorted:= ${WORDS:Ox:O} +.if ${sorted} != ${WORDS:O} +. error ${sorted} != ${WORDS:O} .endif all: - @:; diff --git a/unit-tests/varmod-order-string.exp b/unit-tests/varmod-order-string.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/varmod-order-string.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/varmod-order-string.mk b/unit-tests/varmod-order-string.mk new file mode 100644 index 000000000000..bb0a145ba825 --- /dev/null +++ b/unit-tests/varmod-order-string.mk @@ -0,0 +1,28 @@ +# $NetBSD: varmod-order-string.mk,v 1.2 2021/08/03 04:46:49 rillig Exp $ +# +# Tests for the :O variable modifier, which returns the words, sorted in +# ascending order. + +# Simple words are sorted lexicographically. +WORDS= one two three four five six seven eight nine ten +.if ${WORDS:O} != "eight five four nine one seven six ten three two" +. error ${WORDS:O} +.endif + +# Double quotes and single quotes delimit words, while backticks are just +# regular characters. Therefore '`in' is a separate word from 'backticks`', +# and the additional spaces between them are removed. +QUOTED_WORDS= none "double quoted" 'single quoted' `in backticks` +.if ${QUOTED_WORDS:O} != "\"double quoted\" 'single quoted' `in backticks` none" +. error ${QUOTED_WORDS:O} +.endif + +# Numbers are sorted lexicographically as well. +# To sort the words numerically, use ':On' instead; since var.c 1.939 from +# 2021-07-30. +NUMBERS= -100g -50m -7k -50 -13 0 000 13 50 5k1 7k 50m 100G +.if ${NUMBERS:O} != "-100g -13 -50 -50m -7k 0 000 100G 13 50 50m 5k1 7k" +. error ${NUMBERS:O} +.endif + +all: diff --git a/unit-tests/varmod-order.exp b/unit-tests/varmod-order.exp index 94c3cb694886..46dc45e9f6d6 100644 --- a/unit-tests/varmod-order.exp +++ b/unit-tests/varmod-order.exp @@ -1,7 +1,24 @@ -make: Bad modifier ":OX" for variable "NUMBERS" -make: "varmod-order.mk" line 13: Undefined variable "${NUMBERS:OX" -make: Bad modifier ":OxXX" for variable "NUMBERS" -make: "varmod-order.mk" line 16: Undefined variable "${NUMBERS:Ox" +make: Bad modifier ":OX" for variable "WORDS" +make: "varmod-order.mk" line 14: Undefined variable "${WORDS:OX" +make: Bad modifier ":OxXX" for variable "WORDS" +make: "varmod-order.mk" line 17: Undefined variable "${WORDS:Ox" +make: Unclosed variable expression, expecting '}' for modifier "O" of variable "WORDS" with value "eight five four nine one seven six ten three two" +make: Unclosed variable expression, expecting '}' for modifier "On" of variable "NUMBERS" with value "1 2 3 4 5 6 7 8 9 10" +make: Unclosed variable expression, expecting '}' for modifier "Onr" of variable "NUMBERS" with value "10 9 8 7 6 5 4 3 2 1" +make: Bad modifier ":Oxn" for variable "NUMBERS" +make: "varmod-order.mk" line 29: Malformed conditional (${NUMBERS:Oxn}) +make: Bad modifier ":On_typo" for variable "NUMBERS" +make: "varmod-order.mk" line 39: Malformed conditional (${NUMBERS:On_typo}) +make: Bad modifier ":Onr_typo" for variable "NUMBERS" +make: "varmod-order.mk" line 48: Malformed conditional (${NUMBERS:Onr_typo}) +make: Bad modifier ":Orn_typo" for variable "NUMBERS" +make: "varmod-order.mk" line 57: Malformed conditional (${NUMBERS:Orn_typo}) +make: Bad modifier ":Onn" for variable "NUMBERS" +make: "varmod-order.mk" line 68: Malformed conditional (${NUMBERS:Onn}) +make: Bad modifier ":Onrr" for variable "NUMBERS" +make: "varmod-order.mk" line 77: Malformed conditional (${NUMBERS:Onrr}) +make: Bad modifier ":Orrn" for variable "NUMBERS" +make: "varmod-order.mk" line 86: Malformed conditional (${NUMBERS:Orrn}) make: Fatal errors encountered -- cannot continue make: stopped in unit-tests exit status 1 diff --git a/unit-tests/varmod-order.mk b/unit-tests/varmod-order.mk index 675b6efec5e7..7f3d485fe8e3 100644 --- a/unit-tests/varmod-order.mk +++ b/unit-tests/varmod-order.mk @@ -1,19 +1,92 @@ -# $NetBSD: varmod-order.mk,v 1.5 2020/10/24 08:46:08 rillig Exp $ +# $NetBSD: varmod-order.mk,v 1.7 2021/08/03 04:46:49 rillig Exp $ # -# Tests for the :O variable modifier, which returns the words, sorted in -# ascending order. +# Tests for the :O variable modifier and its variants, which either sort the +# words of the value or shuffle them. -NUMBERS= one two three four five six seven eight nine ten +WORDS= one two three four five six seven eight nine ten +NUMBERS= 8 5 4 9 1 7 6 10 3 2 # in English alphabetical order -.if ${NUMBERS:O} != "eight five four nine one seven six ten three two" -. error ${NUMBERS:O} +.if ${WORDS:O} != "eight five four nine one seven six ten three two" +. error ${WORDS:O} .endif # Unknown modifier "OX" -_:= ${NUMBERS:OX} +_:= ${WORDS:OX} # Unknown modifier "OxXX" -_:= ${NUMBERS:OxXX} +_:= ${WORDS:OxXX} + +# Missing closing brace, to cover the error handling code. +_:= ${WORDS:O +_:= ${NUMBERS:On +_:= ${NUMBERS:Onr + +# Shuffling numerically doesn't make sense, so don't allow 'x' and 'n' to be +# combined. +# +# expect-text: Bad modifier ":Oxn" for variable "NUMBERS" +# expect+1: Malformed conditional (${NUMBERS:Oxn}) +.if ${NUMBERS:Oxn} +. error +.else +. error +.endif + +# Extra characters after ':On' are detected and diagnosed. +# TODO: Add line number information to the "Bad modifier" diagnostic. +# +# expect-text: Bad modifier ":On_typo" for variable "NUMBERS" +.if ${NUMBERS:On_typo} +. error +.else +. error +.endif + +# Extra characters after ':Onr' are detected and diagnosed. +# +# expect-text: Bad modifier ":Onr_typo" for variable "NUMBERS" +.if ${NUMBERS:Onr_typo} +. error +.else +. error +.endif + +# Extra characters after ':Orn' are detected and diagnosed. +# +# expect+1: Bad modifier ":Orn_typo" for variable "NUMBERS" +.if ${NUMBERS:Orn_typo} +. error +.else +. error +.endif + +# Repeating the 'n' is not supported. In the typical use cases, the sorting +# criteria are fixed, not computed, therefore allowing this redundancy does +# not make sense. +# +# expect-text: Bad modifier ":Onn" for variable "NUMBERS" +.if ${NUMBERS:Onn} +. error +.else +. error +.endif + +# Repeating the 'r' is not supported as well, for the same reasons as above. +# +# expect-text: Bad modifier ":Onrr" for variable "NUMBERS" +.if ${NUMBERS:Onrr} +. error +.else +. error +.endif + +# Repeating the 'r' is not supported as well, for the same reasons as above. +# +# expect-text: Bad modifier ":Orrn" for variable "NUMBERS" +.if ${NUMBERS:Orrn} +. error +.else +. error +.endif all: - @:; diff --git a/unit-tests/varmod-root.exp b/unit-tests/varmod-root.exp index 2c99cd3ef4c7..39a9383953dd 100644 --- a/unit-tests/varmod-root.exp +++ b/unit-tests/varmod-root.exp @@ -1,11 +1 @@ -root of 'a/b/c' is 'a/b/c' -root of 'def' is 'def' -root of 'a.b.c' is 'a.b' -root of 'a.b/c' is 'a' -root of 'a' is 'a' -root of 'a.a' is 'a' -root of '.gitignore' is '' -root of 'a' is 'a' -root of 'a.a' is 'a' -root of 'trailing/' is 'trailing/' exit status 0 diff --git a/unit-tests/varmod-root.mk b/unit-tests/varmod-root.mk index 1e3159733df0..cf88491df799 100644 --- a/unit-tests/varmod-root.mk +++ b/unit-tests/varmod-root.mk @@ -1,9 +1,38 @@ -# $NetBSD: varmod-root.mk,v 1.4 2020/12/20 22:57:40 rillig Exp $ +# $NetBSD: varmod-root.mk,v 1.5 2021/12/05 22:31:58 rillig Exp $ # # Tests for the :R variable modifier, which returns the filename root # without the extension. +.if ${a/b/c:L:R} != "a/b/c" +. error +.endif + +.if ${def:L:R} != "def" +. error +.endif + +.if ${a.b.c:L:R} != "a.b" +. error +.endif + +.if ${a.b/c:L:R} != "a" +. error +.endif + +.if ${a:L:R} != "a" +. error +.endif + +.if ${a.a:L:R} != "a" +. error +.endif + +.if ${.gitignore:L:R} != "" +. error +.endif + +.if ${trailing/:L:R} != "trailing/" +. error +.endif + all: -.for path in a/b/c def a.b.c a.b/c a a.a .gitignore a a.a trailing/ - @echo "root of '"${path:Q}"' is '"${path:R:Q}"'" -.endfor diff --git a/unit-tests/varmod-select-words.mk b/unit-tests/varmod-select-words.mk index a9df25f9ff32..ab094bf056b0 100644 --- a/unit-tests/varmod-select-words.mk +++ b/unit-tests/varmod-select-words.mk @@ -1,7 +1,10 @@ -# $NetBSD: varmod-select-words.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# $NetBSD: varmod-select-words.mk,v 1.3 2021/12/05 12:06:23 rillig Exp $ # # Tests for the :[...] variable modifier, which selects a single word # or a range of words from a variable. +# +# See also: +# modword.mk (should be migrated here) # TODO: Implementation diff --git a/unit-tests/varmod-subst.mk b/unit-tests/varmod-subst.mk index 85f41e499ab7..763535ff835a 100644 --- a/unit-tests/varmod-subst.mk +++ b/unit-tests/varmod-subst.mk @@ -1,4 +1,4 @@ -# $NetBSD: varmod-subst.mk,v 1.8 2021/05/14 19:37:16 rillig Exp $ +# $NetBSD: varmod-subst.mk,v 1.9 2021/09/06 21:18:55 rillig Exp $ # # Tests for the :S,from,to, variable modifier. @@ -86,6 +86,19 @@ WORDS= sequences of letters . error The '.' seems to be interpreted as a wildcard of some kind. .endif +.if ${:Uvalue:S,^val,&,} != "value" +. error +.endif +.if ${:Uvalue:S,ue$,&,} != "value" +. error +.endif +.if ${:Uvalue:S,^val,&-&-&,} != "val-val-value" +. error +.endif +.if ${:Uvalue:S,ue$,&-&-&,} != "value-ue-ue" +. error +.endif + mod-subst: @echo $@: @echo :${:Ua b b c:S,a b,,:Q}: diff --git a/unit-tests/varmod-to-separator.exp b/unit-tests/varmod-to-separator.exp index c6e8ce98a21a..3f8f1b2a11eb 100644 --- a/unit-tests/varmod-to-separator.exp +++ b/unit-tests/varmod-to-separator.exp @@ -1,6 +1,6 @@ -make: "varmod-to-separator.mk" line 107: Invalid character number: 400:tu} +make: "varmod-to-separator.mk" line 107: Invalid character number at "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: Invalid character number at "100:tu}" make: "varmod-to-separator.mk" line 121: Malformed conditional (${WORDS:[1..3]:ts\x100:tu}) make: Bad modifier ":ts\-300" for variable "WORDS" make: "varmod-to-separator.mk" line 128: Malformed conditional (${WORDS:[1..3]:ts\-300:tu}) diff --git a/unit-tests/varmod-unique.mk b/unit-tests/varmod-unique.mk index 04d04a575af1..7fef35b69211 100644 --- a/unit-tests/varmod-unique.mk +++ b/unit-tests/varmod-unique.mk @@ -1,47 +1,46 @@ -# $NetBSD: varmod-unique.mk,v 1.5 2021/05/30 20:26:41 rillig Exp $ +# $NetBSD: varmod-unique.mk,v 1.6 2021/12/05 22:37:58 rillig Exp $ # # Tests for the :u variable modifier, which discards adjacent duplicate # words. -.if ${:U1 2 1:u} != "1 2 1" -. warning The :u modifier only merges _adjacent_ duplicate words. +.if ${1 2 1:L:u} != "1 2 1" +. warning The modifier ':u' only merges _adjacent_ duplicate words. .endif -.if ${:U1 2 2 3:u} != "1 2 3" -. warning The :u modifier must merge adjacent duplicate words. +.if ${1 2 2 3:L:u} != "1 2 3" +. warning The modifier ':u' must merge adjacent duplicate words. .endif -.if ${:U:u} != "" -. warning The :u modifier must do nothing with an empty word list. +.if ${:L:u} != "" +. warning The modifier ':u' must do nothing with an empty word list. .endif -.if ${:U :u} != "" +.if ${ :L: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. +.if ${word:L:u} != "word" +. warning The modifier ':u' must do nothing with a single-element word list. .endif -.if ${:U word :u} != "word" +.if ${ word :L: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. +.if ${1 1 1 1 1 1 1 1:L:u} != "1" +. warning The modifier ':u' must merge _all_ adjacent duplicate words. .endif -.if ${:U 1 2 1 1 :u} != "1 2 1" -. warning The :u modifier must normalize whitespace between the words. +.if ${ 1 2 1 1 :L:u} != "1 2 1" +. warning The modifier ':u' must normalize whitespace between the words. .endif -.if ${:U1 1 1 1 2:u} != "1 2" +.if ${1 1 1 1 2:L:u} != "1 2" . warning Duplicate words at the beginning must be merged. .endif -.if ${:U1 2 2 2 2:u} != "1 2" +.if ${1 2 2 2 2:L:u} != "1 2" . warning Duplicate words at the end must be merged. .endif all: - @:; diff --git a/unit-tests/varname-dot-make-save_dollars.mk b/unit-tests/varname-dot-make-save_dollars.mk index 97f37a646d2d..31f228c220b2 100644 --- a/unit-tests/varname-dot-make-save_dollars.mk +++ b/unit-tests/varname-dot-make-save_dollars.mk @@ -1,8 +1,130 @@ -# $NetBSD: varname-dot-make-save_dollars.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# $NetBSD: varname-dot-make-save_dollars.mk,v 1.7 2021/12/03 18:43:52 rillig Exp $ # -# Tests for the special .MAKE.SAVE_DOLLARS variable. +# Tests for the special .MAKE.SAVE_DOLLARS variable, which controls whether +# the assignment operator ':=' converts '$$' to a single '$' or keeps it +# as-is. +# +# See also: +# var-op-expand.mk for ':=' in general +# varmisc.mk for parsing the boolean values + +# Initially, the variable .MAKE.SAVE_DOLLARS is undefined. At this point the +# behavior of the assignment operator ':=' depends. NetBSD's usr.bin/make +# preserves the '$$' as-is, while the bmake distribution replaces '$$' with +# '$'. +.if ${.MAKE.SAVE_DOLLARS:Uundefined} != "undefined" +. error +.endif + + +# When dollars are preserved, this setting not only applies to literal +# dollars, but also to those that come indirectly from other expressions. +DOLLARS= $$$$$$$$ +.MAKE.SAVE_DOLLARS= yes +VAR:= ${DOLLARS} +# The reduction from 8 '$' to 4 '$' happens when ${VAR} is evaluated in the +# condition; .MAKE.SAVE_DOLLARS only applies at the moment where the +# assignment is performed using ':='. +.if ${VAR} != "\$\$\$\$" +. error +.endif + +# When dollars are preserved, this setting not only applies to literal +# dollars, but also to those that come indirectly from other expressions. +DOLLARS= $$$$$$$$ +.MAKE.SAVE_DOLLARS= no +VAR:= ${DOLLARS} +.if ${VAR} != "\$\$" +. error +.endif + +# The 'yes' preserves the dollars from the literal. +.MAKE.SAVE_DOLLARS= yes +VAR:= $$$$$$$$ +.if ${VAR} != "\$\$\$\$" +. error +.endif + +# The 'no' converts each '$$' to '$'. +.MAKE.SAVE_DOLLARS= no +VAR:= $$$$$$$$ +.if ${VAR} != "\$\$" +. error +.endif + +# It's even possible to change the dollar interpretation in the middle of +# evaluating an expression, but there is no practical need for it. +.MAKE.SAVE_DOLLARS= no +VAR:= $$$$-${.MAKE.SAVE_DOLLARS::=yes}-$$$$ +.if ${VAR} != "\$--\$\$" +. error +.endif + +# The '$' from the ':U' expressions do not appear as literal '$$' to the +# parser (no matter whether directly or indirectly), they only appear as '$$' +# in the value of an expression, therefore .MAKE.SAVE_DOLLARS doesn't apply +# here. +.MAKE.SAVE_DOLLARS= no +VAR:= ${:U\$\$\$\$}-${.MAKE.SAVE_DOLLARS::=yes}-${:U\$\$\$\$} +.if ${VAR} != "\$\$--\$\$" +. error +.endif + +# Undefining .MAKE.SAVE_DOLLARS does not have any effect, in particular it +# does not restore the default behavior. +.MAKE.SAVE_DOLLARS= no +.undef .MAKE.SAVE_DOLLARS +VAR:= $$$$$$$$ +.if ${VAR} != "\$\$" +. error +.endif + +# Undefining .MAKE.SAVE_DOLLARS does not have any effect, in particular it +# does not restore the default behavior. +.MAKE.SAVE_DOLLARS= yes +.undef .MAKE.SAVE_DOLLARS +VAR:= $$$$$$$$ +.if ${VAR} != "\$\$\$\$" +. error +.endif + +# The variable '.MAKE.SAVE_DOLLARS' not only affects literal '$$' on the +# right-hand side of the assignment operator ':=', it also affects dollars +# in indirect expressions. +# +# In this example, it affects the command in CMD itself, not the result of +# running that command. +.MAKE.SAVE_DOLLARS= no +CMD= echo '$$$$$$$$' +VAR:= ${CMD:sh} +.if ${VAR} != "\$\$" +. error +.endif + +.MAKE.SAVE_DOLLARS= yes +CMD= echo '$$$$$$$$' +VAR:= ${CMD:sh} +.if ${VAR} != "\$\$\$\$" +. error +.endif + + +# In the modifier ':@var@body@', .MAKE.SAVE_DOLLARS does not affect the body. +# In both cases, each '$$' is replaced with a single '$', no matter whether +# directly or indirectly via another expression. +.MAKE.SAVE_DOLLARS= no +DOLLARS= $$$$$$$$ +VAR:= ${word:L:@word@$$$$$$$$-${DOLLARS}@} +.if ${VAR} != "\$\$-\$\$" +. error +.endif + +.MAKE.SAVE_DOLLARS= yes +DOLLARS= $$$$$$$$ +VAR:= ${word:L:@word@$$$$$$$$-${DOLLARS}@} +.if ${VAR} != "\$\$-\$\$" +. error +.endif -# TODO: Implementation all: - @:; diff --git a/unit-tests/varname-dot-suffixes.exp b/unit-tests/varname-dot-suffixes.exp new file mode 100644 index 000000000000..753ce20d9fa8 --- /dev/null +++ b/unit-tests/varname-dot-suffixes.exp @@ -0,0 +1,39 @@ +Global:delete .SUFFIXES (not found) +Global: .MAKEFLAGS = -r -k -d v -d +Global: .MAKEFLAGS = -r -k -d v -d 0 +Global: .SUFFIXES = set ignored (read-only) +Global: .SUFFIXES = append ignored (read-only) +Global: _ = +Var_Parse: ${.SUFFIXES::=assign} (eval-keep-dollar-and-undefined) +Evaluating modifier ${.SUFFIXES::...} on value ".c .o .1 .err .tar.gz" (eval-keep-dollar-and-undefined, regular) +Modifier part: "assign" +Global: .SUFFIXES = assign ignored (read-only) +Result of ${.SUFFIXES::=assign} is "" (eval-keep-dollar-and-undefined, regular) +Global: _ = +Var_Parse: ${preserve:L:_=.SUFFIXES} (eval-keep-dollar-and-undefined) +Evaluating modifier ${preserve:L} on value "" (eval-keep-dollar-and-undefined, undefined) +Result of ${preserve:L} is "preserve" (eval-keep-dollar-and-undefined, defined) +Evaluating modifier ${preserve:_...} on value "preserve" (eval-keep-dollar-and-undefined, defined) +Global: .SUFFIXES = preserve ignored (read-only) +Result of ${preserve:_=.SUFFIXES} is "preserve" (eval-keep-dollar-and-undefined, defined) +Global: _ = preserve +Global: .MAKEFLAGS = -r -k -d v -d 0 -d v -d +Global: .MAKEFLAGS = -r -k -d v -d 0 -d v -d 0 +Var_Parse: ${1 2:L:@.SUFFIXES@${.SUFFIXES}@} != ".c .o .1 .err .tar.gz .c .o .1 .err .tar.gz" (eval-defined) +Evaluating modifier ${1 2:L} on value "" (eval-defined, undefined) +Result of ${1 2:L} is "1 2" (eval-defined, defined) +Evaluating modifier ${1 2:@...} on value "1 2" (eval-defined, defined) +Modifier part: ".SUFFIXES" +Modifier part: "${.SUFFIXES}" +ModifyWords: split "1 2" into 2 words +Command: .SUFFIXES = 1 ignored (read-only) +Var_Parse: ${.SUFFIXES} (eval-defined) +ModifyWord_Loop: in "1", replace ".SUFFIXES" with "${.SUFFIXES}" to ".c .o .1 .err .tar.gz" +Command: .SUFFIXES = 2 ignored (read-only) +Var_Parse: ${.SUFFIXES} (eval-defined) +ModifyWord_Loop: in "2", replace ".SUFFIXES" with "${.SUFFIXES}" to ".c .o .1 .err .tar.gz" +Command:delete .SUFFIXES (not found) +Result of ${1 2:@.SUFFIXES@${.SUFFIXES}@} is ".c .o .1 .err .tar.gz .c .o .1 .err .tar.gz" (eval-defined, defined) +Global: .MAKEFLAGS = -r -k -d v -d 0 -d v -d 0 -d v -d +Global: .MAKEFLAGS = -r -k -d v -d 0 -d v -d 0 -d v -d 0 +exit status 0 diff --git a/unit-tests/varname-dot-suffixes.mk b/unit-tests/varname-dot-suffixes.mk new file mode 100644 index 000000000000..de8034a172cc --- /dev/null +++ b/unit-tests/varname-dot-suffixes.mk @@ -0,0 +1,104 @@ +# $NetBSD: varname-dot-suffixes.mk,v 1.1 2021/12/12 22:16:48 rillig Exp $ +# +# Tests for the special "variable" .SUFFIXES, which lists the suffixes that +# have been registered for use in suffix transformation rules. Suffixes are +# listed even if there is no actual transformation rule that uses them. +# +# The name '.SUFFIXES' does not refer to a real variable, instead it can be +# used as a starting "variable name" for expressions like ${.SUFFIXES} or +# ${.SUFFIXES:M*o}. + +# In the beginning, there are no suffix rules, the expression is thus empty. +.if ${.SUFFIXES} != "" +.endif + +# There is no actual variable named '.SUFFIXES', it is all made up. +.if defined(.SUFFIXES) +. error +.endif + +# The suffixes list is still empty, and so is the "variable" '.SUFFIXES'. +.if !empty(.SUFFIXES) +. error +.endif + +.SUFFIXES: .c .o .1 .err + +# The suffixes are listed in declaration order. +.if ${.SUFFIXES} != ".c .o .1 .err" +. error +.endif + +# There is still no actual variable named '.SUFFIXES', it is all made up. +.if defined(.SUFFIXES) +. error +.endif + +# Now the suffixes list is not empty anymore. It may seem strange that there +# is no variable named '.SUFFIXES' but evaluating '${.SUFFIXES}' nevertheless +# returns something. For all practical use cases, it's good enough though. +.if empty(.SUFFIXES) +. error +.endif + +.SUFFIXES: .tar.gz + +# Changes to the suffixes list are reflected immediately. +.if ${.SUFFIXES} != ".c .o .1 .err .tar.gz" +. error +.endif + +# Deleting .SUFFIXES has no effect since there is no actual variable of that +# name. +.MAKEFLAGS: -dv +# expect: Global:delete .SUFFIXES (not found) +.undef .SUFFIXES +.MAKEFLAGS: -d0 +.if ${.SUFFIXES} != ".c .o .1 .err .tar.gz" +. error +.endif + +# The list of suffixes can only be modified using dependency declarations, any +# attempt at setting the variable named '.SUFFIXES' is rejected. +.MAKEFLAGS: -dv +# expect: Global: .SUFFIXES = set ignored (read-only) +.SUFFIXES= set +# expect: Global: .SUFFIXES = append ignored (read-only) +.SUFFIXES+= append +# expect: Global: .SUFFIXES = assign ignored (read-only) +_:= ${.SUFFIXES::=assign} +# expect: Command: .SUFFIXES = preserve ignored (read-only) +_:= ${preserve:L:_=.SUFFIXES} +.MAKEFLAGS: -d0 + +# Using the name '.SUFFIXES' in a .for loop looks strange because these +# variable names are typically in singular form, and .for loops do not use +# real variables either, they are made up as well, see directive-for.mk. The +# replacement mechanism for the iteration variables takes precedence. +.for .SUFFIXES in .c .o +. if ${.SUFFIXES} != ".c" && ${.SUFFIXES} != ".o" +. error +. endif +.endfor + +# After the .for loop, the expression '${.SUFFIXES}' refers to the list of +# suffixes again. +.if ${.SUFFIXES} != ".c .o .1 .err .tar.gz" +. error +.endif + +# Using the name '.SUFFIXES' in the modifier ':@var@body@' does not create an +# actual variable either. Like in the .for loop, choosing the name +# '.SUFFIXES' for the iteration variable is unusual. In ODE Make, the +# convention for these iteration variables is to have dots at both ends, so +# the name would be '.SUFFIXES.', furthermore the name of the iteration +# variable is typically in singular form. +.MAKEFLAGS: -dv +# expect: Command: .SUFFIXES = 1 ignored (read-only) +# expect: Command: .SUFFIXES = 2 ignored (read-only) +.if ${1 2:L:@.SUFFIXES@${.SUFFIXES}@} != ".c .o .1 .err .tar.gz .c .o .1 .err .tar.gz" +. error +.endif +.MAKEFLAGS: -d0 + +all: diff --git a/unit-tests/varname-empty.exp b/unit-tests/varname-empty.exp index ec225c6973c8..75a3f4151a8c 100644 --- a/unit-tests/varname-empty.exp +++ b/unit-tests/varname-empty.exp @@ -2,10 +2,6 @@ Var_SetExpand: variable name "${:U}" expands to empty string, with value "cmdlin 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 |