[SV 45728] Detect changes in .VARIABLES more accurately.
For performance, we only recompute .VARIABLES when (a) it's expanded
and (b) when its value will change from a previous expansion. To
determine (b) we were checking the number of entries in the hash
table which used to work until we started undefining entries: now if
you undefine and redefine the same number of entries in between
expanding .VARIABLES, it doesn't detect any change. Instead, keep
an increasing change number.
* variables.c: Add variable_changenum.
(define_variable_in_set, merge_variable_sets): Increment
variable_changenum if adding a new variable to the global set.
(undefine_variable_in_set): Increment variable_changenum if
undefining a variable from the global set.
(lookup_special_var): Test variable_changenum not the hash table.
* tests/scripts/variables/special: Test undefining variables.
diff --git a/tests/scripts/variables/special b/tests/scripts/variables/special
index a5ab93a..2c0b42c 100644
--- a/tests/scripts/variables/special
+++ b/tests/scripts/variables/special
@@ -14,14 +14,22 @@
BAR := bar
-all:
- @echo X1 = $(X1)
- @echo X2 = $(X2)
- @echo LAST = $(sort $(filter FOO BAR,$(.VARIABLES)))
+all: ; @echo X1 = $(X1); echo X2 = $(X2); echo LAST = $(sort $(filter FOO BAR,$(.VARIABLES)))
',
'', "X1 =\nX2 = FOO\nLAST = BAR FOO\n");
+# SV 45728: Test that undefining a variable is reflected properly
+&run_make_test('
+FOO := foo
+BAR := bar
+$(info one: $(sort $(filter FOO BAR BAZ,$(.VARIABLES))))
+undefine BAR
+BAZ := baz
+$(info two: $(sort $(filter FOO BAR BAZ,$(.VARIABLES))))
+all:;@:
+',
+ '', "one: BAR FOO\ntwo: BAZ FOO\n");
# $makefile2 = &get_tmpfile;
# open(MAKEFILE, "> $makefile2");
@@ -31,9 +39,9 @@
# X1 := $(sort $(.TARGETS))
# all: foo
-# @echo X1 = $(X1)
-# @echo X2 = $(X2)
-# @echo LAST = $(sort $(.TARGETS))
+# @echo X1 = $(X1)
+# @echo X2 = $(X2)
+# @echo LAST = $(sort $(.TARGETS))
# X2 := $(sort $(.TARGETS))
@@ -56,16 +64,16 @@
: foo-one\
foo-two
: foo-three
- : foo-four
+ : foo-four
endef
orig: ; : orig-one
- : orig-two \
+ : orig-two \
orig-three \
- orig-four \
- orig-five \\\\
- : orig-six
- $(foo)
+ orig-four \
+ orig-five \\\\
+ : orig-six
+ $(foo)
.RECIPEPREFIX = >
test: ; : test-one
@@ -78,19 +86,19 @@
.RECIPEPREFIX =
reset: ; : reset-one
- : reset-two \
+ : reset-two \
reset-three \
- reset-four \
- reset-five \\\\
- : reset-six
- $(foo)
+ reset-four \
+ reset-five \\\\
+ : reset-six
+ $(foo)
',
'orig test reset',
': orig-one
: orig-two \
orig-three \
orig-four \
- orig-five \\\\
+ orig-five \\\\
: orig-six
: foo-one foo-two
: foo-three
@@ -99,7 +107,7 @@
: test-two \
test-three \
test-four \
- test-five \\\\
+ test-five \\\\
: test-six
: foo-one foo-two
: foo-three
@@ -108,7 +116,7 @@
: reset-two \
reset-three \
reset-four \
- reset-five \\\\
+ reset-five \\\\
: reset-six
: foo-one foo-two
: foo-three
diff --git a/variable.c b/variable.c
index 4b50503..35ba4f2 100644
--- a/variable.c
+++ b/variable.c
@@ -29,6 +29,9 @@
#endif
#include "hash.h"
+/* Incremented every time we add or remove a global variable. */
+static unsigned int variable_changenum;
+
/* Chain of all pattern-specific variables. */
static struct pattern_var *pattern_vars;
@@ -268,6 +271,9 @@
v->name = xstrndup (name, length);
v->length = length;
hash_insert_at (&set->table, v, var_slot);
+ if (set == &global_variable_set)
+ ++variable_changenum;
+
v->value = xstrdup (value);
if (flocp != 0)
v->fileinfo = *flocp;
@@ -350,12 +356,14 @@
before the switches were parsed, it wasn't affected by -e. */
v->origin = o_env_override;
- /* If the definition is from a stronger source than this one, don't
- undefine it. */
+ /* Undefine only if this undefinition is from an equal or stronger
+ source than the variable definition. */
if ((int) origin >= (int) v->origin)
{
hash_delete_at (&set->table, var_slot);
free_variable_name_and_value (v);
+ if (set == &global_variable_set)
+ ++variable_changenum;
}
}
}
@@ -373,7 +381,7 @@
static struct variable *
lookup_special_var (struct variable *var)
{
- static unsigned long last_var_count = 0;
+ static unsigned long last_changenum = 0;
/* This one actually turns out to be very hard, due to the way the parser
@@ -401,8 +409,7 @@
else
*/
- if (streq (var->name, ".VARIABLES")
- && global_variable_set.table.ht_fill != last_var_count)
+ if (variable_changenum != last_changenum && streq (var->name, ".VARIABLES"))
{
unsigned long max = EXPANSION_INCREMENT (strlen (var->value));
unsigned long len;
@@ -438,11 +445,8 @@
}
*(p-1) = '\0';
- /* Remember how many variables are in our current count. Since we never
- remove variables from the list, this is a reliable way to know whether
- the list is up to date or needs to be recomputed. */
-
- last_var_count = global_variable_set.table.ht_fill;
+ /* Remember the current variable change number. */
+ last_changenum = variable_changenum;
}
return var;
@@ -753,6 +757,8 @@
struct variable **from_var_slot = (struct variable **) from_set->table.ht_vec;
struct variable **from_var_end = from_var_slot + from_set->table.ht_size;
+ int inc = to_set == &global_variable_set ? 1 : 0;
+
for ( ; from_var_slot < from_var_end; from_var_slot++)
if (! HASH_VACANT (*from_var_slot))
{
@@ -760,7 +766,10 @@
struct variable **to_var_slot
= (struct variable **) hash_find_slot (&to_set->table, *from_var_slot);
if (HASH_VACANT (*to_var_slot))
- hash_insert_at (&to_set->table, from_var, to_var_slot);
+ {
+ hash_insert_at (&to_set->table, from_var, to_var_slot);
+ variable_changenum += inc;
+ }
else
{
/* GKM FIXME: delete in from_set->table */