var-scope-local.mk revision 1.11 1 # $NetBSD: var-scope-local.mk,v 1.11 2024/03/05 23:07:58 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 # Target-local variables in a target rule
16 #
17 # In target rules, '$*' only strips the extension off the pathname if the
18 # extension is listed in '.SUFFIXES'.
19 #
20 # expect: target-rule.ext: * = <target-rule.ext>
21 all: target-rule.ext dir/subdir/target-rule.ext
22 target-rule.ext dir/subdir/target-rule.ext: .PHONY
23 @echo '$@: @ = <${@:Uundefined}>'
24 @echo '$@: % = <${%:Uundefined}>'
25 @echo '$@: ? = <${?:Uundefined}>'
26 @echo '$@: < = <${<:Uundefined}>'
27 @echo '$@: * = <${*:Uundefined}>'
28
29 .SUFFIXES: .ir-gen-from .ir-from .ir-to
30
31 # In target rules, '$*' strips the extension off the pathname of the target
32 # if the extension is listed in '.SUFFIXES'.
33 #
34 # expect: target-rule.ir-gen-from: * = <target-rule>
35 all: target-rule.ir-gen-from dir/subdir/target-rule-dir.ir-gen-from
36 target-rule.ir-gen-from dir/subdir/target-rule-dir.ir-gen-from:
37 @echo '$@: @ = <${@:Uundefined}>'
38 @echo '$@: % = <${%:Uundefined}>'
39 @echo '$@: ? = <${?:Uundefined}>'
40 @echo '$@: < = <${<:Uundefined}>'
41 @echo '$@: * = <${*:Uundefined}>'
42
43 .ir-from.ir-to:
44 @echo '$@: @ = <${@:Uundefined}>'
45 @echo '$@: % = <${%:Uundefined}>'
46 @echo '$@: ? = <${?:Uundefined}>'
47 @echo '$@: < = <${<:Uundefined}>'
48 @echo '$@: * = <${*:Uundefined}>'
49 .ir-gen-from.ir-from:
50 @echo '$@: @ = <${@:Uundefined}>'
51 @echo '$@: % = <${%:Uundefined}>'
52 @echo '$@: ? = <${?:Uundefined}>'
53 @echo '$@: < = <${<:Uundefined}>'
54 @echo '$@: * = <${*:Uundefined}>'
55
56 # Target-local variables in an inference rule
57 all: inference-rule.ir-to dir/subdir/inference-rule.ir-to
58 inference-rule.ir-from: .PHONY
59 dir/subdir/inference-rule.ir-from: .PHONY
60
61 # Target-local variables in a chain of inference rules
62 all: inference-rule-chain.ir-to dir/subdir/inference-rule-chain.ir-to
63 inference-rule-chain.ir-gen-from: .PHONY
64 dir/subdir/inference-rule-chain.ir-gen-from: .PHONY
65
66 # The run-time 'check' directives from above happen after the parse-time
67 # 'check' directives from below.
68 #
69 # expect-reset
70
71 # Deferred evaluation during parsing
72 #
73 # The target-local variables can be used in expressions, just like other
74 # variables. When these expressions are evaluated outside of a target, these
75 # expressions are not yet expanded, instead their text is preserved, to allow
76 # these expressions to expand right in time when the target-local variables
77 # are actually set.
78 #
79 # Conditions from .if directives are evaluated in the scope of the command
80 # line, which means that variables from the command line, from the global
81 # scope and from the environment are resolved, in this precedence order (but
82 # see the command line option '-e'). In that phase, expressions involving
83 # target-local variables need to be preserved, including the exact names of
84 # the variables.
85 #
86 # Each of the built-in target-local variables has two equivalent names, for
87 # example '@' is equivalent to '.TARGET'. The implementation might
88 # canonicalize these aliases at some point, and that might be surprising.
89 # This aliasing happens for single-character variable names like $@ or $<
90 # (see VarFind, CanonicalVarname), but not for braced or parenthesized
91 # expressions like ${@}, ${.TARGET} ${VAR:Mpattern} (see Var_Parse,
92 # ParseVarname).
93 #
94 # In the following condition, make expands '$@' to the long-format alias
95 # '$(.TARGET)'; note that the alias is not written with braces, as would be
96 # common in BSD makefiles, but with parentheses. This alternative spelling
97 # behaves the same though.
98 .if $@ != "\$\(.TARGET)"
99 . error
100 .endif
101 # In the long form of writing a target-local variable, the text of the
102 # expression is preserved exactly as written, no matter whether it is written
103 # with '{' or '('.
104 .if ${@} != "\$\{@}"
105 . error
106 .endif
107 .if $(@) != "\$\(@)"
108 . error
109 .endif
110 # If the expression contains modifiers, the behavior depends on the
111 # actual modifiers. The modifier ':M' keeps the expression in the state
112 # 'undefined'. Since the expression is still undefined after evaluating all
113 # the modifiers, the value of the expression is discarded and the expression
114 # text is used instead. This preserves the expressions based on target-local
115 # variables as long as possible.
116 .if ${@:M*} != "\$\{@:M*}"
117 . error
118 .endif
119 # In the following examples, the expressions are based on target-local
120 # variables but use the modifier ':L', which turns an undefined expression
121 # into a defined one. At the end of evaluating the expression, the state of
122 # the expression is not 'undefined' anymore. The value of the expression
123 # is the name of the variable, since that's what the modifier ':L' does.
124 .if ${@:L} != "@"
125 . error
126 .endif
127 .if ${.TARGET:L} != ".TARGET"
128 . error
129 .endif
130 .if ${@F:L} != "@F"
131 . error
132 .endif
133 .if ${@D:L} != "@D"
134 . error
135 .endif
136
137
138 # Custom local variables
139 #
140 # Additional target-local variables may be defined in dependency lines.
141 .MAKEFLAGS: -dv
142 # In the following line, the ':=' may either be interpreted as an assignment
143 # operator or as the dependency operator ':', followed by an empty variable
144 # name and the assignment operator '='. It is the latter since in an
145 # assignment, the left-hand side must be a single word or empty.
146 #
147 # The empty variable name is expanded twice, once for 'one' and once for
148 # 'two'.
149 # expect: one: ignoring ' = three' as the variable name '' expands to empty
150 # expect: two: ignoring ' = three' as the variable name '' expands to empty
151 one two:=three
152 # If the two targets to the left are generated by an expression, the
153 # line is parsed as a variable assignment since its left-hand side is a single
154 # word.
155 # expect: Global: one two = three
156 ${:Uone two}:=three
157 .MAKEFLAGS: -d0
158
159
160 .SUFFIXES: .c .o
161
162 # One of the dynamic target-local variables is '.TARGET'. Since this is not
163 # a suffix transformation rule, the variable '.IMPSRC' is not defined.
164 # expect: : Making var-scope-local.c out of nothing.
165 var-scope-local.c:
166 : Making ${.TARGET} ${.IMPSRC:Dfrom ${.IMPSRC}:Uout of nothing}.
167
168 # This is a suffix transformation rule, so both '.TARGET' and '.IMPSRC' are
169 # defined.
170 # expect: : Making var-scope-local.o from var-scope-local.c.
171 # expect: : Making basename "var-scope-local.o" in "." from "var-scope-local.c" in ".".
172 .c.o:
173 : Making ${.TARGET} from ${.IMPSRC}.
174
175 # The local variables @F, @D, <F, <D are legacy forms.
176 # See the manual page for details.
177 : Making basename "${@F}" in "${@D}" from "${<F}" in "${<D}".
178
179 # expect: : all overwritten
180 all: var-scope-local.o
181 # The ::= modifier overwrites the .TARGET variable in the node
182 # 'all', not in the global scope. This can be seen with the -dv
183 # option, looking for "all: @ = overwritten".
184 : ${.TARGET} ${.TARGET::=overwritten}${.TARGET}
185
186
187 # Begin tests for custom target-local variables, for all 5 variable assignment
188 # operators.
189 all: var-scope-local-assign.o
190 all: var-scope-local-append.o
191 all: var-scope-local-append-global.o
192 all: var-scope-local-default.o
193 all: var-scope-local-subst.o
194 all: var-scope-local-shell.o
195
196 var-scope-local-assign.o \
197 var-scope-local-append.o \
198 var-scope-local-append-global.o \
199 var-scope-local-default.o \
200 var-scope-local-subst.o \
201 var-scope-local-shell.o:
202 @echo "Making ${.TARGET} with make '"${VAR:Q}"' and env '$$VAR'."
203
204 # Target-local variables are enabled by default. Force them to be enabled
205 # just in case a test above has disabled them.
206 .MAKE.TARGET_LOCAL_VARIABLES= yes
207
208 VAR= global
209 .export VAR
210
211 # If the sources of a dependency line look like a variable assignment, make
212 # treats them as such. There is only a single variable assignment per
213 # dependency line, which makes whitespace around the assignment operator
214 # irrelevant.
215 #
216 # expect-reset
217 # expect: Making var-scope-local-assign.o with make 'local' and env 'local'.
218 var-scope-local-assign.o: VAR= local
219
220 # Assignments using '+=' do *not* look up the global value, instead they only
221 # look up the variable in the target's own scope.
222 var-scope-local-append.o: VAR+= local
223 # Once a variable is defined in the target-local scope, appending using '+='
224 # behaves as expected. Note that the expression '${.TARGET}' is not resolved
225 # when parsing the dependency line, its evaluation is deferred until the
226 # target is actually made.
227 # 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'.
228 var-scope-local-append.o: VAR += to ${.TARGET}
229 # To access the value of a global variable, use an expression. This
230 # expression is expanded before parsing the whole dependency line. Since the
231 # expansion happens to the right of the dependency operator ':', the expanded
232 # text does not influence parsing of the dependency line. Since the expansion
233 # happens to the right of the assignment operator '=', the expanded text does
234 # not influence the parsing of the variable assignment. The effective
235 # variable assignment, after expanding the whole line first, is thus
236 # 'VAR= global+local'.
237 # expect: Making var-scope-local-append-global.o with make 'global+local' and env 'global+local'.
238 var-scope-local-append-global.o: VAR= ${VAR}+local
239
240 var-scope-local-default.o: VAR ?= first
241 var-scope-local-default.o: VAR ?= second
242 # XXX: '?=' does look at the global variable. That's a long-standing
243 # inconsistency between the assignment operators '+=' and '?='. See
244 # Var_AppendExpand and VarAssign_Eval.
245 # expect: Making var-scope-local-default.o with make 'global' and env 'global'.
246
247 # Using the variable assignment operator ':=' provides another way of
248 # accessing a global variable and extending it with local modifications. The
249 # '$' has to be written as '$$' though to survive the expansion of the
250 # dependency line as a whole. After that, the parser sees the variable
251 # assignment as 'VAR := ${VAR}+local' and searches for the variable 'VAR' in
252 # the usual scopes, picking up the variable from the global scope.
253 # expect: Making var-scope-local-subst.o with make 'global+local' and env 'global+local'.
254 var-scope-local-subst.o: VAR := $${VAR}+local
255
256 # The variable assignment operator '!=' assigns the output of the shell
257 # command, as everywhere else. The shell command is run when the dependency
258 # line is parsed.
259 var-scope-local-shell.o: VAR != echo output
260
261
262 # While VAR=use will be set for a .USE node, it will never be seen since only
263 # the ultimate target's context is searched; the variable assignments from the
264 # .USE target are not copied to the ultimate target's.
265 # expect: Making .USE var-scope-local-use.o with make 'global' and env 'global'.
266 a_use: .USE VAR=use
267 @echo "Making .USE ${.TARGET} with make '"${VAR:Q}"' and env '$$VAR'."
268
269 all: var-scope-local-use.o
270 var-scope-local-use.o: a_use
271