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'.
11VAR: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.
18VAR :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.
29VAR: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.
42VAR: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.
57VAR.${: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.
65VAR.${: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'.
72VAR.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.
91VAR :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 '!='.
104VAR :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.
113VAR :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.
123VAR=		one
124VAR :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.
135VAR :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 '!'.
144VAR:sh !=	echo echo echo echo space-after
145.if ${${:UVAR\:sh}} != "echo echo echo space-after"
146.  error ${${:UVAR\:sh}}
147.endif
148
149all: .PHONY
150