xref: /freebsd/contrib/bmake/unit-tests/varmod.mk (revision 6a7405f5a6b639682cacf01e35d561411ff556aa)
1# $NetBSD: varmod.mk,v 1.22 2025/01/11 20:54:46 rillig Exp $
2#
3# Tests for variable modifiers, such as :Q, :S,from,to or :Ufallback.
4#
5# See also:
6#	varparse-errors.mk
7
8# As of 2024-06-05, the possible behaviors during parsing are:
9#
10# * `strict`: the parsing style used by most modifiers:
11#   * either uses `ParseModifierPart` or parses the modifier literal
12#   * other modifiers may follow, separated by a ':'
13#
14# * `greedy`: calls `ParseModifierPart` with `ch->endc`; this means
15#   that no further modifiers are parsed in that expression.
16#
17# * `no-colon`: after parsing this modifier, the following modifier
18#   does not need to be separated by a colon.
19#   Omitting this colon is bad style.
20#
21# * `individual`: parsing this modifier does not follow the common
22#   pattern of calling `ParseModifierPart`.
23#
24# The SysV column says whether a parse error in the modifier falls back
25# trying the `:from=to` System V modifier.
26#
27# | **Operator** | **Behavior** | **Remarks**        | **SysV** |
28# |--------------|--------------|--------------------|----------|
29# | `!`          | no-colon     |                    | no       |
30# | `:=`         | greedy       |                    | yes      |
31# | `?:`         | greedy       |                    | no       |
32# | `@`          | no-colon     |                    | no       |
33# | `C`          | no-colon     |                    | no       |
34# | `D`          | individual   | custom parser      | N/A      |
35# | `E`          | strict       |                    | yes      |
36# | `H`          | strict       |                    | yes      |
37# | `L`          | no-colon     |                    | N/A      |
38# | `M`          | individual   | custom parser      | N/A      |
39# | `N`          | individual   | custom parser      | N/A      |
40# | `O`          | strict       | only literal value | no       |
41# | `P`          | no-colon     |                    | N/A      |
42# | `Q`          | strict       |                    | yes      |
43# | `R`          | strict       |                    | yes      |
44# | `S`          | no-colon     |                    | N/A      |
45# | `T`          | strict       |                    | N/A      |
46# | `U`          | individual   | custom parser      | N/A      |
47# | `[`          | strict       |                    | no       |
48# | `_`          | individual   | strcspn            | yes      |
49# | `gmtime`     | strict       |                    | yes      |
50# | `hash`       | strict       |                    | N/A      |
51# | `localtime`  | strict       |                    | yes      |
52# | `q`          | strict       |                    | yes      |
53# | `range`      | strict       |                    | N/A      |
54# | `sh`         | strict       |                    | N/A      |
55# | `t`          | strict       |                    | no       |
56# | `u`          | strict       |                    | yes      |
57# | `from=to`    | greedy       | SysV, fallback     | N/A      |
58
59# These tests assume
60.MAKE.SAVE_DOLLARS = yes
61
62DOLLAR1=	$$
63DOLLAR2=	${:U\$}
64
65# To get a single '$' sign in the value of an expression, it has to
66# be written as '$$' in a literal variable value.
67#
68# See Var_Parse, where it calls Var_Subst.
69.if ${DOLLAR1} != "\$"
70.  error
71.endif
72
73# Another way to get a single '$' sign is to use the :U modifier.  In the
74# argument of that modifier, a '$' is escaped using the backslash instead.
75#
76# See Var_Parse, where it calls Var_Subst.
77.if ${DOLLAR2} != "\$"
78.  error
79.endif
80
81# It is also possible to use the :U modifier directly in the expression.
82#
83# See Var_Parse, where it calls Var_Subst.
84.if ${:U\$} != "\$"
85.  error
86.endif
87
88# XXX: As of 2020-09-13, it is not possible to use '$$' in a variable name
89# to mean a single '$'.  This contradicts the manual page, which says that
90# '$' can be escaped as '$$'.
91.if ${$$:L} != ""
92.  error
93.endif
94
95# In lint mode, make prints helpful error messages.
96# For compatibility, make does not print these error messages in normal mode.
97# Should it?
98.MAKEFLAGS: -dL
99# expect+2: To escape a dollar, use \$, not $$, at "$$:L} != """
100# expect+1: Invalid variable name ':', at "$:L} != """
101.if ${$$:L} != ""
102.  error
103.endif
104
105# A '$' followed by nothing is an error as well.
106# expect+1: Dollar followed by nothing
107.if ${:Uword:@word@${word}$@} != "word"
108.  error
109.endif
110
111# The variable modifier :P does not fall back to the SysV modifier.
112# Therefore the modifier :P=RE generates a parse error.
113# XXX: The .error should not be reached since the expression is
114# malformed, and this error should be propagated up to Cond_EvalLine.
115VAR=	STOP
116# expect+1: Missing delimiter ':' after modifier "P"
117.if ${VAR:P=RE} != "STORE"
118# expect+1: Missing argument for ".error"
119.  error
120.endif
121
122# Test the word selection modifier ':[n]' with a very large number that is
123# larger than ULONG_MAX for any supported platform.
124# expect+1: Bad modifier ":[99333000222000111000]"
125.if ${word:L:[99333000222000111000]}
126.endif
127# expect+1: Bad modifier ":[2147483648]"
128.if ${word:L:[2147483648]}
129.endif
130
131# Test the range generation modifier ':range=n' with a very large number that
132# is larger than SIZE_MAX for any supported platform.
133# expect+1: Invalid number "99333000222000111000}" for ':range' modifier
134.if ${word:L:range=99333000222000111000}
135.endif
136
137# In an indirect modifier, the delimiter is '\0', which at the same time marks
138# the end of the string.  The sequence '\\' '\0' is not an escaped delimiter,
139# as it would be wrong to skip past the end of the string.
140# expect+1: Invalid time value "\"
141.if ${:${:Ugmtime=\\}}
142.  error
143.endif
144
145# Test a '$' at the end of a modifier part, for all modifiers in the order
146# listed in ApplyModifier.
147#
148# The only modifier parts where an unescaped '$' makes sense at the end are
149# the 'from' parts of the ':S' and ':C' modifiers.  In all other modifier
150# parts, an unescaped '$' is an undocumented and discouraged edge case, as it
151# means the same as an escaped '$'.
152.if ${:U:!printf '%s\n' $!} != "\$"
153.  error
154.endif
155# expect+1: Dollar followed by nothing
156.if ${VAR::=value$} != "" || ${VAR} != "value"
157.  error
158.endif
159${:U }=		<space>
160# expect+2: Dollar followed by nothing
161# expect+1: Dollar followed by nothing
162.if ${VAR::+=appended$} != "" || ${VAR} != "value<space>appended"
163.  error
164.endif
165.if ${1:?then$:else$} != "then\$"
166.  error
167.endif
168.if ${0:?then$:else$} != "else\$"
169.  error
170.endif
171# expect+1: Dollar followed by nothing
172.if ${word:L:@w@$w$@} != "word"
173.  error
174.endif
175# expect+1: Bad modifier ":[$]"
176.if ${word:[$]}
177.  error
178.else
179.  error
180.endif
181VAR_DOLLAR=	VAR$$
182.if ${word:L:_=VAR$} != "word" || ${${VAR_DOLLAR}} != "word"
183.  error
184.endif
185.if ${word:L:C,d$,m,} != "worm"
186.  error
187.endif
188.if ${word:L:C,d,$,} != "wor\$"
189.  error
190.endif
191# expect+2: Dollar followed by nothing
192# expect+1: Invalid variable name '}', at "$} != "set""
193.if ${VAR:Dset$} != "set"
194.  error
195.endif
196# expect+1: Invalid variable name '}', at "$} != "fallback""
197.if ${:Ufallback$} != "fallback"
198.  error
199.endif
200# expect+1: Invalid time value "1000$"
201.if ${%y:L:gmtime=1000$}
202.  error
203.else
204.  error
205.endif
206# expect+1: Invalid time value "1000$"
207.if ${%y:L:localtime=1000$}
208.  error
209.else
210.  error
211.endif
212# expect+1: Dollar followed by nothing
213.if ${word:L:Mw*$} != "word"
214.  error
215.endif
216# expect+1: Dollar followed by nothing
217.if ${word:L:NX*$} != "word"
218.  error
219.endif
220# expect+1: Invalid argument 'fallback$' for modifier ':mtime'
221.if ${.:L:mtime=fallback$}
222.  error
223.else
224.  error
225.endif
226.if ${word:L:S,d$,m,} != "worm"
227.  error
228.endif
229.if ${word:L:S,d,m$,} != "worm\$"
230.  error
231.endif
232