xref: /freebsd/contrib/bmake/unit-tests/var-op-sunsh.mk (revision 1d3f2ddc32fc37e4835aa5a51eabc8696c1e8114)
1*1d3f2ddcSSimon J. Gerraty# $NetBSD: var-op-sunsh.mk,v 1.10 2022/02/09 21:09:24 rillig Exp $
2956e45f6SSimon J. Gerraty#
3956e45f6SSimon J. Gerraty# Tests for the :sh= variable assignment operator, which runs its right-hand
4956e45f6SSimon J. Gerraty# side through the shell.  It is a seldom-used alternative to the !=
5*1d3f2ddcSSimon J. Gerraty# assignment operator, adopted from Sun make.
6956e45f6SSimon J. Gerraty
7956e45f6SSimon J. Gerraty.MAKEFLAGS: -dL			# Enable sane error messages
8956e45f6SSimon J. Gerraty
9956e45f6SSimon J. Gerraty# This is the idiomatic form of the Sun shell assignment operator.
10956e45f6SSimon J. Gerraty# The assignment operator is directly preceded by the ':sh'.
11956e45f6SSimon J. GerratyVAR:sh=		echo colon-sh
12956e45f6SSimon J. Gerraty.if ${VAR} != "colon-sh"
13956e45f6SSimon J. Gerraty.  error
14956e45f6SSimon J. Gerraty.endif
15956e45f6SSimon J. Gerraty
16956e45f6SSimon J. Gerraty# It is also possible to have whitespace around the :sh assignment
17956e45f6SSimon J. Gerraty# operator modifier.
18956e45f6SSimon J. GerratyVAR :sh =	echo colon-sh-spaced
19956e45f6SSimon J. Gerraty.if ${VAR} != "colon-sh-spaced"
20956e45f6SSimon J. Gerraty.  error
21956e45f6SSimon J. Gerraty.endif
22956e45f6SSimon J. Gerraty
23956e45f6SSimon J. Gerraty# Until 2020-10-04, the ':sh' could even be followed by other characters.
24956e45f6SSimon J. Gerraty# This was neither documented by NetBSD make nor by Solaris make and was
25956e45f6SSimon J. Gerraty# an implementation error.
26956e45f6SSimon J. Gerraty#
279f45a3c8SSimon J. Gerraty# Since 2020-10-04, this is a normal variable assignment to the variable named
289f45a3c8SSimon J. Gerraty# 'VAR:shell', using the '=' assignment operator.
29956e45f6SSimon J. GerratyVAR:shell=	echo colon-shell
309f45a3c8SSimon J. Gerraty# The variable name needs to be generated using a ${:U...} expression because
319f45a3c8SSimon J. Gerraty# it is not possible to express the ':' as part of a literal variable name,
329f45a3c8SSimon J. Gerraty# see ParseVarname.
33956e45f6SSimon J. Gerraty.if ${${:UVAR\:shell}} != "echo colon-shell"
34956e45f6SSimon J. Gerraty.  error
35956e45f6SSimon J. Gerraty.endif
36956e45f6SSimon J. Gerraty
37956e45f6SSimon J. Gerraty# Several colons can syntactically appear in a variable name.
38956e45f6SSimon J. Gerraty# Until 2020-10-04, the last of them was interpreted as the ':sh'
39956e45f6SSimon J. Gerraty# assignment operator.
40956e45f6SSimon J. Gerraty#
41956e45f6SSimon J. Gerraty# Since 2020-10-04, the colons are part of the variable name.
42956e45f6SSimon J. GerratyVAR:shoe:shore=	echo two-colons
43956e45f6SSimon J. Gerraty.if ${${:UVAR\:shoe\:shore}} != "echo two-colons"
44956e45f6SSimon J. Gerraty.  error
45956e45f6SSimon J. Gerraty.endif
46956e45f6SSimon J. Gerraty
47956e45f6SSimon J. Gerraty# Until 2020-10-04, the following expression was wrongly marked as
48956e45f6SSimon J. Gerraty# a parse error.  This was because the parser for variable assignments
49956e45f6SSimon J. Gerraty# just looked for the previous ":sh", without taking any contextual
50956e45f6SSimon J. Gerraty# information into account.
51956e45f6SSimon J. Gerraty#
52956e45f6SSimon J. Gerraty# There are two different syntactical elements that look exactly the same:
53956e45f6SSimon J. Gerraty# The variable modifier ':sh' and the assignment operator modifier ':sh'.
54956e45f6SSimon J. Gerraty# Intuitively this variable name contains the variable modifier, but until
55956e45f6SSimon J. Gerraty# 2020-10-04, the parser regarded it as an assignment operator modifier, in
56b0c40a00SSimon J. Gerraty# Parse_Var.
57956e45f6SSimon J. GerratyVAR.${:Uecho 123:sh}=	ok-123
58956e45f6SSimon J. Gerraty.if ${VAR.123} != "ok-123"
59956e45f6SSimon J. Gerraty.  error
60956e45f6SSimon J. Gerraty.endif
61956e45f6SSimon J. Gerraty
62956e45f6SSimon J. Gerraty# Same pattern here. Until 2020-10-04, the ':sh' inside the nested expression
63956e45f6SSimon J. Gerraty# was taken for the :sh assignment operator modifier, even though it was
64956e45f6SSimon J. Gerraty# escaped by a backslash.
65956e45f6SSimon J. GerratyVAR.${:U echo\:shell}=	ok-shell
66956e45f6SSimon J. Gerraty.if ${VAR.${:U echo\:shell}} != "ok-shell"
67956e45f6SSimon J. Gerraty.  error
68956e45f6SSimon J. Gerraty.endif
69956e45f6SSimon J. Gerraty
70956e45f6SSimon J. Gerraty# Until 2020-10-04, the word 'shift' was also affected since it starts with
71956e45f6SSimon J. Gerraty# ':sh'.
72956e45f6SSimon J. GerratyVAR.key:shift=		Shift
73956e45f6SSimon J. Gerraty.if ${${:UVAR.key\:shift}} != "Shift"
74956e45f6SSimon J. Gerraty.  error
75956e45f6SSimon J. Gerraty.endif
76956e45f6SSimon J. Gerraty
77956e45f6SSimon J. Gerraty# Just for fun: The code in Parse_IsVar allows for multiple appearances of
78956e45f6SSimon J. Gerraty# the ':sh' assignment operator modifier.  Let's see what happens ...
79956e45f6SSimon J. Gerraty#
80956e45f6SSimon J. Gerraty# Well, the end result is correct but the way until there is rather
81b0c40a00SSimon J. Gerraty# adventurous.  This only works because the parser replaces each and every
82b0c40a00SSimon J. Gerraty# whitespace character that is not nested with '\0' (see Parse_Var).
83956e45f6SSimon J. Gerraty# The variable name therefore ends before the first ':sh', and the last
84956e45f6SSimon J. Gerraty# ':sh' turns the assignment operator into the shell command evaluation.
85b0c40a00SSimon J. Gerraty# Parse_Var completely trusts Parse_IsVar to properly verify the syntax.
86956e45f6SSimon J. Gerraty#
87956e45f6SSimon J. Gerraty# The ':sh' is the only word that may occur between the variable name and
88956e45f6SSimon J. Gerraty# the assignment operator at nesting level 0.  All other words would lead
89956e45f6SSimon J. Gerraty# to a parse error since the left-hand side of an assignment must be
90956e45f6SSimon J. Gerraty# exactly one word.
91956e45f6SSimon J. GerratyVAR :sh :sh :sh :sh=	echo multiple
92956e45f6SSimon J. Gerraty.if ${VAR} != "multiple"
93956e45f6SSimon J. Gerraty.  error
94956e45f6SSimon J. Gerraty.endif
95956e45f6SSimon J. Gerraty
96956e45f6SSimon J. Gerraty# The word ':sh' is not the only thing that can occur after a variable name.
97956e45f6SSimon J. Gerraty# Since the parser just counts braces and parentheses instead of properly
98956e45f6SSimon J. Gerraty# expanding nested expressions, the token ' :sh' can be used to add arbitrary
99956e45f6SSimon J. Gerraty# text between the variable name and the assignment operator, it just has to
100956e45f6SSimon J. Gerraty# be enclosed in braces or parentheses.
1019f45a3c8SSimon J. Gerraty#
1029f45a3c8SSimon J. Gerraty# Since the text to the left of the assignment operator '=' does not end with
1039f45a3c8SSimon J. Gerraty# ':sh', the effective assignment operator becomes '=', not '!='.
104956e45f6SSimon J. GerratyVAR :sh(Put a comment here)=	comment in parentheses
105956e45f6SSimon J. Gerraty.if ${VAR} != "comment in parentheses"
106956e45f6SSimon J. Gerraty.  error
107956e45f6SSimon J. Gerraty.endif
108956e45f6SSimon J. Gerraty
109956e45f6SSimon J. Gerraty# The unintended comment can include multiple levels of nested braces and
1109f45a3c8SSimon J. Gerraty# parentheses.  Braces and parentheses are interchangeable, that is, a '(' can
1119f45a3c8SSimon J. Gerraty# be closed by either ')' or '}'.  These braces and parentheses are only
1129f45a3c8SSimon J. Gerraty# counted by Parse_IsVar, in particular Parse_Var doesn't see them.
113956e45f6SSimon J. GerratyVAR :sh{Put}((((a}{comment}}}}{here}=	comment in braces
114956e45f6SSimon J. Gerraty.if ${VAR} != "comment in braces"
115956e45f6SSimon J. Gerraty.  error
116956e45f6SSimon J. Gerraty.endif
117956e45f6SSimon J. Gerraty
1189f45a3c8SSimon J. Gerraty# The assignment modifier ':sh' can be combined with the assignment operator
1199f45a3c8SSimon J. Gerraty# '+='.  In such a case the ':sh' is silently ignored, and the effective
1209f45a3c8SSimon J. Gerraty# assignment operator is '+='.
121956e45f6SSimon J. Gerraty#
1229f45a3c8SSimon J. Gerraty# XXX: This combination should not be allowed at all, as it is confusing.
123956e45f6SSimon J. GerratyVAR=		one
124956e45f6SSimon J. GerratyVAR :sh +=	echo two
125956e45f6SSimon J. Gerraty.if ${VAR} != "one echo two"
126956e45f6SSimon J. Gerraty.  error ${VAR}
127956e45f6SSimon J. Gerraty.endif
128956e45f6SSimon J. Gerraty
1299f45a3c8SSimon J. Gerraty# The assignment modifier ':sh' can be combined with the assignment operator
1309f45a3c8SSimon J. Gerraty# '!='.  In such a case the ':sh' is silently ignored, and the effective
1319f45a3c8SSimon J. Gerraty# assignment operator is '!=', just like with '+=' or the other compound
1329f45a3c8SSimon J. Gerraty# assignment operators.
1339f45a3c8SSimon J. Gerraty#
1349f45a3c8SSimon J. Gerraty# XXX: This combination should not be allowed at all, as it is confusing.
1359f45a3c8SSimon J. GerratyVAR :sh !=	echo echo echo echo spaces-around
1369f45a3c8SSimon J. Gerraty.if ${VAR} != "echo echo echo spaces-around"
1379f45a3c8SSimon J. Gerraty.  error ${VAR}
1389f45a3c8SSimon J. Gerraty.endif
139e2eeea75SSimon J. Gerraty
1409f45a3c8SSimon J. Gerraty# If there is no space between the variable name and the assignment modifier
1419f45a3c8SSimon J. Gerraty# ':sh', the ':sh' becomes part of the variable name, as the parser only
1429f45a3c8SSimon J. Gerraty# expects a single assignment modifier to the left of the '=', which in this
1439f45a3c8SSimon J. Gerraty# case is the '!'.
1449f45a3c8SSimon J. GerratyVAR:sh !=	echo echo echo echo space-after
1459f45a3c8SSimon J. Gerraty.if ${${:UVAR\:sh}} != "echo echo echo space-after"
1469f45a3c8SSimon J. Gerraty.  error ${${:UVAR\:sh}}
1479f45a3c8SSimon J. Gerraty.endif
1489f45a3c8SSimon J. Gerraty
1499f45a3c8SSimon J. Gerratyall: .PHONY
150