1 1.25 rillig # $NetBSD: var-op-expand.mk,v 1.25 2025/06/29 11:27:21 rillig Exp $ 2 1.1 rillig # 3 1.2 rillig # Tests for the := variable assignment operator, which expands its 4 1.2 rillig # right-hand side. 5 1.15 rillig # 6 1.15 rillig # See also: 7 1.15 rillig # varname-dot-make-save_dollars.mk 8 1.1 rillig 9 1.15 rillig # Force the test results to be independent of the default value of this 10 1.15 rillig # setting, which is 'yes' for NetBSD's usr.bin/make but 'no' for the bmake 11 1.15 rillig # distribution and pkgsrc/devel/bmake. 12 1.16 rillig .MAKE.SAVE_DOLLARS:= yes 13 1.5 rillig 14 1.5 rillig # If the right-hand side does not contain a dollar sign, the ':=' assignment 15 1.5 rillig # operator has the same effect as the '=' assignment operator. 16 1.5 rillig VAR:= value 17 1.5 rillig .if ${VAR} != "value" 18 1.5 rillig . error 19 1.5 rillig .endif 20 1.5 rillig 21 1.5 rillig # When a ':=' assignment is performed, its right-hand side is evaluated and 22 1.5 rillig # expanded as far as possible. Contrary to other situations, '$$' and 23 1.19 rillig # expressions based on undefined variables are preserved though. 24 1.5 rillig # 25 1.19 rillig # Whether an expression is undefined or not is determined at the end 26 1.5 rillig # of evaluating the expression. The consequence is that ${:Ufallback} expands 27 1.5 rillig # to "fallback"; initially this expression is undefined since it is based on 28 1.5 rillig # the variable named "", which is guaranteed to be never defined, but at the 29 1.5 rillig # end of evaluating the expression ${:Ufallback}, the modifier ':U' has turned 30 1.5 rillig # the expression into a defined expression. 31 1.5 rillig 32 1.5 rillig 33 1.5 rillig # literal dollar signs 34 1.5 rillig VAR:= $$ $$$$ $$$$$$$$ 35 1.5 rillig .if ${VAR} != "\$ \$\$ \$\$\$\$" 36 1.5 rillig . error 37 1.5 rillig .endif 38 1.5 rillig 39 1.5 rillig 40 1.17 rillig # reference to a variable containing literal dollar signs 41 1.5 rillig REF= $$ $$$$ $$$$$$$$ 42 1.5 rillig VAR:= ${REF} 43 1.5 rillig REF= too late 44 1.5 rillig .if ${VAR} != "\$ \$\$ \$\$\$\$" 45 1.5 rillig . error 46 1.5 rillig .endif 47 1.5 rillig 48 1.5 rillig 49 1.5 rillig # reference to an undefined variable 50 1.5 rillig .undef UNDEF 51 1.5 rillig VAR:= <${UNDEF}> 52 1.17 rillig .if ${VAR} != "<>" 53 1.17 rillig . error 54 1.17 rillig .endif 55 1.5 rillig UNDEF= after 56 1.5 rillig .if ${VAR} != "<after>" 57 1.5 rillig . error 58 1.5 rillig .endif 59 1.5 rillig 60 1.5 rillig 61 1.5 rillig # reference to a variable whose name is computed from another variable 62 1.5 rillig REF2= referred to 63 1.5 rillig REF= REF2 64 1.5 rillig VAR:= ${${REF}} 65 1.5 rillig REF= too late 66 1.5 rillig .if ${VAR} != "referred to" 67 1.5 rillig . error 68 1.5 rillig .endif 69 1.5 rillig 70 1.5 rillig 71 1.5 rillig # expression with an indirect modifier referring to an undefined variable 72 1.5 rillig .undef UNDEF 73 1.5 rillig VAR:= ${:${UNDEF}} 74 1.17 rillig .if ${VAR} != "" 75 1.17 rillig . error 76 1.17 rillig .endif 77 1.5 rillig UNDEF= Uwas undefined 78 1.5 rillig .if ${VAR} != "was undefined" 79 1.5 rillig . error 80 1.5 rillig .endif 81 1.5 rillig 82 1.5 rillig 83 1.5 rillig # expression with an indirect modifier referring to another variable that 84 1.5 rillig # in turn refers to an undefined variable 85 1.5 rillig # 86 1.5 rillig # XXX: Even though this is a ':=' assignment, the '${UNDEF}' in the part of 87 1.5 rillig # the variable modifier is not preserved. To preserve it, ParseModifierPart 88 1.5 rillig # would have to call VarSubstExpr somehow since this is the only piece of 89 1.5 rillig # code that takes care of this global variable. 90 1.5 rillig .undef UNDEF 91 1.5 rillig REF= U${UNDEF} 92 1.5 rillig #.MAKEFLAGS: -dv 93 1.5 rillig VAR:= ${:${REF}} 94 1.5 rillig #.MAKEFLAGS: -d0 95 1.5 rillig REF= too late 96 1.5 rillig UNDEF= Uwas undefined 97 1.5 rillig .if ${VAR} != "" 98 1.5 rillig . error 99 1.5 rillig .endif 100 1.5 rillig 101 1.1 rillig 102 1.6 rillig # In variable assignments using the ':=' operator, undefined variables are 103 1.6 rillig # preserved, no matter how indirectly they are referenced. 104 1.6 rillig .undef REF3 105 1.6 rillig REF2= <${REF3}> 106 1.6 rillig REF= ${REF2} 107 1.6 rillig VAR:= ${REF} 108 1.17 rillig .if ${VAR} != "<>" 109 1.17 rillig . error 110 1.17 rillig .endif 111 1.6 rillig REF3= too late 112 1.6 rillig .if ${VAR} != "<too late>" 113 1.6 rillig . error 114 1.6 rillig .endif 115 1.6 rillig 116 1.6 rillig 117 1.6 rillig # In variable assignments using the ':=' operator, '$$' are preserved, no 118 1.6 rillig # matter how indirectly they are referenced. 119 1.6 rillig REF2= REF2:$$ $$$$ 120 1.6 rillig REF= REF:$$ $$$$ ${REF2} 121 1.6 rillig VAR:= VAR:$$ $$$$ ${REF} 122 1.6 rillig .if ${VAR} != "VAR:\$ \$\$ REF:\$ \$\$ REF2:\$ \$\$" 123 1.6 rillig . error 124 1.6 rillig .endif 125 1.6 rillig 126 1.6 rillig 127 1.6 rillig # In variable assignments using the ':=' operator, '$$' are preserved in the 128 1.6 rillig # expressions of the top level, but not in expressions that are nested. 129 1.6 rillig VAR:= top:$$ ${:Unest1\:\$\$} ${:Unest2${:U\:\$\$}} 130 1.6 rillig .if ${VAR} != "top:\$ nest1:\$ nest2:\$" 131 1.6 rillig . error 132 1.6 rillig .endif 133 1.6 rillig 134 1.6 rillig 135 1.9 rillig # In variable assignments using the ':=' operator, there may be expressions 136 1.9 rillig # containing variable modifiers, and these modifiers may refer to other 137 1.10 rillig # variables. These referred-to variables are expanded at the time of 138 1.10 rillig # assignment. The undefined variables are kept as-is and are later expanded 139 1.10 rillig # when evaluating the condition. 140 1.9 rillig # 141 1.9 rillig # Contrary to the assignment operator '=', the assignment operator ':=' 142 1.9 rillig # consumes the '$' from modifier parts. 143 1.9 rillig REF.word= 1:$$ 2:$$$$ 4:$$$$$$$$ 144 1.10 rillig .undef REF.undef 145 1.10 rillig VAR:= ${:Uword undef:@word@${REF.${word}}@}, direct: ${REF.word} ${REF.undef} 146 1.10 rillig REF.word= word.after 147 1.10 rillig REF.undef= undef.after 148 1.10 rillig .if ${VAR} != "1:2:\$ 4:\$\$ undef.after, direct: 1:\$ 2:\$\$ 4:\$\$\$\$ undef.after" 149 1.9 rillig . error 150 1.9 rillig .endif 151 1.9 rillig 152 1.9 rillig # Just for comparison, the previous example using the assignment operator '=' 153 1.10 rillig # instead of ':='. The right-hand side of the assignment is not evaluated at 154 1.10 rillig # the time of assignment but only later, when ${VAR} appears in the condition. 155 1.10 rillig # 156 1.10 rillig # At that point, both REF.word and REF.undef are defined. 157 1.9 rillig REF.word= 1:$$ 2:$$$$ 4:$$$$$$$$ 158 1.10 rillig .undef REF.undef 159 1.10 rillig VAR= ${:Uword undef:@word@${REF.${word}}@}, direct: ${REF.word} ${REF.undef} 160 1.10 rillig REF.word= word.after 161 1.10 rillig REF.undef= undef.after 162 1.10 rillig .if ${VAR} != "word.after undef.after, direct: word.after undef.after" 163 1.9 rillig . error 164 1.9 rillig .endif 165 1.9 rillig 166 1.9 rillig 167 1.8 rillig # Between var.c 1.42 from 2000-05-11 and before parse.c 1.520 from 2020-12-27, 168 1.8 rillig # if the variable name in a ':=' assignment referred to an undefined variable, 169 1.8 rillig # there were actually 2 assignments to different variables: 170 1.8 rillig # 171 1.8 rillig # Global["VAR_SUBST_${UNDEF}"] = "" 172 1.8 rillig # Global["VAR_SUBST_"] = "" 173 1.8 rillig # 174 1.8 rillig # The variable name with the empty value actually included a dollar sign. 175 1.8 rillig # Variable names with dollars are not used in practice. 176 1.8 rillig # 177 1.8 rillig # It might be a good idea to forbid undefined variables on the left-hand side 178 1.8 rillig # of a variable assignment. 179 1.5 rillig .undef UNDEF 180 1.7 rillig VAR_ASSIGN_${UNDEF}= assigned by '=' 181 1.7 rillig VAR_SUBST_${UNDEF}:= assigned by ':=' 182 1.7 rillig .if ${VAR_ASSIGN_} != "assigned by '='" 183 1.7 rillig . error 184 1.7 rillig .endif 185 1.8 rillig .if defined(${:UVAR_SUBST_\${UNDEF\}}) 186 1.7 rillig . error 187 1.7 rillig .endif 188 1.7 rillig .if ${VAR_SUBST_} != "assigned by ':='" 189 1.7 rillig . error 190 1.7 rillig .endif 191 1.3 rillig 192 1.12 rillig 193 1.12 rillig # The following test case demonstrates that the variable 'LATER' is preserved 194 1.12 rillig # in the ':=' assignment since the variable 'LATER' is not yet defined. 195 1.12 rillig # After the assignment to 'LATER', evaluating the variable 'INDIRECT' 196 1.12 rillig # evaluates 'LATER' as well. 197 1.12 rillig # 198 1.12 rillig .undef LATER 199 1.12 rillig INDIRECT:= ${LATER:S,value,replaced,} 200 1.12 rillig .if ${INDIRECT} != "" 201 1.12 rillig . error 202 1.12 rillig .endif 203 1.12 rillig LATER= late-value 204 1.12 rillig .if ${INDIRECT} != "late-replaced" 205 1.12 rillig . error 206 1.12 rillig .endif 207 1.12 rillig 208 1.12 rillig 209 1.12 rillig # Same as the test case above, except for the additional modifier ':tl' when 210 1.12 rillig # evaluating the variable 'INDIRECT'. Nothing surprising here. 211 1.12 rillig .undef LATER 212 1.12 rillig .undef later 213 1.12 rillig INDIRECT:= ${LATER:S,value,replaced,} 214 1.12 rillig .if ${INDIRECT:tl} != "" 215 1.12 rillig . error 216 1.12 rillig .endif 217 1.12 rillig LATER= uppercase-value 218 1.12 rillig later= lowercase-value 219 1.12 rillig .if ${INDIRECT:tl} != "uppercase-replaced" 220 1.12 rillig . error 221 1.12 rillig .endif 222 1.12 rillig 223 1.12 rillig 224 1.12 rillig # Similar to the two test cases above, the situation gets a bit more involved 225 1.12 rillig # here, due to the double indirection. The variable 'indirect' is supposed to 226 1.12 rillig # be the lowercase version of the variable 'INDIRECT'. 227 1.12 rillig # 228 1.12 rillig # The assignment operator ':=' for the variable 'INDIRECT' could be a '=' as 229 1.12 rillig # well, it wouldn't make a difference in this case. The crucial detail is the 230 1.14 rillig # assignment operator ':=' for the variable 'indirect'. During this 231 1.12 rillig # assignment, the variable modifier ':S,value,replaced,' is converted to 232 1.12 rillig # lowercase, which turns 'S' into 's', thus producing an unknown modifier. 233 1.12 rillig # In this case, make issues a warning, but in cases where the modifier 234 1.12 rillig # includes a '=', the modifier would be interpreted as a SysV-style 235 1.12 rillig # substitution like '.c=.o', and make would not issue a warning, leading to 236 1.12 rillig # silent unexpected behavior. 237 1.12 rillig # 238 1.14 rillig # As of 2021-11-20, the actual behavior is unexpected. Fixing it is not 239 1.14 rillig # trivial. When the assignment to 'indirect' takes place, the expressions 240 1.14 rillig # from the nested expression could be preserved, like this: 241 1.14 rillig # 242 1.14 rillig # Start with: 243 1.14 rillig # 244 1.14 rillig # indirect:= ${INDIRECT:tl} 245 1.14 rillig # 246 1.14 rillig # Since INDIRECT is defined, expand it, remembering that the modifier 247 1.14 rillig # ':tl' must still be applied to the final result. 248 1.14 rillig # 249 1.14 rillig # indirect:= ${LATER:S,value,replaced,} \ 250 1.14 rillig # OK \ 251 1.14 rillig # ${LATER:value=sysv} 252 1.14 rillig # 253 1.14 rillig # The variable 'LATER' is not defined. An idea may be to append the 254 1.14 rillig # remaining modifier ':tl' to each expression that is starting with an 255 1.14 rillig # undefined variable, resulting in: 256 1.14 rillig # 257 1.14 rillig # indirect:= ${LATER:S,value,replaced,:tl} \ 258 1.14 rillig # OK \ 259 1.14 rillig # ${LATER:value=sysv:tl} 260 1.14 rillig # 261 1.14 rillig # This would work for the first expression. The second expression ends 262 1.14 rillig # with the SysV modifier ':from=to', and when this modifier is parsed, 263 1.14 rillig # it consumes all characters until the end of the expression, which in 264 1.14 rillig # this case would replace the suffix 'value' with the literal 'sysv:tl', 265 1.14 rillig # ignoring that the ':tl' was intended to be an additional modifier. 266 1.14 rillig # 267 1.14 rillig # Due to all of this, this surprising behavior is not easy to fix. 268 1.14 rillig # 269 1.12 rillig .undef LATER 270 1.12 rillig .undef later 271 1.13 rillig INDIRECT:= ${LATER:S,value,replaced,} OK ${LATER:value=sysv} 272 1.12 rillig indirect:= ${INDIRECT:tl} 273 1.23 rillig # expect+1: Unknown modifier ":s,value,replaced," 274 1.13 rillig .if ${indirect} != " ok " 275 1.12 rillig . error 276 1.12 rillig .else 277 1.25 rillig . error 278 1.12 rillig .endif 279 1.12 rillig LATER= uppercase-value 280 1.12 rillig later= lowercase-value 281 1.23 rillig # expect+1: Unknown modifier ":s,value,replaced," 282 1.13 rillig .if ${indirect} != "uppercase-replaced ok uppercase-sysv" 283 1.25 rillig . error 284 1.12 rillig .else 285 1.12 rillig . error 286 1.12 rillig .endif 287 1.12 rillig 288 1.12 rillig 289 1.24 rillig # FIXME: The expression is evaluated twice, for no obvious reason. 290 1.24 rillig # expect+5: Bad condition 291 1.24 rillig # expect+4: Unknown modifier ":Z1" 292 1.24 rillig # expect+3: Unknown modifier ":Z2" 293 1.24 rillig # expect+2: Unknown modifier ":Z1" 294 1.24 rillig # expect+1: Unknown modifier ":Z2" 295 1.24 rillig _:= ${ < 0 :?${:Z1}:${:Z2}} 296