diff options
Diffstat (limited to 'contrib/bmake/unit-tests')
24 files changed, 550 insertions, 369 deletions
diff --git a/contrib/bmake/unit-tests/Makefile b/contrib/bmake/unit-tests/Makefile index bcfe853642a5..a3f069133739 100644 --- a/contrib/bmake/unit-tests/Makefile +++ b/contrib/bmake/unit-tests/Makefile @@ -1,6 +1,6 @@ -# $Id: Makefile,v 1.193 2023/02/25 20:03:25 sjg Exp $ +# $Id: Makefile,v 1.195 2023/05/10 18:26:24 sjg Exp $ # -# $NetBSD: Makefile,v 1.333 2023/02/25 19:30:32 sjg Exp $ +# $NetBSD: Makefile,v 1.335 2023/05/10 13:03:06 rillig Exp $ # # Unit tests for make(1) # @@ -211,7 +211,6 @@ TESTS+= export TESTS+= export-all TESTS+= export-env TESTS+= export-variants -TESTS+= forloop TESTS+= forsubst TESTS+= gnode-submake TESTS+= hanoi-include @@ -375,6 +374,7 @@ TESTS+= varmod-loop-delete TESTS+= varmod-loop-varname TESTS+= varmod-match TESTS+= varmod-match-escape +TESTS+= varmod-mtime TESTS+= varmod-no-match TESTS+= varmod-order TESTS+= varmod-order-numeric diff --git a/contrib/bmake/unit-tests/cond-func.mk b/contrib/bmake/unit-tests/cond-func.mk index 959367f5c6ab..e09b363edb64 100644 --- a/contrib/bmake/unit-tests/cond-func.mk +++ b/contrib/bmake/unit-tests/cond-func.mk @@ -1,12 +1,12 @@ -# $NetBSD: cond-func.mk,v 1.11 2022/01/07 19:30:17 rillig Exp $ +# $NetBSD: cond-func.mk,v 1.12 2023/05/10 15:53:32 rillig Exp $ # # Tests for those parts of the functions in .if conditions that are common # among several functions. # -# The below test uses the function defined(...) since it has no side-effects, -# the other functions (except empty(...)) would work equally well. The -# function empty is special because it uses a different parsing algorithm for -# its argument. +# The below test uses the 'defined' function since it has no side-effects. +# The other functions would work equally well, except for 'empty', which +# parses its argument differently from the other functions. +# DEF= defined ${:UA B}= variable name with spaces @@ -74,7 +74,7 @@ ${VARNAME_UNBALANCED_BRACES}= variable name with unbalanced braces # There may be spaces around the operators and parentheses, and even # inside the parentheses. The spaces inside the parentheses are not -# allowed for the empty() function (see cond-func-empty.mk), therefore +# allowed for the 'empty' function (see cond-func-empty.mk), therefore # they are typically omitted for the other functions as well. .if ! defined ( DEF ) . error diff --git a/contrib/bmake/unit-tests/cond-late.mk b/contrib/bmake/unit-tests/cond-late.mk index 4df3df2cf1d4..1cfaaa2ee4e9 100644 --- a/contrib/bmake/unit-tests/cond-late.mk +++ b/contrib/bmake/unit-tests/cond-late.mk @@ -1,11 +1,12 @@ -# $NetBSD: cond-late.mk,v 1.3 2020/11/15 14:07:53 rillig Exp $ +# $NetBSD: cond-late.mk,v 1.4 2023/05/10 15:53:32 rillig Exp $ # # Using the :? modifier, variable expressions can contain conditional # expressions that are evaluated late, at expansion time. # -# Any variables appearing in these -# conditions are expanded before parsing the condition. This is -# different from many other places. +# Any expressions appearing in these conditions are expanded before parsing +# the condition. This is different from conditions in .if directives, where +# expressions are evaluated individually and only as far as necessary, see +# cond-short.mk. # # Because of this, variables that are used in these lazy conditions # should not contain double-quotes, or the parser will probably fail. @@ -22,10 +23,14 @@ COND.false= "yes" != "yes" # If the order of evaluation were to change to first parse the condition # and then expand the variables, the output would change from the # current "yes no" to "yes yes", since both variables are non-empty. +# expect: yes +# expect: no cond-literal: @echo ${ ${COND.true} :?yes:no} @echo ${ ${COND.false} :?yes:no} -VAR+= ${${UNDEF} != "no":?:} +VAR= ${${UNDEF} != "no":?:} +# expect-reset +# expect: make: Bad conditional expression ' != "no"' in ' != "no"?:' .if empty(VAR:Mpattern) .endif diff --git a/contrib/bmake/unit-tests/dep-var.mk b/contrib/bmake/unit-tests/dep-var.mk index f4a724f0ce7c..16f7498fd5cc 100755 --- a/contrib/bmake/unit-tests/dep-var.mk +++ b/contrib/bmake/unit-tests/dep-var.mk @@ -1,4 +1,4 @@ -# $NetBSD: dep-var.mk,v 1.7 2023/02/13 21:01:46 rillig Exp $ +# $NetBSD: dep-var.mk,v 1.8 2023/05/10 15:53:32 rillig Exp $ # # Tests for variable references in dependency declarations. # @@ -91,5 +91,6 @@ undef1 def2 a-def2-b 1-2-$$INDIRECT_2-2-1 ${:U\$)}: .MAKEFLAGS: -d0 -# XXX: Why is the exit status still 0, even though Parse_Error is called -# with PARSE_FATAL in SuffExpandChildren? +# XXX: The exit status is still 0, even though Parse_Error is called with +# PARSE_FATAL in SuffExpandChildren. The exit status is only affected by +# parse errors when they occur in the parsing phase, see Parse_File. diff --git a/contrib/bmake/unit-tests/directive-for-errors.exp b/contrib/bmake/unit-tests/directive-for-errors.exp index da5eee473ec2..36ed569c7932 100644 --- a/contrib/bmake/unit-tests/directive-for-errors.exp +++ b/contrib/bmake/unit-tests/directive-for-errors.exp @@ -1,22 +1,17 @@ -make: "directive-for-errors.mk" line 7: Unknown directive "fori" -make: "directive-for-errors.mk" line 8: warning: -make: "directive-for-errors.mk" line 9: for-less endfor -make: "directive-for-errors.mk" line 19: Unknown directive "for" -make: "directive-for-errors.mk" line 20: warning: -make: "directive-for-errors.mk" line 21: for-less endfor -make: "directive-for-errors.mk" line 37: Dollar $ 1 1 and backslash 2 2 2. -make: "directive-for-errors.mk" line 37: Dollar $ 3 3 and backslash 4 4 4. -make: "directive-for-errors.mk" line 43: no iteration variables in for -make: "directive-for-errors.mk" line 47: warning: Should not be reached. -make: "directive-for-errors.mk" line 48: for-less endfor -make: "directive-for-errors.mk" line 53: Wrong number of words (5) in .for substitution list with 3 variables -make: "directive-for-errors.mk" line 64: missing `in' in for -make: "directive-for-errors.mk" line 66: warning: Should not be reached. -make: "directive-for-errors.mk" line 67: for-less endfor -make: "directive-for-errors.mk" line 73: Unknown modifier "Z" -make: "directive-for-errors.mk" line 74: warning: Should not be reached. -make: "directive-for-errors.mk" line 74: warning: Should not be reached. -make: "directive-for-errors.mk" line 74: warning: Should not be reached. +make: "directive-for-errors.mk" line 11: Unknown directive "fori" +make: "directive-for-errors.mk" line 12: warning: <> +make: "directive-for-errors.mk" line 13: for-less endfor +make: "directive-for-errors.mk" line 27: Unknown directive "for" +make: "directive-for-errors.mk" line 28: warning: <> +make: "directive-for-errors.mk" line 29: for-less endfor +make: "directive-for-errors.mk" line 46: invalid character '$' in .for loop variable name +make: "directive-for-errors.mk" line 54: no iteration variables in for +make: "directive-for-errors.mk" line 66: Wrong number of words (5) in .for substitution list with 3 variables +make: "directive-for-errors.mk" line 80: missing `in' in for +make: "directive-for-errors.mk" line 91: Unknown modifier "Z" +make: "directive-for-errors.mk" line 92: warning: Should not be reached. +make: "directive-for-errors.mk" line 92: warning: Should not be reached. +make: "directive-for-errors.mk" line 92: warning: Should not be reached. make: Fatal errors encountered -- cannot continue make: stopped in unit-tests exit status 1 diff --git a/contrib/bmake/unit-tests/directive-for-errors.mk b/contrib/bmake/unit-tests/directive-for-errors.mk index 602ecbf32e4e..24df5e131839 100644 --- a/contrib/bmake/unit-tests/directive-for-errors.mk +++ b/contrib/bmake/unit-tests/directive-for-errors.mk @@ -1,45 +1,56 @@ -# $NetBSD: directive-for-errors.mk,v 1.3 2021/04/04 10:13:09 rillig Exp $ +# $NetBSD: directive-for-errors.mk,v 1.5 2023/05/09 19:43:12 rillig Exp $ # # Tests for error handling in .for loops. +# expect-all + + # A .for directive must be followed by whitespace, everything else results # in a parse error. +# expect+1: Unknown directive "fori" .fori in 1 2 3 -. warning ${i} +. warning <${i}> .endfor +# expect-2: <> +# expect-2: for-less endfor + # A slash is not whitespace, therefore this is not parsed as a .for loop. # # XXX: The error message is misleading though. As of 2020-12-31, it says -# "Unknown directive "for"", but that directive is actually known. This is +# 'Unknown directive "for"', but that directive is actually known. This is # because ForEval does not detect the .for loop as such, so parsing # continues in ParseLine > ParseDependencyLine > ParseDependency > # ParseDependencyTargets > ParseErrorNoDependency, and there the directive # name is parsed a bit differently. +# expect+1: Unknown directive "for" .for/i in 1 2 3 -. warning ${i} +. warning <${i}> .endfor +# expect-2: warning: <> +# expect-2: for-less endfor -# As of 2020-12-31, the variable name can be an arbitrary word, it just needs -# to be separated by whitespace. Even '$' and '\' are valid variable names, -# which is not useful in practice. -# -# The '$$' is not replaced with the values '1' or '3' from the .for loop, -# instead it is kept as-is, and when the .info directive expands its argument, -# each '$$' gets replaced with a single '$'. The "long variable expression" -# ${$} gets replaced though, even though this would be a parse error everywhere -# outside a .for loop. + +# Before for.c 1.173 from 2023-05-08, the variable name could be an arbitrary +# word, it only needed to be separated by whitespace. Even '$' and '\' were +# valid variable names, which was not useful in practice. # -# The '\' on the other hand is treated as a normal variable name. +# The '$$' was not replaced with the values '1' or '3' from the .for loop, +# instead it was kept as-is, and when the .info directive expanded its +# argument, each '$$' got replaced with a single '$'. The "long variable +# expression" ${$} got replaced though, even though this would be a parse +# error everywhere outside a .for loop. ${:U\$}= dollar # see whether the "variable" '$' is local ${:U\\}= backslash # see whether the "variable" '\' is local +# expect+1: invalid character '$' in .for loop variable name .for $ \ in 1 2 3 4 . info Dollar $$ ${$} $($) and backslash $\ ${\} $(\). .endfor # If there are no variables, there is no point in expanding the .for loop -# since this would end up in an endless loop, each time consuming 0 of the -# 3 values. +# since this would end up in an endless loop, consuming 0 of the 3 values in +# each iteration. +# expect+1: no iteration variables in for .for in 1 2 3 # XXX: This should not be reached. It should be skipped, as already done # when the number of values is not a multiple of the number of variables, @@ -47,29 +58,39 @@ ${:U\\}= backslash # see whether the "variable" '\' is local . warning Should not be reached. .endfor + # There are 3 variables and 5 values. These 5 values cannot be split evenly # among the variables, therefore the loop is not expanded at all, it is -# rather skipped. +# skipped instead. +# expect+1: Wrong number of words (5) in .for substitution list with 3 variables .for a b c in 1 2 3 4 5 . warning Should not be reached. .endfor + # The list of values after the 'in' may be empty, no matter if this emptiness # comes from an empty expansion or even from a syntactically empty line. .for i in . info Would be reached if there were items to loop over. .endfor + # A missing 'in' should parse the .for loop but skip the body. -.for i : k +# expect+1: missing `in' in for +.for i over k # XXX: As of 2020-12-31, this line is reached once. . warning Should not be reached. .endfor + # A malformed modifier should be detected and skip the body of the loop. # # XXX: As of 2020-12-31, Var_Subst doesn't report any errors, therefore # the loop body is expanded as if no error had happened. +# expect+1: Unknown modifier "Z" .for i in 1 2 ${:U3:Z} 4 . warning Should not be reached. .endfor +# expect-2: Should not be reached. +# expect-3: Should not be reached. +# expect-4: Should not be reached. diff --git a/contrib/bmake/unit-tests/directive-for-escape.exp b/contrib/bmake/unit-tests/directive-for-escape.exp index d679e0756e33..0326b62377f3 100644 --- a/contrib/bmake/unit-tests/directive-for-escape.exp +++ b/contrib/bmake/unit-tests/directive-for-escape.exp @@ -2,28 +2,28 @@ For: end for 1 For: loop body: . info ${:U!"#$%&'()*+,-./0-9\:;<=>?@A-Z[\\]_^a-z{|\}~} make: Unclosed variable expression, expecting '}' for modifier "U!"" of variable "" with value "!"" -make: "directive-for-escape.mk" line 19: !" +make: "directive-for-escape.mk" line 21: !" For: end for 1 For: loop body: . info ${:U!"\\\\#$%&'()*+,-./0-9\:;<=>?@A-Z[\\]_^a-z{|\}~} make: Unclosed variable expression, expecting '}' for modifier "U!"\\\\" of variable "" with value "!"\\" -make: "directive-for-escape.mk" line 29: !"\\ +make: "directive-for-escape.mk" line 32: !"\\ For: end for 1 For: loop body: . info ${:U\$} -make: "directive-for-escape.mk" line 43: $ +make: "directive-for-escape.mk" line 47: $ For: loop body: . info ${:U${V}} -make: "directive-for-escape.mk" line 43: value +make: "directive-for-escape.mk" line 47: value For: loop body: . info ${:U${V:=-with-modifier}} -make: "directive-for-escape.mk" line 43: value-with-modifier +make: "directive-for-escape.mk" line 47: value-with-modifier For: loop body: . info ${:U$(V)} -make: "directive-for-escape.mk" line 43: value +make: "directive-for-escape.mk" line 47: value For: loop body: . info ${:U$(V:=-with-modifier)} -make: "directive-for-escape.mk" line 43: value-with-modifier +make: "directive-for-escape.mk" line 47: value-with-modifier For: end for 1 For: loop body: # ${:U\${UNDEF\:U\\$\\$} @@ -34,29 +34,25 @@ For: loop body: For: end for 1 For: loop body: . info ${:U\${UNDEF\:U\\$\\$} -make: "directive-for-escape.mk" line 92: ${UNDEF:U\backslash$ +make: "directive-for-escape.mk" line 101: ${UNDEF:U\backslash$ For: loop body: . info ${:U{{\}\}} -make: "directive-for-escape.mk" line 92: {{}} +make: "directive-for-escape.mk" line 101: {{}} For: loop body: . info ${:Uend\}} -make: "directive-for-escape.mk" line 92: end} +make: "directive-for-escape.mk" line 101: end} For: end for 1 For: loop body: . info ${:Ubegin<${UNDEF:Ufallback:N{{{}}}}>end} -make: "directive-for-escape.mk" line 113: begin<fallback>end +make: "directive-for-escape.mk" line 122: begin<fallback>end For: end for 1 For: loop body: . info ${:U\$} -make: "directive-for-escape.mk" line 121: $ +make: "directive-for-escape.mk" line 131: $ +make: "directive-for-escape.mk" line 140: invalid character ':' in .for loop variable name For: end for 1 -For: loop body: -. info ${NUMBERS} ${:Ureplaced} -make: "directive-for-escape.mk" line 129: one two three replaced +make: "directive-for-escape.mk" line 150: invalid character '}' in .for loop variable name For: end for 1 -For: loop body: -. info ${:Ureplaced} -make: "directive-for-escape.mk" line 139: replaced For: end for 1 For: loop body: . info . $$i: ${:Uinner} @@ -69,46 +65,42 @@ For: loop body: . info . $${i2}: ${i2} . info . $${i,}: ${i,} . info . adjacent: ${:Uinner}${:Uinner}${:Uinner:M*}${:Uinner} -make: "directive-for-escape.mk" line 147: . $i: inner -make: "directive-for-escape.mk" line 148: . ${i}: inner -make: "directive-for-escape.mk" line 149: . ${i:M*}: inner -make: "directive-for-escape.mk" line 150: . $(i): inner -make: "directive-for-escape.mk" line 151: . $(i:M*): inner -make: "directive-for-escape.mk" line 152: . ${i${:U}}: outer -make: "directive-for-escape.mk" line 153: . ${i\}}: inner} -make: "directive-for-escape.mk" line 154: . ${i2}: two -make: "directive-for-escape.mk" line 155: . ${i,}: comma -make: "directive-for-escape.mk" line 156: . 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 164: eight $$$$ and no cents. -make: "directive-for-escape.mk" line 165: eight dollardollardollardollar and no cents. -make: "directive-for-escape.mk" line 174: eight and no cents. -For: end for 1 -make: "directive-for-escape.mk" line 181: newline in .for value -make: "directive-for-escape.mk" line 181: newline in .for value +make: "directive-for-escape.mk" line 159: . $i: inner +make: "directive-for-escape.mk" line 160: . ${i}: inner +make: "directive-for-escape.mk" line 161: . ${i:M*}: inner +make: "directive-for-escape.mk" line 162: . $(i): inner +make: "directive-for-escape.mk" line 163: . $(i:M*): inner +make: "directive-for-escape.mk" line 164: . ${i${:U}}: outer +make: "directive-for-escape.mk" line 165: . ${i\}}: inner} +make: "directive-for-escape.mk" line 166: . ${i2}: two +make: "directive-for-escape.mk" line 167: . ${i,}: comma +make: "directive-for-escape.mk" line 168: . adjacent: innerinnerinnerinner +make: "directive-for-escape.mk" line 187: invalid character '$' in .for loop variable name +For: end for 1 +make: "directive-for-escape.mk" line 199: eight and no cents. +For: end for 1 +make: "directive-for-escape.mk" line 212: newline in .for value +make: "directive-for-escape.mk" line 212: newline in .for value For: loop body: . info short: ${:U" "} . info long: ${:U" "} -make: "directive-for-escape.mk" line 182: short: " " -make: "directive-for-escape.mk" line 183: long: " " +make: "directive-for-escape.mk" line 213: short: " " +make: "directive-for-escape.mk" line 214: long: " " For: end for 1 For: loop body: For: end for 1 -Parse_PushInput: .for loop in directive-for-escape.mk, line 196 -make: "directive-for-escape.mk" line 196: newline in .for value - in .for loop from directive-for-escape.mk:196 with i = " +Parse_PushInput: .for loop in directive-for-escape.mk, line 230 +make: "directive-for-escape.mk" line 230: newline in .for value + in .for loop from directive-for-escape.mk:230 with i = " " For: loop body: : ${:U" "} SetFilenameVars: ${.PARSEDIR} = <some-dir> ${.PARSEFILE} = `directive-for-escape.mk' -Parsing line 197: : ${:U" "} +Parsing line 231: : ${:U" "} ParseDependency(: " ") -ParseEOF: returning to file directive-for-escape.mk, line 199 +ParseEOF: returning to file directive-for-escape.mk, line 233 SetFilenameVars: ${.PARSEDIR} = <some-dir> ${.PARSEFILE} = `directive-for-escape.mk' -Parsing line 199: .MAKEFLAGS: -d0 +Parsing line 233: .MAKEFLAGS: -d0 ParseDependency(.MAKEFLAGS: -d0) For: end for 1 For: loop body: diff --git a/contrib/bmake/unit-tests/directive-for-escape.mk b/contrib/bmake/unit-tests/directive-for-escape.mk index fe704c453046..7fbd09131d2c 100644 --- a/contrib/bmake/unit-tests/directive-for-escape.mk +++ b/contrib/bmake/unit-tests/directive-for-escape.mk @@ -1,9 +1,10 @@ -# $NetBSD: directive-for-escape.mk,v 1.16 2022/06/12 16:09:21 rillig Exp $ +# $NetBSD: directive-for-escape.mk,v 1.18 2023/05/09 19:43:12 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 -# escaping and unescaping must pass all characters and strings effectively -# unmodified. +# escaping and unescaping must pass all characters and strings unmodified. + +# expect-all .MAKEFLAGS: -df @@ -12,12 +13,14 @@ # This could be considered a bug. ASCII= !"\#$$%&'()*+,-./0-9:;<=>?@A-Z[\]_^a-z{|}~ + # XXX: As of 2020-12-31, the '#' is not preserved in the expanded body of # 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 +# expect-2: !" # As of 2020-12-31, using 2 backslashes before be '#' would treat the '#' # as comment character. Using 3 backslashes doesn't help either since @@ -28,6 +31,7 @@ ASCII.2020-12-31= !"\\\#$$%&'()*+,-./0-9:;<=>?@A-Z[\]_^a-z{|}~ .for chars in ${ASCII.2020-12-31} . info ${chars} .endfor +# expect-2: !"\\ # Cover the code in ExprLen. # @@ -42,6 +46,11 @@ VALUES= $$ $${V} $${V:=-with-modifier} $$(V) $$(V:=-with-modifier) .for i in ${VALUES} . info $i .endfor +# expect-2: $ +# expect-3: value +# expect-4: value-with-modifier +# expect-5: value +# expect-6: value-with-modifier # Try to cover the code for nested '{}' in ExprLen, without success. @@ -112,6 +121,7 @@ VALUES= begin<$${UNDEF:Ufallback:N{{{}}}}>end .for i in ${VALUES} . info $i .endfor +# expect-2: begin<fallback>end # A single trailing dollar doesn't happen in practice. # The dollar sign is correctly passed through to the body of the .for loop. @@ -120,21 +130,23 @@ VALUES= begin<$${UNDEF:Ufallback:N{{{}}}}>end .for i in ${:U\$} . info ${i} .endfor +# expect-2: $ -# As of 2020-12-31, the name of the iteration variable can even contain -# colons, which then affects variable expressions having this exact modifier. -# This is clearly an unintended side effect of the implementation. +# Before for.c 1.173 from 2023-05-08, the name of the iteration variable +# could contain colons, which affected variable expressions having this exact +# modifier. This possibility was neither intended nor documented. NUMBERS= one two three +# expect+1: invalid character ':' in .for loop variable name .for NUMBERS:M*e in replaced . info ${NUMBERS} ${NUMBERS:M*e} .endfor -# As of 2020-12-31, the name of the iteration variable can contain braces, -# which gets even more surprising than colons, since it allows to replace -# sequences of variable expressions. There is no practical use case for -# this, though. +# Before for.c 1.173 from 2023-05-08, the name of the iteration variable +# could contain braces, which allowed to replace sequences of variable +# expressions. This possibility was neither intended nor documented. BASENAME= one EXT= .c +# expect+1: invalid character '}' in .for loop variable name .for BASENAME}${EXT in replaced . info ${BASENAME}${EXT} .endfor @@ -155,11 +167,23 @@ i,= comma . info . $${i,}: ${i,} . info . adjacent: $i${i}${i:M*}$i .endfor +# expect-11: . $i: inner +# expect-11: . ${i}: inner +# expect-11: . ${i:M*}: inner +# expect-11: . $(i): inner +# expect-11: . $(i:M*): inner +# expect-11: . ${i${:U}}: outer +# expect-11: . ${i\}}: inner} +# expect-11: . ${i2}: two +# expect-11: . ${i,}: comma +# expect-11: . adjacent: innerinnerinnerinner -# 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'. +# Before for.c 1.173 from 2023-05-08, the variable name could be a single '$' +# since there was no check on valid variable names. ForLoop_SubstVarShort +# skipped "stupid" variable names though, but ForLoop_SubstVarLong naively +# parsed the body of the loop, substituting each '${$}' with an actual +# '${:Udollar}'. +# expect+1: invalid character '$' in .for loop variable name .for $ in dollar . info eight $$$$$$$$ and no cents. . info eight ${$}${$}${$}${$} and no cents. @@ -171,6 +195,7 @@ i,= comma # evaluates to an empty string. closing-brace= } # guard against an ${closing-brace}= <closing-brace> # alternative interpretation +# expect+1: eight and no cents. .info eight ${$}${$}${$}${$} and no cents. # What happens if the values from the .for loop contain a literal newline? @@ -178,10 +203,18 @@ ${closing-brace}= <closing-brace> # alternative interpretation # 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. +# +# The error message occurs in the line of the .for loop since that's the place +# where the body of the .for loop is constructed, and at this point the +# newline character gets replaced with a plain space. +# expect+2: newline in .for value +# expect+1: newline in .for value .for i in "${.newline}" . info short: $i . info long: ${i} .endfor +# expect-3: short: " " +# expect-3: long: " " # No error since the newline character is not actually used. .for i in "${.newline}" @@ -193,6 +226,7 @@ ${closing-brace}= <closing-brace> # alternative interpretation # loop is assembled, and at that point, ForLoop.nextItem had already been # advanced. .MAKEFLAGS: -dp +# expect+1: newline in .for value .for i in "${.newline}" : $i .endfor diff --git a/contrib/bmake/unit-tests/directive-for.exp b/contrib/bmake/unit-tests/directive-for.exp index dda487917e68..97878ee49f44 100755 --- a/contrib/bmake/unit-tests/directive-for.exp +++ b/contrib/bmake/unit-tests/directive-for.exp @@ -1,42 +1,41 @@ -make: "directive-for.mk" line 108: outer -make: "directive-for.mk" line 133: a:\ a:\file.txt -make: "directive-for.mk" line 133: d:\\ -make: "directive-for.mk" line 133: d:\\file.txt -make: "directive-for.mk" line 140: ( ( ( -make: "directive-for.mk" line 140: [ [ [ -make: "directive-for.mk" line 140: { { { -make: "directive-for.mk" line 140: ) ) ) -make: "directive-for.mk" line 140: ] ] ] -make: "directive-for.mk" line 140: } } } -make: "directive-for.mk" line 140: (()) (()) (()) -make: "directive-for.mk" line 140: [[]] [[]] [[]] -make: "directive-for.mk" line 140: {{}} {{}} {{}} -make: "directive-for.mk" line 140: )( )( )( -make: "directive-for.mk" line 140: ][ ][ ][ -make: "directive-for.mk" line 140: }{ }{ }{ -make: "directive-for.mk" line 148: outer value value -make: "directive-for.mk" line 148: outer "quoted" \"quoted\" -make: "directive-for.mk" line 154: Unknown modifier "Z" -make: "directive-for.mk" line 155: XXX: Not reached word1 -make: "directive-for.mk" line 155: XXX: Not reached word3 -make: "directive-for.mk" line 160: no iteration variables in for -make: "directive-for.mk" line 162: Missing argument for ".error" -make: "directive-for.mk" line 163: for-less endfor -make: "directive-for.mk" line 187: 1 open conditional -make: "directive-for.mk" line 203: for-less endfor -make: "directive-for.mk" line 204: if-less endif -make: "directive-for.mk" line 212: if-less endif +make: "directive-for.mk" line 119: outer +make: "directive-for.mk" line 137: a:\ a:\file.txt +make: "directive-for.mk" line 137: d:\\ +make: "directive-for.mk" line 137: d:\\file.txt +make: "directive-for.mk" line 148: ( ( ( +make: "directive-for.mk" line 148: [ [ [ +make: "directive-for.mk" line 148: { { { +make: "directive-for.mk" line 148: ) ) ) +make: "directive-for.mk" line 148: ] ] ] +make: "directive-for.mk" line 148: } } } +make: "directive-for.mk" line 148: (()) (()) (()) +make: "directive-for.mk" line 148: [[]] [[]] [[]] +make: "directive-for.mk" line 148: {{}} {{}} {{}} +make: "directive-for.mk" line 148: )( )( )( +make: "directive-for.mk" line 148: ][ ][ ][ +make: "directive-for.mk" line 148: }{ }{ }{ +make: "directive-for.mk" line 168: invalid character ':' in .for loop variable name +make: "directive-for.mk" line 175: invalid character '$' in .for loop variable name +make: "directive-for.mk" line 187: invalid character '$' in .for loop variable name +make: "directive-for.mk" line 198: Unknown modifier "Z" +make: "directive-for.mk" line 199: XXX: Not reached word1 +make: "directive-for.mk" line 199: XXX: Not reached word3 +make: "directive-for.mk" line 206: no iteration variables in for +make: "directive-for.mk" line 232: 1 open conditional +make: "directive-for.mk" line 248: for-less endfor +make: "directive-for.mk" line 249: if-less endif +make: "directive-for.mk" line 257: if-less endif +For: new loop 2 +For: end for 2 For: end for 1 For: loop body: .\ for inner in i .\ endfor -make: "directive-for.mk" line 229: Unexpected end of file in .for loop +For: end for 1 For: loop body: -.\ - endfor -make: "directive-for.mk" line 227: for-less endfor +make: "directive-for.mk" line 305: newline-item=(a) make: Fatal errors encountered -- cannot continue make: stopped in unit-tests exit status 1 diff --git a/contrib/bmake/unit-tests/directive-for.mk b/contrib/bmake/unit-tests/directive-for.mk index 95171c68031f..224a466a7709 100755 --- a/contrib/bmake/unit-tests/directive-for.mk +++ b/contrib/bmake/unit-tests/directive-for.mk @@ -1,4 +1,4 @@ -# $NetBSD: directive-for.mk,v 1.15 2022/10/01 09:23:04 rillig Exp $ +# $NetBSD: directive-for.mk,v 1.20 2023/05/10 13:03:06 rillig Exp $ # # Tests for the .for directive. # @@ -8,11 +8,15 @@ # .for _FILE_ in values # .for .FILE. in values # .for _f_ in values - -# Using the .for loop, lists of values can be produced. -# In simple cases, the :@var@${var}@ variable modifier can be used to -# achieve the same effects. # +# See also: +# varmod-loop.mk The ':@var@...@' modifier + +# expect-all + +# A typical use case for a .for loop is to populate a variable with a list of +# values depending on other variables. In simple cases, the same effect can +# be achieved using the ':@var@${var}@' modifier. .undef NUMBERS .for num in 1 2 3 NUMBERS+= ${num} @@ -21,8 +25,9 @@ NUMBERS+= ${num} . error .endif + # The .for loop also works for multiple iteration variables. -# This is something that the variable modifier :@ cannot do. +# This is something that the modifier :@ cannot do. .for name value in VARNAME value NAME2 value2 ${name}= ${value} .endfor @@ -30,12 +35,12 @@ ${name}= ${value} . error .endif + # The .for loop splits the items at whitespace, taking quotes into account, -# just like the :M or :S variable modifiers. -# -# Until 2012-06-03, it had split the items exactly at whitespace, without -# taking the quotes into account. This had resulted in 10 words. +# just like the :M or :S modifiers. # +# Until 2012-06-03, the .for loop had split the items exactly at whitespace, +# without taking the quotes into account. This had resulted in 10 words. .undef WORDS .for var in one t\ w\ o "three three" 'four four' `five six` WORDS+= counted @@ -44,16 +49,19 @@ WORDS+= counted . error .endif + # In the body of the .for loop, the iteration variables can be accessed # like normal variables, even though they are not really variables. # -# Instead, the expression ${var} is transformed into ${:U1}, ${:U2} and so -# on, before the loop body is evaluated. +# Instead, before interpreting the body of the .for loop, the body is +# generated by replacing each expression ${var} with ${:U1}, ${:U2} and so +# on. # -# A notable effect of this implementation technique is that the .for +# A noticeable effect of this implementation technique is that the .for # iteration variables and the normal global variables live in separate -# namespaces and do not influence each other. -# +# namespaces and do not influence each other. The "scope" of the .for loop +# variables is restricted to the current makefile, it does not reach over to +# any included makefiles. var= value before var2= value before .for var var2 in 1 2 3 4 @@ -66,9 +74,8 @@ var2= value before .endif # Everything from the paragraph above also applies if the loop body is -# empty, even if there is no actual iteration since the loop items are -# also empty. -# +# empty. In this particular example, the items to be iterated are empty as +# well. var= value before var2= value before .for var var2 in ${:U} @@ -82,11 +89,13 @@ var2= value before # Until 2008-12-21, the values of the iteration variables were simply # inserted as plain text and then parsed as usual, which made it possible -# to achieve all kinds of strange effects. +# to achieve all kinds of strange effects, such as generating '.if' +# directives or inserting '$' characters in random places, thereby changing +# how following '$' are interpreted. # -# Before that date, the .for loop expanded to: +# Before that date, the .for loop below expanded to: # EXPANSION+= value -# Since that date, the .for loop expands to: +# Since that date, the .for loop below expands to: # EXPANSION${:U+}= value # EXPANSION= before @@ -102,13 +111,16 @@ EXPANSION${plus}= value .endif # When the outer .for loop is expanded, it sees the expression ${i} and -# expands it. The inner loop then has nothing more to expand. +# expands it. The inner loop then only sees the expression ${:Uouter} and +# has nothing more to expand. .for i in outer . for i in inner +# expect+1: outer . info ${i} . endfor .endfor + # From https://gnats.netbsd.org/29985. # # Until 2008-12-21, the .for loop was expanded by replacing the variable @@ -121,17 +133,13 @@ EXPANSION${plus}= value # like "a:\ a:\file.txt" that ended in a single backslash. Since then, the # variable values have been replaced with expressions of the form ${:U...}, # which are not interpreted as code anymore. -# -# As of 2020-09-22, a comment in for.c says that it may be possible to -# produce an "unwanted substitution", but there is no demonstration code yet. -# -# The above changes prevent a backslash at the end of a word from being -# interpreted as part of the code. Because of this, the trailingBackslash -# hack in Var_Subst is no longer needed and as of 2020-09-22, has been -# removed. .for path in a:\ a:\file.txt d:\\ d:\\file.txt . info ${path} .endfor +# expect-2: a:\ a:\file.txt +# expect-3: d:\\ +# expect-4: d:\\file.txt + # Ensure that braces and parentheses are properly escaped by the .for loop. # Each line must print the same word 3 times. @@ -139,28 +147,65 @@ EXPANSION${plus}= value .for v in ( [ { ) ] } (()) [[]] {{}} )( ][ }{ . info $v ${v} $(v) .endfor +# expect-02: ( ( ( +# expect-03: [ [ [ +# expect-04: { { { +# expect-05: ) ) ) +# expect-06: ] ] ] +# expect-07: } } } +# expect-08: (()) (()) (()) +# expect-09: [[]] [[]] [[]] +# expect-10: {{}} {{}} {{}} +# expect-11: )( )( )( +# expect-12: ][ ][ ][ +# expect-13: }{ }{ }{ -# As of 2020-10-25, the variable names may contain arbitrary characters, -# except for whitespace. This allows for creative side effects. Hopefully -# nobody is misusing this "feature". +# Before 2023-05-09, the variable names could contain arbitrary characters, +# except for whitespace, allowing for creative side effects, as usual for +# arbitrary code injection. var= outer +# expect+1: invalid character ':' in .for loop variable name .for var:Q in value "quoted" -. info ${var} ${var:Q} ${var:Q:Q} +. info <${var}> <${var:Q}> <${var:Q:Q}> +.endfor + +# Before 2023-05-09, when variable names could contain '$', the short +# expression '$$' was preserved, the long expressions were substituted. +# expect+1: invalid character '$' in .for loop variable name +.for $ in value +. info <$$> <${$}> <$($)> +.endfor + + +# https://gnats.netbsd.org/53146 mentions the idea of using a dynamic +# variable name in .for loops, based on some other variable. The .for loops +# are already tricky enough to understand in detail, even without this +# possibility, therefore the variable names are restricted to using harmless +# characters only. +INDIRECT= direct +# expect+1: invalid character '$' in .for loop variable name +.for $(INDIRECT) in value +# If the variable name could be chosen dynamically, the iteration variable +# might have been 'direct', thereby expanding the expression '${direct}'. +. info <$(INDIRECT)> <$(direct)> <$($(INDIRECT))> .endfor # XXX: A parse error or evaluation error in the items of the .for loop -# should skip the whole loop. As of 2020-12-27, the loop is expanded twice. +# should skip the whole loop. As of 2023-05-09, the loop is expanded as +# usual. +# expect+1: Unknown modifier "Z" .for var in word1 ${:Uword2:Z} word3 . info XXX: Not reached ${var} .endfor +# expect-2: XXX: Not reached word1 +# expect-3: XXX: Not reached word3 # An empty list of variables to the left of the 'in' is a parse error. .for in value # expect+0: no iteration variables in for -# XXX: The loop body is evaluated once, even with the parse error above. -. error # expect+0: Missing argument for ".error" -.endfor # expect+0: for-less endfor +. error +.endfor # An empty list of iteration values to the right of the 'in' is accepted. # Unlike in the shell, it is not a parse error. @@ -214,12 +259,19 @@ var= outer .endif # no 'if-less endif' -# When make parses a .for loop, it assumes that there is no line break between -# the '.' and the 'for' or 'endfor', as there is no practical reason to break -# the line at this point. When make scans the outer .for loop, it does not -# recognize the inner directives as such. When make scans the inner .for -# loop, it recognizes the '.\n for' but does not recognize the '.\n endfor', -# as LK_FOR_BODY preserves the backslash-newline sequences. +# Before for.c 1.172 from 2023-05-08, when make parsed a .for loop, it +# assumed that there was no line continuation between the '.' and the 'for' +# or 'endfor', as there is no practical reason to break the line at this +# point. +# +# When make scanned the outer .for loop, it did not recognize the inner .for +# loop as such and instead treated it as an unknown directive. The body of +# the outer .for loop thus ended above the '.endfor'. +# +# When make scanned the inner .for loop, it did not recognize the inner +# .endfor as such, which led to a parse error 'Unexpected end of file in .for +# loop' from the '.endfor' line, followed by a second parse error 'for-less +# .endfor' from the '.\\n endfor' line. .MAKEFLAGS: -df .for outer in o .\ @@ -244,3 +296,12 @@ var= outer . error . endif .endfor + + +# Since at least 1993, iteration stops at the first newline. +# Back then, the .newline variable didn't exist, therefore it was unlikely +# that a newline ever occurred. +.for var in a${.newline}b${.newline}c +. info newline-item=(${var}) +.endfor +# expect-2: newline-item=(a) diff --git a/contrib/bmake/unit-tests/forloop.exp b/contrib/bmake/unit-tests/forloop.exp deleted file mode 100644 index 422711b41247..000000000000 --- a/contrib/bmake/unit-tests/forloop.exp +++ /dev/null @@ -1,20 +0,0 @@ -make: "forloop.mk" line 14: x=one -make: "forloop.mk" line 14: x="two and three" -make: "forloop.mk" line 14: x=four -make: "forloop.mk" line 14: x="five" -make: "forloop.mk" line 20: x=-I/this -make: "forloop.mk" line 20: x=-I"This or that" -make: "forloop.mk" line 20: x=-Ithat -make: "forloop.mk" line 20: x="-DTHIS=\"this and that\"" -make: "forloop.mk" line 27: cfl=-I/this -I"This or that" -Ithat "-DTHIS=\"this and that\"" -make: "forloop.mk" line 41: newline-item=(a) -make: "forloop.mk" line 47: a=one b="two and three" -make: "forloop.mk" line 47: a=four b="five" -make: "forloop.mk" line 47: a=ONE b="TWO AND THREE" -make: "forloop.mk" line 47: a=FOUR b="FIVE" -We expect an error next: -make: "forloop.mk" line 46: Wrong number of words (9) in .for substitution list with 2 variables -make: Fatal errors encountered -- cannot continue -make: stopped in unit-tests -OK -exit status 0 diff --git a/contrib/bmake/unit-tests/forloop.mk b/contrib/bmake/unit-tests/forloop.mk deleted file mode 100644 index cef05cbe4c61..000000000000 --- a/contrib/bmake/unit-tests/forloop.mk +++ /dev/null @@ -1,53 +0,0 @@ -# $NetBSD: forloop.mk,v 1.7 2020/11/03 17:37:57 rillig Exp $ - -all: for-loop - -LIST= one "two and three" four "five" - -.if make(for-fail) -for-fail: - -XTRA_LIST= xtra -.else - -. for x in ${LIST} -. info x=$x -. endfor - -CFL= -I/this -I"This or that" -Ithat "-DTHIS=\"this and that\"" -cfl= -. for x in ${CFL} -. info x=$x -. if empty(cfl) -cfl= $x -. else -cfl+= $x -. endif -. endfor -. info cfl=${cfl} - -. if ${cfl} != ${CFL} -. error ${.newline}${cfl} != ${.newline}${CFL} -. endif - -. for a b in ${EMPTY} -. info a=$a b=$b -. endfor - -# Since at least 1993, iteration stops at the first newline. -# Back then, the .newline variable didn't exist, therefore it was unlikely -# that a newline ever occurred. -. for var in a${.newline}b${.newline}c -. info newline-item=(${var}) -. endfor - -.endif # for-fail - -.for a b in ${LIST} ${LIST:tu} ${XTRA_LIST} -. info a=$a b=$b -.endfor - -for-loop: - @echo We expect an error next: - @(cd ${.CURDIR} && ${.MAKE} -f ${MAKEFILE} for-fail) && \ - { echo "Oops that should have failed!"; exit 1; } || echo OK diff --git a/contrib/bmake/unit-tests/parse.mk b/contrib/bmake/unit-tests/parse.mk index 986c303083bf..551dc98aaf24 100644 --- a/contrib/bmake/unit-tests/parse.mk +++ b/contrib/bmake/unit-tests/parse.mk @@ -1,4 +1,4 @@ -# $NetBSD: parse.mk,v 1.3 2022/07/24 20:25:23 rillig Exp $ +# $NetBSD: parse.mk,v 1.4 2023/04/28 13:09:48 rillig Exp $ # # Test those parts of the parsing that do not belong in any of the other # categories. @@ -22,3 +22,33 @@ # # https://bugs.freebsd.org/265119 one-target ${:U } + + +# Since parse.c 1.656 from 2022-01-27 and before parse.c 1.662 from +# 2022-02-05, there was an out-of-bounds read in Parse_IsVar when looking for +# a variable assignment in a dependency line with trailing whitespace. Lines +# without trailing whitespace were not affected. Global variable assignments +# were guaranteed to have no trailing whitespace and were thus not affected. +# +# Try to reproduce some variants that may lead to a crash, depending on the +# memory allocator. To get a crash, the terminating '\0' of the line must be +# the last byte of a memory page. The expression '${:U}' forces this trailing +# whitespace. + +# On FreeBSD x86_64, a crash could in some cases be forced using the following +# line, which has length 47, and if the memory for the expanded line starts at +# 0xXXXX_XXd0, the terminating '\0' may end up at 0xXXXX_Xfff: +Try_to_crash_FreeBSD.xxxxxxxxxxxxxxxxxx: 12345 ${:U} + +# The following line has length 4095 after being expanded, so line[4095] == +# '\0'. If the line is +# allocated on a page boundary and the following page is not mapped, this line +# leads to a segmentation fault. +${:U:range=511:@_@1234567@:ts.}: 12345 ${:U} + +# The following line has length 8191, so line[8191] == '\0'. If the line is +# allocated on a page boundary and the following page is not mapped, this line +# leads to a segmentation fault. +${:U:range=1023:@_@1234567@:ts.}: 12345 ${:U} + +12345: diff --git a/contrib/bmake/unit-tests/var-eval-short.exp b/contrib/bmake/unit-tests/var-eval-short.exp index 34380ec61c41..ea6e0d7485f9 100644 --- a/contrib/bmake/unit-tests/var-eval-short.exp +++ b/contrib/bmake/unit-tests/var-eval-short.exp @@ -1,9 +1,5 @@ make: "var-eval-short.mk" line 44: In the :@ modifier of "", the variable name "${FAIL}" must not contain a dollar make: "var-eval-short.mk" line 44: Malformed conditional (0 && ${:Uword:@${FAIL}@expr@}) -make: "var-eval-short.mk" line 84: Invalid time value at "${FAIL}}" -make: "var-eval-short.mk" line 84: Malformed conditional (0 && ${:Uword:gmtime=${FAIL}}) -make: "var-eval-short.mk" line 98: Invalid time value at "${FAIL}}" -make: "var-eval-short.mk" line 98: 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:?...} @@ -12,7 +8,7 @@ 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 +Parsing line 165: DEFINED= defined Global: DEFINED = defined CondParser_Eval: 0 && ${DEFINED:L:?${FAIL}then:${FAIL}else} Var_Parse: ${DEFINED:L:?${FAIL}then:${FAIL}else} (parse-only) @@ -24,7 +20,7 @@ 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 +Parsing line 168: .MAKEFLAGS: -d0 ParseDependency(.MAKEFLAGS: -d0) Global: .MAKEFLAGS = -r -k -d cpv -d Global: .MAKEFLAGS = -r -k -d cpv -d 0 diff --git a/contrib/bmake/unit-tests/var-eval-short.mk b/contrib/bmake/unit-tests/var-eval-short.mk index a099b6871d1e..7faf1cc5d154 100644 --- a/contrib/bmake/unit-tests/var-eval-short.mk +++ b/contrib/bmake/unit-tests/var-eval-short.mk @@ -1,4 +1,4 @@ -# $NetBSD: var-eval-short.mk,v 1.8 2021/12/27 18:54:19 rillig Exp $ +# $NetBSD: var-eval-short.mk,v 1.9 2023/05/09 16:27:00 rillig Exp $ # # Tests for each variable modifier to ensure that they only do the minimum # necessary computations. If the result of the expression is irrelevant, @@ -79,8 +79,9 @@ DEFINED= # defined .if 0 && ${:Uword:E} .endif -# As of 2021-03-14, the error 'Invalid time value: ${FAIL}}' is ok since -# ':gmtime' does not expand its argument. +# Before var.c 1.1050 from 2023-05-09, the ':gmtime' modifier produced the +# error message 'Invalid time value: ${FAIL}}' since it did not expand its +# argument. .if 0 && ${:Uword:gmtime=${FAIL}} .endif @@ -93,8 +94,9 @@ DEFINED= # defined .if 0 && ${value:L} .endif -# As of 2021-03-14, the error 'Invalid time value: ${FAIL}}' is ok since -# ':localtime' does not expand its argument. +# Before var.c 1.1050 from 2023-05-09, the ':localtime' modifier produced the +# error message 'Invalid time value: ${FAIL}}' since it did not expand its +# argument. .if 0 && ${:Uword:localtime=${FAIL}} .endif diff --git a/contrib/bmake/unit-tests/var-scope-local.exp b/contrib/bmake/unit-tests/var-scope-local.exp index 403bf83884f7..a9a947a620c7 100644 --- a/contrib/bmake/unit-tests/var-scope-local.exp +++ b/contrib/bmake/unit-tests/var-scope-local.exp @@ -1,5 +1,5 @@ -Global: .ALLTARGETS = one -Global: .ALLTARGETS = one two +Global: .ALLTARGETS = all target-rule.ext dir/subdir/target-rule.ext target-rule.ir-gen-from dir/subdir/target-rule-dir.ir-gen-from inference-rule.ir-to dir/subdir/inference-rule.ir-to inference-rule.ir-from dir/subdir/inference-rule.ir-from inference-rule-chain.ir-to dir/subdir/inference-rule-chain.ir-to inference-rule-chain.ir-gen-from dir/subdir/inference-rule-chain.ir-gen-from one +Global: .ALLTARGETS = all target-rule.ext dir/subdir/target-rule.ext target-rule.ir-gen-from dir/subdir/target-rule-dir.ir-gen-from inference-rule.ir-to dir/subdir/inference-rule.ir-to inference-rule.ir-from dir/subdir/inference-rule.ir-from inference-rule-chain.ir-to dir/subdir/inference-rule-chain.ir-to inference-rule-chain.ir-gen-from dir/subdir/inference-rule-chain.ir-gen-from one two Var_Parse: ${.MAKE.TARGET_LOCAL_VARIABLES} (eval) Var_SetExpand: variable name "" expands to empty string, with value "three" - ignored Var_SetExpand: variable name "" expands to empty string, with value "three" - ignored @@ -7,6 +7,56 @@ Global: one two = # (empty) Global: one two = three Global: .MAKEFLAGS = -r -k -d v -d Global: .MAKEFLAGS = -r -k -d v -d 0 +target-rule.ext: @ = <target-rule.ext> +target-rule.ext: % = <undefined> +target-rule.ext: ? = <> +target-rule.ext: < = <undefined> +target-rule.ext: * = <target-rule.ext> +dir/subdir/target-rule.ext: @ = <dir/subdir/target-rule.ext> +dir/subdir/target-rule.ext: % = <undefined> +dir/subdir/target-rule.ext: ? = <> +dir/subdir/target-rule.ext: < = <undefined> +dir/subdir/target-rule.ext: * = <dir/subdir/target-rule.ext> +target-rule.ir-gen-from: @ = <target-rule.ir-gen-from> +target-rule.ir-gen-from: % = <undefined> +target-rule.ir-gen-from: ? = <> +target-rule.ir-gen-from: < = <undefined> +target-rule.ir-gen-from: * = <target-rule> +dir/subdir/target-rule-dir.ir-gen-from: @ = <dir/subdir/target-rule-dir.ir-gen-from> +dir/subdir/target-rule-dir.ir-gen-from: % = <undefined> +dir/subdir/target-rule-dir.ir-gen-from: ? = <> +dir/subdir/target-rule-dir.ir-gen-from: < = <undefined> +dir/subdir/target-rule-dir.ir-gen-from: * = <dir/subdir/target-rule-dir> +inference-rule.ir-to: @ = <inference-rule.ir-to> +inference-rule.ir-to: % = <undefined> +inference-rule.ir-to: ? = <inference-rule.ir-from> +inference-rule.ir-to: < = <inference-rule.ir-from> +inference-rule.ir-to: * = <inference-rule> +dir/subdir/inference-rule.ir-to: @ = <dir/subdir/inference-rule.ir-to> +dir/subdir/inference-rule.ir-to: % = <undefined> +dir/subdir/inference-rule.ir-to: ? = <dir/subdir/inference-rule.ir-from> +dir/subdir/inference-rule.ir-to: < = <dir/subdir/inference-rule.ir-from> +dir/subdir/inference-rule.ir-to: * = <dir/subdir/inference-rule> +inference-rule-chain.ir-from: @ = <inference-rule-chain.ir-from> +inference-rule-chain.ir-from: % = <undefined> +inference-rule-chain.ir-from: ? = <inference-rule-chain.ir-gen-from> +inference-rule-chain.ir-from: < = <inference-rule-chain.ir-gen-from> +inference-rule-chain.ir-from: * = <inference-rule-chain> +inference-rule-chain.ir-to: @ = <inference-rule-chain.ir-to> +inference-rule-chain.ir-to: % = <undefined> +inference-rule-chain.ir-to: ? = <inference-rule-chain.ir-from> +inference-rule-chain.ir-to: < = <inference-rule-chain.ir-from> +inference-rule-chain.ir-to: * = <inference-rule-chain> +dir/subdir/inference-rule-chain.ir-from: @ = <dir/subdir/inference-rule-chain.ir-from> +dir/subdir/inference-rule-chain.ir-from: % = <undefined> +dir/subdir/inference-rule-chain.ir-from: ? = <dir/subdir/inference-rule-chain.ir-gen-from> +dir/subdir/inference-rule-chain.ir-from: < = <dir/subdir/inference-rule-chain.ir-gen-from> +dir/subdir/inference-rule-chain.ir-from: * = <dir/subdir/inference-rule-chain> +dir/subdir/inference-rule-chain.ir-to: @ = <dir/subdir/inference-rule-chain.ir-to> +dir/subdir/inference-rule-chain.ir-to: % = <undefined> +dir/subdir/inference-rule-chain.ir-to: ? = <dir/subdir/inference-rule-chain.ir-from> +dir/subdir/inference-rule-chain.ir-to: < = <dir/subdir/inference-rule-chain.ir-from> +dir/subdir/inference-rule-chain.ir-to: * = <dir/subdir/inference-rule-chain> : Making var-scope-local.c out of nothing. : Making var-scope-local.o from var-scope-local.c. : Making basename "var-scope-local.o" in "." from "var-scope-local.c" in ".". diff --git a/contrib/bmake/unit-tests/var-scope-local.mk b/contrib/bmake/unit-tests/var-scope-local.mk index ed1362444504..2cfeda044c40 100644 --- a/contrib/bmake/unit-tests/var-scope-local.mk +++ b/contrib/bmake/unit-tests/var-scope-local.mk @@ -1,4 +1,4 @@ -# $NetBSD: var-scope-local.mk,v 1.5 2022/02/09 21:09:24 rillig Exp $ +# $NetBSD: var-scope-local.mk,v 1.7 2023/04/29 10:16:24 rillig Exp $ # # Tests for target-local variables, such as ${.TARGET} or $@. These variables # are relatively short-lived as they are created just before making the @@ -12,6 +12,64 @@ .MAIN: all +# Target-local variables in a target rule +# +# In target rules, '$*' only strips the extension off the pathname if the +# extension is listed in '.SUFFIXES'. +# +# expect: target-rule.ext: * = <target-rule.ext> +all: target-rule.ext dir/subdir/target-rule.ext +target-rule.ext dir/subdir/target-rule.ext: .PHONY + @echo '$@: @ = <${@:Uundefined}>' + @echo '$@: % = <${%:Uundefined}>' + @echo '$@: ? = <${?:Uundefined}>' + @echo '$@: < = <${<:Uundefined}>' + @echo '$@: * = <${*:Uundefined}>' + +.SUFFIXES: .ir-gen-from .ir-from .ir-to + +# In target rules, '$*' strips the extension off the pathname of the target +# if the extension is listed in '.SUFFIXES'. +# +# expect: target-rule.ir-gen-from: * = <target-rule> +all: target-rule.ir-gen-from dir/subdir/target-rule-dir.ir-gen-from +target-rule.ir-gen-from dir/subdir/target-rule-dir.ir-gen-from: + @echo '$@: @ = <${@:Uundefined}>' + @echo '$@: % = <${%:Uundefined}>' + @echo '$@: ? = <${?:Uundefined}>' + @echo '$@: < = <${<:Uundefined}>' + @echo '$@: * = <${*:Uundefined}>' + +.ir-from.ir-to: + @echo '$@: @ = <${@:Uundefined}>' + @echo '$@: % = <${%:Uundefined}>' + @echo '$@: ? = <${?:Uundefined}>' + @echo '$@: < = <${<:Uundefined}>' + @echo '$@: * = <${*:Uundefined}>' +.ir-gen-from.ir-from: + @echo '$@: @ = <${@:Uundefined}>' + @echo '$@: % = <${%:Uundefined}>' + @echo '$@: ? = <${?:Uundefined}>' + @echo '$@: < = <${<:Uundefined}>' + @echo '$@: * = <${*:Uundefined}>' + +# Target-local variables in an inference rule +all: inference-rule.ir-to dir/subdir/inference-rule.ir-to +inference-rule.ir-from: .PHONY +dir/subdir/inference-rule.ir-from: .PHONY + +# Target-local variables in a chain of inference rules +all: inference-rule-chain.ir-to dir/subdir/inference-rule-chain.ir-to +inference-rule-chain.ir-gen-from: .PHONY +dir/subdir/inference-rule-chain.ir-gen-from: .PHONY + +# The run-time 'check' directives from above happen after the parse-time +# 'check' directives from below. +# +# expect-reset + +# Deferred evaluation during parsing +# # The target-local variables can be used in expressions, just like other # variables. When these expressions are evaluated outside of a target, these # expressions are not yet expanded, instead their text is preserved, to allow @@ -20,8 +78,8 @@ # # Conditions from .if directives are evaluated in the scope of the command # line, which means that variables from the command line, from the global -# scope and from the environment are resolved, in this order (but see the -# command line option '-e'). In that phase, expressions involving +# scope and from the environment are resolved, in this precedence order (but +# see the command line option '-e'). In that phase, expressions involving # target-local variables need to be preserved, including the exact names of # the variables. # @@ -77,13 +135,17 @@ .endif +# Custom local variables +# # Additional target-local variables may be defined in dependency lines. .MAKEFLAGS: -dv # In the following line, the ':=' may either be interpreted as an assignment # operator or as the dependency operator ':', followed by an empty variable # name and the assignment operator '='. It is the latter since in an -# assignment, the left-hand side must be at most a single word. The empty -# variable name is expanded twice, once for 'one' and once for 'two'. +# assignment, the left-hand side must be a single word or empty. +# +# The empty variable name is expanded twice, once for 'one' and once for +# 'two'. # expect: Var_SetExpand: variable name "" expands to empty string, with value "three" - ignored # expect: Var_SetExpand: variable name "" expands to empty string, with value "three" - ignored one two:=three @@ -205,32 +267,3 @@ a_use: .USE VAR=use all: var-scope-local-use.o var-scope-local-use.o: a_use - - -# Since parse.c 1.656 from 2022-01-27 and before parse.c 1.662 from -# 2022-02-05, there was an out-of-bounds read in Parse_IsVar when looking for -# a variable assignment in a dependency line with trailing whitespace. Lines -# without trailing whitespace were not affected. Global variable assignments -# were guaranteed to have no trailing whitespace and were thus not affected. -# -# Try to reproduce some variants that may lead to a crash, depending on the -# memory allocator. To get a crash, the terminating '\0' of the line must be -# the last byte of a memory page. The expression '${:U}' forces this trailing -# whitespace. - -# On FreeBSD x86_64, a crash could in some cases be forced using the following -# line, which has length 47, so the terminating '\0' may end up at an address -# of the form 0xXXXX_XXXX_XXXX_Xfff: -Try_to_crash_FreeBSD.xxxxxxxxxxxxxxxxxx: 12345 ${:U} - -# The following line has length 4095, so line[4095] == '\0'. If the line is -# allocated on a page boundary and the following page is not mapped, this line -# leads to a segmentation fault. -${:U:range=511:@_@1234567@:ts.}: 12345 ${:U} - -# The following line has length 8191, so line[8191] == '\0'. If the line is -# allocated on a page boundary and the following page is not mapped, this line -# leads to a segmentation fault. -${:U:range=1023:@_@1234567@:ts.}: 12345 ${:U} - -12345: diff --git a/contrib/bmake/unit-tests/varmod-gmtime.exp b/contrib/bmake/unit-tests/varmod-gmtime.exp index fdc9a2170e2f..916e471c462b 100644 --- a/contrib/bmake/unit-tests/varmod-gmtime.exp +++ b/contrib/bmake/unit-tests/varmod-gmtime.exp @@ -1,13 +1,13 @@ -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 at "-1} != """ -make: "varmod-gmtime.mk" line 67: Malformed conditional (${:L:gmtime=-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 at "10000000000000000000000000000000} != """ -make: "varmod-gmtime.mk" line 119: Malformed conditional (${:L:gmtime=10000000000000000000000000000000} != "") -make: "varmod-gmtime.mk" line 130: Invalid time value at "error} != """ -make: "varmod-gmtime.mk" line 130: Malformed conditional (${:L:gmtime=error} != "") +make: "varmod-gmtime.mk" line 59: Invalid time value "-1" +make: "varmod-gmtime.mk" line 59: Malformed conditional (${:L:gmtime=-1} != "") +make: "varmod-gmtime.mk" line 68: Invalid time value " 1" +make: "varmod-gmtime.mk" line 68: Malformed conditional (${:L:gmtime= 1} != "") +make: "varmod-gmtime.mk" line 114: Invalid time value "10000000000000000000000000000000" +make: "varmod-gmtime.mk" line 114: Malformed conditional (${:L:gmtime=10000000000000000000000000000000} != "") +make: "varmod-gmtime.mk" line 125: Invalid time value "error" +make: "varmod-gmtime.mk" line 125: Malformed conditional (${:L:gmtime=error} != "") +make: "varmod-gmtime.mk" line 134: Invalid time value "100000S,1970,bad," +make: "varmod-gmtime.mk" line 134: Malformed conditional (${%Y:L:gmtime=100000S,1970,bad,} != "bad") make: Fatal errors encountered -- cannot continue make: stopped in unit-tests exit status 1 diff --git a/contrib/bmake/unit-tests/varmod-gmtime.mk b/contrib/bmake/unit-tests/varmod-gmtime.mk index cb3d4e7eb241..639c95b24f6b 100644 --- a/contrib/bmake/unit-tests/varmod-gmtime.mk +++ b/contrib/bmake/unit-tests/varmod-gmtime.mk @@ -1,7 +1,10 @@ -# $NetBSD: varmod-gmtime.mk,v 1.10 2021/01/19 05:26:34 rillig Exp $ +# $NetBSD: varmod-gmtime.mk,v 1.14 2023/05/10 15:53:32 rillig Exp $ # # Tests for the :gmtime variable modifier, which formats a timestamp # using strftime(3) in UTC. +# +# See also: +# varmod-localtime.mk .if ${TZ:Uundefined} != "undefined" # see unit-tests/Makefile . error @@ -41,20 +44,9 @@ .endif -# As of 2020-08-16, it is not possible to pass the seconds via a -# variable expression. This is because parsing of the :gmtime -# modifier stops at the '$' and returns to ApplyModifiers. -# -# There, a colon would be skipped but not a dollar. -# Parsing therefore continues at the '$' of the ${:U159...}, looking -# for an ordinary variable modifier. -# -# At this point, the ${:U} is expanded and interpreted as a variable -# modifier, which results in the error message "Unknown modifier '1'". -# -# If ApplyModifier_Gmtime were to pass its argument through -# ParseModifierPart, this would work. -.if ${%Y:L:gmtime=${:U1593536400}} != "mtime=11593536400}" +# Before var.c 1.1050 from 2023-05-09, it was not possible to pass the +# seconds via a variable expression. +.if ${%Y:L:gmtime=${:U1593536400}} != "2020" . error .endif @@ -75,6 +67,8 @@ # because it would make sense but just as a side-effect from using strtoul. .if ${:L:gmtime= 1} != "" . error +.else +. error .endif @@ -115,7 +109,8 @@ # ULONG_MAX, which got converted to -1. This resulted in a time stamp of # the second before 1970. # -# Since var.c 1.631, the overflow is detected and produces a parse error. +# Since var.c 1.631 from 2020-10-31, the overflow is detected and produces a +# parse error. .if ${:L:gmtime=10000000000000000000000000000000} != "" . error .else @@ -133,5 +128,11 @@ . error .endif +# Before var.c 1.1050 from 2023-05-09, the timestamp could be directly +# followed by the next modifier, without a ':' separator. This was the same +# bug as for the ':L' and ':P' modifiers. +.if ${%Y:L:gmtime=100000S,1970,bad,} != "bad" +. error +.endif all: diff --git a/contrib/bmake/unit-tests/varmod-localtime.exp b/contrib/bmake/unit-tests/varmod-localtime.exp index 494f160b766e..c734c234f083 100644 --- a/contrib/bmake/unit-tests/varmod-localtime.exp +++ b/contrib/bmake/unit-tests/varmod-localtime.exp @@ -1,13 +1,13 @@ -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 at "-1} != """ -make: "varmod-localtime.mk" line 67: Malformed conditional (${:L:localtime=-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 at "10000000000000000000000000000000} != """ -make: "varmod-localtime.mk" line 119: Malformed conditional (${:L:localtime=10000000000000000000000000000000} != "") -make: "varmod-localtime.mk" line 130: Invalid time value at "error} != """ -make: "varmod-localtime.mk" line 130: Malformed conditional (${:L:localtime=error} != "") +make: "varmod-localtime.mk" line 59: Invalid time value "-1" +make: "varmod-localtime.mk" line 59: Malformed conditional (${:L:localtime=-1} != "") +make: "varmod-localtime.mk" line 68: Invalid time value " 1" +make: "varmod-localtime.mk" line 68: Malformed conditional (${:L:localtime= 1} != "") +make: "varmod-localtime.mk" line 114: Invalid time value "10000000000000000000000000000000" +make: "varmod-localtime.mk" line 114: Malformed conditional (${:L:localtime=10000000000000000000000000000000} != "") +make: "varmod-localtime.mk" line 125: Invalid time value "error" +make: "varmod-localtime.mk" line 125: Malformed conditional (${:L:localtime=error} != "") +make: "varmod-localtime.mk" line 134: Invalid time value "100000S,1970,bad," +make: "varmod-localtime.mk" line 134: Malformed conditional (${%Y:L:localtime=100000S,1970,bad,} != "bad") make: Fatal errors encountered -- cannot continue make: stopped in unit-tests exit status 1 diff --git a/contrib/bmake/unit-tests/varmod-localtime.mk b/contrib/bmake/unit-tests/varmod-localtime.mk index ffa09a0bc5fc..7d66745aae8e 100644 --- a/contrib/bmake/unit-tests/varmod-localtime.mk +++ b/contrib/bmake/unit-tests/varmod-localtime.mk @@ -1,7 +1,10 @@ -# $NetBSD: varmod-localtime.mk,v 1.8 2021/01/19 05:26:34 rillig Exp $ +# $NetBSD: varmod-localtime.mk,v 1.12 2023/05/10 15:53:32 rillig Exp $ # # Tests for the :localtime variable modifier, which formats a timestamp # using strftime(3) in local time. +# +# See also: +# varmod-gmtime.mk .if ${TZ:Uno:NEurope/Berlin:NUTC-1} != "" # see unit-tests/Makefile . error @@ -41,20 +44,9 @@ .endif -# As of 2020-08-16, it is not possible to pass the seconds via a -# variable expression. This is because parsing of the :localtime -# modifier stops at the '$' and returns to ApplyModifiers. -# -# There, a colon would be skipped but not a dollar. -# Parsing therefore continues at the '$' of the ${:U159...}, looking -# for an ordinary variable modifier. -# -# At this point, the ${:U} is expanded and interpreted as a variable -# modifier, which results in the error message "Unknown modifier '1'". -# -# If ApplyModifier_Localtime were to pass its argument through -# ParseModifierPart, this would work. -.if ${%Y:L:localtime=${:U1593536400}} != "mtime=11593536400}" +# Before var.c 1.1050 from 2023-05-09, it was not possible to pass the +# seconds via a variable expression. +.if ${%Y:L:localtime=${:U1593536400}} != "2020" . error .endif @@ -75,6 +67,8 @@ # because it would make sense but just as a side-effect from using strtoul. .if ${:L:localtime= 1} != "" . error +.else +. error .endif @@ -115,7 +109,8 @@ # ULONG_MAX, which got converted to -1. This resulted in a time stamp of # the second before 1970. # -# Since var.c 1.631, the overflow is detected and produces a parse error. +# Since var.c 1.631 from 2020-10-31, the overflow is detected and produces a +# parse error. .if ${:L:localtime=10000000000000000000000000000000} != "" . error .else @@ -133,5 +128,11 @@ . error .endif +# Before var.c 1.1050 from 2023-05-09, the timestamp could be directly +# followed by the next modifier, without a ':' separator. This was the same +# bug as for the ':L' and ':P' modifiers. +.if ${%Y:L:localtime=100000S,1970,bad,} != "bad" +. error +.endif all: diff --git a/contrib/bmake/unit-tests/varmod-mtime.exp b/contrib/bmake/unit-tests/varmod-mtime.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/contrib/bmake/unit-tests/varmod-mtime.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/contrib/bmake/unit-tests/varmod-mtime.mk b/contrib/bmake/unit-tests/varmod-mtime.mk new file mode 100644 index 000000000000..dd57ef3f0f31 --- /dev/null +++ b/contrib/bmake/unit-tests/varmod-mtime.mk @@ -0,0 +1,30 @@ +# $NetBSD: varmod-mtime.mk,v 1.1 2023/05/09 20:14:27 sjg Exp $ +# +# Tests for the :mtime variable modifier, which provides mtime +# of variable value assumed to be a pathname. + +all: + +# mtime of this makefile +mtime:= ${MAKEFILE:mtime} + +# if pathname does not exist and timestamp is provided +# that is the result +.if ${no/such:L:mtime=0} != "0" +. error +.endif + +.if ${no/such:L:mtime=42} != "42" +. error +.endif + +# if no timestamp is provided and stat(2) fails use current time +.if ${no/such:L:mtime} < ${mtime} +. error no/such:L:mtime ${no/such:L:mtime} < ${mtime} +.endif + +COOKIE = ${TMPDIR}/varmod-mtime.cookie +x!= touch ${COOKIE} +.if ${COOKIE:mtime=0} < ${mtime} +. error COOKIE:mtime=0 ${COOKIE:mtime=0} < ${mtime} +.endif diff --git a/contrib/bmake/unit-tests/varmod-path.mk b/contrib/bmake/unit-tests/varmod-path.mk index ebbf755ddbec..25d4e3899b99 100644 --- a/contrib/bmake/unit-tests/varmod-path.mk +++ b/contrib/bmake/unit-tests/varmod-path.mk @@ -1,4 +1,4 @@ -# $NetBSD: varmod-path.mk,v 1.3 2020/08/23 08:10:49 rillig Exp $ +# $NetBSD: varmod-path.mk,v 1.4 2023/05/10 15:53:32 rillig Exp $ # # Tests for the :P variable modifier, which looks up the path for a given # target. @@ -7,11 +7,12 @@ # as of 2020-08-23 it is nevertheless resolved to a path. This is probably # unintended. # -# The real target is located in a subdirectory, and its full path is returned. -# If it had been in the current directory, the difference between its path and -# its name would not be visible. +# In this test, the real target is located in a subdirectory, and its full +# path is returned. If it had been in the current directory, the difference +# between its path and its name would not be visible. # -# The enoent target does not exist, therefore the target name is returned. +# The enoent target does not exist, therefore the plain name of the target +# is returned. .MAIN: all @@ -20,7 +21,8 @@ _!= mkdir varmod-path.subdir _!= > varmod-path.subdir/varmod-path.phony _!= > varmod-path.subdir/varmod-path.real -# To have an effect, this .PATH declaration must be after the directory is created. +# To have an effect, this .PATH declaration must be processed after the +# directory has been created. .PATH: varmod-path.subdir varmod-path.phony: .PHONY |