summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@kolpackov.net>2009-10-06 06:56:57 +0000
committerBoris Kolpackov <boris@kolpackov.net>2009-10-06 06:56:57 +0000
commit4254e88cfa7704ea4a55d94a9aee5b19b081b3db (patch)
tree3810a48808d8d16647c39ccc47fd6af7a810b5c5
parent174e910a1d73f6b12338b6bd0a0727085b041f22 (diff)
downloadgunmake-4254e88cfa7704ea4a55d94a9aee5b19b081b3db.tar.gz
Implement the new undefine directive.
-rw-r--r--ChangeLog18
-rw-r--r--NEWS5
-rw-r--r--doc/make.texi45
-rw-r--r--main.c2
-rw-r--r--read.c48
-rw-r--r--tests/ChangeLog4
-rw-r--r--tests/scripts/variables/undefine73
-rw-r--r--variable.c45
-rw-r--r--variable.h9
9 files changed, 242 insertions, 7 deletions
diff --git a/ChangeLog b/ChangeLog
index 83e687f..f68099f 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,21 @@
+2009-10-06 Boris Kolpackov <boris@codesynthesis.com>
+
+ * variable.h (undefine_variable_in_set): New function declaration.
+ (undefine_variable_global): New macro.
+
+ * variable.c (undefine_variable_in_set): New function implementation.
+
+ * read.c (vmodifiers): Add undefine_v modifier.
+ (parse_var_assignment): Parse undefine.
+ (do_undefine): Handle the undefine directive.
+ (eval): Call do_undefine if undefine_v is set.
+
+ * main.c (.FEATURES): Add a keyword to indicate the new feature.
+
+ * doc/make.texi (Undefine Directive): Describe the new directive.
+
+ * NEWS: Add a note about the new directive.
+
2009-10-05 Boris Kolpackov <boris@codesynthesis.com>
* implicit.c (pattern_search): Initialize file variables only
diff --git a/NEWS b/NEWS
index fd0a254..c89ea46 100644
--- a/NEWS
+++ b/NEWS
@@ -47,6 +47,11 @@ Version 3.81.90
patterns are preferred. To detect this feature search for 'shortest-stem'
in the .FEATURES special variable.
+* New make directive: 'undefine' allows you to undefine a variable so
+ that it appears as if it was never set. Both $(flavor) and $(origin)
+ functions will return 'undefined' for such a variable. To detect this
+ feature search for 'undefine in the .FEATURES special variable.
+
Version 3.81
diff --git a/doc/make.texi b/doc/make.texi
index 8f86c0c..52a56ef 100644
--- a/doc/make.texi
+++ b/doc/make.texi
@@ -4711,6 +4711,8 @@ they have particular specialized uses. @xref{Automatic Variables}.
the user has set it with a command argument.
* Multi-Line:: An alternate way to set a variable
to a multi-line string.
+* Undefine Directive:: How to undefine a variable so that it appears
+ as if it was never set.
* Environment:: Variable values can come from the environment.
* Target-specific:: Variable values can be defined on a per-target
basis.
@@ -5518,7 +5520,7 @@ See the next section for information about @code{define}.
@xref{Multi-Line, ,Defining Multi-Line Variables}.
@end ifnottex
-@node Multi-Line, Environment, Override Directive, Using Variables
+@node Multi-Line, Undefine Directive, Override Directive, Using Variables
@section Defining Multi-Line Variables
@findex define
@findex endef
@@ -5599,7 +5601,42 @@ endef
@noindent
@xref{Override Directive, ,The @code{override} Directive}.
-@node Environment, Target-specific, Multi-Line, Using Variables
+@node Undefine Directive, Environment, Multi-Line, Using Variables
+@section Undefining Variables
+@findex undefine
+@cindex undefining variable
+
+If you want to clear a variable, setting its value to empty is usually
+sufficient. Expanding such a variable will yield the same result (empty
+string) regardless of whether it was set or not. However, if you are
+using the @code{flavor} (@pxref{Flavor Function}) and
+@code{origin} (@pxref{Origin Function}) functions, there is a difference
+between a variable that was never set and a variable with an empty value.
+In such situations you may want to use the @code{undefine} directive to
+make a variable appear as if it was never set. For example:
+
+@example
+foo := foo
+bar = bar
+
+undefine foo
+undefine bar
+
+$(info $(origin foo))
+$(info $(flavor bar))
+@end example
+
+This example will print ``undefined'' for both variables.
+
+If you want to undefine a command-line variable definition, you can use
+the @code{override} directive together with @code{undefine}, similar to
+how this is done for variable definitions:
+
+@example
+override undefine CFLAGS
+@end example
+
+@node Environment, Target-specific, Undefine Directive, Using Variables
@section Variables from the Environment
@cindex variables, environment
@@ -10485,6 +10522,10 @@ Here is a summary of the directives GNU @code{make} recognizes:
Define multi-line variables.@*
@xref{Multi-Line}.
+@item undefine @var{variable}
+Undefining variables.@*
+@xref{Undefine Directive}.
+
@item ifdef @var{variable}
@itemx ifndef @var{variable}
@itemx ifeq (@var{a},@var{b})
diff --git a/main.c b/main.c
index b79888c..4d84b66 100644
--- a/main.c
+++ b/main.c
@@ -1121,7 +1121,7 @@ main (int argc, char **argv, char **envp)
/* Set up .FEATURES */
define_variable (".FEATURES", 9,
"target-specific order-only second-expansion else-if"
- "shortest-stem",
+ "shortest-stem undefine",
o_default, 0);
#ifndef NO_ARCHIVES
do_variable_definition (NILF, ".FEATURES", "archives",
diff --git a/read.c b/read.c
index 7dd5090..a86f791 100644
--- a/read.c
+++ b/read.c
@@ -62,6 +62,7 @@ struct vmodifiers
{
unsigned int assign_v:1;
unsigned int define_v:1;
+ unsigned int undefine_v:1;
unsigned int export_v:1;
unsigned int override_v:1;
unsigned int private_v:1;
@@ -136,6 +137,8 @@ static int eval_makefile (const char *filename, int flags);
static int eval (struct ebuffer *buffer, int flags);
static long readline (struct ebuffer *ebuf);
+static void do_undefine (char *name, enum variable_origin origin,
+ struct ebuffer *ebuf);
static struct variable *do_define (char *name, enum variable_origin origin,
struct ebuffer *ebuf);
static int conditional_line (char *line, int len, const struct floc *flocp);
@@ -464,12 +467,13 @@ eval_buffer (char *buffer)
return r;
}
-/* Check LINE to see if it's a variable assignment.
+/* Check LINE to see if it's a variable assignment or undefine.
It might use one of the modifiers "export", "override", "private", or it
might be one of the conditional tokens like "ifdef", "include", etc.
- If it's not a variable assignment, VMOD.V_ASSIGN is 0. Returns LINE.
+ If it's not a variable assignment or undefine, VMOD.V_ASSIGN is 0.
+ Returns LINE.
Returns a pointer to the first non-modifier character, and sets VMOD
based on the modifiers found if any, plus V_ASSIGN is 1.
@@ -515,6 +519,13 @@ parse_var_assignment (const char *line, struct vmodifiers *vmod)
p = next_token (p2);
break;
}
+ else if (word1eq ("undefine"))
+ {
+ /* We can't have modifiers after 'undefine' */
+ vmod->undefine_v = 1;
+ p = next_token (p2);
+ break;
+ }
else
/* Not a variable or modifier: this is not a variable assignment. */
return (char *)line;
@@ -525,7 +536,7 @@ parse_var_assignment (const char *line, struct vmodifiers *vmod)
return (char *)line;
}
- /* Found a variable assignment. */
+ /* Found a variable assignment or undefine. */
vmod->assign_v = 1;
return (char *)p;
}
@@ -702,7 +713,14 @@ eval (struct ebuffer *ebuf, int set_default)
continue;
}
- if (vmod.define_v)
+ if (vmod.undefine_v)
+ {
+ do_undefine (p, origin, ebuf);
+
+ /* This line has been dealt with. */
+ goto rule_complete;
+ }
+ else if (vmod.define_v)
v = do_define (p, origin, ebuf);
else
v = try_variable_definition (fstart, p, origin, 0);
@@ -1303,6 +1321,28 @@ remove_comments (char *line)
*comment = '\0';
}
+/* Execute a `undefine' directive.
+ The undefine line has already been read, and NAME is the name of
+ the variable to be undefined. */
+
+static void
+do_undefine (char *name, enum variable_origin origin, struct ebuffer *ebuf)
+{
+ char *p, *var;
+
+ /* Expand the variable name and find the beginning (NAME) and end. */
+ var = allocated_variable_expand (name);
+ name = next_token (var);
+ if (*name == '\0')
+ fatal (&ebuf->floc, _("empty variable name"));
+ p = name + strlen (name) - 1;
+ while (p > name && isblank ((unsigned char)*p))
+ --p;
+ p[1] = '\0';
+
+ undefine_variable_global (name, p - name + 1, origin);
+}
+
/* Execute a `define' directive.
The first line has already been read, and NAME is the name of
the variable to be defined. The following lines remain to be read. */
diff --git a/tests/ChangeLog b/tests/ChangeLog
index b9c3d47..939a5f5 100644
--- a/tests/ChangeLog
+++ b/tests/ChangeLog
@@ -1,3 +1,7 @@
+2009-10-06 Boris Kolpackov <boris@codesynthesis.com>
+
+ * scripts/variables/undefine: Tests for the new undefine feature.
+
2009-10-03 Paul Smith <psmith@gnu.org>
* scripts/features/parallelism: Test for open Savannah bug #26846.
diff --git a/tests/scripts/variables/undefine b/tests/scripts/variables/undefine
new file mode 100644
index 0000000..38707b8
--- /dev/null
+++ b/tests/scripts/variables/undefine
@@ -0,0 +1,73 @@
+# -*-perl-*-
+
+$description = "Test variable undefine.";
+
+$details = "";
+
+# TEST 0: basic undefine functionality
+
+run_make_test('
+a = a
+b := b
+define c
+c
+endef
+
+$(info $(flavor a) $(flavor b) $(flavor c))
+
+n := b
+
+undefine a
+undefine $n
+undefine c
+
+$(info $(flavor a) $(flavor b) $(flavor c))
+
+
+all: ;@:
+',
+'', "recursive simple recursive\nundefined undefined undefined");
+
+
+# TEST 1: override
+
+run_make_test('
+undefine a
+override undefine b
+
+$(info $(flavor a) $(flavor b))
+
+
+all: ;@:
+',
+'a=a b=b', "recursive undefined");
+
+1;
+
+# TEST 2: undefine in eval (make sure we undefine from the global var set)
+
+run_make_test('
+define undef
+$(eval undefine $$1)
+endef
+
+a := a
+$(call undef,a)
+$(info $(flavor a))
+
+
+all: ;@:
+',
+'', "undefined");
+
+
+# TEST 3: Missing variable name
+
+run_make_test('
+a =
+undefine $a
+all: ;@echo ouch
+',
+'', "#MAKEFILE#:3: *** empty variable name. Stop.\n", 512);
+
+1;
diff --git a/variable.c b/variable.c
index 6a74dcd..7cee8bd 100644
--- a/variable.c
+++ b/variable.c
@@ -276,6 +276,51 @@ define_variable_in_set (const char *name, unsigned int length,
return v;
}
+
+/* Undefine variable named NAME in SET. LENGTH is the length of NAME, which
+ does not need to be null-terminated. ORIGIN specifies the origin of the
+ variable (makefile, command line or environment). */
+
+static void
+free_variable_name_and_value (const void *item);
+
+void
+undefine_variable_in_set (const char *name, unsigned int length,
+ enum variable_origin origin,
+ struct variable_set *set)
+{
+ struct variable *v;
+ struct variable **var_slot;
+ struct variable var_key;
+
+ if (set == NULL)
+ set = &global_variable_set;
+
+ var_key.name = (char *) name;
+ var_key.length = length;
+ var_slot = (struct variable **) hash_find_slot (&set->table, &var_key);
+
+ if (env_overrides && origin == o_env)
+ origin = o_env_override;
+
+ v = *var_slot;
+ if (! HASH_VACANT (v))
+ {
+ if (env_overrides && v->origin == o_env)
+ /* V came from in the environment. Since it was defined
+ 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. */
+ if ((int) origin >= (int) v->origin)
+ {
+ hash_delete_at (&set->table, var_slot);
+ free_variable_name_and_value (v);
+ }
+ }
+}
+
/* If the variable passed in is "special", handle its special nature.
Currently there are two such variables, both used for introspection:
.VARIABLES expands to a list of all the variables defined in this instance
diff --git a/variable.h b/variable.h
index 5275911..84ae55e 100644
--- a/variable.h
+++ b/variable.h
@@ -196,6 +196,15 @@ struct variable *define_variable_in_set (const char *name, unsigned int length,
#define define_variable_for_file(n,l,v,o,r,f) \
define_variable_in_set((n),(l),(v),(o),(r),(f)->variables->set,NILF)
+void undefine_variable_in_set (const char *name, unsigned int length,
+ enum variable_origin origin,
+ struct variable_set *set);
+
+/* Remove variable from the current variable set. */
+
+#define undefine_variable_global(n,l,o) \
+ undefine_variable_in_set((n),(l),(o),NULL)
+
/* Warn that NAME is an undefined variable. */
#define warn_undefined(n,l) do{\