mirror of
https://github.com/mirror/make.git
synced 2025-01-26 20:30:36 +08:00
Implement the new undefine directive.
This commit is contained in:
parent
174e910a1d
commit
4254e88cfa
18
ChangeLog
18
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
|
||||
|
5
NEWS
5
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
|
||||
|
||||
|
@ -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
2
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",
|
||||
|
48
read.c
48
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. */
|
||||
|
@ -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.
|
||||
|
73
tests/scripts/variables/undefine
Normal file
73
tests/scripts/variables/undefine
Normal 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;
|
45
variable.c
45
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
|
||||
|
@ -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{\
|
||||
|
Loading…
Reference in New Issue
Block a user