xref: /freebsd/contrib/bmake/unit-tests/varmod-indirect.mk (revision c66ec88fed842fbaad62c30d510644ceb7bd2d71)
1# $NetBSD: varmod-indirect.mk,v 1.5 2020/12/27 17:32:25 rillig Exp $
2#
3# Tests for indirect variable modifiers, such as in ${VAR:${M_modifiers}}.
4# These can be used for very basic purposes like converting a string to either
5# uppercase or lowercase, as well as for fairly advanced modifiers that first
6# look like line noise and are hard to decipher.
7#
8# TODO: Since when are indirect modifiers supported?
9
10
11# To apply a modifier indirectly via another variable, the whole
12# modifier must be put into a single variable expression.
13.if ${value:L:${:US}${:U,value,replacement,}} != "S,value,replacement,}"
14.  warning unexpected
15.endif
16
17
18# Adding another level of indirection (the 2 nested :U expressions) helps.
19.if ${value:L:${:U${:US}${:U,value,replacement,}}} != "replacement"
20.  warning unexpected
21.endif
22
23
24# Multiple indirect modifiers can be applied one after another as long as
25# they are separated with colons.
26.if ${value:L:${:US,a,A,}:${:US,e,E,}} != "vAluE"
27.  warning unexpected
28.endif
29
30
31# An indirect variable that evaluates to the empty string is allowed though.
32# This makes it possible to define conditional modifiers, like this:
33#
34# M.little-endian=	S,1234,4321,
35# M.big-endian=		# none
36.if ${value:L:${:Dempty}S,a,A,} != "vAlue"
37.  warning unexpected
38.endif
39
40
41# The nested variable expression expands to "tu", and this is interpreted as
42# a variable modifier for the value "Upper", resulting in "UPPER".
43.if ${Upper:L:${:Utu}} != "UPPER"
44.  error
45.endif
46
47# The nested variable expression expands to "tl", and this is interpreted as
48# a variable modifier for the value "Lower", resulting in "lower".
49.if ${Lower:L:${:Utl}} != "lower"
50.  error
51.endif
52
53
54# The nested variable expression is ${1 != 1:?Z:tl}, consisting of the
55# condition "1 != 1", the then-branch "Z" and the else-branch "tl".  Since
56# the condition evaluates to false, the then-branch is ignored (it would
57# have been an unknown modifier anyway) and the ":tl" modifier is applied.
58.if ${Mixed:L:${1 != 1:?Z:tl}} != "mixed"
59.  error
60.endif
61
62
63# The indirect modifier can also replace an ':L' modifier, which allows for
64# brain twisters since by reading the expression alone, it is not possible
65# to say whether the variable name will be evaluated as a variable name or
66# as the immediate value of the expression.
67VAR=	value
68M_ExpandVar=	# an empty modifier
69M_VarAsValue=	L
70#
71.if ${VAR:${M_ExpandVar}} != "value"
72.  error
73.endif
74.if ${VAR:${M_VarAsValue}} != "VAR"
75.  error
76.endif
77
78# The indirect modifier M_ListToSkip, when applied to a list of patterns,
79# expands to a sequence of ':N' modifiers, each of which filters one of the
80# patterns.  This list of patterns can then be applied to another variable
81# to actually filter that variable.
82#
83M_ListToSkip=	@pat@N$${pat}@:ts:
84#
85# The dollar signs need to be doubled in the above modifier expression,
86# otherwise they would be expanded too early, that is, when parsing the
87# modifier itself.
88#
89# In the following example, M_NoPrimes expands to 'N2:N3:N5:N7:N1[1379]'.
90# The 'N' comes from the expression 'N${pat}', the separating colons come
91# from the modifier ':ts:'.
92#
93#.MAKEFLAGS: -dcv		# Uncomment this line to see the details
94#
95PRIMES=		2 3 5 7 1[1379]
96M_NoPrimes=	${PRIMES:${M_ListToSkip}}
97.if ${:U:range=20:${M_NoPrimes}} != "1 4 6 8 9 10 12 14 15 16 18 20"
98.  error
99.endif
100.MAKEFLAGS: -d0
101
102
103# In contrast to the .if conditions, the .for loop allows undefined variable
104# expressions.  These expressions expand to empty strings.
105
106# An undefined expression without any modifiers expands to an empty string.
107.for var in before ${UNDEF} after
108.  info ${var}
109.endfor
110
111# An undefined expression with only modifiers that keep the expression
112# undefined expands to an empty string.
113.for var in before ${UNDEF:${:US,a,a,}} after
114.  info ${var}
115.endfor
116
117# Even in an indirect modifier based on an undefined variable, the value of
118# the expression in Var_Parse is a simple empty string.
119.for var in before ${UNDEF:${:U}} after
120.  info ${var}
121.endfor
122
123# An error in an indirect modifier.
124.for var in before ${UNDEF:${:UZ}} after
125.  info ${var}
126.endfor
127
128
129# Another slightly different evaluation context is the right-hand side of
130# a variable assignment using ':='.
131.MAKEFLAGS: -dpv
132
133# The undefined variable expression is kept as-is.
134_:=	before ${UNDEF} after
135
136# The undefined variable expression is kept as-is.
137_:=	before ${UNDEF:${:US,a,a,}} after
138
139# XXX: The subexpression ${:U} is fully defined, therefore it is expanded.
140# This results in ${UNDEF:}, which can lead to tricky parse errors later,
141# when the variable '_' is expanded further.
142#
143# XXX: What should be the correct strategy here?  One possibility is to
144# expand the defined subexpression and replace it with ${:U...}, just like
145# in .for loops.  This would preserve the structure of the expression while
146# at the same time expanding the expression as far as possible.
147_:=	before ${UNDEF:${:U}} after
148
149# XXX: This expands to ${UNDEF:Z}, which will behave differently if the
150# variable '_' is used in a context where the variable expression ${_} is
151# parsed but not evaluated.
152_:=	before ${UNDEF:${:UZ}} after
153
154.MAKEFLAGS: -d0
155.undef _
156
157all:
158