From 4254e88cfa7704ea4a55d94a9aee5b19b081b3db Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Tue, 6 Oct 2009 06:56:57 +0000 Subject: Implement the new undefine directive. --- ChangeLog | 18 ++++++++++ NEWS | 5 +++ doc/make.texi | 45 +++++++++++++++++++++++-- main.c | 2 +- read.c | 48 +++++++++++++++++++++++--- tests/ChangeLog | 4 +++ tests/scripts/variables/undefine | 73 ++++++++++++++++++++++++++++++++++++++++ variable.c | 45 +++++++++++++++++++++++++ variable.h | 9 +++++ 9 files changed, 242 insertions(+), 7 deletions(-) create mode 100644 tests/scripts/variables/undefine diff --git a/ChangeLog b/ChangeLog index 83e687f..f68099f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,21 @@ +2009-10-06 Boris Kolpackov + + * 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 * 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 + + * scripts/variables/undefine: Tests for the new undefine feature. + 2009-10-03 Paul Smith * 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{\ -- cgit v1.2.3