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