1*d5e0a182SSimon J. Gerraty# $NetBSD: varparse-undef-partial.mk,v 1.5 2024/01/07 11:39:04 rillig Exp $
2956e45f6SSimon J. Gerraty
3956e45f6SSimon J. Gerraty# When an undefined variable is expanded in a ':=' assignment, only the
4*d5e0a182SSimon J. Gerraty# initial '$' of the expression is skipped by the parser, while
5956e45f6SSimon J. Gerraty# the remaining expression is evaluated.  In edge cases this can lead to
6956e45f6SSimon J. Gerraty# a completely different interpretation of the partially expanded text.
7956e45f6SSimon J. Gerraty
8956e45f6SSimon J. GerratyLIST=	${DEF} ${UNDEF} ${VAR.${PARAM}} end
9956e45f6SSimon J. GerratyDEF=	defined
10956e45f6SSimon J. GerratyPARAM=	:Q
11956e45f6SSimon J. Gerraty
12e2eeea75SSimon J. Gerraty# The expression ${VAR.${PARAM}} refers to the variable named "VAR.:Q",
13956e45f6SSimon J. Gerraty# with the ":Q" being part of the name.  This variable is not defined,
14*d5e0a182SSimon J. Gerraty# therefore the initial '$' of that whole expression is skipped by the parser
15*d5e0a182SSimon J. Gerraty# (see VarSubstExpr) and the rest of the expression is expanded as usual.
16956e45f6SSimon J. Gerraty#
17*d5e0a182SSimon J. Gerraty# The resulting expression is ${VAR.:Q}, which means that the
18956e45f6SSimon J. Gerraty# interpretation of the ":Q" has changed from being part of the variable
19956e45f6SSimon J. Gerraty# name to being a variable modifier.  This is a classical code injection.
20956e45f6SSimon J. GerratyEVAL:=	${LIST}
21956e45f6SSimon J. Gerraty.if ${EVAL} != "defined   end"
22956e45f6SSimon J. Gerraty.  error ${EVAL}
23956e45f6SSimon J. Gerraty.endif
24956e45f6SSimon J. Gerraty
25956e45f6SSimon J. Gerraty# Define the possible outcomes, to see which of them gets expanded.
26956e45f6SSimon J. GerratyVAR.=		var-dot without parameter
27956e45f6SSimon J. Gerraty${:UVAR.\:Q}=	var-dot with parameter :Q
28956e45f6SSimon J. Gerraty
29956e45f6SSimon J. Gerraty# At this point, the variable "VAR." is defined, therefore the expression
30e2eeea75SSimon J. Gerraty# ${VAR.:Q} is expanded, consisting of the variable name "VAR." and the
31e2eeea75SSimon J. Gerraty# modifier ":Q".
32956e45f6SSimon J. Gerraty.if ${EVAL} != "defined  var-dot\\ without\\ parameter end"
33956e45f6SSimon J. Gerraty.  error ${EVAL}
34956e45f6SSimon J. Gerraty.endif
35956e45f6SSimon J. Gerraty
36956e45f6SSimon J. Gerraty# In contrast to the previous line, evaluating the original LIST again now
37e2eeea75SSimon J. Gerraty# produces a different result since the variable named "VAR.:Q" is now
38e2eeea75SSimon J. Gerraty# defined.  It is expanded as usual, interpreting the ":Q" as part of the
39*d5e0a182SSimon J. Gerraty# variable name, as would be expected from reading the expression.
40956e45f6SSimon J. GerratyEVAL:=	${LIST}
41956e45f6SSimon J. Gerraty.if ${EVAL} != "defined  var-dot with parameter :Q end"
42956e45f6SSimon J. Gerraty.  error ${EVAL}
43956e45f6SSimon J. Gerraty.endif
44956e45f6SSimon J. Gerraty
45956e45f6SSimon J. Gerraty# It's difficult to decide what the best behavior is in this situation.
46956e45f6SSimon J. Gerraty# Should the whole expression be skipped for now, or should the inner
47956e45f6SSimon J. Gerraty# subexpressions be expanded already?
48956e45f6SSimon J. Gerraty#
49956e45f6SSimon J. Gerraty# Example 1:
50956e45f6SSimon J. Gerraty# CFLAGS:=	${CFLAGS:N-W*} ${COPTS.${COMPILER}}
51956e45f6SSimon J. Gerraty#
52956e45f6SSimon J. Gerraty# The variable COMPILER typically contains an identifier and the variable is
53956e45f6SSimon J. Gerraty# not modified later.  In this practical case, it does not matter whether the
54956e45f6SSimon J. Gerraty# expression is expanded early, or whether the whole ${COPTS.${COMPILER}} is
55956e45f6SSimon J. Gerraty# expanded as soon as the variable COPTS.${COMPILER} becomes defined.  The
56956e45f6SSimon J. Gerraty# expression ${COMPILER} would be expanded several times, but in this simple
57956e45f6SSimon J. Gerraty# scenario there would not be any side effects.
58956e45f6SSimon J. Gerraty#
59956e45f6SSimon J. Gerraty# TODO: Add a practical example where early/lazy expansion actually makes a
60956e45f6SSimon J. Gerraty# difference.
61956e45f6SSimon J. Gerraty
62956e45f6SSimon J. Gerratyall:
63956e45f6SSimon J. Gerraty	@:
64