Add 'private' variable modifier, feature submitted by Ramon Garcia.
authorPaul Smith <psmith@gnu.org>
Tue, 26 May 2009 01:31:40 +0000 (01:31 +0000)
committerPaul Smith <psmith@gnu.org>
Tue, 26 May 2009 01:31:40 +0000 (01:31 +0000)
Rework the parser for variables to allow multiple modifiers and also
allow for variables and targets with modifier names, like "export" and
"private".

ChangeLog
NEWS
doc/make.texi
read.c
tests/ChangeLog
tests/scripts/features/export
tests/scripts/features/targetvars
tests/scripts/variables/private [new file with mode: 0644]
variable.c
variable.h

index 7b6ec17..803aa03 100644 (file)
--- 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
        * 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 (file)
--- 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.
 
 \f
 Version 3.81
index 21f0270..4c0fe44 100644 (file)
@@ -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 (file)
--- 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))
+
 \f
 /* Read in all the makefiles and return the chain of their names.  */
 
@@ -434,8 +454,75 @@ eval_buffer (char *buffer)
   alloca (0);
   return r;
 }
+\f
+/* 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;
+}
 
 \f
+
 /* 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;
 }
 \f
 /* 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.  */
index d9a0488..2f4ea71 100644 (file)
@@ -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.
index 38efe11..81bff0c 100644 (file)
@@ -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;
index e2e9c90..ad0766c 100644 (file)
@@ -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 (file)
index 0000000..b4baf5f
--- /dev/null
@@ -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;
index 4dafab1..92a96ec 100644 (file)
@@ -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;
 \f
 /* 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;
     }
 }
 \f
@@ -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;
 }
 \f
-/* 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;
+}
+\f
+/* 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 ? "+" : "" : ":");
 
index 8a80b76..d5b194f 100644 (file)
@@ -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);