diff options
Diffstat (limited to 'contrib/bmake/unit-tests')
50 files changed, 837 insertions, 267 deletions
diff --git a/contrib/bmake/unit-tests/Makefile b/contrib/bmake/unit-tests/Makefile index f4d4425e204c..bcfe853642a5 100644 --- a/contrib/bmake/unit-tests/Makefile +++ b/contrib/bmake/unit-tests/Makefile @@ -1,6 +1,6 @@ -# $Id: Makefile,v 1.191 2023/01/24 06:09:49 sjg Exp $ +# $Id: Makefile,v 1.193 2023/02/25 20:03:25 sjg Exp $ # -# $NetBSD: Makefile,v 1.331 2023/01/24 00:24:02 sjg Exp $ +# $NetBSD: Makefile,v 1.333 2023/02/25 19:30:32 sjg Exp $ # # Unit tests for make(1) # @@ -32,6 +32,21 @@ .MAKE.OS?= ${uname -s:L:sh} .MAKE.UID?= ${id -u:L:sh} +# for many tests we need a TMPDIR that will not collide +# with other users. +.if ${.OBJDIR} != ${.CURDIR} +# easy +TMPDIR:= ${.OBJDIR}/tmp +.elif defined(TMPDIR) +TMPDIR:= ${TMPDIR}/uid${.MAKE.UID} +.else +TMPDIR:= /tmp/uid${.MAKE.UID} +.endif +# make sure it exists +.if !exist(${TMPDIR}) +_!= mkdir -p ${TMPDIR} +.endif + # Each test is in a sub-makefile. # Keep the list sorted. # Any test that is commented out must be ignored in @@ -406,12 +421,16 @@ TESTS+= varname-dot-make-makefiles TESTS+= varname-dot-make-meta-bailiwick TESTS+= varname-dot-make-meta-created TESTS+= varname-dot-make-meta-files +.if ${.MAKE.PATH_FILEMON:Uno:Nktrace:N/dev*} == "" && ${TMPDIR:N/tmp*:N/var/tmp*} != "" +# these tests will not work if TMPDIR is or is a subdir of +# /tmp or /var/tmp TESTS+= varname-dot-make-meta-ignore_filter TESTS+= varname-dot-make-meta-ignore_paths TESTS+= varname-dot-make-meta-ignore_patterns +TESTS+= varname-dot-make-path_filemon +.endif TESTS+= varname-dot-make-meta-prefix TESTS+= varname-dot-make-mode -TESTS+= varname-dot-make-path_filemon TESTS+= varname-dot-make-pid TESTS+= varname-dot-make-ppid TESTS+= varname-dot-make-save_dollars @@ -538,6 +557,9 @@ 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,' + +# meta line numbers can vary based on filemon implementation +SED_CMDS.meta-ignore= -e 's,\(\.meta:\) [1-9][0-9]*:,\1 <line>:,' 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} @@ -571,6 +593,9 @@ SED_CMDS.var-op-shell+= ${STD_SED_CMDS.white-space} SED_CMDS.vardebug+= -e 's,${.SHELL},</path/to/shell>,' SED_CMDS.varmod-subst-regex+= ${STD_SED_CMDS.regex} SED_CMDS.varparse-errors+= ${STD_SED_CMDS.timestamp} +SED_CMDS.varname-dot-make-meta-ignore_filter+= ${SED_CMDS.meta-ignore} +SED_CMDS.varname-dot-make-meta-ignore_paths+= ${SED_CMDS.meta-ignore} +SED_CMDS.varname-dot-make-meta-ignore_patterns+= ${SED_CMDS.meta-ignore} SED_CMDS.varname-dot-parsedir= -e '/in some cases/ s,^make: "[^"]*,make: "<normalized>,' SED_CMDS.varname-dot-parsefile= -e '/in some cases/ s,^make: "[^"]*,make: "<normalized>,' SED_CMDS.varname-dot-shell= -e 's, = /[^ ]*, = (details omitted),g' @@ -713,22 +738,6 @@ LANG= C _MKMSG_TEST= : .endif - -# for many tests we need a TMPDIR that will not collide -# with other users. -.if ${.OBJDIR} != ${.CURDIR} -# easy -TMPDIR:= ${.OBJDIR}/tmp -.elif defined(TMPDIR) -TMPDIR:= ${TMPDIR}/uid${.MAKE.UID} -.else -TMPDIR:= /tmp/uid${.MAKE.UID} -.endif -# make sure it exists -.if !exist(${TMPDIR}) -_!= mkdir -p ${TMPDIR} -.endif - # Some Linux systems such as Fedora have deprecated egrep in favor of grep -E. .if ${.MAKE.OS:NLinux} == "" EGREP= grep -E @@ -768,23 +777,23 @@ LIMIT_RESOURCES?= : # Postprocess the test output to make the output platform-independent. # -# always pretend .MAKE was called 'make' -_SED_CMDS+= -e 's,^${TEST_MAKE:T:S,.,\\.,g}[][0-9]*:,make:,' -_SED_CMDS+= -e 's,${TEST_MAKE:S,.,\\.,g},make,' -_SED_CMDS+= -e 's,^usage: ${TEST_MAKE:T:S,.,\\.,g} ,usage: make ,' -# replace anything after 'stopped in' with unit-tests +# Replace anything after 'stopped in' with unit-tests _SED_CMDS+= -e '/stopped/s, /.*, unit-tests,' # Allow the test files to be placed anywhere. _SED_CMDS+= -e 's,\(\.PARSEDIR}\) = `'"/[^']*'"',\1 = <some-dir>,' _SED_CMDS+= -e 's,\(\.INCLUDEDFROMDIR}\) = `'"/[^']*'"',\1 = <some-dir>,' -_SED_CMDS+= -e 's,${TMPDIR},<tmpdir>,g' +_SED_CMDS+= -e 's,${TMPDIR},<tmpdir>,g' -e 's,${TMPDIR:tA},<tmpdir>,g' # canonicalize ${.OBJDIR} and ${.CURDIR} +_SED_CMDS+= -e 's,${.CURDIR},<curdir>,g' .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' +_SED_CMDS+= -e 's,${.OBJDIR},<curdir>,g' -e 's,${.OBJDIR:tA},<curdir>,g' .endif -_SED_CMDS+= -e 's,${.CURDIR},<curdir>,g' +# always pretend .MAKE was called 'make' +_SED_CMDS+= -e 's,^${TEST_MAKE:T:S,.,\\.,g}[][0-9]*:,make:,' +_SED_CMDS+= -e 's,${TEST_MAKE:S,.,\\.,g},make,' +_SED_CMDS+= -e 's,^usage: ${TEST_MAKE:T:S,.,\\.,g} ,usage: make ,' _SED_CMDS+= -e 's,<curdir>/,,g' _SED_CMDS+= -e 's,${UNIT_TESTS:S,.,\\.,g}/,,g' _SED_CMDS+= -e '/MAKE_VERSION/d' diff --git a/contrib/bmake/unit-tests/cmd-interrupt.exp b/contrib/bmake/unit-tests/cmd-interrupt.exp index 91f4439e7bea..242db1d9507d 100755 --- a/contrib/bmake/unit-tests/cmd-interrupt.exp +++ b/contrib/bmake/unit-tests/cmd-interrupt.exp @@ -2,7 +2,6 @@ make: *** cmd-interrupt-ordinary removed interrupt-ordinary: ok > cmd-interrupt-phony -make: *** cmd-interrupt-phony removed interrupt-phony: ok > cmd-interrupt-precious interrupt-precious: ok diff --git a/contrib/bmake/unit-tests/cmd-interrupt.mk b/contrib/bmake/unit-tests/cmd-interrupt.mk index fa0d85fc9063..140651b55c62 100755 --- a/contrib/bmake/unit-tests/cmd-interrupt.mk +++ b/contrib/bmake/unit-tests/cmd-interrupt.mk @@ -1,4 +1,4 @@ -# $NetBSD: cmd-interrupt.mk,v 1.3 2020/11/15 14:07:53 rillig Exp $ +# $NetBSD: cmd-interrupt.mk,v 1.4 2023/03/18 22:20:12 sjg Exp $ # # Tests for interrupting a command. # @@ -30,7 +30,7 @@ interrupt-ordinary: interrupt-phony: .PHONY @${.MAKE} ${MAKEFLAGS} -f ${MAKEFILE} cmd-interrupt-phony || true # The ././ is necessary to work around the file cache. - @echo ${.TARGET}: ${exists(././cmd-interrupt-phony) :? error : ok } + @echo ${.TARGET}: ${exists(././cmd-interrupt-phony) :? ok : error } interrupt-precious: .PRECIOUS @${.MAKE} ${MAKEFLAGS} -f ${MAKEFILE} cmd-interrupt-precious || true diff --git a/contrib/bmake/unit-tests/cond-cmp-numeric.exp b/contrib/bmake/unit-tests/cond-cmp-numeric.exp index c03ecd1b311e..69a8a1e4fca0 100644 --- a/contrib/bmake/unit-tests/cond-cmp-numeric.exp +++ b/contrib/bmake/unit-tests/cond-cmp-numeric.exp @@ -1,15 +1,15 @@ CondParser_Eval: !(${:UINF} > 1e100) -make: "cond-cmp-numeric.mk" line 11: Comparison with '>' requires both operands 'INF' and '1e100' to be numeric +make: "cond-cmp-numeric.mk" line 15: Comparison with '>' requires both operands 'INF' and '1e100' to be numeric CondParser_Eval: ${:UNaN} > NaN -make: "cond-cmp-numeric.mk" line 16: Comparison with '>' requires both operands 'NaN' and 'NaN' to be numeric +make: "cond-cmp-numeric.mk" line 21: Comparison with '>' requires both operands 'NaN' and 'NaN' to be numeric CondParser_Eval: !(${:UNaN} == NaN) Comparing "NaN" == "NaN" CondParser_Eval: 123 ! 123 -make: "cond-cmp-numeric.mk" line 34: Malformed conditional (123 ! 123) +make: "cond-cmp-numeric.mk" line 38: Malformed conditional (123 ! 123) CondParser_Eval: ${:U 123} < 124 Comparing 123.000000 < 124.000000 CondParser_Eval: ${:U123 } < 124 -make: "cond-cmp-numeric.mk" line 50: Comparison with '<' requires both operands '123 ' and '124' to be numeric +make: "cond-cmp-numeric.mk" line 54: Comparison with '<' requires both operands '123 ' and '124' to be numeric make: Fatal errors encountered -- cannot continue make: stopped in unit-tests exit status 1 diff --git a/contrib/bmake/unit-tests/cond-cmp-numeric.mk b/contrib/bmake/unit-tests/cond-cmp-numeric.mk index 5386e4a97297..e025b99b27cd 100644 --- a/contrib/bmake/unit-tests/cond-cmp-numeric.mk +++ b/contrib/bmake/unit-tests/cond-cmp-numeric.mk @@ -1,6 +1,9 @@ -# $NetBSD: cond-cmp-numeric.mk,v 1.6 2022/09/04 22:55:00 rillig Exp $ +# $NetBSD: cond-cmp-numeric.mk,v 1.7 2023/03/04 08:07:29 rillig Exp $ # # Tests for numeric comparisons in .if conditions. +# +# See also: +# cond-token-number.mk .MAKEFLAGS: -dc @@ -8,11 +11,13 @@ # Even if strtod(3) parses "INF" as +Infinity, make does not accept this # since it is not really a number; see TryParseNumber. +# expect+1: Comparison with '>' requires both operands 'INF' and '1e100' to be numeric .if !(${:UINF} > 1e100) . error .endif # Neither is NaN a number; see TryParseNumber. +# expect+1: Comparison with '>' requires both operands 'NaN' and 'NaN' to be numeric .if ${:UNaN} > NaN . error .endif @@ -29,8 +34,7 @@ # whether the operator is valid, leaving the rest of the work to the # evaluation functions EvalCompareNum and EvalCompareStr. Ensure that this # parse error is properly reported. -# -# XXX: The warning message does not mention the actual operator. +# expect+1: Malformed conditional (123 ! 123) .if 123 ! 123 . error .else @@ -54,4 +58,3 @@ .endif all: - @:; diff --git a/contrib/bmake/unit-tests/cond-cmp-string.exp b/contrib/bmake/unit-tests/cond-cmp-string.exp index c9c7a0777383..e0aabfdadca4 100644 --- a/contrib/bmake/unit-tests/cond-cmp-string.exp +++ b/contrib/bmake/unit-tests/cond-cmp-string.exp @@ -1,11 +1,11 @@ -make: "cond-cmp-string.mk" line 18: Malformed conditional (str != str) -make: "cond-cmp-string.mk" line 42: Malformed conditional ("string" != "str""ing") -make: "cond-cmp-string.mk" line 49: Malformed conditional (!("value" = "value")) -make: "cond-cmp-string.mk" line 56: Malformed conditional (!("value" === "value")) -make: "cond-cmp-string.mk" line 113: Comparison with '<' requires both operands 'string' and 'string' to be numeric -make: "cond-cmp-string.mk" line 120: Comparison with '<=' requires both operands 'string' and 'string' to be numeric -make: "cond-cmp-string.mk" line 127: Comparison with '>' requires both operands 'string' and 'string' to be numeric -make: "cond-cmp-string.mk" line 134: Comparison with '>=' requires both operands 'string' and 'string' to be numeric +make: "cond-cmp-string.mk" line 19: Malformed conditional (str != str) +make: "cond-cmp-string.mk" line 44: Malformed conditional ("string" != "str""ing") +make: "cond-cmp-string.mk" line 52: Malformed conditional (!("value" = "value")) +make: "cond-cmp-string.mk" line 60: Malformed conditional (!("value" === "value")) +make: "cond-cmp-string.mk" line 118: Comparison with '<' requires both operands 'string' and 'string' to be numeric +make: "cond-cmp-string.mk" line 126: Comparison with '<=' requires both operands 'string' and 'string' to be numeric +make: "cond-cmp-string.mk" line 134: Comparison with '>' requires both operands 'string' and 'string' to be numeric +make: "cond-cmp-string.mk" line 142: Comparison with '>=' requires both operands 'string' and 'string' to be numeric make: Fatal errors encountered -- cannot continue make: stopped in unit-tests exit status 1 diff --git a/contrib/bmake/unit-tests/cond-cmp-string.mk b/contrib/bmake/unit-tests/cond-cmp-string.mk index 8b504ba0c0d6..44d8beceacdd 100644 --- a/contrib/bmake/unit-tests/cond-cmp-string.mk +++ b/contrib/bmake/unit-tests/cond-cmp-string.mk @@ -1,4 +1,4 @@ -# $NetBSD: cond-cmp-string.mk,v 1.16 2022/05/08 06:51:27 rillig Exp $ +# $NetBSD: cond-cmp-string.mk,v 1.17 2023/03/28 14:38:29 rillig Exp $ # # Tests for string comparisons in .if conditions. @@ -15,6 +15,7 @@ # The left-hand side of the comparison must be enclosed in quotes. # This one is not enclosed in quotes and thus generates an error message. +# expect+1: Malformed conditional (str != str) .if str != str . error .endif @@ -39,6 +40,7 @@ # It is not possible to concatenate two string literals to form a single # string. In C, Python and the shell this is possible, but not in make. +# expect+1: Malformed conditional ("string" != "str""ing") .if "string" != "str""ing" . error .else @@ -46,6 +48,7 @@ .endif # There is no = operator for strings. +# expect+1: Malformed conditional (!("value" = "value")) .if !("value" = "value") . error .else @@ -53,6 +56,7 @@ .endif # There is no === operator for strings either. +# expect+1: Malformed conditional (!("value" === "value")) .if !("value" === "value") . error .else @@ -110,6 +114,7 @@ .endif # Strings cannot be compared relationally, only for equality. +# expect+1: Comparison with '<' requires both operands 'string' and 'string' to be numeric .if "string" < "string" . error .else @@ -117,6 +122,7 @@ .endif # Strings cannot be compared relationally, only for equality. +# expect+1: Comparison with '<=' requires both operands 'string' and 'string' to be numeric .if "string" <= "string" . error .else @@ -124,6 +130,7 @@ .endif # Strings cannot be compared relationally, only for equality. +# expect+1: Comparison with '>' requires both operands 'string' and 'string' to be numeric .if "string" > "string" . error .else @@ -131,6 +138,7 @@ .endif # Strings cannot be compared relationally, only for equality. +# expect+1: Comparison with '>=' requires both operands 'string' and 'string' to be numeric .if "string" >= "string" . error .else diff --git a/contrib/bmake/unit-tests/cond-func-empty.exp b/contrib/bmake/unit-tests/cond-func-empty.exp index d1dfda7c03ee..35d83adad5cb 100644 --- a/contrib/bmake/unit-tests/cond-func-empty.exp +++ b/contrib/bmake/unit-tests/cond-func-empty.exp @@ -1,5 +1,5 @@ -make: "cond-func-empty.mk" line 149: Unclosed variable "WORD" -make: "cond-func-empty.mk" line 149: Malformed conditional (empty(WORD) +make: "cond-func-empty.mk" line 153: Unclosed variable "WORD" +make: "cond-func-empty.mk" line 153: Malformed conditional (empty(WORD) make: Fatal errors encountered -- cannot continue make: stopped in unit-tests exit status 1 diff --git a/contrib/bmake/unit-tests/cond-func-empty.mk b/contrib/bmake/unit-tests/cond-func-empty.mk index 24cb7a680b2a..293305281298 100644 --- a/contrib/bmake/unit-tests/cond-func-empty.mk +++ b/contrib/bmake/unit-tests/cond-func-empty.mk @@ -1,4 +1,4 @@ -# $NetBSD: cond-func-empty.mk,v 1.17 2021/12/28 22:13:56 rillig Exp $ +# $NetBSD: cond-func-empty.mk,v 1.18 2023/03/04 21:15:30 rillig Exp $ # # Tests for the empty() function in .if conditions, which tests a variable # expression for emptiness. @@ -24,11 +24,13 @@ WORD= word . error .endif -# The :S modifier replaces the empty value with an actual word. The -# 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. +# The :S modifier replaces the empty value with an actual word. After +# applying the :S modifier to the expression, it value is 'empty', so it is +# no longer empty, but it is still based on an undefined variable. There are +# a few modifiers that turn an undefined expression into a defined expression, +# among them :U and :D, but not :S. Therefore, at the end of evaluating the +# expression, the expression is still undefined, so its final value becomes an +# empty string. # # XXX: This is hard to explain to someone who doesn't know these # implementation details. @@ -45,15 +47,17 @@ WORD= word . error .endif -# And now to the surprising part. Applying the following :S modifier to 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 +# When an expression is based on an undefined variable, its modifiers interact +# in sometimes surprising ways. Applying the :S modifier to the undefined +# expression makes its value non-empty, but doesn't change that the expression +# is based on an undefined variable. The :U modifier that follows only looks +# at the definedness state 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 -# the :U modifier is used in this expression. +# Since the variable was undefined to begin with, the fallback value from the +# :U modifier is used in this expression, instead of keeping the 'value' from +# the :S modifier. # .if ${UNDEF:S,^$,value,W:Ufallback} != "fallback" . error @@ -90,8 +94,8 @@ WORD= word # neither leading nor trailing spaces are trimmed in the argument of the # function. If the spaces were trimmed, the variable name would be "" and # that variable is indeed undefined. Since CondParser_FuncCallEmpty calls -# Var_Parse without VARE_UNDEFERR, the value of the undefined variable is -# returned as an empty string. +# Var_Parse without VARE_UNDEFERR, the value of the undefined variable "" +# would be returned as an empty string. ${:U }= space .if empty( ) . error @@ -120,9 +124,9 @@ ${:U }= space . error .endif -# Ensure that variable expressions that appear as part of the argument are -# properly parsed. Typical use cases for this are .for loops, which are -# expanded to exactly these ${:U} expressions. +# Ensure that variable expressions that appear as part of the function call +# argument are properly parsed. Typical use cases for this are .for loops, +# which are expanded to exactly these ${:U} expressions. # # 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' @@ -159,10 +163,9 @@ ${:U WORD }= variable name with spaces # 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. +# they just allowed undefined variables to be used in the conditions. These +# unnecessary evaluations were fixed in several commits, starting with var.c +# 1.226 from 2020-07-02. # # 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 diff --git a/contrib/bmake/unit-tests/cond-short.exp b/contrib/bmake/unit-tests/cond-short.exp index 2865dcb6ef33..745d7e912c2b 100644 --- a/contrib/bmake/unit-tests/cond-short.exp +++ b/contrib/bmake/unit-tests/cond-short.exp @@ -7,10 +7,7 @@ expected M pattern expected or expected or exists expected or empty -defined(V42) && ${V42} > 0: Ok -defined(V66) && ( "${iV2}" < ${V42} ): Ok -1 || ${iV1} < ${V42}: Ok -1 || ${iV2:U2} < ${V42}: Ok -0 || ${iV1} <= ${V42}: Ok -0 || ${iV2:U2} < ${V42}: Ok -exit status 0 +make: "cond-short.mk" line 214: Comparison with '<' requires both operands '' and '42' to be numeric +make: Fatal errors encountered -- cannot continue +make: stopped in unit-tests +exit status 1 diff --git a/contrib/bmake/unit-tests/cond-short.mk b/contrib/bmake/unit-tests/cond-short.mk index f4e8f87043b5..525ff7d0f2ab 100644 --- a/contrib/bmake/unit-tests/cond-short.mk +++ b/contrib/bmake/unit-tests/cond-short.mk @@ -1,4 +1,4 @@ -# $NetBSD: cond-short.mk,v 1.19 2021/12/27 18:54:19 rillig Exp $ +# $NetBSD: cond-short.mk,v 1.20 2023/03/04 13:42:36 rillig Exp $ # # Demonstrates that in conditions, the right-hand side of an && or || # is only evaluated if it can actually influence the result. @@ -135,28 +135,32 @@ VAR= # empty again, for the following tests .elif ${echo "unexpected nested or" 1>&2 :L:sh} .endif -# make sure these do not cause complaint -#.MAKEFLAGS: -dc -# TODO: Rewrite this whole section and check all the conditions and variables. -# Several of the assumptions are probably wrong here. -# TODO: replace 'x=' with '.info' or '.error'. -V42= 42 -iV1= ${V42} -iV2= ${V66} +NUMBER= 42 +INDIR_NUMBER= ${NUMBER} +INDIR_UNDEF= ${UNDEF} -.if defined(V42) && ${V42} > 0 -x= Ok +.if defined(NUMBER) && ${NUMBER} > 0 .else -x= Fail +. error .endif -x!= echo 'defined(V42) && $${V42} > 0: $x' >&2; echo -# With cond.c 1.76 from 2020-07-03, the following condition triggered a -# warning: "String comparison operator should be either == or !=". -# This was because the variable expression ${iV2} was defined, but the -# contained variable V66 was undefined. The left-hand side of the comparison -# therefore evaluated to the string "${V66}", which is obviously not a number. +# Starting with var.c 1.226 from from 2020-07-02, the following condition +# triggered a warning: "String comparison operator should be either == or !=". +# +# The left-hand side of the '&&' evaluated to false, which should have made +# the right-hand side irrelevant. +# +# On the right-hand side of the '&&', the expression ${INDIR_UNDEF} was +# defined and had the value '${UNDEF}', but the nested variable UNDEF was +# undefined. The right hand side "${INDIR_UNDEF}" still needed to be parsed, +# and in parse-only mode, the "value" of the parsed expression was the +# uninterpreted variable value, in this case '${UNDEF}'. And even though the +# right hand side of the '&&' should have been irrelevant, the two sides of +# the comparison were still parsed and evaluated. Comparing these two values +# numerically was not possible since the string '${UNDEF}' is not a number, +# so the comparison fell back to string comparison, which then complained +# about the '>' operator. # # This was fixed in cond.c 1.79 from 2020-07-09 by not evaluating irrelevant # comparisons. Instead, they are only parsed and then discarded. @@ -164,59 +168,77 @@ x!= echo 'defined(V42) && $${V42} > 0: $x' >&2; echo # At that time, there was not enough debug logging to see the details in the # -dA log. To actually see it, add debug logging at the beginning and end of # Var_Parse. -.if defined(V66) && ( ${iV2} < ${V42} ) -x= Fail -.else -x= Ok +.if defined(UNDEF) && ${INDIR_UNDEF} < ${NUMBER} +. error +.endif +# Adding a ':U' modifier to the irrelevant expression didn't help, as that +# expression was only parsed, not evaluated. The resulting literal string +# '${INDIR_UNDEF:U2}' was not numeric either, for the same reason as above. +.if defined(UNDEF) && ${INDIR_UNDEF:U2} < ${NUMBER} +. error .endif -# XXX: This condition doesn't match the one above. The quotes are missing -# above. This is a crucial detail since without quotes, the variable -# expression ${iV2} evaluates to "${V66}", and with quotes, it evaluates to "" -# since undefined variables are allowed and expand to an empty string. -x!= echo 'defined(V66) && ( "$${iV2}" < $${V42} ): $x' >&2; echo -.if 1 || ${iV1} < ${V42} -x= Ok -.else -x= Fail +# Enclosing the expression in double quotes changes how that expression is +# evaluated. In irrelevant expressions that are enclosed in double quotes, +# expressions based on undefined variables are allowed and evaluate to an +# empty string. +# +# The manual page stated from at least 1993 on that irrelevant conditions were +# not evaluated, but that was wrong. These conditions were evaluated, the +# only difference was that undefined variables in them didn't trigger an +# error. Since numeric conditions are quite rare, this subtle difference +# didn't catch much attention, as most other conditions such as pattern +# matches or equality comparisons worked fine and never produced error +# messages. +.if defined(UNDEF) && "${INDIR_UNDEF}" < ${NUMBER} +. error .endif -x!= echo '1 || $${iV1} < $${V42}: $x' >&2; echo -# With cond.c 1.76 from 2020-07-03, the following condition triggered a -# warning: "String comparison operator should be either == or !=". -# This was because the variable expression ${iV2} was defined, but the -# contained variable V66 was undefined. The left-hand side of the comparison -# therefore evaluated to the string "${V66}", which is obviously not a number. +# Since the condition is relevant, the indirect undefined variable is +# evaluated as usual, resolving nested undefined expressions to an empty +# string. # -# This was fixed in cond.c 1.79 from 2020-07-09 by not evaluating irrelevant -# comparisons. Instead, they are only parsed and then discarded. +# Comparing an empty string numerically is not possible, however, make has an +# ugly hack in TryParseNumber that treats an empty string as a valid numerical +# value, thus hiding bugs in the makefile. +.if ${INDIR_UNDEF} < ${NUMBER} +# only due to the ugly hack +.else +. error +.endif + +# Due to the quotes around the left-hand side of the '<', the operand is +# marked as a string, thus preventing a numerical comparison. # -# At that time, there was not enough debug logging to see the details in the -# -dA log. To actually see it, add debug logging at the beginning and end of -# Var_Parse. -.if 1 || ${iV2:U2} < ${V42} -x= Ok +# expect+1: Comparison with '<' requires both operands '' and '42' to be numeric +.if "${INDIR_UNDEF}" < ${NUMBER} +. info yes .else -x= Fail +. info no .endif -x!= echo '1 || $${iV2:U2} < $${V42}: $x' >&2; echo -# the same expressions are fine when the lhs is expanded -# ${iV1} expands to 42 -.if 0 || ${iV1} <= ${V42} -x= Ok +# The right-hand side of '||' is irrelevant and thus not evaluated. +.if 1 || ${INDIR_NUMBER} < ${NUMBER} .else -x= Fail +. error +.endif + +# The right-hand side of '||' is relevant and thus evaluated normally. +.if 0 || ${INDIR_NUMBER} < ${NUMBER} +. error .endif -x!= echo '0 || $${iV1} <= $${V42}: $x' >&2; echo -# ${iV2:U2} expands to 2 -.if 0 || ${iV2:U2} < ${V42} -x= Ok +# The right-hand side of '||' evaluates to an empty string, as the variable +# 'INDIR_UNDEF' is defined, therefore the modifier ':U2' has no effect. +# Comparing an empty string numerically is not possible, however, make has an +# ugly hack in TryParseNumber that treats an empty string as a valid numerical +# value, thus hiding bugs in the makefile. +.if 0 || ${INDIR_UNDEF:U2} < ${NUMBER} +# only due to the ugly hack .else -x= Fail +. error .endif -x!= echo '0 || $${iV2:U2} < $${V42}: $x' >&2; echo + # The right-hand side of the '&&' is irrelevant since the left-hand side # already evaluates to false. Before cond.c 1.79 from 2020-07-09, it was @@ -229,8 +251,8 @@ x!= echo '0 || $${iV2:U2} < $${V42}: $x' >&2; echo # 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). +# evaluated to true (see CondParser_FuncCall and CondParser_FuncCallEmpty), an +# irrelevant comparison evaluated 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, diff --git a/contrib/bmake/unit-tests/cond-token-number.exp b/contrib/bmake/unit-tests/cond-token-number.exp index f078cb007323..1d472b63bc77 100644 --- a/contrib/bmake/unit-tests/cond-token-number.exp +++ b/contrib/bmake/unit-tests/cond-token-number.exp @@ -2,7 +2,6 @@ make: "cond-token-number.mk" line 15: Malformed conditional (-0) make: "cond-token-number.mk" line 25: Malformed conditional (+0) make: "cond-token-number.mk" line 35: Malformed conditional (!-1) make: "cond-token-number.mk" line 45: Malformed conditional (!+1) -make: "cond-token-number.mk" line 89: End of the tests. make: Fatal errors encountered -- cannot continue make: stopped in unit-tests exit status 1 diff --git a/contrib/bmake/unit-tests/cond-token-number.mk b/contrib/bmake/unit-tests/cond-token-number.mk index eef528f4b7c6..d1dd7371f447 100644 --- a/contrib/bmake/unit-tests/cond-token-number.mk +++ b/contrib/bmake/unit-tests/cond-token-number.mk @@ -1,4 +1,4 @@ -# $NetBSD: cond-token-number.mk,v 1.7 2022/01/02 02:57:39 rillig Exp $ +# $NetBSD: cond-token-number.mk,v 1.8 2023/03/04 08:07:29 rillig Exp $ # # Tests for number tokens in .if conditions. # @@ -85,7 +85,21 @@ HEX= dead . error .endif -# Ensure that parsing continues until here. -.info End of the tests. +# Very small numbers round to 0. +.if 12345e-400 +. error +.endif +.if 12345e-200 +.else +. error +.endif + +# Very large numbers round up to infinity on IEEE 754 implementations, or to +# the largest representable number (VAX); in particular, make does not fall +# back to checking whether a variable of that name is defined. +.if 12345e400 +.else +. error +.endif -all: # nothing +all: diff --git a/contrib/bmake/unit-tests/cond-token-plain.exp b/contrib/bmake/unit-tests/cond-token-plain.exp index a508bf62a5ea..572e8b3b2c9f 100644 --- a/contrib/bmake/unit-tests/cond-token-plain.exp +++ b/contrib/bmake/unit-tests/cond-token-plain.exp @@ -49,6 +49,7 @@ make: "cond-token-plain.mk" line 172: Now the variable '\\' is defined. CondParser_Eval: "unquoted\"quoted" != unquoted"quoted Comparing "unquoted"quoted" != "unquoted"quoted" CondParser_Eval: $$$$$$$$ != "" +make: "cond-token-plain.mk" line 186: Malformed conditional ($$$$$$$$ != "") CondParser_Eval: left == right make: "cond-token-plain.mk" line 195: Malformed conditional (left == right) CondParser_Eval: ${0:?:} || left == right diff --git a/contrib/bmake/unit-tests/cond-token-plain.mk b/contrib/bmake/unit-tests/cond-token-plain.mk index 5fb4a72b74a5..54bdd4fbf626 100644 --- a/contrib/bmake/unit-tests/cond-token-plain.mk +++ b/contrib/bmake/unit-tests/cond-token-plain.mk @@ -1,4 +1,4 @@ -# $NetBSD: cond-token-plain.mk,v 1.16 2022/09/25 12:51:37 rillig Exp $ +# $NetBSD: cond-token-plain.mk,v 1.17 2023/02/14 20:49:09 rillig Exp $ # # Tests for plain tokens (that is, string literals without quotes) # in .if conditions. These are also called bare words. @@ -89,7 +89,7 @@ # a coincidence that the '!' is both used in the '!=' comparison operator # as well as for negating a comparison result. # -# The boolean operators '&' and '|' don't terminate a comparison operand. +# The characters '&' and '|' are part of the comparison operand. .if ${:Uvar}&&name != "var&&name" . error .endif @@ -97,8 +97,8 @@ . error .endif -# A bare word may appear alone in a condition, without any comparison -# operator. It is implicitly converted into defined(bare). +# A bare word may occur alone in a condition, without any comparison +# operator. It is interpreted as the function call 'defined(bare)'. .if bare . error .else diff --git a/contrib/bmake/unit-tests/cond-undef-lint.exp b/contrib/bmake/unit-tests/cond-undef-lint.exp index 2c4feb0376ff..55073b2116c6 100755 --- a/contrib/bmake/unit-tests/cond-undef-lint.exp +++ b/contrib/bmake/unit-tests/cond-undef-lint.exp @@ -1,7 +1,10 @@ make: "cond-undef-lint.mk" line 23: Variable "UNDEF" is undefined +make: "cond-undef-lint.mk" line 23: Malformed conditional (${UNDEF}) make: "cond-undef-lint.mk" line 38: Variable "UNDEF" is undefined make: "cond-undef-lint.mk" line 38: Variable "VAR." is undefined +make: "cond-undef-lint.mk" line 38: Malformed conditional (${VAR.${UNDEF}}) make: "cond-undef-lint.mk" line 49: Variable "VAR.defined" is undefined +make: "cond-undef-lint.mk" line 49: Malformed conditional (${VAR.${DEF}}) make: Fatal errors encountered -- cannot continue make: stopped in unit-tests exit status 1 diff --git a/contrib/bmake/unit-tests/dep-var.exp b/contrib/bmake/unit-tests/dep-var.exp index d32aca455ceb..4e38057bf6b9 100755 --- a/contrib/bmake/unit-tests/dep-var.exp +++ b/contrib/bmake/unit-tests/dep-var.exp @@ -1,3 +1,27 @@ +Var_Parse: ${UNDEF1} (eval-defined) +Global: .ALLTARGETS = all +Global: .ALLTARGETS = all ${DEF2} +Global: .ALLTARGETS = all ${DEF2} a-${DEF2}-b +Global: .ALLTARGETS = all ${DEF2} a-${DEF2}-b ${UNDEF3} +Global: .ALLTARGETS = all ${DEF2} a-${DEF2}-b ${UNDEF3} 1-${INDIRECT_1}-1 +Global: INDIRECT_1 = 2-$${INDIRECT_2}-2 +Global: INDIRECT_2 = 3-$${INDIRECT_3}-3 +Global: INDIRECT_3 = indirect +Global: UNDEF1 = undef1 +Global: DEF2 = def2 +Global: .ALLTARGETS = all ${DEF2} a-${DEF2}-b ${UNDEF3} 1-${INDIRECT_1}-1 $$) +Var_Parse: ${:U\$)}: (eval-defined) +Evaluating modifier ${:U...} on value "" (eval-defined, undefined) +Result of ${:U\$)} is "$)" (eval-defined, defined) +Global: .ALLTARGETS = all ${DEF2} a-${DEF2}-b ${UNDEF3} 1-${INDIRECT_1}-1 $$) undef1 +Global: .ALLTARGETS = all ${DEF2} a-${DEF2}-b ${UNDEF3} 1-${INDIRECT_1}-1 $$) undef1 def2 +Global: .ALLTARGETS = all ${DEF2} a-${DEF2}-b ${UNDEF3} 1-${INDIRECT_1}-1 $$) undef1 def2 a-def2-b +Var_Parse: $INDIRECT_2-2-1 $): (parse-only) +Global: .ALLTARGETS = all ${DEF2} a-${DEF2}-b ${UNDEF3} 1-${INDIRECT_1}-1 $$) undef1 def2 a-def2-b 1-2-$INDIRECT_2-2-1 +Var_Parse: $): (parse-only) +Global: .ALLTARGETS = all ${DEF2} a-${DEF2}-b ${UNDEF3} 1-${INDIRECT_1}-1 $$) undef1 def2 a-def2-b 1-2-$INDIRECT_2-2-1 $) +Global: .MAKEFLAGS = -r -k -d v -d +Global: .MAKEFLAGS = -r -k -d v -d 0 make: Malformed variable expression at "$)" def2 a-def2-b diff --git a/contrib/bmake/unit-tests/dep-var.mk b/contrib/bmake/unit-tests/dep-var.mk index 4503424e31ab..f4a724f0ce7c 100755 --- a/contrib/bmake/unit-tests/dep-var.mk +++ b/contrib/bmake/unit-tests/dep-var.mk @@ -1,14 +1,16 @@ -# $NetBSD: dep-var.mk,v 1.6 2021/04/04 10:13:09 rillig Exp $ +# $NetBSD: dep-var.mk,v 1.7 2023/02/13 21:01:46 rillig Exp $ # # Tests for variable references in dependency declarations. # # Uh oh, this feels so strange that probably nobody uses it. But it seems to # be the only way to reach the lower half of SuffExpandChildren. -# XXX: The -dv log says: -# Var_Parse: ${UNDEF1} with VARE_UNDEFERR|VARE_WANTRES -# but no error message is generated for this line. -# The variable expression ${UNDEF1} simply expands to an empty string. +.MAKEFLAGS: -dv + +# expect: Var_Parse: ${UNDEF1} (eval-defined) +# Even though undefined expressions should lead to errors, no error message is +# generated for this line. The variable expression ${UNDEF1} simply expands +# to an empty string. all: ${UNDEF1} # Using a double dollar in order to circumvent immediate variable expansion @@ -20,8 +22,8 @@ all: ${UNDEF1} all: $${DEF2} a-$${DEF2}-b # This variable is not defined at all. -# XXX: The -dv log says: -# Var_Parse: ${UNDEF3} with VARE_UNDEFERR|VARE_WANTRES +# XXX: The -dv log says later when expanding the sources of 'all': +# Var_Parse: ${UNDEF3} (eval-defined) # but no error message is generated for this line, just like for UNDEF1. # The variable expression ${UNDEF3} simply expands to an empty string. all: $${UNDEF3} @@ -81,8 +83,13 @@ all: $$$$) # Since 2020-09-13, this generates a parse error in lint mode (-dL), but not # in normal mode since ParseDependency does not handle any errors after # calling Var_Parse. +# expect: Var_Parse: ${:U\$)}: (eval-defined) +# expect: Var_Parse: $INDIRECT_2-2-1 $): (parse-only) +# expect: Var_Parse: $): (parse-only) undef1 def2 a-def2-b 1-2-$$INDIRECT_2-2-1 ${:U\$)}: @echo ${.TARGET:Q} +.MAKEFLAGS: -d0 + # XXX: Why is the exit status still 0, even though Parse_Error is called # with PARSE_FATAL in SuffExpandChildren? diff --git a/contrib/bmake/unit-tests/deptgt-delete_on_error.exp b/contrib/bmake/unit-tests/deptgt-delete_on_error.exp index 9d9f1dc3e5ec..75fbbe12472f 100644 --- a/contrib/bmake/unit-tests/deptgt-delete_on_error.exp +++ b/contrib/bmake/unit-tests/deptgt-delete_on_error.exp @@ -7,10 +7,8 @@ make: *** deptgt-delete_on_error-regular removed make: *** deptgt-delete_on_error-regular-delete removed > deptgt-delete_on_error-phony; false *** Error code 1 (continuing) -make: *** deptgt-delete_on_error-phony removed > deptgt-delete_on_error-phony-delete; false *** Error code 1 (continuing) -make: *** deptgt-delete_on_error-phony-delete removed > deptgt-delete_on_error-precious; false *** Error code 1 (continuing) > deptgt-delete_on_error-precious-delete; false diff --git a/contrib/bmake/unit-tests/meta-ignore.inc b/contrib/bmake/unit-tests/meta-ignore.inc new file mode 100644 index 000000000000..ed74f4d79017 --- /dev/null +++ b/contrib/bmake/unit-tests/meta-ignore.inc @@ -0,0 +1,63 @@ +# $NetBSD: meta-ignore.inc,v 1.2 2023/02/25 19:30:32 sjg Exp $ + +# common logic for testing .MAKE.META.IGNORE_* + +# we want a directory outside of .OBJDIR to drop a file +# that our meta file refers to. +# Note: these tests will not work if TMPDIR is /tmp or /var/tmp +# or a subdir thereof +IGNORE:= ${TMPDIR}/ignore +OBJ:= ${TMPDIR}/obj + +# this is always ignored so make sure it isn't used above +TMPDIR= /tmp/nothanks + +all: one two three + +setup: + @mkdir -p ${IGNORE} ${OBJ} + @echo > ${IGNORE}/check + @rm -f ${OBJ}/check-ignore + +makefile:= ${.INCLUDEDFROMDIR}/${.INCLUDEDFROMFILE} +TEST:= ${.INCLUDEDFROMFILE:R:C,.*meta-,,:S,-,_,g:tu} + +DESC.one= Initialize check-ignore.meta +DESC.two= Use .MAKE.META.${TEST} - check-ignore is up to date +DESC.three= Skip .MAKE.META.${TEST} - check-ignore is out of date + +# just in case someone runs us with -jN +.ORDER: one two three +one two three: .MAKE setup + @echo "${DESC.${.TARGET}}"; \ + ${MAKE} -C ${.CURDIR} -f ${makefile} check-ignore parent=${.TARGET} + +.if make(check-ignore) +.MAKEFLAGS: -dM +.MAKE.MODE = meta verbose silent=yes +.OBJDIR: ${OBJ} +.if ${parent} == "two" +.if ${TEST} == "IGNORE_PATHS" +# this is a prefix list - any path that matches +# one of these prefixes will be ignored +.MAKE.META.IGNORE_PATHS = ${IGNORE} +.elif ${TEST} == "IGNORE_PATTERNS" +# more flexible but more expensive +# this example is equivalent to M*/ignore/* +# a match means ignore +.MAKE.META.IGNORE_PATTERNS = */ignore/* +.elif ${TEST} == "IGNORE_FILTER" +# this is the most flexible, but also most expensive +# if this expands to nothing - ignore the path +.MAKE.META.IGNORE_FILTER = N${IGNORE}/* +.endif +.endif + +# : < just reads from ${IGNORE}/check +# so that our filemon trace will have a reference to it +# we ensure it is always newer than the target. +check-ignore: .META .NOPATH + @: < ${IGNORE}/check > ${.TARGET} + @sleep 1; echo ${.TARGET} > ${IGNORE}/check + +.endif diff --git a/contrib/bmake/unit-tests/opt-debug-lint.exp b/contrib/bmake/unit-tests/opt-debug-lint.exp index 05b341b30dae..83643e9a8772 100644 --- a/contrib/bmake/unit-tests/opt-debug-lint.exp +++ b/contrib/bmake/unit-tests/opt-debug-lint.exp @@ -1,5 +1,7 @@ make: "opt-debug-lint.mk" line 19: Variable "X" is undefined +make: "opt-debug-lint.mk" line 19: Malformed conditional ($X) make: "opt-debug-lint.mk" line 41: Variable "UNDEF" is undefined +make: "opt-debug-lint.mk" line 41: Malformed conditional (${UNDEF}) make: "opt-debug-lint.mk" line 61: Missing delimiter ':' after modifier "L" make: "opt-debug-lint.mk" line 61: Missing delimiter ':' after modifier "P" make: "opt-debug-lint.mk" line 69: Unknown modifier "${" diff --git a/contrib/bmake/unit-tests/opt.mk b/contrib/bmake/unit-tests/opt.mk index 0931a66d3d15..939d5ec35aeb 100644 --- a/contrib/bmake/unit-tests/opt.mk +++ b/contrib/bmake/unit-tests/opt.mk @@ -1,4 +1,4 @@ -# $NetBSD: opt.mk,v 1.6 2020/11/18 01:06:59 sjg Exp $ +# $NetBSD: opt.mk,v 1.7 2023/02/25 00:07:08 rillig Exp $ # # Tests for the command line options. @@ -7,7 +7,7 @@ all: .IGNORE # The options from the top-level make are passed to the sub-makes via # the environment variable MAKEFLAGS. This is where the " -r -k -d 0" - # comes from. See MainParseArg. + # comes from. See MainParseOption. ${MAKE} -r -f /dev/null -V MAKEFLAGS @echo diff --git a/contrib/bmake/unit-tests/parse-var.exp b/contrib/bmake/unit-tests/parse-var.exp index bae925e8c869..39a9383953dd 100644 --- a/contrib/bmake/unit-tests/parse-var.exp +++ b/contrib/bmake/unit-tests/parse-var.exp @@ -1,5 +1 @@ -make: Unfinished modifier for "BRACE_GROUP" (',' missing) -make: "parse-var.mk" line 130: Malformed conditional (0 && ${BRACE_GROUP:S,${BRACE_PAIR:S,{,{{,},<lbraces>,}) -make: Fatal errors encountered -- cannot continue -make: stopped in unit-tests -exit status 1 +exit status 0 diff --git a/contrib/bmake/unit-tests/parse-var.mk b/contrib/bmake/unit-tests/parse-var.mk index cca6931d14a1..6205664c558e 100644 --- a/contrib/bmake/unit-tests/parse-var.mk +++ b/contrib/bmake/unit-tests/parse-var.mk @@ -1,4 +1,4 @@ -# $NetBSD: parse-var.mk,v 1.6 2022/09/25 21:26:23 rillig Exp $ +# $NetBSD: parse-var.mk,v 1.8 2023/02/18 11:16:09 rillig Exp $ # # Tests for parsing variable expressions. # @@ -68,8 +68,7 @@ # # Effects: # How much does the parsing position advance (pp)? -# What's the value of the expression (out_val)? -# What's the status after parsing the expression (VarParseResult)? +# What's the value of the expression (return value)? # What error messages are printed (Parse_Error)? # What no-effect error messages are printed (Error)? # What error messages should be printed but aren't? @@ -86,12 +85,9 @@ VAR.${:U param }= value . error .endif -# XXX: The following paragraph already uses past tense, in the hope that the -# parsing behavior can be cleaned up soon. - -# Since var.c 1.323 from 2020-07-26 18:11 and except for var.c 1.1028 from -# 2022-08-08, the exact way of parsing an expression depended on whether the -# expression was actually evaluated or merely parsed. +# Since var.c 1.323 from 2020-07-26 18:11 and until var.c 1.1047 from +# 2023-02-18, the exact way of parsing an expression with subexpressions +# depended on whether the expression was actually evaluated or merely parsed. # # If it was evaluated, nested expressions were parsed correctly, parsing each # modifier according to its exact definition (see varmod.mk). @@ -103,30 +99,28 @@ VAR.${:U param }= value # expression was not parsed correctly. Instead, make only counted the opening # and closing delimiters, which failed for nested modifiers with unbalanced # braces. -# -# This naive brace counting was implemented in ParseModifierPartDollar. As of -# var.c 1.1029, there are still several other places that merely count braces -# instead of properly parsing subexpressions. #.MAKEFLAGS: -dcpv # Keep these braces outside the conditions below, to keep them simple to -# understand. If the BRACE_PAIR had been replaced with ':U{}', the '}' would -# have to be escaped, but not the '{'. This asymmetry would have made the -# example even more complicated to understand. +# understand. If the expression ${BRACE_PAIR:...} had been replaced with the +# literal ${:U{}}, the '}' would have to be escaped, but not the '{'. This +# asymmetry would have made the example even more complicated to understand. BRACE_PAIR= {} -# In this test word, the '{{}' in the middle will be replaced. +# In this test word, the below conditions will replace the '{{}' in the middle +# with the string '<lbraces>'. BRACE_GROUP= {{{{}}}} # The inner ':S' modifier turns the word '{}' into '{{}'. # The outer ':S' modifier then replaces '{{}' with '<lbraces>'. -# In the first case, the outer expression is relevant and is parsed correctly. +# Due to the always-true condition '1', the outer expression is relevant and +# is parsed correctly. .if 1 && ${BRACE_GROUP:S,${BRACE_PAIR:S,{,{{,},<lbraces>,} .endif -# In the second case, the outer expression was irrelevant. In this case, in -# the parts of the outer ':S' modifier, make only counted the braces, and since -# the inner expression '${BRACE_PAIR:...}' contains more '{' than '}', parsing -# failed with the error message 'Unfinished modifier for "BRACE_GROUP"'. Fixed -# in var.c 1.1028 from 2022-08-08, reverted in var.c 1.1029 from 2022-08-23. +# Due to the always-false condition '0', the outer expression is irrelevant. +# In this case, in the parts of the outer ':S' modifier, the expression parser +# only counted the braces, and since the inner expression '${BRACE_PAIR:...}' +# contains more '{' than '}', parsing failed with the error message 'Unfinished +# modifier for "BRACE_GROUP"'. Fixed in var.c 1.1047 from 2023-02-18. .if 0 && ${BRACE_GROUP:S,${BRACE_PAIR:S,{,{{,},<lbraces>,} .endif #.MAKEFLAGS: -d0 diff --git a/contrib/bmake/unit-tests/var-eval-short.exp b/contrib/bmake/unit-tests/var-eval-short.exp index f574a6444e1b..34380ec61c41 100644 --- a/contrib/bmake/unit-tests/var-eval-short.exp +++ b/contrib/bmake/unit-tests/var-eval-short.exp @@ -7,7 +7,9 @@ make: "var-eval-short.mk" line 98: Malformed conditional (0 && ${:Uword:localtim CondParser_Eval: 0 && ${0:?${FAIL}then:${FAIL}else} Var_Parse: ${0:?${FAIL}then:${FAIL}else} (parse-only) Parsing modifier ${0:?...} +Var_Parse: ${FAIL}then:${FAIL}else} (parse-only) Modifier part: "${FAIL}then" +Var_Parse: ${FAIL}else} (parse-only) Modifier part: "${FAIL}else" Result of ${0:?${FAIL}then:${FAIL}else} is "" (parse-only, defined) Parsing line 163: DEFINED= defined @@ -17,7 +19,9 @@ Var_Parse: ${DEFINED:L:?${FAIL}then:${FAIL}else} (parse-only) Parsing modifier ${DEFINED:L} Result of ${DEFINED:L} is "defined" (parse-only, regular) Parsing modifier ${DEFINED:?...} +Var_Parse: ${FAIL}then:${FAIL}else} (parse-only) Modifier part: "${FAIL}then" +Var_Parse: ${FAIL}else} (parse-only) Modifier part: "${FAIL}else" Result of ${DEFINED:?${FAIL}then:${FAIL}else} is "defined" (parse-only, regular) Parsing line 166: .MAKEFLAGS: -d0 diff --git a/contrib/bmake/unit-tests/var-scope-cmdline.exp b/contrib/bmake/unit-tests/var-scope-cmdline.exp index a1227a1dd1f2..281b5af291ca 100644 --- a/contrib/bmake/unit-tests/var-scope-cmdline.exp +++ b/contrib/bmake/unit-tests/var-scope-cmdline.exp @@ -1,4 +1,4 @@ -make: "var-scope-cmdline.mk" line 67: global -make: "var-scope-cmdline.mk" line 76: makeflags +make: "var-scope-cmdline.mk" line 71: global +make: "var-scope-cmdline.mk" line 80: makeflags makeflags exit status 0 diff --git a/contrib/bmake/unit-tests/var-scope-cmdline.mk b/contrib/bmake/unit-tests/var-scope-cmdline.mk index 1f4a3e700253..fbfb2d0290a4 100644 --- a/contrib/bmake/unit-tests/var-scope-cmdline.mk +++ b/contrib/bmake/unit-tests/var-scope-cmdline.mk @@ -1,4 +1,4 @@ -# $NetBSD: var-scope-cmdline.mk,v 1.1 2022/01/23 16:25:54 rillig Exp $ +# $NetBSD: var-scope-cmdline.mk,v 1.2 2023/04/07 05:54:16 rillig Exp $ # # Tests for variables specified on the command line. # @@ -61,6 +61,10 @@ # Most cmdline variables are set at the very beginning, when parsing the # command line arguments. Using the special target '.MAKEFLAGS', it is # possible to set cmdline variables at any later time. +# +# See also: +# varcmd.mk +# varname-makeflags.mk # A normal global variable, without any cmdline variable nearby. VAR= global diff --git a/contrib/bmake/unit-tests/varcmd.mk b/contrib/bmake/unit-tests/varcmd.mk index 12739df30926..ec0cf96ed75c 100644 --- a/contrib/bmake/unit-tests/varcmd.mk +++ b/contrib/bmake/unit-tests/varcmd.mk @@ -1,4 +1,4 @@ -# $NetBSD: varcmd.mk,v 1.6 2021/02/16 19:43:09 rillig Exp $ +# $NetBSD: varcmd.mk,v 1.7 2023/04/07 05:54:16 rillig Exp $ # # Test behaviour of recursive make and vars set on command line. # @@ -12,6 +12,10 @@ # be rewritten to make it clear why there is a difference and why this is # actually intended. Removing that large block of code makes only this test # and vardebug.mk fail, which is not enough. +# +# See also: +# var-scope-cmdline.mk +# varname-makeflags.mk FU= fu FOO?= foo diff --git a/contrib/bmake/unit-tests/varmod-ifelse.exp b/contrib/bmake/unit-tests/varmod-ifelse.exp index 80361ebf6d61..94cbcbdeae82 100644 --- a/contrib/bmake/unit-tests/varmod-ifelse.exp +++ b/contrib/bmake/unit-tests/varmod-ifelse.exp @@ -27,6 +27,13 @@ make: "varmod-ifelse.mk" line 167: true make: "varmod-ifelse.mk" line 169: false make: Bad conditional expression ' ' in ' ?true:false' make: "varmod-ifelse.mk" line 171: +CondParser_Eval: 0 && ${1:?${:Uthen0:S,}},,}:${:Uelse0:S,}},,}} != "not evaluated" +CondParser_Eval: 1 && ${0:?${:Uthen1:S,}},,}:${:Uelse1:S,}},,}} != "else1" +CondParser_Eval: 0 +Comparing "else1" != "else1" +CondParser_Eval: 2 && ${1:?${:Uthen2:S,}},,}:${:Uelse2:S,}},,}} != "then2" +CondParser_Eval: 1 +Comparing "then2" != "then2" make: Fatal errors encountered -- cannot continue make: stopped in unit-tests exit status 1 diff --git a/contrib/bmake/unit-tests/varmod-ifelse.mk b/contrib/bmake/unit-tests/varmod-ifelse.mk index 2d1c54943ca1..0df45cae6870 100644 --- a/contrib/bmake/unit-tests/varmod-ifelse.mk +++ b/contrib/bmake/unit-tests/varmod-ifelse.mk @@ -1,4 +1,4 @@ -# $NetBSD: varmod-ifelse.mk,v 1.20 2022/09/25 12:51:37 rillig Exp $ +# $NetBSD: varmod-ifelse.mk,v 1.21 2023/02/18 18:23:58 rillig Exp $ # # Tests for the ${cond:?then:else} variable modifier, which evaluates either # the then-expression or the else-expression, depending on the condition. @@ -182,3 +182,67 @@ PRIMES= 2 3 5 7 11 "1:not_prime 2:prime 3:prime 4:not_prime 5:prime" . error .endif + +# When parsing the modifier ':?', there are 3 possible cases: +# +# 1. The whole expression is only parsed. +# 2. The expression is parsed and the 'then' branch is evaluated. +# 3. The expression is parsed and the 'else' branch is evaluated. +# +# In all of these cases, the expression must be parsed in the same way, +# especially when one of the branches contains unbalanced '{}' braces. +# +# At 2020-01-01, the expressions from the 'then' and 'else' branches were +# parsed differently, depending on whether the branch was taken or not. When +# the branch was taken, the parser recognized that in the modifier ':S,}},,', +# the '}}' were ordinary characters. When the branch was not taken, the +# parser only counted balanced '{' and '}', ignoring any escaping or other +# changes in the interpretation. +# +# In var.c 1.285 from 2020-07-20, the parsing of the expressions changed so +# that in both cases the expression is parsed in the same way, taking the +# unbalanced braces in the ':S' modifiers into account. This change was not +# on purpose, the commit message mentioned 'has the same effect', which was a +# wrong assumption. +# +# In var.c 1.323 from 2020-07-26, the unintended fix from var.c 1.285 was +# reverted, still not knowing about the difference between regular parsing and +# balanced-mode parsing. +# +# In var.c 1.1028 from 2022-08-08, there was another attempt at fixing this +# inconsistency in parsing, but since that broke parsing of the modifier ':@', +# it was reverted in var.c 1.1029 from 2022-08-23. +# +# In var.c 1.1047 from 2023-02-18, the inconsistency in parsing was finally +# fixed. The modifier ':@' now parses the body in balanced mode, while +# everywhere else the modifier parts have their subexpressions parsed in the +# same way, no matter whether they are evaluated or not. +# +# The modifiers ':@' and ':?' are similar in that they conceptually contain +# text to be evaluated later or conditionally, still they parse that text +# differently. The crucial difference is that the body of the modifier ':@' +# is always parsed using balanced mode. The modifier ':?', on the other hand, +# must parse both of its branches in the same way, no matter whether they are +# evaluated or not. Since balanced mode and standard mode are incompatible, +# it's impossible to use balanced mode in the modifier ':?'. +.MAKEFLAGS: -dc +.if 0 && ${1:?${:Uthen0:S,}},,}:${:Uelse0:S,}},,}} != "not evaluated" +# At 2020-01-07, the expression evaluated to 'then0,,}}', even though it was +# irrelevant as the '0' had already been evaluated to 'false'. +. error +.endif +.if 1 && ${0:?${:Uthen1:S,}},,}:${:Uelse1:S,}},,}} != "else1" +. error +.endif +.if 2 && ${1:?${:Uthen2:S,}},,}:${:Uelse2:S,}},,}} != "then2" +# At 2020-01-07, the whole expression evaluated to 'then2,,}}' instead of the +# expected 'then2'. The 'then' branch of the ':?' modifier was parsed +# normally, parsing and evaluating the ':S' modifier, thereby treating the +# '}}' as ordinary characters and resulting in 'then2'. The 'else' branch was +# parsed in balanced mode, ignoring that the inner '}}' were ordinary +# characters. The '}}' were thus interpreted as the end of the 'else' branch +# and the whole expression. This left the trailing ',,}}', which together +# with the 'then2' formed the result 'then2,,}}'. +. error +.endif +.MAKEFLAGS: -d0 diff --git a/contrib/bmake/unit-tests/varmod-loop.exp b/contrib/bmake/unit-tests/varmod-loop.exp index 9b432c806885..356946f63a9b 100644 --- a/contrib/bmake/unit-tests/varmod-loop.exp +++ b/contrib/bmake/unit-tests/varmod-loop.exp @@ -1,10 +1,10 @@ -Parsing line 78: USE_8_DOLLARS= ${:U1:@var@${8_DOLLARS}@} ${8_DOLLARS} $$$$$$$$ +Parsing line 91: USE_8_DOLLARS= ${:U1:@var@${8_DOLLARS}@} ${8_DOLLARS} $$$$$$$$ CondParser_Eval: ${USE_8_DOLLARS} != "\$\$\$\$ \$\$\$\$ \$\$\$\$" Comparing "$$$$ $$$$ $$$$" != "$$$$ $$$$ $$$$" -Parsing line 83: SUBST_CONTAINING_LOOP:= ${USE_8_DOLLARS} +Parsing line 96: SUBST_CONTAINING_LOOP:= ${USE_8_DOLLARS} CondParser_Eval: ${SUBST_CONTAINING_LOOP} != "\$\$ \$\$\$\$ \$\$\$\$" Comparing "$$ $$$$ $$$$" != "$$ $$$$ $$$$" -Parsing line 108: .MAKEFLAGS: -d0 +Parsing line 121: .MAKEFLAGS: -d0 ParseDependency(.MAKEFLAGS: -d0) :varname-overwriting-target: :x1y x2y x3y: :: mod-loop-dollar:1: diff --git a/contrib/bmake/unit-tests/varmod-loop.mk b/contrib/bmake/unit-tests/varmod-loop.mk index d3cc0228efd9..d09e49a6d842 100644 --- a/contrib/bmake/unit-tests/varmod-loop.mk +++ b/contrib/bmake/unit-tests/varmod-loop.mk @@ -1,6 +1,20 @@ -# $NetBSD: varmod-loop.mk,v 1.21 2022/08/23 21:13:46 rillig Exp $ +# $NetBSD: varmod-loop.mk,v 1.23 2023/02/18 11:55:20 rillig Exp $ # -# Tests for the :@var@...${var}...@ variable modifier. +# Tests for the expression modifier ':@var@body@', which replaces each word of +# the expression with the expanded body, which may contain references to the +# variable 'var'. For example, '${1 2 3:L:@word@<${word}>@}' encloses each +# word in angle quotes, resulting in '<1> <2> <3>'. +# +# The variable name can be chosen freely, except that it must not contain a +# '$'. For simplicity and readability, variable names should only use the +# characters 'A-Za-z0-9'. +# +# The body may contain subexpressions in the form '${...}' or '$(...)'. These +# subexpressions differ from everywhere else in makefiles in that the parser +# only scans '${...}' for balanced '{' and '}', likewise for '$(...)'. Any +# other '$' is left as-is during parsing. Later, when the body is expanded +# for each word, each '$$' is interpreted as a single '$', and the remaining +# '$' are interpreted as expressions, like when evaluating a regular variable. # 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 @@ -19,7 +33,6 @@ varname-overwriting-target: @echo :$@: :${:U1 2 3:@\@@x${@}y@}: :$@: - # Demonstrate that it is possible to generate dollar signs using the # :@ modifier. # @@ -192,15 +205,14 @@ CMDLINE= global # needed for deleting the environment # except for '$i', which is replaced with the then-current value '1' of the # iteration variable. # -# XXX: was broken in var.c 1.1028 from 2022-08-08, reverted in var.c 1.1029 -# from 2022-08-23; see parse-var.mk, keyword 'BRACE_GROUP'. +# See parse-var.mk, keyword 'BRACE_GROUP'. all: varmod-loop-literal-dollar varmod-loop-literal-dollar: .PHONY : ${:U1:@i@ t=$$(( $${t:-0} + $i ))@} # When parsing the loop body, each '\$', '\@' and '\\' is unescaped to '$', -# '@' and '\'; all other backslashes are retained. +# '@' and '\', respectively; all other backslashes are retained. # # In practice, the '$' is not escaped as '\$', as there is a second round of # unescaping '$$' to '$' later when the loop body is expanded after setting the diff --git a/contrib/bmake/unit-tests/varmod-no-match.mk b/contrib/bmake/unit-tests/varmod-no-match.mk index 2acb27e2e727..c03b4bf94e70 100644 --- a/contrib/bmake/unit-tests/varmod-no-match.mk +++ b/contrib/bmake/unit-tests/varmod-no-match.mk @@ -1,9 +1,97 @@ -# $NetBSD: varmod-no-match.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# $NetBSD: varmod-no-match.mk,v 1.3 2023/02/26 06:08:06 rillig Exp $ # -# Tests for the :N variable modifier, which filters words that do not match -# the given pattern. +# Tests for the expression modifier ':N', which filters words that do not +# match the given pattern. + + +# Keep all words except for 'two'. +.if ${:U one two three :Ntwo} != "one three" +. error +.endif + +# Keep all words except those starting with 't'. +# See varmod-match.mk for the details of pattern matching. +.if ${:U one two three four six :Nt*} != "one four six" +. error +.endif + + +# Idiom: normalize whitespace +# +# The modifier ':N' can be used with an empty pattern. As that pattern never +# matches a word, the only effect is that the string is split into words and +# then joined again, thereby normalizing whitespace around and between the +# words. And even though the 'N' in ':N' might serve as a mnemonic for +# "normalize whitespace", this idiom is not used in practice, resorting to the +# much more common ':M*' to "select all words" instead. +.if ${:U :N} != "" +. error +.endif +.if ${:U one two three :N} != "one two three" +. error +.endif +.if ${:U one two three :M*} != "one two three" +. error +.endif + + +# Idiom: single-word expression equals any of several words or patterns +# +# If an expression is guaranteed to consist of a single word, the modifier +# ':N' can be chained to compare the expression to several words or even +# patterns in a sequence. If one of the patterns matches, the final +# expression will be the empty string. +# +.if ${:U word :None:Ntwo:Nthree} != "" +# good +.else +. error +.endif +.if ${:U two :None:Ntwo:Nthree} != "" +. error +.else +# good +.endif +# +# The modifier ':N' is seldom used in general since positive matches with ':M' +# are easier to grasp. Chaining the ':N' modifier is even more difficult to +# grasp due to the many negations involved. +# +# The final '!= ""' adds to the confusion because at first glance, the +# condition may look like '${VAR} != ""', which for a single-word variable is +# always true. +# +# The '!= ""' can be omitted if the expression cannot have the numeric value +# 0, which is common in practice. In that form, each ':N' can be pronounced +# as 'neither' or 'nor', which makes the expression sound more natural. +# +.if ${:U word :None:Ntwo:Nthree} +# good +.else +. error +.endif +.if ${:U two :None:Ntwo:Nthree} +. error +.else +# good +.endif +# +# Replacing the '${...} != ""' with '!empty(...)' doesn't improve the +# situation as the '!' adds another level of negations, and the word 'empty' +# is a negation on its own, thereby creating a triple negation. Furthermore, +# due to the '!empty', the expression to be evaluated no longer starts with +# '$' and is thus more difficult to spot quickly. +# +.if !empty(:U word :None:Ntwo:Nthree) +# good +.else +. error +.endif +.if !empty(:U two :None:Ntwo:Nthree) +. error +.else +# good +.endif -# TODO: Implementation all: - @:; diff --git a/contrib/bmake/unit-tests/varmod-order-shuffle.mk b/contrib/bmake/unit-tests/varmod-order-shuffle.mk index 16121d7e498f..e9898600355a 100644 --- a/contrib/bmake/unit-tests/varmod-order-shuffle.mk +++ b/contrib/bmake/unit-tests/varmod-order-shuffle.mk @@ -1,4 +1,4 @@ -# $NetBSD: varmod-order-shuffle.mk,v 1.7 2021/08/03 04:46:49 rillig Exp $ +# $NetBSD: varmod-order-shuffle.mk,v 1.8 2023/02/26 06:08:06 rillig Exp $ # # Tests for the :Ox variable modifier, which returns the words of the # variable, shuffled. @@ -6,8 +6,9 @@ # The variable modifier :Ox is available since 2005-06-01. # # As of 2020-08-16, make uses random(3) seeded by the current time in seconds. -# This makes the random numbers completely predictable since there is no other -# part of make that uses random numbers. +# This makes the random numbers completely predictable since the only other +# part of make that uses random numbers is the 'randomize-targets' mode, which +# is off by default. # # Tags: probabilistic diff --git a/contrib/bmake/unit-tests/varmod-order.exp b/contrib/bmake/unit-tests/varmod-order.exp index 46dc45e9f6d6..e5d03f887fc7 100644 --- a/contrib/bmake/unit-tests/varmod-order.exp +++ b/contrib/bmake/unit-tests/varmod-order.exp @@ -19,6 +19,8 @@ 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: Bad modifier ":On=Off" for variable "SWITCH" +make: "varmod-order.mk" line 100: Malformed conditional (${SWITCH:On=Off} != "Off") make: Fatal errors encountered -- cannot continue make: stopped in unit-tests exit status 1 diff --git a/contrib/bmake/unit-tests/varmod-order.mk b/contrib/bmake/unit-tests/varmod-order.mk index c6028fc10abd..b4d5452263c7 100644 --- a/contrib/bmake/unit-tests/varmod-order.mk +++ b/contrib/bmake/unit-tests/varmod-order.mk @@ -1,4 +1,4 @@ -# $NetBSD: varmod-order.mk,v 1.8 2022/01/15 12:35:18 rillig Exp $ +# $NetBSD: varmod-order.mk,v 1.10 2023/02/27 08:29:36 rillig Exp $ # # Tests for the :O variable modifier and its variants, which either sort the # words of the value or shuffle them. @@ -89,4 +89,18 @@ _:= ${NUMBERS:Onr . error .endif + +# If a modifier that starts with ':O' is not one of the known sort or shuffle +# forms, it is a parse error. Several other modifiers such as ':H' or ':u' +# fall back to the SysV modifier, for example, ':H=new' is not the standard +# ':H' modifier but instead replaces a trailing 'H' with 'new' in each word. +# There is no such fallback for the ':O' modifiers. +SWITCH= On +# expect: make: Bad modifier ":On=Off" for variable "SWITCH" +.if ${SWITCH:On=Off} != "Off" +. error +.else +. error +.endif + all: diff --git a/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_filter.exp b/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_filter.exp index 39a9383953dd..045d26dc080e 100644 --- a/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_filter.exp +++ b/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_filter.exp @@ -1 +1,11 @@ +Initialize check-ignore.meta +Building <tmpdir>/obj/check-ignore +Skipping meta for .END: .SPECIAL +Use .MAKE.META.IGNORE_FILTER - check-ignore is up to date +`check-ignore' is up to date. +Skipping meta for .END: .SPECIAL +Skip .MAKE.META.IGNORE_FILTER - check-ignore is out of date +<tmpdir>/obj/check-ignore.meta: <line>: file '<tmpdir>/ignore/check' is newer than the target... +Building <tmpdir>/obj/check-ignore +Skipping meta for .END: .SPECIAL exit status 0 diff --git a/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_filter.mk b/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_filter.mk index c41aec4acdf8..0adf6a202857 100644 --- a/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_filter.mk +++ b/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_filter.mk @@ -1,8 +1,5 @@ -# $NetBSD: varname-dot-make-meta-ignore_filter.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# $NetBSD: varname-dot-make-meta-ignore_filter.mk,v 1.3 2023/02/23 05:20:45 sjg Exp $ # # Tests for the special .MAKE.META.IGNORE_FILTER variable. -# TODO: Implementation - -all: - @:; +.include "meta-ignore.inc" diff --git a/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_paths.exp b/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_paths.exp index 39a9383953dd..161d09c2eff4 100644 --- a/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_paths.exp +++ b/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_paths.exp @@ -1 +1,11 @@ +Initialize check-ignore.meta +Building <tmpdir>/obj/check-ignore +Skipping meta for .END: .SPECIAL +Use .MAKE.META.IGNORE_PATHS - check-ignore is up to date +`check-ignore' is up to date. +Skipping meta for .END: .SPECIAL +Skip .MAKE.META.IGNORE_PATHS - check-ignore is out of date +<tmpdir>/obj/check-ignore.meta: <line>: file '<tmpdir>/ignore/check' is newer than the target... +Building <tmpdir>/obj/check-ignore +Skipping meta for .END: .SPECIAL exit status 0 diff --git a/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_paths.mk b/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_paths.mk index 4ae34f51608b..2c58849af0be 100644 --- a/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_paths.mk +++ b/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_paths.mk @@ -1,8 +1,5 @@ -# $NetBSD: varname-dot-make-meta-ignore_paths.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# $NetBSD: varname-dot-make-meta-ignore_paths.mk,v 1.3 2023/02/23 05:20:45 sjg Exp $ # # Tests for the special .MAKE.META.IGNORE_PATHS variable. -# TODO: Implementation - -all: - @:; +.include "meta-ignore.inc" diff --git a/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_patterns.exp b/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_patterns.exp index 39a9383953dd..ebc1c0c6261c 100644 --- a/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_patterns.exp +++ b/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_patterns.exp @@ -1 +1,11 @@ +Initialize check-ignore.meta +Building <tmpdir>/obj/check-ignore +Skipping meta for .END: .SPECIAL +Use .MAKE.META.IGNORE_PATTERNS - check-ignore is up to date +`check-ignore' is up to date. +Skipping meta for .END: .SPECIAL +Skip .MAKE.META.IGNORE_PATTERNS - check-ignore is out of date +<tmpdir>/obj/check-ignore.meta: <line>: file '<tmpdir>/ignore/check' is newer than the target... +Building <tmpdir>/obj/check-ignore +Skipping meta for .END: .SPECIAL exit status 0 diff --git a/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_patterns.mk b/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_patterns.mk index ea9fc49f1718..d3d6e065857d 100644 --- a/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_patterns.mk +++ b/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_patterns.mk @@ -1,8 +1,5 @@ -# $NetBSD: varname-dot-make-meta-ignore_patterns.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# $NetBSD: varname-dot-make-meta-ignore_patterns.mk,v 1.3 2023/02/23 05:20:45 sjg Exp $ # # Tests for the special .MAKE.META.IGNORE_PATTERNS variable. -# TODO: Implementation - -all: - @:; +.include "meta-ignore.inc" diff --git a/contrib/bmake/unit-tests/varname-dot-makeflags.exp b/contrib/bmake/unit-tests/varname-dot-makeflags.exp index dbf96469f86b..28cc64fd66c8 100644 --- a/contrib/bmake/unit-tests/varname-dot-makeflags.exp +++ b/contrib/bmake/unit-tests/varname-dot-makeflags.exp @@ -1,3 +1,10 @@ -echo "$MAKEFLAGS" - -r -k -d 00000 -D VARNAME WITH SPACES +make: "varname-dot-makeflags.mk" line 10: MAKEFLAGS=<undefined> +make: "varname-dot-makeflags.mk" line 11: .MAKEFLAGS=< -r -k> +make: "varname-dot-makeflags.mk" line 12: .MAKEOVERRIDES=<> +make: "varname-dot-makeflags.mk" line 18: MAKEFLAGS=<undefined> +make: "varname-dot-makeflags.mk" line 20: .MAKEFLAGS=< -r -k -D VARNAME -r> +make: "varname-dot-makeflags.mk" line 22: .MAKEOVERRIDES=< VAR> +runtime: MAKEFLAGS=< -r -k -D VARNAME -r VAR=value> +runtime: .MAKEFLAGS=< -r -k -D VARNAME -r> +runtime: .MAKEOVERRIDES=< VAR> exit status 0 diff --git a/contrib/bmake/unit-tests/varname-dot-makeflags.mk b/contrib/bmake/unit-tests/varname-dot-makeflags.mk index 10d1903022cb..cca285f93013 100644 --- a/contrib/bmake/unit-tests/varname-dot-makeflags.mk +++ b/contrib/bmake/unit-tests/varname-dot-makeflags.mk @@ -1,15 +1,36 @@ -# $NetBSD: varname-dot-makeflags.mk,v 1.1 2020/12/01 20:37:30 rillig Exp $ +# $NetBSD: varname-dot-makeflags.mk,v 1.7 2023/02/25 19:24:07 rillig Exp $ # # Tests for the special .MAKEFLAGS variable, which collects almost all # command line arguments and passes them on to any child processes via # the environment variable MAKEFLAGS (without leading '.'). +# +# See also: +# varname-dot-makeoverrides.mk + +.info MAKEFLAGS=<${MAKEFLAGS:Uundefined}> +.info .MAKEFLAGS=<${.MAKEFLAGS}> +.info .MAKEOVERRIDES=<${.MAKEOVERRIDES:Uundefined}> + +# Append an option with argument, a plain option and a variable assignment. +.MAKEFLAGS: -DVARNAME -r VAR=value + +# expect+1: MAKEFLAGS=<undefined> +.info MAKEFLAGS=<${MAKEFLAGS:Uundefined}> +# expect+1: .MAKEFLAGS=< -r -k -D VARNAME -r> +.info .MAKEFLAGS=<${.MAKEFLAGS}> +# expect+1: .MAKEOVERRIDES=< VAR> +.info .MAKEOVERRIDES=<${.MAKEOVERRIDES}> -# When options are parsed, the option and its argument are appended as -# separate words to .MAKEFLAGS. Special characters in the option argument -# are not quoted though. It seems to have not been necessary at least from -# 1993 until 2020. -.MAKEFLAGS: -d00000 -D"VARNAME WITH SPACES" +# The environment variable 'MAKEFLAGS' is not available to child processes +# when parsing the makefiles. This is different from exported variables, +# which are already available during parse time. +.if ${:!echo "\${MAKEFLAGS-undef}"!} != "undef" +. error +.endif -all: - echo "$$MAKEFLAGS" - @:; +# After parsing, the environment variable 'MAKEFLAGS' is set based on the +# special variables '.MAKEFLAGS' and '.MAKEOVERRIDES'. +runtime: + @echo '$@: MAKEFLAGS=<'${MAKEFLAGS:Q}'>' + @echo '$@: .MAKEFLAGS=<'${.MAKEFLAGS:Q}'>' + @echo '$@: .MAKEOVERRIDES=<'${.MAKEOVERRIDES:Q}'>' diff --git a/contrib/bmake/unit-tests/varname-dot-makeoverrides.exp b/contrib/bmake/unit-tests/varname-dot-makeoverrides.exp index 39a9383953dd..78c0296f8d76 100644 --- a/contrib/bmake/unit-tests/varname-dot-makeoverrides.exp +++ b/contrib/bmake/unit-tests/varname-dot-makeoverrides.exp @@ -1 +1,8 @@ +all: overrides=<> +make -f varname-dot-makeoverrides.mk stage_1 VAR=value +stage_1: overrides=< VAR> +make -f varname-dot-makeoverrides.mk stage_2 +stage_2: overrides=< VAR> +make -f varname-dot-makeoverrides.mk stage_3 +stage_3: overrides=< VAR> exit status 0 diff --git a/contrib/bmake/unit-tests/varname-dot-makeoverrides.mk b/contrib/bmake/unit-tests/varname-dot-makeoverrides.mk index a897f4667175..f3f3897f8aa4 100644 --- a/contrib/bmake/unit-tests/varname-dot-makeoverrides.mk +++ b/contrib/bmake/unit-tests/varname-dot-makeoverrides.mk @@ -1,8 +1,23 @@ -# $NetBSD: varname-dot-makeoverrides.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# $NetBSD: varname-dot-makeoverrides.mk,v 1.5 2023/02/25 06:54:08 rillig Exp $ # -# Tests for the special .MAKE.MAKEOVERRIDES variable. - -# TODO: Implementation +# Tests for the special .MAKEOVERRIDES variable, which lists the names of the +# variables that are passed on to child processes via the MAKEFLAGS +# environment variable. +# +# See also: +# varname-dot-makeflags.mk all: - @:; + @echo '$@: overrides=<'${.MAKEOVERRIDES:Uundefined:Q}'>' + ${MAKE} -f ${MAKEFILE} stage_1 VAR=value + +stage_1: + @echo '$@: overrides=<'${.MAKEOVERRIDES:Q}'>' + ${MAKE} -f ${MAKEFILE} stage_2 + +stage_2: + @echo '$@: overrides=<'${.MAKEOVERRIDES:Q}'>' + ${MAKE} -f ${MAKEFILE} stage_3 + +stage_3: + @echo '$@: overrides=<'${.MAKEOVERRIDES:Q}'>' diff --git a/contrib/bmake/unit-tests/varname-makeflags.exp b/contrib/bmake/unit-tests/varname-makeflags.exp index 39a9383953dd..c1354177ca47 100644 --- a/contrib/bmake/unit-tests/varname-makeflags.exp +++ b/contrib/bmake/unit-tests/varname-makeflags.exp @@ -1 +1,21 @@ +spaces_stage_0: MAKEFLAGS=< -r -k > +spaces_stage_0: env MAKEFLAGS=< -r -k > +spaces_stage_1: MAKEFLAGS=< -r -k -d 00000 -D VARNAME WITH SPACES > +spaces_stage_1: env MAKEFLAGS=< -r -k -d 00000 -D VARNAME WITH SPACES > +dollars_stage_0: MAKEFLAGS=< -r -k > +dollars_stage_1: env MAKEFLAGS=< -r -k DOLLARS=\$\{varname\}> +dollars_stage_1: MAKEFLAGS=< -r -k DOLLARS=\{varname\}> +dollars_stage_1: MAKEFLAGS:q=< -r -k DOLLARS=\{varname\}> +dollars_stage_2: env MAKEFLAGS=< -r -k DOLLARS=> +dollars_stage_2: dollars=<> +dollars_stage_2: MAKEFLAGS=< -r -k DOLLARS=> +dollars_stage_3: env MAKEFLAGS=< -r -k DOLLARS=> +dollars_stage_3: dollars=<> +dollars_stage_3: MAKEFLAGS=< -r -k DOLLARS=> +append_stage_0: MAKEFLAGS=< -r -k > +append_stage_1: MAKEFLAGS=< -r -k -D before-0 -D after-0 VAR0=value> +append_stage_2: MAKEFLAGS=< -r -k -D before-0 -D after-0 -D before-1 -D after-1 VAR0=value VAR1=value> +append_stage_3: MAKEFLAGS=< -r -k -D before-0 -D after-0 -D before-1 -D after-1 -D before-2 -D after-2 VAR0=value VAR1=value VAR2=value> +override_stage_1: run MAKEFLAGS=< -r -k STAGE=1 VAR=value> +override_stage_2: STAGE=<2> VAR=<value> exit status 0 diff --git a/contrib/bmake/unit-tests/varname-makeflags.mk b/contrib/bmake/unit-tests/varname-makeflags.mk index f7840c2eb7a5..da339ba9c5e2 100644 --- a/contrib/bmake/unit-tests/varname-makeflags.mk +++ b/contrib/bmake/unit-tests/varname-makeflags.mk @@ -1,44 +1,182 @@ -# $NetBSD: varname-makeflags.mk,v 1.5 2022/01/16 18:16:06 sjg Exp $ +# $NetBSD: varname-makeflags.mk,v 1.7 2023/02/25 19:24:07 rillig Exp $ # -# Tests for the special MAKEFLAGS variable, which is basically just a normal -# environment variable. It is closely related to .MAKEFLAGS but captures the -# state of .MAKEFLAGS at the very beginning of make, before any makefiles are -# read. +# Tests for the environment variable 'MAKEFLAGS', from which additional +# command line arguments are read before the actual command line arguments. +# +# After reading the makefiles and before making the targets, the arguments +# that were collected in '.MAKEFLAGS' and '.MAKEOVERRIDES' are written back to +# the environment variable 'MAKEFLAGS'. + +all: spaces_stage_0 dollars_stage_0 append_stage_0 override_stage_0 -# TODO: Implementation -.MAKEFLAGS: -d0 +.if !make(*stage*) # The unit tests are run with an almost empty environment. In particular, -# the variable MAKEFLAGS is not set. The '.MAKEFLAGS:' above also doesn't -# influence the environment variable MAKEFLAGS, therefore it is still -# undefined at this point. -.if ${MAKEFLAGS:Uundefined} != "undefined" -. error -.endif +# the variable MAKEFLAGS is not set. +. if ${MAKEFLAGS:Uundefined} != "undefined" +. error +. endif # The special variable .MAKEFLAGS is influenced though. # See varname-dot-makeflags.mk for more details. -.if ${.MAKEFLAGS} != " -r -k -d 0" -. error -.endif +. if ${.MAKEFLAGS} != " -r -k" +. error +. endif # In POSIX mode, the environment variable MAKEFLAGS can contain letters only, # for compatibility. These letters are exploded to form regular options. OUTPUT!= env MAKEFLAGS=ikrs ${MAKE} -f /dev/null -v .MAKEFLAGS -.if ${OUTPUT} != " -i -k -r -s -V .MAKEFLAGS" -. error -.endif +. if ${OUTPUT} != " -i -k -r -s -V .MAKEFLAGS" +. error +. endif # As soon as there is a single non-alphabetic character in the environment # variable MAKEFLAGS, it is no longer split. In this example, the word # "d0ikrs" is treated as a target, but the option '-v' prevents any targets # from being built. OUTPUT!= env MAKEFLAGS=d0ikrs ${MAKE} -r -f /dev/null -v .MAKEFLAGS -.if ${OUTPUT} != " -r -V .MAKEFLAGS" -. error ${OUTPUT} +. if ${OUTPUT} != " -r -V .MAKEFLAGS" +. error ${OUTPUT} +. endif + +.endif + + +# When options are parsed, the option and its argument are appended as +# separate words to the MAKEFLAGS for the child processes. Special characters +# in the option arguments are not quoted though. +spaces_stage_0: + @echo '$@: MAKEFLAGS=<'${MAKEFLAGS:Q}'>' + @echo "$@: env MAKEFLAGS=<$$MAKEFLAGS>" + @${MAKE} -f ${MAKEFILE} spaces_stage_1 -d00000 -D"VARNAME WITH SPACES" + +# At this point, the 'VARNAME WITH SPACES' is no longer recognizable as a +# single command line argument. In practice, variable names don't contain +# spaces. +spaces_stage_1: + @echo '$@: MAKEFLAGS=<'${MAKEFLAGS:Q}'>' + @echo "$@: env MAKEFLAGS=<$$MAKEFLAGS>" + + +# Demonstrate that '$' characters are altered when they are passed on to child +# make processes via MAKEFLAGS. +dollars_stage_0: + @echo '$@: MAKEFLAGS=<'${MAKEFLAGS:Q}'>' + + # The '$$$$' becomes a literal '$$' when building the '${MAKE}' + # command line, making the actual argument 'DOLLARS=$${varname}'. + # At this stage, MAKEFLAGS is not yet involved. + @${MAKE} -f ${MAKEFILE} dollars_stage_1 DOLLARS='$$$${varname}' + +.if make(dollars_stage_1) +# At this point, the variable 'DOLLARS' contains '$${varname}', which +# evaluates to a literal '$' followed by '{varname}'. +. if ${DOLLARS} != "\${varname}" +. error +. endif .endif +dollars_stage_1: + # At this point, the stage 1 make provides the environment variable + # 'MAKEFLAGS' to its child processes, even if the child process is not + # another make. + # + # expect: dollars_stage_1: env MAKEFLAGS=< -r -k DOLLARS=\$\{varname\}> + # + # The 'DOLLARS=\$\{varname\}' assignment is escaped so that the stage + # 2 make will see it as a single word. + @echo "$@: env MAKEFLAGS=<$$MAKEFLAGS>" + # At this point, evaluating the environment variable 'MAKEFLAGS' leads + # to strange side effects as the string '\$\{varname\}' is interpreted + # as: + # + # \ a literal string of a single backslash + # $\ the value of the variable named '\' + # {varname\} a literal string + # + # Since the variable name '\' is not defined, the resulting value is + # '\{varname\}'. Make doesn't handle isolated '$' characters in + # strings well, instead each '$' has to be part of a '$$' or be part + # of a subexpression like '${VAR}'. + @echo '$@: MAKEFLAGS=<'${MAKEFLAGS:Q}'>' + + # The modifier ':q' preserves a '$$' in an expression value instead of + # expanding it to a single '$', but it's already too late, as that + # modifier applies after the expression has been evaluated. Except + # for debug logging, there is no way to process strings that contain + # isolated '$'. + @echo '$@: MAKEFLAGS:q=<'${MAKEFLAGS:q}'>' + + @${MAKE} -f ${MAKEFILE} dollars_stage_2 + +.if make(dollars_stage_2) +# At this point, the variable 'DOLLARS' contains '${varname}', and since +# 'varname' is undefined, that expression evaluates to an empty string. +. if ${DOLLARS} != "" +. error +. endif +varname= varvalue +. if ${DOLLARS} != "varvalue" +. error +. endif +. undef varname +.endif +dollars_stage_2: + @echo "$@: env MAKEFLAGS=<$$MAKEFLAGS>" + @echo '$@: dollars=<'${DOLLARS:Q}'>' + @echo '$@: MAKEFLAGS=<'${MAKEFLAGS:Q}'>' + @${MAKE} -f ${MAKEFILE} dollars_stage_3 + +dollars_stage_3: + @echo "$@: env MAKEFLAGS=<$$MAKEFLAGS>" + @echo '$@: dollars=<'${DOLLARS:Uundefined:Q}'>' + @echo '$@: MAKEFLAGS=<'${MAKEFLAGS:Q}'>' + + +# Demonstrates in which exact order the MAKEFLAGS are built together from the +# parent MAKEFLAGS and the flags from the command line, in particular that +# variable assignments are passed at the end, after the options. +append_stage_0: + @echo '$@: MAKEFLAGS=<'${MAKEFLAGS:Q}'>' + @${MAKE} -Dbefore-0 -f ${MAKEFILE} append_stage_1 VAR0=value -Dafter-0 + +append_stage_1: + @echo '$@: MAKEFLAGS=<'${MAKEFLAGS:Q}'>' + @${MAKE} -Dbefore-1 -f ${MAKEFILE} append_stage_2 VAR1=value -Dafter-1 + +append_stage_2: + @echo '$@: MAKEFLAGS=<'${MAKEFLAGS:Q}'>' + @${MAKE} -Dbefore-2 -f ${MAKEFILE} append_stage_3 VAR2=value -Dafter-2 + +append_stage_3: + @echo '$@: MAKEFLAGS=<'${MAKEFLAGS:Q}'>' + + +# Demonstrates the implementation details of 'MAKEFLAGS', in particular that +# it is an environment variable rather than a global variable. +override_stage_0: + @${MAKE} -f ${MAKEFILE} STAGE=1 VAR=value override_stage_1 + +.if make(override_stage_1) +# While parsing the makefiles, 'MAKEFLAGS' is the value of the environment +# variable, in this case provided by stage 0. +. if ${MAKEFLAGS:M*} != "-r -k" +. error +. endif +MAKEFLAGS= overridden # temporarily override it +. if ${MAKEFLAGS} != "overridden" +. error +. endif +.undef MAKEFLAGS # make the environment variable visible again +. if ${MAKEFLAGS:M*} != "-r -k" +. error +. endif +.endif +override_stage_1: + @echo '$@: run MAKEFLAGS=<'${MAKEFLAGS:Q}'>' + @${MAKE} -f ${MAKEFILE} STAGE=2 override_stage_2 -all: +override_stage_2: + @echo '$@: STAGE=<${STAGE}> VAR=<${VAR}>' diff --git a/contrib/bmake/unit-tests/varparse-errors.exp b/contrib/bmake/unit-tests/varparse-errors.exp index 2c3568e468ca..fc20e427296a 100644 --- a/contrib/bmake/unit-tests/varparse-errors.exp +++ b/contrib/bmake/unit-tests/varparse-errors.exp @@ -1,10 +1,10 @@ -make: "varparse-errors.mk" line 38: Unknown modifier "Z" -make: "varparse-errors.mk" line 46: Unknown modifier "Z" +make: "varparse-errors.mk" line 37: Unknown modifier "Z" +make: "varparse-errors.mk" line 45: Unknown modifier "Z" make: Bad modifier ":OX" for variable "" -make: "varparse-errors.mk" line 68: Undefined variable "${:U:OX" +make: "varparse-errors.mk" line 67: Undefined variable "${:U:OX" make: Bad modifier ":OX" for variable "" make: Bad modifier ":OX" for variable "" -make: "varparse-errors.mk" line 68: Undefined variable "${:U:OX" +make: "varparse-errors.mk" line 67: Undefined variable "${:U:OX" make: Bad modifier ":OX" for variable "" make: Unclosed variable expression, expecting '}' for modifier "Q" of variable "" with value "" make: Unclosed variable expression, expecting '}' for modifier "sh" of variable "" with value "" diff --git a/contrib/bmake/unit-tests/varparse-errors.mk b/contrib/bmake/unit-tests/varparse-errors.mk index 9174d264db0f..9ce41cc3be9a 100644 --- a/contrib/bmake/unit-tests/varparse-errors.mk +++ b/contrib/bmake/unit-tests/varparse-errors.mk @@ -1,4 +1,4 @@ -# $NetBSD: varparse-errors.mk,v 1.7 2022/08/24 22:09:41 rillig Exp $ +# $NetBSD: varparse-errors.mk,v 1.8 2023/02/14 21:56:48 rillig Exp $ # Tests for parsing and evaluating all kinds of variable expressions. # @@ -6,7 +6,6 @@ # Var_Subst, collecting typical and not so typical use cases. # # See also: -# VarParseResult # Var_Parse # Var_Subst |