Implement the new undefine directive.

This commit is contained in:
Boris Kolpackov 2009-10-06 06:56:57 +00:00
parent 174e910a1d
commit 4254e88cfa
9 changed files with 242 additions and 7 deletions

View File

@ -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> 2009-10-05 Boris Kolpackov <boris@codesynthesis.com>
* implicit.c (pattern_search): Initialize file variables only * implicit.c (pattern_search): Initialize file variables only

5
NEWS
View File

@ -47,6 +47,11 @@ Version 3.81.90
patterns are preferred. To detect this feature search for 'shortest-stem' patterns are preferred. To detect this feature search for 'shortest-stem'
in the .FEATURES special variable. 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 Version 3.81

View File

@ -4711,6 +4711,8 @@ they have particular specialized uses. @xref{Automatic Variables}.
the user has set it with a command argument. the user has set it with a command argument.
* Multi-Line:: An alternate way to set a variable * Multi-Line:: An alternate way to set a variable
to a multi-line string. 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. * Environment:: Variable values can come from the environment.
* Target-specific:: Variable values can be defined on a per-target * Target-specific:: Variable values can be defined on a per-target
basis. basis.
@ -5518,7 +5520,7 @@ See the next section for information about @code{define}.
@xref{Multi-Line, ,Defining Multi-Line Variables}. @xref{Multi-Line, ,Defining Multi-Line Variables}.
@end ifnottex @end ifnottex
@node Multi-Line, Environment, Override Directive, Using Variables @node Multi-Line, Undefine Directive, Override Directive, Using Variables
@section Defining Multi-Line Variables @section Defining Multi-Line Variables
@findex define @findex define
@findex endef @findex endef
@ -5599,7 +5601,42 @@ endef
@noindent @noindent
@xref{Override Directive, ,The @code{override} Directive}. @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 @section Variables from the Environment
@cindex variables, environment @cindex variables, environment
@ -10485,6 +10522,10 @@ Here is a summary of the directives GNU @code{make} recognizes:
Define multi-line variables.@* Define multi-line variables.@*
@xref{Multi-Line}. @xref{Multi-Line}.
@item undefine @var{variable}
Undefining variables.@*
@xref{Undefine Directive}.
@item ifdef @var{variable} @item ifdef @var{variable}
@itemx ifndef @var{variable} @itemx ifndef @var{variable}
@itemx ifeq (@var{a},@var{b}) @itemx ifeq (@var{a},@var{b})

2
main.c
View File

@ -1121,7 +1121,7 @@ main (int argc, char **argv, char **envp)
/* Set up .FEATURES */ /* Set up .FEATURES */
define_variable (".FEATURES", 9, define_variable (".FEATURES", 9,
"target-specific order-only second-expansion else-if" "target-specific order-only second-expansion else-if"
"shortest-stem", "shortest-stem undefine",
o_default, 0); o_default, 0);
#ifndef NO_ARCHIVES #ifndef NO_ARCHIVES
do_variable_definition (NILF, ".FEATURES", "archives", do_variable_definition (NILF, ".FEATURES", "archives",

48
read.c
View File

@ -62,6 +62,7 @@ struct vmodifiers
{ {
unsigned int assign_v:1; unsigned int assign_v:1;
unsigned int define_v:1; unsigned int define_v:1;
unsigned int undefine_v:1;
unsigned int export_v:1; unsigned int export_v:1;
unsigned int override_v:1; unsigned int override_v:1;
unsigned int private_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 int eval (struct ebuffer *buffer, int flags);
static long readline (struct ebuffer *ebuf); 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, static struct variable *do_define (char *name, enum variable_origin origin,
struct ebuffer *ebuf); struct ebuffer *ebuf);
static int conditional_line (char *line, int len, const struct floc *flocp); static int conditional_line (char *line, int len, const struct floc *flocp);
@ -464,12 +467,13 @@ eval_buffer (char *buffer)
return r; 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 It might use one of the modifiers "export", "override", "private", or it
might be one of the conditional tokens like "ifdef", "include", etc. 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 Returns a pointer to the first non-modifier character, and sets VMOD
based on the modifiers found if any, plus V_ASSIGN is 1. 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); p = next_token (p2);
break; break;
} }
else if (word1eq ("undefine"))
{
/* We can't have modifiers after 'undefine' */
vmod->undefine_v = 1;
p = next_token (p2);
break;
}
else else
/* Not a variable or modifier: this is not a variable assignment. */ /* Not a variable or modifier: this is not a variable assignment. */
return (char *)line; return (char *)line;
@ -525,7 +536,7 @@ parse_var_assignment (const char *line, struct vmodifiers *vmod)
return (char *)line; return (char *)line;
} }
/* Found a variable assignment. */ /* Found a variable assignment or undefine. */
vmod->assign_v = 1; vmod->assign_v = 1;
return (char *)p; return (char *)p;
} }
@ -702,7 +713,14 @@ eval (struct ebuffer *ebuf, int set_default)
continue; 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); v = do_define (p, origin, ebuf);
else else
v = try_variable_definition (fstart, p, origin, 0); v = try_variable_definition (fstart, p, origin, 0);
@ -1303,6 +1321,28 @@ remove_comments (char *line)
*comment = '\0'; *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. /* Execute a `define' directive.
The first line has already been read, and NAME is the name of 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. */ the variable to be defined. The following lines remain to be read. */

View File

@ -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> 2009-10-03 Paul Smith <psmith@gnu.org>
* scripts/features/parallelism: Test for open Savannah bug #26846. * scripts/features/parallelism: Test for open Savannah bug #26846.

View File

@ -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;

View File

@ -276,6 +276,51 @@ define_variable_in_set (const char *name, unsigned int length,
return v; 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. /* If the variable passed in is "special", handle its special nature.
Currently there are two such variables, both used for introspection: Currently there are two such variables, both used for introspection:
.VARIABLES expands to a list of all the variables defined in this instance .VARIABLES expands to a list of all the variables defined in this instance

View File

@ -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 define_variable_for_file(n,l,v,o,r,f) \
define_variable_in_set((n),(l),(v),(o),(r),(f)->variables->set,NILF) 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. */ /* Warn that NAME is an undefined variable. */
#define warn_undefined(n,l) do{\ #define warn_undefined(n,l) do{\