summaryrefslogtreecommitdiff
path: root/usr.bin/make
diff options
context:
space:
mode:
Diffstat (limited to 'usr.bin/make')
-rw-r--r--usr.bin/make/unit-tests/check-expect.lua5
-rw-r--r--usr.bin/make/unit-tests/var-scope-local.exp25
-rw-r--r--usr.bin/make/unit-tests/var-scope-local.mk192
3 files changed, 184 insertions, 38 deletions
diff --git a/usr.bin/make/unit-tests/check-expect.lua b/usr.bin/make/unit-tests/check-expect.lua
index 2aaf31f9a6d..44c867dd321 100644
--- a/usr.bin/make/unit-tests/check-expect.lua
+++ b/usr.bin/make/unit-tests/check-expect.lua
@@ -1,5 +1,5 @@
#! /usr/bin/lua
--- $NetBSD: check-expect.lua,v 1.1 2022/01/15 12:35:18 rillig Exp $
+-- $NetBSD: check-expect.lua,v 1.2 2022/01/29 00:52:53 rillig Exp $
--[[
@@ -82,6 +82,9 @@ local function check_mk(mk_fname)
mk_fname, mk_lineno, exp_fname, prev_expect_line + 1, text)
end
end
+ if mk_line:match("^#%s*expect%-reset$") then
+ prev_expect_line = 0
+ end
---@param text string
for offset, text in mk_line:gmatch("#%s*expect([+%-]%d+):%s*(.*)") do
diff --git a/usr.bin/make/unit-tests/var-scope-local.exp b/usr.bin/make/unit-tests/var-scope-local.exp
index ec051d0f492..051ede288bc 100644
--- a/usr.bin/make/unit-tests/var-scope-local.exp
+++ b/usr.bin/make/unit-tests/var-scope-local.exp
@@ -1,10 +1,21 @@
+Global: .ALLTARGETS = one
+Global: .ALLTARGETS = 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
+Global: one two =
+Global: one two = three
+Global: .MAKEFLAGS = -r -k -d v -d
+Global: .MAKEFLAGS = -r -k -d v -d 0
: 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 "." VAR="local".
-: Making var-scope-local2.c out of nothing.
-: Making var-scope-local2.o from var-scope-local2.c.
-: Making basename "var-scope-local2.o" in "." from "var-scope-local2.c" in "." VAR="local to var-scope-local2.o".
-: Making var-scope-local3.c out of nothing.
-: var-scope-local3.o uses .USE VAR="global+local"
-: all overwritten VAR="global"
+: Making basename "var-scope-local.o" in "." from "var-scope-local.c" in ".".
+: Making var-scope-local-assign.o with VAR="local".
+: Making var-scope-local-append.o with VAR="local to var-scope-local-append.o".
+: Making var-scope-local-append-global.o with VAR="global+local".
+: Making var-scope-local-default.o with VAR="global".
+: Making var-scope-local-subst.o with VAR="global+local".
+: Making var-scope-local-shell.o with VAR="output".
+: var-scope-local-use.o uses .USE VAR="global"
+: all overwritten
exit status 0
diff --git a/usr.bin/make/unit-tests/var-scope-local.mk b/usr.bin/make/unit-tests/var-scope-local.mk
index bd7b75d7000..91a5f525e85 100644
--- a/usr.bin/make/unit-tests/var-scope-local.mk
+++ b/usr.bin/make/unit-tests/var-scope-local.mk
@@ -1,16 +1,67 @@
-# $NetBSD: var-scope-local.mk,v 1.2 2022/01/27 06:56:27 sjg Exp $
+# $NetBSD: var-scope-local.mk,v 1.3 2022/01/29 00:52:53 rillig Exp $
#
-# Tests for target-local variables, such as ${.TARGET} or $@.
+# Tests for target-local variables, such as ${.TARGET} or $@. These variables
+# are relatively short-lived as they are created just before making the
+# target. In contrast, global variables are typically created when the
+# makefiles are read in.
+#
+# The 7 built-in target-local variables are listed in the manual page. They
+# are defined just before the target is actually made. Additional
+# target-local variables can be defined in dependency lines like
+# 'target: VAR=value', one at a time.
-# TODO: Implementation
+.MAIN: all
-# Ensure that the name of the variable is exactly the given one.
-# The variable "@" is an alias for ".TARGET", so the implementation might
+# 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
+# these expressions to expand right in time when the target-local variables
+# are actually set.
+#
+# Conditions like the ones below 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
+# target-local variables need to be preserved, including the exact names of
+# the variables.
+#
+# Each of the built-in target-local variables has two equivalent names, for
+# example '@' is equivalent to '.TARGET'. The implementation might
# canonicalize these aliases at some point, and that might be surprising.
# This aliasing happens for single-character variable names like $@ or $<
# (see VarFind, CanonicalVarname), but not for braced or parenthesized
# expressions like ${@}, ${.TARGET} ${VAR:Mpattern} (see Var_Parse,
# ParseVarname).
+#
+# In the following condition, make does not expand '$@' but instead changes it
+# to the long-format alias '$(.TARGET)'; note that the alias is not written
+# with braces, as would be common in BSD makefiles, but with parentheses.
+# This alternative form behaves equivalently though.
+.if $@ != "\$\(.TARGET)"
+. error
+.endif
+# In the long form of writing a target-local variable, the expression is
+# preserved exactly as written, no matter whether with '{' or '('.
+.if ${@} != "\$\{@}"
+. error
+.endif
+.if $(@) != "\$\(@)"
+. error
+.endif
+# If the variable expression contains modifiers, the behavior depends on the
+# actual modifiers. The modifier ':M' keeps the expression in the state
+# 'undefined'. Since the expression is still undefined after evaluating all
+# the modifiers, the value of the expression is discarded and the expression
+# text is used instead. This preserves the expressions based on target-local
+# variables as long as possible.
+.if ${@:M*} != "\$\{@:M*}"
+. error
+.endif
+# In the following examples, the expressions are based on target-local
+# variables but use the modifier ':L', which turns an undefined expression
+# into a defined one. At the end of evaluating the expression, the state of
+# the expression is not 'undefined' anymore, and the value of the expression
+# is the name of the variable, since that's what the modifier ':L' does.
.if ${@:L} != "@"
. error
.endif
@@ -24,45 +75,126 @@
. error
.endif
-all:
+
+# 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'.
+# 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
+# If the two targets to the left are generated by a variable expression, the
+# line is parsed as a variable assignment since its left-hand side is a single
+# word.
+# expect: Global: one two = three
+${:Uone two}:=three
+.MAKEFLAGS: -d0
+
.SUFFIXES: .c .o
-var-scope-local.c var-scope-local2.c var-scope-local3.c:
- : Making ${.TARGET} out of nothing.
+# One of the dynamic target-local variables is '.TARGET'. Since this is not
+# a suffix transformation rule, the variable '.IMPSRC' is not defined.
+# expect: : Making var-scope-local.c out of nothing.
+var-scope-local.c:
+ : Making ${.TARGET} ${.IMPSRC:Dfrom ${.IMPSRC}:Uout of nothing}.
+# This is a suffix transformation rule, so both '.TARGET' and '.IMPSRC' are
+# defined.
+# expect: : Making var-scope-local.o from var-scope-local.c.
+# expect: : Making basename "var-scope-local.o" in "." from "var-scope-local.c" in ".".
.c.o:
: Making ${.TARGET} from ${.IMPSRC}.
# The local variables @F, @D, <F, <D are legacy forms.
# See the manual page for details.
- : Making basename "${@F}" in "${@D}" from "${<F}" in "${<D}" VAR="${VAR}".
+ : Making basename "${@F}" in "${@D}" from "${<F}" in "${<D}".
-all: var-scope-local.o var-scope-local2.o var-scope-local3.o
+# expect: : all overwritten
+all: var-scope-local.o
# The ::= modifier overwrites the .TARGET variable in the node
# 'all', not in the global scope. This can be seen with the -dv
- # option, looking for "all:@ = overwritten".
- : ${.TARGET} ${.TARGET::=overwritten}${.TARGET} VAR="${VAR}"
+ # option, looking for "all: @ = overwritten".
+ : ${.TARGET} ${.TARGET::=overwritten}${.TARGET}
+
+
+# Begin tests for custom target-local variables, for all 5 variable assignment
+# operators.
+all: var-scope-local-assign.o
+all: var-scope-local-append.o
+all: var-scope-local-append-global.o
+all: var-scope-local-default.o
+all: var-scope-local-subst.o
+all: var-scope-local-shell.o
+
+var-scope-local-assign.o \
+var-scope-local-append.o \
+var-scope-local-append-global.o \
+var-scope-local-default.o \
+var-scope-local-subst.o \
+var-scope-local-shell.o:
+ : Making ${.TARGET} with VAR="${VAR}".
-# we can set variables per target with some limitations
+# Target-local variables are enabled by default. Force them to be enabled
+# just in case a test above has disabled them.
.MAKE.TARGET_LOCAL_VARIABLES= yes
-VAR= global
-# the rest of the line is the value
-var-scope-local.o: VAR= local
-# += will *not* take global value and add to it in local context
-var-scope-local2.o: VAR+= local
-# but once defined in local context += behaves as expected.
-var-scope-local2.o: VAR += to ${.TARGET}
-# we can get the global value though
-# so complex values can always be set via global variable and then
-# assigned to local context
-# Note: that the global ${VAR} is expanded at this point
-# just as with any dependency line.
-var-scope-local3.o: VAR= ${VAR}+local
-
-# while VAR=use will be set for a .USE node, it will never be seen
-# since only the ultimate target's context is searched
+
+VAR= global
+
+# If the sources of a dependency line look like a variable assignment, make
+# treats them as such. There is only a single variable assignment per
+# dependency line, which makes whitespace around the assignment operator
+# irrelevant.
+#
+# expect-reset
+# expect: : Making var-scope-local-assign.o with VAR="local".
+var-scope-local-assign.o: VAR= local
+
+# Assignments using '+=' do *not* look up the global value, instead they only
+# look up the variable in the target's own scope.
+var-scope-local-append.o: VAR+= local
+# Once a variable is defined in the target-local scope, appending using '+='
+# behaves as expected. Note that the expression '${.TARGET}' is not resolved
+# when parsing the dependency line, its evaluation is deferred until the
+# target is actually made.
+# expect: : Making var-scope-local-append.o with VAR="local to var-scope-local-append.o".
+var-scope-local-append.o: VAR += to ${.TARGET}
+# To access the value of a global variable, use a variable expression. This
+# expression is expanded before parsing the whole dependency line. Since the
+# expansion happens to the right of both the dependency operator ':' and also
+# to the right of the assignment operator '=', the expanded text does not
+# affect the dependency or the variable assignment structurally. The
+# effective variable assignment, after expanding the whole line first, is thus
+# 'VAR= global+local'.
+# expect: : Making var-scope-local-append-global.o with VAR="global+local".
+var-scope-local-append-global.o: VAR= ${VAR}+local
+
+var-scope-local-default.o: VAR ?= first
+var-scope-local-default.o: VAR ?= second
+# XXX: '?=' does look at the global variable. That's a long-standing
+# inconsistency between the assignment operators '+=' and '?='. See
+# Var_AppendExpand and VarAssign_Eval.
+# expect: : Making var-scope-local-default.o with VAR="global".
+
+# Using the variable assignment operator ':=' provides another way of
+# accessing a global variable and extending it with local modifications. The
+# '$' has to be written as '$$' though to survive the expansion of the
+# dependency line as a whole.
+var-scope-local-subst.o: VAR := $${VAR}+local
+
+# The variable assignment operator '!=' assigns the output of the shell
+# command, as everywhere else.
+var-scope-local-shell.o: VAR != echo output
+
+
+# While VAR=use will be set for a .USE node, it will never be seen since only
+# the ultimate target's context is searched; the variable assignments from the
+# .USE target are not copied to the ultimate target's.
a_use: .USE VAR=use
: ${.TARGET} uses .USE VAR="${VAR}"
-var-scope-local3.o: a_use
+all: var-scope-local-use.o
+var-scope-local-use.o: a_use