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