xref: /freebsd/contrib/bmake/unit-tests/varmod-sysv.mk (revision 759b177aecbfc49ebc900739954ac56b1aa5fc53)
1*759b177aSSimon J. Gerraty# $NetBSD: varmod-sysv.mk,v 1.24 2025/03/30 00:35:52 rillig Exp $
22c3632d1SSimon J. Gerraty#
3b0c40a00SSimon J. Gerraty# Tests for the variable modifier ':from=to', which replaces the suffix
42c3632d1SSimon J. Gerraty# "from" with "to".  It can also use '%' as a wildcard.
52c3632d1SSimon J. Gerraty#
62c3632d1SSimon J. Gerraty# This modifier is applied when the other modifiers don't match exactly.
7956e45f6SSimon J. Gerraty#
8956e45f6SSimon J. Gerraty# See ApplyModifier_SysV.
92c3632d1SSimon J. Gerraty
10b0c40a00SSimon J. Gerraty# A typical use case for the modifier ':from=to' is conversion of filename
11956e45f6SSimon J. Gerraty# extensions.
12956e45f6SSimon J. Gerraty.if ${src.c:L:.c=.o} != "src.o"
13956e45f6SSimon J. Gerraty.  error
14956e45f6SSimon J. Gerraty.endif
152c3632d1SSimon J. Gerraty
16956e45f6SSimon J. Gerraty# The modifier applies to each word on its own.
17956e45f6SSimon J. Gerraty.if ${one.c two.c three.c:L:.c=.o} != "one.o two.o three.o"
18956e45f6SSimon J. Gerraty.  error
19956e45f6SSimon J. Gerraty.endif
20956e45f6SSimon J. Gerraty
21956e45f6SSimon J. Gerraty# Words that don't match the pattern are passed unmodified.
22956e45f6SSimon J. Gerraty.if ${src.c src.h:L:.c=.o} != "src.o src.h"
23956e45f6SSimon J. Gerraty.  error
24956e45f6SSimon J. Gerraty.endif
25956e45f6SSimon J. Gerraty
26b0c40a00SSimon J. Gerraty# The modifier ':from=to' is therefore often combined with the modifier ':M'.
27956e45f6SSimon J. Gerraty.if ${src.c src.h:L:M*.c:.c=.o} != "src.o"
28956e45f6SSimon J. Gerraty.  error
29956e45f6SSimon J. Gerraty.endif
30956e45f6SSimon J. Gerraty
31b0c40a00SSimon J. Gerraty# Another use case for the modifier ':from=to' is to append a suffix to each
32956e45f6SSimon J. Gerraty# word.  In this case, the "from" string is empty, therefore it always
33b0c40a00SSimon J. Gerraty# matches.  The same effect can be achieved with the modifier ':S,$,teen,'.
34956e45f6SSimon J. Gerraty.if ${four six seven nine:L:=teen} != "fourteen sixteen seventeen nineteen"
35956e45f6SSimon J. Gerraty.  error
36956e45f6SSimon J. Gerraty.endif
37956e45f6SSimon J. Gerraty
38b0c40a00SSimon J. Gerraty# The modifier ':from=to' can also be used to surround each word by strings.
39956e45f6SSimon J. Gerraty# It might be tempting to use this for enclosing a string in quotes for the
40b0c40a00SSimon J. Gerraty# shell, but that's the job of the modifier ':Q'.
41956e45f6SSimon J. Gerraty.if ${one two three:L:%=(%)} != "(one) (two) (three)"
42956e45f6SSimon J. Gerraty.  error
43956e45f6SSimon J. Gerraty.endif
44956e45f6SSimon J. Gerraty
45b0c40a00SSimon J. Gerraty# When the modifier ':from=to' is parsed, it lasts until the closing brace
46b0c40a00SSimon J. Gerraty# or parenthesis.  The ':Q' in the below expression may look like a modifier
47b0c40a00SSimon J. Gerraty# but it isn't.  It is part of the replacement string.
48956e45f6SSimon J. Gerraty.if ${a b c d e:L:%a=x:Q} != "x:Q b c d e"
49956e45f6SSimon J. Gerraty.  error
50956e45f6SSimon J. Gerraty.endif
51956e45f6SSimon J. Gerraty
52d5e0a182SSimon J. Gerraty# In the modifier ':from=to', both parts can contain expressions.
53956e45f6SSimon J. Gerraty.if ${one two:L:${:Uone}=${:U1}} != "1 two"
54956e45f6SSimon J. Gerraty.  error
55956e45f6SSimon J. Gerraty.endif
56956e45f6SSimon J. Gerraty
57b0c40a00SSimon J. Gerraty# In the modifier ':from=to', the "from" part is expanded exactly once.
58956e45f6SSimon J. Gerraty.if ${:U\$ \$\$ \$\$\$\$:${:U\$\$\$\$}=4} != "\$ \$\$ 4"
59956e45f6SSimon J. Gerraty.  error
60956e45f6SSimon J. Gerraty.endif
61956e45f6SSimon J. Gerraty
62b0c40a00SSimon J. Gerraty# In the modifier ':from=to', the "to" part is expanded exactly twice.
63956e45f6SSimon J. Gerraty# XXX: The right-hand side should be expanded only once.
64956e45f6SSimon J. Gerraty# XXX: It's hard to get the escaping correct here, and to read that.
65956e45f6SSimon J. Gerraty# XXX: It's not intuitive why the closing brace must be escaped but not
66956e45f6SSimon J. Gerraty#      the opening brace.
67956e45f6SSimon J. Gerraty.if ${:U1 2 4:4=${:Uonce\${\:Utwice\}}} != "1 2 oncetwice"
68956e45f6SSimon J. Gerraty.  error
69956e45f6SSimon J. Gerraty.endif
70956e45f6SSimon J. Gerraty
71956e45f6SSimon J. Gerraty# The replacement string can contain spaces, thereby changing the number
72d5e0a182SSimon J. Gerraty# of words in the expression.
73956e45f6SSimon J. Gerraty.if ${In:L:%=% ${:Uthe Sun}} != "In the Sun"
74956e45f6SSimon J. Gerraty.  error
75956e45f6SSimon J. Gerraty.endif
76956e45f6SSimon J. Gerraty
77956e45f6SSimon J. Gerraty# If the variable value is empty, it is debatable whether it consists of a
78b0c40a00SSimon J. Gerraty# single empty word, or no word at all.  The modifier ':from=to' treats it as
79956e45f6SSimon J. Gerraty# no word at all.
8006b9b3e0SSimon J. Gerraty#
8106b9b3e0SSimon J. Gerraty# See SysVMatch, which doesn't handle w_len == p_len specially.
82956e45f6SSimon J. Gerraty.if ${:L:=suffix} != ""
83956e45f6SSimon J. Gerraty.  error
84956e45f6SSimon J. Gerraty.endif
85956e45f6SSimon J. Gerraty
86956e45f6SSimon J. Gerraty# If the variable value is empty, it is debatable whether it consists of a
8706b9b3e0SSimon J. Gerraty# single empty word (before 2020-05-06), or no word at all (since 2020-05-06).
8806b9b3e0SSimon J. Gerraty#
8906b9b3e0SSimon J. Gerraty# See SysVMatch, percent != NULL && w[0] == '\0'.
90956e45f6SSimon J. Gerraty.if ${:L:%=suffix} != ""
91956e45f6SSimon J. Gerraty.  error
92956e45f6SSimon J. Gerraty.endif
932c3632d1SSimon J. Gerraty
942c3632d1SSimon J. Gerraty# Before 2020-07-19, an ampersand could be used in the replacement part
95956e45f6SSimon J. Gerraty# of a SysV substitution modifier, and it was replaced with the whole match,
96b0c40a00SSimon J. Gerraty# just like in the modifier ':S'.
97956e45f6SSimon J. Gerraty#
98956e45f6SSimon J. Gerraty# This was probably a copy-and-paste mistake since the code for the SysV
99b0c40a00SSimon J. Gerraty# modifier looked a lot like the code for the modifiers ':S' and ':C'.
100956e45f6SSimon J. Gerraty# The ampersand is not mentioned in the manual page.
101956e45f6SSimon J. Gerraty.if ${a.bcd.e:L:a.%=%} != "bcd.e"
102956e45f6SSimon J. Gerraty.  error
103956e45f6SSimon J. Gerraty.endif
104956e45f6SSimon J. Gerraty# Before 2020-07-19, the result of the expression was "a.bcd.e".
105956e45f6SSimon J. Gerraty.if ${a.bcd.e:L:a.%=&} != "&"
106956e45f6SSimon J. Gerraty.  error
107956e45f6SSimon J. Gerraty.endif
1082c3632d1SSimon J. Gerraty
1092c3632d1SSimon J. Gerraty# Before 2020-07-20, when a SysV modifier was parsed, a single dollar
110956e45f6SSimon J. Gerraty# before the '=' was parsed (but not interpreted) as an anchor.
111956e45f6SSimon J. Gerraty# Parsing something without then evaluating it accordingly doesn't make
112b0c40a00SSimon J. Gerraty# sense, so this has been fixed.
113956e45f6SSimon J. Gerraty.if ${value:L:e$=x} != "value"
114956e45f6SSimon J. Gerraty.  error
115956e45f6SSimon J. Gerraty.endif
116b0c40a00SSimon J. Gerraty# Before 2020-07-20, the modifier ':e$=x' was parsed as having a left-hand
117b0c40a00SSimon J. Gerraty# side 'e' and a right-hand side 'x'.  The dollar was parsed (but not
118956e45f6SSimon J. Gerraty# interpreted) as 'anchor at the end'.  Therefore the modifier was equivalent
119b0c40a00SSimon J. Gerraty# to ':e=x', which doesn't match the string "value$".  Therefore the whole
120956e45f6SSimon J. Gerraty# expression evaluated to "value$".
121956e45f6SSimon J. Gerraty.if ${${:Uvalue\$}:L:e$=x} != "valux"
122956e45f6SSimon J. Gerraty.  error
123956e45f6SSimon J. Gerraty.endif
124956e45f6SSimon J. Gerraty.if ${value:L:e=x} != "valux"
125956e45f6SSimon J. Gerraty.  error
126956e45f6SSimon J. Gerraty.endif
1272c3632d1SSimon J. Gerraty
1282c3632d1SSimon J. Gerraty# Words that don't match are copied unmodified.
129956e45f6SSimon J. Gerraty.if ${:Ufile.c file.h:%.c=%.cpp} != "file.cpp file.h"
130956e45f6SSimon J. Gerraty.  error
1312c3632d1SSimon J. Gerraty.endif
132956e45f6SSimon J. Gerraty
133956e45f6SSimon J. Gerraty# The % placeholder can be anywhere in the string, it doesn't have to be at
134956e45f6SSimon J. Gerraty# the beginning of the pattern.
135956e45f6SSimon J. Gerraty.if ${:Ufile.c other.c:file.%=renamed.%} != "renamed.c other.c"
136956e45f6SSimon J. Gerraty.  error
137956e45f6SSimon J. Gerraty.endif
138956e45f6SSimon J. Gerraty
139956e45f6SSimon J. Gerraty# It's also possible to modify each word by replacing the prefix and adding
140956e45f6SSimon J. Gerraty# a suffix.
141956e45f6SSimon J. Gerraty.if ${one two:L:o%=a%w} != "anew two"
142956e45f6SSimon J. Gerraty.  error
143956e45f6SSimon J. Gerraty.endif
144956e45f6SSimon J. Gerraty
145956e45f6SSimon J. Gerraty# Each word gets the suffix "X" appended.
146956e45f6SSimon J. Gerraty.if ${one two:L:=X} != "oneX twoX"
147956e45f6SSimon J. Gerraty.  error
148956e45f6SSimon J. Gerraty.endif
149956e45f6SSimon J. Gerraty
150956e45f6SSimon J. Gerraty# The suffix "o" is replaced with "X".
151956e45f6SSimon J. Gerraty.if ${one two:L:o=X} != "one twX"
152956e45f6SSimon J. Gerraty.  error
153956e45f6SSimon J. Gerraty.endif
154956e45f6SSimon J. Gerraty
155956e45f6SSimon J. Gerraty# The suffix "o" is replaced with nothing.
156956e45f6SSimon J. Gerraty.if ${one two:L:o=} != "one tw"
157956e45f6SSimon J. Gerraty.  error
158956e45f6SSimon J. Gerraty.endif
159956e45f6SSimon J. Gerraty
160956e45f6SSimon J. Gerraty# The suffix "o" is replaced with a literal percent.  The percent is only
161956e45f6SSimon J. Gerraty# a wildcard when it appears on the left-hand side.
162956e45f6SSimon J. Gerraty.if ${one two:L:o=%} != "one tw%"
163956e45f6SSimon J. Gerraty.  error
164956e45f6SSimon J. Gerraty.endif
165956e45f6SSimon J. Gerraty
166956e45f6SSimon J. Gerraty# Each word with the suffix "o" is replaced with "X".  The percent is a
167956e45f6SSimon J. Gerraty# wildcard even though the right-hand side does not contain another percent.
168956e45f6SSimon J. Gerraty.if ${one two:L:%o=X} != "one X"
169956e45f6SSimon J. Gerraty.  error
170956e45f6SSimon J. Gerraty.endif
171956e45f6SSimon J. Gerraty
172956e45f6SSimon J. Gerraty# Each word with the prefix "o" is replaced with "X".  The percent is a
173956e45f6SSimon J. Gerraty# wildcard even though the right-hand side does not contain another percent.
174956e45f6SSimon J. Gerraty.if ${one two:L:o%=X} != "X two"
175956e45f6SSimon J. Gerraty.  error
176956e45f6SSimon J. Gerraty.endif
177956e45f6SSimon J. Gerraty
178956e45f6SSimon J. Gerraty# For each word with the prefix "o" and the suffix "e", the whole word is
179956e45f6SSimon J. Gerraty# replaced with "X".
180956e45f6SSimon J. Gerraty.if ${one two oe oxen:L:o%e=X} != "X two X oxen"
181956e45f6SSimon J. Gerraty.  error
182956e45f6SSimon J. Gerraty.endif
183956e45f6SSimon J. Gerraty
184956e45f6SSimon J. Gerraty# Only the first '%' is the wildcard.
185956e45f6SSimon J. Gerraty.if ${one two o%e other%e:L:o%%e=X} != "one two X X"
186956e45f6SSimon J. Gerraty.  error
187956e45f6SSimon J. Gerraty.endif
188956e45f6SSimon J. Gerraty
189956e45f6SSimon J. Gerraty# In the replacement, only the first '%' is the placeholder, all others
190956e45f6SSimon J. Gerraty# are literal percent characters.
191956e45f6SSimon J. Gerraty.if ${one two:L:%=%%} != "one% two%"
192956e45f6SSimon J. Gerraty.  error
193956e45f6SSimon J. Gerraty.endif
194956e45f6SSimon J. Gerraty
195956e45f6SSimon J. Gerraty# In the word "one", only a prefix of the pattern suffix "nes" matches,
196956e45f6SSimon J. Gerraty# the whole word is too short.  Therefore it doesn't match.
197956e45f6SSimon J. Gerraty.if ${one two:L:%nes=%xxx} != "one two"
198956e45f6SSimon J. Gerraty.  error
199956e45f6SSimon J. Gerraty.endif
200956e45f6SSimon J. Gerraty
201b0c40a00SSimon J. Gerraty# The modifier ':from=to' can be used to replace both the prefix and a suffix
202956e45f6SSimon J. Gerraty# of a word with other strings.  This is not possible with a single :S
203956e45f6SSimon J. Gerraty# modifier, and using a :C modifier for the same task looks more complicated
204956e45f6SSimon J. Gerraty# in many cases.
205956e45f6SSimon J. Gerraty.if ${prefix-middle-suffix:L:prefix-%-suffix=p-%-s} != "p-middle-s"
206956e45f6SSimon J. Gerraty.  error
207956e45f6SSimon J. Gerraty.endif
208956e45f6SSimon J. Gerraty
209d5e0a182SSimon J. Gerraty# This is not a SysV modifier since the nested expression expands
210956e45f6SSimon J. Gerraty# to an empty string.  The '=' in it should be irrelevant during parsing.
21122619282SSimon J. Gerraty# XXX: As of 2024-06-30, this expression generates an "Unfinished modifier"
21206b9b3e0SSimon J. Gerraty# error, while the correct error message would be "Unknown modifier" since
21306b9b3e0SSimon J. Gerraty# there is no modifier named "fromto".
214*759b177aSSimon J. Gerraty# expect+1: Unfinished modifier after "from${:D=}to}", expecting "="
21522619282SSimon J. Gerraty.if ${word216:L:from${:D=}to}
216956e45f6SSimon J. Gerraty.  error
217956e45f6SSimon J. Gerraty.endif
218956e45f6SSimon J. Gerraty
219956e45f6SSimon J. Gerraty# XXX: This specially constructed case demonstrates that the SysV modifier
220956e45f6SSimon J. Gerraty# lasts longer than expected.  The whole expression initially has the value
221956e45f6SSimon J. Gerraty# "fromto}...".  The next modifier is a SysV modifier.  ApplyModifier_SysV
222956e45f6SSimon J. Gerraty# parses the modifier as "from${:D=}to", ending at the '}'.  Next, the two
223956e45f6SSimon J. Gerraty# parts of the modifier are parsed using ParseModifierPart, which scans
224d5e0a182SSimon J. Gerraty# differently, properly handling nested expressions.  The two parts
225956e45f6SSimon J. Gerraty# are now "fromto}..." and "replaced".
226956e45f6SSimon J. Gerraty.if "${:Ufromto\}...:from${:D=}to}...=replaced}" != "replaced"
227956e45f6SSimon J. Gerraty.  error
228956e45f6SSimon J. Gerraty.endif
229956e45f6SSimon J. Gerraty
230956e45f6SSimon J. Gerraty# As of 2020-10-06, the right-hand side of the SysV modifier is expanded
231956e45f6SSimon J. Gerraty# twice.  The first expansion happens in ApplyModifier_SysV, where the
232956e45f6SSimon J. Gerraty# modifier is split into its two parts.  The second expansion happens
233956e45f6SSimon J. Gerraty# when each word is replaced in ModifyWord_SYSVSubst.
234956e45f6SSimon J. Gerraty# XXX: This is unexpected.  Add more test case to demonstrate the effects
235956e45f6SSimon J. Gerraty# of removing one of the expansions.
236956e45f6SSimon J. GerratyVALUE=		value
237956e45f6SSimon J. GerratyINDIRECT=	1:${VALUE} 2:$${VALUE} 4:$$$${VALUE}
238956e45f6SSimon J. Gerraty.if ${x:L:x=${INDIRECT}} != "1:value 2:value 4:\${VALUE}"
239956e45f6SSimon J. Gerraty.  error
240956e45f6SSimon J. Gerraty.endif
241956e45f6SSimon J. Gerraty
242b0c40a00SSimon J. Gerraty# Test all relevant combinations of prefix, '%' and suffix in both the pattern
243b0c40a00SSimon J. Gerraty# and the replacement.
244b0c40a00SSimon J. Gerraty!=1>&2	printf '%-24s %-24s %-24s\n' 'word' 'modifier' 'result'
245b0c40a00SSimon J. Gerraty.for from in '' ffix % pre% %ffix pre%ffix
246b0c40a00SSimon J. Gerraty.  for to in '' NS % %NS NPre% NPre%NS
247b0c40a00SSimon J. Gerraty.    for word in '' suffix prefix pre-middle-suffix
248b0c40a00SSimon J. Gerraty.      for mod in ${from:N''}=${to:N''}
249b0c40a00SSimon J. Gerraty!=1>&2	printf '%-24s %-24s "%s"\n' ''${word:Q} ''${mod:Q} ''${word:N'':${mod}:Q}
250b0c40a00SSimon J. Gerraty.      endfor
251b0c40a00SSimon J. Gerraty.    endfor
252b0c40a00SSimon J. Gerraty.  endfor
253b0c40a00SSimon J. Gerraty.endfor
254b0c40a00SSimon J. Gerraty
2558d5c8e21SSimon J. Gerraty
2568d5c8e21SSimon J. Gerraty# The error case of an unfinished ':from=to' modifier after the '=' requires
2578d5c8e21SSimon J. Gerraty# an expression that is missing the closing '}'.
258*759b177aSSimon J. Gerraty# expect+1: Unfinished modifier after "$(})", expecting "}"
2598d5c8e21SSimon J. Gerraty.if ${error:L:from=$(})
2608d5c8e21SSimon J. Gerraty.endif
2618d5c8e21SSimon J. Gerraty
2628d5c8e21SSimon J. Gerraty
263*759b177aSSimon J. Gerraty# The various ":t..." modifiers fall back to the ":from=to" modifier.
264*759b177aSSimon J. Gerraty.if ${:Utarget:target=source} != "source"
265*759b177aSSimon J. Gerraty.  error
266*759b177aSSimon J. Gerraty.endif
267*759b177aSSimon J. Gerraty.if ${:Ufile.ts:ts=js} != "file.js"
268*759b177aSSimon J. Gerraty.  error
269*759b177aSSimon J. Gerraty.endif
270*759b177aSSimon J. Gerraty.if ${:Ufile.tsx:tsx=jsx} != "file.jsx"
271*759b177aSSimon J. Gerraty.  error
272*759b177aSSimon J. Gerraty.endif
273*759b177aSSimon J. Gerraty.if ${:Ufile.ts\\part:ts\part=replaced} != "file.replaced"
274*759b177aSSimon J. Gerraty.  error
275*759b177aSSimon J. Gerraty.endif
276*759b177aSSimon J. Gerraty.if ${:Ufile.ts\\123xyz:ts\123xyz=gone} != "file.gone"
277*759b177aSSimon J. Gerraty.  error
278*759b177aSSimon J. Gerraty.endif
279*759b177aSSimon J. Gerraty# Since the ":ts=" modifier is a valid form of the ":ts" modifier, don't fall
280*759b177aSSimon J. Gerraty# back to the ":from=to" modifier.
281*759b177aSSimon J. Gerraty.if ${:U1 2 3 file.ts:ts=} != "1=2=3=file.ts"
282*759b177aSSimon J. Gerraty.  error
283*759b177aSSimon J. Gerraty.endif
284*759b177aSSimon J. Gerraty
285*759b177aSSimon J. Gerraty
286956e45f6SSimon J. Gerratyall:
287