Home | History | Annotate | Line # | Download | only in unit-tests
      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