varparse-undef-partial.mk revision 1.2 1 1.2 rillig # $NetBSD: varparse-undef-partial.mk,v 1.2 2020/09/27 09:53:41 rillig Exp $
2 1.2 rillig
3 1.2 rillig # When an undefined variable is expanded in a ':=' assignment, only the
4 1.2 rillig # initial '$' of the variable expression is skipped by the parser, while
5 1.2 rillig # the remaining expression is evaluated. In edge cases this can lead to
6 1.2 rillig # a completely different interpretation of the partially expanded text.
7 1.2 rillig
8 1.2 rillig LIST= ${DEF} ${UNDEF} ${VAR.${PARAM}} end
9 1.2 rillig DEF= defined
10 1.2 rillig PARAM= :Q
11 1.2 rillig
12 1.2 rillig # The expression ${VAR.{PARAM}} refers to the variable named "VAR.:Q",
13 1.2 rillig # with the ":Q" being part of the name. This variable is not defined,
14 1.2 rillig # therefore the initial '$' of that whole expression is skipped by the
15 1.2 rillig # parser (see Var_Subst, the Buf_AddByte in the else branch) and the rest
16 1.2 rillig # of the expression is expanded as usual.
17 1.2 rillig #
18 1.2 rillig # The resulting variable expression is ${VAR.:Q}, which means that the
19 1.2 rillig # interpretation of the ":Q" has changed from being part of the variable
20 1.2 rillig # name to being a variable modifier. This is a classical code injection.
21 1.2 rillig EVAL:= ${LIST}
22 1.2 rillig .if ${EVAL} != "defined end"
23 1.2 rillig . error ${EVAL}
24 1.2 rillig .endif
25 1.2 rillig
26 1.2 rillig # Define the possible outcomes, to see which of them gets expanded.
27 1.2 rillig VAR.= var-dot without parameter
28 1.2 rillig ${:UVAR.\:Q}= var-dot with parameter :Q
29 1.2 rillig
30 1.2 rillig # At this point, the variable "VAR." is defined, therefore the expression
31 1.2 rillig # ${VAR.:Q} is expanded as usual.
32 1.2 rillig .if ${EVAL} != "defined var-dot\\ without\\ parameter end"
33 1.2 rillig . error ${EVAL}
34 1.2 rillig .endif
35 1.2 rillig
36 1.2 rillig # In contrast to the previous line, evaluating the original LIST again now
37 1.2 rillig # produces a different result since the ":Q" has already been inserted
38 1.2 rillig # literally into the expression. The variable named "VAR.:Q" is defined,
39 1.2 rillig # therefore it is resolved as usual. The ":Q" is interpreted as part of the
40 1.2 rillig # variable name, as would be expected from reading the variable expression.
41 1.2 rillig EVAL:= ${LIST}
42 1.2 rillig .if ${EVAL} != "defined var-dot with parameter :Q end"
43 1.2 rillig . error ${EVAL}
44 1.2 rillig .endif
45 1.2 rillig
46 1.2 rillig # It's difficult to decide what the best behavior is in this situation.
47 1.2 rillig # Should the whole expression be skipped for now, or should the inner
48 1.2 rillig # subexpressions be expanded already?
49 1.2 rillig #
50 1.2 rillig # Example 1:
51 1.2 rillig # CFLAGS:= ${CFLAGS:N-W*} ${COPTS.${COMPILER}}
52 1.2 rillig #
53 1.2 rillig # The variable COMPILER typically contains an identifier and the variable is
54 1.2 rillig # not modified later. In this practical case, it does not matter whether the
55 1.2 rillig # expression is expanded early, or whether the whole ${COPTS.${COMPILER}} is
56 1.2 rillig # expanded as soon as the variable COPTS.${COMPILER} becomes defined. The
57 1.2 rillig # expression ${COMPILER} would be expanded several times, but in this simple
58 1.2 rillig # scenario there would not be any side effects.
59 1.2 rillig #
60 1.2 rillig # TODO: Add a practical example where early/lazy expansion actually makes a
61 1.2 rillig # difference.
62 1.2 rillig
63 1.2 rillig all:
64 1.2 rillig @:
65