1*dba7b0efSSimon J. Gerraty# $NetBSD: varmod-ifelse.mk,v 1.9 2021/01/25 19:05:39 rillig Exp $ 22c3632d1SSimon J. Gerraty# 32c3632d1SSimon J. Gerraty# Tests for the ${cond:?then:else} variable modifier, which evaluates either 42c3632d1SSimon J. Gerraty# the then-expression or the else-expression, depending on the condition. 5956e45f6SSimon J. Gerraty# 6956e45f6SSimon J. Gerraty# The modifier was added on 1998-04-01. 7956e45f6SSimon J. Gerraty# 8956e45f6SSimon J. Gerraty# Until 2015-10-11, the modifier always evaluated both the "then" and the 9956e45f6SSimon J. Gerraty# "else" expressions. 102c3632d1SSimon J. Gerraty 112c3632d1SSimon J. Gerraty# TODO: Implementation 122c3632d1SSimon J. Gerraty 13956e45f6SSimon J. Gerraty# The variable name of the expression is expanded and then taken as the 14956e45f6SSimon J. Gerraty# condition. In this case it becomes: 15956e45f6SSimon J. Gerraty# 16956e45f6SSimon J. Gerraty# variable expression == "variable expression" 17956e45f6SSimon J. Gerraty# 18956e45f6SSimon J. Gerraty# This confuses the parser, which expects an operator instead of the bare 19956e45f6SSimon J. Gerraty# word "expression". If the name were expanded lazily, everything would be 20956e45f6SSimon J. Gerraty# fine since the condition would be: 21956e45f6SSimon J. Gerraty# 22956e45f6SSimon J. Gerraty# ${:Uvariable expression} == "literal" 23956e45f6SSimon J. Gerraty# 24956e45f6SSimon J. Gerraty# Evaluating the variable name lazily would require additional code in 25956e45f6SSimon J. Gerraty# Var_Parse and ParseVarname, it would be more useful and predictable 26956e45f6SSimon J. Gerraty# though. 27956e45f6SSimon J. Gerraty.if ${${:Uvariable expression} == "literal":?bad:bad} 28956e45f6SSimon J. Gerraty. error 29956e45f6SSimon J. Gerraty.else 30956e45f6SSimon J. Gerraty. error 31956e45f6SSimon J. Gerraty.endif 32956e45f6SSimon J. Gerraty 33956e45f6SSimon J. Gerraty# In a variable assignment, undefined variables are not an error. 34956e45f6SSimon J. Gerraty# Because of the early expansion, the whole condition evaluates to 35956e45f6SSimon J. Gerraty# ' == ""' though, which cannot be parsed because the left-hand side looks 36956e45f6SSimon J. Gerraty# empty. 37956e45f6SSimon J. GerratyCOND:= ${${UNDEF} == "":?bad-assign:bad-assign} 38956e45f6SSimon J. Gerraty 39956e45f6SSimon J. Gerraty# In a condition, undefined variables generate a "Malformed conditional" 40956e45f6SSimon J. Gerraty# error. That error message is wrong though. In lint mode, the correct 41956e45f6SSimon J. Gerraty# "Undefined variable" error message is generated. 42956e45f6SSimon J. Gerraty# The difference to the ':=' variable assignment is the additional 43956e45f6SSimon J. Gerraty# "Malformed conditional" error message. 44956e45f6SSimon J. Gerraty.if ${${UNDEF} == "":?bad-cond:bad-cond} 45956e45f6SSimon J. Gerraty. error 46956e45f6SSimon J. Gerraty.else 47956e45f6SSimon J. Gerraty. error 48956e45f6SSimon J. Gerraty.endif 49956e45f6SSimon J. Gerraty 50956e45f6SSimon J. Gerraty# When the :? is parsed, it is greedy. The else branch spans all the 51956e45f6SSimon J. Gerraty# text, up until the closing character '}', even if the text looks like 52956e45f6SSimon J. Gerraty# another modifier. 53956e45f6SSimon J. Gerraty.if ${1:?then:else:Q} != "then" 54956e45f6SSimon J. Gerraty. error 55956e45f6SSimon J. Gerraty.endif 56956e45f6SSimon J. Gerraty.if ${0:?then:else:Q} != "else:Q" 57956e45f6SSimon J. Gerraty. error 58956e45f6SSimon J. Gerraty.endif 59956e45f6SSimon J. Gerraty 60e2eeea75SSimon J. Gerraty# This line generates 2 error messages. The first comes from evaluating the 61e2eeea75SSimon J. Gerraty# malformed conditional "1 == == 2", which is reported as "Bad conditional 62e2eeea75SSimon J. Gerraty# expression" by ApplyModifier_IfElse. The variable expression containing that 63e2eeea75SSimon J. Gerraty# conditional therefore returns a parse error from Var_Parse, and this parse 64e2eeea75SSimon J. Gerraty# error propagates to CondEvalExpression, where the "Malformed conditional" 65e2eeea75SSimon J. Gerraty# comes from. 66e2eeea75SSimon J. Gerraty.if ${1 == == 2:?yes:no} != "" 67e2eeea75SSimon J. Gerraty. error 68e2eeea75SSimon J. Gerraty.else 69e2eeea75SSimon J. Gerraty. error 70e2eeea75SSimon J. Gerraty.endif 71e2eeea75SSimon J. Gerraty 72e2eeea75SSimon J. Gerraty# If the "Bad conditional expression" appears in a quoted string literal, the 73e2eeea75SSimon J. Gerraty# error message "Malformed conditional" is not printed, leaving only the "Bad 74e2eeea75SSimon J. Gerraty# conditional expression". 75e2eeea75SSimon J. Gerraty# 76e2eeea75SSimon J. Gerraty# XXX: The left-hand side is enclosed in quotes. This results in Var_Parse 77e2eeea75SSimon J. Gerraty# being called without VARE_UNDEFERR being set. When ApplyModifier_IfElse 78e2eeea75SSimon J. Gerraty# returns AMR_CLEANUP as result, Var_Parse returns varUndefined since the 79e2eeea75SSimon J. Gerraty# value of the variable expression is still undefined. CondParser_String is 80e2eeea75SSimon J. Gerraty# then supposed to do proper error handling, but since varUndefined is local 81e2eeea75SSimon J. Gerraty# to var.c, it cannot distinguish this return value from an ordinary empty 82e2eeea75SSimon J. Gerraty# string. The left-hand side of the comparison is therefore just an empty 83e2eeea75SSimon J. Gerraty# string, which is obviously equal to the empty string on the right-hand side. 84e2eeea75SSimon J. Gerraty# 85e2eeea75SSimon J. Gerraty# XXX: The debug log for -dc shows a comparison between 1.0 and 0.0. The 86e2eeea75SSimon J. Gerraty# condition should be detected as being malformed before any comparison is 87e2eeea75SSimon J. Gerraty# done since there is no well-formed comparison in the condition at all. 88e2eeea75SSimon J. Gerraty.MAKEFLAGS: -dc 89e2eeea75SSimon J. Gerraty.if "${1 == == 2:?yes:no}" != "" 90e2eeea75SSimon J. Gerraty. error 91e2eeea75SSimon J. Gerraty.else 92e2eeea75SSimon J. Gerraty. warning Oops, the parse error should have been propagated. 93e2eeea75SSimon J. Gerraty.endif 94e2eeea75SSimon J. Gerraty.MAKEFLAGS: -d0 95e2eeea75SSimon J. Gerraty 9606b9b3e0SSimon J. Gerraty# As of 2020-12-10, the variable "name" is first expanded, and the result of 9706b9b3e0SSimon J. Gerraty# this expansion is then taken as the condition. To force the variable 9806b9b3e0SSimon J. Gerraty# expression in the condition to be evaluated at exactly the right point, 9906b9b3e0SSimon J. Gerraty# the '$' of the intended '${VAR}' escapes from the parser in form of the 10006b9b3e0SSimon J. Gerraty# expression ${:U\$}. Because of this escaping, the variable "name" and thus 10106b9b3e0SSimon J. Gerraty# the condition ends up as "${VAR} == value", just as intended. 10206b9b3e0SSimon J. Gerraty# 10306b9b3e0SSimon J. Gerraty# This hack does not work for variables from .for loops since these are 10406b9b3e0SSimon J. Gerraty# expanded at parse time to their corresponding ${:Uvalue} expressions. 10506b9b3e0SSimon J. Gerraty# Making the '$' of the '${VAR}' expression indirect hides this expression 106*dba7b0efSSimon J. Gerraty# from the parser of the .for loop body. See ForLoop_SubstVarLong. 10706b9b3e0SSimon J. Gerraty.MAKEFLAGS: -dc 10806b9b3e0SSimon J. GerratyVAR= value 10906b9b3e0SSimon J. Gerraty.if ${ ${:U\$}{VAR} == value :?ok:bad} != "ok" 11006b9b3e0SSimon J. Gerraty. error 11106b9b3e0SSimon J. Gerraty.endif 11206b9b3e0SSimon J. Gerraty.MAKEFLAGS: -d0 11306b9b3e0SSimon J. Gerraty 1142c3632d1SSimon J. Gerratyall: 1152c3632d1SSimon J. Gerraty @:; 116