📄 make.c
字号:
* 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 || (cgn->type & OP_SAVE_CMDS) || Dir_MTime(cgn) == 0) { cgn->mtime = now; } if (DEBUG(MAKE)) { printf("update time: %s\n", Targ_FmtTime(cgn->mtime)); }#endif } if (Lst_Open (cgn->parents) == SUCCESS) { while ((ln = Lst_Next (cgn->parents)) != NILLNODE) { pgn = (GNode *)Lst_Datum (ln); if (pgn->make) { pgn->unmade -= 1; if ( ! (cgn->type & (OP_EXEC|OP_USE))) { if (cgn->made == MADE) { pgn->childMade = TRUE; if (pgn->cmtime < cgn->mtime) { pgn->cmtime = cgn->mtime; } } else { (void)Make_TimeStamp (pgn, cgn); } } if (pgn->unmade == 0) { /* * Queue the node up -- any unmade predecessors will * be dealt with in MakeStartJobs. */ (void)Lst_EnQueue (toBeMade, (ClientData)pgn); } else if (pgn->unmade < 0) { Error ("Graph cycles through %s", pgn->name); } } } Lst_Close (cgn->parents); } /* * Deal with successor nodes. If any is marked for making and has an unmade * count of 0, has not been made and isn't in the examination queue, * it means we need to place it in the queue as it restrained itself * before. */ for (ln = Lst_First(cgn->successors); ln != NILLNODE; ln = Lst_Succ(ln)) { GNode *succ = (GNode *)Lst_Datum(ln); if (succ->make && succ->unmade == 0 && succ->made == UNMADE && Lst_Member(toBeMade, (ClientData)succ) == NILLNODE) { (void)Lst_EnQueue(toBeMade, (ClientData)succ); } } /* * 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); while ((ln = Lst_Next (cgn->iParents)) != NILLNODE) { pgn = (GNode *)Lst_Datum (ln); if (pgn->make) { Var_Set (IMPSRC, cname, pgn); Var_Set (PREFIX, cpref, pgn); } } Lst_Close (cgn->iParents); }}/*- *----------------------------------------------------------------------- * MakeAddAllSrc -- * Add a child's name to the ALLSRC and OODATE variables of the given * node. Called from Make_DoAllVar via Lst_ForEach. A child is added only * if it has not been given the .EXEC, .USE or .INVISIBLE attributes. * .EXEC and .USE children are very rarely going to be files, so... * A child is added to the OODATE variable if its modification time is * later than that of its parent, as defined by Make, except if the * parent is a .JOIN node. In that case, it is only added to the OODATE * variable if it was actually made (since .JOIN nodes don't have * modification times, the comparison is rather unfair...).. * * Results: * Always returns 0 * * Side Effects: * The ALLSRC variable for the given node is extended. *----------------------------------------------------------------------- */static intMakeAddAllSrc (cgn, pgn) GNode *cgn; /* The child to add */ GNode *pgn; /* The parent to whose ALLSRC variable it should be */ /* added */{ if ((cgn->type & (OP_EXEC|OP_USE|OP_INVISIBLE)) == 0) { register char *child; child = Var_Value(TARGET, cgn); Var_Append (ALLSRC, child, pgn); if (pgn->type & OP_JOIN) { if (cgn->made == MADE) { Var_Append(OODATE, child, pgn); } } else if ((pgn->mtime < cgn->mtime) || (cgn->mtime >= now && cgn->made == MADE)) { /* * It goes in the OODATE variable if the parent is younger than the * child or if the child has been modified more recently than * the start of the make. This is to keep pmake from getting * confused if something else updates the parent after the * make starts (shouldn't happen, I know, but sometimes it * does). In such a case, if we've updated the kid, the parent * is likely to have a modification time later than that of * the kid and anything that relies on the OODATE variable will * be hosed. * * XXX: This will cause all made children to go in the OODATE * variable, even if they're not touched, if RECHECK isn't defined, * since cgn->mtime is set to now in Make_Update. According to * some people, this is good... */ Var_Append(OODATE, child, pgn); } } return (0);}/*- *----------------------------------------------------------------------- * Make_DoAllVar -- * Set up the ALLSRC and OODATE variables. Sad to say, it must be * done separately, rather than while traversing the graph. This is * because Make defined OODATE to contain all sources whose modification * times were later than that of the target, *not* those sources that * were out-of-date. Since in both compatibility and native modes, * the modification time of the parent isn't found until the child * has been dealt with, we have to wait until now to fill in the * variable. As for ALLSRC, the ordering is important and not * guaranteed when in native mode, so it must be set here, too. * * Results: * None * * Side Effects: * The ALLSRC and OODATE variables of the given node is filled in. * If the node is a .JOIN node, its TARGET variable will be set to * match its ALLSRC variable. *----------------------------------------------------------------------- */voidMake_DoAllVar (gn) GNode *gn;{ Lst_ForEach (gn->children, MakeAddAllSrc, gn); if (!Var_Exists (OODATE, gn)) { Var_Set (OODATE, "", gn); } if (!Var_Exists (ALLSRC, gn)) { Var_Set (ALLSRC, "", gn); } if (gn->type & OP_JOIN) { Var_Set (TARGET, Var_Value (ALLSRC, gn), gn); }}/*- *----------------------------------------------------------------------- * MakeStartJobs -- * Start as many jobs as possible. * * Results: * If the query flag was given to pmake, no job will be started, * but as soon as an out-of-date target is found, this function * returns TRUE. At all other times, this function returns FALSE. * * Side Effects: * Nodes are removed from the toBeMade queue and job table slots * are filled. * *----------------------------------------------------------------------- */static BooleanMakeStartJobs (){ register GNode *gn; while (!Job_Full() && !Lst_IsEmpty (toBeMade)) { gn = (GNode *) Lst_DeQueue (toBeMade); if (DEBUG(MAKE)) { printf ("Examining %s...", gn->name); } /* * Make sure any and all predecessors that are going to be made, * have been. */ if (!Lst_IsEmpty(gn->preds)) { LstNode ln; for (ln = Lst_First(gn->preds); ln != NILLNODE; ln = Lst_Succ(ln)){ GNode *pgn = (GNode *)Lst_Datum(ln); if (pgn->make && pgn->made == UNMADE) { if (DEBUG(MAKE)) { printf("predecessor %s not made yet.\n", pgn->name); } break; } } /* * If ln isn't nil, there's a predecessor as yet unmade, so we * just drop this node on the floor. When the node in question * has been made, it will notice this node as being ready to * make but as yet unmade and will place the node on the queue. */ if (ln != NILLNODE) { continue; } } numNodes--; if (Make_OODate (gn)) { if (DEBUG(MAKE)) { printf ("out-of-date\n"); } if (queryFlag) { return (TRUE); } Make_DoAllVar (gn); Job_Make (gn); } else { if (DEBUG(MAKE)) { printf ("up-to-date\n"); } gn->made = UPTODATE; if (gn->type & OP_JOIN) { /* * Even for an up-to-date .JOIN node, we need it to have its * context variables so references to it get the correct * value for .TARGET when building up the context variables * of its parent(s)... */ Make_DoAllVar (gn); } Make_Update (gn); } } return (FALSE);}/*- *----------------------------------------------------------------------- * MakePrintStatus -- * Print the status of a top-level node, viz. it being up-to-date * already or not created due to an error in a lower level. * Callback function for Make_Run via Lst_ForEach. * * Results: * Always returns 0. * * Side Effects: * A message may be printed. * *----------------------------------------------------------------------- */static intMakePrintStatus(gn, cycle) GNode *gn; /* Node to examine */ Boolean cycle; /* True if gn->unmade being non-zero implies * a cycle in the graph, not an error in an * inferior */{ if (gn->made == UPTODATE) { printf ("`%s' is up to date.\n", gn->name); } else if (gn->unmade != 0) { if (cycle) { /* * If printing cycles and came to one that has unmade children, * print out the cycle by recursing on its children. Note a * cycle like: * a : b * b : c * c : b * will cause this to erroneously complain about a being in * the cycle, but this is a good approximation. */ if (gn->made == CYCLE) { Error("Graph cycles through `%s'", gn->name); gn->made = ENDCYCLE; Lst_ForEach(gn->children, MakePrintStatus, (ClientData)TRUE); gn->made = UNMADE; } else if (gn->made != ENDCYCLE) { gn->made = CYCLE; Lst_ForEach(gn->children, MakePrintStatus, (ClientData)TRUE); } } else { printf ("`%s' not remade because of errors.\n", gn->name); } } return (0);}/*- *----------------------------------------------------------------------- * Make_Run -- * Initialize the nodes to remake and the list of nodes which are * ready to be made by doing a breadth-first traversal of the graph * starting from the nodes in the given list. Once this traversal * is finished, all the 'leaves' of the graph are in the toBeMade * queue. * Using this queue and the Job module, work back up the graph, * calling on MakeStartJobs to keep the job table as full as * possible. * * Results: * TRUE if work was done. FALSE otherwise. * * Side Effects: * The make field of all nodes involved in the creation of the given * targets is set to 1. The toBeMade list is set to contain all the * 'leaves' of these subgraphs. *----------------------------------------------------------------------- */BooleanMake_Run (targs) Lst targs; /* the initial list of targets */{ register GNode *gn; /* a temporary pointer */ register Lst examine; /* List of targets to examine */ int errors; /* Number of errors the Job module reports */ toBeMade = Lst_Init (FALSE); examine = Lst_Duplicate(targs, NOCOPY); numNodes = 0; /* * Make an initial downward pass over the graph, marking nodes to be made * as we go down. We call Suff_FindDeps to find where a node is and * to get some children for it if it has none and also has no commands. * If the node is a leaf, we stick it on the toBeMade queue to * be looked at in a minute, otherwise we add its children to our queue * and go on about our business. */ while (!Lst_IsEmpty (examine)) { gn = (GNode *) Lst_DeQueue (examine); if (!gn->make) { gn->make = TRUE; numNodes++; /* * Apply any .USE rules before looking for implicit dependencies * to make sure everything has commands that should... */ Lst_ForEach (gn->children, Make_HandleUse, (ClientData)gn); Suff_FindDeps (gn); if (gn->unmade != 0) { Lst_ForEach (gn->children, MakeAddChild, (ClientData)examine); } else { (void)Lst_EnQueue (toBeMade, (ClientData)gn); } } } Lst_Destroy (examine, NOFREE); if (queryFlag) { /* * We wouldn't do any work unless we could start some jobs in the * next loop... (we won't actually start any, of course, this is just * to see if any of the targets was out of date) */ return (MakeStartJobs()); } else { /* * Initialization. At the moment, no jobs are running and until some * get started, nothing will happen since the remaining upward * traversal of the graph is performed by the routines in job.c upon * the finishing of a job. So we fill the Job table as much as we can * before going into our loop. */ (void) MakeStartJobs(); } /* * Main Loop: The idea here is that the ending of jobs will take * care of the maintenance of data structures and the waiting for output * will cause us to be idle most of the time while our children run as * much as possible. Because the job table is kept as full as possible, * the only time when it will be empty is when all the jobs which need * running have been run, so that is the end condition of this loop. * Note that the Job module will exit if there were any errors unless the * keepgoing flag was given. */ while (!Job_Empty ()) { Job_CatchOutput (); Job_CatchChildren (!usePipes); (void)MakeStartJobs(); } errors = Job_End(); /* * Print the final status of each target. E.g. if it wasn't made * because some inferior reported an error. */ Lst_ForEach(targs, MakePrintStatus, (ClientData)((errors == 0) && (numNodes != 0))); return (TRUE);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -