version 1.110, 2007/12/01 15:14:34 |
version 1.111, 2008/01/12 13:08:59 |
|
|
* Interface: |
* Interface: |
* Job_Make Start the creation of the given target. |
* Job_Make Start the creation of the given target. |
* |
* |
* Job_CatchChildren Check for and handle the termination of any |
|
* children. This must be called reasonably |
|
* frequently to keep the whole make going at |
|
* a decent clip, since job table entries aren't |
|
* removed until their process is caught this way. |
|
* |
|
* Job_CatchOutput Print any output our children have produced. |
|
* Should also be called fairly frequently to |
|
* keep the user informed of what's going on. |
|
* If no output is waiting, it will block for |
|
* a time given by the SEL_* constants, below, |
|
* or until output is ready. |
|
* |
|
* Job_Init Called to initialize this module. in addition, |
* Job_Init Called to initialize this module. in addition, |
* any commands attached to the .BEGIN target |
* any commands attached to the .BEGIN target |
* are executed before this function returns. |
* are executed before this function returns. |
|
|
#define JOB_CONTINUING 0x200 /* We are in the process of resuming this job. |
#define JOB_CONTINUING 0x200 /* We are in the process of resuming this job. |
* Used to avoid infinite recursion between |
* Used to avoid infinite recursion between |
* JobFinish and JobRestart */ |
* JobFinish and JobRestart */ |
|
#define JOB_DIDOUTPUT 0x001 |
struct job_pipe in[2]; |
struct job_pipe in[2]; |
} Job; |
} Job; |
|
|
|
|
static int errors; |
static int errors; |
struct error_info { |
struct error_info { |
int status; |
int status; |
char *name; |
GNode *n; |
}; |
}; |
|
|
|
|
|
|
static void prepare_pipe(struct job_pipe *, int *); |
static void prepare_pipe(struct job_pipe *, int *); |
static void handle_job_output(Job *, int, bool); |
static void handle_job_output(Job *, int, bool); |
static void register_error(int, Job *); |
static void register_error(int, Job *); |
|
static void loop_handle_running_jobs(void); |
|
static void Job_CatchChildren(void); |
|
static void Job_CatchOutput(void); |
|
|
static void |
static void |
register_error(int status, Job *job) |
register_error(int status, Job *job) |
|
|
errors++; |
errors++; |
p = emalloc(sizeof(struct error_info)); |
p = emalloc(sizeof(struct error_info)); |
p->status = status; |
p->status = status; |
p->name = job->node->name; |
p->n = job->node; |
if (p) |
Lst_AtEnd(&errorsList, p); |
Lst_AtEnd(&errorsList, p); |
|
} |
} |
|
|
void |
void |
|
|
{ |
{ |
LstNode ln; |
LstNode ln; |
struct error_info *p; |
struct error_info *p; |
|
const char *type; |
|
int r; |
|
|
for (ln = Lst_First(&errorsList); ln != NULL; ln = Lst_Adv(ln)) { |
for (ln = Lst_First(&errorsList); ln != NULL; ln = Lst_Adv(ln)) { |
p = (struct error_info *)Lst_Datum(ln); |
p = (struct error_info *)Lst_Datum(ln); |
if (WIFEXITED(p->status)) { |
if (WIFEXITED(p->status)) { |
Error("\tExit status %d in target %s", |
type = "Exit status"; |
WEXITSTATUS(p->status), p->name); |
r = WEXITSTATUS(p->status); |
} else if (WIFSIGNALED(p->status)) { |
} else if (WIFSIGNALED(p->status)) { |
Error("\tReceived signal %d in target s", |
type = "Received signal"; |
WTERMSIG(p->status), p->name); |
r = WTERMSIG(p->status); |
} else { |
} else { |
Error("\tStatus %d in target %s", p->status, p->name); |
type = "Status"; |
|
r = p->status; |
} |
} |
|
if (p->n->lineno) |
|
Error(" %s %d (%s, line %lu of %s)", |
|
type, r, p->n->name, p->n->lineno, p->n->fname); |
|
else |
|
Error(" %s %d (%s)", type, r, p->n->name); |
} |
} |
} |
} |
|
|
|
|
debug_printf("Process %ld exited.\n", (long)job->pid); |
debug_printf("Process %ld exited.\n", (long)job->pid); |
if (WEXITSTATUS(status) != 0) { |
if (WEXITSTATUS(status) != 0) { |
banner(job, stdout); |
banner(job, stdout); |
(void)fprintf(stdout, "*** Error code %d%s\n", |
(void)fprintf(stdout, "*** Error code %d %s\n", |
WEXITSTATUS(status), |
WEXITSTATUS(status), |
(job->node->type & OP_IGNORE) ? |
(job->node->type & OP_IGNORE) ? |
"(ignored)" : ""); |
"(ignored)" : ""); |
|
|
free(job); |
free(job); |
} |
} |
|
|
JobRestartJobs(); |
|
|
|
/* |
/* |
* Set aborting if any error. |
* Set aborting if any error. |
*/ |
*/ |
|
|
aborting = ABORT_ERROR; |
aborting = ABORT_ERROR; |
} |
} |
|
|
|
if (aborting != ABORT_ERROR) |
|
JobRestartJobs(); |
|
|
if (aborting == ABORT_ERROR && Job_Empty()) { |
if (aborting == ABORT_ERROR && Job_Empty()) { |
/* |
/* |
* If we are aborting and the job table is now empty, we finish. |
* If we are aborting and the job table is now empty, we finish. |
|
|
size_t i; |
size_t i; |
|
|
banner(job, out); |
banner(job, out); |
|
job->flags |= JOB_DIDOUTPUT; |
for (i = 0; i < endPos; i++) |
for (i = 0; i < endPos; i++) |
putc(p->buffer[i], out); |
putc(p->buffer[i], out); |
} |
} |
|
|
} |
} |
return false; |
return false; |
} |
} |
|
|
/*- |
/*- |
*----------------------------------------------------------------------- |
*----------------------------------------------------------------------- |
* handle_pipe -- |
* handle_pipe -- |
|
|
/*- |
/*- |
*----------------------------------------------------------------------- |
*----------------------------------------------------------------------- |
* Job_CatchChildren -- |
* Job_CatchChildren -- |
* Handle the exit of a child. Called from Make_Make. |
* Handle the exit of a child. Called by handle_running_jobs |
* |
* |
* Side Effects: |
* Side Effects: |
* The job descriptor is removed from the list of children. |
* The job descriptor is removed from the list of children. |
|
|
/*- |
/*- |
*----------------------------------------------------------------------- |
*----------------------------------------------------------------------- |
* Job_CatchOutput -- |
* Job_CatchOutput -- |
* Catch the output from our children, if we're using |
* Catch the output from our children. |
* pipes do so. Otherwise just block time until we get a |
|
* signal (most likely a SIGCHLD) since there's no point in |
|
* just spinning when there's nothing to do and the reaping |
|
* of a child can wait for a while. |
|
* |
|
* Side Effects: |
|
* Output is read from pipes if we're piping. |
|
* ----------------------------------------------------------------------- |
* ----------------------------------------------------------------------- |
*/ |
*/ |
void |
void |
|
|
{ |
{ |
int nfds; |
int nfds; |
struct timeval timeout; |
struct timeval timeout; |
LstNode ln; |
LstNode ln, ln2; |
Job *job; |
Job *job; |
int i; |
int i; |
|
int status; |
|
|
int count = howmany(outputsn+1, NFDBITS) * sizeof(fd_mask); |
int count = howmany(outputsn+1, NFDBITS) * sizeof(fd_mask); |
fd_set *readfdsp = malloc(count); |
fd_set *readfdsp = malloc(count); |
|
|
|
|
nfds = select(outputsn+1, readfdsp, NULL, NULL, &timeout); |
nfds = select(outputsn+1, readfdsp, NULL, NULL, &timeout); |
handle_all_signals(); |
handle_all_signals(); |
if (nfds > 0) { |
for (ln = Lst_First(&runningJobs); nfds && ln != NULL; |
for (ln = Lst_First(&runningJobs); nfds && ln != NULL; |
ln = ln2) { |
ln = Lst_Adv(ln)) { |
ln2 = Lst_Adv(ln); |
job = (Job *)Lst_Datum(ln); |
job = (Job *)Lst_Datum(ln); |
for (i = 0; i < 2; i++) { |
job->flags &= ~JOB_DIDOUTPUT; |
if (FD_ISSET(job->in[i].fd, readfdsp)) { |
for (i = 1; i >= 0; i--) { |
handle_job_output(job, i, false); |
if (FD_ISSET(job->in[i].fd, readfdsp)) { |
nfds--; |
nfds--; |
} |
handle_job_output(job, i, false); |
} |
} |
} |
} |
|
if (job->flags & JOB_DIDOUTPUT) { |
|
if (wait4(job->pid, &status, WNOHANG|WUNTRACED, NULL) == |
|
job->pid) { |
|
Lst_Remove(&runningJobs, ln); |
|
nJobs--; |
|
jobFull = false; |
|
JobFinish(job, status); |
|
} else { |
|
Lst_Requeue(&runningJobs, ln); |
|
} |
|
} |
} |
} |
free(readfdsp); |
free(readfdsp); |
} |
} |
|
|
|
void |
|
handle_running_jobs() |
|
{ |
|
Job_CatchOutput(); |
|
Job_CatchChildren(); |
|
} |
|
|
|
static void |
|
loop_handle_running_jobs() |
|
{ |
|
while (nJobs) |
|
handle_running_jobs(); |
|
} |
/*- |
/*- |
*----------------------------------------------------------------------- |
*----------------------------------------------------------------------- |
* Job_Make -- |
* Job_Make -- |
|
|
|
|
if ((begin_node->type & OP_DUMMY) == 0) { |
if ((begin_node->type & OP_DUMMY) == 0) { |
JobStart(begin_node, JOB_SPECIAL); |
JobStart(begin_node, JOB_SPECIAL); |
while (nJobs) { |
loop_handle_running_jobs(); |
Job_CatchOutput(); |
|
Job_CatchChildren(); |
|
} |
|
} |
} |
} |
} |
|
|
|
|
ignoreErrors = false; |
ignoreErrors = false; |
|
|
JobStart(interrupt_node, 0); |
JobStart(interrupt_node, 0); |
while (nJobs) { |
loop_handle_running_jobs(); |
Job_CatchOutput(); |
|
Job_CatchChildren(); |
|
} |
|
} |
} |
} |
} |
exit(signo); |
exit(signo); |
|
|
Error("Errors reported so .END ignored"); |
Error("Errors reported so .END ignored"); |
} else { |
} else { |
JobStart(end_node, JOB_SPECIAL); |
JobStart(end_node, JOB_SPECIAL); |
|
loop_handle_running_jobs(); |
while (nJobs) { |
|
Job_CatchOutput(); |
|
Job_CatchChildren(); |
|
} |
|
} |
} |
} |
} |
return errors; |
return errors; |
|
|
Job_Wait(void) |
Job_Wait(void) |
{ |
{ |
aborting = ABORT_WAIT; |
aborting = ABORT_WAIT; |
while (nJobs != 0) { |
loop_handle_running_jobs(); |
Job_CatchOutput(); |
|
Job_CatchChildren(); |
|
} |
|
aborting = 0; |
aborting = 0; |
} |
} |
|
|
|
|
{ |
{ |
Job *job; |
Job *job; |
|
|
while (!jobFull && (job = (Job *)Lst_DeQueue(&stoppedJobs)) != NULL) { |
while (!Job_Full() && |
|
(job = (Job *)Lst_DeQueue(&stoppedJobs)) != NULL) { |
debug_printf("Job queue is not full. " |
debug_printf("Job queue is not full. " |
"Restarting a stopped job.\n"); |
"Restarting a stopped job.\n"); |
JobRestart(job); |
JobRestart(job); |