mirror of
https://salsa.debian.org/srivasta/make-dfsg.git
synced 2024-12-29 00:05:59 +00:00
Initial patch for output synchronization. See Savannah bug #33138.
Based on work by David Boyce <David.S.Boyce@gmail.com>.
This commit is contained in:
parent
6a52395ed1
commit
cb64352c75
7 changed files with 486 additions and 13 deletions
|
@ -4057,9 +4057,12 @@ If there is nothing looking like an integer after the @samp{-j} option,
|
|||
there is no limit on the number of job slots. The default number of job
|
||||
slots is one, which means serial execution (one thing at a time).
|
||||
|
||||
One unpleasant consequence of running several recipes simultaneously is
|
||||
that output generated by the recipes appears whenever each recipe
|
||||
sends it, so messages from different recipes may be interspersed.
|
||||
One consequence of running several recipes simultaneously is that by
|
||||
default, output from each recipe appears as soon as it is generated,
|
||||
with the result that messages from different recipes may be interspersed.
|
||||
This may create problems in interpreting output. If the @samp{-P} option
|
||||
is used, however, recipes will save their output until completion and
|
||||
then take turns writing it, with a more coherent result.
|
||||
|
||||
Another problem is that two processes cannot both take input from the
|
||||
same device; so to make sure that only one recipe tries to take input
|
||||
|
@ -8612,6 +8615,24 @@ The data base output contains file name and line number information for
|
|||
recipe and variable definitions, so it can be a useful debugging tool
|
||||
in complex environments.
|
||||
|
||||
@item -P
|
||||
@cindex @code{-P}
|
||||
@itemx --parallel-sync
|
||||
@cindex @code{--parallel-sync}
|
||||
@cindex parallel recipe execution, output
|
||||
When jobs are running in parallel under @samp{--jobs}, the output of
|
||||
each job is held until the job is complete thus ensuring that the output
|
||||
of each recipe is grouped together.
|
||||
|
||||
With no argument or the argument @samp{1}, messages from each job in
|
||||
recursive makes are grouped together. With the argument @samp{2}, the
|
||||
complete output from any recursive make is grouped together. The latter
|
||||
achieves better grouping of output from related jobs, but causes longer
|
||||
delay, since messages do not appear until the recursive make has
|
||||
completed. Therefore @samp{-P} is more useful when watching the output
|
||||
while make runs, and @samp{-P2} is better suited when running a complex
|
||||
parallel build in the background and checking its output afterwards.
|
||||
|
||||
@item -q
|
||||
@cindex @code{-q}
|
||||
@itemx --question
|
||||
|
|
253
job.c
253
job.c
|
@ -242,6 +242,16 @@ unsigned long job_counter = 0;
|
|||
/* Number of jobserver tokens this instance is currently using. */
|
||||
|
||||
unsigned int jobserver_tokens = 0;
|
||||
|
||||
#ifdef PARALLEL_SYNC
|
||||
/* Semaphore for use in -j mode with parallel_sync. */
|
||||
|
||||
int sync_handle = -1;
|
||||
|
||||
#define STREAM_OK(strm) ((fcntl (fileno ((strm)), F_GETFD) != -1) || (errno != EBADF))
|
||||
|
||||
#define FD_NOT_EMPTY(FD) ((FD) >= 0 && lseek ((FD), 0, SEEK_CUR) > 0)
|
||||
#endif /* PARALLEL_SYNC */
|
||||
|
||||
#ifdef WINDOWS32
|
||||
/*
|
||||
|
@ -539,6 +549,184 @@ child_handler (int sig UNUSED)
|
|||
*/
|
||||
}
|
||||
|
||||
#ifdef PARALLEL_SYNC
|
||||
/* Adds file descriptors to the child structure to support parallel_sync;
|
||||
one for stdout and one for stderr as long as they are (a) open and
|
||||
(b) separate. If stdout and stderr share a device they can share a
|
||||
temp file too. */
|
||||
static int
|
||||
assign_child_tempfiles (struct child *c, int combined)
|
||||
{
|
||||
FILE *outstrm = NULL, *errstrm = NULL;
|
||||
int o_ok, e_ok;
|
||||
const char *suppressed = "parallel-sync suppressed: ";
|
||||
char *failmode = NULL;
|
||||
|
||||
/* Check status of stdout and stderr before hooking up temp files. */
|
||||
o_ok = STREAM_OK (stdout);
|
||||
e_ok = STREAM_OK (stderr);
|
||||
|
||||
/* The tmpfile() function returns a FILE pointer but those can be in
|
||||
limited supply, so we immediately dup its file descriptor and keep
|
||||
only that, closing the FILE pointer. */
|
||||
|
||||
if (combined)
|
||||
{
|
||||
if (!(outstrm = tmpfile ()))
|
||||
failmode = "tmpfile()";
|
||||
else
|
||||
errstrm = outstrm;
|
||||
}
|
||||
else if (o_ok && e_ok)
|
||||
{
|
||||
if (!(outstrm = tmpfile ()) || !(errstrm = tmpfile ()))
|
||||
failmode = "tmpfile()";
|
||||
}
|
||||
else if (o_ok)
|
||||
{
|
||||
if (!(outstrm = tmpfile ()))
|
||||
failmode = "tmpfile()";
|
||||
}
|
||||
else if (e_ok)
|
||||
{
|
||||
if (!(errstrm = tmpfile ()))
|
||||
failmode = "tmpfile()";
|
||||
}
|
||||
else
|
||||
failmode = "stdout";
|
||||
|
||||
if (!failmode && outstrm)
|
||||
{
|
||||
if ((c->outfd = dup (fileno (outstrm))) == -1)
|
||||
failmode = "dup2()";
|
||||
else
|
||||
CLOSE_ON_EXEC (c->outfd);
|
||||
}
|
||||
|
||||
if (!failmode && errstrm)
|
||||
{
|
||||
if (combined)
|
||||
c->errfd = c->outfd;
|
||||
else
|
||||
{
|
||||
if ((c->errfd = dup (fileno (errstrm))) == -1)
|
||||
failmode = "dup2()";
|
||||
else
|
||||
CLOSE_ON_EXEC (c->errfd);
|
||||
}
|
||||
}
|
||||
|
||||
if (failmode)
|
||||
perror_with_name (suppressed, failmode);
|
||||
|
||||
if (outstrm)
|
||||
(void) fclose (outstrm);
|
||||
|
||||
if (errstrm && errstrm != outstrm)
|
||||
(void) fclose (errstrm);
|
||||
|
||||
return failmode == NULL;
|
||||
}
|
||||
|
||||
/* Support routine for sync_output() */
|
||||
static void
|
||||
pump_from_tmp_fd (int from_fd, int to_fd)
|
||||
{
|
||||
ssize_t nleft, nwrite;
|
||||
char buffer[8192];
|
||||
|
||||
if (lseek (from_fd, 0, SEEK_SET) == -1)
|
||||
perror ("lseek()");
|
||||
|
||||
while (1)
|
||||
{
|
||||
char *write_buf = buffer;
|
||||
EINTRLOOP (nleft, read (from_fd, buffer, sizeof (buffer)));
|
||||
if (nleft < 0)
|
||||
perror ("read()");
|
||||
if (nleft <= 0)
|
||||
break;
|
||||
while (nleft > 0)
|
||||
{
|
||||
EINTRLOOP (nwrite, write (to_fd, write_buf, nleft));
|
||||
if (nwrite < 0)
|
||||
{
|
||||
perror ("write()");
|
||||
return;
|
||||
}
|
||||
|
||||
write_buf += nwrite;
|
||||
nleft -= nwrite;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Support routine for sync_output() */
|
||||
static void *
|
||||
acquire_semaphore (void)
|
||||
{
|
||||
static struct flock fl;
|
||||
|
||||
fl.l_type = F_WRLCK;
|
||||
fl.l_whence = SEEK_SET;
|
||||
fl.l_start = 0; /* lock just one byte according to pid */
|
||||
fl.l_len = 1;
|
||||
if (fcntl (sync_handle, F_SETLKW, &fl) != -1)
|
||||
return &fl;
|
||||
perror ("fcntl()");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Support routine for sync_output() */
|
||||
static void
|
||||
release_semaphore (void *sem)
|
||||
{
|
||||
struct flock *flp = (struct flock *)sem;
|
||||
flp->l_type = F_UNLCK;
|
||||
if (fcntl (sync_handle, F_SETLKW, flp) == -1)
|
||||
perror ("fcntl()");
|
||||
}
|
||||
|
||||
/* Synchronize the output of jobs in -j mode to keep the results of
|
||||
each job together. This is done by holding the results in temp files,
|
||||
one for stdout and potentially another for stderr, and only releasing
|
||||
them to "real" stdout/stderr when a semaphore can be obtained. */
|
||||
|
||||
static void
|
||||
sync_output (struct child *c)
|
||||
{
|
||||
void *sem;
|
||||
|
||||
int outfd_not_empty = FD_NOT_EMPTY (c->outfd);
|
||||
int errfd_not_empty = FD_NOT_EMPTY (c->errfd);
|
||||
|
||||
if ((outfd_not_empty || errfd_not_empty) && (sem = acquire_semaphore ()))
|
||||
{
|
||||
/*
|
||||
* We've entered the "critical section" during which a lock is held.
|
||||
* We want to keep it as short as possible.
|
||||
*/
|
||||
if (outfd_not_empty)
|
||||
{
|
||||
log_working_directory (1, 1);
|
||||
pump_from_tmp_fd (c->outfd, fileno (stdout));
|
||||
log_working_directory (0, 1);
|
||||
}
|
||||
if (errfd_not_empty && c->errfd != c->outfd)
|
||||
pump_from_tmp_fd (c->errfd, fileno (stderr));
|
||||
|
||||
/* Exit the critical section */
|
||||
release_semaphore (sem);
|
||||
}
|
||||
|
||||
if (c->outfd >= 0)
|
||||
close (c->outfd);
|
||||
if (c->errfd >= 0)
|
||||
close (c->errfd);
|
||||
c->outfd = c->errfd = -1;
|
||||
}
|
||||
#endif /* PARALLEL_SYNC */
|
||||
|
||||
extern int shell_function_pid, shell_function_completed;
|
||||
|
||||
/* Reap all dead children, storing the returned status and the new command
|
||||
|
@ -833,6 +1021,12 @@ reap_children (int block, int err)
|
|||
c->sh_batch_file = NULL;
|
||||
}
|
||||
|
||||
#ifdef PARALLEL_SYNC
|
||||
/* Synchronize parallel output if requested */
|
||||
if (parallel_sync)
|
||||
sync_output (c);
|
||||
#endif /* PARALLEL_SYNC */
|
||||
|
||||
/* If this child had the good stdin, say it is now free. */
|
||||
if (c->good_stdin)
|
||||
good_stdin_used = 0;
|
||||
|
@ -1413,6 +1607,45 @@ start_job_command (struct child *child)
|
|||
|
||||
#else /* !__EMX__ */
|
||||
|
||||
#ifdef PARALLEL_SYNC
|
||||
if (parallel_sync)
|
||||
{
|
||||
static int combined_output;
|
||||
/* If parallel_sync is turned on, find a resource to
|
||||
synchronize on. This block is traversed only once. */
|
||||
if (sync_handle == -1)
|
||||
{
|
||||
struct stat stbuf_o, stbuf_e;
|
||||
|
||||
if (STREAM_OK (stdout))
|
||||
{
|
||||
sync_handle = fileno (stdout);
|
||||
combined_output =
|
||||
fstat (fileno (stdout), &stbuf_o) == 0 &&
|
||||
fstat (fileno (stderr), &stbuf_e) == 0 &&
|
||||
stbuf_o.st_dev == stbuf_e.st_dev &&
|
||||
stbuf_o.st_ino == stbuf_e.st_ino;
|
||||
}
|
||||
else if (STREAM_OK (stderr))
|
||||
sync_handle = fileno (stderr);
|
||||
else
|
||||
{
|
||||
perror_with_name ("parallel-sync suppressed: ", "stderr");
|
||||
parallel_sync = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* If it still looks like we can synchronize, create a temp
|
||||
file to hold stdout (and one for stderr if separate). */
|
||||
if (parallel_sync >= PARALLEL_SYNC_COARSE
|
||||
|| (parallel_sync == PARALLEL_SYNC_FINE && !(flags & COMMANDS_RECURSE)))
|
||||
{
|
||||
if (!assign_child_tempfiles (child, combined_output))
|
||||
parallel_sync = 0;
|
||||
}
|
||||
}
|
||||
#endif /* PARALLEL_SYNC */
|
||||
|
||||
child->pid = vfork ();
|
||||
environ = parent_environ; /* Restore value child may have clobbered. */
|
||||
if (child->pid == 0)
|
||||
|
@ -1436,6 +1669,23 @@ start_job_command (struct child *child)
|
|||
setrlimit (RLIMIT_STACK, &stack_limit);
|
||||
#endif
|
||||
|
||||
#ifdef PARALLEL_SYNC
|
||||
/* Divert child output into tempfile(s) if parallel_sync in use. */
|
||||
if (parallel_sync)
|
||||
{
|
||||
int outfd = fileno (stdout);
|
||||
int errfd = fileno (stderr);
|
||||
|
||||
if ((child->outfd >= 0 &&
|
||||
(close (outfd) == -1 || dup2 (child->outfd, outfd) == -1))
|
||||
|| (child->errfd >= 0 &&
|
||||
(close (errfd) == -1 || dup2 (child->errfd, errfd) == -1)))
|
||||
{
|
||||
perror_with_name ("parallel-sync: ", "dup2()");
|
||||
}
|
||||
}
|
||||
#endif /* PARALLEL_SYNC */
|
||||
|
||||
child_execute_job (child->good_stdin ? 0 : bad_stdin, 1,
|
||||
argv, child->environment);
|
||||
}
|
||||
|
@ -1769,6 +2019,9 @@ new_job (struct file *file)
|
|||
c->file = file;
|
||||
c->command_lines = lines;
|
||||
c->sh_batch_file = NULL;
|
||||
#ifdef PARALLEL_SYNC
|
||||
c->outfd = c->errfd = -1;
|
||||
#endif
|
||||
|
||||
/* Cache dontcare flag because file->dontcare can be changed once we
|
||||
return. Check dontcare inheritance mechanism for details. */
|
||||
|
|
8
job.h
8
job.h
|
@ -34,6 +34,10 @@ this program. If not, see <http://www.gnu.org/licenses/>. */
|
|||
# define CLOSE_ON_EXEC(_d) (void) fcntl ((_d), F_SETFD, FD_CLOEXEC)
|
||||
#endif
|
||||
|
||||
#ifdef POSIX /* PARALLEL_SYNC */
|
||||
#define PARALLEL_SYNC
|
||||
#endif /* POSIX */
|
||||
|
||||
/* Structure describing a running or dead child process. */
|
||||
|
||||
struct child
|
||||
|
@ -60,6 +64,10 @@ struct child
|
|||
unsigned int good_stdin:1; /* Nonzero if this child has a good stdin. */
|
||||
unsigned int deleted:1; /* Nonzero if targets have been deleted. */
|
||||
unsigned int dontcare:1; /* Saved dontcare flag. */
|
||||
#ifdef PARALLEL_SYNC
|
||||
int outfd; /* Optional file descriptor for saving stdout */
|
||||
int errfd; /* Optional file descriptor for saving stderr */
|
||||
#endif /* PARALLEL_SYNC */
|
||||
};
|
||||
|
||||
extern struct child *children;
|
||||
|
|
37
main.c
37
main.c
|
@ -228,6 +228,15 @@ static unsigned int master_job_slots = 0;
|
|||
|
||||
static unsigned int inf_jobs = 0;
|
||||
|
||||
#ifdef PARALLEL_SYNC
|
||||
|
||||
/* Default value for parallel sync without an argument. */
|
||||
|
||||
static unsigned int no_parallel_sync = 0;
|
||||
static unsigned int default_parallel_sync = PARALLEL_SYNC_FINE;
|
||||
|
||||
#endif
|
||||
|
||||
/* File descriptors for the jobs pipe. */
|
||||
|
||||
static struct stringlist *jobserver_fds = 0;
|
||||
|
@ -344,6 +353,10 @@ static const char *const usage[] =
|
|||
Consider FILE to be very old and don't remake it.\n"),
|
||||
N_("\
|
||||
-p, --print-data-base Print make's internal database.\n"),
|
||||
#ifdef PARALLEL_SYNC
|
||||
N_("\
|
||||
-P [2], --parallel-sync[=2] Synchronize output of parallel jobs [coarse].\n"),
|
||||
#endif
|
||||
N_("\
|
||||
-q, --question Run no recipe; exit status says if up to date.\n"),
|
||||
N_("\
|
||||
|
@ -408,6 +421,11 @@ static const struct command_switch switches[] =
|
|||
{ 'n', flag, &just_print_flag, 1, 1, 1, 0, 0, "just-print" },
|
||||
{ 'o', filename, &old_files, 0, 0, 0, 0, 0, "old-file" },
|
||||
{ 'p', flag, &print_data_base_flag, 1, 1, 0, 0, 0, "print-data-base" },
|
||||
#ifdef PARALLEL_SYNC
|
||||
// { 'P', flag, ¶llel_sync, 1, 1, 0, 0, 0, "parallel-sync" }, // two-state
|
||||
{ 'P', positive_int, ¶llel_sync, 1, 1, 0, &default_parallel_sync,
|
||||
&no_parallel_sync, "parallel-sync" },
|
||||
#endif
|
||||
{ 'q', flag, &question_flag, 1, 1, 1, 0, 0, "question" },
|
||||
{ 'r', flag, &no_builtin_rules_flag, 1, 1, 0, 0, 0, "no-builtin-rules" },
|
||||
{ 'R', flag, &no_builtin_variables_flag, 1, 1, 0, 0, 0,
|
||||
|
@ -503,6 +521,14 @@ int second_expansion;
|
|||
|
||||
int one_shell;
|
||||
|
||||
/* Either PARALLEL_SYNC_FINE or PARALLEL_SYNC_COARSE
|
||||
if the "--parallel-sync" option was given.
|
||||
This attempts to synchronize the output of parallel
|
||||
jobs such that the results of each job stay together.
|
||||
It works best in combination with .ONESHELL. */
|
||||
|
||||
int parallel_sync;
|
||||
|
||||
/* Nonzero if we have seen the '.NOTPARALLEL' target.
|
||||
This turns off parallel builds for this invocation of make. */
|
||||
|
||||
|
@ -2095,7 +2121,7 @@ main (int argc, char **argv, char **envp)
|
|||
if (print_data_base_flag)
|
||||
print_data_base ();
|
||||
|
||||
log_working_directory (0);
|
||||
log_working_directory (0, 0);
|
||||
|
||||
clean_jobserver (0);
|
||||
|
||||
|
@ -3280,7 +3306,7 @@ die (int status)
|
|||
_x = chdir (directory_before_chdir);
|
||||
}
|
||||
|
||||
log_working_directory (0);
|
||||
log_working_directory (0, 0);
|
||||
}
|
||||
|
||||
exit (status);
|
||||
|
@ -3290,17 +3316,18 @@ die (int status)
|
|||
left (according to ENTERING) the current directory. */
|
||||
|
||||
void
|
||||
log_working_directory (int entering)
|
||||
log_working_directory (int entering, int force)
|
||||
{
|
||||
static int entered = 0;
|
||||
|
||||
/* Print nothing without the flag. Don't print the entering message
|
||||
again if we already have. Don't print the leaving message if we
|
||||
haven't printed the entering message. */
|
||||
if (! print_directory_flag || entering == entered)
|
||||
if (! print_directory_flag || (!force && entering == entered))
|
||||
return;
|
||||
|
||||
entered = entering;
|
||||
if (!force)
|
||||
entered = entering;
|
||||
|
||||
if (print_data_base_flag)
|
||||
fputs ("# ", stdout);
|
||||
|
|
|
@ -396,7 +396,7 @@ void fatal ();
|
|||
#endif
|
||||
|
||||
void die (int) __attribute__ ((noreturn));
|
||||
void log_working_directory (int);
|
||||
void log_working_directory (int, int);
|
||||
void pfatal_with_name (const char *) __attribute__ ((noreturn));
|
||||
void perror_with_name (const char *, const char *);
|
||||
void *xmalloc (unsigned int);
|
||||
|
@ -525,6 +525,9 @@ int strncasecmp (const char *s1, const char *s2, int n);
|
|||
# endif
|
||||
#endif
|
||||
|
||||
#define PARALLEL_SYNC_FINE 1
|
||||
#define PARALLEL_SYNC_COARSE 2
|
||||
|
||||
extern const gmk_floc *reading_file;
|
||||
extern const gmk_floc **expanding_var;
|
||||
|
||||
|
@ -536,7 +539,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, trace_flag, posix_pedantic;
|
||||
extern int not_parallel, second_expansion, clock_skew_detected;
|
||||
extern int rebuilding_makefiles, one_shell;
|
||||
extern int rebuilding_makefiles, one_shell, parallel_sync;
|
||||
|
||||
/* can we run commands via 'sh -c xxx' or must we use batch files? */
|
||||
extern int batch_mode_shell;
|
||||
|
|
24
misc.c
24
misc.c
|
@ -231,10 +231,13 @@ message (prefix, fmt, va_alist)
|
|||
va_list args;
|
||||
#endif
|
||||
|
||||
log_working_directory (1);
|
||||
log_working_directory (1, 0);
|
||||
|
||||
if (fmt != 0)
|
||||
{
|
||||
if (parallel_sync)
|
||||
log_working_directory (1, 1);
|
||||
|
||||
if (prefix)
|
||||
{
|
||||
if (makelevel == 0)
|
||||
|
@ -246,6 +249,9 @@ message (prefix, fmt, va_alist)
|
|||
VA_PRINTF (stdout, fmt, args);
|
||||
VA_END (args);
|
||||
putchar ('\n');
|
||||
|
||||
if (parallel_sync)
|
||||
log_working_directory (0, 1);
|
||||
}
|
||||
|
||||
fflush (stdout);
|
||||
|
@ -267,7 +273,10 @@ error (flocp, fmt, va_alist)
|
|||
va_list args;
|
||||
#endif
|
||||
|
||||
log_working_directory (1);
|
||||
if (parallel_sync)
|
||||
log_working_directory (1, 1);
|
||||
else
|
||||
log_working_directory (1, 0);
|
||||
|
||||
if (flocp && flocp->filenm)
|
||||
fprintf (stderr, "%s:%lu: ", flocp->filenm, flocp->lineno);
|
||||
|
@ -282,6 +291,9 @@ error (flocp, fmt, va_alist)
|
|||
|
||||
putc ('\n', stderr);
|
||||
fflush (stderr);
|
||||
|
||||
if (parallel_sync)
|
||||
log_working_directory (0, 1);
|
||||
}
|
||||
|
||||
/* Print an error message and exit. */
|
||||
|
@ -300,7 +312,10 @@ fatal (flocp, fmt, va_alist)
|
|||
va_list args;
|
||||
#endif
|
||||
|
||||
log_working_directory (1);
|
||||
if (parallel_sync)
|
||||
log_working_directory (1, 1);
|
||||
else
|
||||
log_working_directory (1, 0);
|
||||
|
||||
if (flocp && flocp->filenm)
|
||||
fprintf (stderr, "%s:%lu: *** ", flocp->filenm, flocp->lineno);
|
||||
|
@ -315,6 +330,9 @@ fatal (flocp, fmt, va_alist)
|
|||
|
||||
fputs (_(". Stop.\n"), stderr);
|
||||
|
||||
if (parallel_sync)
|
||||
log_working_directory (0, 1);
|
||||
|
||||
die (2);
|
||||
}
|
||||
|
||||
|
|
143
tests/scripts/features/parallel-sync
Normal file
143
tests/scripts/features/parallel-sync
Normal file
|
@ -0,0 +1,143 @@
|
|||
# -*-perl-*-
|
||||
|
||||
$description = "Test parallel-sync (-P) option.";
|
||||
|
||||
$details = "Test the synchronization of output from parallel jobs.";
|
||||
|
||||
if (!$parallel_jobs) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ($vos) {
|
||||
$sleep_command = "sleep -seconds";
|
||||
}
|
||||
else {
|
||||
$sleep_command = "sleep";
|
||||
}
|
||||
|
||||
# The following subdirectories with Makefiles are used in several
|
||||
# of the following tests.
|
||||
mkdir('foo', 0777);
|
||||
mkdir('bar', 0777);
|
||||
|
||||
open(MAKEFILE,"> foo/Makefile");
|
||||
print MAKEFILE <<EOF;
|
||||
all: foo
|
||||
|
||||
foo:
|
||||
\@echo foo: start; $sleep_command 2; echo foo: end
|
||||
|
||||
foo-fail:
|
||||
\@$sleep_command 2; false
|
||||
EOF
|
||||
close(MAKEFILE);
|
||||
|
||||
open(MAKEFILE,"> bar/Makefile");
|
||||
print MAKEFILE <<EOF;
|
||||
all: bar baz
|
||||
|
||||
bar:
|
||||
\@echo bar: start; $sleep_command 1; echo bar: end
|
||||
|
||||
baz:
|
||||
\@echo baz: start; $sleep_command 4; echo baz: end
|
||||
EOF
|
||||
close(MAKEFILE);
|
||||
|
||||
# Test coarse synchronization.
|
||||
run_make_test('
|
||||
all: make-foo make-bar
|
||||
|
||||
make-foo:
|
||||
$(MAKE) -C foo
|
||||
|
||||
make-bar:
|
||||
$(MAKE) -C bar',
|
||||
'-j -P2',
|
||||
"#MAKEPATH# -C foo
|
||||
#MAKEPATH# -C bar
|
||||
#MAKE#[1]: Entering directory '#PWD#/foo'
|
||||
#MAKE#[1]: Entering directory '#PWD#/foo'
|
||||
foo: start
|
||||
foo: end
|
||||
#MAKE#[1]: Leaving directory '#PWD#/foo'
|
||||
#MAKE#[1]: Leaving directory '#PWD#/foo'
|
||||
#MAKE#[1]: Entering directory '#PWD#/bar'
|
||||
#MAKE#[1]: Entering directory '#PWD#/bar'
|
||||
bar: start
|
||||
bar: end
|
||||
#MAKE#[1]: Leaving directory '#PWD#/bar'
|
||||
#MAKE#[1]: Entering directory '#PWD#/bar'
|
||||
baz: start
|
||||
baz: end
|
||||
#MAKE#[1]: Leaving directory '#PWD#/bar'
|
||||
#MAKE#[1]: Leaving directory '#PWD#/bar'\n");
|
||||
|
||||
# Test fine synchronization.
|
||||
run_make_test('
|
||||
all: make-foo make-bar
|
||||
|
||||
make-foo:
|
||||
$(MAKE) -C foo
|
||||
|
||||
make-bar:
|
||||
$(MAKE) -C bar',
|
||||
'-j -P',
|
||||
"#MAKEPATH# -C foo
|
||||
#MAKEPATH# -C bar
|
||||
#MAKE#[1]: Entering directory '#PWD#/foo'
|
||||
#MAKE#[1]: Entering directory '#PWD#/bar'
|
||||
#MAKE#[1]: Entering directory '#PWD#/bar'
|
||||
bar: start
|
||||
bar: end
|
||||
#MAKE#[1]: Leaving directory '#PWD#/bar'
|
||||
#MAKE#[1]: Entering directory '#PWD#/foo'
|
||||
foo: start
|
||||
foo: end
|
||||
#MAKE#[1]: Leaving directory '#PWD#/foo'
|
||||
#MAKE#[1]: Leaving directory '#PWD#/foo'
|
||||
#MAKE#[1]: Entering directory '#PWD#/bar'
|
||||
baz: start
|
||||
baz: end
|
||||
#MAKE#[1]: Leaving directory '#PWD#/bar'
|
||||
#MAKE#[1]: Leaving directory '#PWD#/bar'\n");
|
||||
|
||||
# Test that messages from make itself are enclosed with
|
||||
# "Entering/Leaving directory" messages.
|
||||
run_make_test('
|
||||
all: make-foo-fail make-bar-bar
|
||||
|
||||
make-foo-fail:
|
||||
$(MAKE) -C foo foo-fail
|
||||
|
||||
make-bar-bar:
|
||||
$(MAKE) -C bar bar',
|
||||
'-j -P',
|
||||
"#MAKEPATH# -C foo foo-fail
|
||||
#MAKEPATH# -C bar bar
|
||||
#MAKE#[1]: Entering directory '#PWD#/foo'
|
||||
#MAKE#[1]: Entering directory '#PWD#/bar'
|
||||
#MAKE#[1]: Entering directory '#PWD#/bar'
|
||||
bar: start
|
||||
bar: end
|
||||
#MAKE#[1]: Leaving directory '#PWD#/bar'
|
||||
#MAKE#[1]: Leaving directory '#PWD#/bar'
|
||||
#MAKE#[1]: Entering directory '#PWD#/foo'
|
||||
Makefile:7: recipe for target 'foo-fail' failed
|
||||
#MAKE#[1]: Leaving directory '#PWD#/foo'
|
||||
#MAKE#[1]: Entering directory '#PWD#/foo'
|
||||
#MAKE#[1]: *** [foo-fail] Error 1
|
||||
#MAKE#[1]: Leaving directory '#PWD#/foo'
|
||||
#MAKE#[1]: Leaving directory '#PWD#/foo'
|
||||
#MAKEFILE#:5: recipe for target 'make-foo-fail' failed
|
||||
#MAKE#: *** [make-foo-fail] Error 2\n",
|
||||
512);
|
||||
|
||||
# Remove temporary directories and contents.
|
||||
rmfiles('foo/Makefile');
|
||||
rmdir('foo');
|
||||
rmfiles('bar/Makefile');
|
||||
rmdir('bar');
|
||||
|
||||
# This tells the test driver that the perl test script executed properly.
|
||||
1;
|
Loading…
Reference in a new issue