summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog30
-rw-r--r--NEWS30
-rw-r--r--doc/make.texi67
-rw-r--r--read.c374
-rw-r--r--tests/ChangeLog8
-rw-r--r--tests/scripts/features/export158
-rw-r--r--tests/scripts/features/targetvars171
-rw-r--r--tests/scripts/variables/private78
-rw-r--r--variable.c169
-rw-r--r--variable.h9
10 files changed, 585 insertions, 509 deletions
diff --git a/ChangeLog b/ChangeLog
index 7b6ec17..803aa03 100644
--- a/ChangeLog
+++ b/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>
* 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.
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>
* configure.in: Check for strndup().
diff --git a/NEWS b/NEWS
index 15d3d6e..8004ab9 100644
--- a/NEWS
+++ b/NEWS
@@ -1,6 +1,6 @@
GNU make NEWS -*-indented-text-*-
History of user-visible changes.
- 1 April 2006
+ 25 May 2009
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
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
- introduction character from the default (TAB) to something else. The
- first character of this variable value is the new recipe introduction
- character. If the variable is set to the empty string, TAB is used
- again. It can be set and reset at will; rules will be parsed
- according to the current value.
+ introduction character from the default (TAB) to something else. The first
+ character of this variable value is the new recipe introduction character.
+ If the variable is set to the empty string, TAB is used again. It can be
+ set and reset at will; recipes will use the value active when they were
+ 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
diff --git a/doc/make.texi b/doc/make.texi
index 21f0270..4c0fe44 100644
--- a/doc/make.texi
+++ b/doc/make.texi
@@ -240,6 +240,7 @@ How to Use Variables
basis.
* Pattern-specific:: Target-specific variable values can be applied
to a group of targets that match a pattern.
+* Suppressing Inheritance:: Suppress inheritance of variables.
* Special Variables:: Variables with special meaning or behavior.
Advanced Features for Reference to Variables
@@ -4691,6 +4692,7 @@ they have particular specialized uses. @xref{Automatic Variables}.
basis.
* Pattern-specific:: Target-specific variable values can be applied
to a group of targets that match a pattern.
+* Suppressing Inheritance:: Suppress inheritance of variables.
* Special Variables:: Variables with special meaning or behavior.
@end menu
@@ -5625,19 +5627,9 @@ Set a target-specific variable value like this:
@var{target} @dots{} : @var{variable-assignment}
@end example
-@noindent
-or like this:
-
-@example
-@var{target} @dots{} : override @var{variable-assignment}
-@end example
-
-@noindent
-or like this:
-
-@example
-@var{target} @dots{} : export @var{variable-assignment}
-@end example
+Target-specific variable assignments can be prefixed with any or all of the
+special keywords @code{export}, @code{override}, or @code{private};
+these apply their normal behavior to this instance of the variable only.
Multiple @var{target} values create a target-specific variable value for
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
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
@cindex pattern-specific variables
@cindex variables, pattern-specific
@@ -5704,15 +5696,6 @@ Set a pattern-specific variable value like this:
@example
@var{pattern} @dots{} : @var{variable-assignment}
@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
values, multiple @var{pattern} values create a pattern-specific variable
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
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
@section Other Special Variables
@cindex makefiles, and special variables
diff --git a/read.c b/read.c
index eb4b212..ec6d6af 100644
--- a/read.c
+++ b/read.c
@@ -56,6 +56,17 @@ struct ebuffer
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. */
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 long readline (struct ebuffer *ebuf);
-static void do_define (char *name, unsigned int namelen,
- enum variable_origin origin, struct ebuffer *ebuf);
+static struct variable *do_define (char *name, unsigned int namelen,
+ enum variable_origin origin,
+ struct ebuffer *ebuf);
static int conditional_line (char *line, int len, const struct floc *flocp);
static void record_files (struct nameseq *filenames, const char *pattern,
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,
const struct floc *flocp);
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);
static enum make_word_type get_next_mword (char *buffer, char *delim,
char **startp, unsigned int *length);
static void remove_comments (char *line);
static char *find_char_unquote (char *string, int stop1, int stop2,
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. */
@@ -434,8 +454,75 @@ eval_buffer (char *buffer)
alloca (0);
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.
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;
char *p;
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 */
ebuf->floc.lineno += nlines;
nlines = readline (ebuf);
@@ -527,7 +616,7 @@ eval (struct ebuffer *ebuf, int set_default)
continue;
/* 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. */
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
contents of the buffer. */
@@ -575,6 +664,7 @@ eval (struct ebuffer *ebuf, int set_default)
collapsed_length = linelen+1;
if (collapsed)
free (collapsed);
+ /* Don't need xrealloc: we don't need to preserve the content. */
collapsed = xmalloc (collapsed_length);
}
strcpy (collapsed, line);
@@ -582,183 +672,113 @@ eval (struct ebuffer *ebuf, int set_default)
collapse_continuations (collapsed);
remove_comments (collapsed);
- /* Compare a word, both length and contents. */
-#define word1eq(s) (wlen == sizeof(s)-1 && strneq (s, p, sizeof(s)-1))
- p = collapsed;
- while (isspace ((unsigned char)*p))
- ++p;
+ /* See if this is a variable assignment. We need to do this early, to
+ allow variables with names like 'ifdef', 'export', 'private', etc. */
+ p = parse_var_assignment(collapsed, &vmod);
+ if (vmod.assign_v)
+ {
+ struct variable *v;
+ enum variable_origin origin = vmod.override_v ? o_override : o_file;
- if (*p == '\0')
- /* This line is completely empty--ignore it. */
- continue;
+ /* If we're ignoring then we're done now. */
+ if (ignoring)
+ {
+ if (vmod.define_v)
+ in_ignored_define = 1;
+ 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;
+ /* If it's a multi-line define / endef, manage that. */
+ if (vmod.define_v)
+ {
+ if (*p == '\0')
+ fatal (fstart, _("empty variable name"));
- /* 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;
+ /* 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. */
+ p2 = p + strlen (p);
+ while (isblank ((unsigned char)p2[-1]))
+ --p2;
+ v = do_define (p, p2 - p, origin, ebuf);
+ }
+ else
+ {
+ v = try_variable_definition (fstart, p, origin, 0);
+ assert (v != NULL);
+ }
- if ((p2[0] == ':' || p2[0] == '+' || p2[0] == '=') && p2[1] == '\0')
- {
- /* It can't be a preprocessor token so skip it if we're ignoring */
- if (ignoring)
- continue;
+ if (vmod.export_v)
+ v->export = v_export;
+ if (vmod.private_v)
+ v->private_var = 1;
- goto skip_conditionals;
+ /* This line has been dealt with. */
+ goto rule_complete;
}
- /* 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)
- fatal (fstart, _("invalid syntax in conditional"));
-
- ignoring = i;
- continue;
- }
- }
+ /* If this line is completely empty, ignore it. */
+ if (*p == '\0')
+ continue;
- if (word1eq ("endef"))
- {
- if (!in_ignored_define)
- fatal (fstart, _("extraneous `endef'"));
- in_ignored_define = 0;
- continue;
- }
+ p2 = end_of_token (p);
+ wlen = p2 - p;
+ p2 = next_token (p2);
- if (word1eq ("define"))
+ /* If we're in an ignored define, skip this line (but maybe get out). */
+ if (in_ignored_define)
{
- if (ignoring)
- in_ignored_define = 1;
- else
- {
- if (*p2 == '\0')
- fatal (fstart, _("empty variable name"));
+ /* See if this is an endef line (plus optional comment). */
+ if (word1eq ("endef") && (*p2 == '\0' || *p2 == '#'))
+ in_ignored_define = 0;
- /* 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_file, 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"));
+ /* Check for conditional state changes. */
+ {
+ int i = conditional_line (p, wlen, fstart);
+ if (i != -2)
+ {
+ if (i == -1)
+ fatal (fstart, _("invalid syntax in conditional"));
- continue;
- }
+ ignoring = i;
+ continue;
+ }
+ }
+ /* Nothing to see here... move along. */
if (ignoring)
- /* Ignore the line. We continue here so conditionals
- can appear in the middle of a rule. */
continue;
- if (word1eq ("export"))
+ /* Manage the "export" keyword used outside of variable assignment
+ as well as "unexport". */
+ if (word1eq ("export") || word1eq ("unexport"))
{
- /* 'export' by itself causes everything to be exported. */
- if (*p2 == '\0')
- export_all_variables = 1;
- else
- {
- struct variable *v;
-
- 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);
- }
- }
- goto rule_complete;
- }
+ int exporting = *p == 'u' ? 0 : 1;
- if (word1eq ("unexport"))
- {
+ /* (un)export by itself causes everything to be (un)exported. */
if (*p2 == '\0')
- export_all_variables = 0;
+ export_all_variables = exporting;
else
{
unsigned int l;
- struct variable *v;
const char *cp;
char *ap;
/* 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);
for (p = find_next_token (&cp, &l); p != 0;
p = find_next_token (&cp, &l))
{
- v = lookup_variable (p, l);
+ struct variable *v = lookup_variable (p, l);
if (v == 0)
v = define_variable_loc (p, l, "", o_file, 0, fstart);
-
- v->export = v_noexport;
+ v->export = exporting ? v_export : v_noexport;
}
free (ap);
@@ -766,7 +786,7 @@ eval (struct ebuffer *ebuf, int set_default)
goto rule_complete;
}
- skip_conditionals:
+ /* Handle the special syntax for vpath. */
if (word1eq ("vpath"))
{
const char *cp;
@@ -791,6 +811,7 @@ eval (struct ebuffer *ebuf, int set_default)
goto rule_complete;
}
+ /* Handle include and variants. */
if (word1eq ("include") || word1eq ("-include") || word1eq ("sinclude"))
{
/* We have found an `include' line specifying a nested
@@ -849,10 +870,6 @@ eval (struct ebuffer *ebuf, int set_default)
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
was no preceding target, and the line might have been usable as a
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 variable_origin v_origin;
- int exported;
char *cmdleft, *semip, *lb_next;
unsigned int plen = 0;
char *colonp;
@@ -1038,31 +1053,8 @@ eval (struct ebuffer *ebuf, int set_default)
p2 = variable_buffer + l;
}
- /* See if it's an "override" or "export" keyword; if so see if what
- comes after it looks like a variable definition. */
-
- 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)
+ p2 = parse_var_assignment (p2, &vmod);
+ if (vmod.assign_v)
{
/* If there was a semicolon found, add it back, plus anything
after it. */
@@ -1074,7 +1066,9 @@ eval (struct ebuffer *ebuf, int set_default)
semip, strlen (semip)+1);
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;
continue;
}
@@ -1319,7 +1313,7 @@ remove_comments (char *line)
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. */
-static void
+static struct variable *
do_define (char *name, unsigned int namelen,
enum variable_origin origin, struct ebuffer *ebuf)
{
@@ -1382,6 +1376,8 @@ do_define (char *name, unsigned int namelen,
if (--nlevels == 0)
{
+ struct variable *v;
+
/* Define the variable. */
if (idx == 0)
definition[0] = '\0';
@@ -1389,10 +1385,10 @@ do_define (char *name, unsigned int namelen,
definition[idx - 1] = '\0';
/* Always define these variables in the global set. */
- define_variable_global (var, strlen (var), definition,
- origin, 1, &defstart);
+ v = define_variable_global (var, strlen (var), definition,
+ origin, 1, &defstart);
free (definition);
- return;
+ return (v);
}
}
}
@@ -1413,9 +1409,6 @@ do_define (char *name, unsigned int namelen,
/* No `endef'!! */
fatal (&defstart, _("missing `endef', unterminated `define'"));
-
- /* NOTREACHED */
- return;
}
/* Interpret conditional commands "ifdef", "ifndef", "ifeq",
@@ -1763,7 +1756,7 @@ uniquize_deps (struct dep *chain)
static void
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)
{
struct nameseq *nextf;
@@ -1795,7 +1788,7 @@ record_target_var (struct nameseq *filenames, char *defn,
p->variable.fileinfo = *flocp;
/* I don't think this can fail since we already determined it was a
variable definition. */
- v = parse_variable_definition (&p->variable, defn);
+ v = assign_variable_definition (&p->variable, defn);
assert (v != 0);
if (v->flavor == f_simple)
@@ -1825,14 +1818,15 @@ record_target_var (struct nameseq *filenames, char *defn,
current_variable_set_list = f->variables;
v = try_variable_definition (flocp, defn, origin, 1);
if (!v)
- error (flocp, _("Malformed target-specific variable definition"));
+ fatal (flocp, _("Malformed target-specific variable definition"));
current_variable_set_list = global;
}
/* Set up the variable to be *-specific. */
v->origin = origin;
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
setting. If so, reset the value. */
diff --git a/tests/ChangeLog b/tests/ChangeLog
index d9a0488..2f4ea71 100644
--- a/tests/ChangeLog
+++ b/tests/ChangeLog
@@ -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>
* scripts/functions/eval: Update error message for command -> recipe.
diff --git a/tests/scripts/features/export b/tests/scripts/features/export
index 38efe11..81bff0c 100644
--- a/tests/scripts/features/export
+++ b/tests/scripts/features/export
@@ -6,12 +6,7 @@ $details = "";
# The test driver cleans out our environment for us so we don't have to worry
# about that here.
-open(MAKEFILE,"> $makefile");
-
-# The Contents of the MAKEFILE ...
-
-print MAKEFILE <<'EOMAKE';
-
+&run_make_test('
FOO = foo
BAR = bar
BOZ = boz
@@ -40,76 +35,44 @@ endif
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"
-
-EOMAKE
-
-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));
+',
+ '', "FOO=foo BAR=bar BAZ=baz BOZ=boz BITZ=bitz BOTZ=botz
+FOO= BAR= BAZ=baz BOZ=boz BITZ=bitz BOTZ=\n");
# TEST 1: make sure vars inherited from the parent are exported
$extraENV{FOO} = 1;
-&run_make_with_options($makefile,"",&get_logfile,0);
-
-$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));
+&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");
# TEST 2: global export. Explicit unexport takes precedence.
-&run_make_with_options($makefile,"EXPORT_ALL=1",&get_logfile,0);
-
-$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";
-
-&compare_output($answer,&get_logfile(1));
+run_make_test(undef, "EXPORT_ALL=1" ,
+ "FOO=foo BAR=bar BAZ=baz BOZ=boz BITZ=bitz BOTZ=botz
+FOO=foo BAR=bar BAZ=baz BOZ=boz BITZ=bitz BOTZ=\n");
# TEST 3: global unexport. Explicit export takes precedence.
-&run_make_with_options($makefile,"UNEXPORT_ALL=1",&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));
+&run_make_test(undef, "UNEXPORT_ALL=1",
+ "FOO=foo BAR=bar BAZ=baz BOZ=boz BITZ=bitz BOTZ=botz
+FOO= BAR= BAZ=baz BOZ=boz BITZ=bitz BOTZ=\n");
# 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);
-
-$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));
+&run_make_test(undef, "EXPORT_ALL=1 UNEXPORT_ALL=1",
+ "FOO=foo BAR=bar BAZ=baz BOZ=boz BITZ=bitz BOTZ=botz
+FOO= BAR= BAZ=baz BOZ=boz BITZ=bitz BOTZ=\n");
# TEST 5: test the pseudo target.
-&run_make_with_options($makefile,"EXPORT_ALL_PSEUDO=1",&get_logfile,0);
-
-$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";
-
-&compare_output($answer,&get_logfile(1));
-
+&run_make_test(undef, "EXPORT_ALL_PSEUDO=1",
+ "FOO=foo BAR=bar BAZ=baz BOZ=boz BITZ=bitz BOTZ=botz
+FOO=foo BAR=bar BAZ=baz BOZ=boz BITZ=bitz BOTZ=\n");
# TEST 6: Test the expansion of variables inside export
-$makefile2 = &get_tmpfile;
-
-open(MAKEFILE, "> $makefile2");
-
-print MAKEFILE <<'EOF';
-
+&run_make_test('
foo = f-ok
bar = b-ok
@@ -125,24 +88,12 @@ export $(B)ar
all:
@echo foo=$(foo) bar=$(bar)
@echo foo=$$foo bar=$$bar
-
-EOF
-
-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));
-
+',
+ "", "foo=f-ok bar=b-ok\nfoo=f-ok bar=b-ok\n");
# TEST 7: Test the expansion of variables inside unexport
-$makefile3 = &get_tmpfile;
-
-open(MAKEFILE, "> $makefile3");
-
-print MAKEFILE <<'EOF';
-
+&run_make_test('
foo = f-ok
bar = b-ok
@@ -160,24 +111,12 @@ unexport $(B)ar
all:
@echo foo=$(foo) bar=$(bar)
@echo foo=$$foo bar=$$bar
-
-EOF
-
-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));
-
+',
+ '', "foo=f-ok bar=b-ok\nfoo= bar=\n");
# TEST 7: Test exporting multiple variables on the same line
-$makefile4 = &get_tmpfile;
-
-open(MAKEFILE, "> $makefile4");
-
-print MAKEFILE <<'EOF';
-
+&run_make_test('
A = a
B = b
C = c
@@ -196,23 +135,14 @@ export F G H I J
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
-EOF
-
-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));
-
+',
+ '', "A=a B=b C=c D=d E=e F=f G=g H=h I=i J=j\n");
# TEST 8: Test unexporting multiple variables on the same line
-$makefile5 = &get_tmpfile;
-
-open(MAKEFILE, "> $makefile5");
-
-print MAKEFILE <<'EOF';
+@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('
A = a
B = b
C = c
@@ -231,16 +161,26 @@ unexport F G H I J
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
-EOF
-
-close(MAKEFILE);
-
-@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_with_options($makefile5,"",&get_logfile,0);
-$answer = "A= B= C= D= E= F= G= H= I= J=\n";
-&compare_output($answer,&get_logfile(1));
-
+',
+ '', "A= B= C= D= E= F= G= H= I= J=\n");
+
+# TEST 9: Check setting a variable named "export"
+
+&run_make_test('
+export = 123
+export export
+export export = 456
+a: ; @echo "\$$(export)=$(export) / \$$export=$$export"
+',
+ '', "\$(export)=456 / \$export=456\n");
+
+# TEST 9: Check "export" as a target
+
+&run_make_test('
+a: export
+export: ; @echo "$@"
+',
+ '', "export\n");
# This tells the test driver that the perl test script executed properly.
1;
diff --git a/tests/scripts/features/targetvars b/tests/scripts/features/targetvars
index e2e9c90..ad0766c 100644
--- a/tests/scripts/features/targetvars
+++ b/tests/scripts/features/targetvars
@@ -6,9 +6,7 @@ Create a makefile containing various flavors of target-specific variable
values, override and non-override, and using various variable expansion
rules, semicolon interference, etc.";
-open(MAKEFILE,"> $makefile");
-
-print MAKEFILE <<'EOF';
+run_make_test('
SHELL = /bin/sh
export FOO = foo
export BAR = bar
@@ -17,17 +15,17 @@ one two: ; @echo $(FOO) $(BAR)
two: BAR = two
three: ; BAR=1000
@echo $(FOO) $(BAR)
-# Some things that shouldn't be target vars
+# Some things that shouldn not be target vars
funk : override
funk : override adelic
adelic override : ; echo $@
# Test per-target recursive variables
four:FOO=x
four:VAR$(FOO)=ok
-four: ; @echo '$(FOO) $(VAR$(FOO)) $(VAR) $(VARx)'
+four: ; @echo "$(FOO) $(VAR$(FOO)) $(VAR) $(VARx)"
five:FOO=x
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
seven: eight
seven eight: ; @echo $@: $(FOO) $(BAR)
@@ -41,8 +39,8 @@ nine-a: export BAZ = baz
nine-a: ; @echo $$BAZ
# Test = escaping
EQ = =
-ten: one\=two
-ten: one \= two
+ten: one$(EQ)two
+ten: one $(EQ) two
ten one$(EQ)two $(EQ):;@echo $@
.PHONY: one two three four five six seven eight nine ten $(EQ) one$(EQ)two
# Test target-specific vars with pattern/suffix rules
@@ -54,92 +52,58 @@ foo.q : RVAR += rvar
%.r %.s %.t: ; @echo $(QVAR) $(RVAR) $(SVAR) $(TVAR)
foo.r : RVAR += rvar
foo.t : TVAR := $(QVAR)
-EOF
-
-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));
+',
+ "one two three", "one bar\nfoo two\nBAR=1000\nfoo bar\n");
# TEST #2
-&run_make_with_options($makefile, "one two FOO=1 BAR=2", &get_logfile);
-$answer = "one 2\n1 2\n";
-&compare_output($answer,&get_logfile(1));
+run_make_test(undef, "one two FOO=1 BAR=2", "one 2\n1 2\n");
# TEST #3
-&run_make_with_options($makefile, "four", &get_logfile);
-$answer = "x ok ok\n";
-&compare_output($answer,&get_logfile(1));
+run_make_test(undef, "four", "x ok ok\n");
# TEST #4
-&run_make_with_options($makefile, "seven", &get_logfile);
-$answer = "eight: seven eight\nseven: seven seven\n";
-&compare_output($answer,&get_logfile(1));
+run_make_test(undef, "seven", "eight: seven eight\nseven: seven seven\n");
# TEST #5
-&run_make_with_options($makefile, "nine", &get_logfile);
-$answer = "wallace bar wallace bar\n";
-&compare_output($answer,&get_logfile(1));
+run_make_test(undef, "nine", "wallace bar wallace bar\n");
# TEST #5-a
-&run_make_with_options($makefile, "nine-a", &get_logfile);
-$answer = "baz\n";
-&compare_output($answer,&get_logfile(1));
+run_make_test(undef, "nine-a", "baz\n");
# TEST #6
-&run_make_with_options($makefile, "ten", &get_logfile);
-$answer = "one=two\none bar\n=\nfoo two\nten\n";
-&compare_output($answer,&get_logfile(1));
+run_make_test(undef, "ten", "one=two\none bar\n=\nfoo two\nten\n");
# TEST #6
-&run_make_with_options($makefile, "foo.q bar.q", &get_logfile);
-$answer = "qvar = rvar\nqvar =\n";
-&compare_output($answer,&get_logfile(1));
+run_make_test(undef, "foo.q bar.q", "qvar = rvar\nqvar =\n");
# TEST #7
-&run_make_with_options($makefile, "foo.t bar.s", &get_logfile);
-$answer = "qvar = qvar\nqvar =\n";
-&compare_output($answer,&get_logfile(1));
+run_make_test(undef, "foo.t bar.s", "qvar = qvar\nqvar =\n");
# TEST #8
# For PR/1378: Target-specific vars don't inherit correctly
-$makefile2 = &get_tmpfile;
-
-open(MAKEFILE,"> $makefile2");
-print MAKEFILE <<'EOF';
+run_make_test('
foo: FOO = foo
bar: BAR = bar
foo: bar
bar: baz
baz: ; @echo $(FOO) $(BAR)
-EOF
-close(MAKEFILE);
-
-&run_make_with_options("$makefile2", "", &get_logfile);
-$answer = "foo bar\n";
-&compare_output($answer, &get_logfile(1));
+', "", "foo bar\n");
# TEST #9
# For PR/1380: Using += assignment in target-specific variables sometimes fails
# Also PR/1831
-$makefile3 = &get_tmpfile;
-
-open(MAKEFILE,"> $makefile3");
-print MAKEFILE <<'EOF';
+run_make_test('
.PHONY: all one
all: FOO += baz
all: one; @echo $(FOO)
@@ -149,43 +113,27 @@ FOO = bar
one: FOO += biz
one: FOO += boz
one: ; @echo $(FOO)
-EOF
-close(MAKEFILE);
-
-&run_make_with_options("$makefile3", "", &get_logfile);
-$answer = "bar baz biz boz\nbar baz\n";
-&compare_output($answer, &get_logfile(1));
+',
+ '', "bar baz biz boz\nbar baz\n");
# Test #10
-&run_make_with_options("$makefile3", "one", &get_logfile);
-$answer = "bar biz boz\n";
-&compare_output($answer, &get_logfile(1));
+run_make_test(undef, 'one', "bar biz boz\n");
# Test #11
# PR/1709: Test semicolons in target-specific variable values
-$makefile4 = &get_tmpfile;
-
-open(MAKEFILE, "> $makefile4");
-print MAKEFILE <<'EOF';
+run_make_test('
foo : FOO = ; ok
-foo : ; @echo '$(FOO)'
-EOF
-close(MAKEFILE);
-
-&run_make_with_options("$makefile4", "", &get_logfile);
-$answer = "; ok\n";
-&compare_output($answer, &get_logfile(1));
+foo : ; @echo "$(FOO)"
+',
+ '', "; ok\n");
# Test #12
# PR/2020: More hassles with += target-specific vars. I _really_ think
# I nailed it this time :-/.
-$makefile5 = &get_tmpfile;
-
-open(MAKEFILE, "> $makefile5");
-print MAKEFILE <<'EOF';
+run_make_test('
.PHONY: a
BLAH := foo
@@ -195,20 +143,13 @@ a: ; @$(COMMAND)
a: BLAH := bar
a: COMMAND += snafu $(BLAH)
-EOF
-close(MAKEFILE);
-
-&run_make_with_options("$makefile5", "", &get_logfile);
-$answer = "bar snafu bar\n";
-&compare_output($answer, &get_logfile(1));
+',
+ '', "bar snafu bar\n");
# Test #13
# Test double-colon rules with target-specific variable values
-$makefile6 = &get_tmpfile;
-
-open(MAKEFILE, "> $makefile6");
-print MAKEFILE <<'EOF';
+run_make_test('
W = bad
X = bad
foo: W = ok
@@ -224,48 +165,30 @@ Z = nopat
ifdef PATTERN
fo% : Z = pat
endif
-
-EOF
-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));
+',
+ 'foo', "ok ok foo nopat\nok ok foo nopat\n");
# Test #14
# Test double-colon rules with target-specific variable values and
# inheritance
-&run_make_with_options("$makefile6", "bar", &get_logfile);
-$answer = "ok ok bar nopat\nok ok bar nopat\n";
-&compare_output($answer, &get_logfile(1));
+run_make_test(undef, 'bar', "ok ok bar nopat\nok ok bar nopat\n");
# Test #15
# Test double-colon rules with pattern-specific variable values
-&run_make_with_options("$makefile6", "foo PATTERN=yes", &get_logfile);
-$answer = "ok ok foo pat\nok ok foo pat\n";
-&compare_output($answer, &get_logfile(1));
-
+run_make_test(undef, 'foo PATTERN=yes', "ok ok foo pat\nok ok foo pat\n");
# Test #16
# Test target-specific variables with very long command line
# (> make default buffer length)
-$makefile7 = &get_tmpfile;
-
-open(MAKEFILE, "> $makefile7");
-print MAKEFILE <<'EOF';
+run_make_test('
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)
-
-EOF
-close(MAKEFILE);
-
-&run_make_with_options("$makefile7", '', &get_logfile);
-$answer = "no build information\n";
-&compare_output($answer, &get_logfile(1));
+',
+ '', "no build information\n");
# TEST #17
@@ -286,8 +209,7 @@ foo.x: FOOVAR = bar
rules.mk : MYVAR = foo
.INTERMEDIATE: foo.x rules.mk
',
- '-I t1',
- 'MYVAR= FOOVAR=bar ALLVAR=xxx');
+ '-I t1', 'MYVAR= FOOVAR=bar ALLVAR=xxx');
rmfiles('t1/rules.mk');
rmdir('t1');
@@ -301,7 +223,20 @@ run_make_test("
VAR := \$\$FOO
foo: VAR += BAR
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;
diff --git a/tests/scripts/variables/private b/tests/scripts/variables/private
new file mode 100644
index 0000000..b4baf5f
--- /dev/null
+++ b/tests/scripts/variables/private
@@ -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;
diff --git a/variable.c b/variable.c
index 4dafab1..92a96ec 100644
--- a/variable.c
+++ b/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_list global_setlist
- = { 0, &global_variable_set };
+ = { 0, &global_variable_set, 0 };
struct variable_set_list *current_variable_set_list = &global_setlist;
/* Implement variables. */
@@ -221,6 +221,7 @@ define_variable_in_set (const char *name, unsigned int length,
v->exp_count = 0;
v->per_target = 0;
v->append = 0;
+ v->private_var = 0;
v->export = v_default;
v->exportable = 1;
@@ -340,6 +341,7 @@ lookup_variable (const char *name, unsigned int length)
{
const struct variable_set_list *setlist;
struct variable var_key;
+ int is_parent = 0;
var_key.name = (char *) name;
var_key.length = length;
@@ -351,8 +353,10 @@ lookup_variable (const char *name, unsigned int length)
struct variable *v;
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;
+
+ is_parent |= setlist->next_is_parent;
}
#ifdef VMS
@@ -463,6 +467,7 @@ initialize_file_variables (struct file *file, int reading)
{
initialize_file_variables (file->double_colon, reading);
l->next = file->double_colon->variables;
+ l->next_is_parent = 0;
return;
}
@@ -473,6 +478,7 @@ initialize_file_variables (struct file *file, int reading)
initialize_file_variables (file->parent, reading);
l->next = file->parent->variables;
}
+ l->next_is_parent = 1;
/* If we're not reading makefiles and we haven't looked yet, see if
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. */
v->per_target = p->variable.per_target;
v->export = p->variable.export;
+ v->private_var = p->variable.private_var;
}
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)
{
file->pat_variables->next = l->next;
+ file->pat_variables->next_is_parent = l->next_is_parent;
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));
setlist->set = set;
setlist->next = current_variable_set_list;
+ setlist->next_is_parent = 0;
return setlist;
}
@@ -623,6 +633,7 @@ pop_variable_scope (void)
set = global_setlist.set;
global_setlist.set = setlist->set;
global_setlist.next = setlist->next;
+ global_setlist.next_is_parent = setlist->next_is_parent;
}
/* 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;
}
-/* 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,
- 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.
+ If it is not a variable definition, return NULL.
- 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
- variable' is returned. If LINE is not a variable definition, NULL is
- returned. */
-
-struct variable *
-parse_variable_definition (struct variable *v, char *line)
+char *
+parse_variable_definition (const char *p, enum variable_flavor *flavor)
{
- register int c;
- register char *p = line;
- register char *beg;
- register char *end;
- enum variable_flavor flavor = f_bogus;
- char *name;
+ int wspace = 0;
+
+ p = next_token (p);
while (1)
{
- c = *p++;
+ int c = *p++;
+
+ /* If we find a comment or EOS, it's not a variable definition. */
if (c == '\0' || c == '#')
- return 0;
- if (c == '=')
- {
- end = p - 1;
- flavor = f_recursive;
- 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 == '$')
+ return NULL;
+
+ if (c == '$')
{
- /* This might begin a variable expansion reference. Make sure we
- don't misrecognize chars inside the reference as =, := or +=. */
+ /* This begins a variable expansion reference. Make sure we don't
+ treat chars inside the reference as assignment tokens. */
char closeparen;
int count;
c = *p++;
@@ -1318,7 +1295,8 @@ parse_variable_definition (struct variable *v, char *line)
else if (c == '{')
closeparen = '}';
else
- continue; /* Nope. */
+ /* '$$' or '$X'. Either way, nothing special to do here. */
+ continue;
/* P now points past the opening paren or brace.
Count parens or braces until it is matched. */
@@ -1333,15 +1311,84 @@ parse_variable_definition (struct variable *v, char *line)
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);
+ 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]))
--end;
- p = next_token (p);
- v->value = p;
+ line = next_token (line);
+ v->value = line;
+ v->flavor = flavor;
/* Expand the name, so "$(foo)bar = baz" works. */
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
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
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
v.fileinfo.filenm = 0;
- if (!parse_variable_definition (&v, line))
+ if (!assign_variable_definition (&v, line))
return 0;
vp = do_variable_definition (flocp, v.name, v.value,
@@ -1429,6 +1476,8 @@ print_variable (const void *item, void *arg)
}
fputs ("# ", stdout);
fputs (origin, stdout);
+ if (v->private_var)
+ fputs (" private", stdout);
if (v->fileinfo.filenm)
printf (_(" (from `%s', line %lu)"),
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);
else
{
- register char *p;
+ char *p;
printf ("%s %s= ", v->name, v->recursive ? v->append ? "+" : "" : ":");
diff --git a/variable.h b/variable.h
index 8a80b76..d5b194f 100644
--- a/variable.h
+++ b/variable.h
@@ -59,10 +59,12 @@ struct variable
variable. */
unsigned int conditional:1; /* Nonzero if set with a ?=. */
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
exported. */
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;
/* If >1, allow this many self-referential
expansions. */
@@ -92,6 +94,7 @@ struct variable_set_list
{
struct variable_set_list *next; /* Link in the chain. */
struct variable_set *set; /* Variable set. */
+ int next_is_parent; /* True if next is a parent target. */
};
/* 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_flavor flavor,
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,
enum variable_origin origin,
int target_var);