static int meta_ignore(GNode *gn, const char *p) { char fname[MAXPATHLEN]; if (p == NULL) return TRUE; if (*p == '/') { cached_realpath(p, fname); /* clean it up */ if (Lst_ForEach(metaIgnorePaths, prefix_match, fname)) { #ifdef DEBUG_META_MODE if (DEBUG(META)) fprintf(debug_file, "meta_oodate: ignoring path: %s\n", p); #endif return TRUE; } } if (metaIgnorePatterns) { char *pm; snprintf(fname, sizeof(fname), "${%s:@m@${%s:L:M$m}@}", MAKE_META_IGNORE_PATTERNS, p); pm = Var_Subst(NULL, fname, gn, VARF_WANTRES); if (*pm) { #ifdef DEBUG_META_MODE if (DEBUG(META)) fprintf(debug_file, "meta_oodate: ignoring pattern: %s\n", p); #endif free(pm); return TRUE; } free(pm); } if (metaIgnoreFilter) { char *fm; /* skip if filter result is empty */ snprintf(fname, sizeof(fname), "${%s:L:${%s:ts:}}", p, MAKE_META_IGNORE_FILTER); fm = Var_Subst(NULL, fname, gn, VARF_WANTRES); if (*fm == '\0') { #ifdef DEBUG_META_MODE if (DEBUG(META)) fprintf(debug_file, "meta_oodate: ignoring filtered: %s\n", p); #endif free(fm); return TRUE; } free(fm); } return FALSE; }
/* * 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; }
/*- *----------------------------------------------------------------------- * Targ_PrintGraph -- * print the entire graph. heh heh * * Results: * none * * Side Effects: * lots o' output *----------------------------------------------------------------------- */ void Targ_PrintGraph (int pass) /* Which pass this is. 1 => no processing * 2 => processing done */ { printf("#*** Input graph:\n"); Lst_ForEach (allTargets, TargPrintNode, (ClientData)pass); printf("\n\n"); printf("#\n# Files that are only sources:\n"); Lst_ForEach (allTargets, TargPrintOnlySrc, (ClientData)NULL); printf("#*** Global Variables:\n"); Var_Dump (VAR_GLOBAL); printf("#*** Command-line Variables:\n"); Var_Dump (VAR_CMD); printf("\n"); Dir_PrintDirectories(); printf("\n"); Suff_PrintAll(); }
/* Add stuff to the toBeMade queue. we try to sort things so that stuff * that can be done directly is done right away. This won't be perfect, * since some dependencies are only discovered later (e.g., SuffFindDeps). */ static void add_targets_to_make(Lst todo) { GNode *gn; unsigned int slot; AppendList2Array(todo, &examine); while ((gn = Array_Pop(&examine)) != NULL) { if (gn->must_make) /* already known */ continue; gn->must_make = true; slot = ohash_qlookup(&targets, gn->name); if (!ohash_find(&targets, slot)) ohash_insert(&targets, slot, gn); look_harder_for_target(gn); kludge_look_harder_for_target(gn); /* * Apply any .USE rules before looking for implicit * dependencies to make sure everything that should have * commands has commands ... */ Lst_ForEach(&gn->children, MakeHandleUse, gn); expand_all_children(gn); if (gn->unmade != 0) { if (DEBUG(MAKE)) printf("%s: not queuing (%d unmade children)\n", gn->name, gn->unmade); Lst_ForEach(&gn->children, MakeAddChild, &examine); } else { if (DEBUG(MAKE)) printf("%s: queuing\n", gn->name); Array_Push(&toBeMade, gn); } } if (randomize_queue) randomize_garray(&toBeMade); }
MAKE_ATTR_DEAD static void make_abort(GNode *gn, int line) { static int two = 2; fprintf(debug_file, "make_abort from line %d\n", line); Targ_PrintNode(gn, &two); Lst_ForEach(toBeMade, Targ_PrintNode, &two); Targ_PrintGraph(3); abort(); }
/*- *----------------------------------------------------------------------- * 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. * * Side Effects: * A message may be printed. *----------------------------------------------------------------------- */ static void MakePrintStatus( void *gnp, /* Node to examine */ void *cyclep) /* True if gn->unmade being non-zero implies * a cycle in the graph, not an error in an * inferior */ { GNode *gn = (GNode *)gnp; bool cycle = *(bool *)cyclep; if (gn->built_status == UPTODATE) { printf("`%s' is up to date.\n", gn->name); } else if (gn->unmade != 0) { if (cycle) { bool t = true; /* * 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->built_status == CYCLE) { Error("Graph cycles through `%s'", gn->name); gn->built_status = ENDCYCLE; Lst_ForEach(&gn->children, MakePrintStatus, &t); gn->built_status = UNKNOWN; } else if (gn->built_status != ENDCYCLE) { gn->built_status = CYCLE; Lst_ForEach(&gn->children, MakePrintStatus, &t); } } else { printf("`%s' not remade because of errors.\n", gn->name); } } }
/*- *----------------------------------------------------------------------- * 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_OODate -- * See if a given node is out of date with respect to its sources. * Used by Make_Run when deciding which nodes to place on the * toBeMade queue initially and by Make_Update to screen out USE and * EXEC nodes. In the latter case, however, any other sort of node * must be considered out-of-date since at least one of its children * will have been recreated. * * Input: * gn the node to check * * Results: * TRUE if the node is out of date. FALSE otherwise. * * Side Effects: * The mtime field of the node and the cmgn field of its parents * will/may be changed. *----------------------------------------------------------------------- */ Boolean Make_OODate(GNode *gn) { Boolean oodate; /* * Certain types of targets needn't even be sought as their datedness * doesn't depend on their modification time... */ if ((gn->type & (OP_JOIN|OP_USE|OP_USEBEFORE|OP_EXEC)) == 0) { (void)Dir_MTime(gn, 1); if (DEBUG(MAKE)) { if (gn->mtime != 0) { fprintf(debug_file, "modified %s...", Targ_FmtTime(gn->mtime)); } else { fprintf(debug_file, "non-existent..."); } } } /* * A target is remade in one of the following circumstances: * its modification time is smaller than that of its youngest child * and it would actually be run (has commands or type OP_NOP) * it's the object of a force operator * it has no children, was on the lhs of an operator and doesn't exist * already. * * Libraries are only considered out-of-date if the archive module says * they are. * * These weird rules are brought to you by Backward-Compatibility and * the strange people who wrote 'Make'. */ if (gn->type & (OP_USE|OP_USEBEFORE)) { /* * If the node is a USE node it is *never* out of date * no matter *what*. */ if (DEBUG(MAKE)) { fprintf(debug_file, ".USE node..."); } oodate = FALSE; } else if ((gn->type & OP_LIB) && ((gn->mtime==0) || Arch_IsLib(gn))) { if (DEBUG(MAKE)) { fprintf(debug_file, "library..."); } /* * always out of date if no children and :: target * or non-existent. */ oodate = (gn->mtime == 0 || Arch_LibOODate(gn) || (gn->cmgn == NULL && (gn->type & OP_DOUBLEDEP))); } else if (gn->type & OP_JOIN) { /* * A target with the .JOIN attribute is only considered * out-of-date if any of its children was out-of-date. */ if (DEBUG(MAKE)) { fprintf(debug_file, ".JOIN node..."); } if (DEBUG(MAKE)) { fprintf(debug_file, "source %smade...", gn->flags & CHILDMADE ? "" : "not "); } oodate = (gn->flags & CHILDMADE) ? TRUE : FALSE; } else if (gn->type & (OP_FORCE|OP_EXEC|OP_PHONY)) { /* * A node which is the object of the force (!) operator or which has * the .EXEC attribute is always considered out-of-date. */ if (DEBUG(MAKE)) { if (gn->type & OP_FORCE) { fprintf(debug_file, "! operator..."); } else if (gn->type & OP_PHONY) { fprintf(debug_file, ".PHONY node..."); } else { fprintf(debug_file, ".EXEC node..."); } } oodate = TRUE; } else if ((gn->cmgn != NULL && gn->mtime < gn->cmgn->mtime) || (gn->cmgn == NULL && ((gn->mtime == 0 && !(gn->type & OP_OPTIONAL)) || gn->type & OP_DOUBLEDEP))) { /* * A node whose modification time is less than that of its * youngest child or that has no children (cmgn == NULL) and * either doesn't exist (mtime == 0) and it isn't optional * or was the object of a * :: operator is out-of-date. * Why? Because that's the way Make does it. */ if (DEBUG(MAKE)) { if (gn->cmgn != NULL && gn->mtime < gn->cmgn->mtime) { fprintf(debug_file, "modified before source %s...", gn->cmgn->path); } else if (gn->mtime == 0) { fprintf(debug_file, "non-existent and no sources..."); } else { fprintf(debug_file, ":: operator and no sources..."); } } oodate = TRUE; } else { /* * When a non-existing child with no sources * (such as a typically used FORCE source) has been made and * the target of the child (usually a directory) has the same * timestamp as the timestamp just given to the non-existing child * after it was considered made. */ if (DEBUG(MAKE)) { if (gn->flags & FORCE) fprintf(debug_file, "non existing child..."); } oodate = (gn->flags & FORCE) ? TRUE : FALSE; } #ifdef USE_META if (useMeta) { oodate = meta_oodate(gn, oodate); } #endif /* * If the target isn't out-of-date, the parents need to know its * modification time. Note that targets that appear to be out-of-date * but aren't, because they have no commands and aren't of type OP_NOP, * have their mtime stay below their children's mtime to keep parents from * thinking they're out-of-date. */ if (!oodate) { Lst_ForEach(gn->parents, MakeTimeStamp, gn); } return (oodate); }
/*- *----------------------------------------------------------------------- * Make_Run -- * Initialize the nodes to remake and the list of nodes which are * ready to be made by doing a breadth-first traversal of the graph * starting from the nodes in the given list. Once this traversal * is finished, all the 'leaves' of the graph are in the toBeMade * queue. * Using this queue and the Job module, work back up the graph, * calling on MakeStartJobs to keep the job table as full as * possible. * * Results: * true if work was done. false otherwise. * * Side Effects: * The must_make field of all nodes involved in the creation of the given * targets is set to 1. The toBeMade list is set to contain all the * 'leaves' of these subgraphs. *----------------------------------------------------------------------- */ bool Make_Run(Lst targs) /* the initial list of targets */ { int errors; /* Number of errors the Job module reports */ GNode *gn; unsigned int i; bool cycle; /* wild guess at initial sizes */ Array_Init(&toBeMade, 500); Array_Init(&examine, 150); ohash_init(&targets, 10, &gnode_info); if (DEBUG(PARALLEL)) random_setup(); add_targets_to_make(targs); if (queryFlag) { /* * We wouldn't do any work unless we could start some jobs in * the next loop... (we won't actually start any, of course, * this is just to see if any of the targets was out of date) */ return MakeStartJobs(); } else { /* * Initialization. At the moment, no jobs are running and until * some get started, nothing will happen since the remaining * upward traversal of the graph is performed by the routines * in job.c upon the finishing of a job. So we fill the Job * table as much as we can before going into our loop. */ (void)MakeStartJobs(); } /* * Main Loop: The idea here is that the ending of jobs will take * care of the maintenance of data structures and the waiting for output * will cause us to be idle most of the time while our children run as * much as possible. Because the job table is kept as full as possible, * the only time when it will be empty is when all the jobs which need * running have been run, so that is the end condition of this loop. * Note that the Job module will exit if there were any errors unless * the keepgoing flag was given. */ while (!Job_Empty()) { handle_running_jobs(); (void)MakeStartJobs(); } errors = Job_Finish(); cycle = false; for (gn = ohash_first(&targets, &i); gn != NULL; gn = ohash_next(&targets, &i)) { if (has_been_built(gn)) continue; cycle = true; errors++; printf("Error: target %s unaccounted for (%s)\n", gn->name, status_to_string(gn)); } /* * Print the final status of each target. E.g. if it wasn't made * because some inferior reported an error. */ Lst_ForEach(targs, MakePrintStatus, &cycle); if (errors) Fatal("Errors while building"); return true; }
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; }
bool Make_OODate(GNode *gn) { bool oodate; /* * Certain types of targets needn't even be sought as their datedness * doesn't depend on their modification time... */ if ((gn->type & (OP_JOIN|OP_USE|OP_EXEC|OP_PHONY)) == 0) { (void)Dir_MTime(gn); if (DEBUG(MAKE)) { if (!is_out_of_date(gn->mtime)) printf("modified %s...", time_to_string(gn->mtime)); else printf("non-existent..."); } } /* * A target is remade in one of the following circumstances: * - its modification time is smaller than that of its youngest child * and it would actually be run (has commands or type OP_NOP) * - it's the object of a force operator * - it has no children, was on the lhs of an operator and doesn't * exist already. * * Libraries are only considered out-of-date if the archive module says * they are. */ if (gn->type & OP_USE) { /* * If the node is a USE node it is *never* out of date * no matter *what*. */ if (DEBUG(MAKE)) printf(".USE node..."); oodate = false; } else if ((gn->type & OP_LIB) && Arch_IsLib(gn)) { if (DEBUG(MAKE)) printf("library..."); /* always out of date if no children and :: target */ oodate = Arch_LibOODate(gn) || (is_out_of_date(gn->cmtime) && (gn->type & OP_DOUBLEDEP)); } else if (gn->type & OP_JOIN) { /* * A target with the .JOIN attribute is only considered * out-of-date if any of its children was out-of-date. */ if (DEBUG(MAKE)) printf(".JOIN node..."); oodate = gn->childMade; } else if (gn->type & (OP_FORCE|OP_EXEC|OP_PHONY)) { /* * A node which is the object of the force (!) operator or which * has the .EXEC attribute is always considered out-of-date. */ if (DEBUG(MAKE)) { if (gn->type & OP_FORCE) printf("! operator..."); else if (gn->type & OP_PHONY) printf(".PHONY node..."); else printf(".EXEC node..."); } oodate = true; } else if (is_strictly_before(gn->mtime, gn->cmtime) || (is_out_of_date(gn->cmtime) && (is_out_of_date(gn->mtime) || (gn->type & OP_DOUBLEDEP)))) { /* * A node whose modification time is less than that of its * youngest child or that has no children (cmtime == * OUT_OF_DATE) and either doesn't exist (mtime == OUT_OF_DATE) * or was the object of a :: operator is out-of-date. */ if (DEBUG(MAKE)) { if (is_strictly_before(gn->mtime, gn->cmtime)) printf("modified before source..."); else if (is_out_of_date(gn->mtime)) printf("non-existent and no sources..."); else printf(":: operator and no sources..."); } oodate = true; } else { oodate = false; } /* * If the target isn't out-of-date, the parents need to know its * modification time. Note that targets that appear to be out-of-date * but aren't, because they have no commands and aren't of type OP_NOP, * have their mtime stay below their children's mtime to keep parents * from thinking they're out-of-date. */ if (!oodate) Lst_ForEach(&gn->parents, MakeTimeStamp, gn); return oodate; }
/*- *----------------------------------------------------------------------- * CompatMake -- * Make a target. * * Side Effects: * If an error is detected and not being ignored, the process exits. *----------------------------------------------------------------------- */ static void CompatMake(void *gnp, /* The node to make */ void *pgnp) /* Parent to abort if necessary */ { GNode *gn = (GNode *)gnp; GNode *pgn = (GNode *)pgnp; GNode *sib; bool cmdsOk; if (DEBUG(MAKE)) printf("CompatMake(%s, %s)\n", pgn ? pgn->name : "NULL", gn->name); /* XXX some loops are not loops, people write dependencies * between siblings to make sure they get built. * Also, we don't recognize direct loops. */ if (gn == pgn) return; /* handle .USE right away */ if (gn->type & OP_USE) { Make_HandleUse(gn, pgn); return; } look_harder_for_target(gn); if (pgn != NULL && is_sibling(gn, pgn)) return; if (pgn == NULL) pgn = gn; if (pgn->type & OP_MADE) { sib = gn; do { sib->mtime = gn->mtime; sib->built_status = UPTODATE; sib = sib->sibling; } while (sib != gn); } switch(gn->built_status) { case UNKNOWN: /* First mark ourselves to be made, then apply whatever * transformations the suffix module thinks are necessary. * Once that's done, we can descend and make all our children. * If any of them has an error but the -k flag was given, * our 'must_make' field will be set false again. This is our * signal to not attempt to do anything but abort our * parent as well. */ gn->must_make = true; gn->built_status = BEINGMADE; /* note that, in case we have siblings, we only check all * children for all siblings, but we don't try to apply * any other rule. */ sib = gn; do { Suff_FindDeps(sib); Lst_ForEach(&sib->children, CompatMake, gn); sib = sib->sibling; } while (sib != gn); if (!gn->must_make) { Error("Build for %s aborted", gn->name); gn->built_status = ABORTED; pgn->must_make = false; return; } /* All the children were made ok. Now youngest points to * the newest child, we need to find out * if we exist and when we were modified last. The criteria * for datedness are defined by the Make_OODate function. */ if (DEBUG(MAKE)) printf("Examining %s...", gn->name); if (!Make_OODate(gn)) { gn->built_status = UPTODATE; if (DEBUG(MAKE)) printf("up-to-date.\n"); return; } else if (DEBUG(MAKE)) printf("out-of-date.\n"); /* If the user is just seeing if something is out-of-date, * exit now to tell him/her "yes". */ if (queryFlag) exit(1); /* normally, we run the job, but if we can't find any * commands, we defer to siblings instead. */ sib = gn; do { /* We need to be re-made. We also have to make sure * we've got a $? variable. To be nice, we also define * the $> variable using Make_DoAllVar(). */ Make_DoAllVar(sib); cmdsOk = node_find_valid_commands(sib); if (cmdsOk || (gn->type & OP_OPTIONAL)) break; sib = sib->sibling; } while (sib != gn); if (cmdsOk) { /* Our commands are ok, but we still have to worry * about the -t flag... */ if (!touchFlag) run_gnode(sib); else { Job_Touch(sib); if (gn != sib) Job_Touch(gn); } } else { node_failure(gn); sib->built_status = ERROR; } /* copy over what we just did */ gn->built_status = sib->built_status; if (gn->built_status != ERROR) { /* If the node was made successfully, mark it so, * update its modification time and timestamp all * its parents. * This is to keep its state from affecting that of * its parent. */ gn->built_status = MADE; sib->built_status = MADE; /* 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 (and the speed of * this program), there are times when the * modification time of a file created on a remote * machine will not be modified before the stat() * implied by the Dir_MTime occurs, thus leading us * to believe that the file is unchanged, wreaking * havoc with files that depend on this one. */ if (noExecute || is_out_of_date(Dir_MTime(gn))) clock_gettime(CLOCK_REALTIME, &gn->mtime); if (is_strictly_before(gn->mtime, gn->youngest->mtime)) gn->mtime = gn->youngest->mtime; if (sib != gn) { if (noExecute || is_out_of_date(Dir_MTime(sib))) clock_gettime(CLOCK_REALTIME, &sib->mtime); if (is_strictly_before(sib->mtime, sib->youngest->mtime)) sib->mtime = sib->youngest->mtime; } if (DEBUG(MAKE)) printf("update time: %s\n", time_to_string(&gn->mtime)); if (!(gn->type & OP_EXEC)) { pgn->childMade = true; Make_TimeStamp(pgn, gn); } } else if (keepgoing) pgn->must_make = false; else { print_errors(); exit(1); } break; case ERROR: /* Already had an error when making this beastie. Tell the * parent to abort. */ pgn->must_make = false; break; case BEINGMADE: Error("Graph cycles through %s", gn->name); gn->built_status = ERROR; pgn->must_make = false; break; case MADE: if ((gn->type & OP_EXEC) == 0) { pgn->childMade = true; Make_TimeStamp(pgn, gn); } break; case UPTODATE: if ((gn->type & OP_EXEC) == 0) Make_TimeStamp(pgn, gn); break; default: break; } }
/*- *----------------------------------------------------------------------- * 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); }
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; mf.fp = NULL; i = 0; dname = Var_Value(".OBJDIR", gn, &p[i++]); tname = Var_Value(TARGET, gn, &p[i++]); /* if this succeeds objdir is realpath of dname */ if (!meta_needed(gn, dname, tname, objdir, TRUE)) goto out; dname = objdir; if (metaVerbose) { char *mp; /* Describe the target we are building */ mp = Var_Subst(NULL, "${" MAKE_META_PREFIX "}", gn, VARF_WANTRES); 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 (!writeMeta) /* Don't create meta data. */ goto out; fname = meta_name(gn, pbm->meta_fname, sizeof(pbm->meta_fname), dname, tname, objdir); #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--) { free(p[i]); } return (mf.fp); }
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); }
/*- *----------------------------------------------------------------------- * Compat_Make -- * Make a target. * * Input: * gnp The node to make * pgnp Parent to abort if necessary * * Results: * 0 * * Side Effects: * If an error is detected and not being ignored, the process exits. * *----------------------------------------------------------------------- */ int Compat_Make(void *gnp, void *pgnp) { GNode *gn = (GNode *)gnp; GNode *pgn = (GNode *)pgnp; if (!meta[0]) /* we came here from jobs */ Compat_Init(); if (gn->made == UNMADE && (gn == pgn || (pgn->type & OP_MADE) == 0)) { /* * First mark ourselves to be made, then apply whatever transformations * the suffix module thinks are necessary. Once that's done, we can * descend and make all our children. If any of them has an error * but the -k flag was given, our 'make' field will be set FALSE again. * This is our signal to not attempt to do anything but abort our * parent as well. */ gn->flags |= REMAKE; gn->made = BEINGMADE; if ((gn->type & OP_MADE) == 0) Suff_FindDeps(gn); Lst_ForEach(gn->children, Compat_Make, gn); if ((gn->flags & REMAKE) == 0) { gn->made = ABORTED; pgn->flags &= ~REMAKE; goto cohorts; } if (Lst_Member(gn->iParents, pgn) != NULL) { char *p1; Var_Set(IMPSRC, Var_Value(TARGET, gn, &p1), pgn, 0); if (p1) free(p1); } /* * All the children were made ok. Now cmgn->mtime contains the * modification time of the newest child, we need to find out if we * exist and when we were modified last. The criteria for datedness * are defined by the Make_OODate function. */ if (DEBUG(MAKE)) { fprintf(debug_file, "Examining %s...", gn->name); } if (! Make_OODate(gn)) { gn->made = UPTODATE; if (DEBUG(MAKE)) { fprintf(debug_file, "up-to-date.\n"); } goto cohorts; } else if (DEBUG(MAKE)) { fprintf(debug_file, "out-of-date.\n"); } /* * If the user is just seeing if something is out-of-date, exit now * to tell him/her "yes". */ if (queryFlag) { exit(1); } /* * We need to be re-made. We also have to make sure we've got a $? * variable. To be nice, we also define the $> variable using * Make_DoAllVar(). */ Make_DoAllVar(gn); /* * Alter our type to tell if errors should be ignored or things * should not be printed so CompatRunCommand knows what to do. */ if (Targ_Ignore(gn)) { gn->type |= OP_IGNORE; } if (Targ_Silent(gn)) { gn->type |= OP_SILENT; } if (Job_CheckCommands(gn, Fatal)) { /* * Our commands are ok, but we still have to worry about the -t * flag... */ if (!touchFlag || (gn->type & OP_MAKE)) { curTarg = gn; #ifdef USE_META if (useMeta && !NoExecute(gn)) { meta_job_start(NULL, gn); } #endif Lst_ForEach(gn->commands, CompatRunCommand, gn); curTarg = NULL; } else { Job_Touch(gn, gn->type & OP_SILENT); } } else { gn->made = ERROR; } #ifdef USE_META if (useMeta && !NoExecute(gn)) { meta_job_finish(NULL); } #endif if (gn->made != ERROR) { /* * If the node was made successfully, mark it so, update * its modification time and timestamp all its parents. Note * that for .ZEROTIME targets, the timestamping isn't done. * This is to keep its state from affecting that of its parent. */ gn->made = MADE; pgn->flags |= Make_Recheck(gn) == 0 ? FORCE : 0; if (!(gn->type & OP_EXEC)) { pgn->flags |= CHILDMADE; Make_TimeStamp(pgn, gn); } } else if (keepgoing) { pgn->flags &= ~REMAKE; } else { PrintOnError(gn, "\n\nStop."); exit(1); } } else if (gn->made == ERROR) { /* * Already had an error when making this beastie. Tell the parent * to abort. */ pgn->flags &= ~REMAKE; } else { if (Lst_Member(gn->iParents, pgn) != NULL) { char *p1; Var_Set(IMPSRC, Var_Value(TARGET, gn, &p1), pgn, 0); if (p1) free(p1); } switch(gn->made) { case BEINGMADE: Error("Graph cycles through %s", gn->name); gn->made = ERROR; pgn->flags &= ~REMAKE; break; case MADE: if ((gn->type & OP_EXEC) == 0) { pgn->flags |= CHILDMADE; Make_TimeStamp(pgn, gn); } break; case UPTODATE: if ((gn->type & OP_EXEC) == 0) { Make_TimeStamp(pgn, gn); } break; default: break; } } cohorts: Lst_ForEach(gn->cohorts, Compat_Make, pgnp); 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; }
void For_Run(For *arg) { arg->text = Buf_Retrieve(&arg->buf); arg->guess = Buf_Size(&arg->buf) + GUESS_EXPANSION; arg->var = NULL; Lst_ForEach(&arg->lst, ForExec, arg); Buf_Destroy(&arg->buf); Lst_Destroy(&arg->vars, (SimpleProc)Var_DeleteLoopVar); Lst_Destroy(&arg->lst, (SimpleProc)free); free(arg); }