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