xref: /freebsd/contrib/bmake/unit-tests/varmod-ifelse.mk (revision dba7b0ef928af88caa38728a73657b837aeeac93)
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