/*- *----------------------------------------------------------------------- * Make_Run -- * Initialize the nodes to remake and the list of nodes which are * ready to be made by doing a breadth-first traversal of the graph * starting from the nodes in the given list. Once this traversal * is finished, all the 'leaves' of the graph are in the toBeMade * queue. * Using this queue and the Job module, work back up the graph, * calling on MakeStartJobs to keep the job table as full as * possible. * * Results: * true if work was done. false otherwise. * * Side Effects: * The must_make field of all nodes involved in the creation of the given * targets is set to 1. The toBeMade list is set to contain all the * 'leaves' of these subgraphs. *----------------------------------------------------------------------- */ bool Make_Run(Lst targs) /* the initial list of targets */ { int errors; /* Number of errors the Job module reports */ GNode *gn; unsigned int i; bool cycle; /* wild guess at initial sizes */ Array_Init(&toBeMade, 500); Array_Init(&examine, 150); ohash_init(&targets, 10, &gnode_info); if (DEBUG(PARALLEL)) random_setup(); add_targets_to_make(targs); if (queryFlag) { /* * We wouldn't do any work unless we could start some jobs in * the next loop... (we won't actually start any, of course, * this is just to see if any of the targets was out of date) */ return MakeStartJobs(); } else { /* * Initialization. At the moment, no jobs are running and until * some get started, nothing will happen since the remaining * upward traversal of the graph is performed by the routines * in job.c upon the finishing of a job. So we fill the Job * table as much as we can before going into our loop. */ (void)MakeStartJobs(); } /* * Main Loop: The idea here is that the ending of jobs will take * care of the maintenance of data structures and the waiting for output * will cause us to be idle most of the time while our children run as * much as possible. Because the job table is kept as full as possible, * the only time when it will be empty is when all the jobs which need * running have been run, so that is the end condition of this loop. * Note that the Job module will exit if there were any errors unless * the keepgoing flag was given. */ while (!Job_Empty()) { handle_running_jobs(); (void)MakeStartJobs(); } errors = Job_Finish(); cycle = false; for (gn = ohash_first(&targets, &i); gn != NULL; gn = ohash_next(&targets, &i)) { if (has_been_built(gn)) continue; cycle = true; errors++; printf("Error: target %s unaccounted for (%s)\n", gn->name, status_to_string(gn)); } /* * Print the final status of each target. E.g. if it wasn't made * because some inferior reported an error. */ Lst_ForEach(targs, MakePrintStatus, &cycle); if (errors) Fatal("Errors while building"); return true; }
static void process_job_status(Job *job, int status) { int reason, code; bool done; debug_printf("Process %ld (%s) exited with status %d.\n", (long)job->pid, job->node->name, status); /* parse status */ if (WIFEXITED(status)) { reason = JOB_EXITED; code = WEXITSTATUS(status); } else if (WIFSIGNALED(status)) { reason = JOB_SIGNALED; code = WTERMSIG(status); } else { /* can't happen, set things to be bad. */ reason = UNKNOWN; code = status; } if ((reason == JOB_EXITED && code != 0 && !(job->node->type & OP_IGNORE)) || reason == JOB_SIGNALED) { /* * If it exited non-zero and either we're doing things our * way or we're not ignoring errors, the job is finished. * Similarly, if the shell died because of a signal * the job is also finished. In these * cases, finish out the job's output before printing the exit * status... */ close_job_pipes(job); done = true; } else if (reason == JOB_EXITED) { /* * Deal with ignored errors. We need to print a message telling * of the ignored error as well as setting status.w_status to 0 * so the next command gets run. To do this, we set done to be * true and the job exited non-zero. */ done = code != 0; close_job_pipes(job); } else { /* * No need to close things down or anything. */ done = false; } if (done || DEBUG(JOB)) { if (reason == JOB_EXITED) { debug_printf("Process %ld (%s) exited.\n", (long)job->pid, job->node->name); if (code != 0) { banner(job, stdout); (void)fprintf(stdout, "*** Error code %d %s\n", code, (job->node->type & OP_IGNORE) ? "(ignored)" : ""); if (job->node->type & OP_IGNORE) { reason = JOB_EXITED; code = 0; } } else if (DEBUG(JOB)) { (void)fprintf(stdout, "*** %ld (%s) Completed successfully\n", (long)job->pid, job->node->name); } } else { banner(job, stdout); (void)fprintf(stdout, "*** Signal %d\n", code); } (void)fflush(stdout); } done = true; if (done && aborting != ABORT_ERROR && aborting != ABORT_INTERRUPT && reason == JOB_EXITED && code == 0) { /* As long as we aren't aborting and the job didn't return a * non-zero status that we shouldn't ignore, we call * Make_Update to update the parents. */ job->node->built_status = MADE; Make_Update(job->node); } else if (!(reason == JOB_EXITED && code == 0)) { register_error(reason, code, job); } free(job); if (errors && !keepgoing && aborting != ABORT_INTERRUPT) aborting = ABORT_ERROR; if (aborting == ABORT_ERROR && Job_Empty()) Finish(errors); }