var-op-sunsh.mk revision 1.10 1 # $NetBSD: var-op-sunsh.mk,v 1.10 2022/02/09 21:09:24 rillig Exp $
2 #
3 # Tests for the :sh= variable assignment operator, which runs its right-hand
4 # side through the shell. It is a seldom-used alternative to the !=
5 # assignment operator, adopted from Sun make.
6
7 .MAKEFLAGS: -dL # Enable sane error messages
8
9 # This is the idiomatic form of the Sun shell assignment operator.
10 # The assignment operator is directly preceded by the ':sh'.
11 VAR:sh= echo colon-sh
12 .if ${VAR} != "colon-sh"
13 . error
14 .endif
15
16 # It is also possible to have whitespace around the :sh assignment
17 # operator modifier.
18 VAR :sh = echo colon-sh-spaced
19 .if ${VAR} != "colon-sh-spaced"
20 . error
21 .endif
22
23 # Until 2020-10-04, the ':sh' could even be followed by other characters.
24 # This was neither documented by NetBSD make nor by Solaris make and was
25 # an implementation error.
26 #
27 # Since 2020-10-04, this is a normal variable assignment to the variable named
28 # 'VAR:shell', using the '=' assignment operator.
29 VAR:shell= echo colon-shell
30 # The variable name needs to be generated using a ${:U...} expression because
31 # it is not possible to express the ':' as part of a literal variable name,
32 # see ParseVarname.
33 .if ${${:UVAR\:shell}} != "echo colon-shell"
34 . error
35 .endif
36
37 # Several colons can syntactically appear in a variable name.
38 # Until 2020-10-04, the last of them was interpreted as the ':sh'
39 # assignment operator.
40 #
41 # Since 2020-10-04, the colons are part of the variable name.
42 VAR:shoe:shore= echo two-colons
43 .if ${${:UVAR\:shoe\:shore}} != "echo two-colons"
44 . error
45 .endif
46
47 # Until 2020-10-04, the following expression was wrongly marked as
48 # a parse error. This was because the parser for variable assignments
49 # just looked for the previous ":sh", without taking any contextual
50 # information into account.
51 #
52 # There are two different syntactical elements that look exactly the same:
53 # The variable modifier ':sh' and the assignment operator modifier ':sh'.
54 # Intuitively this variable name contains the variable modifier, but until
55 # 2020-10-04, the parser regarded it as an assignment operator modifier, in
56 # Parse_Var.
57 VAR.${:Uecho 123:sh}= ok-123
58 .if ${VAR.123} != "ok-123"
59 . error
60 .endif
61
62 # Same pattern here. Until 2020-10-04, the ':sh' inside the nested expression
63 # was taken for the :sh assignment operator modifier, even though it was
64 # escaped by a backslash.
65 VAR.${:U echo\:shell}= ok-shell
66 .if ${VAR.${:U echo\:shell}} != "ok-shell"
67 . error
68 .endif
69
70 # Until 2020-10-04, the word 'shift' was also affected since it starts with
71 # ':sh'.
72 VAR.key:shift= Shift
73 .if ${${:UVAR.key\:shift}} != "Shift"
74 . error
75 .endif
76
77 # Just for fun: The code in Parse_IsVar allows for multiple appearances of
78 # the ':sh' assignment operator modifier. Let's see what happens ...
79 #
80 # Well, the end result is correct but the way until there is rather
81 # adventurous. This only works because the parser replaces each and every
82 # whitespace character that is not nested with '\0' (see Parse_Var).
83 # The variable name therefore ends before the first ':sh', and the last
84 # ':sh' turns the assignment operator into the shell command evaluation.
85 # Parse_Var completely trusts Parse_IsVar to properly verify the syntax.
86 #
87 # The ':sh' is the only word that may occur between the variable name and
88 # the assignment operator at nesting level 0. All other words would lead
89 # to a parse error since the left-hand side of an assignment must be
90 # exactly one word.
91 VAR :sh :sh :sh :sh= echo multiple
92 .if ${VAR} != "multiple"
93 . error
94 .endif
95
96 # The word ':sh' is not the only thing that can occur after a variable name.
97 # Since the parser just counts braces and parentheses instead of properly
98 # expanding nested expressions, the token ' :sh' can be used to add arbitrary
99 # text between the variable name and the assignment operator, it just has to
100 # be enclosed in braces or parentheses.
101 #
102 # Since the text to the left of the assignment operator '=' does not end with
103 # ':sh', the effective assignment operator becomes '=', not '!='.
104 VAR :sh(Put a comment here)= comment in parentheses
105 .if ${VAR} != "comment in parentheses"
106 . error
107 .endif
108
109 # The unintended comment can include multiple levels of nested braces and
110 # parentheses. Braces and parentheses are interchangeable, that is, a '(' can
111 # be closed by either ')' or '}'. These braces and parentheses are only
112 # counted by Parse_IsVar, in particular Parse_Var doesn't see them.
113 VAR :sh{Put}((((a}{comment}}}}{here}= comment in braces
114 .if ${VAR} != "comment in braces"
115 . error
116 .endif
117
118 # The assignment modifier ':sh' can be combined with the assignment operator
119 # '+='. In such a case the ':sh' is silently ignored, and the effective
120 # assignment operator is '+='.
121 #
122 # XXX: This combination should not be allowed at all, as it is confusing.
123 VAR= one
124 VAR :sh += echo two
125 .if ${VAR} != "one echo two"
126 . error ${VAR}
127 .endif
128
129 # The assignment modifier ':sh' can be combined with the assignment operator
130 # '!='. In such a case the ':sh' is silently ignored, and the effective
131 # assignment operator is '!=', just like with '+=' or the other compound
132 # assignment operators.
133 #
134 # XXX: This combination should not be allowed at all, as it is confusing.
135 VAR :sh != echo echo echo echo spaces-around
136 .if ${VAR} != "echo echo echo spaces-around"
137 . error ${VAR}
138 .endif
139
140 # If there is no space between the variable name and the assignment modifier
141 # ':sh', the ':sh' becomes part of the variable name, as the parser only
142 # expects a single assignment modifier to the left of the '=', which in this
143 # case is the '!'.
144 VAR:sh != echo echo echo echo space-after
145 .if ${${:UVAR\:sh}} != "echo echo echo space-after"
146 . error ${${:UVAR\:sh}}
147 .endif
148
149 all: .PHONY
150