mirror of
https://github.com/mirror/make.git
synced 2024-12-26 21:00:30 +08:00
Add the beginning of the .ONESHELL special feature.
Original patch by David Boyce. Modified by Paul Smith.
This commit is contained in:
parent
b85b7e0a55
commit
7ba7dbca47
25
ChangeLog
25
ChangeLog
@ -1,3 +1,24 @@
|
||||
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.
|
||||
@ -28,6 +49,10 @@
|
||||
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.
|
||||
|
10
NEWS
10
NEWS
@ -71,6 +71,16 @@ Version 3.81.90
|
||||
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
|
||||
|
91
commands.c
91
commands.c
@ -330,7 +330,6 @@ set_file_variables (struct file *file)
|
||||
void
|
||||
chop_commands (struct commands *cmds)
|
||||
{
|
||||
const char *p;
|
||||
unsigned int nlines, idx;
|
||||
char **lines;
|
||||
|
||||
@ -340,64 +339,82 @@ chop_commands (struct commands *cmds)
|
||||
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] == '\\')
|
||||
{
|
||||
++end;
|
||||
goto find_end;
|
||||
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)
|
||||
{
|
||||
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;
|
||||
|
@ -17,7 +17,7 @@
|
||||
# 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$]])
|
||||
|
131
job.c
131
job.c
@ -382,6 +382,53 @@ _is_unixy_shell (const char *path)
|
||||
}
|
||||
#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;
|
||||
}
|
||||
|
||||
|
||||
/* Write an error message describing the exit status given in
|
||||
EXIT_CODE, EXIT_SIG, and COREDUMP, for the target TARGET_NAME.
|
||||
@ -1117,10 +1164,13 @@ start_job_command (struct child *child)
|
||||
#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)
|
||||
{
|
||||
@ -2511,6 +2561,9 @@ construct_command_argv_internal (char *line, char **restp, char *shell,
|
||||
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. */
|
||||
@ -2738,16 +2791,80 @@ construct_command_argv_internal (char *line, char **restp, char *shell,
|
||||
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;
|
||||
@ -3108,7 +3225,7 @@ dup2 (int old, int new)
|
||||
|
||||
return fd;
|
||||
}
|
||||
#endif /* !HAPE_DUP2 && !_AMIGA */
|
||||
#endif /* !HAVE_DUP2 && !_AMIGA */
|
||||
|
||||
/* On VMS systems, include special VMS functions. */
|
||||
|
||||
|
1
job.h
1
job.h
@ -67,6 +67,7 @@ struct child
|
||||
|
||||
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);
|
||||
|
6
main.c
6
main.c
@ -496,6 +496,12 @@ int posix_pedantic;
|
||||
|
||||
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. */
|
||||
|
||||
|
1
make.h
1
make.h
@ -500,6 +500,7 @@ extern int env_overrides, no_builtin_rules_flag, no_builtin_variables_flag;
|
||||
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;
|
||||
|
4
read.c
4
read.c
@ -1964,6 +1964,10 @@ record_files (struct nameseq *filenames, const char *pattern,
|
||||
}
|
||||
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',
|
||||
|
@ -1,3 +1,13 @@
|
||||
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.
|
||||
|
@ -122,6 +122,7 @@ sub run_make_test
|
||||
$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!
|
||||
@ -136,6 +137,7 @@ sub run_make_test
|
||||
$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),
|
||||
|
@ -54,6 +54,8 @@ $test_passed = 1;
|
||||
# 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 = ();
|
||||
@ -236,9 +238,10 @@ sub toplevel
|
||||
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;
|
||||
@ -273,14 +276,14 @@ sub get_osname
|
||||
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;
|
||||
|
Loading…
Reference in New Issue
Block a user