1*759b177aSSimon J. Gerraty# $NetBSD: var-op-expand.mk,v 1.23 2025/03/29 19:08:52 rillig Exp $ 22c3632d1SSimon J. Gerraty# 32c3632d1SSimon J. Gerraty# Tests for the := variable assignment operator, which expands its 42c3632d1SSimon J. Gerraty# right-hand side. 512904384SSimon J. Gerraty# 612904384SSimon J. Gerraty# See also: 712904384SSimon J. Gerraty# varname-dot-make-save_dollars.mk 82c3632d1SSimon J. Gerraty 912904384SSimon J. Gerraty# Force the test results to be independent of the default value of this 1012904384SSimon J. Gerraty# setting, which is 'yes' for NetBSD's usr.bin/make but 'no' for the bmake 1112904384SSimon J. Gerraty# distribution and pkgsrc/devel/bmake. 1206b9b3e0SSimon J. Gerraty.MAKE.SAVE_DOLLARS:= yes 132c3632d1SSimon J. Gerraty 1406b9b3e0SSimon J. Gerraty# If the right-hand side does not contain a dollar sign, the ':=' assignment 1506b9b3e0SSimon J. Gerraty# operator has the same effect as the '=' assignment operator. 1606b9b3e0SSimon J. GerratyVAR:= value 1706b9b3e0SSimon J. Gerraty.if ${VAR} != "value" 1806b9b3e0SSimon J. Gerraty. error 1906b9b3e0SSimon J. Gerraty.endif 2006b9b3e0SSimon J. Gerraty 2106b9b3e0SSimon J. Gerraty# When a ':=' assignment is performed, its right-hand side is evaluated and 2206b9b3e0SSimon J. Gerraty# expanded as far as possible. Contrary to other situations, '$$' and 23d5e0a182SSimon J. Gerraty# expressions based on undefined variables are preserved though. 24e2eeea75SSimon J. Gerraty# 25d5e0a182SSimon J. Gerraty# Whether an expression is undefined or not is determined at the end 2606b9b3e0SSimon J. Gerraty# of evaluating the expression. The consequence is that ${:Ufallback} expands 2706b9b3e0SSimon J. Gerraty# to "fallback"; initially this expression is undefined since it is based on 2806b9b3e0SSimon J. Gerraty# the variable named "", which is guaranteed to be never defined, but at the 2906b9b3e0SSimon J. Gerraty# end of evaluating the expression ${:Ufallback}, the modifier ':U' has turned 3006b9b3e0SSimon J. Gerraty# the expression into a defined expression. 3106b9b3e0SSimon J. Gerraty 3206b9b3e0SSimon J. Gerraty 3306b9b3e0SSimon J. Gerraty# literal dollar signs 3406b9b3e0SSimon J. GerratyVAR:= $$ $$$$ $$$$$$$$ 3506b9b3e0SSimon J. Gerraty.if ${VAR} != "\$ \$\$ \$\$\$\$" 3606b9b3e0SSimon J. Gerraty. error 3706b9b3e0SSimon J. Gerraty.endif 3806b9b3e0SSimon J. Gerraty 3906b9b3e0SSimon J. Gerraty 404fde40d9SSimon J. Gerraty# reference to a variable containing literal dollar signs 4106b9b3e0SSimon J. GerratyREF= $$ $$$$ $$$$$$$$ 4206b9b3e0SSimon J. GerratyVAR:= ${REF} 4306b9b3e0SSimon J. GerratyREF= too late 4406b9b3e0SSimon J. Gerraty.if ${VAR} != "\$ \$\$ \$\$\$\$" 4506b9b3e0SSimon J. Gerraty. error 4606b9b3e0SSimon J. Gerraty.endif 4706b9b3e0SSimon J. Gerraty 4806b9b3e0SSimon J. Gerraty 4906b9b3e0SSimon J. Gerraty# reference to an undefined variable 5006b9b3e0SSimon J. Gerraty.undef UNDEF 5106b9b3e0SSimon J. GerratyVAR:= <${UNDEF}> 524fde40d9SSimon J. Gerraty.if ${VAR} != "<>" 534fde40d9SSimon J. Gerraty. error 544fde40d9SSimon J. Gerraty.endif 5506b9b3e0SSimon J. GerratyUNDEF= after 5606b9b3e0SSimon J. Gerraty.if ${VAR} != "<after>" 5706b9b3e0SSimon J. Gerraty. error 5806b9b3e0SSimon J. Gerraty.endif 5906b9b3e0SSimon J. Gerraty 6006b9b3e0SSimon J. Gerraty 6106b9b3e0SSimon J. Gerraty# reference to a variable whose name is computed from another variable 6206b9b3e0SSimon J. GerratyREF2= referred to 6306b9b3e0SSimon J. GerratyREF= REF2 6406b9b3e0SSimon J. GerratyVAR:= ${${REF}} 6506b9b3e0SSimon J. GerratyREF= too late 6606b9b3e0SSimon J. Gerraty.if ${VAR} != "referred to" 6706b9b3e0SSimon J. Gerraty. error 6806b9b3e0SSimon J. Gerraty.endif 6906b9b3e0SSimon J. Gerraty 7006b9b3e0SSimon J. Gerraty 7106b9b3e0SSimon J. Gerraty# expression with an indirect modifier referring to an undefined variable 7206b9b3e0SSimon J. Gerraty.undef UNDEF 7306b9b3e0SSimon J. GerratyVAR:= ${:${UNDEF}} 744fde40d9SSimon J. Gerraty.if ${VAR} != "" 754fde40d9SSimon J. Gerraty. error 764fde40d9SSimon J. Gerraty.endif 7706b9b3e0SSimon J. GerratyUNDEF= Uwas undefined 7806b9b3e0SSimon J. Gerraty.if ${VAR} != "was undefined" 7906b9b3e0SSimon J. Gerraty. error 8006b9b3e0SSimon J. Gerraty.endif 8106b9b3e0SSimon J. Gerraty 8206b9b3e0SSimon J. Gerraty 8306b9b3e0SSimon J. Gerraty# expression with an indirect modifier referring to another variable that 8406b9b3e0SSimon J. Gerraty# in turn refers to an undefined variable 85e2eeea75SSimon J. Gerraty# 8606b9b3e0SSimon J. Gerraty# XXX: Even though this is a ':=' assignment, the '${UNDEF}' in the part of 8706b9b3e0SSimon J. Gerraty# the variable modifier is not preserved. To preserve it, ParseModifierPart 8806b9b3e0SSimon J. Gerraty# would have to call VarSubstExpr somehow since this is the only piece of 8906b9b3e0SSimon J. Gerraty# code that takes care of this global variable. 9006b9b3e0SSimon J. Gerraty.undef UNDEF 9106b9b3e0SSimon J. GerratyREF= U${UNDEF} 9206b9b3e0SSimon J. Gerraty#.MAKEFLAGS: -dv 9306b9b3e0SSimon J. GerratyVAR:= ${:${REF}} 9406b9b3e0SSimon J. Gerraty#.MAKEFLAGS: -d0 9506b9b3e0SSimon J. GerratyREF= too late 9606b9b3e0SSimon J. GerratyUNDEF= Uwas undefined 9706b9b3e0SSimon J. Gerraty.if ${VAR} != "" 9806b9b3e0SSimon J. Gerraty. error 9906b9b3e0SSimon J. Gerraty.endif 10006b9b3e0SSimon J. Gerraty 10106b9b3e0SSimon J. Gerraty 10206b9b3e0SSimon J. Gerraty# In variable assignments using the ':=' operator, undefined variables are 10306b9b3e0SSimon J. Gerraty# preserved, no matter how indirectly they are referenced. 10406b9b3e0SSimon J. Gerraty.undef REF3 10506b9b3e0SSimon J. GerratyREF2= <${REF3}> 10606b9b3e0SSimon J. GerratyREF= ${REF2} 10706b9b3e0SSimon J. GerratyVAR:= ${REF} 1084fde40d9SSimon J. Gerraty.if ${VAR} != "<>" 1094fde40d9SSimon J. Gerraty. error 1104fde40d9SSimon J. Gerraty.endif 11106b9b3e0SSimon J. GerratyREF3= too late 11206b9b3e0SSimon J. Gerraty.if ${VAR} != "<too late>" 11306b9b3e0SSimon J. Gerraty. error 11406b9b3e0SSimon J. Gerraty.endif 11506b9b3e0SSimon J. Gerraty 11606b9b3e0SSimon J. Gerraty 11706b9b3e0SSimon J. Gerraty# In variable assignments using the ':=' operator, '$$' are preserved, no 11806b9b3e0SSimon J. Gerraty# matter how indirectly they are referenced. 11906b9b3e0SSimon J. GerratyREF2= REF2:$$ $$$$ 12006b9b3e0SSimon J. GerratyREF= REF:$$ $$$$ ${REF2} 12106b9b3e0SSimon J. GerratyVAR:= VAR:$$ $$$$ ${REF} 12206b9b3e0SSimon J. Gerraty.if ${VAR} != "VAR:\$ \$\$ REF:\$ \$\$ REF2:\$ \$\$" 12306b9b3e0SSimon J. Gerraty. error 12406b9b3e0SSimon J. Gerraty.endif 12506b9b3e0SSimon J. Gerraty 12606b9b3e0SSimon J. Gerraty 12706b9b3e0SSimon J. Gerraty# In variable assignments using the ':=' operator, '$$' are preserved in the 12806b9b3e0SSimon J. Gerraty# expressions of the top level, but not in expressions that are nested. 12906b9b3e0SSimon J. GerratyVAR:= top:$$ ${:Unest1\:\$\$} ${:Unest2${:U\:\$\$}} 13006b9b3e0SSimon J. Gerraty.if ${VAR} != "top:\$ nest1:\$ nest2:\$" 13106b9b3e0SSimon J. Gerraty. error 13206b9b3e0SSimon J. Gerraty.endif 13306b9b3e0SSimon J. Gerraty 13406b9b3e0SSimon J. Gerraty 13506b9b3e0SSimon J. Gerraty# In variable assignments using the ':=' operator, there may be expressions 13606b9b3e0SSimon J. Gerraty# containing variable modifiers, and these modifiers may refer to other 13706b9b3e0SSimon J. Gerraty# variables. These referred-to variables are expanded at the time of 13806b9b3e0SSimon J. Gerraty# assignment. The undefined variables are kept as-is and are later expanded 13906b9b3e0SSimon J. Gerraty# when evaluating the condition. 14006b9b3e0SSimon J. Gerraty# 14106b9b3e0SSimon J. Gerraty# Contrary to the assignment operator '=', the assignment operator ':=' 14206b9b3e0SSimon J. Gerraty# consumes the '$' from modifier parts. 14306b9b3e0SSimon J. GerratyREF.word= 1:$$ 2:$$$$ 4:$$$$$$$$ 14406b9b3e0SSimon J. Gerraty.undef REF.undef 14506b9b3e0SSimon J. GerratyVAR:= ${:Uword undef:@word@${REF.${word}}@}, direct: ${REF.word} ${REF.undef} 14606b9b3e0SSimon J. GerratyREF.word= word.after 14706b9b3e0SSimon J. GerratyREF.undef= undef.after 14806b9b3e0SSimon J. Gerraty.if ${VAR} != "1:2:\$ 4:\$\$ undef.after, direct: 1:\$ 2:\$\$ 4:\$\$\$\$ undef.after" 14906b9b3e0SSimon J. Gerraty. error 15006b9b3e0SSimon J. Gerraty.endif 15106b9b3e0SSimon J. Gerraty 15206b9b3e0SSimon J. Gerraty# Just for comparison, the previous example using the assignment operator '=' 15306b9b3e0SSimon J. Gerraty# instead of ':='. The right-hand side of the assignment is not evaluated at 15406b9b3e0SSimon J. Gerraty# the time of assignment but only later, when ${VAR} appears in the condition. 15506b9b3e0SSimon J. Gerraty# 15606b9b3e0SSimon J. Gerraty# At that point, both REF.word and REF.undef are defined. 15706b9b3e0SSimon J. GerratyREF.word= 1:$$ 2:$$$$ 4:$$$$$$$$ 15806b9b3e0SSimon J. Gerraty.undef REF.undef 15906b9b3e0SSimon J. GerratyVAR= ${:Uword undef:@word@${REF.${word}}@}, direct: ${REF.word} ${REF.undef} 16006b9b3e0SSimon J. GerratyREF.word= word.after 16106b9b3e0SSimon J. GerratyREF.undef= undef.after 16206b9b3e0SSimon J. Gerraty.if ${VAR} != "word.after undef.after, direct: word.after undef.after" 16306b9b3e0SSimon J. Gerraty. error 16406b9b3e0SSimon J. Gerraty.endif 16506b9b3e0SSimon J. Gerraty 16606b9b3e0SSimon J. Gerraty 16706b9b3e0SSimon J. Gerraty# Between var.c 1.42 from 2000-05-11 and before parse.c 1.520 from 2020-12-27, 16806b9b3e0SSimon J. Gerraty# if the variable name in a ':=' assignment referred to an undefined variable, 16906b9b3e0SSimon J. Gerraty# there were actually 2 assignments to different variables: 17006b9b3e0SSimon J. Gerraty# 17106b9b3e0SSimon J. Gerraty# Global["VAR_SUBST_${UNDEF}"] = "" 17206b9b3e0SSimon J. Gerraty# Global["VAR_SUBST_"] = "" 17306b9b3e0SSimon J. Gerraty# 17406b9b3e0SSimon J. Gerraty# The variable name with the empty value actually included a dollar sign. 17506b9b3e0SSimon J. Gerraty# Variable names with dollars are not used in practice. 17606b9b3e0SSimon J. Gerraty# 17706b9b3e0SSimon J. Gerraty# It might be a good idea to forbid undefined variables on the left-hand side 17806b9b3e0SSimon J. Gerraty# of a variable assignment. 17906b9b3e0SSimon J. Gerraty.undef UNDEF 18006b9b3e0SSimon J. GerratyVAR_ASSIGN_${UNDEF}= assigned by '=' 18106b9b3e0SSimon J. GerratyVAR_SUBST_${UNDEF}:= assigned by ':=' 18206b9b3e0SSimon J. Gerraty.if ${VAR_ASSIGN_} != "assigned by '='" 18306b9b3e0SSimon J. Gerraty. error 18406b9b3e0SSimon J. Gerraty.endif 18506b9b3e0SSimon J. Gerraty.if defined(${:UVAR_SUBST_\${UNDEF\}}) 18606b9b3e0SSimon J. Gerraty. error 18706b9b3e0SSimon J. Gerraty.endif 18806b9b3e0SSimon J. Gerraty.if ${VAR_SUBST_} != "assigned by ':='" 18906b9b3e0SSimon J. Gerraty. error 19006b9b3e0SSimon J. Gerraty.endif 191e2eeea75SSimon J. Gerraty 19212904384SSimon J. Gerraty 19312904384SSimon J. Gerraty# The following test case demonstrates that the variable 'LATER' is preserved 19412904384SSimon J. Gerraty# in the ':=' assignment since the variable 'LATER' is not yet defined. 19512904384SSimon J. Gerraty# After the assignment to 'LATER', evaluating the variable 'INDIRECT' 19612904384SSimon J. Gerraty# evaluates 'LATER' as well. 19712904384SSimon J. Gerraty# 19812904384SSimon J. Gerraty.undef LATER 19912904384SSimon J. GerratyINDIRECT:= ${LATER:S,value,replaced,} 20012904384SSimon J. Gerraty.if ${INDIRECT} != "" 20112904384SSimon J. Gerraty. error 20212904384SSimon J. Gerraty.endif 20312904384SSimon J. GerratyLATER= late-value 20412904384SSimon J. Gerraty.if ${INDIRECT} != "late-replaced" 20512904384SSimon J. Gerraty. error 20612904384SSimon J. Gerraty.endif 20712904384SSimon J. Gerraty 20812904384SSimon J. Gerraty 20912904384SSimon J. Gerraty# Same as the test case above, except for the additional modifier ':tl' when 21012904384SSimon J. Gerraty# evaluating the variable 'INDIRECT'. Nothing surprising here. 21112904384SSimon J. Gerraty.undef LATER 21212904384SSimon J. Gerraty.undef later 21312904384SSimon J. GerratyINDIRECT:= ${LATER:S,value,replaced,} 21412904384SSimon J. Gerraty.if ${INDIRECT:tl} != "" 21512904384SSimon J. Gerraty. error 21612904384SSimon J. Gerraty.endif 21712904384SSimon J. GerratyLATER= uppercase-value 21812904384SSimon J. Gerratylater= lowercase-value 21912904384SSimon J. Gerraty.if ${INDIRECT:tl} != "uppercase-replaced" 22012904384SSimon J. Gerraty. error 22112904384SSimon J. Gerraty.endif 22212904384SSimon J. Gerraty 22312904384SSimon J. Gerraty 22412904384SSimon J. Gerraty# Similar to the two test cases above, the situation gets a bit more involved 22512904384SSimon J. Gerraty# here, due to the double indirection. The variable 'indirect' is supposed to 22612904384SSimon J. Gerraty# be the lowercase version of the variable 'INDIRECT'. 22712904384SSimon J. Gerraty# 22812904384SSimon J. Gerraty# The assignment operator ':=' for the variable 'INDIRECT' could be a '=' as 22912904384SSimon J. Gerraty# well, it wouldn't make a difference in this case. The crucial detail is the 23012904384SSimon J. Gerraty# assignment operator ':=' for the variable 'indirect'. During this 23112904384SSimon J. Gerraty# assignment, the variable modifier ':S,value,replaced,' is converted to 23212904384SSimon J. Gerraty# lowercase, which turns 'S' into 's', thus producing an unknown modifier. 23312904384SSimon J. Gerraty# In this case, make issues a warning, but in cases where the modifier 23412904384SSimon J. Gerraty# includes a '=', the modifier would be interpreted as a SysV-style 23512904384SSimon J. Gerraty# substitution like '.c=.o', and make would not issue a warning, leading to 23612904384SSimon J. Gerraty# silent unexpected behavior. 23712904384SSimon J. Gerraty# 23812904384SSimon J. Gerraty# As of 2021-11-20, the actual behavior is unexpected. Fixing it is not 23912904384SSimon J. Gerraty# trivial. When the assignment to 'indirect' takes place, the expressions 24012904384SSimon J. Gerraty# from the nested expression could be preserved, like this: 24112904384SSimon J. Gerraty# 24212904384SSimon J. Gerraty# Start with: 24312904384SSimon J. Gerraty# 24412904384SSimon J. Gerraty# indirect:= ${INDIRECT:tl} 24512904384SSimon J. Gerraty# 24612904384SSimon J. Gerraty# Since INDIRECT is defined, expand it, remembering that the modifier 24712904384SSimon J. Gerraty# ':tl' must still be applied to the final result. 24812904384SSimon J. Gerraty# 24912904384SSimon J. Gerraty# indirect:= ${LATER:S,value,replaced,} \ 25012904384SSimon J. Gerraty# OK \ 25112904384SSimon J. Gerraty# ${LATER:value=sysv} 25212904384SSimon J. Gerraty# 25312904384SSimon J. Gerraty# The variable 'LATER' is not defined. An idea may be to append the 25412904384SSimon J. Gerraty# remaining modifier ':tl' to each expression that is starting with an 25512904384SSimon J. Gerraty# undefined variable, resulting in: 25612904384SSimon J. Gerraty# 25712904384SSimon J. Gerraty# indirect:= ${LATER:S,value,replaced,:tl} \ 25812904384SSimon J. Gerraty# OK \ 25912904384SSimon J. Gerraty# ${LATER:value=sysv:tl} 26012904384SSimon J. Gerraty# 26112904384SSimon J. Gerraty# This would work for the first expression. The second expression ends 26212904384SSimon J. Gerraty# with the SysV modifier ':from=to', and when this modifier is parsed, 26312904384SSimon J. Gerraty# it consumes all characters until the end of the expression, which in 26412904384SSimon J. Gerraty# this case would replace the suffix 'value' with the literal 'sysv:tl', 26512904384SSimon J. Gerraty# ignoring that the ':tl' was intended to be an additional modifier. 26612904384SSimon J. Gerraty# 26712904384SSimon J. Gerraty# Due to all of this, this surprising behavior is not easy to fix. 26812904384SSimon J. Gerraty# 26912904384SSimon J. Gerraty.undef LATER 27012904384SSimon J. Gerraty.undef later 27112904384SSimon J. GerratyINDIRECT:= ${LATER:S,value,replaced,} OK ${LATER:value=sysv} 27212904384SSimon J. Gerratyindirect:= ${INDIRECT:tl} 273*759b177aSSimon J. Gerraty# expect+1: Unknown modifier ":s,value,replaced," 27412904384SSimon J. Gerraty.if ${indirect} != " ok " 27512904384SSimon J. Gerraty. error 27612904384SSimon J. Gerraty.else 277148ee845SSimon J. Gerraty# expect+1: warning: XXX Neither branch should be taken. 27812904384SSimon J. Gerraty. warning XXX Neither branch should be taken. 27912904384SSimon J. Gerraty.endif 28012904384SSimon J. GerratyLATER= uppercase-value 28112904384SSimon J. Gerratylater= lowercase-value 282*759b177aSSimon J. Gerraty# expect+1: Unknown modifier ":s,value,replaced," 28312904384SSimon J. Gerraty.if ${indirect} != "uppercase-replaced ok uppercase-sysv" 284148ee845SSimon J. Gerraty# expect+1: warning: XXX Neither branch should be taken. 28512904384SSimon J. Gerraty. warning XXX Neither branch should be taken. 28612904384SSimon J. Gerraty.else 28712904384SSimon J. Gerraty. error 28812904384SSimon J. Gerraty.endif 28912904384SSimon J. Gerraty 29012904384SSimon J. Gerraty 2912c3632d1SSimon J. Gerratyall: 2922c3632d1SSimon J. Gerraty @:; 293