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