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>
* 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'
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

View File

@ -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})

2
main.c
View File

@ -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",

48
read.c
View File

@ -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. */

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>
* 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;
}
/* 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

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_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{\