void print_errors() { LstNode ln; struct error_info *p; const char *type; for (ln = Lst_First(&errorsList); ln != NULL; ln = Lst_Adv(ln)) { p = (struct error_info *)Lst_Datum(ln); switch(p->reason) { case JOB_EXITED: type = "Exit status"; break; case JOB_SIGNALED: type = "Received signal"; break; default: type = "Should not happen"; break; } if (p->n->origin.lineno) Error(" %s %d (%s, line %lu of %s)", type, p->code, p->n->name, p->n->origin.lineno, p->n->origin.fname); else Error(" %s %d (%s)", type, p->code, p->n->name); } }
static void TargPrintNode(GNode *gn, bool full) { if (OP_NOP(gn->type)) return; switch((gn->special & SPECIAL_MASK)) { case SPECIAL_SUFFIXES: case SPECIAL_PHONY: case SPECIAL_ORDER: case SPECIAL_NOTHING: case SPECIAL_MAIN: case SPECIAL_IGNORE: return; default: break; } if (full) { printf("# %d unmade prerequisites\n", gn->unmade); if (! (gn->type & (OP_JOIN|OP_USE|OP_EXEC))) { if (!is_out_of_date(gn->mtime)) { printf("# last modified %s: %s\n", time_to_string(&gn->mtime), status_to_string(gn)); } else if (gn->built_status != UNKNOWN) { printf("# non-existent (maybe): %s\n", status_to_string(gn)); } else { printf("# unmade\n"); } } } if (!Lst_IsEmpty(&gn->parents)) { printf("# parent targets: "); Lst_Every(&gn->parents, TargPrintName); fputc('\n', stdout); } if (gn->impliedsrc) printf("# implied prerequisite: %s\n", gn->impliedsrc->name); printf("%-16s", gn->name); switch (gn->type & OP_OPMASK) { case OP_DEPENDS: printf(": "); break; case OP_FORCE: printf("! "); break; case OP_DOUBLEDEP: printf(":: "); break; } Targ_PrintType(gn->type); Lst_Every(&gn->children, TargPrintName); fputc('\n', stdout); Lst_Every(&gn->commands, Targ_PrintCmd); printf("\n\n"); if (gn->type & OP_DOUBLEDEP) { LstNode ln; for (ln = Lst_First(&gn->cohorts); ln != NULL; ln = Lst_Adv(ln)) TargPrintNode((GNode *)Lst_Datum(ln), full); } }
/*- *----------------------------------------------------------------------- * Job_AbortAll -- * Abort all currently running jobs without handling output or anything. * This function is to be called only in the event of a major * error. Most definitely NOT to be called from JobInterrupt. * * Side Effects: * All children are killed, not just the firstborn *----------------------------------------------------------------------- */ void Job_AbortAll(void) { LstNode ln; /* element in job table */ Job *job; /* the job descriptor in that element */ int foo; aborting = ABORT_ERROR; if (nJobs) { for (ln = Lst_First(&runningJobs); ln != NULL; ln = Lst_Adv(ln)) { job = (Job *)Lst_Datum(ln); /* * kill the child process with increasingly drastic * signals to make darn sure it's dead. */ killpg(job->pid, SIGINT); killpg(job->pid, SIGKILL); } } /* * Catch as many children as want to report in at first, then give up */ while (waitpid(WAIT_ANY, &foo, WNOHANG) > 0) continue; }
static bool expensive_commands(Lst l) { LstNode ln; for (ln = Lst_First(l); ln != NULL; ln = Lst_Adv(ln)) if (expensive_command(Lst_Datum(ln))) return true; return false; }
void expand_commands(GNode *gn) { LstNode ln; char *cmd; for (ln = Lst_First(&gn->commands); ln != NULL; ln = Lst_Adv(ln)) { cmd = Var_Subst(Lst_Datum(ln), &gn->context, false); Lst_AtEnd(&gn->expanded, cmd); } }
/* this is safe from interrupts, actually */ void parallel_handler(int signo) { int save_errno = errno; LstNode ln; for (ln = Lst_First(&job_pids); ln != NULL; ln = Lst_Adv(ln)) { struct job_pid *p = Lst_Datum(ln); killpg(p->pid, signo); } errno = save_errno; switch(signo) { case SIGINT: got_SIGINT++; got_signal = 1; return; case SIGHUP: got_SIGHUP++; got_signal = 1; return; case SIGQUIT: got_SIGQUIT++; got_signal = 1; return; case SIGTERM: got_SIGTERM++; got_signal = 1; return; case SIGTSTP: got_SIGTSTP++; got_signal = 1; break; case SIGTTOU: got_SIGTTOU++; got_signal = 1; break; case SIGTTIN: got_SIGTTIN++; got_signal = 1; break; case SIGWINCH: got_SIGWINCH++; got_signal = 1; break; case SIGCONT: got_SIGCONT++; got_signal = 1; break; } (void)killpg(getpid(), signo); (void)signal(signo, SIG_DFL); errno = save_errno; }
/*- *----------------------------------------------------------------------- * CondDoMake -- * Handle the 'make' function for conditionals. * * Results: * true if the given target is being made. *----------------------------------------------------------------------- */ static bool CondDoMake(struct Name *arg) { LstNode ln; for (ln = Lst_First(create); ln != NULL; ln = Lst_Adv(ln)) { char *s = (char *)Lst_Datum(ln); if (Str_Matchi(s, strchr(s, '\0'), arg->s, arg->e)) return true; } return false; }
/*- *--------------------------------------------------------------------- * ParseDoOp -- * Apply the parsed operator to the given target node. Used in a * Array_Find call by ParseDoDependency once all targets have * been found and their operator parsed. If the previous and new * operators are incompatible, a major error is taken. * * Side Effects: * The type field of the node is altered to reflect any new bits in * the op. *--------------------------------------------------------------------- */ static int ParseDoOp(GNode **gnp, unsigned int op) { GNode *gn = *gnp; /* * If the dependency mask of the operator and the node don't match and * the node has actually had an operator applied to it before, and the * operator actually has some dependency information in it, complain. */ if (((op & OP_OPMASK) != (gn->type & OP_OPMASK)) && !OP_NOP(gn->type) && !OP_NOP(op)) { Parse_Error(PARSE_FATAL, "Inconsistent dependency operator for target %s\n" "\t(was %s%s, now %s%s)", gn->name, gn->name, operator_string(gn->type), gn->name, operator_string(op)); return 0; } if (op == OP_DOUBLEDEP && ((gn->type & OP_OPMASK) == OP_DOUBLEDEP)) { /* If the node was the object of a :: operator, we need to * create a new instance of it for the children and commands on * this dependency line. The new instance is placed on the * 'cohorts' list of the initial one (note the initial one is * not on its own cohorts list) and the new instance is linked * to all parents of the initial instance. */ GNode *cohort; LstNode ln; cohort = Targ_NewGN(gn->name); /* Duplicate links to parents so graph traversal is simple. * Perhaps some type bits should be duplicated? * * Make the cohort invisible as well to avoid duplicating it * into other variables. True, parents of this target won't * tend to do anything with their local variables, but better * safe than sorry. */ for (ln = Lst_First(&gn->parents); ln != NULL; ln = Lst_Adv(ln)) ParseLinkSrc((GNode *)Lst_Datum(ln), cohort); cohort->type = OP_DOUBLEDEP|OP_INVISIBLE; Lst_AtEnd(&gn->cohorts, cohort); /* Replace the node in the targets list with the new copy */ *gnp = cohort; gn = cohort; } /* We don't want to nuke any previous flags (whatever they were) so we * just OR the new operator into the old. */ gn->type |= op; return 1; }
static void requeue_successors(GNode *gn) { LstNode ln; /* Deal with successor nodes. If any is marked for making and has an * unmade count of 0, has not been made and isn't in the examination * queue, it means we need to place it in the queue as it restrained * itself before. */ for (ln = Lst_First(&gn->successors); ln != NULL; ln = Lst_Adv(ln)) { GNode *succ = (GNode *)Lst_Datum(ln); if (succ->must_make && succ->unmade == 0 && succ->built_status == UNKNOWN) Array_PushNew(&toBeMade, succ); } }
void Make_HandleUse(GNode *cgn, /* The .USE node */ GNode *pgn) /* The target of the .USE node */ { GNode *gn; /* A child of the .USE node */ LstNode ln; /* An element in the children list */ assert(cgn->type & (OP_USE|OP_TRANSFORM)); if ((cgn->type & OP_USE) || Lst_IsEmpty(&pgn->commands)) { /* .USE or transformation and target has no commands * -- append the child's commands to the parent. */ Lst_Concat(&pgn->commands, &cgn->commands); } for (ln = Lst_First(&cgn->children); ln != NULL; ln = Lst_Adv(ln)) { gn = (GNode *)Lst_Datum(ln); if (Lst_AddNew(&pgn->children, gn)) { Lst_AtEnd(&gn->parents, pgn); pgn->unmade++; } } pgn->type |= cgn->type & ~(OP_OPMASK|OP_USE|OP_TRANSFORM); /* * This child node is now "made", so we decrement the count of * unmade children in the parent... We also remove the child * from the parent's list to accurately reflect the number of * decent children the parent has. This is used by Make_Run to * decide whether to queue the parent or examine its children... */ if (cgn->type & OP_USE) pgn->unmade--; /* if the parent node doesn't have any location, then inherit the * use stuff, since that gives us better error messages. */ if (!pgn->lineno) { pgn->lineno = cgn->lineno; pgn->fname = cgn->fname; } }
void Targ_FindList(Lst nodes, Lst names) { LstNode ln; GNode *gn; char *name; for (ln = Lst_First(names); ln != NULL; ln = Lst_Adv(ln)) { name = (char *)Lst_Datum(ln); gn = Targ_FindNode(name, TARG_CREATE); /* Note: Lst_AtEnd must come before the Lst_Concat so the nodes * are added to the list in the order in which they were * encountered in the makefile. */ Lst_AtEnd(nodes, gn); if (gn->type & OP_DOUBLEDEP) Lst_Concat(nodes, &gn->cohorts); } }
void handle_all_jobs_output(void) { int nfds; struct timeval timeout; LstNode ln, ln2; Job *job; int i; int status; /* no jobs */ if (Lst_IsEmpty(&runningJobs)) return; (void)fflush(stdout); memcpy(actual_mask, output_mask, mask_size); timeout.tv_sec = SEL_SEC; timeout.tv_usec = SEL_USEC; nfds = select(largest_fd+1, actual_mask, NULL, NULL, &timeout); handle_all_signals(); for (ln = Lst_First(&runningJobs); nfds && ln != NULL; ln = ln2) { ln2 = Lst_Adv(ln); job = (Job *)Lst_Datum(ln); job->flags &= ~JOB_DIDOUTPUT; for (i = 1; i >= 0; i--) { if (FD_ISSET(job->in[i].fd, actual_mask)) { nfds--; handle_job_output(job, i, false); } } if (job->flags & JOB_DIDOUTPUT) { if (waitpid(job->pid, &status, WNOHANG) == job->pid) { remove_job(ln, status); } else { Lst_Requeue(&runningJobs, ln); } } } }
static bool has_unmade_predecessor(GNode *gn) { LstNode ln; if (Lst_IsEmpty(&gn->preds)) return false; for (ln = Lst_First(&gn->preds); ln != NULL; ln = Lst_Adv(ln)) { GNode *pgn = (GNode *)Lst_Datum(ln); if (pgn->must_make && pgn->built_status == UNKNOWN) { if (DEBUG(MAKE)) printf("predecessor %s not made yet.\n", pgn->name); return true; } } return false; }
/*- *----------------------------------------------------------------------- * JobInterrupt -- * Handle the receipt of an interrupt. * * Side Effects: * All children are killed. Another job will be started if the * .INTERRUPT target was given. *----------------------------------------------------------------------- */ static void JobInterrupt(bool runINTERRUPT, /* true if commands for the .INTERRUPT * target should be executed */ int signo) /* signal received */ { LstNode ln; /* element in job table */ Job *job; /* job descriptor in that element */ aborting = ABORT_INTERRUPT; for (ln = Lst_First(&runningJobs); ln != NULL; ln = Lst_Adv(ln)) { job = (Job *)Lst_Datum(ln); if (!Targ_Precious(job->node)) { const char *file = job->node->path == NULL ? job->node->name : job->node->path; if (!noExecute && eunlink(file) != -1) { Error("*** %s removed", file); } } if (job->pid) { debug_printf("JobInterrupt passing signal to " "child %ld.\n", (long)job->pid); killpg(job->pid, signo); } } if (runINTERRUPT && !touchFlag) { if ((interrupt_node->type & OP_DUMMY) == 0) { ignoreErrors = false; JobStart(interrupt_node, 0); loop_handle_running_jobs(); } } exit(signo); }
/*- *--------------------------------------------------------------------- * ParseDoSrc -- * Given the name of a source, figure out if it is an attribute * and apply it to the targets if it is. Else decide if there is * some attribute which should be applied *to* the source because * of some special target and apply it if so. Otherwise, make the * source be a child of the targets in the list 'targets' * * Side Effects: * Operator bits may be added to the list of targets or to the source. * The targets may have a new source added to their lists of children. *--------------------------------------------------------------------- */ static void ParseDoSrc( struct growableArray *targets, struct growableArray *sources, int tOp, /* operator (if any) from special targets */ const char *src, /* name of the source to handle */ const char *esrc) { GNode *gn = Targ_FindNodei(src, esrc, TARG_CREATE); if ((gn->special & SPECIAL_SOURCE) != 0) { if (gn->special_op) { Array_FindP(targets, ParseDoOp, gn->special_op); return; } else { assert((gn->special & SPECIAL_MASK) == SPECIAL_WAIT); waiting++; return; } } switch (specType) { case SPECIAL_MAIN: /* * If we have noted the existence of a .MAIN, it means we need * to add the sources of said target to the list of things * to create. Note that this will only be invoked if the user * didn't specify a target on the command line. This is to * allow #ifmake's to succeed, or something... */ Lst_AtEnd(create, gn->name); /* * Add the name to the .TARGETS variable as well, so the user * can employ that, if desired. */ Var_Append(".TARGETS", gn->name); return; case SPECIAL_ORDER: /* * Create proper predecessor/successor links between the * previous source and the current one. */ if (predecessor != NULL) { Lst_AtEnd(&predecessor->successors, gn); Lst_AtEnd(&gn->preds, predecessor); } predecessor = gn; break; default: /* * In the case of a source that was the object of a :: operator, * the attribute is applied to all of its instances (as kept in * the 'cohorts' list of the node) or all the cohorts are linked * to all the targets. */ apply_op(targets, tOp, gn); if ((gn->type & OP_OPMASK) == OP_DOUBLEDEP) { LstNode ln; for (ln=Lst_First(&gn->cohorts); ln != NULL; ln = Lst_Adv(ln)){ apply_op(targets, tOp, (GNode *)Lst_Datum(ln)); } } break; } gn->order = waiting; Array_AtEnd(sources, gn); if (waiting) Array_Find(sources, ParseAddDep, gn); }
For * For_Eval(const char *line) { const char *ptr = line; const char *wrd; char *sub; const char *endVar; For *arg; unsigned long n; while (ISSPACE(*ptr)) ptr++; /* Parse loop. */ arg = emalloc(sizeof(*arg)); arg->nvars = 0; Lst_Init(&arg->vars); for (;;) { /* Grab the variables. */ for (wrd = ptr; *ptr && !ISSPACE(*ptr); ptr++) continue; if (ptr - wrd == 0) { Parse_Error(PARSE_FATAL, "Syntax error in for"); return 0; } endVar = ptr++; while (ISSPACE(*ptr)) ptr++; /* End of variable list ? */ if (endVar - wrd == 2 && wrd[0] == 'i' && wrd[1] == 'n') break; Lst_AtEnd(&arg->vars, Var_NewLoopVar(wrd, endVar)); arg->nvars++; } if (arg->nvars == 0) { Parse_Error(PARSE_FATAL, "Missing variable in for"); return 0; } /* Make a list with the remaining words. */ sub = Var_Subst(ptr, NULL, false); if (DEBUG(FOR)) { LstNode ln; (void)fprintf(stderr, "For: Iterator "); for (ln = Lst_First(&arg->vars); ln != NULL; ln = Lst_Adv(ln)) (void)fprintf(stderr, "%s ", Var_LoopVarName(Lst_Datum(ln))); (void)fprintf(stderr, "List %s\n", sub); } Lst_Init(&arg->lst); n = build_words_list(&arg->lst, sub); free(sub); if (arg->nvars != 1 && n % arg->nvars != 0) { LstNode ln; Parse_Error(PARSE_FATAL, "Wrong number of items in for loop"); (void)fprintf(stderr, "%lu items for %d variables:", n, arg->nvars); for (ln = Lst_First(&arg->lst); ln != NULL; ln = Lst_Adv(ln)) { char *p = Lst_Datum(ln); (void)fprintf(stderr, " %s", p); } (void)fprintf(stderr, "\n"); return 0; } arg->lineno = Parse_Getlineno(); arg->level = 1; Buf_Init(&arg->buf, 0); return arg; }
void Make_DoAllVar(GNode *gn) { GNode *child; LstNode ln; BUFFER allsrc, oodate; char *target; bool do_oodate; int oodate_count, allsrc_count = 0; oodate_count = 0; allsrc_count = 0; for (ln = Lst_First(&gn->children); ln != NULL; ln = Lst_Adv(ln)) { child = (GNode *)Lst_Datum(ln); if ((child->type & (OP_EXEC|OP_USE|OP_INVISIBLE)) != 0) continue; if (OP_NOP(child->type) || (target = Var(TARGET_INDEX, child)) == NULL) { /* * this node is only source; use the specific pathname * for it */ target = child->path != NULL ? child->path : child->name; } /* * It goes in the OODATE variable if the parent is younger than * the child or if the child has been modified more recently * than the start of the make. This is to keep make from * getting confused if something else updates the parent after * the make starts (shouldn't happen, I know, but sometimes it * does). In such a case, if we've updated the kid, the parent * is likely to have a modification time later than that of the * kid and anything that relies on the OODATE variable will be * hosed. */ do_oodate = false; if (gn->type & OP_JOIN) { if (child->built_status == MADE) do_oodate = true; } else if (is_strictly_before(gn->mtime, child->mtime) || (!is_strictly_before(child->mtime, now) && child->built_status == MADE)) do_oodate = true; if (do_oodate) { oodate_count++; if (oodate_count == 1) Var(OODATE_INDEX, gn) = target; else { if (oodate_count == 2) { Buf_Init(&oodate, 0); Buf_AddString(&oodate, Var(OODATE_INDEX, gn)); } Buf_AddSpace(&oodate); Buf_AddString(&oodate, target); } } allsrc_count++; if (allsrc_count == 1) Var(ALLSRC_INDEX, gn) = target; else { if (allsrc_count == 2) { Buf_Init(&allsrc, 0); Buf_AddString(&allsrc, Var(ALLSRC_INDEX, gn)); } Buf_AddSpace(&allsrc); Buf_AddString(&allsrc, target); } } if (allsrc_count > 1) Var(ALLSRC_INDEX, gn) = Buf_Retrieve(&allsrc); if (oodate_count > 1) Var(OODATE_INDEX, gn) = Buf_Retrieve(&oodate); if (gn->impliedsrc) Var(IMPSRC_INDEX, gn) = Var(TARGET_INDEX, gn->impliedsrc); if (gn->type & OP_JOIN) Var(TARGET_INDEX, gn) = Var(ALLSRC_INDEX, gn); }
/*- *----------------------------------------------------------------------- * JobExec -- * Execute the shell for the given job. Called from JobStart * * Side Effects: * A shell is executed, outputs is altered and the Job structure added * to the job table. *----------------------------------------------------------------------- */ static void JobExec(Job *job) { pid_t cpid; /* ID of new child */ struct job_pid *p; int fds[4]; int *fdout = fds; int *fderr = fds+2; int i; banner(job, stdout); setup_engine(1); /* Create the pipe by which we'll get the shell's output. */ if (pipe(fdout) == -1) Punt("Cannot create pipe: %s", strerror(errno)); if (pipe(fderr) == -1) Punt("Cannot create pipe: %s", strerror(errno)); block_signals(); if ((cpid = fork()) == -1) { Punt("Cannot fork"); unblock_signals(); } else if (cpid == 0) { supervise_jobs = false; /* standard pipe code to route stdout and stderr */ close(fdout[0]); if (dup2(fdout[1], 1) == -1) Punt("Cannot dup2(outPipe): %s", strerror(errno)); if (fdout[1] != 1) close(fdout[1]); close(fderr[0]); if (dup2(fderr[1], 2) == -1) Punt("Cannot dup2(errPipe): %s", strerror(errno)); if (fderr[1] != 2) close(fderr[1]); /* * We want to switch the child into a different process family * so we can kill it and all its descendants in one fell swoop, * by killing its process family, but not commit suicide. */ (void)setpgid(0, getpid()); if (random_delay) if (!(nJobs == 1 && no_jobs_left())) usleep(random() % random_delay); setup_all_signals(SigHandler, SIG_DFL); unblock_signals(); /* this exits directly */ run_gnode_parallel(job->node); /*NOTREACHED*/ } else { supervise_jobs = true; job->pid = cpid; /* we set the current position in the buffers to the beginning * and mark another stream to watch in the outputs mask */ for (i = 0; i < 2; i++) prepare_pipe(&job->in[i], fds+2*i); } /* * Now the job is actually running, add it to the table. */ nJobs++; Lst_AtEnd(&runningJobs, job); if (job->flags & JOB_IS_EXPENSIVE) expensive_job = true; p = emalloc(sizeof(struct job_pid)); p->pid = cpid; Lst_AtEnd(&job_pids, p); job->p = Lst_Last(&job_pids); unblock_signals(); if (DEBUG(JOB)) { LstNode ln; (void)fprintf(stdout, "Running %ld (%s)\n", (long)cpid, job->node->name); for (ln = Lst_First(&job->node->commands); ln != NULL ; ln = Lst_Adv(ln)) fprintf(stdout, "\t%s\n", (char *)Lst_Datum(ln)); (void)fflush(stdout); } }
/*- *----------------------------------------------------------------------- * Make_Update -- * Perform update on the parents of a node. Used by JobFinish once * a node has been dealt with and by MakeStartJobs if it finds an * up-to-date node. * * Results: * Always returns 0 * * Side Effects: * The unmade field of pgn is decremented and pgn may be placed on * the toBeMade queue if this field becomes 0. * * If the child was made, the parent's childMade field will be set true * and its cmtime set to now. * * If the child wasn't made, the cmtime field of the parent will be * altered if the child's mtime is big enough. * *----------------------------------------------------------------------- */ void Make_Update(GNode *cgn) /* the child node */ { GNode *pgn; /* the parent node */ LstNode ln; /* Element in parents list */ /* * If the child was actually made, see what its modification time is * now -- some rules won't actually update the file. If the file still * doesn't exist, make its mtime now. */ if (cgn->built_status != UPTODATE) { /* * This is what Make does and it's actually a good thing, as it * allows rules like * * cmp -s y.tab.h parse.h || cp y.tab.h parse.h * * to function as intended. Unfortunately, thanks to the * stateless nature of NFS, there are times when the * modification time of a file created on a remote machine * will not be modified before the local stat() implied by * the Dir_MTime occurs, thus leading us to believe that the * file is unchanged, wreaking havoc with files that depend * on this one. */ if (noExecute || is_out_of_date(Dir_MTime(cgn))) ts_set_from_now(cgn->mtime); if (DEBUG(MAKE)) printf("update time: %s\n", time_to_string(cgn->mtime)); } /* SIB: this is where I should mark the build as finished */ cgn->build_lock = false; for (ln = Lst_First(&cgn->parents); ln != NULL; ln = Lst_Adv(ln)) { pgn = (GNode *)Lst_Datum(ln); /* SIB: there should be a siblings loop there */ pgn->unmade--; if (pgn->must_make) { if (DEBUG(MAKE)) printf("%s--=%d ", pgn->name, pgn->unmade); if ( ! (cgn->type & (OP_EXEC|OP_USE))) { if (cgn->built_status == MADE) { pgn->childMade = true; if (is_strictly_before(pgn->cmtime, cgn->mtime)) pgn->cmtime = cgn->mtime; } else { (void)Make_TimeStamp(pgn, cgn); } } if (pgn->unmade == 0) { /* * Queue the node up -- any unmade * predecessors will be dealt with in * MakeStartJobs. */ if (DEBUG(MAKE)) printf("QUEUING "); Array_Push(&toBeMade, pgn); } else if (pgn->unmade < 0) { Error("Child %s discovered graph cycles through %s", cgn->name, pgn->name); } } } if (DEBUG(MAKE)) printf("\n"); requeue_successors(cgn); }
/*- * main -- * The main function, for obvious reasons. Initializes variables * and a few modules, then parses the arguments give it in the * environment and on the command line. Reads the system makefile * followed by either Makefile, makefile or the file given by the * -f argument. Sets the .MAKEFLAGS PMake variable based on all the * flags it has received by then uses either the Make or the Compat * module to create the initial list of targets. * * Results: * If -q was given, exits -1 if anything was out-of-date. Else it exits * 0. * * Side Effects: * The program exits when done. Targets are created. etc. etc. etc. */ int main(int argc, char **argv) { static LIST targs; /* target nodes to create */ bool outOfDate = true; /* false if all targets up to date */ char *machine = figure_out_MACHINE(); char *machine_arch = figure_out_MACHINE_ARCH(); char *machine_cpu = figure_out_MACHINE_CPU(); const char *syspath = _PATH_DEFSYSPATH; char *p; static struct dirs d; bool read_depend = true;/* false if we don't want to read .depend */ MainParseChdir(argc, argv); setup_CURDIR_OBJDIR(&d, machine); esetenv("PWD", d.object); unsetenv("CDPATH"); Static_Lst_Init(create); Static_Lst_Init(&makefiles); Static_Lst_Init(&varstoprint); Static_Lst_Init(&targs); beSilent = false; /* Print commands as executed */ ignoreErrors = false; /* Pay attention to non-zero returns */ noExecute = false; /* Execute all commands */ keepgoing = false; /* Stop on error */ allPrecious = false; /* Remove targets when interrupted */ queryFlag = false; /* This is not just a check-run */ noBuiltins = false; /* Read the built-in rules */ touchFlag = false; /* Actually update targets */ debug = 0; /* No debug verbosity, please. */ maxJobs = DEFMAXJOBS; compatMake = false; /* No compat mode */ /* * Initialize all external modules. */ Init(); if (d.object != d.current) Dir_AddDir(defaultPath, d.current); Var_Set(".CURDIR", d.current); Var_Set(".OBJDIR", d.object); Parse_setcurdir(d.current); Targ_setdirs(d.current, d.object); /* * Initialize various variables. * MAKE also gets this name, for compatibility * .MAKEFLAGS gets set to the empty string just in case. * MFLAGS also gets initialized empty, for compatibility. */ Var_Set("MAKE", argv[0]); Var_Set(".MAKE", argv[0]); Var_Set(MAKEFLAGS, ""); Var_Set("MFLAGS", ""); Var_Set("MACHINE", machine); Var_Set("MACHINE_ARCH", machine_arch); Var_Set("MACHINE_CPU", machine_cpu); /* * First snag any flags out of the MAKEFLAGS environment variable. */ Main_ParseArgLine(getenv("MAKEFLAGS")); basedirectory = getenv("MAKEBASEDIRECTORY"); if (basedirectory == NULL) setenv("MAKEBASEDIRECTORY", d.current, 0); MainParseArgs(argc, argv); /* * Be compatible if user did not specify -j */ if (!forceJobs) compatMake = true; /* And set up everything for sub-makes */ Var_AddCmdline(MAKEFLAGS); /* * Set up the .TARGETS variable to contain the list of targets to be * created. If none specified, make the variable empty -- the parser * will fill the thing in with the default or .MAIN target. */ if (!Lst_IsEmpty(create)) { LstNode ln; for (ln = Lst_First(create); ln != NULL; ln = Lst_Adv(ln)) { char *name = (char *)Lst_Datum(ln); if (strcmp(name, "depend") == 0) read_depend = false; Var_Append(".TARGETS", name); } } else Var_Set(".TARGETS", ""); /* * If no user-supplied system path was given (through the -m option) * add the directories from the DEFSYSPATH (more than one may be given * as dir1:...:dirn) to the system include path. */ if (Lst_IsEmpty(systemIncludePath)) add_dirpath(systemIncludePath, syspath); read_all_make_rules(noBuiltins, read_depend, &makefiles, &d); Var_Append("MFLAGS", Var_Value(MAKEFLAGS)); /* Install all the flags into the MAKEFLAGS env variable. */ if (((p = Var_Value(MAKEFLAGS)) != NULL) && *p) esetenv("MAKEFLAGS", p); setup_VPATH(); process_suffixes_after_makefile_is_read(); if (dumpData) { dump_data(); exit(0); } /* Print the initial graph, if the user requested it. */ if (DEBUG(GRAPH1)) dump_data(); /* Print the values of any variables requested by the user. */ if (!Lst_IsEmpty(&varstoprint)) { LstNode ln; for (ln = Lst_First(&varstoprint); ln != NULL; ln = Lst_Adv(ln)) { char *value = Var_Value((char *)Lst_Datum(ln)); printf("%s\n", value ? value : ""); } } else { /* Have now read the entire graph and need to make a list * of targets to create. If none was given on the command * line, we consult the parsing module to find the main * target(s) to create. */ if (Lst_IsEmpty(create)) Parse_MainName(&targs); else Targ_FindList(&targs, create); Job_Init(maxJobs); /* If the user has defined a .BEGIN target, execute the commands * attached to it. */ if (!queryFlag) Job_Begin(); if (compatMake) /* Compat_Init will take care of creating all the * targets as well as initializing the module. */ Compat_Run(&targs); else { /* Traverse the graph, checking on all the targets. */ outOfDate = Make_Run(&targs); } } /* print the graph now it's been processed if the user requested it */ if (DEBUG(GRAPH2)) post_mortem(); if (queryFlag && outOfDate) return 1; else return 0; }