]> git.ipfire.org Git - thirdparty/make.git/commitdiff
Add support for "::=" simple assignment operator.
authorPaul Smith <psmith@gnu.org>
Mon, 30 Jan 2012 00:21:57 +0000 (00:21 +0000)
committerPaul Smith <psmith@gnu.org>
Mon, 30 Jan 2012 00:21:57 +0000 (00:21 +0000)
The next POSIX standard will define "::=" to have the same behavior
as GNU make's ":=", so add support for this new operator.

ChangeLog
NEWS
doc/make.texi
read.c
tests/ChangeLog
tests/scripts/variables/define
tests/scripts/variables/flavors
variable.c
variable.h

index facb4cc40cbe4a8c291576c26246df6594615e6b..9cbb1e61e3eb4a8a58b1c49a739539290fc9f369 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,17 @@
 2012-01-29  Paul Smith  <psmith@gnu.org>
 
+       * variable.c (parse_variable_definition): New POSIX assignment ::=
+       Take a struct variable to return more information after parsing.
+       (assign_variable_definition): New parse_variable_definition() call.
+       * variable.h: New declaration of parse_variable_definition().
+       * read.c (do_define): New parse_variable_definition() call.
+       (parse_var_assignment): Ditto.
+       (get_next_mword): Parse ::= as a variable assignment.
+       * doc/make.texi (Flavors): Describe the new ::= syntax.
+       * NEWS: Mention the ::= operator.
+
+       * variable.h (struct variable): Rearrange elts to reduce struct size.
+
        * function.c (func_file): Create a new function, $(file ...)
        * doc/make.texi (File Function): Document the $(file ..) function.
        * NEWS: Announce it.
diff --git a/NEWS b/NEWS
index 20b8704918655c9e3a56f0199259f2fb86833fb8..b07ab98944eb0911c92caf1bc52703eb95d73452 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -38,6 +38,12 @@ http://sv.gnu.org/bugs/index.php?group=make&report_id=111&fix_release_id=101&set
   interpreted as shell assignment.  Change your assignment to add whitespace
   between the "!" and "=": "variable! = value"
 
+* New feature: "::=" simple assignment operator as defined by POSIX in 2012.
+  This operator has identical functionality to ":=" in GNU make, but will be
+  portable to any implementation of make conforming to a sufficiently new
+  version of POSIX (see http://austingroupbugs.net/view.php?id=330).  It is
+  not necessary to define the .POSIX target to access this operator.
+
 * New feature: GNU Guile integration
   This version of GNU make can be compiled with GNU Guile integration.
   GNU Guile serves as an embedded extension language for make.
index 9b8faee67552e616714ff0be44a2becc8acf9729..d723d2df9775db745654cd1c98b72a35ef50c9f1 100644 (file)
@@ -1399,6 +1399,7 @@ Variable definitions are parsed as follows:
 @var{immediate} = @var{deferred}
 @var{immediate} ?= @var{deferred}
 @var{immediate} := @var{immediate}
+@var{immediate} ::= @var{immediate}
 @var{immediate} += @var{deferred} or @var{immediate}
 @var{immediate} != @var{immediate}
 
@@ -1418,6 +1419,10 @@ define @var{immediate} :=
   @var{immediate}
 endef
 
+define @var{immediate} ::=
+  @var{immediate}
+endef
+
 define @var{immediate} +=
   @var{deferred} or @var{immediate}
 endef
@@ -1429,7 +1434,7 @@ endef
 
 For the append operator, @samp{+=}, the right-hand side is considered
 immediate if the variable was previously set as a simple variable
-(@samp{:=}), and deferred otherwise.
+(@samp{:=} or @samp{::=}), and deferred otherwise.
 
 For the shell assignment operator, @samp{!=}, the right-hand side is
 evaluated immediately and handed to the shell.  The result is stored in the
@@ -4967,9 +4972,9 @@ all:;echo $(foo)
 will echo @samp{Huh?}: @samp{$(foo)} expands to @samp{$(bar)} which
 expands to @samp{$(ugh)} which finally expands to @samp{Huh?}.@refill
 
-This flavor of variable is the only sort supported by other versions of
-@code{make}.  It has its advantages and its disadvantages.  An advantage
-(most would say) is that:
+This flavor of variable is the only sort supported by most other
+versions of @code{make}.  It has its advantages and its disadvantages.
+An advantage (most would say) is that:
 
 @example
 CFLAGS = $(include_dirs) -O
@@ -5005,8 +5010,14 @@ variables, there is another flavor: simply expanded variables.
 @cindex simply expanded variables
 @cindex variables, simply expanded
 @cindex :=
+@cindex ::=
 @dfn{Simply expanded variables} are defined by lines using @samp{:=}
-(@pxref{Setting, ,Setting Variables}).
+or @samp{::=} (@pxref{Setting, ,Setting Variables}).  Both forms are
+equivalent in GNU @code{make}; however only the @samp{::=} form is
+described by the POSIX standard (support for @samp{::=} was added to
+the POSIX standard in 2012, so older versions of @code{make} won't
+accept this form either).
+
 The value of a simply expanded variable is scanned
 once and for all, expanding any references to other variables and
 functions, when the variable is defined.  The actual value of the simply
@@ -5425,12 +5436,14 @@ Several variables have constant initial values.
 @cindex variables, setting
 @cindex =
 @cindex :=
+@cindex ::=
 @cindex ?=
 @cindex !=
 
 To set a variable from the makefile, write a line starting with the
-variable name followed by @samp{=} or @samp{:=}.  Whatever follows the
-@samp{=} or @samp{:=} on the line becomes the value.  For example,
+variable name followed by @samp{=} @samp{:=}, or @samp{::=}.  Whatever
+follows the @samp{=}, @samp{:=}, or @samp{::=} on the line becomes the
+value.  For example,
 
 @example
 objects = main.o foo.o bar.o utils.o
@@ -5440,10 +5453,11 @@ objects = main.o foo.o bar.o utils.o
 defines a variable named @code{objects}.  Whitespace around the variable
 name and immediately after the @samp{=} is ignored.
 
-Variables defined with @samp{=} are @dfn{recursively expanded} variables.
-Variables defined with @samp{:=} are @dfn{simply expanded} variables; these
-definitions can contain variable references which will be expanded before
-the definition is made.  @xref{Flavors, ,The Two Flavors of Variables}.
+Variables defined with @samp{=} are @dfn{recursively expanded}
+variables.  Variables defined with @samp{:=} or @samp{::=} are
+@dfn{simply expanded} variables; these definitions can contain
+variable references which will be expanded before the definition is
+made.  @xref{Flavors, ,The Two Flavors of Variables}.
 
 The variable name may contain function and variable references, which
 are expanded when the line is read to find the actual variable name to use.
@@ -5553,12 +5567,12 @@ explanation of the two flavors of variables.
 
 When you add to a variable's value with @samp{+=}, @code{make} acts
 essentially as if you had included the extra text in the initial
-definition of the variable.  If you defined it first with @samp{:=},
-making it a simply-expanded variable, @samp{+=} adds to that
-simply-expanded definition, and expands the new text before appending it
-to the old value just as @samp{:=} does
-(see @ref{Setting, ,Setting Variables}, for a full explanation of @samp{:=}).
-In fact,
+definition of the variable.  If you defined it first with @samp{:=} or
+@samp{::=}, making it a simply-expanded variable, @samp{+=} adds to
+that simply-expanded definition, and expands the new text before
+appending it to the old value just as @samp{:=} does (see
+@ref{Setting, ,Setting Variables}, for a full explanation of
+@samp{:=} or @samp{::=}).  In fact,
 
 @example
 variable := value
@@ -5898,13 +5912,13 @@ Multiple @var{target} values create a target-specific variable value for
 each member of the target list individually.
 
 The @var{variable-assignment} can be any valid form of assignment;
-recursive (@samp{=}), simple (@samp{:=}), appending (@samp{+=}), or
-conditional (@samp{?=}).  All variables that appear within the
-@var{variable-assignment} are evaluated within the context of the
-target: thus, any previously-defined target-specific variable values
-will be in effect.  Note that this variable is actually distinct from
-any ``global'' value: the two variables do not have to have the same
-flavor (recursive vs.@: simple).
+recursive (@samp{=}), simple (@samp{:=} or @samp{::=}), appending
+(@samp{+=}), or conditional (@samp{?=}).  All variables that appear
+within the @var{variable-assignment} are evaluated within the context
+of the target: thus, any previously-defined target-specific variable
+values will be in effect.  Note that this variable is actually
+distinct from any ``global'' value: the two variables do not have to
+have the same flavor (recursive vs.@: simple).
 
 Target-specific variables have the same priority as any other makefile
 variable.  Variables provided on the command line (and in the
@@ -8463,10 +8477,10 @@ makefile works by changing the variables.
 When you override a variable with a command line argument, you can
 define either a recursively-expanded variable or a simply-expanded
 variable.  The examples shown above make a recursively-expanded
-variable; to make a simply-expanded variable, write @samp{:=} instead
-of @samp{=}.  But, unless you want to include a variable reference or
-function call in the @emph{value} that you specify, it makes no
-difference which kind of variable you create.
+variable; to make a simply-expanded variable, write @samp{:=} or
+@samp{::=} instead of @samp{=}.  But, unless you want to include a
+variable reference or function call in the @emph{value} that you
+specify, it makes no difference which kind of variable you create.
 
 There is one way that the makefile can change a variable that you have
 overridden.  This is to use the @code{override} directive, which is a line
@@ -11024,6 +11038,7 @@ Here is a summary of the directives GNU @code{make} recognizes:
 @item define @var{variable}
 @itemx define @var{variable} =
 @itemx define @var{variable} :=
+@itemx define @var{variable} ::=
 @itemx define @var{variable} +=
 @itemx define @var{variable} ?=
 @itemx endef
@@ -11479,9 +11494,9 @@ prerequisites, etc., one of them depended on @var{xxx} again.
 @item Recursive variable `@var{xxx}' references itself (eventually).  Stop.
 This means you've defined a normal (recursive) @code{make} variable
 @var{xxx} that, when it's expanded, will refer to itself (@var{xxx}).
-This is not allowed; either use simply-expanded variables (@code{:=}) or
-use the append operator (@code{+=}).  @xref{Using Variables, ,How to Use
-Variables}.
+This is not allowed; either use simply-expanded variables (@samp{:=}
+or @samp{::=}) or use the append operator (@samp{+=}).  @xref{Using
+Variables, ,How to Use Variables}.
 
 @item Unterminated variable reference.  Stop.
 This means you forgot to provide the proper closing parenthesis
diff --git a/read.c b/read.c
index 634eb4c2124e7d5ec1c120c14425de383d552eee..4378d1b482862a5ac8499fa9129f1a9662b686b4 100644 (file)
--- a/read.c
+++ b/read.c
@@ -495,9 +495,9 @@ parse_var_assignment (const char *line, struct vmodifiers *vmod)
     {
       int wlen;
       const char *p2;
-      enum variable_flavor flavor;
+      struct variable v;
 
-      p2 = parse_variable_definition (p, &flavor);
+      p2 = parse_variable_definition (p, &v);
 
       /* If this is a variable assignment, we're done.  */
       if (p2)
@@ -1342,33 +1342,33 @@ static struct variable *
 do_define (char *name, enum variable_origin origin, struct ebuffer *ebuf)
 {
   struct variable *v;
-  enum variable_flavor flavor;
+  struct variable var;
   struct floc defstart;
   int nlevels = 1;
   unsigned int length = 100;
   char *definition = xmalloc (length);
   unsigned int idx = 0;
-  char *p, *var;
+  char *p, *n;
 
   defstart = ebuf->floc;
 
-  p = parse_variable_definition (name, &flavor);
+  p = parse_variable_definition (name, &var);
   if (p == NULL)
     /* No assignment token, so assume recursive.  */
-    flavor = f_recursive;
+    var.flavor = f_recursive;
   else
     {
-      if (*(next_token (p)) != '\0')
+      if (var.value[0] != '\0')
         error (&defstart, _("extraneous text after `define' directive"));
 
       /* Chop the string before the assignment token to get the name.  */
-      p[flavor == f_recursive ? -1 : -2] = '\0';
+      var.name[var.length] = '\0';
     }
 
   /* Expand the variable name and find the beginning (NAME) and end.  */
-  var = allocated_variable_expand (name);
-  name = next_token (var);
-  if (*name == '\0')
+  n = allocated_variable_expand (name);
+  name = next_token (n);
+  if (name[0] == '\0')
     fatal (&defstart, _("empty variable name"));
   p = name + strlen (name) - 1;
   while (p > name && isblank ((unsigned char)*p))
@@ -1439,9 +1439,10 @@ do_define (char *name, enum variable_origin origin, struct ebuffer *ebuf)
   else
     definition[idx - 1] = '\0';
 
-  v = do_variable_definition (&defstart, name, definition, origin, flavor, 0);
+  v = do_variable_definition (&defstart, name,
+                              definition, origin, var.flavor, 0);
   free (definition);
-  free (var);
+  free (n);
   return (v);
 }
 \f
@@ -2467,7 +2468,7 @@ readline (struct ebuffer *ebuf)
      w_colon        A colon
      w_dcolon       A double-colon
      w_semicolon    A semicolon
-     w_varassign    A variable assignment operator (=, :=, +=, ?=, or !=)
+     w_varassign    A variable assignment operator (=, :=, ::=, +=, ?=, or !=)
 
    Note that this function is only used when reading certain parts of the
    makefile.  Don't use it where special rules hold sway (RHS of a variable,
@@ -2506,7 +2507,13 @@ get_next_mword (char *buffer, char *delim, char **startp, unsigned int *length)
         {
         case ':':
           ++p;
-          wtype = w_dcolon;
+          if (p[1] != '=')
+            wtype = w_dcolon;
+          else
+            {
+              wtype = w_varassign;
+              ++p;
+            }
           break;
 
         case '=':
index 3e0643cdb7deec400db9e922640e559c1d598fa4..7ad4085b967bf89dbec7f3b3598d914da644f0b6 100644 (file)
@@ -1,5 +1,8 @@
 2012-01-29  Paul Smith  <psmith@gnu.org>
 
+       * scripts/variables/flavors: Add tests for ::=
+       * scripts/variables/define: Ditto
+
        * scripts/functions/file: Test the new $(file ...) function.
 
 2012-01-12  Paul Smith  <psmith@gnu.org>
index f91519e3276fa5c7178cabd5aa0c572c78167978..68d493b1b387273ac079a4f32547b88bc1ce910a 100644 (file)
@@ -30,6 +30,10 @@ define simple :=
 @echo $(FOO)
 endef
 
+define posix ::=
+@echo $(FOO)
+endef
+
 append = @echo a
 
 define append +=
@@ -49,10 +53,54 @@ FOO = there
 
 all: ; $(multi)
        $(simple)
+       $(posix)
+       $(append)
+       $(cond)
+',
+              '', "echo hi\nhi\nthere\nfoo\nfoo\na\nb\nfirst\n");
+
+# TEST 1a: Various new-style define/endef, with no spaces
+
+run_make_test('
+FOO = foo
+
+define multi=
+echo hi
+@echo $(FOO)
+endef # this is the end
+
+define simple:=
+@echo $(FOO)
+endef
+
+define posix::=
+@echo $(FOO)
+endef
+
+append = @echo a
+
+define append+=
+
+@echo b
+endef
+
+define cond?= # this is a conditional
+@echo first
+endef
+
+define cond?=
+@echo second
+endef
+
+FOO = there
+
+all: ; $(multi)
+       $(simple)
+       $(posix)
        $(append)
        $(cond)
 ',
-              '', "echo hi\nhi\nthere\nfoo\na\nb\nfirst\n");
+              '', "echo hi\nhi\nthere\nfoo\nfoo\na\nb\nfirst\n");
 
 # TEST 2: define in true section of conditional (containing conditional)
 
index 92feed6ec8a8368585d152880a379143dbad083b..ba133ea860d2fc204b3b75c54837da2e2dad328f 100644 (file)
@@ -73,4 +73,24 @@ all: ; @echo $(foo)
 ',
               '', "Hello\n");
 
+# TEST 6: Simple using POSIX syntax
+run_make_test('
+bar = Goodbye
+foo ::= $(bar)
+bar = ${ugh}
+ugh = Hello
+all: ; @echo $(foo)
+',
+              '', "Goodbye\n");
+
+# TEST 7: POSIX syntax no spaces
+run_make_test('
+bar = Goodbye
+foo::=$(bar)
+bar = ${ugh}
+ugh = Hello
+all: ; @echo $(foo)
+',
+              '', "Goodbye\n");
+
 1;
index a0eef9b69241c9c5dc32b9338eb7cdd2ceb696b4..839aa5a6b993cd60ff55b0ae559727cf9827e071 100644 (file)
@@ -1396,17 +1396,27 @@ do_variable_definition (const struct floc *flocp, const char *varname,
 \f
 /* Parse P (a null-terminated string) as a variable definition.
 
-   If it is not a variable definition, return NULL.
+   If it is not a variable definition, return NULL and the contents of *VAR
+   are undefined, except NAME is set to the first non-space character or NIL.
 
    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.  */
+   assignment token and set the following fields (only) of *VAR:
+    name   : name of the variable (ALWAYS SET) (NOT NUL-TERMINATED!)
+    length : length of the variable name
+    value  : value of the variable (nul-terminated)
+    flavor : flavor of the variable
+   Other values in *VAR are unchanged.
+  */
 
 char *
-parse_variable_definition (const char *p, enum variable_flavor *flavor)
+parse_variable_definition (const char *p, struct variable *var)
 {
   int wspace = 0;
+  const char *e = NULL;
 
   p = next_token (p);
+  var->name = (char *)p;
+  var->length = 0;
 
   while (1)
     {
@@ -1451,6 +1461,7 @@ parse_variable_definition (const char *p, enum variable_flavor *flavor)
       if (isblank ((unsigned char)c))
         {
           wspace = 1;
+          e = p - 1;
           p = next_token (p);
           c = *p;
           if (c == '\0')
@@ -1461,8 +1472,10 @@ parse_variable_definition (const char *p, enum variable_flavor *flavor)
 
       if (c == '=')
        {
-         *flavor = f_recursive;
-         return (char *)p;
+         var->flavor = f_recursive;
+          if (! e)
+            e = p - 1;
+         break;
        }
 
       /* Match assignment variants (:=, +=, ?=, !=)  */
@@ -1471,16 +1484,16 @@ parse_variable_definition (const char *p, enum variable_flavor *flavor)
           switch (c)
             {
               case ':':
-                *flavor = f_simple;
+                var->flavor = f_simple;
                 break;
               case '+':
-                *flavor = f_append;
+                var->flavor = f_append;
                 break;
               case '?':
-                *flavor = f_conditional;
+                var->flavor = f_conditional;
                 break;
               case '!':
-                *flavor = f_shell;
+                var->flavor = f_shell;
                 break;
               default:
                 /* If we skipped whitespace, non-assignments means no var.  */
@@ -1490,17 +1503,34 @@ parse_variable_definition (const char *p, enum variable_flavor *flavor)
                 /* Might be assignment, or might be $= or #=.  Check.  */
                 continue;
             }
-          return (char *)++p;
+          if (! e)
+            e = p - 1;
+          ++p;
+          break;
+        }
+
+      /* Check for POSIX ::= syntax  */
+      if (c == ':')
+        {
+          /* A colon other than :=/::= is not a variable defn.  */
+          if (*p != ':' || p[1] != '=')
+            return NULL;
+
+          /* POSIX allows ::= to be the same as GNU make's := */
+          var->flavor = f_simple;
+          if (! e)
+            e = p - 1;
+          p += 2;
+          break;
         }
-      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;
     }
 
+  var->length = e - var->name;
+  var->value = next_token (p);
   return (char *)p;
 }
 \f
@@ -1513,27 +1543,15 @@ parse_variable_definition (const char *p, enum variable_flavor *flavor)
 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)
+  if (!parse_variable_definition (line, v))
     return NULL;
 
-  end = line - (flavor == f_recursive ? 1 : 2);
-  while (end > beg && isblank ((unsigned char)end[-1]))
-    --end;
-  line = next_token (line);
-  v->value = line;
-  v->flavor = flavor;
-
   /* Expand the name, so "$(foo)bar = baz" works.  */
-  name = alloca (end - beg + 1);
-  memcpy (name, beg, end - beg);
-  name[end - beg] = '\0';
+  name = alloca (v->length + 1);
+  memcpy (name, v->name, v->length);
+  name[v->length] = '\0';
   v->name = allocated_variable_expand (name);
 
   if (v->name[0] == '\0')
index 1fd1b668eb87b7a50d1fd941ef18b8afc7a4a184..1fe3d1ecc78b1e61b051380c4dfd70fe5f25e223 100644 (file)
@@ -35,7 +35,7 @@ enum variable_origin
 enum variable_flavor
   {
     f_bogus,            /* Bogus (error) */
-    f_simple,           /* Simple definition (:=) */
+    f_simple,           /* Simple definition (:= or ::=) */
     f_recursive,        /* Recursive definition (=) */
     f_append,           /* Appending definition (+=) */
     f_conditional,      /* Conditional definition (?=) */
@@ -52,9 +52,9 @@ enum variable_flavor
 struct variable
   {
     char *name;                        /* Variable name.  */
-    int length;                        /* strlen (name) */
     char *value;               /* Variable value.  */
     struct floc fileinfo;       /* Where the variable was defined.  */
+    int length;                        /* strlen (name) */
     unsigned int recursive:1;  /* Gets recursively re-evaluated.  */
     unsigned int append:1;     /* Nonzero if an appending target-specific
                                    variable.  */
@@ -160,7 +160,7 @@ struct variable *do_variable_definition (const struct floc *flocp,
                                          enum variable_flavor flavor,
                                          int target_var);
 char *parse_variable_definition (const char *line,
-                                 enum variable_flavor *flavor);
+                                 struct variable *v);
 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,