xref: /freebsd/contrib/bmake/unit-tests/varmod.mk (revision c5c02a131a0e2ef52771e683269bc8778fe511f3)
1# $NetBSD: varmod.mk,v 1.18 2024/07/05 19:47:22 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: while evaluating "${:Uword:@word@${word}$@} != "word"" with value "word": 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: while evaluating variable "VAR" with value "VAR": 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+2: while evaluating variable "word" with value "word": Bad modifier ":[99333000222000111000]"
125# expect+1: Malformed conditional (${word:L:[99333000222000111000]})
126.if ${word:L:[99333000222000111000]}
127.endif
128# expect+2: while evaluating variable "word" with value "word": Bad modifier ":[2147483648]"
129# expect+1: Malformed conditional (${word:L:[2147483648]})
130.if ${word:L:[2147483648]}
131.endif
132
133# Test the range generation modifier ':range=n' with a very large number that
134# is larger than SIZE_MAX for any supported platform.
135# expect+2: Malformed conditional (${word:L:range=99333000222000111000})
136# expect+1: while evaluating variable "word" with value "word": Invalid number "99333000222000111000}" for ':range' modifier
137.if ${word:L:range=99333000222000111000}
138.endif
139
140# In an indirect modifier, the delimiter is '\0', which at the same time marks
141# the end of the string.  The sequence '\\' '\0' is not an escaped delimiter,
142# as it would be wrong to skip past the end of the string.
143# expect+2: while evaluating "${:${:Ugmtime=\\}}" with value "": Invalid time value "\"
144# expect+1: Malformed conditional (${:${:Ugmtime=\\}})
145.if ${:${:Ugmtime=\\}}
146.  error
147.endif
148
149# Test a '$' at the end of a modifier part, for all modifiers in the order
150# listed in ApplyModifier.
151#
152# The only modifier parts where an unescaped '$' makes sense at the end are
153# the 'from' parts of the ':S' and ':C' modifiers.  In all other modifier
154# parts, an unescaped '$' is an undocumented and discouraged edge case, as it
155# means the same as an escaped '$'.
156.if ${:U:!printf '%s\n' $!} != "\$"
157.  error
158.endif
159# expect+1: while evaluating variable "VAR" with value "value$": Dollar followed by nothing
160.if ${VAR::=value$} != "" || ${VAR} != "value"
161.  error
162.endif
163${:U }=		<space>
164# expect+2: while evaluating variable "VAR" with value "value$": Dollar followed by nothing
165# expect+1: while evaluating variable "VAR" with value "value$ appended$": Dollar followed by nothing
166.if ${VAR::+=appended$} != "" || ${VAR} != "value<space>appended"
167.  error
168.endif
169.if ${1:?then$:else$} != "then\$"
170.  error
171.endif
172.if ${0:?then$:else$} != "else\$"
173.  error
174.endif
175# expect+1: while evaluating variable "word" with value "word": Dollar followed by nothing
176.if ${word:L:@w@$w$@} != "word"
177.  error
178.endif
179# expect+2: while evaluating variable "word" with value "": Bad modifier ":[$]"
180# expect+1: Malformed conditional (${word:[$]})
181.if ${word:[$]}
182.  error
183.else
184.  error
185.endif
186VAR_DOLLAR=	VAR$$
187.if ${word:L:_=VAR$} != "word" || ${${VAR_DOLLAR}} != "word"
188.  error
189.endif
190.if ${word:L:C,d$,m,} != "worm"
191.  error
192.endif
193.if ${word:L:C,d,$,} != "wor\$"
194.  error
195.endif
196# expect+2: while evaluating variable "VAR" with value "value$ appended$": Dollar followed by nothing
197# expect+1: while evaluating variable "VAR" with value "value<space>appended": Invalid variable name '}', at "$} != "set""
198.if ${VAR:Dset$} != "set"
199.  error
200.endif
201# expect+1: while evaluating "${:Ufallback$} != "fallback"" with value "": Invalid variable name '}', at "$} != "fallback""
202.if ${:Ufallback$} != "fallback"
203.  error
204.endif
205# expect+2: Malformed conditional (${%y:L:gmtime=1000$})
206# expect+1: while evaluating variable "%y" with value "%y": Invalid time value "1000$"
207.if ${%y:L:gmtime=1000$}
208.  error
209.else
210.  error
211.endif
212# expect+2: Malformed conditional (${%y:L:localtime=1000$})
213# expect+1: while evaluating variable "%y" with value "%y": Invalid time value "1000$"
214.if ${%y:L:localtime=1000$}
215.  error
216.else
217.  error
218.endif
219# expect+1: while evaluating variable "word" with value "word": Dollar followed by nothing
220.if ${word:L:Mw*$} != "word"
221.  error
222.endif
223# expect+1: while evaluating variable "word" with value "word": Dollar followed by nothing
224.if ${word:L:NX*$} != "word"
225.  error
226.endif
227# expect+2: while evaluating variable "." with value ".": Invalid argument 'fallback$' for modifier ':mtime'
228# expect+1: Malformed conditional (${.:L:mtime=fallback$})
229.if ${.:L:mtime=fallback$}
230.  error
231.else
232.  error
233.endif
234.if ${word:L:S,d$,m,} != "worm"
235.  error
236.endif
237.if ${word:L:S,d,m$,} != "worm\$"
238.  error
239.endif
240