Original patch by David Boyce. Modified by Paul Smith.
+2010-07-12 Paul Smith <psmith@gnu.org>
+
+ Integrated new .ONESHELL feature.
+ Patch by David Boyce <dsb@boyski.com>. Modified by me.
+
+ * NEWS: Add a note about the new feature.
+ * job.c (is_bourne_compatible_shell): Determine whether we're
+ using a standard POSIX shell or not.
+ (start_job_command): Accept '-ec' as POSIX shell flags.
+ (construct_command_argv_internal): If one_shell is set and we are
+ using a POSIX shell, remove "interior" prefix characters such as
+ "@", "+", "-". Also treat "\n" as a special character when
+ choosing the slow path, if ONESHELL is set.
+ * job.h (is_bourne_compatible_argv): Define the new function.
+
+ * make.h (one_shell): New global variable to remember setting.
+ * main.c: Declare it.
+ * read.c (record_files): Set it.
+ * commands.c (chop_commands): If one_shell is set, don't chop
+ commands into multiple lines; just keep one line.
+
2010-07-09 Eli Zaretskii <eliz@gnu.org>
* w32/subproc/sub_proc.c: Include stdint.h.
Windows builds that don't use GCC.
Savannah bug #27809. Patch by Ozkan Sezer <sezeroz@gmail.com>
+2010-07-07 Paul Smith <psmith@gnu.org>
+
+ * configure.in: Bump to a new prerelease version 3.81.91.
+
2010-07-06 Paul Smith <psmith@gnu.org>
* main.c (main): Set a default value of "-c" for .SHELLFLAGS.
the shell when it invokes recipes. By default the value will be "-c" (or
"-ec" if .POSIX is set).
+* New special target: .ONESHELL instructs make to invoke a single instance of
+ the shell and provide it with the entire recipe, regardless of how many
+ lines it contains. As a special feature to allow more straightforward
+ conversion of makefiles to use .ONESHELL, any recipe line control characters
+ ('@', '+', or '-') will be removed from the second and subsequent recipe
+ lines. This happens _only_ if the SHELL value is deemed to be a standard
+ POSIX-style shell. If not, then no interior line control characters are
+ removed (as they may be part of the scripting language used with the
+ alternate SHELL).
+
* 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
void
chop_commands (struct commands *cmds)
{
- const char *p;
unsigned int nlines, idx;
char **lines;
if (!cmds || cmds->command_lines != 0)
return;
- /* Chop CMDS->commands up into lines in CMDS->command_lines.
- Also set the corresponding CMDS->lines_flags elements,
- and the CMDS->any_recurse flag. */
+ /* Chop CMDS->commands up into lines in CMDS->command_lines. */
- nlines = 5;
- lines = xmalloc (5 * sizeof (char *));
- idx = 0;
- p = cmds->commands;
- while (*p != '\0')
+ if (one_shell)
{
- const char *end = p;
- find_end:;
- end = strchr (end, '\n');
- if (end == 0)
- end = p + strlen (p);
- else if (end > p && end[-1] == '\\')
+ int l = strlen (cmds->commands);
+
+ nlines = 1;
+ lines = xmalloc (nlines * sizeof (char *));
+ lines[0] = xstrdup (cmds->commands);
+
+ /* Strip the trailing newline. */
+ if (l > 0 && lines[0][l-1] == '\n')
+ lines[0][l-1] = '\0';
+ }
+ else
+ {
+ const char *p;
+
+ nlines = 5;
+ lines = xmalloc (nlines * sizeof (char *));
+ idx = 0;
+ p = cmds->commands;
+ while (*p != '\0')
{
- int backslash = 1;
- const char *b;
- for (b = end - 2; b >= p && *b == '\\'; --b)
- backslash = !backslash;
- if (backslash)
+ const char *end = p;
+ find_end:;
+ end = strchr (end, '\n');
+ if (end == 0)
+ end = p + strlen (p);
+ else if (end > p && end[-1] == '\\')
+ {
+ int backslash = 1;
+ const char *b;
+ for (b = end - 2; b >= p && *b == '\\'; --b)
+ backslash = !backslash;
+ if (backslash)
+ {
+ ++end;
+ goto find_end;
+ }
+ }
+
+ if (idx == nlines)
{
- ++end;
- goto find_end;
+ nlines += 2;
+ lines = xrealloc (lines, nlines * sizeof (char *));
}
+ lines[idx++] = xstrndup (p, end - p);
+ p = end;
+ if (*p != '\0')
+ ++p;
}
- if (idx == nlines)
+ if (idx != nlines)
{
- nlines += 2;
+ nlines = idx;
lines = xrealloc (lines, nlines * sizeof (char *));
}
- lines[idx++] = xstrndup (p, end - p);
- p = end;
- if (*p != '\0')
- ++p;
}
- if (idx != nlines)
- {
- nlines = idx;
- lines = xrealloc (lines, nlines * sizeof (char *));
- }
+ /* Finally, set the corresponding CMDS->lines_flags elements and the
+ CMDS->any_recurse flag. */
cmds->ncommand_lines = nlines;
cmds->command_lines = lines;
cmds->any_recurse = 0;
cmds->lines_flags = xmalloc (nlines);
+
for (idx = 0; idx < nlines; ++idx)
{
int flags = 0;
+ const char *p = lines[idx];
- for (p = lines[idx];
- isblank ((unsigned char)*p) || *p == '-' || *p == '@' || *p == '+';
- ++p)
- switch (*p)
+ while (isblank (*p) || *p == '-' || *p == '@' || *p == '+')
+ switch (*(p++))
{
case '+':
flags |= COMMANDS_RECURSE;
# You should have received a copy of the GNU General Public License along with
# this program. If not, see <http://www.gnu.org/licenses/>.
-AC_INIT([GNU make],[3.81.90],[bug-make@gnu.org])
+AC_INIT([GNU make],[3.81.91],[bug-make@gnu.org])
AC_PREREQ(2.59)
AC_REVISION([[$Id$]])
}
#endif /* __EMX__ */
+/* determines whether path looks to be a Bourne-like shell. */
+int
+is_bourne_compatible_shell (const char *path)
+{
+ /* list of known unix (Bourne-like) shells */
+ const char *unix_shells[] = {
+ "sh",
+ "bash",
+ "ksh",
+ "rksh",
+ "zsh",
+ "ash",
+ "dash",
+ NULL
+ };
+ unsigned i, len;
+
+ /* find the rightmost '/' or '\\' */
+ const char *name = strrchr (path, '/');
+ char *p = strrchr (path, '\\');
+
+ if (name && p) /* take the max */
+ name = (name > p) ? name : p;
+ else if (p) /* name must be 0 */
+ name = p;
+ else if (!name) /* name and p must be 0 */
+ name = path;
+
+ if (*name == '/' || *name == '\\') name++;
+
+ /* this should be able to deal with extensions on Windows-like systems */
+ for (i = 0; unix_shells[i] != NULL; i++) {
+ len = strlen(unix_shells[i]);
+#if defined(WINDOWS32) || defined(__MSDOS__)
+ if ((strncasecmp (name, unix_shells[i], len) == 0) &&
+ (strlen(name) >= len && (name[len] == '\0' || name[len] == '.')))
+#else
+ if ((strncmp (name, unix_shells[i], len) == 0) &&
+ (strlen(name) >= len && name[len] == '\0'))
+#endif
+ return 1; /* a known unix-style shell */
+ }
+
+ /* if not on the list, assume it's not a Bourne-like shell */
+ return 0;
+}
+
\f
/* Write an error message describing the exit status given in
EXIT_CODE, EXIT_SIG, and COREDUMP, for the target TARGET_NAME.
#if defined __MSDOS__ || defined (__EMX__)
unixy_shell /* the test is complicated and we already did it */
#else
- (argv[0] && !strcmp (argv[0], "/bin/sh"))
+ (argv[0] && is_bourne_compatible_shell(argv[0]))
#endif
- && (argv[1]
- && argv[1][0] == '-' && argv[1][1] == 'c' && argv[1][2] == '\0')
+ && (argv[1] && argv[1][0] == '-'
+ &&
+ ((argv[1][1] == 'c' && argv[1][2] == '\0')
+ ||
+ (argv[1][1] == 'e' && argv[1][2] == 'c' && argv[1][3] == '\0')))
&& (argv[2] && argv[2][0] == ':' && argv[2][1] == '\0')
&& argv[3] == NULL)
{
else if (strchr (sh_chars, *p) != 0)
/* Not inside a string, but it's a special char. */
goto slow;
+ else if (one_shell && *p == '\n')
+ /* In .ONESHELL mode \n is a separator like ; or && */
+ goto slow;
#ifdef __MSDOS__
else if (*p == '.' && p[1] == '.' && p[2] == '.' && p[3] != '.')
/* `...' is a wildcard in DJGPP. */
unsigned int shell_len = strlen (shell);
unsigned int line_len = strlen (line);
unsigned int sflags_len = strlen (shellflags);
-
- char *new_line = alloca (shell_len + 1 + sflags_len + 1
- + (line_len*2) + 1);
char *command_ptr = NULL; /* used for batch_mode_shell mode */
+ char *new_line;
# ifdef __EMX__ /* is this necessary? */
if (!unixy_shell)
shellflags[0] = '/'; /* "/c" */
# endif
+ /* In .ONESHELL mode we are allowed to throw the entire current
+ recipe string at a single shell and trust that the user
+ has configured the shell and shell flags, and formatted
+ the string, appropriately. */
+ if (one_shell)
+ {
+ /* If the shell is Bourne compatible, we must remove and ignore
+ interior special chars [@+-] because they're meaningless to
+ the shell itself. If, however, we're in .ONESHELL mode and
+ have changed SHELL to something non-standard, we should
+ leave those alone because they could be part of the
+ script. In this case we must also leave in place
+ any leading [@+-] for the same reason. */
+
+ /* Remove and ignore interior prefix chars [@+-] because they're
+ meaningless given a single shell. */
+#if defined __MSDOS__ || defined (__EMX__)
+ if (unixy_shell) /* the test is complicated and we already did it */
+#else
+ if (is_bourne_compatible_shell(shell))
+#endif
+ {
+ const char *f = line;
+ char *t = line;
+
+ /* Copy the recipe, removing and ignoring interior prefix chars
+ [@+-]: they're meaningless in .ONESHELL mode. */
+ while (f[0] != '\0')
+ {
+ int esc = 0;
+
+ /* This is the start of a new recipe line.
+ Skip whitespace and prefix characters. */
+ while (isblank (*f) || *f == '-' || *f == '@' || *f == '+')
+ ++f;
+
+ /* Copy until we get to the next logical recipe line. */
+ while (*f != '\0')
+ {
+ *(t++) = *(f++);
+ if (f[-1] == '\\')
+ esc = !esc;
+ else
+ {
+ /* On unescaped newline, we're done with this line. */
+ if (f[-1] == '\n' && ! esc)
+ break;
+
+ /* Something else: reset the escape sequence. */
+ esc = 0;
+ }
+ }
+ }
+ *t = '\0';
+ }
+
+ new_argv = xmalloc (4 * sizeof (char *));
+ new_argv[0] = xstrdup(shell);
+ new_argv[1] = xstrdup(shellflags);
+ new_argv[2] = line;
+ new_argv[3] = NULL;
+ return new_argv;
+ }
+
+ new_line = alloca (shell_len + 1 + sflags_len + 1
+ + (line_len*2) + 1);
ap = new_line;
memcpy (ap, shell, shell_len);
ap += shell_len;
return fd;
}
-#endif /* !HAPE_DUP2 && !_AMIGA */
+#endif /* !HAVE_DUP2 && !_AMIGA */
/* On VMS systems, include special VMS functions. */
extern struct child *children;
+int is_bourne_compatible_shell(const char *path);
void new_job (struct file *file);
void reap_children (int block, int err);
void start_waiting_jobs (void);
int second_expansion;
+/* Nonzero if we have seen the '.ONESHELL' target.
+ This causes the entire recipe to be handed to SHELL
+ as a single string, potentially containing newlines. */
+
+int one_shell;
+
/* Nonzero if we have seen the `.NOTPARALLEL' target.
This turns off parallel builds for this invocation of make. */
extern int print_version_flag, print_directory_flag, check_symlink_flag;
extern int warn_undefined_variables_flag, posix_pedantic, not_parallel;
extern int second_expansion, clock_skew_detected, rebuilding_makefiles;
+extern int one_shell;
/* can we run commands via 'sh -c xxx' or must we use batch files? */
extern int batch_mode_shell;
}
else if (streq (name, ".SECONDEXPANSION"))
second_expansion = 1;
+#if !defined(WINDOWS32) && !defined (__MSDOS__) && !defined (__EMX__)
+ else if (streq (name, ".ONESHELL"))
+ one_shell = 1;
+#endif
/* If this is a static pattern rule:
`targets: target%pattern: prereq%pattern; recipe',
+2010-07-12 Paul Smith <psmith@gnu.org>
+
+ * test_driver.pl: Add a new $perl_name containing the path to Perl.
+ * run_make_tests.pl (run_make_test): Replace the special string
+ #PERL# in a makefile etc. with the path the Perl executable so
+ makefiles can use it.
+
+ * scripts/targets/ONESHELL: Add a new set of regression tests for
+ the .ONESHELL feature.
+
2010-07-06 Paul Smith <psmith@gnu.org>
* scripts/variables/SHELL: Test the new .SHELLFLAGS variable.
$makestring =~ s/#MAKEFILE#/$makefile/g;
$makestring =~ s/#MAKEPATH#/$mkpath/g;
$makestring =~ s/#MAKE#/$make_name/g;
+ $makestring =~ s/#PERL#/$perl_name/g;
$makestring =~ s/#PWD#/$pwd/g;
# Populate the makefile!
$answer =~ s/#MAKEFILE#/$makefile/g;
$answer =~ s/#MAKEPATH#/$mkpath/g;
$answer =~ s/#MAKE#/$make_name/g;
+ $answer =~ s/#PERL#/$perl_name/g;
$answer =~ s/#PWD#/$pwd/g;
run_make_with_options($makefile, $options, &get_logfile(0),
# Timeout in seconds. If the test takes longer than this we'll fail it.
$test_timeout = 5;
+# Path to Perl
+$perl_name = $^X;
# %makeENV is the cleaned-out environment.
%makeENV = ();
sub get_osname
{
# Set up an initial value. In perl5 we can do it the easy way.
- #
$osname = defined($^O) ? $^O : '';
+ # Find a path to Perl
+
# See if the filesystem supports long file names with multiple
# dots. DOS doesn't.
$short_filenames = 0;
eval "chop (\$osname = `sh -c 'uname -nmsr 2>&1'`)";
if ($osname =~ /not found/i)
{
- $osname = "(something unixy with no uname)";
+ $osname = "(something posixy with no uname)";
}
elsif ($@ ne "" || $?)
{
eval "chop (\$osname = `sh -c 'uname -a 2>&1'`)";
if ($@ ne "" || $?)
{
- $osname = "(something unixy)";
+ $osname = "(something posixy)";
}
}
$vos = 0;