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