varmod.mk revision 1.17 1 # $NetBSD: varmod.mk,v 1.17 2024/07/04 18:53:37 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
62 DOLLAR1= $$
63 DOLLAR2= ${: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"": 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.
115 VAR= 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=\\}}": 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
186 VAR_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"": 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