URL:
  <http://savannah.gnu.org/bugs/?31430>

                 Summary: Add support for the BSD "shell assignment" operator
(!=)
                 Project: make
            Submitted by: None
            Submitted on: Sun 24 Oct 2010 03:53:40 AM UTC
                Severity: 3 - Normal
              Item Group: Enhancement
                  Status: None
                 Privacy: Public
             Assigned to: None
             Open/Closed: Open
         Discussion Lock: Any
       Component Version: CVS
        Operating System: Any
           Fixed Release: None
           Triage Status: None

    _______________________________________________________

Details:

GNU make shell assignment patch by David A. Wheeler, 2010-10-23

I propose adding support for the BSD "shell assignment" operator, and I've
attached a patch to add this support.

Many "make" implementations support the shell assignment operator "!=".  This
includes the "make" of FreeBSD, OpenBSD, and NetBSD:
http://www.freebsd.org/cgi/man.cgi?query=make&sektion=1
http://www.openbsd.org/cgi-bin/man.cgi?query=make&apropos=0&sektion=0&manpath=OpenBSD+Current&arch=i386&format=html
http://netbsd.gw.com/cgi-bin/man-cgi?make+1+NetBSD-current
It is also supported by the "pmake" program of Adam de Boor:
http://www.freebsd.org/doc/en/books/pmake/variables.html

The semantics are not complicated.  Given:
  string1 != string2
The macro named string1 is a recursively-defined variable, with a value
determined by the following process: First, string2 is *immediately*
evaluated. Second, that resulting evaluation is passed to the shell for
execution. Third, that result is modified removing a single trailing newline
(if there is one), and then replacing all remaining newlines with spaces.

GNU make can't currently handle BSD makefiles with "!=", since it doesn't
have the operator.  It's not even easy to simulate with the current GNU make
constructs.  In many cases, this GNU make construct will have the same final
result:
  VAR = ${system echo "$(STUFF)" | sed -e 's/://'}
but notice that $(VAR) is re-evaluated *EVERY TIME*, creating efficiency
problems, and the change in result may be surprising.  GNU make's ":=" doesn't
quite do it either:
  VAR := ${system echo "$(STUFF)" | sed -e 's/://'}
In this case, in GNU make it's executed only once, while with BSD make the
result is recursively evaluated.  Thus, in GNU make using := you cannot insert
$(VAR2) as the output of the shell script and have it be re-evaluated. 
Nothing in GNU make does this exactly... so those wanting this functionality
will want it added.

The following patch adds this capability to GNU make, and includes test
cases.  This patch works against GNU make per its CVS repository of
2010-10-23.

This patch is released under either GNU GPL version 2 or later, or
the BSD 3-clause license.  Thus, it's compatible with lots of licenses
including the current and older licenses used by GNU make.

This is also available from:
 http://www.dwheeler.com/misc/shell-assignment.patch


==================================================

--- ./read.c.orig       2010-08-13 22:50:14.000000000 -0400
+++ ./read.c    2010-10-23 18:52:42.285751829 -0400
@@ -2470,7 +2470,7 @@
      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,
@@ -2521,6 +2521,7 @@
 
     case '+':
     case '?':
+    case '!':
       if (*p == '=')
         {
           ++p;
@@ -2540,7 +2541,7 @@
 
   /* This is some non-operator word.  A word consists of the longest
      string of characters that doesn't contain whitespace, one of [:=#],
-     or [?+]=, or one of the chars in the DELIM string.  */
+     or [?+!]=, or one of the chars in the DELIM string.  */
 
   /* We start out assuming a static word; if we see a variable we'll
      adjust our assumptions then.  */
--- ./tests/scripts/features/shell_assignment.orig      2010-10-23
23:14:22.797322744 -0400
+++ ./tests/scripts/features/shell_assignment   2010-10-23 23:11:33.714947475
-0400
@@ -0,0 +1,48 @@
+#                                                                   
-*-perl-*-
+
+$description = "Test BSD-style shell assignments (VAR != VAL) for
variables.";
+
+$details = "";
+
+# TEST 0: Basic shell assignment (!=).
+
+run_make_test('
+.POSIX:
+
+demo1!=printf \'  1   2 3\n4\n\n5 \n \n 6\n\n\n\n\'
+demo2 != printf \'7 8\n \'
+demo3 != printf \'$$(demo2)\'
+all: ; @echo "<$(demo1)> <$(demo2)> <$(demo3)>"
+',
+              '', "<  1   2 3 4  5     6   > <7 8  > <7 8  >\n");
+
+# TEST 1: Handle '#' the same way as BSD make
+
+run_make_test('
+foo1!=echo bar#baz
+hash != printf \'\043\'
+foo2!= echo "bar$(hash)baz"
+
+all: ; @echo "<$(foo1)> <$(hash)> <$(foo2)>"
+',
+              '', "<bar> <#> <bar#baz>\n");
+
+# TEST 2: shell assignment variables (from !=) should be recursive.
+# Note that variables are re-evaluated later, so the shell can output
+# a value like $(XYZZY) as part of !=.  The $(XYZZY) will be EVALUATED
+# when the value containing it is evaluated.  On the negative side, this
+# means if you don't want this, you need to escape dollar signs as $$.
+# On the positive side, it means that shell programs can output macros
+# that are then evaluated as they are traditionally evaluated.. and that
+# you can use traditional macro evaluation semantics to implement !=.
+
+run_make_test('
+XYZZY = fiddle-dee-dee
+dollar = $$
+VAR3 != printf \'%s\' \'$(dollar)(XYZZY)\'
+
+all: ; @echo "<$(VAR3)>"
+',
+              '', "<fiddle-dee-dee>\n");
+
+1;
--- ./variable.c.orig   2010-08-27 11:01:42.000000000 -0400
+++ ./variable.c        2010-10-23 22:45:33.899545348 -0400
@@ -1111,6 +1111,52 @@
   return var;
 }
 
+/* Given a string, shell-execute it and return a malloc'ed string of the
+ * result. If it fails, returns NULL. */
+
+char *
+shell_result (const char *p)
+{
+  size_t length = 0;     /* Length of actual result */
+  size_t alloc_size = 0; /* size of buffer we've allocated */
+  FILE *result_file;
+  char *buffer = NULL;   /* We read our results into this */
+  size_t length_read;
+  unsigned i;
+
+  result_file = popen(p, "r");
+  if (!result_file)
+    return NULL;
+
+  while (!feof(result_file) && !ferror(result_file)) {
+    if (length >= alloc_size) { /* Need to (re)allocate a result buffer */
+      alloc_size += 8192; /* This size amount is arbitrary */
+      buffer = xrealloc(buffer, alloc_size); /* No return on fail */
+    }
+
+    length_read = fread(buffer + length, (size_t) 1, (size_t)
+                     (size_t) (alloc_size - length), result_file);
+    /* Defend against fread implementations that incorrectly return -1: */
+    if (length_read != (size_t) -1)
+      length += length_read;
+  }
+  pclose(result_file);
+
+  /* Per http://austingroupbugs.net/view.php?id=337 the semantics are:
+     result is modified removing a single trailing newline (if there is
one),
+     and then replacing all remaining newlines with spaces." */
+  if (length > 0 && buffer[length-1]=='\n') {
+    buffer[length-1] = '\0';
+    length--;
+  }
+  for (i = 0; i < length; i++)
+    if (buffer[i] == '\n')
+      buffer[i] = ' ';
+
+  return buffer;
+}
+
+
 /* Given a variable, a value, and a flavor, define the variable.
    See the try_variable_definition() function for details on the parameters.
*/
 
@@ -1120,7 +1166,9 @@
                         enum variable_flavor flavor, int target_var)
 {
   const char *p;
+  char *q;
   char *alloc_value = NULL;
+  char *alloc_value2 = NULL;
   struct variable *v;
   int append = 0;
   int conditional = 0;
@@ -1140,6 +1188,11 @@
          target-specific variable.  */
       p = alloc_value = allocated_variable_expand (value);
       break;
+    case f_shell:
+      q = alloc_value = allocated_variable_expand (value);
+      p = alloc_value2 = shell_result (q);
+      flavor = f_recursive;
+      break;
     case f_conditional:
       /* A conditional variable definition "var ?= value".
          The value is set IFF the variable is not defined yet. */
@@ -1357,6 +1410,8 @@
 
   if (alloc_value)
     free (alloc_value);
+  if (alloc_value2)
+    free (alloc_value2);
 
   return v->special ? set_special_var (v) : v;
 }
@@ -1432,7 +1487,7 @@
          return (char *)p;
        }
 
-      /* Match assignment variants (:=, +=, ?=)  */
+      /* Match assignment variants (:=, +=, ?=, !=)  */
       if (*p == '=')
         {
           switch (c)
@@ -1446,6 +1501,9 @@
               case '?':
                 *flavor = f_conditional;
                 break;
+              case '!':
+                *flavor = f_shell;
+                break;
               default:
                 /* If we skipped whitespace, non-assignments means no var. 
*/
                 if (wspace)
--- ./variable.h.orig   2010-07-12 21:20:43.000000000 -0400
+++ ./variable.h        2010-10-23 11:08:36.036623200 -0400
@@ -38,7 +38,8 @@
     f_simple,           /* Simple definition (:=) */
     f_recursive,        /* Recursive definition (=) */
     f_append,           /* Appending definition (+=) */
-    f_conditional       /* Conditional definition (?=) */
+    f_conditional,      /* Conditional definition (?=) */
+    f_shell             /* Shell definition (!=) */
   };
 
 /* Structure that represents one variable definition.




    _______________________________________________________

File Attachments:


-------------------------------------------------------
Date: Sun 24 Oct 2010 03:53:40 AM UTC  Name: shell-assignment.patch  Size:
9kB   By: None
I entered this earlier under &quot;support&quot;, but it probably belongs
here instead
<http://savannah.gnu.org/bugs/download.php?file_id=21781>

    _______________________________________________________

Reply to this item at:

  <http://savannah.gnu.org/bugs/?31430>

_______________________________________________
  Message sent via/by Savannah
  http://savannah.gnu.org/


_______________________________________________
Bug-make mailing list
Bug-make@gnu.org
http://lists.gnu.org/mailman/listinfo/bug-make

Reply via email to