xref: /freebsd/contrib/bmake/unit-tests/var-scope-cmdline.mk (revision ba3c1f5972d7b90feb6e6da47905ff2757e0fe57)
1# $NetBSD: var-scope-cmdline.mk,v 1.3 2023/06/01 20:56:35 rillig Exp $
2#
3# Tests for variables specified on the command line.
4#
5# Variables that are specified on the command line override those from the
6# global scope.
7#
8# For performance reasons, the actual implementation is more complex than the
9# above single-sentence rule, in order to avoid unnecessary lookups in scopes,
10# which before var.c 1.586 from 2020-10-25 calculated the hash value of the
11# variable name once for each lookup.  Instead, when looking up the value of
12# a variable, the search often starts in the global scope since that is where
13# most of the variables are stored.  This conflicts with the statement that
14# variables from the cmdline scope override global variables, since after the
15# common case of finding a variable in the global scope, another lookup would
16# be needed in the cmdline scope to ensure that there is no overriding
17# variable there.
18#
19# Instead of this costly lookup scheme, make implements it in a different
20# way:
21#
22#	Whenever a global variable is created, this creation is ignored if
23#	there is a cmdline variable of the same name.
24#
25#	Whenever a cmdline variable is created, any global variable of the
26#	same name is deleted.
27#
28#	Whenever a global variable is deleted, nothing special happens.
29#
30#	Deleting a cmdline variable is not possible.
31#
32# These 4 rules provide the guarantee that whenever a global variable exists,
33# there cannot be a cmdline variable of the same name.  Therefore, after
34# finding a variable in the global scope, no additional lookup is needed in
35# the cmdline scope.
36#
37# The above ruleset provides the same guarantees as the simple rule "cmdline
38# overrides global".  Due to an implementation mistake, the actual behavior
39# was not entirely equivalent to the simple rule though.  The mistake was
40# that when a cmdline variable with '$$' in its name was added, a global
41# variable was deleted, but not with the exact same name as the cmdline
42# variable.  Instead, the name of the global variable was expanded one more
43# time than the name of the cmdline variable.  For variable names that didn't
44# have a '$$' in their name, it was implemented correctly all the time.
45#
46# The bug was added in var.c 1.183 on 2013-07-16, when Var_Set called
47# Var_Delete to delete the global variable.  Just two months earlier, in var.c
48# 1.174 from 2013-05-18, Var_Delete had started to expand the variable name.
49# Together, these two changes made the variable name be expanded twice in a
50# row.  This bug was fixed in var.c 1.835 from 2021-02-22.
51#
52# Another bug was the wrong assumption that "deleting a cmdline variable is
53# not possible".  Deleting such a variable has been possible since var.c 1.204
54# from 2016-02-19, when the variable modifier ':@' started to delete the
55# temporary loop variable after finishing the loop.  It was probably not
56# intended back then that a side effect of this seemingly simple change was
57# that both global and cmdline variables could now be undefined at will as a
58# side effect of evaluating a variable expression.  As of 2021-02-23, this is
59# still possible.
60#
61# Most cmdline variables are set at the very beginning, when parsing the
62# command line arguments.  Using the special target '.MAKEFLAGS', it is
63# possible to set cmdline variables at any later time.
64#
65# See also:
66#	varcmd.mk
67#	varname-makeflags.mk
68
69# A normal global variable, without any cmdline variable nearby.
70VAR=	global
71# expect+1: global
72.info ${VAR}
73
74# The global variable is "overridden" by simply deleting it and then
75# installing the cmdline variable instead.  Since there is no obvious way to
76# undefine a cmdline variable, there is no need to remember the old value
77# of the global variable could become visible again.
78#
79# See varmod-loop.mk for a non-obvious way to undefine a cmdline variable.
80.MAKEFLAGS: VAR=makeflags
81# expect+1: makeflags
82.info ${VAR}
83
84# If Var_SetWithFlags should ever forget to delete the global variable,
85# the below line would print "global" instead of the current "makeflags".
86.MAKEFLAGS: -V VAR
87