varmod.mk revision 1.14 1 # $NetBSD: varmod.mk,v 1.14 2024/06/05 22:06:53 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": 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: Malformed conditional (${word:L:[99333000222000111000]})
125 .if ${word:L:[99333000222000111000]}
126 .endif
127 # expect+1: Malformed conditional (${word:L:[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+2: Malformed conditional (${word:L:range=99333000222000111000})
134 # expect+1: while evaluating variable "word": Invalid number "99333000222000111000}" for ':range' modifier
135 .if ${word:L:range=99333000222000111000}
136 .endif
137
138 # In an indirect modifier, the delimiter is '\0', which at the same time marks
139 # the end of the string. The sequence '\\' '\0' is not an escaped delimiter,
140 # as it would be wrong to skip past the end of the string.
141 # expect+2: while evaluating "${:${:Ugmtime=\\}}": Invalid time value "\"
142 # expect+1: Malformed conditional (${:${:Ugmtime=\\}})
143 .if ${:${:Ugmtime=\\}}
144 . error
145 .endif
146