1# $NetBSD: var-scope-local.mk,v 1.4 2022/02/05 10:41:15 rillig Exp $
2#
3# Tests for target-local variables, such as ${.TARGET} or $@.  These variables
4# are relatively short-lived as they are created just before making the
5# target.  In contrast, global variables are typically created when the
6# makefiles are read in.
7#
8# The 7 built-in target-local variables are listed in the manual page.  They
9# are defined just before the target is actually made.  Additional
10# target-local variables can be defined in dependency lines like
11# 'target: VAR=value', one at a time.
12
13.MAIN: all
14
15# The target-local variables can be used in expressions, just like other
16# variables.  When these expressions are evaluated outside of a target, these
17# expressions are not yet expanded, instead their text is preserved, to allow
18# these expressions to expand right in time when the target-local variables
19# are actually set.
20#
21# Conditions like the ones below are evaluated in the scope of the command
22# line, which means that variables from the command line, from the global
23# scope and from the environment are resolved, in this order (but see the
24# command line option '-e').  In that phase, expressions involving
25# target-local variables need to be preserved, including the exact names of
26# the variables.
27#
28# Each of the built-in target-local variables has two equivalent names, for
29# example '@' is equivalent to '.TARGET'.  The implementation might
30# canonicalize these aliases at some point, and that might be surprising.
31# This aliasing happens for single-character variable names like $@ or $<
32# (see VarFind, CanonicalVarname), but not for braced or parenthesized
33# expressions like ${@}, ${.TARGET} ${VAR:Mpattern} (see Var_Parse,
34# ParseVarname).
35#
36# In the following condition, make does not expand '$@' but instead changes it
37# to the long-format alias '$(.TARGET)'; note that the alias is not written
38# with braces, as would be common in BSD makefiles, but with parentheses.
39# This alternative form behaves equivalently though.
40.if $@ != "\$\(.TARGET)"
41.  error
42.endif
43# In the long form of writing a target-local variable, the expression is
44# preserved exactly as written, no matter whether with '{' or '('.
45.if ${@} != "\$\{@}"
46.  error
47.endif
48.if $(@) != "\$\(@)"
49.  error
50.endif
51# If the variable expression contains modifiers, the behavior depends on the
52# actual modifiers.  The modifier ':M' keeps the expression in the state
53# 'undefined'.  Since the expression is still undefined after evaluating all
54# the modifiers, the value of the expression is discarded and the expression
55# text is used instead.  This preserves the expressions based on target-local
56# variables as long as possible.
57.if ${@:M*} != "\$\{@:M*}"
58.  error
59.endif
60# In the following examples, the expressions are based on target-local
61# variables but use the modifier ':L', which turns an undefined expression
62# into a defined one.  At the end of evaluating the expression, the state of
63# the expression is not 'undefined' anymore, and the value of the expression
64# is the name of the variable, since that's what the modifier ':L' does.
65.if ${@:L} != "@"
66.  error
67.endif
68.if ${.TARGET:L} != ".TARGET"
69.  error
70.endif
71.if ${@F:L} != "@F"
72.  error
73.endif
74.if ${@D:L} != "@D"
75.  error
76.endif
77
78
79# Additional target-local variables may be defined in dependency lines.
80.MAKEFLAGS: -dv
81# In the following line, the ':=' may either be interpreted as an assignment
82# operator or as the dependency operator ':', followed by an empty variable
83# name and the assignment operator '='.  It is the latter since in an
84# assignment, the left-hand side must be at most a single word.  The empty
85# variable name is expanded twice, once for 'one' and once for 'two'.
86# expect: Var_SetExpand: variable name "" expands to empty string, with value "three" - ignored
87# expect: Var_SetExpand: variable name "" expands to empty string, with value "three" - ignored
88one two:=three
89# If the two targets to the left are generated by a variable expression, the
90# line is parsed as a variable assignment since its left-hand side is a single
91# word.
92# expect: Global: one two = three
93${:Uone two}:=three
94.MAKEFLAGS: -d0
95
96
97.SUFFIXES: .c .o
98
99# One of the dynamic target-local variables is '.TARGET'.  Since this is not
100# a suffix transformation rule, the variable '.IMPSRC' is not defined.
101# expect: : Making var-scope-local.c out of nothing.
102var-scope-local.c:
103	: Making ${.TARGET} ${.IMPSRC:Dfrom ${.IMPSRC}:Uout of nothing}.
104
105# This is a suffix transformation rule, so both '.TARGET' and '.IMPSRC' are
106# defined.
107# expect: : Making var-scope-local.o from var-scope-local.c.
108# expect: : Making basename "var-scope-local.o" in "." from "var-scope-local.c" in ".".
109.c.o:
110	: Making ${.TARGET} from ${.IMPSRC}.
111
112	# The local variables @F, @D, <F, <D are legacy forms.
113	# See the manual page for details.
114	: Making basename "${@F}" in "${@D}" from "${<F}" in "${<D}".
115
116# expect: : all overwritten
117all: var-scope-local.o
118	# The ::= modifier overwrites the .TARGET variable in the node
119	# 'all', not in the global scope.  This can be seen with the -dv
120	# option, looking for "all: @ = overwritten".
121	: ${.TARGET} ${.TARGET::=overwritten}${.TARGET}
122
123
124# Begin tests for custom target-local variables, for all 5 variable assignment
125# operators.
126all: var-scope-local-assign.o
127all: var-scope-local-append.o
128all: var-scope-local-append-global.o
129all: var-scope-local-default.o
130all: var-scope-local-subst.o
131all: var-scope-local-shell.o
132
133var-scope-local-assign.o \
134var-scope-local-append.o \
135var-scope-local-append-global.o \
136var-scope-local-default.o \
137var-scope-local-subst.o \
138var-scope-local-shell.o:
139	: Making ${.TARGET} with VAR="${VAR}".
140
141# Target-local variables are enabled by default.  Force them to be enabled
142# just in case a test above has disabled them.
143.MAKE.TARGET_LOCAL_VARIABLES= yes
144
145VAR=	global
146
147# If the sources of a dependency line look like a variable assignment, make
148# treats them as such.  There is only a single variable assignment per
149# dependency line, which makes whitespace around the assignment operator
150# irrelevant.
151#
152# expect-reset
153# expect: : Making var-scope-local-assign.o with VAR="local".
154var-scope-local-assign.o: VAR= local
155
156# Assignments using '+=' do *not* look up the global value, instead they only
157# look up the variable in the target's own scope.
158var-scope-local-append.o: VAR+= local
159# Once a variable is defined in the target-local scope, appending using '+='
160# behaves as expected.  Note that the expression '${.TARGET}' is not resolved
161# when parsing the dependency line, its evaluation is deferred until the
162# target is actually made.
163# expect: : Making var-scope-local-append.o with VAR="local to var-scope-local-append.o".
164var-scope-local-append.o: VAR += to ${.TARGET}
165# To access the value of a global variable, use a variable expression.  This
166# expression is expanded before parsing the whole dependency line.  Since the
167# expansion happens to the right of both the dependency operator ':' and also
168# to the right of the assignment operator '=', the expanded text does not
169# affect the dependency or the variable assignment structurally.  The
170# effective variable assignment, after expanding the whole line first, is thus
171# 'VAR= global+local'.
172# expect: : Making var-scope-local-append-global.o with VAR="global+local".
173var-scope-local-append-global.o: VAR= ${VAR}+local
174
175var-scope-local-default.o: VAR ?= first
176var-scope-local-default.o: VAR ?= second
177# XXX: '?=' does look at the global variable.  That's a long-standing
178# inconsistency between the assignment operators '+=' and '?='.  See
179# Var_AppendExpand and VarAssign_Eval.
180# expect: : Making var-scope-local-default.o with VAR="global".
181
182# Using the variable assignment operator ':=' provides another way of
183# accessing a global variable and extending it with local modifications.  The
184# '$' has to be written as '$$' though to survive the expansion of the
185# dependency line as a whole.
186var-scope-local-subst.o: VAR := $${VAR}+local
187
188# The variable assignment operator '!=' assigns the output of the shell
189# command, as everywhere else.
190var-scope-local-shell.o: VAR != echo output
191
192
193# While VAR=use will be set for a .USE node, it will never be seen since only
194# the ultimate target's context is searched; the variable assignments from the
195# .USE target are not copied to the ultimate target's.
196a_use: .USE VAR=use
197	: ${.TARGET} uses .USE VAR="${VAR}"
198
199all: var-scope-local-use.o
200var-scope-local-use.o: a_use
201
202
203# Since parse.c 1.656 from 2022-01-27 and before parse.c 1.662 from
204# 2022-02-05, there was an out-of-bounds read in Parse_IsVar when looking for
205# a variable assignment in a dependency line with trailing whitespace.  Lines
206# without trailing whitespace were not affected.  Global variable assignments
207# were guaranteed to have no trailing whitespace and were thus not affected.
208#
209# Try to reproduce some variants that may lead to a crash, depending on the
210# memory allocator.  To get a crash, the terminating '\0' of the line must be
211# the last byte of a memory page.  The expression '${:U}' forces this trailing
212# whitespace.
213
214# On FreeBSD x86_64, a crash could in some cases be forced using the following
215# line, which has length 47, so the terminating '\0' may end up at an address
216# of the form 0xXXXX_XXXX_XXXX_Xfff:
217Try_to_crash_FreeBSD.xxxxxxxxxxxxxxxxxx: 12345 ${:U}
218
219# The following line has length 4095, so line[4095] == '\0'.  If the line is
220# allocated on a page boundary and the following page is not mapped, this line
221# leads to a segmentation fault.
222${:U:range=511:@_@1234567@:ts.}: 12345 ${:U}
223
224# The following line has length 8191, so line[8191] == '\0'.  If the line is
225# allocated on a page boundary and the following page is not mapped, this line
226# leads to a segmentation fault.
227${:U:range=1023:@_@1234567@:ts.}: 12345 ${:U}
228
22912345:
230