mirror of
https://github.com/mirror/make.git
synced 2025-01-27 21:00:22 +08:00
Add 'private' variable modifier, feature submitted by Ramon Garcia.
Rework the parser for variables to allow multiple modifiers and also allow for variables and targets with modifier names, like "export" and "private".
This commit is contained in:
parent
7b16a8e3ca
commit
5b4d419476
30
ChangeLog
30
ChangeLog
@ -1,3 +1,18 @@
|
|||||||
|
2009-05-25 Paul Smith <psmith@gnu.org>
|
||||||
|
|
||||||
|
Reworked the parser for variable assignments to allow multiple
|
||||||
|
modifiers, and in any order. Also allows variable and
|
||||||
|
prerequisites to be modifier names ('export', 'private', etc.)
|
||||||
|
|
||||||
|
* NEWS: Add notes about user-visible changes.
|
||||||
|
|
||||||
|
* read.c (struct vmodifiers): Remember what modifiers were seen.
|
||||||
|
(parse_var_assignment): New function to parse variable assignments.
|
||||||
|
(eval): Call the new function. Handle variable assignments earlier.
|
||||||
|
|
||||||
|
* variable.c (parse_variable_definition): Only parse; don't create var.
|
||||||
|
(assign_variable_definition): Call parse, then create the var.
|
||||||
|
|
||||||
2009-05-24 Paul Smith <psmith@gnu.org>
|
2009-05-24 Paul Smith <psmith@gnu.org>
|
||||||
|
|
||||||
* doc/make.texi: Fix the ISBN for the GNU make manual. Incorrect
|
* doc/make.texi: Fix the ISBN for the GNU make manual. Incorrect
|
||||||
@ -23,6 +38,21 @@
|
|||||||
* function.c (func_shell): Don't close pipedes[1] if it is -1.
|
* function.c (func_shell): Don't close pipedes[1] if it is -1.
|
||||||
Fixes Savannah bug #20495.
|
Fixes Savannah bug #20495.
|
||||||
|
|
||||||
|
2009-02-23 Ramon Garcia <ramon.garcia.f@gmail.com>
|
||||||
|
|
||||||
|
Introduce a new keyword "private" which applies to target-specific
|
||||||
|
variables and prevents their values from being inherited.
|
||||||
|
|
||||||
|
* variable.h (struct variable): Add private_var flag to each variable.
|
||||||
|
Add a flag to specify which list entry switches to the parent target.
|
||||||
|
* variable.c (define_variable_in_set): Initialize private_var flag.
|
||||||
|
(lookup_variable): Skip private variables in parent contexts.
|
||||||
|
(initialize_file_variables): Set next_is_parent appropriately.
|
||||||
|
(print_variable): Show the private_var flag.
|
||||||
|
* read.c (eval): Recognize the private keyword.
|
||||||
|
(record_target_var): Set private_var.
|
||||||
|
* doc/make.texi (Suppressing Inheritance): Add documentation.
|
||||||
|
|
||||||
2008-10-26 Paul Smith <psmith@gnu.org>
|
2008-10-26 Paul Smith <psmith@gnu.org>
|
||||||
|
|
||||||
* configure.in: Check for strndup().
|
* configure.in: Check for strndup().
|
||||||
|
30
NEWS
30
NEWS
@ -1,6 +1,6 @@
|
|||||||
GNU make NEWS -*-indented-text-*-
|
GNU make NEWS -*-indented-text-*-
|
||||||
History of user-visible changes.
|
History of user-visible changes.
|
||||||
1 April 2006
|
25 May 2009
|
||||||
|
|
||||||
See the end of this file for copyrights and conditions.
|
See the end of this file for copyrights and conditions.
|
||||||
|
|
||||||
@ -14,12 +14,30 @@ Version 3.81.90
|
|||||||
* Compiling GNU make now requires a conforming ISO C 1989 compiler and
|
* Compiling GNU make now requires a conforming ISO C 1989 compiler and
|
||||||
standard runtime library.
|
standard runtime library.
|
||||||
|
|
||||||
|
* The parser for variable assignments has been enhanced to allow multiple
|
||||||
|
modifiers ('export', 'override', 'private' (see below)) on the same line as
|
||||||
|
variables, including define/endef variables, and in any order. Also, it is
|
||||||
|
possible to create variables and targets named as these modifiers.
|
||||||
|
|
||||||
|
* WARNING: Backward-incompatibility!
|
||||||
|
As a result of the parser changes, two backward-compatibility issues exist:
|
||||||
|
first, a prerequisite containing an "=" cannot be escaped with a backslash
|
||||||
|
any longer. You must create a variable containing an "=" and use that
|
||||||
|
variable in the prerequisite. Second, variable names can no longer contain
|
||||||
|
whitespace, unless you put the whitespace in a variable and use the
|
||||||
|
variable.
|
||||||
|
|
||||||
* New special variable: .RECIPEPREFIX allows you to reset the recipe
|
* New special variable: .RECIPEPREFIX allows you to reset the recipe
|
||||||
introduction character from the default (TAB) to something else. The
|
introduction character from the default (TAB) to something else. The first
|
||||||
first character of this variable value is the new recipe introduction
|
character of this variable value is the new recipe introduction character.
|
||||||
character. If the variable is set to the empty string, TAB is used
|
If the variable is set to the empty string, TAB is used again. It can be
|
||||||
again. It can be set and reset at will; rules will be parsed
|
set and reset at will; recipes will use the value active when they were
|
||||||
according to the current value.
|
first parsed.
|
||||||
|
|
||||||
|
* New variable modifier 'private': prefixing a variable assignment with the
|
||||||
|
modifier 'private' suppresses inheritance of that variable by
|
||||||
|
prerequisites. This is most useful for target- and pattern-specific
|
||||||
|
variables.
|
||||||
|
|
||||||
|
|
||||||
Version 3.81
|
Version 3.81
|
||||||
|
@ -240,6 +240,7 @@ How to Use Variables
|
|||||||
basis.
|
basis.
|
||||||
* Pattern-specific:: Target-specific variable values can be applied
|
* Pattern-specific:: Target-specific variable values can be applied
|
||||||
to a group of targets that match a pattern.
|
to a group of targets that match a pattern.
|
||||||
|
* Suppressing Inheritance:: Suppress inheritance of variables.
|
||||||
* Special Variables:: Variables with special meaning or behavior.
|
* Special Variables:: Variables with special meaning or behavior.
|
||||||
|
|
||||||
Advanced Features for Reference to Variables
|
Advanced Features for Reference to Variables
|
||||||
@ -4691,6 +4692,7 @@ they have particular specialized uses. @xref{Automatic Variables}.
|
|||||||
basis.
|
basis.
|
||||||
* Pattern-specific:: Target-specific variable values can be applied
|
* Pattern-specific:: Target-specific variable values can be applied
|
||||||
to a group of targets that match a pattern.
|
to a group of targets that match a pattern.
|
||||||
|
* Suppressing Inheritance:: Suppress inheritance of variables.
|
||||||
* Special Variables:: Variables with special meaning or behavior.
|
* Special Variables:: Variables with special meaning or behavior.
|
||||||
@end menu
|
@end menu
|
||||||
|
|
||||||
@ -5625,19 +5627,9 @@ Set a target-specific variable value like this:
|
|||||||
@var{target} @dots{} : @var{variable-assignment}
|
@var{target} @dots{} : @var{variable-assignment}
|
||||||
@end example
|
@end example
|
||||||
|
|
||||||
@noindent
|
Target-specific variable assignments can be prefixed with any or all of the
|
||||||
or like this:
|
special keywords @code{export}, @code{override}, or @code{private};
|
||||||
|
these apply their normal behavior to this instance of the variable only.
|
||||||
@example
|
|
||||||
@var{target} @dots{} : override @var{variable-assignment}
|
|
||||||
@end example
|
|
||||||
|
|
||||||
@noindent
|
|
||||||
or like this:
|
|
||||||
|
|
||||||
@example
|
|
||||||
@var{target} @dots{} : export @var{variable-assignment}
|
|
||||||
@end example
|
|
||||||
|
|
||||||
Multiple @var{target} values create a target-specific variable value for
|
Multiple @var{target} values create a target-specific variable value for
|
||||||
each member of the target list individually.
|
each member of the target list individually.
|
||||||
@ -5683,7 +5675,7 @@ will cause that prerequisite to be built and the prerequisite will
|
|||||||
inherit the target-specific value from the first target. It will
|
inherit the target-specific value from the first target. It will
|
||||||
ignore the target-specific values from any other targets.
|
ignore the target-specific values from any other targets.
|
||||||
|
|
||||||
@node Pattern-specific, Special Variables, Target-specific, Using Variables
|
@node Pattern-specific, Suppressing Inheritance, Target-specific, Using Variables
|
||||||
@section Pattern-specific Variable Values
|
@section Pattern-specific Variable Values
|
||||||
@cindex pattern-specific variables
|
@cindex pattern-specific variables
|
||||||
@cindex variables, pattern-specific
|
@cindex variables, pattern-specific
|
||||||
@ -5704,15 +5696,6 @@ Set a pattern-specific variable value like this:
|
|||||||
@example
|
@example
|
||||||
@var{pattern} @dots{} : @var{variable-assignment}
|
@var{pattern} @dots{} : @var{variable-assignment}
|
||||||
@end example
|
@end example
|
||||||
|
|
||||||
@noindent
|
|
||||||
or like this:
|
|
||||||
|
|
||||||
@example
|
|
||||||
@var{pattern} @dots{} : override @var{variable-assignment}
|
|
||||||
@end example
|
|
||||||
|
|
||||||
@noindent
|
|
||||||
where @var{pattern} is a %-pattern. As with target-specific variable
|
where @var{pattern} is a %-pattern. As with target-specific variable
|
||||||
values, multiple @var{pattern} values create a pattern-specific variable
|
values, multiple @var{pattern} values create a pattern-specific variable
|
||||||
value for each pattern individually. The @var{variable-assignment} can
|
value for each pattern individually. The @var{variable-assignment} can
|
||||||
@ -5729,7 +5712,43 @@ For example:
|
|||||||
will assign @code{CFLAGS} the value of @samp{-O} for all targets
|
will assign @code{CFLAGS} the value of @samp{-O} for all targets
|
||||||
matching the pattern @code{%.o}.
|
matching the pattern @code{%.o}.
|
||||||
|
|
||||||
@node Special Variables, , Pattern-specific, Using Variables
|
@node Suppressing Inheritance, Special Variables, Pattern-specific, Using Variables
|
||||||
|
@section Suppressing Inheritance
|
||||||
|
@findex private
|
||||||
|
@cindex suppressing inheritance
|
||||||
|
@cindex inheritance, suppressing
|
||||||
|
|
||||||
|
As described in previous sections, @code{make} variables are inherited
|
||||||
|
by prerequisites. This capability allows you to modify the behavior
|
||||||
|
of a prerequisite based on which targets caused it to be rebuilt. For
|
||||||
|
example, you might set a target-specific variable on a @code{debug}
|
||||||
|
target, then running @samp{make debug} will cause that variable to be
|
||||||
|
inherited by all prerequisites of @code{debug}, while just running
|
||||||
|
@samp{make all} (for example) would not have that assignment.
|
||||||
|
|
||||||
|
Sometimes, however, you may not want a variable to be inherited. For
|
||||||
|
these situations, @code{make} provides the @code{private} modifier.
|
||||||
|
Although this modifier can be used with any variable assignment, it
|
||||||
|
makes the most sense with target- and pattern-specific variables. Any
|
||||||
|
variable marked @code{private} will be visible to its local target but
|
||||||
|
will not be inherited by prerequisites of that target. A global
|
||||||
|
variable marked @code{private} will be visible in the global scope but
|
||||||
|
will not be inherited by any target, and hence will not be visible
|
||||||
|
in any recipe.
|
||||||
|
|
||||||
|
As an example, consider this makefile:
|
||||||
|
@example
|
||||||
|
EXTRA_CFLAGS =
|
||||||
|
|
||||||
|
prog: private EXTRA_CFLAGS = -L/usr/local/lib
|
||||||
|
prog: a.o b.o
|
||||||
|
@end example
|
||||||
|
|
||||||
|
Due to the @code{private} modifier, @code{a.o} and @code{b.o} will not
|
||||||
|
inherit the @code{EXTRA_CFLAGS} variable assignment from the
|
||||||
|
@code{progs} target.
|
||||||
|
|
||||||
|
@node Special Variables, , Suppressing Inheritance, Using Variables
|
||||||
@comment node-name, next, previous, up
|
@comment node-name, next, previous, up
|
||||||
@section Other Special Variables
|
@section Other Special Variables
|
||||||
@cindex makefiles, and special variables
|
@cindex makefiles, and special variables
|
||||||
|
386
read.c
386
read.c
@ -56,6 +56,17 @@ struct ebuffer
|
|||||||
struct floc floc; /* Info on the file in fp (if any). */
|
struct floc floc; /* Info on the file in fp (if any). */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Track the modifiers we can have on variable assignments */
|
||||||
|
|
||||||
|
struct vmodifiers
|
||||||
|
{
|
||||||
|
unsigned int assign_v:1;
|
||||||
|
unsigned int define_v:1;
|
||||||
|
unsigned int export_v:1;
|
||||||
|
unsigned int override_v:1;
|
||||||
|
unsigned int private_v:1;
|
||||||
|
};
|
||||||
|
|
||||||
/* Types of "words" that can be read in a makefile. */
|
/* Types of "words" that can be read in a makefile. */
|
||||||
enum make_word_type
|
enum make_word_type
|
||||||
{
|
{
|
||||||
@ -125,8 +136,9 @@ 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_define (char *name, unsigned int namelen,
|
static struct variable *do_define (char *name, unsigned int namelen,
|
||||||
enum variable_origin origin, struct ebuffer *ebuf);
|
enum variable_origin origin,
|
||||||
|
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);
|
||||||
static void record_files (struct nameseq *filenames, const char *pattern,
|
static void record_files (struct nameseq *filenames, const char *pattern,
|
||||||
const char *pattern_percent, struct dep *deps,
|
const char *pattern_percent, struct dep *deps,
|
||||||
@ -134,13 +146,21 @@ static void record_files (struct nameseq *filenames, const char *pattern,
|
|||||||
unsigned int commands_idx, int two_colon,
|
unsigned int commands_idx, int two_colon,
|
||||||
const struct floc *flocp);
|
const struct floc *flocp);
|
||||||
static void record_target_var (struct nameseq *filenames, char *defn,
|
static void record_target_var (struct nameseq *filenames, char *defn,
|
||||||
enum variable_origin origin, int enabled,
|
enum variable_origin origin,
|
||||||
|
struct vmodifiers *vmod,
|
||||||
const struct floc *flocp);
|
const struct floc *flocp);
|
||||||
static enum make_word_type get_next_mword (char *buffer, char *delim,
|
static enum make_word_type get_next_mword (char *buffer, char *delim,
|
||||||
char **startp, unsigned int *length);
|
char **startp, unsigned int *length);
|
||||||
static void remove_comments (char *line);
|
static void remove_comments (char *line);
|
||||||
static char *find_char_unquote (char *string, int stop1, int stop2,
|
static char *find_char_unquote (char *string, int stop1, int stop2,
|
||||||
int blank, int ignorevars);
|
int blank, int ignorevars);
|
||||||
|
|
||||||
|
|
||||||
|
/* Compare a word, both length and contents.
|
||||||
|
P must point to the word to be tested, and WLEN must be the length.
|
||||||
|
*/
|
||||||
|
#define word1eq(s) (wlen == sizeof(s)-1 && strneq (s, p, sizeof(s)-1))
|
||||||
|
|
||||||
|
|
||||||
/* Read in all the makefiles and return the chain of their names. */
|
/* Read in all the makefiles and return the chain of their names. */
|
||||||
|
|
||||||
@ -434,8 +454,75 @@ eval_buffer (char *buffer)
|
|||||||
alloca (0);
|
alloca (0);
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Check LINE to see if it's a variable assignment.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
Returns a pointer to the first non-modifier character, and sets VMOD
|
||||||
|
based on the modifiers found if any, plus V_ASSIGN is 1.
|
||||||
|
*/
|
||||||
|
char *
|
||||||
|
parse_var_assignment (const char *line, struct vmodifiers *vmod)
|
||||||
|
{
|
||||||
|
const char *p;
|
||||||
|
memset (vmod, '\0', sizeof (*vmod));
|
||||||
|
|
||||||
|
/* Find the start of the next token. If there isn't one we're done. */
|
||||||
|
line = next_token (line);
|
||||||
|
if (*line == '\0')
|
||||||
|
return (char *)line;
|
||||||
|
|
||||||
|
p = line;
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
int wlen;
|
||||||
|
const char *p2;
|
||||||
|
enum variable_flavor flavor;
|
||||||
|
|
||||||
|
p2 = parse_variable_definition (p, &flavor);
|
||||||
|
|
||||||
|
/* If this is a variable assignment, we're done. */
|
||||||
|
if (p2)
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* It's not a variable; see if it's a modifier. */
|
||||||
|
p2 = end_of_token (p);
|
||||||
|
wlen = p2 - p;
|
||||||
|
|
||||||
|
if (word1eq ("export"))
|
||||||
|
vmod->export_v = 1;
|
||||||
|
else if (word1eq ("override"))
|
||||||
|
vmod->override_v = 1;
|
||||||
|
else if (word1eq ("private"))
|
||||||
|
vmod->private_v = 1;
|
||||||
|
else if (word1eq ("define"))
|
||||||
|
{
|
||||||
|
/* We can't have modifiers after 'define' */
|
||||||
|
vmod->define_v = 1;
|
||||||
|
p = next_token (p2);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
/* Not a variable or modifier: this is not a variable assignment. */
|
||||||
|
return (char *)line;
|
||||||
|
|
||||||
|
/* It was a modifier. Try the next word. */
|
||||||
|
p = next_token (p2);
|
||||||
|
if (*p == '\0')
|
||||||
|
return (char *)line;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Found a variable assignment. */
|
||||||
|
vmod->assign_v = 1;
|
||||||
|
return (char *)p;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Read file FILENAME as a makefile and add its contents to the data base.
|
/* Read file FILENAME as a makefile and add its contents to the data base.
|
||||||
|
|
||||||
SET_DEFAULT is true if we are allowed to set the default goal. */
|
SET_DEFAULT is true if we are allowed to set the default goal. */
|
||||||
@ -502,7 +589,9 @@ eval (struct ebuffer *ebuf, int set_default)
|
|||||||
unsigned int wlen;
|
unsigned int wlen;
|
||||||
char *p;
|
char *p;
|
||||||
char *p2;
|
char *p2;
|
||||||
|
struct vmodifiers vmod;
|
||||||
|
|
||||||
|
/* At the top of this loop, we are starting a brand new line. */
|
||||||
/* Grab the next line to be evaluated */
|
/* Grab the next line to be evaluated */
|
||||||
ebuf->floc.lineno += nlines;
|
ebuf->floc.lineno += nlines;
|
||||||
nlines = readline (ebuf);
|
nlines = readline (ebuf);
|
||||||
@ -527,7 +616,7 @@ eval (struct ebuffer *ebuf, int set_default)
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
/* If there is no preceding rule line, don't treat this line
|
/* If there is no preceding rule line, don't treat this line
|
||||||
as a command, even though it begins with a tab character.
|
as a command, even though it begins with a recipe prefix.
|
||||||
SunOS 4 make appears to behave this way. */
|
SunOS 4 make appears to behave this way. */
|
||||||
|
|
||||||
if (filenames != 0)
|
if (filenames != 0)
|
||||||
@ -566,7 +655,7 @@ eval (struct ebuffer *ebuf, int set_default)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This line is not a shell command line. Don't worry about tabs.
|
/* This line is not a shell command line. Don't worry about whitespace.
|
||||||
Get more space if we need it; we don't need to preserve the current
|
Get more space if we need it; we don't need to preserve the current
|
||||||
contents of the buffer. */
|
contents of the buffer. */
|
||||||
|
|
||||||
@ -575,6 +664,7 @@ eval (struct ebuffer *ebuf, int set_default)
|
|||||||
collapsed_length = linelen+1;
|
collapsed_length = linelen+1;
|
||||||
if (collapsed)
|
if (collapsed)
|
||||||
free (collapsed);
|
free (collapsed);
|
||||||
|
/* Don't need xrealloc: we don't need to preserve the content. */
|
||||||
collapsed = xmalloc (collapsed_length);
|
collapsed = xmalloc (collapsed_length);
|
||||||
}
|
}
|
||||||
strcpy (collapsed, line);
|
strcpy (collapsed, line);
|
||||||
@ -582,183 +672,113 @@ eval (struct ebuffer *ebuf, int set_default)
|
|||||||
collapse_continuations (collapsed);
|
collapse_continuations (collapsed);
|
||||||
remove_comments (collapsed);
|
remove_comments (collapsed);
|
||||||
|
|
||||||
/* Compare a word, both length and contents. */
|
/* See if this is a variable assignment. We need to do this early, to
|
||||||
#define word1eq(s) (wlen == sizeof(s)-1 && strneq (s, p, sizeof(s)-1))
|
allow variables with names like 'ifdef', 'export', 'private', etc. */
|
||||||
p = collapsed;
|
p = parse_var_assignment(collapsed, &vmod);
|
||||||
while (isspace ((unsigned char)*p))
|
if (vmod.assign_v)
|
||||||
++p;
|
|
||||||
|
|
||||||
if (*p == '\0')
|
|
||||||
/* This line is completely empty--ignore it. */
|
|
||||||
continue;
|
|
||||||
|
|
||||||
/* Find the end of the first token. Note we don't need to worry about
|
|
||||||
* ":" here since we compare tokens by length (so "export" will never
|
|
||||||
* be equal to "export:").
|
|
||||||
*/
|
|
||||||
for (p2 = p+1; *p2 != '\0' && !isspace ((unsigned char)*p2); ++p2)
|
|
||||||
;
|
|
||||||
wlen = p2 - p;
|
|
||||||
|
|
||||||
/* Find the start of the second token. If it looks like a target or
|
|
||||||
variable definition it can't be a preprocessor token so skip
|
|
||||||
them--this allows variables/targets named `ifdef', `export', etc. */
|
|
||||||
while (isspace ((unsigned char)*p2))
|
|
||||||
++p2;
|
|
||||||
|
|
||||||
if ((p2[0] == ':' || p2[0] == '+' || p2[0] == '=') && p2[1] == '\0')
|
|
||||||
{
|
{
|
||||||
/* It can't be a preprocessor token so skip it if we're ignoring */
|
struct variable *v;
|
||||||
if (ignoring)
|
enum variable_origin origin = vmod.override_v ? o_override : o_file;
|
||||||
continue;
|
|
||||||
|
|
||||||
goto skip_conditionals;
|
/* If we're ignoring then we're done now. */
|
||||||
}
|
if (ignoring)
|
||||||
|
|
||||||
/* We must first check for conditional and `define' directives before
|
|
||||||
ignoring anything, since they control what we will do with
|
|
||||||
following lines. */
|
|
||||||
|
|
||||||
if (!in_ignored_define)
|
|
||||||
{
|
|
||||||
int i = conditional_line (p, wlen, fstart);
|
|
||||||
if (i != -2)
|
|
||||||
{
|
{
|
||||||
if (i == -1)
|
if (vmod.define_v)
|
||||||
fatal (fstart, _("invalid syntax in conditional"));
|
in_ignored_define = 1;
|
||||||
|
|
||||||
ignoring = i;
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (word1eq ("endef"))
|
/* If it's a multi-line define / endef, manage that. */
|
||||||
{
|
if (vmod.define_v)
|
||||||
if (!in_ignored_define)
|
{
|
||||||
fatal (fstart, _("extraneous `endef'"));
|
if (*p == '\0')
|
||||||
in_ignored_define = 0;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (word1eq ("define"))
|
|
||||||
{
|
|
||||||
if (ignoring)
|
|
||||||
in_ignored_define = 1;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (*p2 == '\0')
|
|
||||||
fatal (fstart, _("empty variable name"));
|
fatal (fstart, _("empty variable name"));
|
||||||
|
|
||||||
/* Let the variable name be the whole rest of the line,
|
/* Let the variable name be the whole rest of the line,
|
||||||
with trailing blanks stripped (comments have already been
|
with trailing blanks stripped (comments have already been
|
||||||
removed), so it could be a complex variable/function
|
removed), so it could be a complex variable/function
|
||||||
reference that might contain blanks. */
|
reference that might contain blanks. */
|
||||||
p = strchr (p2, '\0');
|
p2 = p + strlen (p);
|
||||||
while (isblank ((unsigned char)p[-1]))
|
while (isblank ((unsigned char)p2[-1]))
|
||||||
--p;
|
--p2;
|
||||||
do_define (p2, p - p2, o_file, ebuf);
|
v = do_define (p, p2 - p, origin, ebuf);
|
||||||
}
|
}
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (word1eq ("override"))
|
|
||||||
{
|
|
||||||
if (*p2 == '\0')
|
|
||||||
error (fstart, _("empty `override' directive"));
|
|
||||||
|
|
||||||
if (strneq (p2, "define", 6)
|
|
||||||
&& (isblank ((unsigned char)p2[6]) || p2[6] == '\0'))
|
|
||||||
{
|
|
||||||
if (ignoring)
|
|
||||||
in_ignored_define = 1;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
p2 = next_token (p2 + 6);
|
|
||||||
if (*p2 == '\0')
|
|
||||||
fatal (fstart, _("empty variable name"));
|
|
||||||
|
|
||||||
/* Let the variable name be the whole rest of the line,
|
|
||||||
with trailing blanks stripped (comments have already been
|
|
||||||
removed), so it could be a complex variable/function
|
|
||||||
reference that might contain blanks. */
|
|
||||||
p = strchr (p2, '\0');
|
|
||||||
while (isblank ((unsigned char)p[-1]))
|
|
||||||
--p;
|
|
||||||
do_define (p2, p - p2, o_override, ebuf);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (!ignoring
|
|
||||||
&& !try_variable_definition (fstart, p2, o_override, 0))
|
|
||||||
error (fstart, _("invalid `override' directive"));
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ignoring)
|
|
||||||
/* Ignore the line. We continue here so conditionals
|
|
||||||
can appear in the middle of a rule. */
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (word1eq ("export"))
|
|
||||||
{
|
|
||||||
/* 'export' by itself causes everything to be exported. */
|
|
||||||
if (*p2 == '\0')
|
|
||||||
export_all_variables = 1;
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
struct variable *v;
|
v = try_variable_definition (fstart, p, origin, 0);
|
||||||
|
assert (v != NULL);
|
||||||
v = try_variable_definition (fstart, p2, o_file, 0);
|
|
||||||
if (v != 0)
|
|
||||||
v->export = v_export;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
unsigned int l;
|
|
||||||
const char *cp;
|
|
||||||
char *ap;
|
|
||||||
|
|
||||||
/* Expand the line so we can use indirect and constructed
|
|
||||||
variable names in an export command. */
|
|
||||||
cp = ap = allocated_variable_expand (p2);
|
|
||||||
|
|
||||||
for (p = find_next_token (&cp, &l); p != 0;
|
|
||||||
p = find_next_token (&cp, &l))
|
|
||||||
{
|
|
||||||
v = lookup_variable (p, l);
|
|
||||||
if (v == 0)
|
|
||||||
v = define_variable_loc (p, l, "", o_file, 0, fstart);
|
|
||||||
v->export = v_export;
|
|
||||||
}
|
|
||||||
|
|
||||||
free (ap);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (vmod.export_v)
|
||||||
|
v->export = v_export;
|
||||||
|
if (vmod.private_v)
|
||||||
|
v->private_var = 1;
|
||||||
|
|
||||||
|
/* This line has been dealt with. */
|
||||||
goto rule_complete;
|
goto rule_complete;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If this line is completely empty, ignore it. */
|
||||||
|
if (*p == '\0')
|
||||||
|
continue;
|
||||||
|
|
||||||
|
p2 = end_of_token (p);
|
||||||
|
wlen = p2 - p;
|
||||||
|
p2 = next_token (p2);
|
||||||
|
|
||||||
|
/* If we're in an ignored define, skip this line (but maybe get out). */
|
||||||
|
if (in_ignored_define)
|
||||||
|
{
|
||||||
|
/* See if this is an endef line (plus optional comment). */
|
||||||
|
if (word1eq ("endef") && (*p2 == '\0' || *p2 == '#'))
|
||||||
|
in_ignored_define = 0;
|
||||||
|
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (word1eq ("unexport"))
|
/* Check for conditional state changes. */
|
||||||
|
{
|
||||||
|
int i = conditional_line (p, wlen, fstart);
|
||||||
|
if (i != -2)
|
||||||
|
{
|
||||||
|
if (i == -1)
|
||||||
|
fatal (fstart, _("invalid syntax in conditional"));
|
||||||
|
|
||||||
|
ignoring = i;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Nothing to see here... move along. */
|
||||||
|
if (ignoring)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* Manage the "export" keyword used outside of variable assignment
|
||||||
|
as well as "unexport". */
|
||||||
|
if (word1eq ("export") || word1eq ("unexport"))
|
||||||
{
|
{
|
||||||
|
int exporting = *p == 'u' ? 0 : 1;
|
||||||
|
|
||||||
|
/* (un)export by itself causes everything to be (un)exported. */
|
||||||
if (*p2 == '\0')
|
if (*p2 == '\0')
|
||||||
export_all_variables = 0;
|
export_all_variables = exporting;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
unsigned int l;
|
unsigned int l;
|
||||||
struct variable *v;
|
|
||||||
const char *cp;
|
const char *cp;
|
||||||
char *ap;
|
char *ap;
|
||||||
|
|
||||||
/* Expand the line so we can use indirect and constructed
|
/* Expand the line so we can use indirect and constructed
|
||||||
variable names in an unexport command. */
|
variable names in an (un)export command. */
|
||||||
cp = ap = allocated_variable_expand (p2);
|
cp = ap = allocated_variable_expand (p2);
|
||||||
|
|
||||||
for (p = find_next_token (&cp, &l); p != 0;
|
for (p = find_next_token (&cp, &l); p != 0;
|
||||||
p = find_next_token (&cp, &l))
|
p = find_next_token (&cp, &l))
|
||||||
{
|
{
|
||||||
v = lookup_variable (p, l);
|
struct variable *v = lookup_variable (p, l);
|
||||||
if (v == 0)
|
if (v == 0)
|
||||||
v = define_variable_loc (p, l, "", o_file, 0, fstart);
|
v = define_variable_loc (p, l, "", o_file, 0, fstart);
|
||||||
|
v->export = exporting ? v_export : v_noexport;
|
||||||
v->export = v_noexport;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
free (ap);
|
free (ap);
|
||||||
@ -766,7 +786,7 @@ eval (struct ebuffer *ebuf, int set_default)
|
|||||||
goto rule_complete;
|
goto rule_complete;
|
||||||
}
|
}
|
||||||
|
|
||||||
skip_conditionals:
|
/* Handle the special syntax for vpath. */
|
||||||
if (word1eq ("vpath"))
|
if (word1eq ("vpath"))
|
||||||
{
|
{
|
||||||
const char *cp;
|
const char *cp;
|
||||||
@ -791,6 +811,7 @@ eval (struct ebuffer *ebuf, int set_default)
|
|||||||
goto rule_complete;
|
goto rule_complete;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Handle include and variants. */
|
||||||
if (word1eq ("include") || word1eq ("-include") || word1eq ("sinclude"))
|
if (word1eq ("include") || word1eq ("-include") || word1eq ("sinclude"))
|
||||||
{
|
{
|
||||||
/* We have found an `include' line specifying a nested
|
/* We have found an `include' line specifying a nested
|
||||||
@ -849,10 +870,6 @@ eval (struct ebuffer *ebuf, int set_default)
|
|||||||
goto rule_complete;
|
goto rule_complete;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (try_variable_definition (fstart, p, o_file, 0))
|
|
||||||
/* This line has been dealt with. */
|
|
||||||
goto rule_complete;
|
|
||||||
|
|
||||||
/* This line starts with a tab but was not caught above because there
|
/* This line starts with a tab but was not caught above because there
|
||||||
was no preceding target, and the line might have been usable as a
|
was no preceding target, and the line might have been usable as a
|
||||||
variable definition. But now we know it is definitely lossage. */
|
variable definition. But now we know it is definitely lossage. */
|
||||||
@ -871,8 +888,6 @@ eval (struct ebuffer *ebuf, int set_default)
|
|||||||
|
|
||||||
{
|
{
|
||||||
enum make_word_type wtype;
|
enum make_word_type wtype;
|
||||||
enum variable_origin v_origin;
|
|
||||||
int exported;
|
|
||||||
char *cmdleft, *semip, *lb_next;
|
char *cmdleft, *semip, *lb_next;
|
||||||
unsigned int plen = 0;
|
unsigned int plen = 0;
|
||||||
char *colonp;
|
char *colonp;
|
||||||
@ -1038,31 +1053,8 @@ eval (struct ebuffer *ebuf, int set_default)
|
|||||||
p2 = variable_buffer + l;
|
p2 = variable_buffer + l;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* See if it's an "override" or "export" keyword; if so see if what
|
p2 = parse_var_assignment (p2, &vmod);
|
||||||
comes after it looks like a variable definition. */
|
if (vmod.assign_v)
|
||||||
|
|
||||||
wtype = get_next_mword (p2, NULL, &p, &wlen);
|
|
||||||
|
|
||||||
v_origin = o_file;
|
|
||||||
exported = 0;
|
|
||||||
if (wtype == w_static)
|
|
||||||
{
|
|
||||||
if (word1eq ("override"))
|
|
||||||
{
|
|
||||||
v_origin = o_override;
|
|
||||||
wtype = get_next_mword (p+wlen, NULL, &p, &wlen);
|
|
||||||
}
|
|
||||||
else if (word1eq ("export"))
|
|
||||||
{
|
|
||||||
exported = 1;
|
|
||||||
wtype = get_next_mword (p+wlen, NULL, &p, &wlen);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (wtype != w_eol)
|
|
||||||
wtype = get_next_mword (p+wlen, NULL, NULL, NULL);
|
|
||||||
|
|
||||||
if (wtype == w_varassign)
|
|
||||||
{
|
{
|
||||||
/* If there was a semicolon found, add it back, plus anything
|
/* If there was a semicolon found, add it back, plus anything
|
||||||
after it. */
|
after it. */
|
||||||
@ -1074,7 +1066,9 @@ eval (struct ebuffer *ebuf, int set_default)
|
|||||||
semip, strlen (semip)+1);
|
semip, strlen (semip)+1);
|
||||||
p = variable_buffer + l;
|
p = variable_buffer + l;
|
||||||
}
|
}
|
||||||
record_target_var (filenames, p, v_origin, exported, fstart);
|
record_target_var (filenames, p2,
|
||||||
|
vmod.override_v ? o_override : o_file,
|
||||||
|
&vmod, fstart);
|
||||||
filenames = 0;
|
filenames = 0;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -1319,7 +1313,7 @@ remove_comments (char *line)
|
|||||||
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. */
|
||||||
|
|
||||||
static void
|
static struct variable *
|
||||||
do_define (char *name, unsigned int namelen,
|
do_define (char *name, unsigned int namelen,
|
||||||
enum variable_origin origin, struct ebuffer *ebuf)
|
enum variable_origin origin, struct ebuffer *ebuf)
|
||||||
{
|
{
|
||||||
@ -1382,6 +1376,8 @@ do_define (char *name, unsigned int namelen,
|
|||||||
|
|
||||||
if (--nlevels == 0)
|
if (--nlevels == 0)
|
||||||
{
|
{
|
||||||
|
struct variable *v;
|
||||||
|
|
||||||
/* Define the variable. */
|
/* Define the variable. */
|
||||||
if (idx == 0)
|
if (idx == 0)
|
||||||
definition[0] = '\0';
|
definition[0] = '\0';
|
||||||
@ -1389,10 +1385,10 @@ do_define (char *name, unsigned int namelen,
|
|||||||
definition[idx - 1] = '\0';
|
definition[idx - 1] = '\0';
|
||||||
|
|
||||||
/* Always define these variables in the global set. */
|
/* Always define these variables in the global set. */
|
||||||
define_variable_global (var, strlen (var), definition,
|
v = define_variable_global (var, strlen (var), definition,
|
||||||
origin, 1, &defstart);
|
origin, 1, &defstart);
|
||||||
free (definition);
|
free (definition);
|
||||||
return;
|
return (v);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1413,9 +1409,6 @@ do_define (char *name, unsigned int namelen,
|
|||||||
|
|
||||||
/* No `endef'!! */
|
/* No `endef'!! */
|
||||||
fatal (&defstart, _("missing `endef', unterminated `define'"));
|
fatal (&defstart, _("missing `endef', unterminated `define'"));
|
||||||
|
|
||||||
/* NOTREACHED */
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Interpret conditional commands "ifdef", "ifndef", "ifeq",
|
/* Interpret conditional commands "ifdef", "ifndef", "ifeq",
|
||||||
@ -1763,7 +1756,7 @@ uniquize_deps (struct dep *chain)
|
|||||||
|
|
||||||
static void
|
static void
|
||||||
record_target_var (struct nameseq *filenames, char *defn,
|
record_target_var (struct nameseq *filenames, char *defn,
|
||||||
enum variable_origin origin, int exported,
|
enum variable_origin origin, struct vmodifiers *vmod,
|
||||||
const struct floc *flocp)
|
const struct floc *flocp)
|
||||||
{
|
{
|
||||||
struct nameseq *nextf;
|
struct nameseq *nextf;
|
||||||
@ -1795,7 +1788,7 @@ record_target_var (struct nameseq *filenames, char *defn,
|
|||||||
p->variable.fileinfo = *flocp;
|
p->variable.fileinfo = *flocp;
|
||||||
/* I don't think this can fail since we already determined it was a
|
/* I don't think this can fail since we already determined it was a
|
||||||
variable definition. */
|
variable definition. */
|
||||||
v = parse_variable_definition (&p->variable, defn);
|
v = assign_variable_definition (&p->variable, defn);
|
||||||
assert (v != 0);
|
assert (v != 0);
|
||||||
|
|
||||||
if (v->flavor == f_simple)
|
if (v->flavor == f_simple)
|
||||||
@ -1825,14 +1818,15 @@ record_target_var (struct nameseq *filenames, char *defn,
|
|||||||
current_variable_set_list = f->variables;
|
current_variable_set_list = f->variables;
|
||||||
v = try_variable_definition (flocp, defn, origin, 1);
|
v = try_variable_definition (flocp, defn, origin, 1);
|
||||||
if (!v)
|
if (!v)
|
||||||
error (flocp, _("Malformed target-specific variable definition"));
|
fatal (flocp, _("Malformed target-specific variable definition"));
|
||||||
current_variable_set_list = global;
|
current_variable_set_list = global;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set up the variable to be *-specific. */
|
/* Set up the variable to be *-specific. */
|
||||||
v->origin = origin;
|
v->origin = origin;
|
||||||
v->per_target = 1;
|
v->per_target = 1;
|
||||||
v->export = exported ? v_export : v_default;
|
v->private_var = vmod->private_v;
|
||||||
|
v->export = vmod->export_v ? v_export : v_default;
|
||||||
|
|
||||||
/* If it's not an override, check to see if there was a command-line
|
/* If it's not an override, check to see if there was a command-line
|
||||||
setting. If so, reset the value. */
|
setting. If so, reset the value. */
|
||||||
|
@ -1,3 +1,11 @@
|
|||||||
|
2009-05-25 Paul Smith <psmith@gnu.org>
|
||||||
|
|
||||||
|
* scripts/features/export: Test new variable parsing abilities.
|
||||||
|
|
||||||
|
2009-02-23 Ramon Garcia <ramon.garcia.f@gmail.com>
|
||||||
|
|
||||||
|
* scripts/variables/private: Create a new suite of tests for 'private'.
|
||||||
|
|
||||||
2007-11-04 Paul Smith <psmith@gnu.org>
|
2007-11-04 Paul Smith <psmith@gnu.org>
|
||||||
|
|
||||||
* scripts/functions/eval: Update error message for command -> recipe.
|
* scripts/functions/eval: Update error message for command -> recipe.
|
||||||
|
@ -6,12 +6,7 @@ $details = "";
|
|||||||
# The test driver cleans out our environment for us so we don't have to worry
|
# The test driver cleans out our environment for us so we don't have to worry
|
||||||
# about that here.
|
# about that here.
|
||||||
|
|
||||||
open(MAKEFILE,"> $makefile");
|
&run_make_test('
|
||||||
|
|
||||||
# The Contents of the MAKEFILE ...
|
|
||||||
|
|
||||||
print MAKEFILE <<'EOMAKE';
|
|
||||||
|
|
||||||
FOO = foo
|
FOO = foo
|
||||||
BAR = bar
|
BAR = bar
|
||||||
BOZ = boz
|
BOZ = boz
|
||||||
@ -40,76 +35,44 @@ endif
|
|||||||
all:
|
all:
|
||||||
@echo "FOO=$(FOO) BAR=$(BAR) BAZ=$(BAZ) BOZ=$(BOZ) BITZ=$(BITZ) BOTZ=$(BOTZ)"
|
@echo "FOO=$(FOO) BAR=$(BAR) BAZ=$(BAZ) BOZ=$(BOZ) BITZ=$(BITZ) BOTZ=$(BOTZ)"
|
||||||
@echo "FOO=$$FOO BAR=$$BAR BAZ=$$BAZ BOZ=$$BOZ BITZ=$$BITZ BOTZ=$$BOTZ"
|
@echo "FOO=$$FOO BAR=$$BAR BAZ=$$BAZ BOZ=$$BOZ BITZ=$$BITZ BOTZ=$$BOTZ"
|
||||||
|
',
|
||||||
EOMAKE
|
'', "FOO=foo BAR=bar BAZ=baz BOZ=boz BITZ=bitz BOTZ=botz
|
||||||
|
FOO= BAR= BAZ=baz BOZ=boz BITZ=bitz BOTZ=\n");
|
||||||
close(MAKEFILE);
|
|
||||||
|
|
||||||
# TEST 0: basics
|
|
||||||
|
|
||||||
&run_make_with_options($makefile,"",&get_logfile,0);
|
|
||||||
|
|
||||||
$answer = "FOO=foo BAR=bar BAZ=baz BOZ=boz BITZ=bitz BOTZ=botz
|
|
||||||
FOO= BAR= BAZ=baz BOZ=boz BITZ=bitz BOTZ=\n";
|
|
||||||
|
|
||||||
&compare_output($answer,&get_logfile(1));
|
|
||||||
|
|
||||||
# TEST 1: make sure vars inherited from the parent are exported
|
# TEST 1: make sure vars inherited from the parent are exported
|
||||||
|
|
||||||
$extraENV{FOO} = 1;
|
$extraENV{FOO} = 1;
|
||||||
|
|
||||||
&run_make_with_options($makefile,"",&get_logfile,0);
|
&run_make_test(undef, '', "FOO=foo BAR=bar BAZ=baz BOZ=boz BITZ=bitz BOTZ=botz
|
||||||
|
FOO=foo BAR= BAZ=baz BOZ=boz BITZ=bitz BOTZ=\n");
|
||||||
$answer = "FOO=foo BAR=bar BAZ=baz BOZ=boz BITZ=bitz BOTZ=botz
|
|
||||||
FOO=foo BAR= BAZ=baz BOZ=boz BITZ=bitz BOTZ=\n";
|
|
||||||
|
|
||||||
&compare_output($answer,&get_logfile(1));
|
|
||||||
|
|
||||||
# TEST 2: global export. Explicit unexport takes precedence.
|
# TEST 2: global export. Explicit unexport takes precedence.
|
||||||
|
|
||||||
&run_make_with_options($makefile,"EXPORT_ALL=1",&get_logfile,0);
|
run_make_test(undef, "EXPORT_ALL=1" ,
|
||||||
|
"FOO=foo BAR=bar BAZ=baz BOZ=boz BITZ=bitz BOTZ=botz
|
||||||
$answer = "FOO=foo BAR=bar BAZ=baz BOZ=boz BITZ=bitz BOTZ=botz
|
FOO=foo BAR=bar BAZ=baz BOZ=boz BITZ=bitz BOTZ=\n");
|
||||||
FOO=foo BAR=bar BAZ=baz BOZ=boz BITZ=bitz BOTZ=\n";
|
|
||||||
|
|
||||||
&compare_output($answer,&get_logfile(1));
|
|
||||||
|
|
||||||
# TEST 3: global unexport. Explicit export takes precedence.
|
# TEST 3: global unexport. Explicit export takes precedence.
|
||||||
|
|
||||||
&run_make_with_options($makefile,"UNEXPORT_ALL=1",&get_logfile,0);
|
&run_make_test(undef, "UNEXPORT_ALL=1",
|
||||||
|
"FOO=foo BAR=bar BAZ=baz BOZ=boz BITZ=bitz BOTZ=botz
|
||||||
$answer = "FOO=foo BAR=bar BAZ=baz BOZ=boz BITZ=bitz BOTZ=botz
|
FOO= BAR= BAZ=baz BOZ=boz BITZ=bitz BOTZ=\n");
|
||||||
FOO= BAR= BAZ=baz BOZ=boz BITZ=bitz BOTZ=\n";
|
|
||||||
|
|
||||||
&compare_output($answer,&get_logfile(1));
|
|
||||||
|
|
||||||
# TEST 4: both: in the above makefile the unexport comes last so that rules.
|
# TEST 4: both: in the above makefile the unexport comes last so that rules.
|
||||||
|
|
||||||
&run_make_with_options($makefile,"EXPORT_ALL=1 UNEXPORT_ALL=1",&get_logfile,0);
|
&run_make_test(undef, "EXPORT_ALL=1 UNEXPORT_ALL=1",
|
||||||
|
"FOO=foo BAR=bar BAZ=baz BOZ=boz BITZ=bitz BOTZ=botz
|
||||||
$answer = "FOO=foo BAR=bar BAZ=baz BOZ=boz BITZ=bitz BOTZ=botz
|
FOO= BAR= BAZ=baz BOZ=boz BITZ=bitz BOTZ=\n");
|
||||||
FOO= BAR= BAZ=baz BOZ=boz BITZ=bitz BOTZ=\n";
|
|
||||||
|
|
||||||
&compare_output($answer,&get_logfile(1));
|
|
||||||
|
|
||||||
# TEST 5: test the pseudo target.
|
# TEST 5: test the pseudo target.
|
||||||
|
|
||||||
&run_make_with_options($makefile,"EXPORT_ALL_PSEUDO=1",&get_logfile,0);
|
&run_make_test(undef, "EXPORT_ALL_PSEUDO=1",
|
||||||
|
"FOO=foo BAR=bar BAZ=baz BOZ=boz BITZ=bitz BOTZ=botz
|
||||||
$answer = "FOO=foo BAR=bar BAZ=baz BOZ=boz BITZ=bitz BOTZ=botz
|
FOO=foo BAR=bar BAZ=baz BOZ=boz BITZ=bitz BOTZ=\n");
|
||||||
FOO=foo BAR=bar BAZ=baz BOZ=boz BITZ=bitz BOTZ=\n";
|
|
||||||
|
|
||||||
&compare_output($answer,&get_logfile(1));
|
|
||||||
|
|
||||||
|
|
||||||
# TEST 6: Test the expansion of variables inside export
|
# TEST 6: Test the expansion of variables inside export
|
||||||
|
|
||||||
$makefile2 = &get_tmpfile;
|
&run_make_test('
|
||||||
|
|
||||||
open(MAKEFILE, "> $makefile2");
|
|
||||||
|
|
||||||
print MAKEFILE <<'EOF';
|
|
||||||
|
|
||||||
foo = f-ok
|
foo = f-ok
|
||||||
bar = b-ok
|
bar = b-ok
|
||||||
|
|
||||||
@ -125,24 +88,12 @@ export $(B)ar
|
|||||||
all:
|
all:
|
||||||
@echo foo=$(foo) bar=$(bar)
|
@echo foo=$(foo) bar=$(bar)
|
||||||
@echo foo=$$foo bar=$$bar
|
@echo foo=$$foo bar=$$bar
|
||||||
|
',
|
||||||
EOF
|
"", "foo=f-ok bar=b-ok\nfoo=f-ok bar=b-ok\n");
|
||||||
|
|
||||||
close(MAKEFILE);
|
|
||||||
|
|
||||||
&run_make_with_options($makefile2,"",&get_logfile,0);
|
|
||||||
$answer = "foo=f-ok bar=b-ok\nfoo=f-ok bar=b-ok\n";
|
|
||||||
&compare_output($answer,&get_logfile(1));
|
|
||||||
|
|
||||||
|
|
||||||
# TEST 7: Test the expansion of variables inside unexport
|
# TEST 7: Test the expansion of variables inside unexport
|
||||||
|
|
||||||
$makefile3 = &get_tmpfile;
|
&run_make_test('
|
||||||
|
|
||||||
open(MAKEFILE, "> $makefile3");
|
|
||||||
|
|
||||||
print MAKEFILE <<'EOF';
|
|
||||||
|
|
||||||
foo = f-ok
|
foo = f-ok
|
||||||
bar = b-ok
|
bar = b-ok
|
||||||
|
|
||||||
@ -160,24 +111,12 @@ unexport $(B)ar
|
|||||||
all:
|
all:
|
||||||
@echo foo=$(foo) bar=$(bar)
|
@echo foo=$(foo) bar=$(bar)
|
||||||
@echo foo=$$foo bar=$$bar
|
@echo foo=$$foo bar=$$bar
|
||||||
|
',
|
||||||
EOF
|
'', "foo=f-ok bar=b-ok\nfoo= bar=\n");
|
||||||
|
|
||||||
close(MAKEFILE);
|
|
||||||
|
|
||||||
&run_make_with_options($makefile3,"",&get_logfile,0);
|
|
||||||
$answer = "foo=f-ok bar=b-ok\nfoo= bar=\n";
|
|
||||||
&compare_output($answer,&get_logfile(1));
|
|
||||||
|
|
||||||
|
|
||||||
# TEST 7: Test exporting multiple variables on the same line
|
# TEST 7: Test exporting multiple variables on the same line
|
||||||
|
|
||||||
$makefile4 = &get_tmpfile;
|
&run_make_test('
|
||||||
|
|
||||||
open(MAKEFILE, "> $makefile4");
|
|
||||||
|
|
||||||
print MAKEFILE <<'EOF';
|
|
||||||
|
|
||||||
A = a
|
A = a
|
||||||
B = b
|
B = b
|
||||||
C = c
|
C = c
|
||||||
@ -196,23 +135,14 @@ export F G H I J
|
|||||||
export D E $(SOME)
|
export D E $(SOME)
|
||||||
|
|
||||||
all: ; @echo A=$$A B=$$B C=$$C D=$$D E=$$E F=$$F G=$$G H=$$H I=$$I J=$$J
|
all: ; @echo A=$$A B=$$B C=$$C D=$$D E=$$E F=$$F G=$$G H=$$H I=$$I J=$$J
|
||||||
EOF
|
',
|
||||||
|
'', "A=a B=b C=c D=d E=e F=f G=g H=h I=i J=j\n");
|
||||||
close(MAKEFILE);
|
|
||||||
|
|
||||||
&run_make_with_options($makefile4,"",&get_logfile,0);
|
|
||||||
$answer = "A=a B=b C=c D=d E=e F=f G=g H=h I=i J=j\n";
|
|
||||||
&compare_output($answer,&get_logfile(1));
|
|
||||||
|
|
||||||
|
|
||||||
# TEST 8: Test unexporting multiple variables on the same line
|
# TEST 8: Test unexporting multiple variables on the same line
|
||||||
|
|
||||||
$makefile5 = &get_tmpfile;
|
@extraENV{qw(A B C D E F G H I J)} = qw(1 2 3 4 5 6 7 8 9 10);
|
||||||
|
|
||||||
open(MAKEFILE, "> $makefile5");
|
|
||||||
|
|
||||||
print MAKEFILE <<'EOF';
|
|
||||||
|
|
||||||
|
&run_make_test('
|
||||||
A = a
|
A = a
|
||||||
B = b
|
B = b
|
||||||
C = c
|
C = c
|
||||||
@ -231,16 +161,26 @@ unexport F G H I J
|
|||||||
unexport D E $(SOME)
|
unexport D E $(SOME)
|
||||||
|
|
||||||
all: ; @echo A=$$A B=$$B C=$$C D=$$D E=$$E F=$$F G=$$G H=$$H I=$$I J=$$J
|
all: ; @echo A=$$A B=$$B C=$$C D=$$D E=$$E F=$$F G=$$G H=$$H I=$$I J=$$J
|
||||||
EOF
|
',
|
||||||
|
'', "A= B= C= D= E= F= G= H= I= J=\n");
|
||||||
|
|
||||||
close(MAKEFILE);
|
# TEST 9: Check setting a variable named "export"
|
||||||
|
|
||||||
@extraENV{qw(A B C D E F G H I J)} = qw(1 2 3 4 5 6 7 8 9 10);
|
&run_make_test('
|
||||||
|
export = 123
|
||||||
|
export export
|
||||||
|
export export = 456
|
||||||
|
a: ; @echo "\$$(export)=$(export) / \$$export=$$export"
|
||||||
|
',
|
||||||
|
'', "\$(export)=456 / \$export=456\n");
|
||||||
|
|
||||||
&run_make_with_options($makefile5,"",&get_logfile,0);
|
# TEST 9: Check "export" as a target
|
||||||
$answer = "A= B= C= D= E= F= G= H= I= J=\n";
|
|
||||||
&compare_output($answer,&get_logfile(1));
|
|
||||||
|
|
||||||
|
&run_make_test('
|
||||||
|
a: export
|
||||||
|
export: ; @echo "$@"
|
||||||
|
',
|
||||||
|
'', "export\n");
|
||||||
|
|
||||||
# This tells the test driver that the perl test script executed properly.
|
# This tells the test driver that the perl test script executed properly.
|
||||||
1;
|
1;
|
||||||
|
@ -6,9 +6,7 @@ Create a makefile containing various flavors of target-specific variable
|
|||||||
values, override and non-override, and using various variable expansion
|
values, override and non-override, and using various variable expansion
|
||||||
rules, semicolon interference, etc.";
|
rules, semicolon interference, etc.";
|
||||||
|
|
||||||
open(MAKEFILE,"> $makefile");
|
run_make_test('
|
||||||
|
|
||||||
print MAKEFILE <<'EOF';
|
|
||||||
SHELL = /bin/sh
|
SHELL = /bin/sh
|
||||||
export FOO = foo
|
export FOO = foo
|
||||||
export BAR = bar
|
export BAR = bar
|
||||||
@ -17,17 +15,17 @@ one two: ; @echo $(FOO) $(BAR)
|
|||||||
two: BAR = two
|
two: BAR = two
|
||||||
three: ; BAR=1000
|
three: ; BAR=1000
|
||||||
@echo $(FOO) $(BAR)
|
@echo $(FOO) $(BAR)
|
||||||
# Some things that shouldn't be target vars
|
# Some things that shouldn not be target vars
|
||||||
funk : override
|
funk : override
|
||||||
funk : override adelic
|
funk : override adelic
|
||||||
adelic override : ; echo $@
|
adelic override : ; echo $@
|
||||||
# Test per-target recursive variables
|
# Test per-target recursive variables
|
||||||
four:FOO=x
|
four:FOO=x
|
||||||
four:VAR$(FOO)=ok
|
four:VAR$(FOO)=ok
|
||||||
four: ; @echo '$(FOO) $(VAR$(FOO)) $(VAR) $(VARx)'
|
four: ; @echo "$(FOO) $(VAR$(FOO)) $(VAR) $(VARx)"
|
||||||
five:FOO=x
|
five:FOO=x
|
||||||
five six : VAR$(FOO)=good
|
five six : VAR$(FOO)=good
|
||||||
five six: ;@echo '$(FOO) $(VAR$(FOO)) $(VAR) $(VARx) $(VARfoo)'
|
five six: ;@echo "$(FOO) $(VAR$(FOO)) $(VAR) $(VARx) $(VARfoo)"
|
||||||
# Test per-target variable inheritance
|
# Test per-target variable inheritance
|
||||||
seven: eight
|
seven: eight
|
||||||
seven eight: ; @echo $@: $(FOO) $(BAR)
|
seven eight: ; @echo $@: $(FOO) $(BAR)
|
||||||
@ -41,8 +39,8 @@ nine-a: export BAZ = baz
|
|||||||
nine-a: ; @echo $$BAZ
|
nine-a: ; @echo $$BAZ
|
||||||
# Test = escaping
|
# Test = escaping
|
||||||
EQ = =
|
EQ = =
|
||||||
ten: one\=two
|
ten: one$(EQ)two
|
||||||
ten: one \= two
|
ten: one $(EQ) two
|
||||||
ten one$(EQ)two $(EQ):;@echo $@
|
ten one$(EQ)two $(EQ):;@echo $@
|
||||||
.PHONY: one two three four five six seven eight nine ten $(EQ) one$(EQ)two
|
.PHONY: one two three four five six seven eight nine ten $(EQ) one$(EQ)two
|
||||||
# Test target-specific vars with pattern/suffix rules
|
# Test target-specific vars with pattern/suffix rules
|
||||||
@ -54,92 +52,58 @@ foo.q : RVAR += rvar
|
|||||||
%.r %.s %.t: ; @echo $(QVAR) $(RVAR) $(SVAR) $(TVAR)
|
%.r %.s %.t: ; @echo $(QVAR) $(RVAR) $(SVAR) $(TVAR)
|
||||||
foo.r : RVAR += rvar
|
foo.r : RVAR += rvar
|
||||||
foo.t : TVAR := $(QVAR)
|
foo.t : TVAR := $(QVAR)
|
||||||
EOF
|
',
|
||||||
|
"one two three", "one bar\nfoo two\nBAR=1000\nfoo bar\n");
|
||||||
close(MAKEFILE);
|
|
||||||
|
|
||||||
# TEST #1
|
|
||||||
|
|
||||||
&run_make_with_options($makefile, "one two three", &get_logfile);
|
|
||||||
$answer = "one bar\nfoo two\nBAR=1000\nfoo bar\n";
|
|
||||||
&compare_output($answer,&get_logfile(1));
|
|
||||||
|
|
||||||
# TEST #2
|
# TEST #2
|
||||||
|
|
||||||
&run_make_with_options($makefile, "one two FOO=1 BAR=2", &get_logfile);
|
run_make_test(undef, "one two FOO=1 BAR=2", "one 2\n1 2\n");
|
||||||
$answer = "one 2\n1 2\n";
|
|
||||||
&compare_output($answer,&get_logfile(1));
|
|
||||||
|
|
||||||
# TEST #3
|
# TEST #3
|
||||||
|
|
||||||
&run_make_with_options($makefile, "four", &get_logfile);
|
run_make_test(undef, "four", "x ok ok\n");
|
||||||
$answer = "x ok ok\n";
|
|
||||||
&compare_output($answer,&get_logfile(1));
|
|
||||||
|
|
||||||
# TEST #4
|
# TEST #4
|
||||||
|
|
||||||
&run_make_with_options($makefile, "seven", &get_logfile);
|
run_make_test(undef, "seven", "eight: seven eight\nseven: seven seven\n");
|
||||||
$answer = "eight: seven eight\nseven: seven seven\n";
|
|
||||||
&compare_output($answer,&get_logfile(1));
|
|
||||||
|
|
||||||
# TEST #5
|
# TEST #5
|
||||||
|
|
||||||
&run_make_with_options($makefile, "nine", &get_logfile);
|
run_make_test(undef, "nine", "wallace bar wallace bar\n");
|
||||||
$answer = "wallace bar wallace bar\n";
|
|
||||||
&compare_output($answer,&get_logfile(1));
|
|
||||||
|
|
||||||
# TEST #5-a
|
# TEST #5-a
|
||||||
|
|
||||||
&run_make_with_options($makefile, "nine-a", &get_logfile);
|
run_make_test(undef, "nine-a", "baz\n");
|
||||||
$answer = "baz\n";
|
|
||||||
&compare_output($answer,&get_logfile(1));
|
|
||||||
|
|
||||||
# TEST #6
|
# TEST #6
|
||||||
|
|
||||||
&run_make_with_options($makefile, "ten", &get_logfile);
|
run_make_test(undef, "ten", "one=two\none bar\n=\nfoo two\nten\n");
|
||||||
$answer = "one=two\none bar\n=\nfoo two\nten\n";
|
|
||||||
&compare_output($answer,&get_logfile(1));
|
|
||||||
|
|
||||||
# TEST #6
|
# TEST #6
|
||||||
|
|
||||||
&run_make_with_options($makefile, "foo.q bar.q", &get_logfile);
|
run_make_test(undef, "foo.q bar.q", "qvar = rvar\nqvar =\n");
|
||||||
$answer = "qvar = rvar\nqvar =\n";
|
|
||||||
&compare_output($answer,&get_logfile(1));
|
|
||||||
|
|
||||||
# TEST #7
|
# TEST #7
|
||||||
|
|
||||||
&run_make_with_options($makefile, "foo.t bar.s", &get_logfile);
|
run_make_test(undef, "foo.t bar.s", "qvar = qvar\nqvar =\n");
|
||||||
$answer = "qvar = qvar\nqvar =\n";
|
|
||||||
&compare_output($answer,&get_logfile(1));
|
|
||||||
|
|
||||||
|
|
||||||
# TEST #8
|
# TEST #8
|
||||||
# For PR/1378: Target-specific vars don't inherit correctly
|
# For PR/1378: Target-specific vars don't inherit correctly
|
||||||
|
|
||||||
$makefile2 = &get_tmpfile;
|
run_make_test('
|
||||||
|
|
||||||
open(MAKEFILE,"> $makefile2");
|
|
||||||
print MAKEFILE <<'EOF';
|
|
||||||
foo: FOO = foo
|
foo: FOO = foo
|
||||||
bar: BAR = bar
|
bar: BAR = bar
|
||||||
foo: bar
|
foo: bar
|
||||||
bar: baz
|
bar: baz
|
||||||
baz: ; @echo $(FOO) $(BAR)
|
baz: ; @echo $(FOO) $(BAR)
|
||||||
EOF
|
', "", "foo bar\n");
|
||||||
close(MAKEFILE);
|
|
||||||
|
|
||||||
&run_make_with_options("$makefile2", "", &get_logfile);
|
|
||||||
$answer = "foo bar\n";
|
|
||||||
&compare_output($answer, &get_logfile(1));
|
|
||||||
|
|
||||||
# TEST #9
|
# TEST #9
|
||||||
# For PR/1380: Using += assignment in target-specific variables sometimes fails
|
# For PR/1380: Using += assignment in target-specific variables sometimes fails
|
||||||
# Also PR/1831
|
# Also PR/1831
|
||||||
|
|
||||||
$makefile3 = &get_tmpfile;
|
run_make_test('
|
||||||
|
|
||||||
open(MAKEFILE,"> $makefile3");
|
|
||||||
print MAKEFILE <<'EOF';
|
|
||||||
.PHONY: all one
|
.PHONY: all one
|
||||||
all: FOO += baz
|
all: FOO += baz
|
||||||
all: one; @echo $(FOO)
|
all: one; @echo $(FOO)
|
||||||
@ -149,43 +113,27 @@ FOO = bar
|
|||||||
one: FOO += biz
|
one: FOO += biz
|
||||||
one: FOO += boz
|
one: FOO += boz
|
||||||
one: ; @echo $(FOO)
|
one: ; @echo $(FOO)
|
||||||
EOF
|
',
|
||||||
close(MAKEFILE);
|
'', "bar baz biz boz\nbar baz\n");
|
||||||
|
|
||||||
&run_make_with_options("$makefile3", "", &get_logfile);
|
|
||||||
$answer = "bar baz biz boz\nbar baz\n";
|
|
||||||
&compare_output($answer, &get_logfile(1));
|
|
||||||
|
|
||||||
# Test #10
|
# Test #10
|
||||||
|
|
||||||
&run_make_with_options("$makefile3", "one", &get_logfile);
|
run_make_test(undef, 'one', "bar biz boz\n");
|
||||||
$answer = "bar biz boz\n";
|
|
||||||
&compare_output($answer, &get_logfile(1));
|
|
||||||
|
|
||||||
# Test #11
|
# Test #11
|
||||||
# PR/1709: Test semicolons in target-specific variable values
|
# PR/1709: Test semicolons in target-specific variable values
|
||||||
|
|
||||||
$makefile4 = &get_tmpfile;
|
run_make_test('
|
||||||
|
|
||||||
open(MAKEFILE, "> $makefile4");
|
|
||||||
print MAKEFILE <<'EOF';
|
|
||||||
foo : FOO = ; ok
|
foo : FOO = ; ok
|
||||||
foo : ; @echo '$(FOO)'
|
foo : ; @echo "$(FOO)"
|
||||||
EOF
|
',
|
||||||
close(MAKEFILE);
|
'', "; ok\n");
|
||||||
|
|
||||||
&run_make_with_options("$makefile4", "", &get_logfile);
|
|
||||||
$answer = "; ok\n";
|
|
||||||
&compare_output($answer, &get_logfile(1));
|
|
||||||
|
|
||||||
# Test #12
|
# Test #12
|
||||||
# PR/2020: More hassles with += target-specific vars. I _really_ think
|
# PR/2020: More hassles with += target-specific vars. I _really_ think
|
||||||
# I nailed it this time :-/.
|
# I nailed it this time :-/.
|
||||||
|
|
||||||
$makefile5 = &get_tmpfile;
|
run_make_test('
|
||||||
|
|
||||||
open(MAKEFILE, "> $makefile5");
|
|
||||||
print MAKEFILE <<'EOF';
|
|
||||||
.PHONY: a
|
.PHONY: a
|
||||||
|
|
||||||
BLAH := foo
|
BLAH := foo
|
||||||
@ -195,20 +143,13 @@ a: ; @$(COMMAND)
|
|||||||
|
|
||||||
a: BLAH := bar
|
a: BLAH := bar
|
||||||
a: COMMAND += snafu $(BLAH)
|
a: COMMAND += snafu $(BLAH)
|
||||||
EOF
|
',
|
||||||
close(MAKEFILE);
|
'', "bar snafu bar\n");
|
||||||
|
|
||||||
&run_make_with_options("$makefile5", "", &get_logfile);
|
|
||||||
$answer = "bar snafu bar\n";
|
|
||||||
&compare_output($answer, &get_logfile(1));
|
|
||||||
|
|
||||||
# Test #13
|
# Test #13
|
||||||
# Test double-colon rules with target-specific variable values
|
# Test double-colon rules with target-specific variable values
|
||||||
|
|
||||||
$makefile6 = &get_tmpfile;
|
run_make_test('
|
||||||
|
|
||||||
open(MAKEFILE, "> $makefile6");
|
|
||||||
print MAKEFILE <<'EOF';
|
|
||||||
W = bad
|
W = bad
|
||||||
X = bad
|
X = bad
|
||||||
foo: W = ok
|
foo: W = ok
|
||||||
@ -224,48 +165,30 @@ Z = nopat
|
|||||||
ifdef PATTERN
|
ifdef PATTERN
|
||||||
fo% : Z = pat
|
fo% : Z = pat
|
||||||
endif
|
endif
|
||||||
|
',
|
||||||
EOF
|
'foo', "ok ok foo nopat\nok ok foo nopat\n");
|
||||||
close(MAKEFILE);
|
|
||||||
|
|
||||||
&run_make_with_options("$makefile6", "foo", &get_logfile);
|
|
||||||
$answer = "ok ok foo nopat\nok ok foo nopat\n";
|
|
||||||
&compare_output($answer, &get_logfile(1));
|
|
||||||
|
|
||||||
# Test #14
|
# Test #14
|
||||||
# Test double-colon rules with target-specific variable values and
|
# Test double-colon rules with target-specific variable values and
|
||||||
# inheritance
|
# inheritance
|
||||||
|
|
||||||
&run_make_with_options("$makefile6", "bar", &get_logfile);
|
run_make_test(undef, 'bar', "ok ok bar nopat\nok ok bar nopat\n");
|
||||||
$answer = "ok ok bar nopat\nok ok bar nopat\n";
|
|
||||||
&compare_output($answer, &get_logfile(1));
|
|
||||||
|
|
||||||
# Test #15
|
# Test #15
|
||||||
# Test double-colon rules with pattern-specific variable values
|
# Test double-colon rules with pattern-specific variable values
|
||||||
|
|
||||||
&run_make_with_options("$makefile6", "foo PATTERN=yes", &get_logfile);
|
run_make_test(undef, 'foo PATTERN=yes', "ok ok foo pat\nok ok foo pat\n");
|
||||||
$answer = "ok ok foo pat\nok ok foo pat\n";
|
|
||||||
&compare_output($answer, &get_logfile(1));
|
|
||||||
|
|
||||||
|
|
||||||
# Test #16
|
# Test #16
|
||||||
# Test target-specific variables with very long command line
|
# Test target-specific variables with very long command line
|
||||||
# (> make default buffer length)
|
# (> make default buffer length)
|
||||||
|
|
||||||
$makefile7 = &get_tmpfile;
|
run_make_test('
|
||||||
|
|
||||||
open(MAKEFILE, "> $makefile7");
|
|
||||||
print MAKEFILE <<'EOF';
|
|
||||||
base_metals_fmd_reports.sun5 base_metals_fmd_reports CreateRealPositions CreateMarginFunds deals_changed_since : BUILD_OBJ=$(shell if [ -f "build_information.generate" ]; then echo "$(OBJ_DIR)/build_information.o"; else echo "no build information"; fi )
|
base_metals_fmd_reports.sun5 base_metals_fmd_reports CreateRealPositions CreateMarginFunds deals_changed_since : BUILD_OBJ=$(shell if [ -f "build_information.generate" ]; then echo "$(OBJ_DIR)/build_information.o"; else echo "no build information"; fi )
|
||||||
|
|
||||||
deals_changed_since: ; @echo $(BUILD_OBJ)
|
deals_changed_since: ; @echo $(BUILD_OBJ)
|
||||||
|
',
|
||||||
EOF
|
'', "no build information\n");
|
||||||
close(MAKEFILE);
|
|
||||||
|
|
||||||
&run_make_with_options("$makefile7", '', &get_logfile);
|
|
||||||
$answer = "no build information\n";
|
|
||||||
&compare_output($answer, &get_logfile(1));
|
|
||||||
|
|
||||||
# TEST #17
|
# TEST #17
|
||||||
|
|
||||||
@ -286,8 +209,7 @@ foo.x: FOOVAR = bar
|
|||||||
rules.mk : MYVAR = foo
|
rules.mk : MYVAR = foo
|
||||||
.INTERMEDIATE: foo.x rules.mk
|
.INTERMEDIATE: foo.x rules.mk
|
||||||
',
|
',
|
||||||
'-I t1',
|
'-I t1', 'MYVAR= FOOVAR=bar ALLVAR=xxx');
|
||||||
'MYVAR= FOOVAR=bar ALLVAR=xxx');
|
|
||||||
|
|
||||||
rmfiles('t1/rules.mk');
|
rmfiles('t1/rules.mk');
|
||||||
rmdir('t1');
|
rmdir('t1');
|
||||||
@ -301,7 +223,20 @@ run_make_test("
|
|||||||
VAR := \$\$FOO
|
VAR := \$\$FOO
|
||||||
foo: VAR += BAR
|
foo: VAR += BAR
|
||||||
foo: ; \@echo '\$(VAR)'",
|
foo: ; \@echo '\$(VAR)'",
|
||||||
'',
|
'', '$FOO BAR');
|
||||||
'$FOO BAR');
|
|
||||||
|
# TEST #19: Test define/endef variables as target-specific vars
|
||||||
|
|
||||||
|
# run_make_test('
|
||||||
|
# define b
|
||||||
|
# @echo global
|
||||||
|
# endef
|
||||||
|
# a: define b
|
||||||
|
# @echo local
|
||||||
|
# endef
|
||||||
|
|
||||||
|
# a: ; $(b)
|
||||||
|
# ',
|
||||||
|
# '', "local\n");
|
||||||
|
|
||||||
1;
|
1;
|
||||||
|
78
tests/scripts/variables/private
Normal file
78
tests/scripts/variables/private
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
# -*-perl-*-
|
||||||
|
|
||||||
|
$description = "Test 'private' variables.";
|
||||||
|
|
||||||
|
$details = "";
|
||||||
|
|
||||||
|
# 1: Simple verification that private variables are not inherited
|
||||||
|
&run_make_test('
|
||||||
|
a:
|
||||||
|
F = g
|
||||||
|
a: F = a
|
||||||
|
b: private F = b
|
||||||
|
|
||||||
|
a b c: ; @echo $@: F=$(F)
|
||||||
|
a: b
|
||||||
|
b: c
|
||||||
|
',
|
||||||
|
'', "c: F=a\nb: F=b\na: F=a\n");
|
||||||
|
|
||||||
|
# 2: Again, but this time we start with "b" so "a"'s variable is not in scope
|
||||||
|
&run_make_test(undef, 'b', "c: F=g\nb: F=b\n");
|
||||||
|
|
||||||
|
# 3: Verification with pattern-specific variables
|
||||||
|
&run_make_test('
|
||||||
|
t.a:
|
||||||
|
|
||||||
|
F1 = g
|
||||||
|
F2 = g
|
||||||
|
%.a: private F1 = a
|
||||||
|
%.a: F2 = a
|
||||||
|
|
||||||
|
t.a t.b: ; @echo $@: F1=$(F1) / F2=$(F2)
|
||||||
|
t.a: t.b
|
||||||
|
',
|
||||||
|
'', "t.b: F1=g / F2=a\nt.a: F1=a / F2=a\n");
|
||||||
|
|
||||||
|
# 4: Test private global variables
|
||||||
|
&run_make_test('
|
||||||
|
a:
|
||||||
|
private F = g
|
||||||
|
G := $(F)
|
||||||
|
a:
|
||||||
|
b: F = b
|
||||||
|
|
||||||
|
a b: ; @echo $@: F=$(F) / G=$(G)
|
||||||
|
a: b
|
||||||
|
',
|
||||||
|
'', "b: F=b / G=g\na: F= / G=g\n");
|
||||||
|
|
||||||
|
# 5: Multiple conditions on the same variable. Test export.
|
||||||
|
delete $ENV{'_X'};
|
||||||
|
&run_make_test('
|
||||||
|
_X = x
|
||||||
|
a: export override private _X = a
|
||||||
|
a: ; @echo _X=$(_X) / _X=$$_X
|
||||||
|
',
|
||||||
|
'', "_X=a / _X=a");
|
||||||
|
|
||||||
|
# 6: Test override.
|
||||||
|
&run_make_test(undef, '_X=c', "_X=a / _X=a\n");
|
||||||
|
|
||||||
|
# 7: Ensure keywords still work as targets
|
||||||
|
&run_make_test('
|
||||||
|
a: export override private foo bar
|
||||||
|
foo bar export override private: ; @echo $@
|
||||||
|
',
|
||||||
|
'', "export\noverride\nprivate\nfoo\nbar\n");
|
||||||
|
|
||||||
|
# 8: Ensure keywords still work as variables
|
||||||
|
&run_make_test('
|
||||||
|
private = g
|
||||||
|
a: private = a
|
||||||
|
a: b
|
||||||
|
a b: ; @echo $@=$(private)
|
||||||
|
',
|
||||||
|
'', "b=a\na=a\n");
|
||||||
|
|
||||||
|
1;
|
169
variable.c
169
variable.c
@ -138,7 +138,7 @@ variable_hash_cmp (const void *xv, const void *yv)
|
|||||||
|
|
||||||
static struct variable_set global_variable_set;
|
static struct variable_set global_variable_set;
|
||||||
static struct variable_set_list global_setlist
|
static struct variable_set_list global_setlist
|
||||||
= { 0, &global_variable_set };
|
= { 0, &global_variable_set, 0 };
|
||||||
struct variable_set_list *current_variable_set_list = &global_setlist;
|
struct variable_set_list *current_variable_set_list = &global_setlist;
|
||||||
|
|
||||||
/* Implement variables. */
|
/* Implement variables. */
|
||||||
@ -221,6 +221,7 @@ define_variable_in_set (const char *name, unsigned int length,
|
|||||||
v->exp_count = 0;
|
v->exp_count = 0;
|
||||||
v->per_target = 0;
|
v->per_target = 0;
|
||||||
v->append = 0;
|
v->append = 0;
|
||||||
|
v->private_var = 0;
|
||||||
v->export = v_default;
|
v->export = v_default;
|
||||||
|
|
||||||
v->exportable = 1;
|
v->exportable = 1;
|
||||||
@ -340,6 +341,7 @@ lookup_variable (const char *name, unsigned int length)
|
|||||||
{
|
{
|
||||||
const struct variable_set_list *setlist;
|
const struct variable_set_list *setlist;
|
||||||
struct variable var_key;
|
struct variable var_key;
|
||||||
|
int is_parent = 0;
|
||||||
|
|
||||||
var_key.name = (char *) name;
|
var_key.name = (char *) name;
|
||||||
var_key.length = length;
|
var_key.length = length;
|
||||||
@ -351,8 +353,10 @@ lookup_variable (const char *name, unsigned int length)
|
|||||||
struct variable *v;
|
struct variable *v;
|
||||||
|
|
||||||
v = (struct variable *) hash_find_item ((struct hash_table *) &set->table, &var_key);
|
v = (struct variable *) hash_find_item ((struct hash_table *) &set->table, &var_key);
|
||||||
if (v)
|
if (v && (!is_parent || !v->private_var))
|
||||||
return v->special ? lookup_special_var (v) : v;
|
return v->special ? lookup_special_var (v) : v;
|
||||||
|
|
||||||
|
is_parent |= setlist->next_is_parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef VMS
|
#ifdef VMS
|
||||||
@ -463,6 +467,7 @@ initialize_file_variables (struct file *file, int reading)
|
|||||||
{
|
{
|
||||||
initialize_file_variables (file->double_colon, reading);
|
initialize_file_variables (file->double_colon, reading);
|
||||||
l->next = file->double_colon->variables;
|
l->next = file->double_colon->variables;
|
||||||
|
l->next_is_parent = 0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -473,6 +478,7 @@ initialize_file_variables (struct file *file, int reading)
|
|||||||
initialize_file_variables (file->parent, reading);
|
initialize_file_variables (file->parent, reading);
|
||||||
l->next = file->parent->variables;
|
l->next = file->parent->variables;
|
||||||
}
|
}
|
||||||
|
l->next_is_parent = 1;
|
||||||
|
|
||||||
/* If we're not reading makefiles and we haven't looked yet, see if
|
/* If we're not reading makefiles and we haven't looked yet, see if
|
||||||
we can find pattern variables for this target. */
|
we can find pattern variables for this target. */
|
||||||
@ -518,6 +524,7 @@ initialize_file_variables (struct file *file, int reading)
|
|||||||
/* Also mark it as a per-target and copy export status. */
|
/* Also mark it as a per-target and copy export status. */
|
||||||
v->per_target = p->variable.per_target;
|
v->per_target = p->variable.per_target;
|
||||||
v->export = p->variable.export;
|
v->export = p->variable.export;
|
||||||
|
v->private_var = p->variable.private_var;
|
||||||
}
|
}
|
||||||
while ((p = lookup_pattern_var (p, file->name)) != 0);
|
while ((p = lookup_pattern_var (p, file->name)) != 0);
|
||||||
|
|
||||||
@ -531,7 +538,9 @@ initialize_file_variables (struct file *file, int reading)
|
|||||||
if (file->pat_variables != 0)
|
if (file->pat_variables != 0)
|
||||||
{
|
{
|
||||||
file->pat_variables->next = l->next;
|
file->pat_variables->next = l->next;
|
||||||
|
file->pat_variables->next_is_parent = l->next_is_parent;
|
||||||
l->next = file->pat_variables;
|
l->next = file->pat_variables;
|
||||||
|
l->next_is_parent = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -552,6 +561,7 @@ create_new_variable_set (void)
|
|||||||
xmalloc (sizeof (struct variable_set_list));
|
xmalloc (sizeof (struct variable_set_list));
|
||||||
setlist->set = set;
|
setlist->set = set;
|
||||||
setlist->next = current_variable_set_list;
|
setlist->next = current_variable_set_list;
|
||||||
|
setlist->next_is_parent = 0;
|
||||||
|
|
||||||
return setlist;
|
return setlist;
|
||||||
}
|
}
|
||||||
@ -623,6 +633,7 @@ pop_variable_scope (void)
|
|||||||
set = global_setlist.set;
|
set = global_setlist.set;
|
||||||
global_setlist.set = setlist->set;
|
global_setlist.set = setlist->set;
|
||||||
global_setlist.next = setlist->next;
|
global_setlist.next = setlist->next;
|
||||||
|
global_setlist.next_is_parent = setlist->next_is_parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Free the one we no longer need. */
|
/* Free the one we no longer need. */
|
||||||
@ -1250,66 +1261,32 @@ do_variable_definition (const struct floc *flocp, const char *varname,
|
|||||||
return v->special ? set_special_var (v) : v;
|
return v->special ? set_special_var (v) : v;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Try to interpret LINE (a null-terminated string) as a variable definition.
|
/* Parse P (a null-terminated string) as a variable definition.
|
||||||
|
|
||||||
ORIGIN may be o_file, o_override, o_env, o_env_override,
|
If it is not a variable definition, return NULL.
|
||||||
or o_command specifying that the variable definition comes
|
|
||||||
from a makefile, an override directive, the environment with
|
|
||||||
or without the -e switch, or the command line.
|
|
||||||
|
|
||||||
See the comments for parse_variable_definition().
|
If it is a variable definition, return a pointer to the char after the
|
||||||
|
assignment token and set *FLAVOR to the type of variable assignment. */
|
||||||
|
|
||||||
If LINE was recognized as a variable definition, a pointer to its `struct
|
char *
|
||||||
variable' is returned. If LINE is not a variable definition, NULL is
|
parse_variable_definition (const char *p, enum variable_flavor *flavor)
|
||||||
returned. */
|
|
||||||
|
|
||||||
struct variable *
|
|
||||||
parse_variable_definition (struct variable *v, char *line)
|
|
||||||
{
|
{
|
||||||
register int c;
|
int wspace = 0;
|
||||||
register char *p = line;
|
|
||||||
register char *beg;
|
p = next_token (p);
|
||||||
register char *end;
|
|
||||||
enum variable_flavor flavor = f_bogus;
|
|
||||||
char *name;
|
|
||||||
|
|
||||||
while (1)
|
while (1)
|
||||||
{
|
{
|
||||||
c = *p++;
|
int c = *p++;
|
||||||
|
|
||||||
|
/* If we find a comment or EOS, it's not a variable definition. */
|
||||||
if (c == '\0' || c == '#')
|
if (c == '\0' || c == '#')
|
||||||
return 0;
|
return NULL;
|
||||||
if (c == '=')
|
|
||||||
|
if (c == '$')
|
||||||
{
|
{
|
||||||
end = p - 1;
|
/* This begins a variable expansion reference. Make sure we don't
|
||||||
flavor = f_recursive;
|
treat chars inside the reference as assignment tokens. */
|
||||||
break;
|
|
||||||
}
|
|
||||||
else if (c == ':')
|
|
||||||
if (*p == '=')
|
|
||||||
{
|
|
||||||
end = p++ - 1;
|
|
||||||
flavor = f_simple;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
/* A colon other than := is a rule line, not a variable defn. */
|
|
||||||
return 0;
|
|
||||||
else if (c == '+' && *p == '=')
|
|
||||||
{
|
|
||||||
end = p++ - 1;
|
|
||||||
flavor = f_append;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else if (c == '?' && *p == '=')
|
|
||||||
{
|
|
||||||
end = p++ - 1;
|
|
||||||
flavor = f_conditional;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else if (c == '$')
|
|
||||||
{
|
|
||||||
/* This might begin a variable expansion reference. Make sure we
|
|
||||||
don't misrecognize chars inside the reference as =, := or +=. */
|
|
||||||
char closeparen;
|
char closeparen;
|
||||||
int count;
|
int count;
|
||||||
c = *p++;
|
c = *p++;
|
||||||
@ -1318,7 +1295,8 @@ parse_variable_definition (struct variable *v, char *line)
|
|||||||
else if (c == '{')
|
else if (c == '{')
|
||||||
closeparen = '}';
|
closeparen = '}';
|
||||||
else
|
else
|
||||||
continue; /* Nope. */
|
/* '$$' or '$X'. Either way, nothing special to do here. */
|
||||||
|
continue;
|
||||||
|
|
||||||
/* P now points past the opening paren or brace.
|
/* P now points past the opening paren or brace.
|
||||||
Count parens or braces until it is matched. */
|
Count parens or braces until it is matched. */
|
||||||
@ -1333,15 +1311,84 @@ parse_variable_definition (struct variable *v, char *line)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* If we find whitespace skip it, and remember we found it. */
|
||||||
|
if (isblank ((unsigned char)c))
|
||||||
|
{
|
||||||
|
wspace = 1;
|
||||||
|
p = next_token (p);
|
||||||
|
c = *p++;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (c == '=')
|
||||||
|
{
|
||||||
|
*flavor = f_recursive;
|
||||||
|
return (char *)p;
|
||||||
|
}
|
||||||
|
/* Match assignment variants (:=, +=, ?=) */
|
||||||
|
else if (*p == '=')
|
||||||
|
{
|
||||||
|
switch (c)
|
||||||
|
{
|
||||||
|
case ':':
|
||||||
|
*flavor = f_simple;
|
||||||
|
break;
|
||||||
|
case '+':
|
||||||
|
*flavor = f_append;
|
||||||
|
break;
|
||||||
|
case '?':
|
||||||
|
*flavor = f_conditional;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/* If we skipped whitespace, non-assignments means no var. */
|
||||||
|
if (wspace)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/* Might be assignment, or might be $= or #=. Check. */
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
return (char *)++p;
|
||||||
|
}
|
||||||
|
else if (c == ':')
|
||||||
|
/* A colon other than := is a rule line, not a variable defn. */
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/* If we skipped whitespace, non-assignments means no var. */
|
||||||
|
if (wspace)
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
v->flavor = flavor;
|
|
||||||
|
return (char *)p;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Try to interpret LINE (a null-terminated string) as a variable definition.
|
||||||
|
|
||||||
|
If LINE was recognized as a variable definition, a pointer to its `struct
|
||||||
|
variable' is returned. If LINE is not a variable definition, NULL is
|
||||||
|
returned. */
|
||||||
|
|
||||||
|
struct variable *
|
||||||
|
assign_variable_definition (struct variable *v, char *line)
|
||||||
|
{
|
||||||
|
char *beg;
|
||||||
|
char *end;
|
||||||
|
enum variable_flavor flavor;
|
||||||
|
char *name;
|
||||||
|
|
||||||
beg = next_token (line);
|
beg = next_token (line);
|
||||||
|
line = parse_variable_definition (beg, &flavor);
|
||||||
|
if (!line)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
end = line - (flavor == f_recursive ? 1 : 2);
|
||||||
while (end > beg && isblank ((unsigned char)end[-1]))
|
while (end > beg && isblank ((unsigned char)end[-1]))
|
||||||
--end;
|
--end;
|
||||||
p = next_token (p);
|
line = next_token (line);
|
||||||
v->value = p;
|
v->value = line;
|
||||||
|
v->flavor = flavor;
|
||||||
|
|
||||||
/* Expand the name, so "$(foo)bar = baz" works. */
|
/* Expand the name, so "$(foo)bar = baz" works. */
|
||||||
name = alloca (end - beg + 1);
|
name = alloca (end - beg + 1);
|
||||||
@ -1362,7 +1409,7 @@ parse_variable_definition (struct variable *v, char *line)
|
|||||||
from a makefile, an override directive, the environment with
|
from a makefile, an override directive, the environment with
|
||||||
or without the -e switch, or the command line.
|
or without the -e switch, or the command line.
|
||||||
|
|
||||||
See the comments for parse_variable_definition().
|
See the comments for assign_variable_definition().
|
||||||
|
|
||||||
If LINE was recognized as a variable definition, a pointer to its `struct
|
If LINE was recognized as a variable definition, a pointer to its `struct
|
||||||
variable' is returned. If LINE is not a variable definition, NULL is
|
variable' is returned. If LINE is not a variable definition, NULL is
|
||||||
@ -1380,7 +1427,7 @@ try_variable_definition (const struct floc *flocp, char *line,
|
|||||||
else
|
else
|
||||||
v.fileinfo.filenm = 0;
|
v.fileinfo.filenm = 0;
|
||||||
|
|
||||||
if (!parse_variable_definition (&v, line))
|
if (!assign_variable_definition (&v, line))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
vp = do_variable_definition (flocp, v.name, v.value,
|
vp = do_variable_definition (flocp, v.name, v.value,
|
||||||
@ -1429,6 +1476,8 @@ print_variable (const void *item, void *arg)
|
|||||||
}
|
}
|
||||||
fputs ("# ", stdout);
|
fputs ("# ", stdout);
|
||||||
fputs (origin, stdout);
|
fputs (origin, stdout);
|
||||||
|
if (v->private_var)
|
||||||
|
fputs (" private", stdout);
|
||||||
if (v->fileinfo.filenm)
|
if (v->fileinfo.filenm)
|
||||||
printf (_(" (from `%s', line %lu)"),
|
printf (_(" (from `%s', line %lu)"),
|
||||||
v->fileinfo.filenm, v->fileinfo.lineno);
|
v->fileinfo.filenm, v->fileinfo.lineno);
|
||||||
@ -1440,7 +1489,7 @@ print_variable (const void *item, void *arg)
|
|||||||
printf ("define %s\n%s\nendef\n", v->name, v->value);
|
printf ("define %s\n%s\nendef\n", v->name, v->value);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
register char *p;
|
char *p;
|
||||||
|
|
||||||
printf ("%s %s= ", v->name, v->recursive ? v->append ? "+" : "" : ":");
|
printf ("%s %s= ", v->name, v->recursive ? v->append ? "+" : "" : ":");
|
||||||
|
|
||||||
|
@ -59,10 +59,12 @@ struct variable
|
|||||||
variable. */
|
variable. */
|
||||||
unsigned int conditional:1; /* Nonzero if set with a ?=. */
|
unsigned int conditional:1; /* Nonzero if set with a ?=. */
|
||||||
unsigned int per_target:1; /* Nonzero if a target-specific variable. */
|
unsigned int per_target:1; /* Nonzero if a target-specific variable. */
|
||||||
unsigned int special:1; /* Nonzero if this is a special variable. */
|
unsigned int special:1; /* Nonzero if this is a special variable. */
|
||||||
unsigned int exportable:1; /* Nonzero if the variable _could_ be
|
unsigned int exportable:1; /* Nonzero if the variable _could_ be
|
||||||
exported. */
|
exported. */
|
||||||
unsigned int expanding:1; /* Nonzero if currently being expanded. */
|
unsigned int expanding:1; /* Nonzero if currently being expanded. */
|
||||||
|
unsigned int private_var:1; /* Nonzero avoids inheritance of this
|
||||||
|
target-specific variable. */
|
||||||
unsigned int exp_count:EXP_COUNT_BITS;
|
unsigned int exp_count:EXP_COUNT_BITS;
|
||||||
/* If >1, allow this many self-referential
|
/* If >1, allow this many self-referential
|
||||||
expansions. */
|
expansions. */
|
||||||
@ -92,6 +94,7 @@ struct variable_set_list
|
|||||||
{
|
{
|
||||||
struct variable_set_list *next; /* Link in the chain. */
|
struct variable_set_list *next; /* Link in the chain. */
|
||||||
struct variable_set *set; /* Variable set. */
|
struct variable_set *set; /* Variable set. */
|
||||||
|
int next_is_parent; /* True if next is a parent target. */
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Structure used for pattern-specific variables. */
|
/* Structure used for pattern-specific variables. */
|
||||||
@ -151,7 +154,9 @@ struct variable *do_variable_definition (const struct floc *flocp,
|
|||||||
enum variable_origin origin,
|
enum variable_origin origin,
|
||||||
enum variable_flavor flavor,
|
enum variable_flavor flavor,
|
||||||
int target_var);
|
int target_var);
|
||||||
struct variable *parse_variable_definition (struct variable *v, char *line);
|
char *parse_variable_definition (const char *line,
|
||||||
|
enum variable_flavor *flavor);
|
||||||
|
struct variable *assign_variable_definition (struct variable *v, char *line);
|
||||||
struct variable *try_variable_definition (const struct floc *flocp, char *line,
|
struct variable *try_variable_definition (const struct floc *flocp, char *line,
|
||||||
enum variable_origin origin,
|
enum variable_origin origin,
|
||||||
int target_var);
|
int target_var);
|
||||||
|
Loading…
Reference in New Issue
Block a user