/*- *----------------------------------------------------------------------- * Lst_Insert -- * Insert a new node with the given piece of data before the given * node in the given list. * * Side Effects: * the firstPtr field will be changed if ln is the first node in the * list. * *----------------------------------------------------------------------- */ void Lst_Insert(Lst l, LstNode before, void *d) { LstNode nLNode; if (before == NULL && !Lst_IsEmpty(l)) return; if (before != NULL && Lst_IsEmpty(l)) return; PAlloc(nLNode, LstNode); nLNode->datum = d; if (before == NULL) { nLNode->prevPtr = nLNode->nextPtr = NULL; l->firstPtr = l->lastPtr = nLNode; } else { nLNode->prevPtr = before->prevPtr; nLNode->nextPtr = before; if (nLNode->prevPtr != NULL) nLNode->prevPtr->nextPtr = nLNode; before->prevPtr = nLNode; if (before == l->firstPtr) l->firstPtr = nLNode; } }
/*- *----------------------------------------------------------------------- * Lst_Append -- * Create a new node and add it to the given list after the given node. * * Side Effects: * A new ListNode is created and linked in to the List. The lastPtr * field of the List will be altered if ln is the last node in the * list. lastPtr and firstPtr will alter if the list was empty and * ln was NULL. * *----------------------------------------------------------------------- */ void Lst_Append(Lst l, LstNode after, void *d) { LstNode nLNode; if (after == NULL && !Lst_IsEmpty(l)) return; if (after != NULL && Lst_IsEmpty(l)) return; PAlloc(nLNode, LstNode); nLNode->datum = d; if (after == NULL) { nLNode->nextPtr = nLNode->prevPtr = NULL; l->firstPtr = l->lastPtr = nLNode; } else { nLNode->prevPtr = after; nLNode->nextPtr = after->nextPtr; after->nextPtr = nLNode; if (nLNode->nextPtr != NULL) nLNode->nextPtr->prevPtr = nLNode; if (after == l->lastPtr) l->lastPtr = nLNode; } }
static void read_all_make_rules(bool noBuiltins, bool read_depend, Lst makefiles, struct dirs *d) { /* * Read in the built-in rules first, followed by the specified * makefile(s), or the default Makefile or makefile, in that order. */ if (!noBuiltins) { LIST sysMkPath; /* Path of sys.mk */ Lst_Init(&sysMkPath); Dir_Expand(_PATH_DEFSYSMK, systemIncludePath, &sysMkPath); if (Lst_IsEmpty(&sysMkPath)) Fatal("make: no system rules (%s).", _PATH_DEFSYSMK); read_makefile_list(&sysMkPath, d); } if (!Lst_IsEmpty(makefiles)) { read_makefile_list(makefiles, d); } else if (!ReadMakefile("makefile", d)) (void)ReadMakefile("Makefile", d); /* read a .depend file, if it exists, and we're not building depend */ if (read_depend) (void)ReadMakefile(".depend", d); Parse_End(); }
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); } }
void run_gnode_parallel(GNode *gn) { char *cmd; gn->built_status = MADE; /* XXX don't bother freeing cmd, we're dead anyways ! */ while ((cmd = Lst_DeQueue(&gn->expanded)) != NULL) { if (setup_and_run_command(cmd, gn, Lst_IsEmpty(&gn->expanded)) == 0) break; } /* Normally, we don't reach this point, unless the last command * ignores error, in which case we interpret the status ourselves. */ switch(gn->built_status) { case MADE: exit(0); case ERROR: exit(1); default: fprintf(stderr, "Could not run gnode, returned %d\n", gn->built_status); exit(1); } }
/*- *----------------------------------------------------------------------- * CondDoCommands -- * See if the given node exists and is an actual target with commands * associated with it. * * Results: * TRUE if the node exists as a target and has commands associated with * it and FALSE if it does not. * * Side Effects: * None. * *----------------------------------------------------------------------- */ static Boolean CondDoCommands(int argLen MAKE_ATTR_UNUSED, const char *arg) { GNode *gn; gn = Targ_FindNode(arg, TARG_NOCREATE); return (gn != NULL) && !OP_NOP(gn->type) && !Lst_IsEmpty(gn->commands); }
/* * Do we need/want a .meta file ? */ static Boolean meta_needed(GNode *gn, const char *dname, const char *tname, char *objdir, int verbose) { struct stat fs; if (verbose) verbose = DEBUG(META); /* This may be a phony node which we don't want meta data for... */ /* Skip .meta for .BEGIN, .END, .ERROR etc as well. */ /* Or it may be explicitly flagged as .NOMETA */ SKIP_META_TYPE(NOMETA); /* Unless it is explicitly flagged as .META */ if (!(gn->type & OP_META)) { SKIP_META_TYPE(PHONY); SKIP_META_TYPE(SPECIAL); SKIP_META_TYPE(MAKE); } /* Check if there are no commands to execute. */ if (Lst_IsEmpty(gn->commands)) { if (verbose) fprintf(debug_file, "Skipping meta for %s: no commands\n", gn->name); return FALSE; } if ((gn->type & (OP_META|OP_SUBMAKE)) == OP_SUBMAKE) { /* OP_SUBMAKE is a bit too aggressive */ if (Lst_ForEach(gn->commands, is_submake, gn)) { if (DEBUG(META)) fprintf(debug_file, "Skipping meta for %s: .SUBMAKE\n", gn->name); return FALSE; } } /* The object directory may not exist. Check it.. */ if (cached_stat(dname, &fs) != 0) { if (verbose) fprintf(debug_file, "Skipping meta for %s: no .OBJDIR\n", gn->name); return FALSE; } /* make sure these are canonical */ if (cached_realpath(dname, objdir)) dname = objdir; /* If we aren't in the object directory, don't create a meta file. */ if (!metaCurdirOk && strcmp(curdir, dname) == 0) { if (verbose) fprintf(debug_file, "Skipping meta for %s: .OBJDIR == .CURDIR\n", gn->name); return FALSE; } return TRUE; }
bool Job_CheckCommands(GNode *gn) { /* Alter our type to tell if errors should be ignored or things * should not be printed so setup_and_run_command knows what to do. */ if (Targ_Ignore(gn)) gn->type |= OP_IGNORE; if (Targ_Silent(gn)) gn->type |= OP_SILENT; if (OP_NOP(gn->type) && Lst_IsEmpty(&gn->commands) && (gn->type & OP_LIB) == 0) { /* * No commands. Look for .DEFAULT rule from which we might infer * commands */ if ((gn->type & OP_NODEFAULT) == 0 && (DEFAULT->type & OP_DUMMY) == 0 && !Lst_IsEmpty(&DEFAULT->commands)) { /* * Make only looks for a .DEFAULT if the node was never * the target of an operator, so that's what we do too. * If a .DEFAULT was given, we substitute its commands * for gn's commands and set the IMPSRC variable to be * the target's name The DEFAULT node acts like a * transformation rule, in that gn also inherits any * attributes or sources attached to .DEFAULT itself. */ Make_HandleUse(DEFAULT, gn); Var(IMPSRC_INDEX, gn) = Var(TARGET_INDEX, gn); } else if (is_out_of_date(Dir_MTime(gn))) { /* * The node wasn't the target of an operator we have no * .DEFAULT rule to go on and the target doesn't * already exist. There's nothing more we can do for * this branch. */ return false; } } return true; }
/*- *----------------------------------------------------------------------- * Arch_LibOODate -- * Decide if a node with the OP_LIB attribute is out-of-date. Called * from Make_OODate to make its life easier. * * There are several ways for a library to be out-of-date that are * not available to ordinary files. In addition, there are ways * that are open to regular files that are not available to * libraries. A library that is only used as a source is never * considered out-of-date by itself. This does not preclude the * library's modification time from making its parent be out-of-date. * A library will be considered out-of-date for any of these reasons, * given that it is a target on a dependency line somewhere: * Its modification time is less than that of one of its * sources (gn->mtime < gn->cmgn->mtime). * Its modification time is greater than the time at which the * make began (i.e. it's been modified in the course * of the make, probably by archiving). * The modification time of one of its sources is greater than * the one of its RANLIBMAG member (i.e. its table of contents * is out-of-date). We don't compare of the archive time * vs. TOC time because they can be too close. In my * opinion we should not bother with the TOC at all since * this is used by 'ar' rules that affect the data contents * of the archive, not by ranlib rules, which affect the * TOC. * * Input: * gn The library's graph node * * Results: * TRUE if the library is out-of-date. FALSE otherwise. * * Side Effects: * The library will be hashed if it hasn't been already. * *----------------------------------------------------------------------- */ Boolean Arch_LibOODate(GNode *gn) { Boolean oodate; if (gn->type & OP_PHONY) { oodate = TRUE; } else if (OP_NOP(gn->type) && Lst_IsEmpty(gn->children)) { oodate = FALSE; } else if ((!Lst_IsEmpty(gn->children) && gn->cmgn == NULL) || (gn->mtime > now) || (gn->cmgn != NULL && gn->mtime < gn->cmgn->mtime)) { oodate = TRUE; } else { #ifdef RANLIBMAG struct ar_hdr *arhPtr; /* Header for __.SYMDEF */ int modTimeTOC; /* The table-of-contents's mod time */ arhPtr = ArchStatMember(gn->path, UNCONST(RANLIBMAG), FALSE); if (arhPtr != NULL) { modTimeTOC = (int)strtol(arhPtr->AR_DATE, NULL, 10); if (DEBUG(ARCH) || DEBUG(MAKE)) { fprintf(debug_file, "%s modified %s...", RANLIBMAG, Targ_FmtTime(modTimeTOC)); } oodate = (gn->cmgn == NULL || gn->cmgn->mtime > modTimeTOC); } else { /* * A library w/o a table of contents is out-of-date */ if (DEBUG(ARCH) || DEBUG(MAKE)) { fprintf(debug_file, "No t.o.c...."); } oodate = TRUE; } #else oodate = FALSE; #endif } return (oodate); }
/*********************************************************************** * AllocPrefix *********************************************************************** * SYNOPSIS: Allocate a new Prefix record * CALLED BY: EXTERNAL * RETURN: The Prefix * with only the generation field set. * SIDE EFFECTS: * * STRATEGY: * * REVISION HISTORY: * Name Date Description * ---- ---- ----------- * ardeb 10/ 9/89 Initial Revision * ***********************************************************************/ Prefix * AllocPrefix() { Prefix *pp; if (Lst_IsEmpty(freePrefixes)) { pp = (Prefix *)malloc(sizeof(Prefix)); pp->generation = 0; } else { pp = (Prefix *)Lst_DeQueue(freePrefixes); } return(pp); }
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 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; }
static FILE * meta_create(BuildMon *pbm, GNode *gn) { meta_file_t mf; char buf[MAXPATHLEN]; char objdir[MAXPATHLEN]; char **ptr; const char *dname; const char *tname; char *fname; const char *cp; char *p[4]; /* >= possible uses */ int i; struct stat fs; /* This may be a phony node which we don't want meta data for... */ /* Skip .meta for .BEGIN, .END, .ERROR etc as well. */ /* Or it may be explicitly flagged as .NOMETA */ SKIP_META_TYPE(NOMETA); /* Unless it is explicitly flagged as .META */ if (!(gn->type & OP_META)) { SKIP_META_TYPE(PHONY); SKIP_META_TYPE(SPECIAL); SKIP_META_TYPE(MAKE); } mf.fp = NULL; i = 0; dname = Var_Value(".OBJDIR", gn, &p[i++]); tname = Var_Value(TARGET, gn, &p[i++]); /* The object directory may not exist. Check it.. */ if (stat(dname, &fs) != 0) { if (DEBUG(META)) fprintf(debug_file, "Skipping meta for %s: no .OBJDIR\n", gn->name); goto out; } /* Check if there are no commands to execute. */ if (Lst_IsEmpty(gn->commands)) { if (DEBUG(META)) fprintf(debug_file, "Skipping meta for %s: no commands\n", gn->name); goto out; } /* make sure these are canonical */ if (realpath(dname, objdir)) dname = objdir; /* If we aren't in the object directory, don't create a meta file. */ if (!metaCurdirOk && strcmp(curdir, dname) == 0) { if (DEBUG(META)) fprintf(debug_file, "Skipping meta for %s: .OBJDIR == .CURDIR\n", gn->name); goto out; } if (!(gn->type & OP_META)) { /* We do not generate .meta files for sub-makes */ if (Lst_ForEach(gn->commands, is_submake, gn)) { if (DEBUG(META)) fprintf(debug_file, "Skipping meta for %s: .MAKE\n", gn->name); goto out; } } if (metaVerbose) { char *mp; /* Describe the target we are building */ mp = Var_Subst(NULL, "${" MAKE_META_PREFIX "}", gn, 0); if (*mp) fprintf(stdout, "%s\n", mp); free(mp); } /* Get the basename of the target */ if ((cp = strrchr(tname, '/')) == NULL) { cp = tname; } else { cp++; } fflush(stdout); if (strcmp(cp, makeDependfile) == 0) goto out; if (!writeMeta) /* Don't create meta data. */ goto out; fname = meta_name(gn, pbm->meta_fname, sizeof(pbm->meta_fname), dname, tname); #ifdef DEBUG_META_MODE if (DEBUG(META)) fprintf(debug_file, "meta_create: %s\n", fname); #endif if ((mf.fp = fopen(fname, "w")) == NULL) err(1, "Could not open meta file '%s'", fname); fprintf(mf.fp, "# Meta data file %s\n", fname); mf.gn = gn; Lst_ForEach(gn->commands, printCMD, &mf); fprintf(mf.fp, "CWD %s\n", getcwd(buf, sizeof(buf))); fprintf(mf.fp, "TARGET %s\n", tname); if (metaEnv) { for (ptr = environ; *ptr != NULL; ptr++) fprintf(mf.fp, "ENV %s\n", *ptr); } fprintf(mf.fp, "-- command output --\n"); fflush(mf.fp); Var_Append(".MAKE.META.FILES", fname, VAR_GLOBAL); Var_Append(".MAKE.META.CREATED", fname, VAR_GLOBAL); gn->type |= OP_META; /* in case anyone wants to know */ if (metaSilent) { gn->type |= OP_SILENT; } out: for (i--; i >= 0; i--) { if (p[i]) free(p[i]); } return (mf.fp); }
/*- *----------------------------------------------------------------------- * 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. * * Input: * cgn the child 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 flag CHILDMADE field will be * set true. * * If the child is not up-to-date and still does not exist, * set the FORCE flag on the parents. * * If the child wasn't made, the cmgn field of the parent will be * altered if the child's mtime is big enough. * * Finally, if the child is the implied source for the parent, the * parent's IMPSRC variable is set appropriately. * *----------------------------------------------------------------------- */ void Make_Update(GNode *cgn) { GNode *pgn; /* the parent node */ char *cname; /* the child's name */ LstNode ln; /* Element in parents and iParents lists */ time_t mtime = -1; char *p1; Lst parents; GNode *centurion; /* It is save to re-examine any nodes again */ checked++; cname = Var_Value(TARGET, cgn, &p1); free(p1); if (DEBUG(MAKE)) fprintf(debug_file, "Make_Update: %s%s\n", cgn->name, cgn->cohort_num); /* * 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->made != UPTODATE) { mtime = Make_Recheck(cgn); } /* * If this is a `::' node, we must consult its first instance * which is where all parents are linked. */ if ((centurion = cgn->centurion) != NULL) { if (!Lst_IsEmpty(cgn->parents)) Punt("%s%s: cohort has parents", cgn->name, cgn->cohort_num); centurion->unmade_cohorts -= 1; if (centurion->unmade_cohorts < 0) Error("Graph cycles through centurion %s", centurion->name); } else { centurion = cgn; } parents = centurion->parents; /* If this was a .ORDER node, schedule the RHS */ Lst_ForEach(centurion->order_succ, MakeBuildParent, Lst_First(toBeMade)); /* Now mark all the parents as having one less unmade child */ if (Lst_Open(parents) == SUCCESS) { while ((ln = Lst_Next(parents)) != NULL) { pgn = (GNode *)Lst_Datum(ln); if (DEBUG(MAKE)) fprintf(debug_file, "inspect parent %s%s: flags %x, " "type %x, made %d, unmade %d ", pgn->name, pgn->cohort_num, pgn->flags, pgn->type, pgn->made, pgn->unmade-1); if (!(pgn->flags & REMAKE)) { /* This parent isn't needed */ if (DEBUG(MAKE)) fprintf(debug_file, "- not needed\n"); continue; } if (mtime == 0 && !(cgn->type & OP_WAIT)) pgn->flags |= FORCE; /* * If the parent has the .MADE attribute, its timestamp got * updated to that of its newest child, and its unmake * child count got set to zero in Make_ExpandUse(). * However other things might cause us to build one of its * children - and so we mustn't do any processing here when * the child build finishes. */ if (pgn->type & OP_MADE) { if (DEBUG(MAKE)) fprintf(debug_file, "- .MADE\n"); continue; } if ( ! (cgn->type & (OP_EXEC|OP_USE|OP_USEBEFORE))) { if (cgn->made == MADE) pgn->flags |= CHILDMADE; (void)Make_TimeStamp(pgn, cgn); } /* * A parent must wait for the completion of all instances * of a `::' dependency. */ if (centurion->unmade_cohorts != 0 || centurion->made < MADE) { if (DEBUG(MAKE)) fprintf(debug_file, "- centurion made %d, %d unmade cohorts\n", centurion->made, centurion->unmade_cohorts); continue; } /* One more child of this parent is now made */ pgn->unmade -= 1; if (pgn->unmade < 0) { if (DEBUG(MAKE)) { fprintf(debug_file, "Graph cycles through %s%s\n", pgn->name, pgn->cohort_num); Targ_PrintGraph(2); } Error("Graph cycles through %s%s", pgn->name, pgn->cohort_num); } /* We must always rescan the parents of .WAIT and .ORDER nodes. */ if (pgn->unmade != 0 && !(centurion->type & OP_WAIT) && !(centurion->flags & DONE_ORDER)) { if (DEBUG(MAKE)) fprintf(debug_file, "- unmade children\n"); continue; } if (pgn->made != DEFERRED) { /* * Either this parent is on a different branch of the tree, * or it on the RHS of a .WAIT directive * or it is already on the toBeMade list. */ if (DEBUG(MAKE)) fprintf(debug_file, "- not deferred\n"); continue; } if (pgn->order_pred && Lst_ForEach(pgn->order_pred, MakeCheckOrder, 0)) { /* A .ORDER rule stops us building this */ continue; } if (DEBUG(MAKE)) { static int two = 2; fprintf(debug_file, "- %s%s made, schedule %s%s (made %d)\n", cgn->name, cgn->cohort_num, pgn->name, pgn->cohort_num, pgn->made); Targ_PrintNode(pgn, &two); } /* Ok, we can schedule the parent again */ pgn->made = REQUESTED; (void)Lst_EnQueue(toBeMade, pgn); } Lst_Close(parents); } /* * Set the .PREFIX and .IMPSRC variables for all the implied parents * of this node. */ if (Lst_Open(cgn->iParents) == SUCCESS) { char *cpref = Var_Value(PREFIX, cgn, &p1); while ((ln = Lst_Next(cgn->iParents)) != NULL) { pgn = (GNode *)Lst_Datum(ln); if (pgn->flags & REMAKE) { Var_Set(IMPSRC, cname, pgn, 0); if (cpref != NULL) Var_Set(PREFIX, cpref, pgn, 0); } } free(p1); Lst_Close(cgn->iParents); } }
/*- *----------------------------------------------------------------------- * Make_Recheck -- * Check the modification time of a gnode, and update it as described * in the comments below. * * Results: * returns 0 if the gnode does not exist, or its filesystem * time if it does. * * Side Effects: * the gnode's modification time and path name are affected. * *----------------------------------------------------------------------- */ time_t Make_Recheck(GNode *gn) { time_t mtime = Dir_MTime(gn, 1); #ifndef RECHECK /* * We can't re-stat the thing, but we can at least take care of rules * where a target depends on a source that actually creates the * target, but only if it has changed, e.g. * * parse.h : parse.o * * parse.o : parse.y * yacc -d parse.y * cc -c y.tab.c * mv y.tab.o parse.o * cmp -s y.tab.h parse.h || mv y.tab.h parse.h * * In this case, if the definitions produced by yacc haven't changed * from before, parse.h won't have been updated and gn->mtime will * reflect the current modification time for parse.h. This is * something of a kludge, I admit, but it's a useful one.. * XXX: People like to use a rule like * * FRC: * * To force things that depend on FRC to be made, so we have to * check for gn->children being empty as well... */ if (!Lst_IsEmpty(gn->commands) || Lst_IsEmpty(gn->children)) { gn->mtime = now; } #else /* * 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 (by which I mean the loose coupling of two clients * using the same file from a common server), 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. * * I have decided it is better to make too much than to make too * little, so this stuff is commented out unless you're sure it's ok. * -- ardeb 1/12/88 */ /* * Christos, 4/9/92: If we are saving commands pretend that * the target is made now. Otherwise archives with ... rules * don't work! */ if (NoExecute(gn) || (gn->type & OP_SAVE_CMDS) || (mtime == 0 && !(gn->type & OP_WAIT))) { if (DEBUG(MAKE)) { fprintf(debug_file, " recheck(%s): update time from %s to now\n", gn->name, Targ_FmtTime(gn->mtime)); } gn->mtime = now; } else { if (DEBUG(MAKE)) { fprintf(debug_file, " recheck(%s): current update time: %s\n", gn->name, Targ_FmtTime(gn->mtime)); } } #endif return mtime; }
/*- *----------------------------------------------------------------------- * Make_HandleUse -- * Function called by Make_Run and SuffApplyTransform on the downward * pass to handle .USE and transformation nodes. It implements the * .USE and transformation functionality by copying the node's commands, * type flags and children to the parent node. * * A .USE node is much like an explicit transformation rule, except * its commands are always added to the target node, even if the * target already has commands. * * Input: * cgn The .USE node * pgn The target of the .USE node * * Results: * none * * Side Effects: * Children and commands may be added to the parent and the parent's * type may be changed. * *----------------------------------------------------------------------- */ void Make_HandleUse(GNode *cgn, GNode *pgn) { LstNode ln; /* An element in the children list */ #ifdef DEBUG_SRC if ((cgn->type & (OP_USE|OP_USEBEFORE|OP_TRANSFORM)) == 0) { fprintf(debug_file, "Make_HandleUse: called for plain node %s\n", cgn->name); return; } #endif if ((cgn->type & (OP_USE|OP_USEBEFORE)) || Lst_IsEmpty(pgn->commands)) { if (cgn->type & OP_USEBEFORE) { /* * .USEBEFORE -- * prepend the child's commands to the parent. */ Lst cmds = pgn->commands; pgn->commands = Lst_Duplicate(cgn->commands, NULL); (void)Lst_Concat(pgn->commands, cmds, LST_CONCNEW); Lst_Destroy(cmds, NULL); } else { /* * .USE or target has no commands -- * append the child's commands to the parent. */ (void)Lst_Concat(pgn->commands, cgn->commands, LST_CONCNEW); } } if (Lst_Open(cgn->children) == SUCCESS) { while ((ln = Lst_Next(cgn->children)) != NULL) { GNode *tgn, *gn = (GNode *)Lst_Datum(ln); /* * Expand variables in the .USE node's name * and save the unexpanded form. * We don't need to do this for commands. * They get expanded properly when we execute. */ if (gn->uname == NULL) { gn->uname = gn->name; } else { free(gn->name); } gn->name = Var_Subst(NULL, gn->uname, pgn, VARF_WANTRES); if (gn->name && gn->uname && strcmp(gn->name, gn->uname) != 0) { /* See if we have a target for this node. */ tgn = Targ_FindNode(gn->name, TARG_NOCREATE); if (tgn != NULL) gn = tgn; } (void)Lst_AtEnd(pgn->children, gn); (void)Lst_AtEnd(gn->parents, pgn); pgn->unmade += 1; } Lst_Close(cgn->children); } pgn->type |= cgn->type & ~(OP_OPMASK|OP_USE|OP_USEBEFORE|OP_TRANSFORM); }
/*- * 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) { Lst targs; /* target nodes to create -- passed to Make_Init */ Boolean outOfDate; /* FALSE if all targets up to date */ Boolean hadErrors; /* TRUE if we had errors */ const char *p; create = Lst_Init(); makefiles = Lst_Init(); 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. */ jobsRunning = FALSE; noisy = FALSE; maxJobs = 0; /* Set default max concurrency */ maxLocal = -1; /* Set default local max concurrency */ /* * create internal commonly used strings */ string_init(); sNULL = string_create(""); sDOT = string_create("."); sTARGET = string_create("@"); sOODATE = string_create("?"); sALLSRC = string_create(">"); sIMPSRC = string_create("<"); sPREFIX = string_create("*"); sARCHIVE = string_create("!"); sMEMBER = string_create("%"); s_TARGET = string_create(".TARGET"); s_OODATE = string_create(".OODATE"); s_ALLSRC = string_create(".ALLSRC"); s_IMPSRC = string_create(".IMPSRC"); s_PREFIX = string_create(".PREFIX"); s_ARCHIVE = string_create(".ARCHIVE"); s_MEMBER = string_create(".MEMBER"); s_TARGETS = string_create(".TARGETS"); s_INCLUDES = string_create(".INCLUDES"); s_LIBS = string_create(".LIBS"); sMAKE = string_create("MAKE"); sMAKEFLAGS = string_create("MAKEFLAGS"); sMFLAGS = string_create("MFLAGS"); sMACHINE = string_create("MACHINE"); sNPROC = string_create("NPROC"); sVPATH = string_create("VPATH"); sMAKEFILE = string_create("MAKEFILE"); sLIBSUFF = string_create(LIBSUFF); sMAKEOBJDIR = string_create("MAKEOBJDIR"); sMAKESRCDIRPATH = string_create("MAKESRCDIRPATH"); sMAKEDIR = string_create("MAKEDIR"); sMAKETOP = string_create("MAKETOP"); sMAKESUB = string_create("MAKESUB"); s_DEFAULT = string_create(".DEFAULT"); s_INTERRUPT = string_create(".INTERRUPT"); s_BEGIN = string_create(".BEGIN"); s_END = string_create(".END"); s_ERROR = string_create(".ERROR"); s_EXIT = string_create(".EXIT"); /* * Initialize the parsing, directory and variable modules to prepare * for the reading of inclusion paths and variable settings on the * command line */ Dir_Init(); /* Initialize directory structures so -I flags * can be processed correctly */ Parse_Init(); /* Need to initialize the paths of #include * directories */ Var_Init(); /* As well as the lists of variables for * parsing arguments */ /* * 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(sMAKE, string_create(argv[0]), VAR_GLOBAL); Var_Set(sMAKEFLAGS, sNULL, VAR_GLOBAL); Var_Set(sMFLAGS, sNULL, VAR_GLOBAL); Var_Set(sMACHINE, string_create(MACHINE), VAR_GLOBAL); Var_Set(sNPROC, string_create((p = getenv("NPROC")) ? p : "1"), VAR_GLOBAL); /* * First snag any flags out of the MAKE environment variable. * (Note this is *not* MAKEFLAGS since /bin/make uses that and it's * in a different format). */ Main_ParseArgLine(getenv("MAKEFLAGS")); MainParseArgs(argc, argv); Cond_Setup(); /* * Now make sure that maxJobs and maxLocal are positive. If * not, use the value of NPROC (or 1 if NPROC is not positive) */ { int nproc = atoi(Var_Value(sNPROC, VAR_GLOBAL)); if (nproc < 1) nproc = 1; if (maxJobs < 1) maxJobs = nproc; } /* * Initialize archive, target and suffix modules in preparation for * parsing the makefile(s) */ Arch_Init(); Targ_Init(); Suff_Init(); DEFAULT = NILGNODE; (void)time(&now); /* * 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 != NILLNODE; ln = Lst_Succ(ln)) { string_t name = (string_t)Lst_Datum(ln); Var_Append(s_TARGETS, name, VAR_GLOBAL); } } else Var_Set(s_TARGETS, sNULL, VAR_GLOBAL); /* * Locate the correct working directory for make. Adjust for any * movement taken from the directory of our parent if we are a * sub-make. */ MakeSetWorkingDir("Makeconf"); /* * Read in the built-in rules first, followed by the specified * makefile, if it was (makefile != (char *) NULL), or the default * Makefile and makefile, in that order, if it wasn't. */ if (!noBuiltins && !ReadMakefile(_PATH_DEFSYSMK, TRUE)) Fatal("make: no system rules (%s).", _PATH_DEFSYSMK); if (!Lst_IsEmpty(makefiles)) { LstNode ln; ln = Lst_Find(makefiles, (ClientData)FALSE, ReadMakefileP); if (ln != NILLNODE) Fatal("make: cannot open %s.", (char *)Lst_Datum(ln)); } else if (!ReadMakefile("makefile", FALSE)) (void)ReadMakefile("Makefile", FALSE); Var_Append(sMFLAGS, Var_StrValue(sMAKEFLAGS, VAR_GLOBAL), VAR_GLOBAL); /* Install all the flags into the MAKEFLAGS envariable. */ if ((p = Var_Value(sMAKEFLAGS, VAR_GLOBAL)) && *p) setenv("MAKEFLAGS", p, 1); /* * For compatibility, look at the directories in the VPATH variable * and add them to the search path, if the variable is defined. The * variable's value is in the same format as the PATH envariable, i.e. * <directory>:<directory>:<directory>... */ if (Var_Exists(sVPATH, VAR_CMD)) { char *vpath, *path, *cp, savec; /* * GCC stores string constants in read-only memory, but * Var_Subst will want to write this thing, so store it * in an array */ static char VPATH[] = "${VPATH}"; vpath = Var_Subst(VPATH, VAR_CMD, FALSE); path = vpath; do { /* skip to end of directory */ for (cp = path; *cp != ':' && *cp != '\0'; cp++); /* Save terminator character so know when to stop */ savec = *cp; *cp = '\0'; /* Add directory to search path */ Dir_AddDir(dirSearchPath, string_create(path)); *cp = savec; path = cp + 1; } while (savec == ':'); (void)free((Address)vpath); } /* * Now that all search paths have been read for suffixes et al, it's * time to add the default search path to their lists... */ Suff_DoPaths(); /* print the initial graph, if the user requested it */ if (DEBUG(GRAPH1)) Targ_PrintGraph(1); /* * 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)) targs = Parse_MainName(); else targs = Targ_FindList(create, TARG_CREATE); /* * Initialize job module before traversing the graph, now that * any .BEGIN and .END targets have been read. This is done * only if the -q flag wasn't given (to prevent the .BEGIN from * being executed should it exist). */ if (!queryFlag) { if (maxLocal < 0 || maxLocal > maxJobs) maxLocal = maxJobs; Job_Init(maxJobs, maxLocal); jobsRunning = TRUE; } /* Traverse the graph, checking on all the targets */ outOfDate = Make_Run(targs, &hadErrors); /* print the graph now it's been processed if the user requested it */ if (DEBUG(GRAPH2)) Targ_PrintGraph(2); return (hadErrors || (queryFlag && outOfDate)); }
/*- * ReadMakefile -- * Open and parse the given makefile. * * Results: * TRUE if ok. FALSE if couldn't open file. * * Side Effects: * lots */ static Boolean ReadMakefile(ClientData p, ClientData q __unused) { char *fname = p; /* makefile to read */ FILE *stream; size_t len = MAXPATHLEN; char *name, *path = emalloc(len); int setMAKEFILE; if (!strcmp(fname, "-")) { Parse_File("(stdin)", stdin); Var_Set("MAKEFILE", "", VAR_GLOBAL, 0); } else { setMAKEFILE = strcmp(fname, ".depend"); /* if we've chdir'd, rebuild the path name */ if (strcmp(curdir, objdir) && *fname != '/') { size_t plen = strlen(curdir) + strlen(fname) + 2; if (len < plen) path = erealloc(path, len = 2 * plen); (void)snprintf(path, len, "%s/%s", curdir, fname); if ((stream = fopen(path, "r")) != NULL) { fname = path; goto found; } /* If curdir failed, try objdir (ala .depend) */ plen = strlen(objdir) + strlen(fname) + 2; if (len < plen) path = erealloc(path, len = 2 * plen); (void)snprintf(path, len, "%s/%s", objdir, fname); if ((stream = fopen(path, "r")) != NULL) { fname = path; goto found; } } else if ((stream = fopen(fname, "r")) != NULL) goto found; /* look in -I and system include directories. */ name = Dir_FindFile(fname, parseIncPath); if (!name) name = Dir_FindFile(fname, Lst_IsEmpty(sysIncPath) ? defIncPath : sysIncPath); if (!name || !(stream = fopen(name, "r"))) { free(path); return(FALSE); } fname = name; /* * set the MAKEFILE variable desired by System V fans -- the * placement of the setting here means it gets set to the last * makefile specified, as it is set by SysV make. */ found: if (setMAKEFILE) Var_Set("MAKEFILE", fname, VAR_GLOBAL, 0); Parse_File(fname, stream); (void)fclose(stream); } free(path); return(TRUE); }
/*- *----------------------------------------------------------------------- * Arch_ParseArchive -- * Parse the archive specification in the given line and find/create * the nodes for the specified archive members, placing their nodes * on the given list. * * Input: * linePtr Pointer to start of specification * nodeLst Lst on which to place the nodes * ctxt Context in which to expand variables * * Results: * SUCCESS if it was a valid specification. The linePtr is updated * to point to the first non-space after the archive spec. The * nodes for the members are placed on the given list. * * Side Effects: * Some nodes may be created. The given list is extended. * *----------------------------------------------------------------------- */ ReturnStatus Arch_ParseArchive(char **linePtr, Lst nodeLst, GNode *ctxt) { char *cp; /* Pointer into line */ GNode *gn; /* New node */ char *libName; /* Library-part of specification */ char *memName; /* Member-part of specification */ char *nameBuf; /* temporary place for node name */ char saveChar; /* Ending delimiter of member-name */ Boolean subLibName; /* TRUE if libName should have/had * variable substitution performed on it */ libName = *linePtr; subLibName = FALSE; for (cp = libName; *cp != '(' && *cp != '\0'; cp++) { if (*cp == '$') { /* * Variable spec, so call the Var module to parse the puppy * so we can safely advance beyond it... */ int length; void *freeIt; char *result; result = Var_Parse(cp, ctxt, VARF_UNDEFERR|VARF_WANTRES, &length, &freeIt); free(freeIt); if (result == var_Error) { return(FAILURE); } else { subLibName = TRUE; } cp += length-1; } } *cp++ = '\0'; if (subLibName) { libName = Var_Subst(NULL, libName, ctxt, VARF_UNDEFERR|VARF_WANTRES); } for (;;) { /* * First skip to the start of the member's name, mark that * place and skip to the end of it (either white-space or * a close paren). */ Boolean doSubst = FALSE; /* TRUE if need to substitute in memName */ while (*cp != '\0' && *cp != ')' && isspace ((unsigned char)*cp)) { cp++; } memName = cp; while (*cp != '\0' && *cp != ')' && !isspace ((unsigned char)*cp)) { if (*cp == '$') { /* * Variable spec, so call the Var module to parse the puppy * so we can safely advance beyond it... */ int length; void *freeIt; char *result; result = Var_Parse(cp, ctxt, VARF_UNDEFERR|VARF_WANTRES, &length, &freeIt); free(freeIt); if (result == var_Error) { return(FAILURE); } else { doSubst = TRUE; } cp += length; } else { cp++; } } /* * If the specification ends without a closing parenthesis, * chances are there's something wrong (like a missing backslash), * so it's better to return failure than allow such things to happen */ if (*cp == '\0') { printf("No closing parenthesis in archive specification\n"); return (FAILURE); } /* * If we didn't move anywhere, we must be done */ if (cp == memName) { break; } saveChar = *cp; *cp = '\0'; /* * XXX: This should be taken care of intelligently by * SuffExpandChildren, both for the archive and the member portions. */ /* * If member contains variables, try and substitute for them. * This will slow down archive specs with dynamic sources, of course, * since we'll be (non-)substituting them three times, but them's * the breaks -- we need to do this since SuffExpandChildren calls * us, otherwise we could assume the thing would be taken care of * later. */ if (doSubst) { char *buf; char *sacrifice; char *oldMemName = memName; size_t sz; memName = Var_Subst(NULL, memName, ctxt, VARF_UNDEFERR|VARF_WANTRES); /* * Now form an archive spec and recurse to deal with nested * variables and multi-word variable values.... The results * are just placed at the end of the nodeLst we're returning. */ sz = strlen(memName)+strlen(libName)+3; buf = sacrifice = bmake_malloc(sz); snprintf(buf, sz, "%s(%s)", libName, memName); if (strchr(memName, '$') && strcmp(memName, oldMemName) == 0) { /* * Must contain dynamic sources, so we can't deal with it now. * Just create an ARCHV node for the thing and let * SuffExpandChildren handle it... */ gn = Targ_FindNode(buf, TARG_CREATE); if (gn == NULL) { free(buf); return(FAILURE); } else { gn->type |= OP_ARCHV; (void)Lst_AtEnd(nodeLst, gn); } } else if (Arch_ParseArchive(&sacrifice, nodeLst, ctxt)!=SUCCESS) { /* * Error in nested call -- free buffer and return FAILURE * ourselves. */ free(buf); return(FAILURE); } /* * Free buffer and continue with our work. */ free(buf); } else if (Dir_HasWildcards(memName)) { Lst members = Lst_Init(FALSE); char *member; size_t sz = MAXPATHLEN, nsz; nameBuf = bmake_malloc(sz); Dir_Expand(memName, dirSearchPath, members); while (!Lst_IsEmpty(members)) { member = (char *)Lst_DeQueue(members); nsz = strlen(libName) + strlen(member) + 3; if (sz > nsz) nameBuf = bmake_realloc(nameBuf, sz = nsz * 2); snprintf(nameBuf, sz, "%s(%s)", libName, member); free(member); gn = Targ_FindNode(nameBuf, TARG_CREATE); if (gn == NULL) { free(nameBuf); return (FAILURE); } else { /* * We've found the node, but have to make sure the rest of * the world knows it's an archive member, without having * to constantly check for parentheses, so we type the * thing with the OP_ARCHV bit before we place it on the * end of the provided list. */ gn->type |= OP_ARCHV; (void)Lst_AtEnd(nodeLst, gn); } } Lst_Destroy(members, NULL); free(nameBuf); } else { size_t sz = strlen(libName) + strlen(memName) + 3; nameBuf = bmake_malloc(sz); snprintf(nameBuf, sz, "%s(%s)", libName, memName); gn = Targ_FindNode(nameBuf, TARG_CREATE); free(nameBuf); if (gn == NULL) { return (FAILURE); } else { /* * We've found the node, but have to make sure the rest of the * world knows it's an archive member, without having to * constantly check for parentheses, so we type the thing with * the OP_ARCHV bit before we place it on the end of the * provided list. */ gn->type |= OP_ARCHV; (void)Lst_AtEnd(nodeLst, gn); } } if (doSubst) { free(memName); } *cp = saveChar; } /* * If substituted libName, free it now, since we need it no longer. */ if (subLibName) { free(libName); } /* * We promised the pointer would be set up at the next non-space, so * we must advance cp there before setting *linePtr... (note that on * entrance to the loop, cp is guaranteed to point at a ')') */ do { cp++; } while (*cp != '\0' && isspace ((unsigned char)*cp)); *linePtr = cp; return (SUCCESS); }
/*- * 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) { Lst targs; /* target nodes to create -- passed to Make_Init */ Boolean outOfDate = TRUE; /* FALSE if all targets up to date */ struct stat sb, sa; char *p1, *path, *pwd; char mdpath[MAXPATHLEN]; #ifdef FORCE_MACHINE char *machine = FORCE_MACHINE; #else char *machine = getenv("MACHINE"); #endif const char *machine_arch = getenv("MACHINE_ARCH"); char *syspath = getenv("MAKESYSPATH"); Lst sysMkPath; /* Path of sys.mk */ char *cp = NULL, *start; /* avoid faults on read-only strings */ static char defsyspath[] = DEFAULT_SYS_PATH; char found_path[MAXPATHLEN + 1]; /* for searching for sys.mk */ struct timeval rightnow; /* to initialize random seed */ /* * Set the seed to produce a different random sequences * on each program execution. */ gettimeofday(&rightnow, NULL); srandom(rightnow.tv_sec + rightnow.tv_usec); if ((progname = strrchr(argv[0], '/')) != NULL) progname++; else progname = argv[0]; #ifdef RLIMIT_NOFILE /* * get rid of resource limit on file descriptors */ { struct rlimit rl; if (getrlimit(RLIMIT_NOFILE, &rl) != -1 && rl.rlim_cur != rl.rlim_max) { rl.rlim_cur = rl.rlim_max; (void)setrlimit(RLIMIT_NOFILE, &rl); } } #endif /* * Find where we are and take care of PWD for the automounter... * All this code is so that we know where we are when we start up * on a different machine with pmake. */ if (getcwd(curdir, MAXPATHLEN) == NULL) { (void)fprintf(stderr, "%s: %s.\n", progname, strerror(errno)); exit(2); } if (stat(curdir, &sa) == -1) { (void)fprintf(stderr, "%s: %s: %s.\n", progname, curdir, strerror(errno)); exit(2); } /* * Overriding getcwd() with $PWD totally breaks MAKEOBJDIRPREFIX * since the value of curdir can very depending on how we got * here. Ie sitting at a shell prompt (shell that provides $PWD) * or via subdir.mk in which case its likely a shell which does * not provide it. * So, to stop it breaking this case only, we ignore PWD if * MAKEOBJDIRPREFIX is set or MAKEOBJDIR contains a transform. */ #ifndef NO_PWD_OVERRIDE if ((pwd = getenv("PWD")) != NULL && getenv("MAKEOBJDIRPREFIX") == NULL) { const char *makeobjdir = getenv("MAKEOBJDIR"); if (makeobjdir == NULL || !strchr(makeobjdir, '$')) { if (stat(pwd, &sb) == 0 && sa.st_ino == sb.st_ino && sa.st_dev == sb.st_dev) (void)strncpy(curdir, pwd, MAXPATHLEN); } } #endif /* * Get the name of this type of MACHINE from utsname * so we can share an executable for similar machines. * (i.e. m68k: amiga hp300, mac68k, sun3, ...) * * Note that both MACHINE and MACHINE_ARCH are decided at * run-time. */ if (!machine) { #ifdef MAKE_NATIVE struct utsname utsname; if (uname(&utsname) == -1) { (void)fprintf(stderr, "%s: uname failed (%s).\n", progname, strerror(errno)); exit(2); } machine = utsname.machine; #else #ifdef MAKE_MACHINE machine = MAKE_MACHINE; #else machine = "unknown"; #endif #endif } if (!machine_arch) { #ifndef MACHINE_ARCH #ifdef MAKE_MACHINE_ARCH machine_arch = MAKE_MACHINE_ARCH; #else machine_arch = "unknown"; #endif #else machine_arch = MACHINE_ARCH; #endif } /* * Just in case MAKEOBJDIR wants us to do something tricky. */ Var_Init(); /* Initialize the lists of variables for * parsing arguments */ Var_Set(".CURDIR", curdir, VAR_GLOBAL, 0); Var_Set("MACHINE", machine, VAR_GLOBAL, 0); Var_Set("MACHINE_ARCH", machine_arch, VAR_GLOBAL, 0); #ifdef MAKE_VERSION Var_Set("MAKE_VERSION", MAKE_VERSION, VAR_GLOBAL, 0); #endif Var_Set(".newline", "\n", VAR_GLOBAL, 0); /* handy for :@ loops */ /* * Find the .OBJDIR. If MAKEOBJDIRPREFIX, or failing that, * MAKEOBJDIR is set in the environment, try only that value * and fall back to .CURDIR if it does not exist. * * Otherwise, try _PATH_OBJDIR.MACHINE, _PATH_OBJDIR, and * finally _PATH_OBJDIRPREFIX`pwd`, in that order. If none * of these paths exist, just use .CURDIR. */ Dir_Init(curdir); (void)Main_SetObjdir(curdir); if ((path = getenv("MAKEOBJDIRPREFIX")) != NULL) { (void)snprintf(mdpath, MAXPATHLEN, "%s%s", path, curdir); (void)Main_SetObjdir(mdpath); } else if ((path = getenv("MAKEOBJDIR")) != NULL) { (void)Main_SetObjdir(path); } else { (void)snprintf(mdpath, MAXPATHLEN, "%s.%s", _PATH_OBJDIR, machine); if (!Main_SetObjdir(mdpath) && !Main_SetObjdir(_PATH_OBJDIR)) { (void)snprintf(mdpath, MAXPATHLEN, "%s%s", _PATH_OBJDIRPREFIX, curdir); (void)Main_SetObjdir(mdpath); } } create = Lst_Init(FALSE); makefiles = Lst_Init(FALSE); printVars = FALSE; variables = Lst_Init(FALSE); beSilent = FALSE; /* Print commands as executed */ ignoreErrors = FALSE; /* Pay attention to non-zero returns */ noExecute = FALSE; /* Execute all commands */ noRecursiveExecute = FALSE; /* Execute all .MAKE targets */ 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 */ usePipes = TRUE; /* Catch child output in pipes */ debug = 0; /* No debug verbosity, please. */ jobsRunning = FALSE; maxLocal = DEFMAXLOCAL; /* Set default local max concurrency */ #ifdef REMOTE maxJobs = DEFMAXJOBS; /* Set default max concurrency */ #else maxJobs = maxLocal; #endif compatMake = FALSE; /* No compat mode */ /* * Initialize the parsing, directory and variable modules to prepare * for the reading of inclusion paths and variable settings on the * command line */ /* * 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. */ Parse_Init(); Var_Set("MAKE", argv[0], VAR_GLOBAL, 0); Var_Set(".MAKE", argv[0], VAR_GLOBAL, 0); Var_Set(MAKEFLAGS, "", VAR_GLOBAL, 0); Var_Set(MAKEOVERRIDES, "", VAR_GLOBAL, 0); Var_Set("MFLAGS", "", VAR_GLOBAL, 0); Var_Set(".ALLTARGETS", "", VAR_GLOBAL, 0); /* * First snag any flags out of the MAKE environment variable. * (Note this is *not* MAKEFLAGS since /bin/make uses that and it's * in a different format). */ #ifdef POSIX Main_ParseArgLine(getenv("MAKEFLAGS")); #else Main_ParseArgLine(getenv("MAKE")); #endif MainParseArgs(argc, argv); /* * Be compatible if user did not specify -j and did not explicitly * turned compatibility on */ if (!compatMake && !forceJobs) { compatMake = TRUE; } /* * Initialize archive, target and suffix modules in preparation for * parsing the makefile(s) */ Arch_Init(); Targ_Init(); Suff_Init(); Trace_Init(tracefile); DEFAULT = NILGNODE; (void)time(&now); Trace_Log(MAKESTART, NULL); /* * 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 != NILLNODE; ln = Lst_Succ(ln)) { char *name = (char *)Lst_Datum(ln); Var_Append(".TARGETS", name, VAR_GLOBAL); } } else Var_Set(".TARGETS", "", VAR_GLOBAL, 0); /* * 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 (syspath == NULL || *syspath == '\0') syspath = defsyspath; else syspath = strdup(syspath); for (start = syspath; *start != '\0'; start = cp) { for (cp = start; *cp != '\0' && *cp != ':'; cp++) continue; if (*cp == ':') { *cp++ = '\0'; } /* look for magic parent directory search string */ if (strncmp(".../", start, 4) != 0) { (void)Dir_AddDir(defIncPath, start); } else { if (Dir_FindHereOrAbove(curdir, start+4, found_path, sizeof(found_path))) { (void)Dir_AddDir(defIncPath, found_path); } } } if (syspath != defsyspath) free(syspath); /* * Read in the built-in rules first, followed by the specified * makefile, if it was (makefile != NULL), or the default * makefile and Makefile, in that order, if it wasn't. */ if (!noBuiltins) { LstNode ln; sysMkPath = Lst_Init(FALSE); Dir_Expand(_PATH_DEFSYSMK, Lst_IsEmpty(sysIncPath) ? defIncPath : sysIncPath, sysMkPath); if (Lst_IsEmpty(sysMkPath)) Fatal("%s: no system rules (%s).", progname, _PATH_DEFSYSMK); ln = Lst_Find(sysMkPath, (ClientData)NULL, ReadMakefile); if (ln != NILLNODE) Fatal("%s: cannot open %s.", progname, (char *)Lst_Datum(ln)); } if (!Lst_IsEmpty(makefiles)) { LstNode ln; ln = Lst_Find(makefiles, (ClientData)NULL, ReadMakefile); if (ln != NILLNODE) Fatal("%s: cannot open %s.", progname, (char *)Lst_Datum(ln)); } else if (!ReadMakefile(UNCONST("makefile"), NULL)) (void)ReadMakefile(UNCONST("Makefile"), NULL); (void)ReadMakefile(UNCONST(".depend"), NULL); Var_Append("MFLAGS", Var_Value(MAKEFLAGS, VAR_GLOBAL, &p1), VAR_GLOBAL); if (p1) free(p1); if (!jobServer && !compatMake) Job_ServerStart(maxJobs); if (DEBUG(JOB)) printf("job_pipe %d %d, maxjobs %d maxlocal %d compat %d\n", job_pipe[0], job_pipe[1], maxJobs, maxLocal, compatMake); Main_ExportMAKEFLAGS(TRUE); /* initial export */ #ifndef NO_CHECK_MAKE_CHDIR Check_Cwd_av(0, NULL, 0); /* initialize it */ #endif /* * For compatibility, look at the directories in the VPATH variable * and add them to the search path, if the variable is defined. The * variable's value is in the same format as the PATH envariable, i.e. * <directory>:<directory>:<directory>... */ if (Var_Exists("VPATH", VAR_CMD)) { char *vpath, savec; /* * GCC stores string constants in read-only memory, but * Var_Subst will want to write this thing, so store it * in an array */ static char VPATH[] = "${VPATH}"; vpath = Var_Subst(NULL, VPATH, VAR_CMD, FALSE); path = vpath; do { /* skip to end of directory */ for (cp = path; *cp != ':' && *cp != '\0'; cp++) continue; /* Save terminator character so know when to stop */ savec = *cp; *cp = '\0'; /* Add directory to search path */ (void)Dir_AddDir(dirSearchPath, path); *cp = savec; path = cp + 1; } while (savec == ':'); free(vpath); } /* * Now that all search paths have been read for suffixes et al, it's * time to add the default search path to their lists... */ Suff_DoPaths(); /* * Propagate attributes through :: dependency lists. */ Targ_Propagate(); /* print the initial graph, if the user requested it */ if (DEBUG(GRAPH1)) Targ_PrintGraph(1); /* print the values of any variables requested by the user */ if (printVars) { LstNode ln; for (ln = Lst_First(variables); ln != NILLNODE; ln = Lst_Succ(ln)) { char *var = (char *)Lst_Datum(ln); char *value; if (strchr(var, '$')) { value = p1 = Var_Subst(NULL, var, VAR_GLOBAL, 0); } else { value = Var_Value(var, VAR_GLOBAL, &p1); } printf("%s\n", value ? value : ""); if (p1) free(p1); } } /* * 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)) targs = Parse_MainName(); else targs = Targ_FindList(create, TARG_CREATE); if (!compatMake && !printVars) { /* * Initialize job module before traversing the graph, now that * any .BEGIN and .END targets have been read. This is done * only if the -q flag wasn't given (to prevent the .BEGIN from * being executed should it exist). */ if (!queryFlag) { if (maxLocal == -1) maxLocal = maxJobs; Job_Init(maxJobs, maxLocal); jobsRunning = TRUE; } /* Traverse the graph, checking on all the targets */ outOfDate = Make_Run(targs); } else if (!printVars) { /* * Compat_Init will take care of creating all the targets as * well as initializing the module. */ Compat_Run(targs); } #ifdef CLEANUP Lst_Destroy(targs, NOFREE); Lst_Destroy(variables, NOFREE); Lst_Destroy(makefiles, NOFREE); Lst_Destroy(create, (FreeProc *)free); #endif /* print the graph now it's been processed if the user requested it */ if (DEBUG(GRAPH2)) Targ_PrintGraph(2); Trace_Log(MAKEEND, 0); Suff_End(); Targ_End(); Arch_End(); Var_End(); Parse_End(); Dir_End(); Job_End(); Trace_End(); if (queryFlag && outOfDate) return(1); else return(0); }
Boolean meta_oodate(GNode *gn, Boolean oodate) { static char *tmpdir = NULL; static char cwd[MAXPATHLEN]; char lcwd_vname[64]; char ldir_vname[64]; char lcwd[MAXPATHLEN]; char latestdir[MAXPATHLEN]; char fname[MAXPATHLEN]; char fname1[MAXPATHLEN]; char fname2[MAXPATHLEN]; char fname3[MAXPATHLEN]; const char *dname; const char *tname; char *p; char *cp; char *link_src; char *move_target; static size_t cwdlen = 0; static size_t tmplen = 0; FILE *fp; Boolean needOODATE = FALSE; Lst missingFiles; char *pa[4]; /* >= possible uses */ int i; int have_filemon = FALSE; if (oodate) return oodate; /* we're done */ i = 0; dname = Var_Value(".OBJDIR", gn, &pa[i++]); tname = Var_Value(TARGET, gn, &pa[i++]); /* if this succeeds fname3 is realpath of dname */ if (!meta_needed(gn, dname, tname, fname3, FALSE)) goto oodate_out; dname = fname3; missingFiles = Lst_Init(FALSE); /* * We need to check if the target is out-of-date. This includes * checking if the expanded command has changed. This in turn * requires that all variables are set in the same way that they * would be if the target needs to be re-built. */ Make_DoAllVar(gn); meta_name(gn, fname, sizeof(fname), dname, tname, dname); #ifdef DEBUG_META_MODE if (DEBUG(META)) fprintf(debug_file, "meta_oodate: %s\n", fname); #endif if ((fp = fopen(fname, "r")) != NULL) { static char *buf = NULL; static size_t bufsz; int lineno = 0; int lastpid = 0; int pid; int x; LstNode ln; struct stat fs; if (!buf) { bufsz = 8 * BUFSIZ; buf = bmake_malloc(bufsz); } if (!cwdlen) { if (getcwd(cwd, sizeof(cwd)) == NULL) err(1, "Could not get current working directory"); cwdlen = strlen(cwd); } strlcpy(lcwd, cwd, sizeof(lcwd)); strlcpy(latestdir, cwd, sizeof(latestdir)); if (!tmpdir) { tmpdir = getTmpdir(); tmplen = strlen(tmpdir); } /* we want to track all the .meta we read */ Var_Append(".MAKE.META.FILES", fname, VAR_GLOBAL); ln = Lst_First(gn->commands); while (!oodate && (x = fgetLine(&buf, &bufsz, 0, fp)) > 0) { lineno++; if (buf[x - 1] == '\n') buf[x - 1] = '\0'; else { warnx("%s: %d: line truncated at %u", fname, lineno, x); oodate = TRUE; break; } link_src = NULL; move_target = NULL; /* Find the start of the build monitor section. */ if (!have_filemon) { if (strncmp(buf, "-- filemon", 10) == 0) { have_filemon = TRUE; continue; } if (strncmp(buf, "# buildmon", 10) == 0) { have_filemon = TRUE; continue; } } /* Delimit the record type. */ p = buf; #ifdef DEBUG_META_MODE if (DEBUG(META)) fprintf(debug_file, "%s: %d: %s\n", fname, lineno, buf); #endif strsep(&p, " "); if (have_filemon) { /* * We are in the 'filemon' output section. * Each record from filemon follows the general form: * * <key> <pid> <data> * * Where: * <key> is a single letter, denoting the syscall. * <pid> is the process that made the syscall. * <data> is the arguments (of interest). */ switch(buf[0]) { case '#': /* comment */ case 'V': /* version */ break; default: /* * We need to track pathnames per-process. * * Each process run by make, starts off in the 'CWD' * recorded in the .meta file, if it chdirs ('C') * elsewhere we need to track that - but only for * that process. If it forks ('F'), we initialize * the child to have the same cwd as its parent. * * We also need to track the 'latestdir' of * interest. This is usually the same as cwd, but * not if a process is reading directories. * * Each time we spot a different process ('pid') * we save the current value of 'latestdir' in a * variable qualified by 'lastpid', and * re-initialize 'latestdir' to any pre-saved * value for the current 'pid' and 'CWD' if none. */ CHECK_VALID_META(p); pid = atoi(p); if (pid > 0 && pid != lastpid) { char *ldir; char *tp; if (lastpid > 0) { /* We need to remember these. */ Var_Set(lcwd_vname, lcwd, VAR_GLOBAL, 0); Var_Set(ldir_vname, latestdir, VAR_GLOBAL, 0); } snprintf(lcwd_vname, sizeof(lcwd_vname), LCWD_VNAME_FMT, pid); snprintf(ldir_vname, sizeof(ldir_vname), LDIR_VNAME_FMT, pid); lastpid = pid; ldir = Var_Value(ldir_vname, VAR_GLOBAL, &tp); if (ldir) { strlcpy(latestdir, ldir, sizeof(latestdir)); free(tp); } ldir = Var_Value(lcwd_vname, VAR_GLOBAL, &tp); if (ldir) { strlcpy(lcwd, ldir, sizeof(lcwd)); free(tp); } } /* Skip past the pid. */ if (strsep(&p, " ") == NULL) continue; #ifdef DEBUG_META_MODE if (DEBUG(META)) fprintf(debug_file, "%s: %d: %d: %c: cwd=%s lcwd=%s ldir=%s\n", fname, lineno, pid, buf[0], cwd, lcwd, latestdir); #endif break; } CHECK_VALID_META(p); /* Process according to record type. */ switch (buf[0]) { case 'X': /* eXit */ Var_Delete(lcwd_vname, VAR_GLOBAL); Var_Delete(ldir_vname, VAR_GLOBAL); lastpid = 0; /* no need to save ldir_vname */ break; case 'F': /* [v]Fork */ { char cldir[64]; int child; child = atoi(p); if (child > 0) { snprintf(cldir, sizeof(cldir), LCWD_VNAME_FMT, child); Var_Set(cldir, lcwd, VAR_GLOBAL, 0); snprintf(cldir, sizeof(cldir), LDIR_VNAME_FMT, child); Var_Set(cldir, latestdir, VAR_GLOBAL, 0); #ifdef DEBUG_META_MODE if (DEBUG(META)) fprintf(debug_file, "%s: %d: %d: cwd=%s lcwd=%s ldir=%s\n", fname, lineno, child, cwd, lcwd, latestdir); #endif } } break; case 'C': /* Chdir */ /* Update lcwd and latest directory. */ strlcpy(latestdir, p, sizeof(latestdir)); strlcpy(lcwd, p, sizeof(lcwd)); Var_Set(lcwd_vname, lcwd, VAR_GLOBAL, 0); Var_Set(ldir_vname, lcwd, VAR_GLOBAL, 0); #ifdef DEBUG_META_MODE if (DEBUG(META)) fprintf(debug_file, "%s: %d: cwd=%s ldir=%s\n", fname, lineno, cwd, lcwd); #endif break; case 'M': /* renaMe */ /* * For 'M'oves we want to check * the src as for 'R'ead * and the target as for 'W'rite. */ cp = p; /* save this for a second */ /* now get target */ if (strsep(&p, " ") == NULL) continue; CHECK_VALID_META(p); move_target = p; p = cp; /* 'L' and 'M' put single quotes around the args */ DEQUOTE(p); DEQUOTE(move_target); /* FALLTHROUGH */ case 'D': /* unlink */ if (*p == '/' && !Lst_IsEmpty(missingFiles)) { /* remove any missingFiles entries that match p */ if ((ln = Lst_Find(missingFiles, p, path_match)) != NULL) { LstNode nln; char *tp; do { nln = Lst_FindFrom(missingFiles, Lst_Succ(ln), p, path_match); tp = Lst_Datum(ln); Lst_Remove(missingFiles, ln); free(tp); } while ((ln = nln) != NULL); } } if (buf[0] == 'M') { /* the target of the mv is a file 'W'ritten */ #ifdef DEBUG_META_MODE if (DEBUG(META)) fprintf(debug_file, "meta_oodate: M %s -> %s\n", p, move_target); #endif p = move_target; goto check_write; } break; case 'L': /* Link */ /* * For 'L'inks check * the src as for 'R'ead * and the target as for 'W'rite. */ link_src = p; /* now get target */ if (strsep(&p, " ") == NULL) continue; CHECK_VALID_META(p); /* 'L' and 'M' put single quotes around the args */ DEQUOTE(p); DEQUOTE(link_src); #ifdef DEBUG_META_MODE if (DEBUG(META)) fprintf(debug_file, "meta_oodate: L %s -> %s\n", link_src, p); #endif /* FALLTHROUGH */ case 'W': /* Write */ check_write: /* * If a file we generated within our bailiwick * but outside of .OBJDIR is missing, * we need to do it again. */ /* ignore non-absolute paths */ if (*p != '/') break; if (Lst_IsEmpty(metaBailiwick)) break; /* ignore cwd - normal dependencies handle those */ if (strncmp(p, cwd, cwdlen) == 0) break; if (!Lst_ForEach(metaBailiwick, prefix_match, p)) break; /* tmpdir might be within */ if (tmplen > 0 && strncmp(p, tmpdir, tmplen) == 0) break; /* ignore anything containing the string "tmp" */ if ((strstr("tmp", p))) break; if ((link_src != NULL && cached_lstat(p, &fs) < 0) || (link_src == NULL && cached_stat(p, &fs) < 0)) { if (!meta_ignore(gn, p)) { if (Lst_Find(missingFiles, p, string_match) == NULL) Lst_AtEnd(missingFiles, bmake_strdup(p)); } } break; check_link_src: p = link_src; link_src = NULL; #ifdef DEBUG_META_MODE if (DEBUG(META)) fprintf(debug_file, "meta_oodate: L src %s\n", p); #endif /* FALLTHROUGH */ case 'R': /* Read */ case 'E': /* Exec */ /* * Check for runtime files that can't * be part of the dependencies because * they are _expected_ to change. */ if (meta_ignore(gn, p)) break; /* * The rest of the record is the file name. * Check if it's not an absolute path. */ { char *sdirs[4]; char **sdp; int sdx = 0; int found = 0; if (*p == '/') { sdirs[sdx++] = p; /* done */ } else { if (strcmp(".", p) == 0) continue; /* no point */ /* Check vs latestdir */ snprintf(fname1, sizeof(fname1), "%s/%s", latestdir, p); sdirs[sdx++] = fname1; if (strcmp(latestdir, lcwd) != 0) { /* Check vs lcwd */ snprintf(fname2, sizeof(fname2), "%s/%s", lcwd, p); sdirs[sdx++] = fname2; } if (strcmp(lcwd, cwd) != 0) { /* Check vs cwd */ snprintf(fname3, sizeof(fname3), "%s/%s", cwd, p); sdirs[sdx++] = fname3; } } sdirs[sdx++] = NULL; for (sdp = sdirs; *sdp && !found; sdp++) { #ifdef DEBUG_META_MODE if (DEBUG(META)) fprintf(debug_file, "%s: %d: looking for: %s\n", fname, lineno, *sdp); #endif if (cached_stat(*sdp, &fs) == 0) { found = 1; p = *sdp; } } if (found) { #ifdef DEBUG_META_MODE if (DEBUG(META)) fprintf(debug_file, "%s: %d: found: %s\n", fname, lineno, p); #endif if (!S_ISDIR(fs.st_mode) && fs.st_mtime > gn->mtime) { if (DEBUG(META)) fprintf(debug_file, "%s: %d: file '%s' is newer than the target...\n", fname, lineno, p); oodate = TRUE; } else if (S_ISDIR(fs.st_mode)) { /* Update the latest directory. */ cached_realpath(p, latestdir); } } else if (errno == ENOENT && *p == '/' && strncmp(p, cwd, cwdlen) != 0) { /* * A referenced file outside of CWD is missing. * We cannot catch every eventuality here... */ if (Lst_Find(missingFiles, p, string_match) == NULL) Lst_AtEnd(missingFiles, bmake_strdup(p)); } } if (buf[0] == 'E') { /* previous latestdir is no longer relevant */ strlcpy(latestdir, lcwd, sizeof(latestdir)); } break; default: break; } if (!oodate && buf[0] == 'L' && link_src != NULL) goto check_link_src; } else if (strcmp(buf, "CMD") == 0) { /* * Compare the current command with the one in the * meta data file. */ if (ln == NULL) { if (DEBUG(META)) fprintf(debug_file, "%s: %d: there were more build commands in the meta data file than there are now...\n", fname, lineno); oodate = TRUE; } else { char *cmd = (char *)Lst_Datum(ln); Boolean hasOODATE = FALSE; if (strstr(cmd, "$?")) hasOODATE = TRUE; else if ((cp = strstr(cmd, ".OODATE"))) { /* check for $[{(].OODATE[:)}] */ if (cp > cmd + 2 && cp[-2] == '$') hasOODATE = TRUE; } if (hasOODATE) { needOODATE = TRUE; if (DEBUG(META)) fprintf(debug_file, "%s: %d: cannot compare command using .OODATE\n", fname, lineno); } cmd = Var_Subst(NULL, cmd, gn, VARF_WANTRES|VARF_UNDEFERR); if ((cp = strchr(cmd, '\n'))) { int n; /* * This command contains newlines, we need to * fetch more from the .meta file before we * attempt a comparison. */ /* first put the newline back at buf[x - 1] */ buf[x - 1] = '\n'; do { /* now fetch the next line */ if ((n = fgetLine(&buf, &bufsz, x, fp)) <= 0) break; x = n; lineno++; if (buf[x - 1] != '\n') { warnx("%s: %d: line truncated at %u", fname, lineno, x); break; } cp = strchr(++cp, '\n'); } while (cp); if (buf[x - 1] == '\n') buf[x - 1] = '\0'; } if (!hasOODATE && !(gn->type & OP_NOMETA_CMP) && strcmp(p, cmd) != 0) { if (DEBUG(META)) fprintf(debug_file, "%s: %d: a build command has changed\n%s\nvs\n%s\n", fname, lineno, p, cmd); if (!metaIgnoreCMDs) oodate = TRUE; } free(cmd); ln = Lst_Succ(ln); } } else if (strcmp(buf, "CWD") == 0) { /* * Check if there are extra commands now * that weren't in the meta data file. */ if (!oodate && ln != NULL) { if (DEBUG(META)) fprintf(debug_file, "%s: %d: there are extra build commands now that weren't in the meta data file\n", fname, lineno); oodate = TRUE; } if (strcmp(p, cwd) != 0) { if (DEBUG(META)) fprintf(debug_file, "%s: %d: the current working directory has changed from '%s' to '%s'\n", fname, lineno, p, curdir); oodate = TRUE; } } } fclose(fp); if (!Lst_IsEmpty(missingFiles)) { if (DEBUG(META)) fprintf(debug_file, "%s: missing files: %s...\n", fname, (char *)Lst_Datum(Lst_First(missingFiles))); oodate = TRUE; } if (!oodate && !have_filemon && filemonMissing) { if (DEBUG(META)) fprintf(debug_file, "%s: missing filemon data\n", fname); oodate = TRUE; } } else { if (writeMeta && metaMissing) { cp = NULL; /* if target is in .CURDIR we do not need a meta file */ if (gn->path && (cp = strrchr(gn->path, '/')) && cp > gn->path) { if (strncmp(curdir, gn->path, (cp - gn->path)) != 0) { cp = NULL; /* not in .CURDIR */ } } if (!cp) { if (DEBUG(META)) fprintf(debug_file, "%s: required but missing\n", fname); oodate = TRUE; needOODATE = TRUE; /* assume the worst */ } } } Lst_Destroy(missingFiles, (FreeProc *)free); if (oodate && needOODATE) { /* * Target uses .OODATE which is empty; or we wouldn't be here. * We have decided it is oodate, so .OODATE needs to be set. * All we can sanely do is set it to .ALLSRC. */ Var_Delete(OODATE, gn); Var_Set(OODATE, Var_Value(ALLSRC, gn, &cp), gn, 0); free(cp); } oodate_out: for (i--; i >= 0; i--) { free(pa[i]); } return oodate; }
/*- *----------------------------------------------------------------------- * Compat_Run -- * Initialize this mode and start making. * * Input: * targs List of target nodes to re-create * * Results: * None. * * Side Effects: * Guess what? * *----------------------------------------------------------------------- */ void Compat_Run(Lst targs) { GNode *gn = NULL;/* Current root target */ int errors; /* Number of targets not remade due to errors */ Compat_Init(); if (bmake_signal(SIGINT, SIG_IGN) != SIG_IGN) { bmake_signal(SIGINT, CompatInterrupt); } if (bmake_signal(SIGTERM, SIG_IGN) != SIG_IGN) { bmake_signal(SIGTERM, CompatInterrupt); } if (bmake_signal(SIGHUP, SIG_IGN) != SIG_IGN) { bmake_signal(SIGHUP, CompatInterrupt); } if (bmake_signal(SIGQUIT, SIG_IGN) != SIG_IGN) { bmake_signal(SIGQUIT, CompatInterrupt); } ENDNode = Targ_FindNode(".END", TARG_CREATE); ENDNode->type = OP_SPECIAL; /* * If the user has defined a .BEGIN target, execute the commands attached * to it. */ if (!queryFlag) { gn = Targ_FindNode(".BEGIN", TARG_NOCREATE); if (gn != NULL) { Compat_Make(gn, gn); if (gn->made == ERROR) { PrintOnError(gn, "\n\nStop."); exit(1); } } } /* * Expand .USE nodes right now, because they can modify the structure * of the tree. */ Make_ExpandUse(targs); /* * For each entry in the list of targets to create, call Compat_Make on * it to create the thing. Compat_Make will leave the 'made' field of gn * in one of several states: * UPTODATE gn was already up-to-date * MADE gn was recreated successfully * ERROR An error occurred while gn was being created * ABORTED gn was not remade because one of its inferiors * could not be made due to errors. */ errors = 0; while (!Lst_IsEmpty (targs)) { gn = (GNode *)Lst_DeQueue(targs); Compat_Make(gn, gn); if (gn->made == UPTODATE) { printf("`%s' is up to date.\n", gn->name); } else if (gn->made == ABORTED) { printf("`%s' not remade because of errors.\n", gn->name); errors += 1; } } /* * If the user has defined a .END target, run its commands. */ if (errors == 0) { Compat_Make(ENDNode, ENDNode); if (gn->made == ERROR) { PrintOnError(gn, "\n\nStop."); exit(1); } } }
/*- * 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; }
Boolean meta_oodate(GNode *gn, Boolean oodate) { static char *tmpdir = NULL; static char cwd[MAXPATHLEN]; char ldir_vname[64]; char latestdir[MAXPATHLEN]; char fname[MAXPATHLEN]; char fname1[MAXPATHLEN]; char fname2[MAXPATHLEN]; char *p; char *cp; static size_t cwdlen = 0; static size_t tmplen = 0; FILE *fp; Boolean ignoreOODATE = FALSE; Lst missingFiles; if (oodate) return oodate; /* we're done */ missingFiles = Lst_Init(FALSE); /* * We need to check if the target is out-of-date. This includes * checking if the expanded command has changed. This in turn * requires that all variables are set in the same way that they * would be if the target needs to be re-built. */ Make_DoAllVar(gn); meta_name(gn, fname, sizeof(fname), NULL, NULL); #ifdef DEBUG_META_MODE if (DEBUG(META)) fprintf(debug_file, "meta_oodate: %s\n", fname); #endif if ((fp = fopen(fname, "r")) != NULL) { static char *buf = NULL; static size_t bufsz; int lineno = 0; int lastpid = 0; int pid; int f = 0; int x; LstNode ln; struct stat fs; if (!buf) { bufsz = 8 * BUFSIZ; buf = bmake_malloc(bufsz); } if (!cwdlen) { if (getcwd(cwd, sizeof(cwd)) == NULL) err(1, "Could not get current working directory"); cwdlen = strlen(cwd); } if (!tmpdir) { tmpdir = getTmpdir(); tmplen = strlen(tmpdir); } /* we want to track all the .meta we read */ Var_Append(".MAKE.META.FILES", fname, VAR_GLOBAL); ln = Lst_First(gn->commands); while (!oodate && (x = fgetLine(&buf, &bufsz, 0, fp)) > 0) { lineno++; if (buf[x - 1] == '\n') buf[x - 1] = '\0'; else { warnx("%s: %d: line truncated at %u", fname, lineno, x); oodate = TRUE; break; } /* Find the start of the build monitor section. */ if (!f) { if (strncmp(buf, "-- filemon", 10) == 0) { f = 1; continue; } if (strncmp(buf, "# buildmon", 10) == 0) { f = 1; continue; } } /* Delimit the record type. */ p = buf; #ifdef DEBUG_META_MODE if (DEBUG(META)) fprintf(debug_file, "%s: %d: %s\n", fname, lineno, buf); #endif strsep(&p, " "); if (f) { /* * We are in the 'filemon' output section. * Each record from filemon follows the general form: * * <key> <pid> <data> * * Where: * <key> is a single letter, denoting the syscall. * <pid> is the process that made the syscall. * <data> is the arguments (of interest). */ switch(buf[0]) { case '#': /* comment */ case 'V': /* version */ break; default: /* * We need to track pathnames per-process. * * Each process run by make, starts off in the 'CWD' * recorded in the .meta file, if it chdirs ('C') * elsewhere we need to track that - but only for * that process. If it forks ('F'), we initialize * the child to have the same cwd as its parent. * * We also need to track the 'latestdir' of * interest. This is usually the same as cwd, but * not if a process is reading directories. * * Each time we spot a different process ('pid') * we save the current value of 'latestdir' in a * variable qualified by 'lastpid', and * re-initialize 'latestdir' to any pre-saved * value for the current 'pid' and 'CWD' if none. */ CHECK_VALID_META(p); pid = atoi(p); if (pid > 0 && pid != lastpid) { char *ldir; char *tp; if (lastpid > 0) { /* We need to remember this. */ Var_Set(ldir_vname, latestdir, VAR_GLOBAL, 0); } snprintf(ldir_vname, sizeof(ldir_vname), LDIR_VNAME_FMT, pid); lastpid = pid; ldir = Var_Value(ldir_vname, VAR_GLOBAL, &tp); if (ldir) { strlcpy(latestdir, ldir, sizeof(latestdir)); if (tp) free(tp); } else strlcpy(latestdir, cwd, sizeof(latestdir)); } /* Skip past the pid. */ if (strsep(&p, " ") == NULL) continue; #ifdef DEBUG_META_MODE if (DEBUG(META)) fprintf(debug_file, "%s: %d: cwd=%s ldir=%s\n", fname, lineno, cwd, latestdir); #endif break; } CHECK_VALID_META(p); /* Process according to record type. */ switch (buf[0]) { case 'X': /* eXit */ Var_Delete(ldir_vname, VAR_GLOBAL); lastpid = 0; /* no need to save ldir_vname */ break; case 'F': /* [v]Fork */ { char cldir[64]; int child; child = atoi(p); if (child > 0) { snprintf(cldir, sizeof(cldir), LDIR_VNAME_FMT, child); Var_Set(cldir, latestdir, VAR_GLOBAL, 0); } } break; case 'C': /* Chdir */ /* Update the latest directory. */ strlcpy(latestdir, p, sizeof(latestdir)); break; case 'M': /* renaMe */ if (Lst_IsEmpty(missingFiles)) break; /* 'L' and 'M' put single quotes around the args */ if (*p == '\'') { char *ep; p++; if ((ep = strchr(p, '\''))) *ep = '\0'; } /* FALLTHROUGH */ case 'D': /* unlink */ if (*p == '/' && !Lst_IsEmpty(missingFiles)) { /* remove p from the missingFiles list if present */ if ((ln = Lst_Find(missingFiles, p, string_match)) != NULL) { char *tp = Lst_Datum(ln); Lst_Remove(missingFiles, ln); free(tp); } } break; case 'L': /* Link */ /* we want the target */ if (strsep(&p, " ") == NULL) continue; CHECK_VALID_META(p); /* 'L' and 'M' put single quotes around the args */ if (*p == '\'') { char *ep; p++; if ((ep = strchr(p, '\''))) *ep = '\0'; } /* FALLTHROUGH */ case 'W': /* Write */ /* * If a file we generated within our bailiwick * but outside of .OBJDIR is missing, * we need to do it again. */ /* ignore non-absolute paths */ if (*p != '/') break; if (Lst_IsEmpty(metaBailiwick)) break; /* ignore cwd - normal dependencies handle those */ if (strncmp(p, cwd, cwdlen) == 0) break; if (!Lst_ForEach(metaBailiwick, prefix_match, p)) break; /* tmpdir might be within */ if (tmplen > 0 && strncmp(p, tmpdir, tmplen) == 0) break; /* ignore anything containing the string "tmp" */ if ((strstr("tmp", p))) break; if (stat(p, &fs) < 0) { Lst_AtEnd(missingFiles, bmake_strdup(p)); } break; case 'R': /* Read */ case 'E': /* Exec */ /* * Check for runtime files that can't * be part of the dependencies because * they are _expected_ to change. */ if (strncmp(p, "/tmp/", 5) == 0 || (tmplen > 0 && strncmp(p, tmpdir, tmplen) == 0)) break; if (strncmp(p, "/var/", 5) == 0) break; /* Ignore device files. */ if (strncmp(p, "/dev/", 5) == 0) break; /* Ignore /etc/ files. */ if (strncmp(p, "/etc/", 5) == 0) break; if ((cp = strrchr(p, '/'))) { cp++; /* * We don't normally expect to see this, * but we do expect it to change. */ if (strcmp(cp, makeDependfile) == 0) break; } /* * The rest of the record is the file name. * Check if it's not an absolute path. */ { char *sdirs[4]; char **sdp; int sdx = 0; int found = 0; if (*p == '/') { sdirs[sdx++] = p; /* done */ } else { if (strcmp(".", p) == 0) continue; /* no point */ /* Check vs latestdir */ snprintf(fname1, sizeof(fname1), "%s/%s", latestdir, p); sdirs[sdx++] = fname1; if (strcmp(latestdir, cwd) != 0) { /* Check vs cwd */ snprintf(fname2, sizeof(fname2), "%s/%s", cwd, p); sdirs[sdx++] = fname2; } } sdirs[sdx++] = NULL; for (sdp = sdirs; *sdp && !found; sdp++) { #ifdef DEBUG_META_MODE if (DEBUG(META)) fprintf(debug_file, "%s: %d: looking for: %s\n", fname, lineno, *sdp); #endif if (stat(*sdp, &fs) == 0) { found = 1; p = *sdp; } } if (found) { #ifdef DEBUG_META_MODE if (DEBUG(META)) fprintf(debug_file, "%s: %d: found: %s\n", fname, lineno, p); #endif if (!S_ISDIR(fs.st_mode) && fs.st_mtime > gn->mtime) { if (DEBUG(META)) fprintf(debug_file, "%s: %d: file '%s' is newer than the target...\n", fname, lineno, p); oodate = TRUE; } else if (S_ISDIR(fs.st_mode)) { /* Update the latest directory. */ realpath(p, latestdir); } } else if (errno == ENOENT && *p == '/' && strncmp(p, cwd, cwdlen) != 0) { /* * A referenced file outside of CWD is missing. * We cannot catch every eventuality here... */ if (DEBUG(META)) fprintf(debug_file, "%s: %d: file '%s' may have moved?...\n", fname, lineno, p); oodate = TRUE; } } break; default: break; } } else if (strcmp(buf, "CMD") == 0) { /* * Compare the current command with the one in the * meta data file. */ if (ln == NULL) { if (DEBUG(META)) fprintf(debug_file, "%s: %d: there were more build commands in the meta data file than there are now...\n", fname, lineno); oodate = TRUE; } else { char *cmd = (char *)Lst_Datum(ln); if (!ignoreOODATE) { if (strstr(cmd, "$?")) ignoreOODATE = TRUE; else if ((cp = strstr(cmd, ".OODATE"))) { /* check for $[{(].OODATE[)}] */ if (cp > cmd + 2 && cp[-2] == '$') ignoreOODATE = TRUE; } if (ignoreOODATE && DEBUG(META)) fprintf(debug_file, "%s: %d: cannot compare commands using .OODATE\n", fname, lineno); } cmd = Var_Subst(NULL, cmd, gn, TRUE); if ((cp = strchr(cmd, '\n'))) { int n; /* * This command contains newlines, we need to * fetch more from the .meta file before we * attempt a comparison. */ /* first put the newline back at buf[x - 1] */ buf[x - 1] = '\n'; do { /* now fetch the next line */ if ((n = fgetLine(&buf, &bufsz, x, fp)) <= 0) break; x = n; lineno++; if (buf[x - 1] != '\n') { warnx("%s: %d: line truncated at %u", fname, lineno, x); break; } cp = strchr(++cp, '\n'); } while (cp); if (buf[x - 1] == '\n') buf[x - 1] = '\0'; } if (!ignoreOODATE && !(gn->type & OP_NOMETA_CMP) && strcmp(p, cmd) != 0) { if (DEBUG(META)) fprintf(debug_file, "%s: %d: a build command has changed\n%s\nvs\n%s\n", fname, lineno, p, cmd); if (!metaIgnoreCMDs) oodate = TRUE; } free(cmd); ln = Lst_Succ(ln); } } else if (strcmp(buf, "CWD") == 0) { /* * Check if there are extra commands now * that weren't in the meta data file. */ if (!oodate && ln != NULL) { if (DEBUG(META)) fprintf(debug_file, "%s: %d: there are extra build commands now that weren't in the meta data file\n", fname, lineno); oodate = TRUE; } if (strcmp(p, cwd) != 0) { if (DEBUG(META)) fprintf(debug_file, "%s: %d: the current working directory has changed from '%s' to '%s'\n", fname, lineno, p, curdir); oodate = TRUE; } } } fclose(fp); if (!Lst_IsEmpty(missingFiles)) { if (DEBUG(META)) fprintf(debug_file, "%s: missing files: %s...\n", fname, (char *)Lst_Datum(Lst_First(missingFiles))); oodate = TRUE; Lst_Destroy(missingFiles, (FreeProc *)free); } } else { if ((gn->type & OP_META)) { if (DEBUG(META)) fprintf(debug_file, "%s: required but missing\n", fname); oodate = TRUE; } } if (oodate && ignoreOODATE) { /* * Target uses .OODATE, so we need to re-compute it. * We need to clean up what Make_DoAllVar() did. */ Var_Delete(ALLSRC, gn); Var_Delete(OODATE, gn); gn->flags &= ~DONE_ALLSRC; } return oodate; }
/*- *----------------------------------------------------------------------- * TargPrintNode -- * print the contents of a node *----------------------------------------------------------------------- */ static int TargPrintNode (ClientData gnCD, ClientData pass) { GNode *gn = (GNode *)gnCD; if (!OP_NOP(gn->type)) { printf("#\n# Target %s assigned in %s\n", gn->name->data, gn->makefilename ? gn->makefilename->data : "(unknown makefile)"); if (gn == mainTarg) { printf("# *** MAIN TARGET ***\n"); } if ((int)pass == 2) { if (gn->unmade) { printf("# %d unmade children\n", gn->unmade); } else { printf("# No unmade children\n"); } if (! (gn->type & (OP_JOIN|OP_USE|OP_EXEC))) { if (gn->mtime != 0) { printf("# last modified %s: %s\n", Targ_FmtTime(gn->mtime), (gn->made == UNMADE ? "unmade" : (gn->made == MADE ? "made" : (gn->made == UPTODATE ? "up-to-date" : "error when made")))); } else if (gn->made != UNMADE) { printf("# non-existent (maybe): %s\n", (gn->made == MADE ? "made" : (gn->made == UPTODATE ? "up-to-date" : (gn->made == ERROR ? "error when made" : "aborted")))); } else { printf("# unmade\n"); } } if (!Lst_IsEmpty (gn->iParents)) { printf("# implicit parents: "); Lst_ForEach (gn->iParents, TargPrintName, (ClientData)0); putc ('\n', stdout); } } if (!Lst_IsEmpty (gn->parents)) { printf("# parents: "); Lst_ForEach (gn->parents, TargPrintName, (ClientData)0); putc ('\n', stdout); } printf("%-16s", gn->name->data); 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_ForEach (gn->children, TargPrintName, (ClientData)0); putc ('\n', stdout); Lst_ForEach (gn->commands, Targ_PrintCmd, (ClientData)0); printf("\n\n"); if (gn->type & OP_DOUBLEDEP) { Lst_ForEach (gn->cohorts, TargPrintNode, pass); } } return (0); }
/*- * 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) { Lst targs; /* target nodes to create -- passed to Make_Init */ Boolean outOfDate = FALSE; /* FALSE if all targets up to date */ struct stat sb, sa; char *p1, *path; char mdpath[MAXPATHLEN]; #ifdef FORCE_MACHINE const char *machine = FORCE_MACHINE; #else const char *machine = getenv("MACHINE"); #endif const char *machine_arch = getenv("MACHINE_ARCH"); char *syspath = getenv("MAKESYSPATH"); Lst sysMkPath; /* Path of sys.mk */ char *cp = NULL, *start; /* avoid faults on read-only strings */ static char defsyspath[] = _PATH_DEFSYSPATH; char found_path[MAXPATHLEN + 1]; /* for searching for sys.mk */ struct timeval rightnow; /* to initialize random seed */ struct utsname utsname; /* default to writing debug to stderr */ debug_file = stderr; #ifdef SIGINFO (void)bmake_signal(SIGINFO, siginfo); #endif /* * Set the seed to produce a different random sequence * on each program execution. */ gettimeofday(&rightnow, NULL); srandom(rightnow.tv_sec + rightnow.tv_usec); if ((progname = strrchr(argv[0], '/')) != NULL) progname++; else progname = argv[0]; #if defined(MAKE_NATIVE) || (defined(HAVE_SETRLIMIT) && defined(RLIMIT_NOFILE)) /* * get rid of resource limit on file descriptors */ { struct rlimit rl; if (getrlimit(RLIMIT_NOFILE, &rl) != -1 && rl.rlim_cur != rl.rlim_max) { rl.rlim_cur = rl.rlim_max; (void)setrlimit(RLIMIT_NOFILE, &rl); } } #endif if (uname(&utsname) == -1) { (void)fprintf(stderr, "%s: uname failed (%s).\n", progname, strerror(errno)); exit(2); } /* * Get the name of this type of MACHINE from utsname * so we can share an executable for similar machines. * (i.e. m68k: amiga hp300, mac68k, sun3, ...) * * Note that both MACHINE and MACHINE_ARCH are decided at * run-time. */ if (!machine) { #ifdef MAKE_NATIVE machine = utsname.machine; #else #ifdef MAKE_MACHINE machine = MAKE_MACHINE; #else machine = "unknown"; #endif #endif } if (!machine_arch) { #if defined(MAKE_NATIVE) && defined(HAVE_SYSCTL) && defined(CTL_HW) && defined(HW_MACHINE_ARCH) static char machine_arch_buf[sizeof(utsname.machine)]; int mib[2] = { CTL_HW, HW_MACHINE_ARCH }; size_t len = sizeof(machine_arch_buf); if (sysctl(mib, __arraycount(mib), machine_arch_buf, &len, NULL, 0) < 0) { (void)fprintf(stderr, "%s: sysctl failed (%s).\n", progname, strerror(errno)); exit(2); } machine_arch = machine_arch_buf; #else #ifndef MACHINE_ARCH #ifdef MAKE_MACHINE_ARCH machine_arch = MAKE_MACHINE_ARCH; #else machine_arch = "unknown"; #endif #else machine_arch = MACHINE_ARCH; #endif #endif } myPid = getpid(); /* remember this for vFork() */ /* * Just in case MAKEOBJDIR wants us to do something tricky. */ Var_Init(); /* Initialize the lists of variables for * parsing arguments */ Var_Set(".MAKE.OS", utsname.sysname, VAR_GLOBAL, 0); Var_Set("MACHINE", machine, VAR_GLOBAL, 0); Var_Set("MACHINE_ARCH", machine_arch, VAR_GLOBAL, 0); #ifdef MAKE_VERSION Var_Set("MAKE_VERSION", MAKE_VERSION, VAR_GLOBAL, 0); #endif Var_Set(".newline", "\n", VAR_GLOBAL, 0); /* handy for :@ loops */ /* * This is the traditional preference for makefiles. */ #ifndef MAKEFILE_PREFERENCE_LIST # define MAKEFILE_PREFERENCE_LIST "makefile Makefile" #endif Var_Set(MAKEFILE_PREFERENCE, MAKEFILE_PREFERENCE_LIST, VAR_GLOBAL, 0); Var_Set(MAKE_DEPENDFILE, ".depend", VAR_GLOBAL, 0); create = Lst_Init(FALSE); makefiles = Lst_Init(FALSE); printVars = FALSE; debugVflag = FALSE; variables = Lst_Init(FALSE); beSilent = FALSE; /* Print commands as executed */ ignoreErrors = FALSE; /* Pay attention to non-zero returns */ noExecute = FALSE; /* Execute all commands */ noRecursiveExecute = FALSE; /* Execute all .MAKE targets */ 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. */ jobsRunning = FALSE; maxJobs = DEFMAXLOCAL; /* Set default local max concurrency */ maxJobTokens = maxJobs; compatMake = FALSE; /* No compat mode */ ignorePWD = FALSE; /* * Initialize the parsing, directory and variable modules to prepare * for the reading of inclusion paths and variable settings on the * command line */ /* * 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. */ Parse_Init(); if (argv[0][0] == '/' || strchr(argv[0], '/') == NULL) { /* * Leave alone if it is an absolute path, or if it does * not contain a '/' in which case we need to find it in * the path, like execvp(3) and the shells do. */ p1 = argv[0]; } else { /* * A relative path, canonicalize it. */ p1 = cached_realpath(argv[0], mdpath); if (!p1 || *p1 != '/' || stat(p1, &sb) < 0) { p1 = argv[0]; /* realpath failed */ } } Var_Set("MAKE", p1, VAR_GLOBAL, 0); Var_Set(".MAKE", p1, VAR_GLOBAL, 0); Var_Set(MAKEFLAGS, "", VAR_GLOBAL, 0); Var_Set(MAKEOVERRIDES, "", VAR_GLOBAL, 0); Var_Set("MFLAGS", "", VAR_GLOBAL, 0); Var_Set(".ALLTARGETS", "", VAR_GLOBAL, 0); /* some makefiles need to know this */ Var_Set(MAKE_LEVEL ".ENV", MAKE_LEVEL_ENV, VAR_CMD, 0); /* * Set some other useful macros */ { char tmp[64], *ep; makelevel = ((ep = getenv(MAKE_LEVEL_ENV)) && *ep) ? atoi(ep) : 0; if (makelevel < 0) makelevel = 0; snprintf(tmp, sizeof(tmp), "%d", makelevel); Var_Set(MAKE_LEVEL, tmp, VAR_GLOBAL, 0); snprintf(tmp, sizeof(tmp), "%u", myPid); Var_Set(".MAKE.PID", tmp, VAR_GLOBAL, 0); snprintf(tmp, sizeof(tmp), "%u", getppid()); Var_Set(".MAKE.PPID", tmp, VAR_GLOBAL, 0); } if (makelevel > 0) { char pn[1024]; snprintf(pn, sizeof(pn), "%s[%d]", progname, makelevel); progname = bmake_strdup(pn); } #ifdef USE_META meta_init(); #endif /* * First snag any flags out of the MAKE environment variable. * (Note this is *not* MAKEFLAGS since /bin/make uses that and it's * in a different format). */ #ifdef POSIX p1 = explode(getenv("MAKEFLAGS")); Main_ParseArgLine(p1); free(p1); #else Main_ParseArgLine(getenv("MAKE")); #endif /* * Find where we are (now). * We take care of PWD for the automounter below... */ if (getcwd(curdir, MAXPATHLEN) == NULL) { (void)fprintf(stderr, "%s: getcwd: %s.\n", progname, strerror(errno)); exit(2); } MainParseArgs(argc, argv); if (enterFlag) printf("%s: Entering directory `%s'\n", progname, curdir); /* * Verify that cwd is sane. */ if (stat(curdir, &sa) == -1) { (void)fprintf(stderr, "%s: %s: %s.\n", progname, curdir, strerror(errno)); exit(2); } /* * All this code is so that we know where we are when we start up * on a different machine with pmake. * Overriding getcwd() with $PWD totally breaks MAKEOBJDIRPREFIX * since the value of curdir can vary depending on how we got * here. Ie sitting at a shell prompt (shell that provides $PWD) * or via subdir.mk in which case its likely a shell which does * not provide it. * So, to stop it breaking this case only, we ignore PWD if * MAKEOBJDIRPREFIX is set or MAKEOBJDIR contains a transform. */ #ifndef NO_PWD_OVERRIDE if (!ignorePWD) { char *pwd, *ptmp1 = NULL, *ptmp2 = NULL; if ((pwd = getenv("PWD")) != NULL && Var_Value("MAKEOBJDIRPREFIX", VAR_CMD, &ptmp1) == NULL) { const char *makeobjdir = Var_Value("MAKEOBJDIR", VAR_CMD, &ptmp2); if (makeobjdir == NULL || !strchr(makeobjdir, '$')) { if (stat(pwd, &sb) == 0 && sa.st_ino == sb.st_ino && sa.st_dev == sb.st_dev) (void)strncpy(curdir, pwd, MAXPATHLEN); } } free(ptmp1); free(ptmp2); } #endif Var_Set(".CURDIR", curdir, VAR_GLOBAL, 0); /* * Find the .OBJDIR. If MAKEOBJDIRPREFIX, or failing that, * MAKEOBJDIR is set in the environment, try only that value * and fall back to .CURDIR if it does not exist. * * Otherwise, try _PATH_OBJDIR.MACHINE, _PATH_OBJDIR, and * finally _PATH_OBJDIRPREFIX`pwd`, in that order. If none * of these paths exist, just use .CURDIR. */ Dir_Init(curdir); (void)Main_SetObjdir(curdir); if ((path = Var_Value("MAKEOBJDIRPREFIX", VAR_CMD, &p1)) != NULL) { (void)snprintf(mdpath, MAXPATHLEN, "%s%s", path, curdir); (void)Main_SetObjdir(mdpath); free(p1); } else if ((path = Var_Value("MAKEOBJDIR", VAR_CMD, &p1)) != NULL) { (void)Main_SetObjdir(path); free(p1); } else { (void)snprintf(mdpath, MAXPATHLEN, "%s.%s", _PATH_OBJDIR, machine); if (!Main_SetObjdir(mdpath) && !Main_SetObjdir(_PATH_OBJDIR)) { (void)snprintf(mdpath, MAXPATHLEN, "%s%s", _PATH_OBJDIRPREFIX, curdir); (void)Main_SetObjdir(mdpath); } } /* * Initialize archive, target and suffix modules in preparation for * parsing the makefile(s) */ Arch_Init(); Targ_Init(); Suff_Init(); Trace_Init(tracefile); DEFAULT = NULL; (void)time(&now); Trace_Log(MAKESTART, NULL); /* * 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_Succ(ln)) { char *name = (char *)Lst_Datum(ln); Var_Append(".TARGETS", name, VAR_GLOBAL); } } else Var_Set(".TARGETS", "", VAR_GLOBAL, 0); /* * 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 (syspath == NULL || *syspath == '\0') syspath = defsyspath; else syspath = bmake_strdup(syspath); for (start = syspath; *start != '\0'; start = cp) { for (cp = start; *cp != '\0' && *cp != ':'; cp++) continue; if (*cp == ':') { *cp++ = '\0'; } /* look for magic parent directory search string */ if (strncmp(".../", start, 4) != 0) { (void)Dir_AddDir(defIncPath, start); } else { if (Dir_FindHereOrAbove(curdir, start+4, found_path, sizeof(found_path))) { (void)Dir_AddDir(defIncPath, found_path); } } } if (syspath != defsyspath) free(syspath); /* * Read in the built-in rules first, followed by the specified * makefile, if it was (makefile != NULL), or the default * makefile and Makefile, in that order, if it wasn't. */ if (!noBuiltins) { LstNode ln; sysMkPath = Lst_Init(FALSE); Dir_Expand(_PATH_DEFSYSMK, Lst_IsEmpty(sysIncPath) ? defIncPath : sysIncPath, sysMkPath); if (Lst_IsEmpty(sysMkPath)) Fatal("%s: no system rules (%s).", progname, _PATH_DEFSYSMK); ln = Lst_Find(sysMkPath, NULL, ReadMakefile); if (ln == NULL) Fatal("%s: cannot open %s.", progname, (char *)Lst_Datum(ln)); } if (!Lst_IsEmpty(makefiles)) { LstNode ln; ln = Lst_Find(makefiles, NULL, ReadAllMakefiles); if (ln != NULL) Fatal("%s: cannot open %s.", progname, (char *)Lst_Datum(ln)); } else { p1 = Var_Subst(NULL, "${" MAKEFILE_PREFERENCE "}", VAR_CMD, VARF_WANTRES); if (p1) { (void)str2Lst_Append(makefiles, p1, NULL); (void)Lst_Find(makefiles, NULL, ReadMakefile); free(p1); } } /* In particular suppress .depend for '-r -V .OBJDIR -f /dev/null' */ if (!noBuiltins || !printVars) { makeDependfile = Var_Subst(NULL, "${.MAKE.DEPENDFILE:T}", VAR_CMD, VARF_WANTRES); doing_depend = TRUE; (void)ReadMakefile(makeDependfile, NULL); doing_depend = FALSE; } if (enterFlagObj) printf("%s: Entering directory `%s'\n", progname, objdir); MakeMode(NULL); Var_Append("MFLAGS", Var_Value(MAKEFLAGS, VAR_GLOBAL, &p1), VAR_GLOBAL); free(p1); if (!forceJobs && !compatMake && Var_Exists(".MAKE.JOBS", VAR_GLOBAL)) { char *value; int n; value = Var_Subst(NULL, "${.MAKE.JOBS}", VAR_GLOBAL, VARF_WANTRES); n = strtol(value, NULL, 0); if (n < 1) { (void)fprintf(stderr, "%s: illegal value for .MAKE.JOBS -- must be positive integer!\n", progname); exit(1); } if (n != maxJobs) { Var_Append(MAKEFLAGS, "-j", VAR_GLOBAL); Var_Append(MAKEFLAGS, value, VAR_GLOBAL); } maxJobs = n; maxJobTokens = maxJobs; forceJobs = TRUE; free(value); } /* * Be compatible if user did not specify -j and did not explicitly * turned compatibility on */ if (!compatMake && !forceJobs) { compatMake = TRUE; } if (!compatMake) Job_ServerStart(maxJobTokens, jp_0, jp_1); if (DEBUG(JOB)) fprintf(debug_file, "job_pipe %d %d, maxjobs %d, tokens %d, compat %d\n", jp_0, jp_1, maxJobs, maxJobTokens, compatMake); Main_ExportMAKEFLAGS(TRUE); /* initial export */ /* * For compatibility, look at the directories in the VPATH variable * and add them to the search path, if the variable is defined. The * variable's value is in the same format as the PATH envariable, i.e. * <directory>:<directory>:<directory>... */ if (Var_Exists("VPATH", VAR_CMD)) { char *vpath, savec; /* * GCC stores string constants in read-only memory, but * Var_Subst will want to write this thing, so store it * in an array */ static char VPATH[] = "${VPATH}"; vpath = Var_Subst(NULL, VPATH, VAR_CMD, VARF_WANTRES); path = vpath; do { /* skip to end of directory */ for (cp = path; *cp != ':' && *cp != '\0'; cp++) continue; /* Save terminator character so know when to stop */ savec = *cp; *cp = '\0'; /* Add directory to search path */ (void)Dir_AddDir(dirSearchPath, path); *cp = savec; path = cp + 1; } while (savec == ':'); free(vpath); } /* * Now that all search paths have been read for suffixes et al, it's * time to add the default search path to their lists... */ Suff_DoPaths(); /* * Propagate attributes through :: dependency lists. */ Targ_Propagate(); /* print the initial graph, if the user requested it */ if (DEBUG(GRAPH1)) Targ_PrintGraph(1); /* print the values of any variables requested by the user */ if (printVars) { LstNode ln; Boolean expandVars; if (debugVflag) expandVars = FALSE; else expandVars = getBoolean(".MAKE.EXPAND_VARIABLES", FALSE); for (ln = Lst_First(variables); ln != NULL; ln = Lst_Succ(ln)) { char *var = (char *)Lst_Datum(ln); char *value; if (strchr(var, '$')) { value = p1 = Var_Subst(NULL, var, VAR_GLOBAL, VARF_WANTRES); } else if (expandVars) { char tmp[128]; if (snprintf(tmp, sizeof(tmp), "${%s}", var) >= (int)(sizeof(tmp))) Fatal("%s: variable name too big: %s", progname, var); value = p1 = Var_Subst(NULL, tmp, VAR_GLOBAL, VARF_WANTRES); } else { value = Var_Value(var, VAR_GLOBAL, &p1); } printf("%s\n", value ? value : ""); free(p1); } } 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)) targs = Parse_MainName(); else targs = Targ_FindList(create, TARG_CREATE); if (!compatMake) { /* * Initialize job module before traversing the graph * now that any .BEGIN and .END targets have been read. * This is done only if the -q flag wasn't given * (to prevent the .BEGIN from being executed should * it exist). */ if (!queryFlag) { Job_Init(); jobsRunning = TRUE; } /* Traverse the graph, checking on all the targets */ outOfDate = Make_Run(targs); } else { /* * Compat_Init will take care of creating all the * targets as well as initializing the module. */ Compat_Run(targs); } } #ifdef CLEANUP Lst_Destroy(targs, NULL); Lst_Destroy(variables, NULL); Lst_Destroy(makefiles, NULL); Lst_Destroy(create, (FreeProc *)free); #endif /* print the graph now it's been processed if the user requested it */ if (DEBUG(GRAPH2)) Targ_PrintGraph(2); Trace_Log(MAKEEND, 0); if (enterFlagObj) printf("%s: Leaving directory `%s'\n", progname, objdir); if (enterFlag) printf("%s: Leaving directory `%s'\n", progname, curdir); #ifdef USE_META meta_finish(); #endif Suff_End(); Targ_End(); Arch_End(); Var_End(); Parse_End(); Dir_End(); Job_End(); Trace_End(); return outOfDate ? 1 : 0; }
static Job * prepare_job(GNode *gn, int flags) { bool cmdsOK; /* true if the nodes commands were all right */ bool noExec; /* Set true if we decide not to run the job */ /* * Check the commands now so any attributes from .DEFAULT have a chance * to migrate to the node */ cmdsOK = Job_CheckCommands(gn); expand_commands(gn); if ((gn->type & OP_MAKE) || (!noExecute && !touchFlag)) { /* * We're serious here, but if the commands were bogus, we're * also dead... */ if (!cmdsOK) job_failure(gn, Punt); if (Lst_IsEmpty(&gn->commands)) noExec = true; else noExec = false; } else if (noExecute) { if (!cmdsOK || Lst_IsEmpty(&gn->commands)) noExec = true; else noExec = false; } else { /* * Just touch the target and note that no shell should be * executed. Check * the commands, too, but don't die if they're no good -- it * does no harm to keep working up the graph. */ Job_Touch(gn); noExec = true; } /* * If we're not supposed to execute a shell, don't. */ if (noExec) { /* * We only want to work our way up the graph if we aren't here * because the commands for the job were no good. */ if (cmdsOK && !aborting) { gn->built_status = MADE; Make_Update(gn); } return NULL; } else { Job *job; /* new job descriptor */ job = emalloc(sizeof(Job)); if (job == NULL) Punt("JobStart out of memory"); job->node = gn; /* * Set the initial value of the flags for this job based on the * global ones and the node's attributes... Any flags supplied * by the caller are also added to the field. */ job->flags = flags; if (gn->type & OP_CHEAP) return job; if ((gn->type & OP_EXPENSIVE) || expensive_commands(&gn->expanded)) job->flags |= JOB_IS_EXPENSIVE; return job; } }