Home | History | Annotate | Line # | Download | only in unit-tests
varparse-undef-partial.mk revision 1.3
      1  1.3  rillig # $NetBSD: varparse-undef-partial.mk,v 1.3 2020/11/04 05:10:01 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.3  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.3  rillig # ${VAR.:Q} is expanded, consisting of the variable name "VAR." and the
     32  1.3  rillig # modifier ":Q".
     33  1.2  rillig .if ${EVAL} != "defined  var-dot\\ without\\ parameter end"
     34  1.2  rillig .  error ${EVAL}
     35  1.2  rillig .endif
     36  1.2  rillig 
     37  1.2  rillig # In contrast to the previous line, evaluating the original LIST again now
     38  1.3  rillig # produces a different result since the variable named "VAR.:Q" is now
     39  1.3  rillig # defined.  It is expanded as usual, interpreting the ":Q" 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