xref: /freebsd/contrib/bmake/unit-tests/var-scope-local.mk (revision c59c3bf34db360695f07735bebc76a768cac5afc)
1*c59c3bf3SSimon J. Gerraty# $NetBSD: var-scope-local.mk,v 1.11 2024/03/05 23:07:58 rillig Exp $
29f45a3c8SSimon J. Gerraty#
39f45a3c8SSimon J. Gerraty# Tests for target-local variables, such as ${.TARGET} or $@.  These variables
49f45a3c8SSimon J. Gerraty# are relatively short-lived as they are created just before making the
59f45a3c8SSimon J. Gerraty# target.  In contrast, global variables are typically created when the
69f45a3c8SSimon J. Gerraty# makefiles are read in.
79f45a3c8SSimon J. Gerraty#
89f45a3c8SSimon J. Gerraty# The 7 built-in target-local variables are listed in the manual page.  They
99f45a3c8SSimon J. Gerraty# are defined just before the target is actually made.  Additional
109f45a3c8SSimon J. Gerraty# target-local variables can be defined in dependency lines like
119f45a3c8SSimon J. Gerraty# 'target: VAR=value', one at a time.
129f45a3c8SSimon J. Gerraty
139f45a3c8SSimon J. Gerraty.MAIN: all
149f45a3c8SSimon J. Gerraty
15c1d01b5fSSimon J. Gerraty# Target-local variables in a target rule
16c1d01b5fSSimon J. Gerraty#
17c1d01b5fSSimon J. Gerraty# In target rules, '$*' only strips the extension off the pathname if the
18c1d01b5fSSimon J. Gerraty# extension is listed in '.SUFFIXES'.
19c1d01b5fSSimon J. Gerraty#
20c1d01b5fSSimon J. Gerraty# expect: target-rule.ext: * = <target-rule.ext>
21c1d01b5fSSimon J. Gerratyall: target-rule.ext dir/subdir/target-rule.ext
22c1d01b5fSSimon J. Gerratytarget-rule.ext dir/subdir/target-rule.ext: .PHONY
23c1d01b5fSSimon J. Gerraty	@echo '$@: @ = <${@:Uundefined}>'
24c1d01b5fSSimon J. Gerraty	@echo '$@: % = <${%:Uundefined}>'
25c1d01b5fSSimon J. Gerraty	@echo '$@: ? = <${?:Uundefined}>'
26c1d01b5fSSimon J. Gerraty	@echo '$@: < = <${<:Uundefined}>'
27c1d01b5fSSimon J. Gerraty	@echo '$@: * = <${*:Uundefined}>'
28c1d01b5fSSimon J. Gerraty
29c1d01b5fSSimon J. Gerraty.SUFFIXES: .ir-gen-from .ir-from .ir-to
30c1d01b5fSSimon J. Gerraty
31c1d01b5fSSimon J. Gerraty# In target rules, '$*' strips the extension off the pathname of the target
32c1d01b5fSSimon J. Gerraty# if the extension is listed in '.SUFFIXES'.
33c1d01b5fSSimon J. Gerraty#
34c1d01b5fSSimon J. Gerraty# expect: target-rule.ir-gen-from: * = <target-rule>
35c1d01b5fSSimon J. Gerratyall: target-rule.ir-gen-from dir/subdir/target-rule-dir.ir-gen-from
36c1d01b5fSSimon J. Gerratytarget-rule.ir-gen-from dir/subdir/target-rule-dir.ir-gen-from:
37c1d01b5fSSimon J. Gerraty	@echo '$@: @ = <${@:Uundefined}>'
38c1d01b5fSSimon J. Gerraty	@echo '$@: % = <${%:Uundefined}>'
39c1d01b5fSSimon J. Gerraty	@echo '$@: ? = <${?:Uundefined}>'
40c1d01b5fSSimon J. Gerraty	@echo '$@: < = <${<:Uundefined}>'
41c1d01b5fSSimon J. Gerraty	@echo '$@: * = <${*:Uundefined}>'
42c1d01b5fSSimon J. Gerraty
43c1d01b5fSSimon J. Gerraty.ir-from.ir-to:
44c1d01b5fSSimon J. Gerraty	@echo '$@: @ = <${@:Uundefined}>'
45c1d01b5fSSimon J. Gerraty	@echo '$@: % = <${%:Uundefined}>'
46c1d01b5fSSimon J. Gerraty	@echo '$@: ? = <${?:Uundefined}>'
47c1d01b5fSSimon J. Gerraty	@echo '$@: < = <${<:Uundefined}>'
48c1d01b5fSSimon J. Gerraty	@echo '$@: * = <${*:Uundefined}>'
49c1d01b5fSSimon J. Gerraty.ir-gen-from.ir-from:
50c1d01b5fSSimon J. Gerraty	@echo '$@: @ = <${@:Uundefined}>'
51c1d01b5fSSimon J. Gerraty	@echo '$@: % = <${%:Uundefined}>'
52c1d01b5fSSimon J. Gerraty	@echo '$@: ? = <${?:Uundefined}>'
53c1d01b5fSSimon J. Gerraty	@echo '$@: < = <${<:Uundefined}>'
54c1d01b5fSSimon J. Gerraty	@echo '$@: * = <${*:Uundefined}>'
55c1d01b5fSSimon J. Gerraty
56c1d01b5fSSimon J. Gerraty# Target-local variables in an inference rule
57c1d01b5fSSimon J. Gerratyall: inference-rule.ir-to dir/subdir/inference-rule.ir-to
58c1d01b5fSSimon J. Gerratyinference-rule.ir-from: .PHONY
59c1d01b5fSSimon J. Gerratydir/subdir/inference-rule.ir-from: .PHONY
60c1d01b5fSSimon J. Gerraty
61c1d01b5fSSimon J. Gerraty# Target-local variables in a chain of inference rules
62c1d01b5fSSimon J. Gerratyall: inference-rule-chain.ir-to dir/subdir/inference-rule-chain.ir-to
63c1d01b5fSSimon J. Gerratyinference-rule-chain.ir-gen-from: .PHONY
64c1d01b5fSSimon J. Gerratydir/subdir/inference-rule-chain.ir-gen-from: .PHONY
65c1d01b5fSSimon J. Gerraty
66c1d01b5fSSimon J. Gerraty# The run-time 'check' directives from above happen after the parse-time
67c1d01b5fSSimon J. Gerraty# 'check' directives from below.
68c1d01b5fSSimon J. Gerraty#
69c1d01b5fSSimon J. Gerraty# expect-reset
70c1d01b5fSSimon J. Gerraty
71c1d01b5fSSimon J. Gerraty# Deferred evaluation during parsing
72c1d01b5fSSimon J. Gerraty#
739f45a3c8SSimon J. Gerraty# The target-local variables can be used in expressions, just like other
749f45a3c8SSimon J. Gerraty# variables.  When these expressions are evaluated outside of a target, these
759f45a3c8SSimon J. Gerraty# expressions are not yet expanded, instead their text is preserved, to allow
769f45a3c8SSimon J. Gerraty# these expressions to expand right in time when the target-local variables
779f45a3c8SSimon J. Gerraty# are actually set.
789f45a3c8SSimon J. Gerraty#
791d3f2ddcSSimon J. Gerraty# Conditions from .if directives are evaluated in the scope of the command
809f45a3c8SSimon J. Gerraty# line, which means that variables from the command line, from the global
81c1d01b5fSSimon J. Gerraty# scope and from the environment are resolved, in this precedence order (but
82c1d01b5fSSimon J. Gerraty# see the command line option '-e').  In that phase, expressions involving
839f45a3c8SSimon J. Gerraty# target-local variables need to be preserved, including the exact names of
849f45a3c8SSimon J. Gerraty# the variables.
859f45a3c8SSimon J. Gerraty#
869f45a3c8SSimon J. Gerraty# Each of the built-in target-local variables has two equivalent names, for
879f45a3c8SSimon J. Gerraty# example '@' is equivalent to '.TARGET'.  The implementation might
889f45a3c8SSimon J. Gerraty# canonicalize these aliases at some point, and that might be surprising.
899f45a3c8SSimon J. Gerraty# This aliasing happens for single-character variable names like $@ or $<
909f45a3c8SSimon J. Gerraty# (see VarFind, CanonicalVarname), but not for braced or parenthesized
919f45a3c8SSimon J. Gerraty# expressions like ${@}, ${.TARGET} ${VAR:Mpattern} (see Var_Parse,
929f45a3c8SSimon J. Gerraty# ParseVarname).
939f45a3c8SSimon J. Gerraty#
941d3f2ddcSSimon J. Gerraty# In the following condition, make expands '$@' to the long-format alias
951d3f2ddcSSimon J. Gerraty# '$(.TARGET)'; note that the alias is not written with braces, as would be
961d3f2ddcSSimon J. Gerraty# common in BSD makefiles, but with parentheses.  This alternative spelling
971d3f2ddcSSimon J. Gerraty# behaves the same though.
989f45a3c8SSimon J. Gerraty.if $@ != "\$\(.TARGET)"
999f45a3c8SSimon J. Gerraty.  error
1009f45a3c8SSimon J. Gerraty.endif
1011d3f2ddcSSimon J. Gerraty# In the long form of writing a target-local variable, the text of the
1021d3f2ddcSSimon J. Gerraty# expression is preserved exactly as written, no matter whether it is written
1031d3f2ddcSSimon J. Gerraty# with '{' or '('.
1049f45a3c8SSimon J. Gerraty.if ${@} != "\$\{@}"
1059f45a3c8SSimon J. Gerraty.  error
1069f45a3c8SSimon J. Gerraty.endif
1079f45a3c8SSimon J. Gerraty.if $(@) != "\$\(@)"
1089f45a3c8SSimon J. Gerraty.  error
1099f45a3c8SSimon J. Gerraty.endif
110d5e0a182SSimon J. Gerraty# If the expression contains modifiers, the behavior depends on the
1119f45a3c8SSimon J. Gerraty# actual modifiers.  The modifier ':M' keeps the expression in the state
1129f45a3c8SSimon J. Gerraty# 'undefined'.  Since the expression is still undefined after evaluating all
1139f45a3c8SSimon J. Gerraty# the modifiers, the value of the expression is discarded and the expression
1149f45a3c8SSimon J. Gerraty# text is used instead.  This preserves the expressions based on target-local
1159f45a3c8SSimon J. Gerraty# variables as long as possible.
1169f45a3c8SSimon J. Gerraty.if ${@:M*} != "\$\{@:M*}"
1179f45a3c8SSimon J. Gerraty.  error
1189f45a3c8SSimon J. Gerraty.endif
1199f45a3c8SSimon J. Gerraty# In the following examples, the expressions are based on target-local
1209f45a3c8SSimon J. Gerraty# variables but use the modifier ':L', which turns an undefined expression
1219f45a3c8SSimon J. Gerraty# into a defined one.  At the end of evaluating the expression, the state of
1221d3f2ddcSSimon J. Gerraty# the expression is not 'undefined' anymore.  The value of the expression
1239f45a3c8SSimon J. Gerraty# is the name of the variable, since that's what the modifier ':L' does.
1249f45a3c8SSimon J. Gerraty.if ${@:L} != "@"
1259f45a3c8SSimon J. Gerraty.  error
1269f45a3c8SSimon J. Gerraty.endif
1279f45a3c8SSimon J. Gerraty.if ${.TARGET:L} != ".TARGET"
1289f45a3c8SSimon J. Gerraty.  error
1299f45a3c8SSimon J. Gerraty.endif
1309f45a3c8SSimon J. Gerraty.if ${@F:L} != "@F"
1319f45a3c8SSimon J. Gerraty.  error
1329f45a3c8SSimon J. Gerraty.endif
1339f45a3c8SSimon J. Gerraty.if ${@D:L} != "@D"
1349f45a3c8SSimon J. Gerraty.  error
1359f45a3c8SSimon J. Gerraty.endif
1369f45a3c8SSimon J. Gerraty
1379f45a3c8SSimon J. Gerraty
138c1d01b5fSSimon J. Gerraty# Custom local variables
139c1d01b5fSSimon J. Gerraty#
1409f45a3c8SSimon J. Gerraty# Additional target-local variables may be defined in dependency lines.
1419f45a3c8SSimon J. Gerraty.MAKEFLAGS: -dv
1429f45a3c8SSimon J. Gerraty# In the following line, the ':=' may either be interpreted as an assignment
1439f45a3c8SSimon J. Gerraty# operator or as the dependency operator ':', followed by an empty variable
1449f45a3c8SSimon J. Gerraty# name and the assignment operator '='.  It is the latter since in an
145c1d01b5fSSimon J. Gerraty# assignment, the left-hand side must be a single word or empty.
146c1d01b5fSSimon J. Gerraty#
147c1d01b5fSSimon J. Gerraty# The empty variable name is expanded twice, once for 'one' and once for
148c1d01b5fSSimon J. Gerraty# 'two'.
149d5e0a182SSimon J. Gerraty# expect: one: ignoring ' = three' as the variable name '' expands to empty
150d5e0a182SSimon J. Gerraty# expect: two: ignoring ' = three' as the variable name '' expands to empty
1519f45a3c8SSimon J. Gerratyone two:=three
152d5e0a182SSimon J. Gerraty# If the two targets to the left are generated by an expression, the
1539f45a3c8SSimon J. Gerraty# line is parsed as a variable assignment since its left-hand side is a single
1549f45a3c8SSimon J. Gerraty# word.
1559f45a3c8SSimon J. Gerraty# expect: Global: one two = three
1569f45a3c8SSimon J. Gerraty${:Uone two}:=three
1579f45a3c8SSimon J. Gerraty.MAKEFLAGS: -d0
1589f45a3c8SSimon J. Gerraty
1599f45a3c8SSimon J. Gerraty
1609f45a3c8SSimon J. Gerraty.SUFFIXES: .c .o
1619f45a3c8SSimon J. Gerraty
1629f45a3c8SSimon J. Gerraty# One of the dynamic target-local variables is '.TARGET'.  Since this is not
1639f45a3c8SSimon J. Gerraty# a suffix transformation rule, the variable '.IMPSRC' is not defined.
1649f45a3c8SSimon J. Gerraty# expect: : Making var-scope-local.c out of nothing.
1659f45a3c8SSimon J. Gerratyvar-scope-local.c:
1669f45a3c8SSimon J. Gerraty	: Making ${.TARGET} ${.IMPSRC:Dfrom ${.IMPSRC}:Uout of nothing}.
1679f45a3c8SSimon J. Gerraty
1689f45a3c8SSimon J. Gerraty# This is a suffix transformation rule, so both '.TARGET' and '.IMPSRC' are
1699f45a3c8SSimon J. Gerraty# defined.
1709f45a3c8SSimon J. Gerraty# expect: : Making var-scope-local.o from var-scope-local.c.
1719f45a3c8SSimon J. Gerraty# expect: : Making basename "var-scope-local.o" in "." from "var-scope-local.c" in ".".
1729f45a3c8SSimon J. Gerraty.c.o:
1739f45a3c8SSimon J. Gerraty	: Making ${.TARGET} from ${.IMPSRC}.
1749f45a3c8SSimon J. Gerraty
1759f45a3c8SSimon J. Gerraty	# The local variables @F, @D, <F, <D are legacy forms.
1769f45a3c8SSimon J. Gerraty	# See the manual page for details.
1779f45a3c8SSimon J. Gerraty	: Making basename "${@F}" in "${@D}" from "${<F}" in "${<D}".
1789f45a3c8SSimon J. Gerraty
1799f45a3c8SSimon J. Gerraty# expect: : all overwritten
1809f45a3c8SSimon J. Gerratyall: var-scope-local.o
1819f45a3c8SSimon J. Gerraty	# The ::= modifier overwrites the .TARGET variable in the node
1829f45a3c8SSimon J. Gerraty	# 'all', not in the global scope.  This can be seen with the -dv
1839f45a3c8SSimon J. Gerraty	# option, looking for "all: @ = overwritten".
1849f45a3c8SSimon J. Gerraty	: ${.TARGET} ${.TARGET::=overwritten}${.TARGET}
1859f45a3c8SSimon J. Gerraty
1869f45a3c8SSimon J. Gerraty
1879f45a3c8SSimon J. Gerraty# Begin tests for custom target-local variables, for all 5 variable assignment
1889f45a3c8SSimon J. Gerraty# operators.
1899f45a3c8SSimon J. Gerratyall: var-scope-local-assign.o
1909f45a3c8SSimon J. Gerratyall: var-scope-local-append.o
1919f45a3c8SSimon J. Gerratyall: var-scope-local-append-global.o
1929f45a3c8SSimon J. Gerratyall: var-scope-local-default.o
1939f45a3c8SSimon J. Gerratyall: var-scope-local-subst.o
1949f45a3c8SSimon J. Gerratyall: var-scope-local-shell.o
1959f45a3c8SSimon J. Gerraty
1969f45a3c8SSimon J. Gerratyvar-scope-local-assign.o \
1979f45a3c8SSimon J. Gerratyvar-scope-local-append.o \
1989f45a3c8SSimon J. Gerratyvar-scope-local-append-global.o \
1999f45a3c8SSimon J. Gerratyvar-scope-local-default.o \
2009f45a3c8SSimon J. Gerratyvar-scope-local-subst.o \
2019f45a3c8SSimon J. Gerratyvar-scope-local-shell.o:
202*c59c3bf3SSimon J. Gerraty	@echo "Making ${.TARGET} with make '"${VAR:Q}"' and env '$$VAR'."
2039f45a3c8SSimon J. Gerraty
2049f45a3c8SSimon J. Gerraty# Target-local variables are enabled by default.  Force them to be enabled
2059f45a3c8SSimon J. Gerraty# just in case a test above has disabled them.
2069f45a3c8SSimon J. Gerraty.MAKE.TARGET_LOCAL_VARIABLES= yes
2079f45a3c8SSimon J. Gerraty
2089f45a3c8SSimon J. GerratyVAR=	global
209*c59c3bf3SSimon J. Gerraty.export VAR
2109f45a3c8SSimon J. Gerraty
2119f45a3c8SSimon J. Gerraty# If the sources of a dependency line look like a variable assignment, make
2129f45a3c8SSimon J. Gerraty# treats them as such.  There is only a single variable assignment per
2139f45a3c8SSimon J. Gerraty# dependency line, which makes whitespace around the assignment operator
2149f45a3c8SSimon J. Gerraty# irrelevant.
2159f45a3c8SSimon J. Gerraty#
2169f45a3c8SSimon J. Gerraty# expect-reset
217*c59c3bf3SSimon J. Gerraty# expect: Making var-scope-local-assign.o with make 'local' and env 'local'.
2189f45a3c8SSimon J. Gerratyvar-scope-local-assign.o: VAR= local
2199f45a3c8SSimon J. Gerraty
2209f45a3c8SSimon J. Gerraty# Assignments using '+=' do *not* look up the global value, instead they only
2219f45a3c8SSimon J. Gerraty# look up the variable in the target's own scope.
2229f45a3c8SSimon J. Gerratyvar-scope-local-append.o: VAR+= local
2239f45a3c8SSimon J. Gerraty# Once a variable is defined in the target-local scope, appending using '+='
2249f45a3c8SSimon J. Gerraty# behaves as expected.  Note that the expression '${.TARGET}' is not resolved
2259f45a3c8SSimon J. Gerraty# when parsing the dependency line, its evaluation is deferred until the
2269f45a3c8SSimon J. Gerraty# target is actually made.
227*c59c3bf3SSimon J. Gerraty# expect: Making var-scope-local-append.o with make 'local to var-scope-local-append.o' and env 'local to var-scope-local-append.o'.
2289f45a3c8SSimon J. Gerratyvar-scope-local-append.o: VAR += to ${.TARGET}
229d5e0a182SSimon J. Gerraty# To access the value of a global variable, use an expression.  This
2309f45a3c8SSimon J. Gerraty# expression is expanded before parsing the whole dependency line.  Since the
2311d3f2ddcSSimon J. Gerraty# expansion happens to the right of the dependency operator ':', the expanded
2321d3f2ddcSSimon J. Gerraty# text does not influence parsing of the dependency line.  Since the expansion
2331d3f2ddcSSimon J. Gerraty# happens to the right of the assignment operator '=', the expanded text does
2341d3f2ddcSSimon J. Gerraty# not influence the parsing of the variable assignment.  The effective
2351d3f2ddcSSimon J. Gerraty# variable assignment, after expanding the whole line first, is thus
2369f45a3c8SSimon J. Gerraty# 'VAR= global+local'.
237*c59c3bf3SSimon J. Gerraty# expect: Making var-scope-local-append-global.o with make 'global+local' and env 'global+local'.
2389f45a3c8SSimon J. Gerratyvar-scope-local-append-global.o: VAR= ${VAR}+local
2399f45a3c8SSimon J. Gerraty
2409f45a3c8SSimon J. Gerratyvar-scope-local-default.o: VAR ?= first
2419f45a3c8SSimon J. Gerratyvar-scope-local-default.o: VAR ?= second
2429f45a3c8SSimon J. Gerraty# XXX: '?=' does look at the global variable.  That's a long-standing
2439f45a3c8SSimon J. Gerraty# inconsistency between the assignment operators '+=' and '?='.  See
2449f45a3c8SSimon J. Gerraty# Var_AppendExpand and VarAssign_Eval.
245*c59c3bf3SSimon J. Gerraty# expect: Making var-scope-local-default.o with make 'global' and env 'global'.
2469f45a3c8SSimon J. Gerraty
2479f45a3c8SSimon J. Gerraty# Using the variable assignment operator ':=' provides another way of
2489f45a3c8SSimon J. Gerraty# accessing a global variable and extending it with local modifications.  The
2499f45a3c8SSimon J. Gerraty# '$' has to be written as '$$' though to survive the expansion of the
2501d3f2ddcSSimon J. Gerraty# dependency line as a whole.  After that, the parser sees the variable
2511d3f2ddcSSimon J. Gerraty# assignment as 'VAR := ${VAR}+local' and searches for the variable 'VAR' in
2521d3f2ddcSSimon J. Gerraty# the usual scopes, picking up the variable from the global scope.
253*c59c3bf3SSimon J. Gerraty# expect: Making var-scope-local-subst.o with make 'global+local' and env 'global+local'.
2549f45a3c8SSimon J. Gerratyvar-scope-local-subst.o: VAR := $${VAR}+local
2559f45a3c8SSimon J. Gerraty
2569f45a3c8SSimon J. Gerraty# The variable assignment operator '!=' assigns the output of the shell
2571d3f2ddcSSimon J. Gerraty# command, as everywhere else.  The shell command is run when the dependency
2581d3f2ddcSSimon J. Gerraty# line is parsed.
2599f45a3c8SSimon J. Gerratyvar-scope-local-shell.o: VAR != echo output
2609f45a3c8SSimon J. Gerraty
2619f45a3c8SSimon J. Gerraty
2629f45a3c8SSimon J. Gerraty# While VAR=use will be set for a .USE node, it will never be seen since only
2639f45a3c8SSimon J. Gerraty# the ultimate target's context is searched; the variable assignments from the
2649f45a3c8SSimon J. Gerraty# .USE target are not copied to the ultimate target's.
265*c59c3bf3SSimon J. Gerraty# expect: Making .USE var-scope-local-use.o with make 'global' and env 'global'.
2669f45a3c8SSimon J. Gerratya_use: .USE VAR=use
267*c59c3bf3SSimon J. Gerraty	@echo "Making .USE ${.TARGET} with make '"${VAR:Q}"' and env '$$VAR'."
2689f45a3c8SSimon J. Gerraty
2699f45a3c8SSimon J. Gerratyall: var-scope-local-use.o
2709f45a3c8SSimon J. Gerratyvar-scope-local-use.o: a_use
271