var-op-expand.mk revision 1.25 1 1.25 rillig # $NetBSD: var-op-expand.mk,v 1.25 2025/06/29 11:27:21 rillig Exp $
2 1.1 rillig #
3 1.2 rillig # Tests for the := variable assignment operator, which expands its
4 1.2 rillig # right-hand side.
5 1.15 rillig #
6 1.15 rillig # See also:
7 1.15 rillig # varname-dot-make-save_dollars.mk
8 1.1 rillig
9 1.15 rillig # Force the test results to be independent of the default value of this
10 1.15 rillig # setting, which is 'yes' for NetBSD's usr.bin/make but 'no' for the bmake
11 1.15 rillig # distribution and pkgsrc/devel/bmake.
12 1.16 rillig .MAKE.SAVE_DOLLARS:= yes
13 1.5 rillig
14 1.5 rillig # If the right-hand side does not contain a dollar sign, the ':=' assignment
15 1.5 rillig # operator has the same effect as the '=' assignment operator.
16 1.5 rillig VAR:= value
17 1.5 rillig .if ${VAR} != "value"
18 1.5 rillig . error
19 1.5 rillig .endif
20 1.5 rillig
21 1.5 rillig # When a ':=' assignment is performed, its right-hand side is evaluated and
22 1.5 rillig # expanded as far as possible. Contrary to other situations, '$$' and
23 1.19 rillig # expressions based on undefined variables are preserved though.
24 1.5 rillig #
25 1.19 rillig # Whether an expression is undefined or not is determined at the end
26 1.5 rillig # of evaluating the expression. The consequence is that ${:Ufallback} expands
27 1.5 rillig # to "fallback"; initially this expression is undefined since it is based on
28 1.5 rillig # the variable named "", which is guaranteed to be never defined, but at the
29 1.5 rillig # end of evaluating the expression ${:Ufallback}, the modifier ':U' has turned
30 1.5 rillig # the expression into a defined expression.
31 1.5 rillig
32 1.5 rillig
33 1.5 rillig # literal dollar signs
34 1.5 rillig VAR:= $$ $$$$ $$$$$$$$
35 1.5 rillig .if ${VAR} != "\$ \$\$ \$\$\$\$"
36 1.5 rillig . error
37 1.5 rillig .endif
38 1.5 rillig
39 1.5 rillig
40 1.17 rillig # reference to a variable containing literal dollar signs
41 1.5 rillig REF= $$ $$$$ $$$$$$$$
42 1.5 rillig VAR:= ${REF}
43 1.5 rillig REF= too late
44 1.5 rillig .if ${VAR} != "\$ \$\$ \$\$\$\$"
45 1.5 rillig . error
46 1.5 rillig .endif
47 1.5 rillig
48 1.5 rillig
49 1.5 rillig # reference to an undefined variable
50 1.5 rillig .undef UNDEF
51 1.5 rillig VAR:= <${UNDEF}>
52 1.17 rillig .if ${VAR} != "<>"
53 1.17 rillig . error
54 1.17 rillig .endif
55 1.5 rillig UNDEF= after
56 1.5 rillig .if ${VAR} != "<after>"
57 1.5 rillig . error
58 1.5 rillig .endif
59 1.5 rillig
60 1.5 rillig
61 1.5 rillig # reference to a variable whose name is computed from another variable
62 1.5 rillig REF2= referred to
63 1.5 rillig REF= REF2
64 1.5 rillig VAR:= ${${REF}}
65 1.5 rillig REF= too late
66 1.5 rillig .if ${VAR} != "referred to"
67 1.5 rillig . error
68 1.5 rillig .endif
69 1.5 rillig
70 1.5 rillig
71 1.5 rillig # expression with an indirect modifier referring to an undefined variable
72 1.5 rillig .undef UNDEF
73 1.5 rillig VAR:= ${:${UNDEF}}
74 1.17 rillig .if ${VAR} != ""
75 1.17 rillig . error
76 1.17 rillig .endif
77 1.5 rillig UNDEF= Uwas undefined
78 1.5 rillig .if ${VAR} != "was undefined"
79 1.5 rillig . error
80 1.5 rillig .endif
81 1.5 rillig
82 1.5 rillig
83 1.5 rillig # expression with an indirect modifier referring to another variable that
84 1.5 rillig # in turn refers to an undefined variable
85 1.5 rillig #
86 1.5 rillig # XXX: Even though this is a ':=' assignment, the '${UNDEF}' in the part of
87 1.5 rillig # the variable modifier is not preserved. To preserve it, ParseModifierPart
88 1.5 rillig # would have to call VarSubstExpr somehow since this is the only piece of
89 1.5 rillig # code that takes care of this global variable.
90 1.5 rillig .undef UNDEF
91 1.5 rillig REF= U${UNDEF}
92 1.5 rillig #.MAKEFLAGS: -dv
93 1.5 rillig VAR:= ${:${REF}}
94 1.5 rillig #.MAKEFLAGS: -d0
95 1.5 rillig REF= too late
96 1.5 rillig UNDEF= Uwas undefined
97 1.5 rillig .if ${VAR} != ""
98 1.5 rillig . error
99 1.5 rillig .endif
100 1.5 rillig
101 1.1 rillig
102 1.6 rillig # In variable assignments using the ':=' operator, undefined variables are
103 1.6 rillig # preserved, no matter how indirectly they are referenced.
104 1.6 rillig .undef REF3
105 1.6 rillig REF2= <${REF3}>
106 1.6 rillig REF= ${REF2}
107 1.6 rillig VAR:= ${REF}
108 1.17 rillig .if ${VAR} != "<>"
109 1.17 rillig . error
110 1.17 rillig .endif
111 1.6 rillig REF3= too late
112 1.6 rillig .if ${VAR} != "<too late>"
113 1.6 rillig . error
114 1.6 rillig .endif
115 1.6 rillig
116 1.6 rillig
117 1.6 rillig # In variable assignments using the ':=' operator, '$$' are preserved, no
118 1.6 rillig # matter how indirectly they are referenced.
119 1.6 rillig REF2= REF2:$$ $$$$
120 1.6 rillig REF= REF:$$ $$$$ ${REF2}
121 1.6 rillig VAR:= VAR:$$ $$$$ ${REF}
122 1.6 rillig .if ${VAR} != "VAR:\$ \$\$ REF:\$ \$\$ REF2:\$ \$\$"
123 1.6 rillig . error
124 1.6 rillig .endif
125 1.6 rillig
126 1.6 rillig
127 1.6 rillig # In variable assignments using the ':=' operator, '$$' are preserved in the
128 1.6 rillig # expressions of the top level, but not in expressions that are nested.
129 1.6 rillig VAR:= top:$$ ${:Unest1\:\$\$} ${:Unest2${:U\:\$\$}}
130 1.6 rillig .if ${VAR} != "top:\$ nest1:\$ nest2:\$"
131 1.6 rillig . error
132 1.6 rillig .endif
133 1.6 rillig
134 1.6 rillig
135 1.9 rillig # In variable assignments using the ':=' operator, there may be expressions
136 1.9 rillig # containing variable modifiers, and these modifiers may refer to other
137 1.10 rillig # variables. These referred-to variables are expanded at the time of
138 1.10 rillig # assignment. The undefined variables are kept as-is and are later expanded
139 1.10 rillig # when evaluating the condition.
140 1.9 rillig #
141 1.9 rillig # Contrary to the assignment operator '=', the assignment operator ':='
142 1.9 rillig # consumes the '$' from modifier parts.
143 1.9 rillig REF.word= 1:$$ 2:$$$$ 4:$$$$$$$$
144 1.10 rillig .undef REF.undef
145 1.10 rillig VAR:= ${:Uword undef:@word@${REF.${word}}@}, direct: ${REF.word} ${REF.undef}
146 1.10 rillig REF.word= word.after
147 1.10 rillig REF.undef= undef.after
148 1.10 rillig .if ${VAR} != "1:2:\$ 4:\$\$ undef.after, direct: 1:\$ 2:\$\$ 4:\$\$\$\$ undef.after"
149 1.9 rillig . error
150 1.9 rillig .endif
151 1.9 rillig
152 1.9 rillig # Just for comparison, the previous example using the assignment operator '='
153 1.10 rillig # instead of ':='. The right-hand side of the assignment is not evaluated at
154 1.10 rillig # the time of assignment but only later, when ${VAR} appears in the condition.
155 1.10 rillig #
156 1.10 rillig # At that point, both REF.word and REF.undef are defined.
157 1.9 rillig REF.word= 1:$$ 2:$$$$ 4:$$$$$$$$
158 1.10 rillig .undef REF.undef
159 1.10 rillig VAR= ${:Uword undef:@word@${REF.${word}}@}, direct: ${REF.word} ${REF.undef}
160 1.10 rillig REF.word= word.after
161 1.10 rillig REF.undef= undef.after
162 1.10 rillig .if ${VAR} != "word.after undef.after, direct: word.after undef.after"
163 1.9 rillig . error
164 1.9 rillig .endif
165 1.9 rillig
166 1.9 rillig
167 1.8 rillig # Between var.c 1.42 from 2000-05-11 and before parse.c 1.520 from 2020-12-27,
168 1.8 rillig # if the variable name in a ':=' assignment referred to an undefined variable,
169 1.8 rillig # there were actually 2 assignments to different variables:
170 1.8 rillig #
171 1.8 rillig # Global["VAR_SUBST_${UNDEF}"] = ""
172 1.8 rillig # Global["VAR_SUBST_"] = ""
173 1.8 rillig #
174 1.8 rillig # The variable name with the empty value actually included a dollar sign.
175 1.8 rillig # Variable names with dollars are not used in practice.
176 1.8 rillig #
177 1.8 rillig # It might be a good idea to forbid undefined variables on the left-hand side
178 1.8 rillig # of a variable assignment.
179 1.5 rillig .undef UNDEF
180 1.7 rillig VAR_ASSIGN_${UNDEF}= assigned by '='
181 1.7 rillig VAR_SUBST_${UNDEF}:= assigned by ':='
182 1.7 rillig .if ${VAR_ASSIGN_} != "assigned by '='"
183 1.7 rillig . error
184 1.7 rillig .endif
185 1.8 rillig .if defined(${:UVAR_SUBST_\${UNDEF\}})
186 1.7 rillig . error
187 1.7 rillig .endif
188 1.7 rillig .if ${VAR_SUBST_} != "assigned by ':='"
189 1.7 rillig . error
190 1.7 rillig .endif
191 1.3 rillig
192 1.12 rillig
193 1.12 rillig # The following test case demonstrates that the variable 'LATER' is preserved
194 1.12 rillig # in the ':=' assignment since the variable 'LATER' is not yet defined.
195 1.12 rillig # After the assignment to 'LATER', evaluating the variable 'INDIRECT'
196 1.12 rillig # evaluates 'LATER' as well.
197 1.12 rillig #
198 1.12 rillig .undef LATER
199 1.12 rillig INDIRECT:= ${LATER:S,value,replaced,}
200 1.12 rillig .if ${INDIRECT} != ""
201 1.12 rillig . error
202 1.12 rillig .endif
203 1.12 rillig LATER= late-value
204 1.12 rillig .if ${INDIRECT} != "late-replaced"
205 1.12 rillig . error
206 1.12 rillig .endif
207 1.12 rillig
208 1.12 rillig
209 1.12 rillig # Same as the test case above, except for the additional modifier ':tl' when
210 1.12 rillig # evaluating the variable 'INDIRECT'. Nothing surprising here.
211 1.12 rillig .undef LATER
212 1.12 rillig .undef later
213 1.12 rillig INDIRECT:= ${LATER:S,value,replaced,}
214 1.12 rillig .if ${INDIRECT:tl} != ""
215 1.12 rillig . error
216 1.12 rillig .endif
217 1.12 rillig LATER= uppercase-value
218 1.12 rillig later= lowercase-value
219 1.12 rillig .if ${INDIRECT:tl} != "uppercase-replaced"
220 1.12 rillig . error
221 1.12 rillig .endif
222 1.12 rillig
223 1.12 rillig
224 1.12 rillig # Similar to the two test cases above, the situation gets a bit more involved
225 1.12 rillig # here, due to the double indirection. The variable 'indirect' is supposed to
226 1.12 rillig # be the lowercase version of the variable 'INDIRECT'.
227 1.12 rillig #
228 1.12 rillig # The assignment operator ':=' for the variable 'INDIRECT' could be a '=' as
229 1.12 rillig # well, it wouldn't make a difference in this case. The crucial detail is the
230 1.14 rillig # assignment operator ':=' for the variable 'indirect'. During this
231 1.12 rillig # assignment, the variable modifier ':S,value,replaced,' is converted to
232 1.12 rillig # lowercase, which turns 'S' into 's', thus producing an unknown modifier.
233 1.12 rillig # In this case, make issues a warning, but in cases where the modifier
234 1.12 rillig # includes a '=', the modifier would be interpreted as a SysV-style
235 1.12 rillig # substitution like '.c=.o', and make would not issue a warning, leading to
236 1.12 rillig # silent unexpected behavior.
237 1.12 rillig #
238 1.14 rillig # As of 2021-11-20, the actual behavior is unexpected. Fixing it is not
239 1.14 rillig # trivial. When the assignment to 'indirect' takes place, the expressions
240 1.14 rillig # from the nested expression could be preserved, like this:
241 1.14 rillig #
242 1.14 rillig # Start with:
243 1.14 rillig #
244 1.14 rillig # indirect:= ${INDIRECT:tl}
245 1.14 rillig #
246 1.14 rillig # Since INDIRECT is defined, expand it, remembering that the modifier
247 1.14 rillig # ':tl' must still be applied to the final result.
248 1.14 rillig #
249 1.14 rillig # indirect:= ${LATER:S,value,replaced,} \
250 1.14 rillig # OK \
251 1.14 rillig # ${LATER:value=sysv}
252 1.14 rillig #
253 1.14 rillig # The variable 'LATER' is not defined. An idea may be to append the
254 1.14 rillig # remaining modifier ':tl' to each expression that is starting with an
255 1.14 rillig # undefined variable, resulting in:
256 1.14 rillig #
257 1.14 rillig # indirect:= ${LATER:S,value,replaced,:tl} \
258 1.14 rillig # OK \
259 1.14 rillig # ${LATER:value=sysv:tl}
260 1.14 rillig #
261 1.14 rillig # This would work for the first expression. The second expression ends
262 1.14 rillig # with the SysV modifier ':from=to', and when this modifier is parsed,
263 1.14 rillig # it consumes all characters until the end of the expression, which in
264 1.14 rillig # this case would replace the suffix 'value' with the literal 'sysv:tl',
265 1.14 rillig # ignoring that the ':tl' was intended to be an additional modifier.
266 1.14 rillig #
267 1.14 rillig # Due to all of this, this surprising behavior is not easy to fix.
268 1.14 rillig #
269 1.12 rillig .undef LATER
270 1.12 rillig .undef later
271 1.13 rillig INDIRECT:= ${LATER:S,value,replaced,} OK ${LATER:value=sysv}
272 1.12 rillig indirect:= ${INDIRECT:tl}
273 1.23 rillig # expect+1: Unknown modifier ":s,value,replaced,"
274 1.13 rillig .if ${indirect} != " ok "
275 1.12 rillig . error
276 1.12 rillig .else
277 1.25 rillig . error
278 1.12 rillig .endif
279 1.12 rillig LATER= uppercase-value
280 1.12 rillig later= lowercase-value
281 1.23 rillig # expect+1: Unknown modifier ":s,value,replaced,"
282 1.13 rillig .if ${indirect} != "uppercase-replaced ok uppercase-sysv"
283 1.25 rillig . error
284 1.12 rillig .else
285 1.12 rillig . error
286 1.12 rillig .endif
287 1.12 rillig
288 1.12 rillig
289 1.24 rillig # FIXME: The expression is evaluated twice, for no obvious reason.
290 1.24 rillig # expect+5: Bad condition
291 1.24 rillig # expect+4: Unknown modifier ":Z1"
292 1.24 rillig # expect+3: Unknown modifier ":Z2"
293 1.24 rillig # expect+2: Unknown modifier ":Z1"
294 1.24 rillig # expect+1: Unknown modifier ":Z2"
295 1.24 rillig _:= ${ < 0 :?${:Z1}:${:Z2}}
296