- Enhance .POSIX to set -e when invoking shells, as demanded by a
authorPaul Smith <psmith@gnu.org>
Tue, 6 Jul 2010 06:37:42 +0000 (06:37 +0000)
committerPaul Smith <psmith@gnu.org>
Tue, 6 Jul 2010 06:37:42 +0000 (06:37 +0000)
  backward-incompatible change in the 2008 POSIX specification.
- Add the .SHELLFLAGS variable so people can choose their own shell flags.
- Add tests for this.
- Add documentation for this.

ChangeLog
NEWS
doc/make.texi
job.c
main.c
read.c
tests/ChangeLog
tests/scripts/targets/POSIX [new file with mode: 0644]
tests/scripts/variables/SHELL

index 7b9711b9b9a56b89f39135a05c144427f28ecccf..f77391ce36b936bdf85d546077475c0913dd3637 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,13 @@
+2010-07-06  Paul Smith  <psmith@gnu.org>
+
+       * main.c (main): Set a default value of "-c" for .SHELLFLAGS.
+       * NEWS: Mention the new behavior of .POSIX and the new .SHELLFLAGS
+       variable.
+       * job.c (construct_command_argv): Retrieve the .SHELLFLAGS value
+       and pass it to construct_command_argv_internal().
+       (construct_command_argv_internal): If .SHELLFLAGS is non-standard
+       use the slow path.  Use that value instead of hard-coded "-c".
+
 2010-07-05  Paul Smith  <psmith@gnu.org>
 
        * implicit.c (pattern_search): lastslash can be const.
diff --git a/NEWS b/NEWS
index f452686a5dad4e9de96aa97f1d719b95072bcbe4..b9577c9061b613d9a68815ddd614cc8405ccd73e 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -14,6 +14,15 @@ Version 3.81.90
 * Compiling GNU make now requires a conforming ISO C 1989 compiler and
   standard runtime library.
 
+* WARNING: Backward-incompatibility!
+  The POSIX standard for make was changed in the 2008 version in a
+  fundamentally incompatible way: make is required to invoke the shell as if
+  the '-e' flag were provided.  Because this would break many makefiles that
+  have been written to conform to the original text of the standard, the
+  default behavior of GNU make remains to invoke the shell with simply '-c'.
+  However, any makefile specifying the .POSIX special target will follow the
+  new POSIX standard and pass '-e' to the shell.  See also .SHELLFLAGS below.
+
 * WARNING: Backward-incompatibility!
   The '$?' variable now contains all prerequisites that caused the target to
   be considered out of date, even if they do not exist (previously only
@@ -58,6 +67,10 @@ Version 3.81.90
   set and reset at will; recipes will use the value active when they were
   first parsed.  To detect this feature check the value of $(.RECIPEPREFIX).
 
+* New special variable: .SHELLFLAGS allows you to change the options passed to
+  the shell when it invokes recipes.  By default the value will be "-c" (or
+  "-ec" if .POSIX is set).
+
 * 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
@@ -66,13 +79,17 @@ Version 3.81.90
 * New make directive: 'undefine' allows you to undefine a variable so
   that it appears as if it was never set. Both $(flavor) and $(origin)
   functions will return 'undefined' for such a variable. To detect this
-  feature search for 'undefine in the .FEATURES special variable.
+  feature search for 'undefine' in the .FEATURES special variable.
 
 * The parser for variable assignments has been enhanced to allow multiple
   modifiers ('export', 'override', 'private') 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.
 
+* The 'define' make directive now allows a variable assignment operator after
+  the variable name, to allow for simple, conditional, or appending multi-line
+  variable assignment.
+
 \f
 Version 3.81
 
@@ -109,7 +126,6 @@ Version 3.81
   of this SysV feature you will need to update them.
 
 * WARNING: Backward-incompatibility!
-
   In order to comply with POSIX, the way in which GNU make processes
   backslash-newline sequences in recipes has changed.  If your makefiles
   use backslash-newline sequences inside of single-quoted strings in
index 7187e1954e6585254ff4d034d15cf624f83ffa5b..35a40dd08db140306c006eb58df75de60ba51169 100644 (file)
@@ -2873,6 +2873,21 @@ of @code{make} will be run serially, even if the @samp{-j} option is
 given.  Any recursively invoked @code{make} command will still run
 recipes in parallel (unless its makefile also contains this target).
 Any prerequisites on this target are ignored.
+
+@findex .POSIX
+@item .POSIX
+@cindex POSIX-conforming mode, setting
+
+If @code{.POSIX} is mentioned as a target, then the makefile will be
+parsed and run in POSIX-conforming mode.  This does @emph{not} mean
+that only POSIX-conforming makefiles will be accepted: all advanced
+GNU @code{make} features are still available.  Rather, this target
+causes @code{make} to behave as required by POSIX in those areas
+where @code{make}'s default behavior differs.
+
+In particular, if this target is mentioned then recipes will be
+invoked as if the shell had been passed the @code{-e} flag: the first
+failing command in a recipe will cause the recipe to fail immediately.
 @end table
 
 Any defined implicit rule suffix also counts as a special target if it
@@ -3713,11 +3728,16 @@ truncated, at least).
 @subsection Choosing the Shell
 @cindex shell, choosing the
 @cindex @code{SHELL}, value of
+@cindex @code{.SHELLFLAGS}, value of
 
 @vindex SHELL
+@vindex .SHELLFLAGS
 The program used as the shell is taken from the variable @code{SHELL}.
 If this variable is not set in your makefile, the program
-@file{/bin/sh} is used as the shell.
+@file{/bin/sh} is used as the shell.  The argument(s) passed to the
+shell are taken from the variable @code{.SHELLFLAGS}.  The default
+value of @code{.SHELLFLAGS} is @code{-c} normally, or @code{-ec} in
+POSIX-conforming mode.
 
 @cindex environment, @code{SHELL} in
 Unlike most variables, the variable @code{SHELL} is never set from the
diff --git a/job.c b/job.c
index 03d8a83abce7c5eb5d7d4e31453e2982cdc17819..8e83a477fc93ab5be9327ae50de0254491b44582 100644 (file)
--- a/job.c
+++ b/job.c
@@ -2228,7 +2228,7 @@ void clean_tmp (void)
 
 static char **
 construct_command_argv_internal (char *line, char **restp, char *shell,
-                                 char *ifs, int flags,
+                                 char *shellflags, char *ifs, int flags,
                                 char **batch_filename_ptr)
 {
 #ifdef __MSDOS__
@@ -2436,6 +2436,12 @@ construct_command_argv_internal (char *line, char **restp, char *shell,
       if (*ap != ' ' && *ap != '\t' && *ap != '\n')
        goto slow;
 
+  if (shellflags != 0)
+    if (shellflags[0] != '-'
+        || ((shellflags[1] != 'c' || shellflags[2] != '\0')
+            && (shellflags[1] != 'e' || shellflags[2] != 'c' || shellflags[3] != '\0')))
+      goto slow;
+
   i = strlen (line) + 1;
 
   /* More than 1 arg per character is impossible.  */
@@ -2717,34 +2723,33 @@ construct_command_argv_internal (char *line, char **restp, char *shell,
   if (*line == '\0')
     return 0;
 #endif /* WINDOWS32 */
+
   {
     /* SHELL may be a multi-word command.  Construct a command line
-       "SHELL -c LINE", with all special chars in LINE escaped.
+       "$(SHELL) $(.SHELLFLAGS) LINE", with all special chars in LINE escaped.
        Then recurse, expanding this command line to get the final
        argument list.  */
 
     unsigned int shell_len = strlen (shell);
-#ifndef VMS
-    static char minus_c[] = " -c ";
-#else
-    static char minus_c[] = "";
-#endif
     unsigned int line_len = strlen (line);
+    unsigned int sflags_len = strlen (shellflags);
 
-    char *new_line = alloca (shell_len + (sizeof (minus_c)-1)
+    char *new_line = alloca (shell_len + 1 + sflags_len + 1
                              + (line_len*2) + 1);
     char *command_ptr = NULL; /* used for batch_mode_shell mode */
 
 # ifdef __EMX__ /* is this necessary? */
     if (!unixy_shell)
-      minus_c[1] = '/'; /* " /c " */
+      shellflags[0] = '/'; /* "/c" */
 # endif
 
     ap = new_line;
     memcpy (ap, shell, shell_len);
     ap += shell_len;
-    memcpy (ap, minus_c, sizeof (minus_c) - 1);
-    ap += sizeof (minus_c) - 1;
+    *(ap++) = ' ';
+    memcpy (ap, shellflags, sflags_len);
+    ap += sflags_len;
+    *(ap++) = ' ';
     command_ptr = ap;
     for (p = line; *p != '\0'; ++p)
       {
@@ -2794,7 +2799,7 @@ construct_command_argv_internal (char *line, char **restp, char *shell,
 #endif
        *ap++ = *p;
       }
-    if (ap == new_line + shell_len + sizeof (minus_c) - 1)
+    if (ap == new_line + shell_len + sflags_len + 2)
       /* Line was empty.  */
       return 0;
     *ap = '\0';
@@ -2846,8 +2851,10 @@ construct_command_argv_internal (char *line, char **restp, char *shell,
       new_argv[2] = NULL;
     } else
 #endif /* WINDOWS32 */
+
     if (unixy_shell)
-      new_argv = construct_command_argv_internal (new_line, 0, 0, 0, flags, 0);
+      new_argv = construct_command_argv_internal (new_line, 0, 0, 0, 0, flags, 0);
+
 #ifdef __EMX__
     else if (!unixy_shell)
       {
@@ -2923,10 +2930,10 @@ construct_command_argv_internal (char *line, char **restp, char *shell,
            instead of recursively calling ourselves, because we
            cannot backslash-escape the special characters (see above).  */
         new_argv = xmalloc (sizeof (char *));
-        line_len = strlen (new_line) - shell_len - sizeof (minus_c) + 1;
+        line_len = strlen (new_line) - shell_len - sflags_len - 2;
         new_argv[0] = xmalloc (line_len + 1);
         strncpy (new_argv[0],
-                 new_line + shell_len + sizeof (minus_c) - 1, line_len);
+                 new_line + shell_len + sflags_len + 2, line_len);
         new_argv[0][line_len] = '\0';
       }
 #else
@@ -2958,7 +2965,7 @@ char **
 construct_command_argv (char *line, char **restp, struct file *file,
                         int cmd_flags, char **batch_filename_ptr)
 {
-  char *shell, *ifs;
+  char *shell, *ifs, *shellflags;
   char **argv;
 
 #ifdef VMS
@@ -3063,15 +3070,17 @@ construct_command_argv (char *line, char **restp, struct file *file,
     }
 #endif /* __EMX__ */
 
+    shellflags = allocated_variable_expand_for_file ("$(.SHELLFLAGS)", file);
     ifs = allocated_variable_expand_for_file ("$(IFS)", file);
 
     warn_undefined_variables_flag = save;
   }
 
-  argv = construct_command_argv_internal (line, restp, shell, ifs,
+  argv = construct_command_argv_internal (line, restp, shell, shellflags, ifs,
                                           cmd_flags, batch_filename_ptr);
 
   free (shell);
+  free (shellflags);
   free (ifs);
 #endif /* !VMS */
   return argv;
diff --git a/main.c b/main.c
index 20b445a085cfc5f3bb0704952a7ed1fafc3954b6..c972e65e08ef7d5394e4513baa7f3f35eaf9e200 100644 (file)
--- a/main.c
+++ b/main.c
@@ -1123,26 +1123,27 @@ main (int argc, char **argv, char **envp)
   define_variable_cname (".VARIABLES", "", o_default, 0)->special = 1;
   /* define_variable_cname (".TARGETS", "", o_default, 0)->special = 1; */
   define_variable_cname (".RECIPEPREFIX", "", o_default, 0)->special = 1;
+  define_variable_cname (".SHELLFLAGS", "-c", o_default, 0);
 
   /* Set up .FEATURES
      We must do this in multiple calls because define_variable_cname() is
      a macro and some compilers (MSVC) don't like conditionals in macros.  */
-  define_variable_cname (".FEATURES",
-                         "target-specific order-only second-expansion else-if"
-                         " shortest-stem undefine",
-                         o_default, 0);
+  {
+    const char *features = "target-specific order-only second-expansion"
+                           " else-if shortest-stem undefine"
 #ifndef NO_ARCHIVES
-  do_variable_definition (NILF, ".FEATURES", "archives",
-                          o_default, f_append, 0);
+                           " archives"
 #endif
 #ifdef MAKE_JOBSERVER
-  do_variable_definition (NILF, ".FEATURES", "jobserver",
-                          o_default, f_append, 0);
+                           " jobserver"
 #endif
 #ifdef MAKE_SYMLINKS
-  do_variable_definition (NILF, ".FEATURES", "check-symlink",
-                          o_default, f_append, 0);
+                           " check-symlink"
 #endif
+                           ;
+
+    define_variable_cname (".FEATURES", features, o_default, 0);
+  }
 
   /* Read in variables from the environment.  It is important that this be
      done before $(MAKE) is figured out so its definitions will not be
diff --git a/read.c b/read.c
index 591f1f37ea243cf31cf165693d000a16e5ecd086..1e8d2f376b76c44d722f380e17af87213e07a40e 100644 (file)
--- a/read.c
+++ b/read.c
@@ -1958,7 +1958,10 @@ record_files (struct nameseq *filenames, const char *pattern,
       /* Check for special targets.  Do it here instead of, say, snap_deps()
          so that we can immediately use the value.  */
       if (streq (name, ".POSIX"))
-        posix_pedantic = 1;
+        {
+          posix_pedantic = 1;
+          define_variable_cname (".SHELLFLAGS", "-ec", o_default, 0);
+        }
       else if (streq (name, ".SECONDEXPANSION"))
         second_expansion = 1;
 
index c8f3aab2d2531d74a3014a8c463bd25aa94f512d..a61142260fee324fd6f1ba18476b7d2d7c3d6983 100644 (file)
@@ -1,3 +1,10 @@
+2010-07-06  Paul Smith  <psmith@gnu.org>
+
+       * scripts/variables/SHELL: Test the new .SHELLFLAGS variable.
+
+       * scripts/targets/POSIX: New file.  Test the .POSIX special target.
+       Verify that enabling .POSIX changes the shell flags to set -e.
+
 2010-07-01  Paul Smith  <psmith@gnu.org>
 
        * scripts/features/recursion: Add a space to separate command-line
diff --git a/tests/scripts/targets/POSIX b/tests/scripts/targets/POSIX
new file mode 100644 (file)
index 0000000..662c16d
--- /dev/null
@@ -0,0 +1,26 @@
+#                                                                    -*-perl-*-
+
+$description = "Test the behaviour of the .PHONY target.";
+
+$details = "";
+
+
+# Ensure turning on .POSIX enables the -e flag for the shell
+
+run_make_test(q!
+.POSIX:
+all: ; @false; true
+!,
+              '', "#MAKE#: *** [all] Error 1\n", 512);
+
+# User settings must override .POSIX
+
+run_make_test(q!
+.SHELLFLAGS = -xc
+.POSIX:
+all: ; @false; true
+!,
+              '', "+ false\n+ true\n");
+
+# This tells the test driver that the perl test script executed properly.
+1;
index a30354033e53a24afcdef3597213fb982d876d28..69871d2d2f6850f77031121273bcaafbe7b4c076 100644 (file)
@@ -56,4 +56,18 @@ two: export SHELL := /./$mshell\n".'
 one two:;@echo "$@: $(SHELL) $$SHELL"
 ', '', "two: /./$mshell /./$mshell\none: /././$mshell $mshell\n");
 
+# Test .SHELLFLAGS
+
+run_make_test(q!
+.SHELLFLAGS = -xc
+all: ; @true
+!,
+              '', "+ true\n");
+
+run_make_test(q!
+.SHELLFLAGS = -xec
+all: ; @true; false; true
+!,
+              '', "+ true\n+ false\n#MAKE#: *** [all] Error 1\n", 512);
+
 1;