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