Home | History | Annotate | Line # | Download | only in unit-tests
varmod-indirect.mk revision 1.17
      1  1.17  rillig # $NetBSD: varmod-indirect.mk,v 1.17 2024/02/04 09:56:24 rillig Exp $
      2   1.1  rillig #
      3   1.1  rillig # Tests for indirect variable modifiers, such as in ${VAR:${M_modifiers}}.
      4   1.1  rillig # These can be used for very basic purposes like converting a string to either
      5   1.1  rillig # uppercase or lowercase, as well as for fairly advanced modifiers that first
      6   1.1  rillig # look like line noise and are hard to decipher.
      7   1.1  rillig #
      8   1.7  rillig # Initial support for indirect modifiers was added in var.c 1.101 from
      9   1.7  rillig # 2006-02-18.  Since var.c 1.108 from 2006-05-11 it is possible to use
     10   1.7  rillig # indirect modifiers for all but the very first modifier as well.
     11   1.1  rillig 
     12   1.1  rillig 
     13   1.2  rillig # To apply a modifier indirectly via another variable, the whole
     14  1.13  rillig # modifier must be put into a single expression.
     15   1.6  rillig # The following expression generates a parse error since its indirect
     16  1.13  rillig # modifier contains more than a sole expression.
     17   1.6  rillig #
     18  1.11  rillig # expect+1: Unknown modifier "${"
     19   1.2  rillig .if ${value:L:${:US}${:U,value,replacement,}} != "S,value,replacement,}"
     20   1.2  rillig .  warning unexpected
     21   1.2  rillig .endif
     22   1.2  rillig 
     23   1.2  rillig 
     24   1.2  rillig # Adding another level of indirection (the 2 nested :U expressions) helps.
     25   1.2  rillig .if ${value:L:${:U${:US}${:U,value,replacement,}}} != "replacement"
     26   1.2  rillig .  warning unexpected
     27   1.2  rillig .endif
     28   1.2  rillig 
     29   1.2  rillig 
     30   1.2  rillig # Multiple indirect modifiers can be applied one after another as long as
     31   1.2  rillig # they are separated with colons.
     32   1.2  rillig .if ${value:L:${:US,a,A,}:${:US,e,E,}} != "vAluE"
     33   1.2  rillig .  warning unexpected
     34   1.2  rillig .endif
     35   1.2  rillig 
     36   1.2  rillig 
     37   1.6  rillig # An indirect variable that evaluates to the empty string is allowed.
     38   1.6  rillig # It is even allowed to write another modifier directly afterwards.
     39   1.6  rillig # There is no practical use case for this feature though, as demonstrated
     40   1.6  rillig # in the test case directly below.
     41   1.6  rillig .if ${value:L:${:Dempty}S,value,replaced,} != "replaced"
     42   1.6  rillig .  warning unexpected
     43   1.6  rillig .endif
     44   1.6  rillig 
     45   1.6  rillig # If an expression for an indirect modifier evaluates to anything else than an
     46   1.6  rillig # empty string and is neither followed by a ':' nor '}', this produces a parse
     47   1.6  rillig # error.  Because of this parse error, this feature cannot be used reasonably
     48   1.6  rillig # in practice.
     49   1.6  rillig #
     50  1.11  rillig # expect+2: Unknown modifier "${"
     51   1.6  rillig #.MAKEFLAGS: -dvc
     52   1.6  rillig .if ${value:L:${:UM*}S,value,replaced,} == "M*S,value,replaced,}"
     53  1.12  rillig # expect+1: warning: FIXME: this expression should have resulted in a parse error rather than returning the unparsed portion of the expression.
     54   1.6  rillig .  warning	FIXME: this expression should have resulted in a parse $\
     55   1.6  rillig  		error rather than returning the unparsed portion of the $\
     56   1.6  rillig  		expression.
     57   1.6  rillig .else
     58   1.6  rillig .  error
     59   1.6  rillig .endif
     60   1.6  rillig #.MAKEFLAGS: -d0
     61   1.6  rillig 
     62   1.6  rillig # An indirect modifier can be followed by other modifiers, no matter if the
     63   1.6  rillig # indirect modifier evaluates to an empty string or not.
     64   1.6  rillig #
     65   1.2  rillig # This makes it possible to define conditional modifiers, like this:
     66   1.2  rillig #
     67   1.2  rillig # M.little-endian=	S,1234,4321,
     68   1.2  rillig # M.big-endian=		# none
     69   1.6  rillig .if ${value:L:${:D empty }:S,value,replaced,} != "replaced"
     70   1.6  rillig .  error
     71   1.2  rillig .endif
     72   1.2  rillig 
     73   1.2  rillig 
     74  1.13  rillig # The nested expression expands to "tu", and this is interpreted as
     75   1.1  rillig # a variable modifier for the value "Upper", resulting in "UPPER".
     76   1.1  rillig .if ${Upper:L:${:Utu}} != "UPPER"
     77   1.1  rillig .  error
     78   1.1  rillig .endif
     79   1.1  rillig 
     80  1.13  rillig # The nested expression expands to "tl", and this is interpreted as
     81   1.1  rillig # a variable modifier for the value "Lower", resulting in "lower".
     82   1.1  rillig .if ${Lower:L:${:Utl}} != "lower"
     83   1.1  rillig .  error
     84   1.1  rillig .endif
     85   1.1  rillig 
     86   1.1  rillig 
     87  1.13  rillig # The nested expression is ${1 != 1:?Z:tl}, consisting of the
     88   1.1  rillig # condition "1 != 1", the then-branch "Z" and the else-branch "tl".  Since
     89   1.1  rillig # the condition evaluates to false, the then-branch is ignored (it would
     90   1.1  rillig # have been an unknown modifier anyway) and the ":tl" modifier is applied.
     91   1.1  rillig .if ${Mixed:L:${1 != 1:?Z:tl}} != "mixed"
     92   1.1  rillig .  error
     93   1.1  rillig .endif
     94   1.1  rillig 
     95   1.1  rillig 
     96   1.1  rillig # The indirect modifier can also replace an ':L' modifier, which allows for
     97   1.1  rillig # brain twisters since by reading the expression alone, it is not possible
     98   1.1  rillig # to say whether the variable name will be evaluated as a variable name or
     99   1.1  rillig # as the immediate value of the expression.
    100   1.1  rillig VAR=	value
    101   1.1  rillig M_ExpandVar=	# an empty modifier
    102   1.1  rillig M_VarAsValue=	L
    103   1.1  rillig #
    104   1.1  rillig .if ${VAR:${M_ExpandVar}} != "value"
    105   1.1  rillig .  error
    106   1.1  rillig .endif
    107   1.1  rillig .if ${VAR:${M_VarAsValue}} != "VAR"
    108   1.1  rillig .  error
    109   1.1  rillig .endif
    110   1.1  rillig 
    111   1.1  rillig # The indirect modifier M_ListToSkip, when applied to a list of patterns,
    112   1.1  rillig # expands to a sequence of ':N' modifiers, each of which filters one of the
    113   1.1  rillig # patterns.  This list of patterns can then be applied to another variable
    114   1.1  rillig # to actually filter that variable.
    115   1.1  rillig #
    116   1.1  rillig M_ListToSkip=	@pat@N$${pat}@:ts:
    117   1.1  rillig #
    118   1.1  rillig # The dollar signs need to be doubled in the above modifier expression,
    119   1.1  rillig # otherwise they would be expanded too early, that is, when parsing the
    120   1.1  rillig # modifier itself.
    121   1.1  rillig #
    122   1.1  rillig # In the following example, M_NoPrimes expands to 'N2:N3:N5:N7:N1[1379]'.
    123   1.1  rillig # The 'N' comes from the expression 'N${pat}', the separating colons come
    124   1.1  rillig # from the modifier ':ts:'.
    125   1.1  rillig #
    126   1.1  rillig #.MAKEFLAGS: -dcv		# Uncomment this line to see the details
    127   1.1  rillig #
    128   1.1  rillig PRIMES=		2 3 5 7 1[1379]
    129   1.1  rillig M_NoPrimes=	${PRIMES:${M_ListToSkip}}
    130   1.1  rillig .if ${:U:range=20:${M_NoPrimes}} != "1 4 6 8 9 10 12 14 15 16 18 20"
    131   1.1  rillig .  error
    132   1.1  rillig .endif
    133   1.1  rillig .MAKEFLAGS: -d0
    134   1.1  rillig 
    135   1.3  rillig 
    136  1.14  rillig # In contrast to the .if conditions, the .for loop allows undefined
    137   1.3  rillig # expressions.  These expressions expand to empty strings.
    138   1.4  rillig 
    139   1.4  rillig # An undefined expression without any modifiers expands to an empty string.
    140   1.4  rillig .for var in before ${UNDEF} after
    141  1.12  rillig # expect+2: before
    142  1.12  rillig # expect+1: after
    143   1.4  rillig .  info ${var}
    144   1.4  rillig .endfor
    145   1.4  rillig 
    146   1.4  rillig # An undefined expression with only modifiers that keep the expression
    147   1.4  rillig # undefined expands to an empty string.
    148   1.4  rillig .for var in before ${UNDEF:${:US,a,a,}} after
    149  1.12  rillig # expect+2: before
    150  1.12  rillig # expect+1: after
    151   1.3  rillig .  info ${var}
    152   1.3  rillig .endfor
    153   1.3  rillig 
    154   1.3  rillig # Even in an indirect modifier based on an undefined variable, the value of
    155   1.3  rillig # the expression in Var_Parse is a simple empty string.
    156   1.4  rillig .for var in before ${UNDEF:${:U}} after
    157  1.12  rillig # expect+2: before
    158  1.12  rillig # expect+1: after
    159   1.4  rillig .  info ${var}
    160   1.4  rillig .endfor
    161   1.4  rillig 
    162   1.4  rillig # An error in an indirect modifier.
    163  1.12  rillig # expect+1: Unknown modifier "Z"
    164   1.4  rillig .for var in before ${UNDEF:${:UZ}} after
    165  1.12  rillig # expect+2: before
    166  1.12  rillig # expect+1: after
    167   1.3  rillig .  info ${var}
    168   1.3  rillig .endfor
    169   1.3  rillig 
    170   1.4  rillig 
    171   1.4  rillig # Another slightly different evaluation context is the right-hand side of
    172   1.4  rillig # a variable assignment using ':='.
    173   1.4  rillig .MAKEFLAGS: -dpv
    174   1.5  rillig 
    175  1.13  rillig # The undefined expression is kept as-is.
    176   1.4  rillig _:=	before ${UNDEF} after
    177   1.5  rillig 
    178  1.13  rillig # The undefined expression is kept as-is.
    179   1.4  rillig _:=	before ${UNDEF:${:US,a,a,}} after
    180   1.5  rillig 
    181   1.4  rillig # XXX: The subexpression ${:U} is fully defined, therefore it is expanded.
    182   1.4  rillig # This results in ${UNDEF:}, which can lead to tricky parse errors later,
    183   1.4  rillig # when the variable '_' is expanded further.
    184   1.5  rillig #
    185   1.4  rillig # XXX: What should be the correct strategy here?  One possibility is to
    186   1.5  rillig # expand the defined subexpression and replace it with ${:U...}, just like
    187   1.4  rillig # in .for loops.  This would preserve the structure of the expression while
    188   1.4  rillig # at the same time expanding the expression as far as possible.
    189   1.4  rillig _:=	before ${UNDEF:${:U}} after
    190   1.5  rillig 
    191   1.4  rillig # XXX: This expands to ${UNDEF:Z}, which will behave differently if the
    192  1.13  rillig # variable '_' is used in a context where the expression ${_} is
    193   1.4  rillig # parsed but not evaluated.
    194  1.12  rillig # expect+1: Unknown modifier "Z"
    195   1.4  rillig _:=	before ${UNDEF:${:UZ}} after
    196   1.5  rillig 
    197   1.4  rillig .MAKEFLAGS: -d0
    198   1.4  rillig .undef _
    199   1.4  rillig 
    200   1.6  rillig 
    201   1.6  rillig # When evaluating indirect modifiers, these modifiers may expand to ':tW',
    202   1.6  rillig # which modifies the interpretation of the expression value. This modified
    203   1.6  rillig # interpretation only lasts until the end of the indirect modifier, it does
    204  1.13  rillig # not influence the outer expression.
    205   1.6  rillig .if ${1 2 3:L:tW:[#]} != 1		# direct :tW applies to the :[#]
    206   1.6  rillig .  error
    207   1.6  rillig .endif
    208   1.6  rillig .if ${1 2 3:L:${:UtW}:[#]} != 3		# indirect :tW does not apply to :[#]
    209   1.6  rillig .  error
    210   1.6  rillig .endif
    211   1.6  rillig 
    212   1.6  rillig 
    213   1.6  rillig # When evaluating indirect modifiers, these modifiers may expand to ':ts*',
    214   1.6  rillig # which modifies the interpretation of the expression value. This modified
    215   1.6  rillig # interpretation only lasts until the end of the indirect modifier, it does
    216  1.13  rillig # not influence the outer expression.
    217   1.6  rillig #
    218   1.7  rillig # In this first expression, the direct ':ts*' has no effect since ':U' does not
    219   1.6  rillig # treat the expression value as a list of words but as a single word.  It has
    220   1.6  rillig # to be ':U', not ':D', since the "expression name" is "1 2 3" and there is no
    221   1.6  rillig # variable of that name.
    222   1.6  rillig #.MAKEFLAGS: -dcpv
    223   1.6  rillig .if ${1 2 3:L:ts*:Ua b c} != "a b c"
    224   1.6  rillig .  error
    225   1.6  rillig .endif
    226   1.6  rillig # In this expression, the direct ':ts*' affects the ':M' at the end.
    227   1.6  rillig .if ${1 2 3:L:ts*:Ua b c:M*} != "a*b*c"
    228   1.6  rillig .  error
    229   1.6  rillig .endif
    230   1.6  rillig # In this expression, the ':ts*' is indirect, therefore the changed separator
    231   1.8  rillig # only applies to the modifiers from the indirect text.  It does not affect
    232   1.8  rillig # the ':M' since that is not part of the text from the indirect modifier.
    233   1.8  rillig #
    234   1.8  rillig # Implementation detail: when ApplyModifiersIndirect calls ApplyModifiers
    235   1.9  rillig # (which creates a new ModChain containing a fresh separator),
    236   1.8  rillig # the outer separator character is not passed by reference to the inner
    237   1.8  rillig # evaluation, therefore the scope of the inner separator ends after applying
    238   1.8  rillig # the modifier ':ts*'.
    239   1.6  rillig .if ${1 2 3:L:${:Uts*}:Ua b c:M*} != "a b c"
    240   1.6  rillig .  error
    241   1.6  rillig .endif
    242   1.6  rillig 
    243   1.8  rillig # A direct modifier ':U' turns the expression from undefined to defined.
    244   1.8  rillig # An indirect modifier ':U' has the same effect, unlike the separator from
    245   1.8  rillig # ':ts*' or the single-word marker from ':tW'.
    246   1.8  rillig #
    247   1.8  rillig # This is because when ApplyModifiersIndirect calls ApplyModifiers, it passes
    248   1.8  rillig # the definedness of the outer expression by reference.  If that weren't the
    249   1.8  rillig # case, the first condition below would result in a parse error because its
    250   1.8  rillig # left-hand side would be undefined.
    251   1.8  rillig .if ${UNDEF:${:UUindirect-fallback}} != "indirect-fallback"
    252   1.8  rillig .  error
    253   1.8  rillig .endif
    254   1.8  rillig .if ${UNDEF:${:UUindirect-fallback}:Uouter-fallback} != "outer-fallback"
    255   1.8  rillig .  error
    256   1.8  rillig .endif
    257   1.8  rillig 
    258  1.15  rillig 
    259  1.15  rillig # In parse-only mode, the indirect modifiers must not be evaluated.
    260  1.15  rillig #
    261  1.17  rillig # Before var.c TODO from 2024-02-04, the expression for an indirect modifier
    262  1.16  rillig # was partially evaluated (only the variable value, without applying any
    263  1.16  rillig # modifiers) and then interpreted as modifiers to the main expression.
    264  1.15  rillig #
    265  1.15  rillig # The expression ${:UZ} starts with the value "", and in parse-only mode, the
    266  1.15  rillig # modifier ':UZ' does not modify the expression value.  This results in an
    267  1.15  rillig # empty string for the indirect modifiers, generating no warning.
    268  1.15  rillig .if 0 && ${VAR:${:UZ}}
    269  1.15  rillig .endif
    270  1.15  rillig # The expression ${M_invalid} starts with the value "Z", which is an unknown
    271  1.15  rillig # modifier.  Trying to apply this unknown modifier generated a warning.
    272  1.15  rillig M_invalid=	Z
    273  1.15  rillig # expect+2: Unknown modifier "Z"
    274  1.15  rillig # expect+1: Malformed conditional (0 && ${VAR:${M_invalid}})
    275  1.15  rillig .if 0 && ${VAR:${M_invalid}}
    276  1.15  rillig .endif
    277  1.16  rillig # The expression ${M_invalid} starts with the value "Z", and if its modifiers
    278  1.16  rillig # were evaluated, would result in "N*Z", which is a valid modifier.  The
    279  1.16  rillig # modifiers were not applied though, keeping the invalid value "Z".
    280  1.17  rillig # expect+2: Unknown modifier "Z"
    281  1.17  rillig # expect+1: Malformed conditional (0 && ${VAR:${M_invalid:S,^,N*,:ts:}})
    282  1.16  rillig .if 0 && ${VAR:${M_invalid:S,^,N*,:ts:}}
    283  1.16  rillig .endif
    284  1.16  rillig # The ':@' modifier does not change the expression value in parse-only mode,
    285  1.16  rillig # keeping the "Z".
    286  1.16  rillig # expect+2: Unknown modifier "Z"
    287  1.16  rillig # expect+1: Malformed conditional (0 && ${VAR:${M_invalid:@m@N*$m@:ts:}})
    288  1.16  rillig .if 0 && ${VAR:${M_invalid:@m@N*$m@:ts:}}
    289  1.16  rillig .endif
    290