/*- *----------------------------------------------------------------------- * Lst_Duplicate -- * Duplicate an entire list. If a function to copy a void *is * given, the individual client elements will be duplicated as well. * * Input: * l the list to duplicate * copyProc A function to duplicate each void * * * Results: * The new Lst structure or NULL if failure. * * Side Effects: * A new list is created. *----------------------------------------------------------------------- */ Lst Lst_Duplicate(Lst l, DuplicateProc *copyProc) { Lst nl; ListNode ln; List list = l; if (!LstValid (l)) { return NULL; } nl = Lst_Init(list->isCirc); if (nl == NULL) { return NULL; } ln = list->firstPtr; while (ln != NULL) { if (copyProc != NULL) { if (Lst_AtEnd(nl, copyProc(ln->datum)) == FAILURE) { return NULL; } } else if (Lst_AtEnd(nl, ln->datum) == FAILURE) { return NULL; } if (list->isCirc && ln == list->lastPtr) { ln = NULL; } else { ln = ln->nextPtr; } } return (nl); }
/*- *--------------------------------------------------------------------- * ParseAddDep -- * Check if the pair of GNodes given needs to be synchronized. * This has to be when two nodes are on different sides of a * .WAIT directive. * * Results: * Returns 0 if the two targets need to be ordered, 1 otherwise. * If it returns 0, the search can stop. * * Side Effects: * A dependency can be added between the two nodes. * *--------------------------------------------------------------------- */ static int ParseAddDep(GNode *p, GNode *s) { if (p->order < s->order) { /* XXX: This can cause loops, and loops can cause unmade * targets, but checking is tedious, and the debugging output * can show the problem. */ Lst_AtEnd(&p->successors, s); Lst_AtEnd(&s->preds, p); return 1; } else return 0; }
/** * Lst_Duplicate * Duplicate an entire list. If a function to copy a void * is * given, the individual client elements will be duplicated as well. * * Arguments: * dst the destination list (initialized) * src the list to duplicate * copyProc A function to duplicate each void */ void Lst_Duplicate(Lst *dst, Lst *src, DuplicateProc *copyProc) { LstNode *ln; ln = src->firstPtr; while (ln != NULL) { if (copyProc != NOCOPY) Lst_AtEnd(dst, (*copyProc)(ln->datum)); else Lst_AtEnd(dst, ln->datum); ln = ln->nextPtr; } }
/*- *----------------------------------------------------------------------- * Lst_Clone -- * Duplicate an entire list. If a function to copy a void * is * given, the individual client elements will be duplicated as well. * * Results: * returns the new list. * * Side Effects: * The new list is created. *----------------------------------------------------------------------- */ Lst Lst_Clone(Lst nl, Lst l, DuplicateProc copyProc) { LstNode ln; Lst_Init(nl); for (ln = l->firstPtr; ln != NULL; ln = ln->nextPtr) { if (copyProc != NOCOPY) Lst_AtEnd(nl, (*copyProc)(ln->datum)); else Lst_AtEnd(nl, ln->datum); } return nl; }
/*- *----------------------------------------------------------------------- * Targ_FindList -- * Make a complete list of GNodes from the given list of names * * Results: * A complete list of graph nodes corresponding to all instances of all * the names in names. * * Side Effects: * If flags is TARG_CREATE, nodes will be created for all names in * names which do not yet have graph nodes. If flags is TARG_NOCREATE, * an error message will be printed for each name which can't be found. * ----------------------------------------------------------------------- */ Lst Targ_FindList ( Lst names, /* list of names to find */ int flags) /* flags used if no node is found for a given * name */ { Lst nodes; /* result list */ register LstNode ln; /* name list element */ register GNode *gn; /* node in tLn */ string_t name; nodes = Lst_Init(); Lst_Open (names); while ((ln = Lst_Next (names)) != NILLNODE) { name = (string_t)Lst_Datum(ln); gn = Targ_FindNode (name, flags); if (gn != NILGNODE) { /* * Note: Lst_AtEnd must come before the Lst_Concat so the nodes * are added to the list in the order in which they were * encountered in the makefile. */ (void) Lst_AtEnd (nodes, (ClientData)gn); if (gn->type & OP_DOUBLEDEP) { (void)Lst_Concat (nodes, gn->cohorts, LST_CONCNEW); } } else if (flags == TARG_NOCREATE) { Error ("\"%s\" -- target unknown.", name->data); } } Lst_Close (names); return (nodes); }
/*- *----------------------------------------------------------------------- * Targ_FindNode -- * Find a node in the list using the given name for matching * * Results: * The node in the list if it was. If it wasn't, return NILGNODE of * flags was TARG_NOCREATE or the newly created and initialized node * if it was TARG_CREATE * * Side Effects: * Sometimes a node is created and added to the list *----------------------------------------------------------------------- */ GNode * Targ_FindNode ( string_t name, /* the name to find */ int flags) /* flags governing events when target not * found */ { GNode *gn; /* node in that element */ Hash_Entry *he; /* New or used hash entry for node */ Boolean isNew; /* Set TRUE if Hash_CreateEntry had to create */ /* an entry for the node */ if (DEBUG(TARG)) { printf("Targ_FindNode(name %s)\n", name->data); } if (flags & TARG_CREATE) { he = Hash_CreateEntry (&targets, name, &isNew); if (isNew) { gn = Targ_NewGN (name); Hash_SetValue (he, gn); (void) Lst_AtEnd (allTargets, (ClientData)gn); } } else { he = Hash_FindEntry (&targets, name); } if (he == (Hash_Entry *) NULL) { return (NILGNODE); } else { return ((GNode *) Hash_GetValue (he)); } }
/*- *----------------------------------------------------------------------- * Targ_FindList -- * Make a complete list of GNodes from the given list of names * * Input: * name list of names to find * flags flags used if no node is found for a given name * * Results: * A complete list of graph nodes corresponding to all instances of all * the names in names. * * Side Effects: * If flags is TARG_CREATE, nodes will be created for all names in * names which do not yet have graph nodes. If flags is TARG_NOCREATE, * an error message will be printed for each name which can't be found. * ----------------------------------------------------------------------- */ Lst Targ_FindList(Lst names, int flags) { Lst nodes; /* result list */ LstNode ln; /* name list element */ GNode *gn; /* node in tLn */ char *name; nodes = Lst_Init(FALSE); if (Lst_Open(names) == FAILURE) { return (nodes); } while ((ln = Lst_Next(names)) != NULL) { name = (char *)Lst_Datum(ln); gn = Targ_FindNode(name, flags); if (gn != NULL) { /* * Note: Lst_AtEnd must come before the Lst_Concat so the nodes * are added to the list in the order in which they were * encountered in the makefile. */ (void)Lst_AtEnd(nodes, gn); } else if (flags == TARG_NOCREATE) { Error("\"%s\" -- target unknown.", name); } } Lst_Close(names); return (nodes); }
/*- *----------------------------------------------------------------------- * Targ_FindNode -- * Find a node in the list using the given name for matching * * Input: * name the name to find * flags flags governing events when target not * found * * Results: * The node in the list if it was. If it wasn't, return NULL of * flags was TARG_NOCREATE or the newly created and initialized node * if it was TARG_CREATE * * Side Effects: * Sometimes a node is created and added to the list *----------------------------------------------------------------------- */ GNode * Targ_FindNode(const char *name, int flags) { GNode *gn; /* node in that element */ Hash_Entry *he; /* New or used hash entry for node */ Boolean isNew; /* Set TRUE if Hash_CreateEntry had to create */ /* an entry for the node */ if (!(flags & (TARG_CREATE | TARG_NOHASH))) { he = Hash_FindEntry(&targets, name); if (he == NULL) return NULL; return (GNode *)Hash_GetValue(he); } if (!(flags & TARG_NOHASH)) { he = Hash_CreateEntry(&targets, name, &isNew); if (!isNew) return (GNode *)Hash_GetValue(he); } gn = Targ_NewGN(name); if (!(flags & TARG_NOHASH)) Hash_SetValue(he, gn); Var_Append(".ALLTARGETS", name, VAR_GLOBAL); (void)Lst_AtEnd(allTargets, gn); if (doing_depend) gn->flags |= FROM_DEPEND; return gn; }
/*- *--------------------------------------------------------------------- * ParseLinkSrc -- * Link the parent node to its new child. Used by * ParseDoDependency. If the specType isn't 'Not', the parent * isn't linked as a parent of the child. * * Side Effects: * New elements are added to the parents list of cgn and the * children list of cgn. the unmade field of pgn is updated * to reflect the additional child. *--------------------------------------------------------------------- */ static void ParseLinkSrc(GNode *pgn, GNode *cgn) { if (Lst_AddNew(&pgn->children, cgn)) { if (specType == SPECIAL_NONE) Lst_AtEnd(&cgn->parents, pgn); pgn->unmade++; } }
/* Add datum to the end of a list only if it wasn't there already. * Returns false if datum was already there. */ bool Lst_AddNew(Lst l, void *d) { if (Lst_Member(l, d) != NULL) return false; else { Lst_AtEnd(l, d); return true; } }
void expand_commands(GNode *gn) { LstNode ln; char *cmd; for (ln = Lst_First(&gn->commands); ln != NULL; ln = Lst_Adv(ln)) { cmd = Var_Subst(Lst_Datum(ln), &gn->context, false); Lst_AtEnd(&gn->expanded, cmd); } }
static void register_error(int reason, int code, Job *job) { struct error_info *p; errors++; p = emalloc(sizeof(struct error_info)); p->reason = reason; p->code = code; p->n = job->node; Lst_AtEnd(&errorsList, p); }
/*- *--------------------------------------------------------------------- * ParseDoOp -- * Apply the parsed operator to the given target node. Used in a * Array_Find call by ParseDoDependency once all targets have * been found and their operator parsed. If the previous and new * operators are incompatible, a major error is taken. * * Side Effects: * The type field of the node is altered to reflect any new bits in * the op. *--------------------------------------------------------------------- */ static int ParseDoOp(GNode **gnp, unsigned int op) { GNode *gn = *gnp; /* * If the dependency mask of the operator and the node don't match and * the node has actually had an operator applied to it before, and the * operator actually has some dependency information in it, complain. */ if (((op & OP_OPMASK) != (gn->type & OP_OPMASK)) && !OP_NOP(gn->type) && !OP_NOP(op)) { Parse_Error(PARSE_FATAL, "Inconsistent dependency operator for target %s\n" "\t(was %s%s, now %s%s)", gn->name, gn->name, operator_string(gn->type), gn->name, operator_string(op)); return 0; } if (op == OP_DOUBLEDEP && ((gn->type & OP_OPMASK) == OP_DOUBLEDEP)) { /* If the node was the object of a :: operator, we need to * create a new instance of it for the children and commands on * this dependency line. The new instance is placed on the * 'cohorts' list of the initial one (note the initial one is * not on its own cohorts list) and the new instance is linked * to all parents of the initial instance. */ GNode *cohort; LstNode ln; cohort = Targ_NewGN(gn->name); /* Duplicate links to parents so graph traversal is simple. * Perhaps some type bits should be duplicated? * * Make the cohort invisible as well to avoid duplicating it * into other variables. True, parents of this target won't * tend to do anything with their local variables, but better * safe than sorry. */ for (ln = Lst_First(&gn->parents); ln != NULL; ln = Lst_Adv(ln)) ParseLinkSrc((GNode *)Lst_Datum(ln), cohort); cohort->type = OP_DOUBLEDEP|OP_INVISIBLE; Lst_AtEnd(&gn->cohorts, cohort); /* Replace the node in the targets list with the new copy */ *gnp = cohort; gn = cohort; } /* We don't want to nuke any previous flags (whatever they were) so we * just OR the new operator into the old. */ gn->type |= op; return 1; }
int str2Lst_Append(Lst lp, char *str, const char *sep) { char *cp; int n; if (!sep) sep = " \t"; for (n = 0, cp = strtok(str, sep); cp; cp = strtok(NULL, sep)) { (void)Lst_AtEnd(lp, cp); n++; } return (n); }
static struct input_stream * new_input_file(const char *name, FILE *stream) { struct input_stream *istream; #if 0 Lst_AtEnd(&fileNames, name); #endif istream = emalloc(sizeof(*istream)); istream->fname = name; istream->str = NULL; /* Naturally enough, we start reading at line 0. */ istream->lineno = 0; istream->F = stream; istream->ptr = istream->end = NULL; return istream; }
void Make_HandleUse(GNode *cgn, /* The .USE node */ GNode *pgn) /* The target of the .USE node */ { GNode *gn; /* A child of the .USE node */ LstNode ln; /* An element in the children list */ assert(cgn->type & (OP_USE|OP_TRANSFORM)); if ((cgn->type & OP_USE) || Lst_IsEmpty(&pgn->commands)) { /* .USE or transformation and target has no commands * -- append the child's commands to the parent. */ Lst_Concat(&pgn->commands, &cgn->commands); } for (ln = Lst_First(&cgn->children); ln != NULL; ln = Lst_Adv(ln)) { gn = (GNode *)Lst_Datum(ln); if (Lst_AddNew(&pgn->children, gn)) { Lst_AtEnd(&gn->parents, pgn); pgn->unmade++; } } pgn->type |= cgn->type & ~(OP_OPMASK|OP_USE|OP_TRANSFORM); /* * This child node is now "made", so we decrement the count of * unmade children in the parent... We also remove the child * from the parent's list to accurately reflect the number of * decent children the parent has. This is used by Make_Run to * decide whether to queue the parent or examine its children... */ if (cgn->type & OP_USE) pgn->unmade--; /* if the parent node doesn't have any location, then inherit the * use stuff, since that gives us better error messages. */ if (!pgn->lineno) { pgn->lineno = cgn->lineno; pgn->fname = cgn->fname; } }
void Targ_FindList(Lst nodes, Lst names) { LstNode ln; GNode *gn; char *name; for (ln = Lst_First(names); ln != NULL; ln = Lst_Adv(ln)) { name = (char *)Lst_Datum(ln); gn = Targ_FindNode(name, TARG_CREATE); /* Note: Lst_AtEnd must come before the Lst_Concat so the nodes * are added to the list in the order in which they were * encountered in the makefile. */ Lst_AtEnd(nodes, gn); if (gn->type & OP_DOUBLEDEP) Lst_Concat(nodes, &gn->cohorts); } }
/*- *----------------------------------------------------------------------- * Targ_NewGN -- * Create and initialize a new graph node * * Input: * name the name to stick in the new node * * Results: * An initialized graph node with the name field filled with a copy * of the passed name * * Side Effects: * The gnode is added to the list of all gnodes. *----------------------------------------------------------------------- */ GNode * Targ_NewGN(const char *name) { GNode *gn; gn = bmake_malloc(sizeof(GNode)); gn->name = bmake_strdup(name); gn->uname = NULL; gn->path = NULL; if (name[0] == '-' && name[1] == 'l') { gn->type = OP_LIB; } else { gn->type = 0; } gn->unmade = 0; gn->unmade_cohorts = 0; gn->cohort_num[0] = 0; gn->centurion = NULL; gn->made = UNMADE; gn->flags = 0; gn->checked = 0; gn->mtime = 0; gn->cmgn = NULL; gn->iParents = Lst_Init(FALSE); gn->cohorts = Lst_Init(FALSE); gn->parents = Lst_Init(FALSE); gn->children = Lst_Init(FALSE); gn->order_pred = Lst_Init(FALSE); gn->order_succ = Lst_Init(FALSE); Hash_InitTable(&gn->context, 0); gn->commands = Lst_Init(FALSE); gn->suffix = NULL; gn->lineno = 0; gn->fname = NULL; #ifdef CLEANUP if (allGNs == NULL) allGNs = Lst_Init(FALSE); Lst_AtEnd(allGNs, gn); #endif return (gn); }
/*- *----------------------------------------------------------------------- * CompatRunCommand -- * Execute the next command for a target. If the command returns an * error, the node's made field is set to ERROR and creation stops. * * Input: * cmdp Command to execute * gnp Node from which the command came * * Results: * 0 if the command succeeded, 1 if an error occurred. * * Side Effects: * The node's 'made' field may be set to ERROR. * *----------------------------------------------------------------------- */ int CompatRunCommand(void *cmdp, void *gnp) { char *cmdStart; /* Start of expanded command */ char *cp, *bp; Boolean silent, /* Don't print command */ doIt; /* Execute even if -n */ volatile Boolean errCheck; /* Check errors */ WAIT_T reason; /* Reason for child's death */ int status; /* Description of child's death */ pid_t cpid; /* Child actually found */ pid_t retstat; /* Result of wait */ LstNode cmdNode; /* Node where current command is located */ const char ** volatile av; /* Argument vector for thing to exec */ char ** volatile mav;/* Copy of the argument vector for freeing */ int argc; /* Number of arguments in av or 0 if not * dynamically allocated */ Boolean local; /* TRUE if command should be executed * locally */ Boolean useShell; /* TRUE if command should be executed * using a shell */ char * volatile cmd = (char *)cmdp; GNode *gn = (GNode *)gnp; silent = gn->type & OP_SILENT; errCheck = !(gn->type & OP_IGNORE); doIt = FALSE; cmdNode = Lst_Member(gn->commands, cmd); cmdStart = Var_Subst(NULL, cmd, gn, FALSE); /* * brk_string will return an argv with a NULL in av[0], thus causing * execvp to choke and die horribly. Besides, how can we execute a null * command? In any case, we warn the user that the command expanded to * nothing (is this the right thing to do?). */ if (*cmdStart == '\0') { free(cmdStart); Error("%s expands to empty string", cmd); return(0); } cmd = cmdStart; Lst_Replace(cmdNode, cmdStart); if ((gn->type & OP_SAVE_CMDS) && (gn != ENDNode)) { (void)Lst_AtEnd(ENDNode->commands, cmdStart); return(0); } if (strcmp(cmdStart, "...") == 0) { gn->type |= OP_SAVE_CMDS; return(0); } while ((*cmd == '@') || (*cmd == '-') || (*cmd == '+')) { switch (*cmd) { case '@': silent = DEBUG(LOUD) ? FALSE : TRUE; break; case '-': errCheck = FALSE; break; case '+': doIt = TRUE; if (!meta[0]) /* we came here from jobs */ Compat_Init(); break; } cmd++; } while (isspace((unsigned char)*cmd)) cmd++; #if !defined(MAKE_NATIVE) /* * In a non-native build, the host environment might be weird enough * that it's necessary to go through a shell to get the correct * behaviour. Or perhaps the shell has been replaced with something * that does extra logging, and that should not be bypassed. */ useShell = TRUE; #else /* * Search for meta characters in the command. If there are no meta * characters, there's no need to execute a shell to execute the * command. */ for (cp = cmd; !meta[(unsigned char)*cp]; cp++) { continue; } useShell = (*cp != '\0'); #endif /* * Print the command before echoing if we're not supposed to be quiet for * this one. We also print the command if -n given. */ if (!silent || NoExecute(gn)) { printf("%s\n", cmd); fflush(stdout); } /* * If we're not supposed to execute any commands, this is as far as * we go... */ if (!doIt && NoExecute(gn)) { return (0); } if (DEBUG(JOB)) fprintf(debug_file, "Execute: '%s'\n", cmd); again: if (useShell) { /* * We need to pass the command off to the shell, typically * because the command contains a "meta" character. */ static const char *shargv[4]; shargv[0] = shellPath; /* * The following work for any of the builtin shell specs. */ if (DEBUG(SHELL)) shargv[1] = "-xc"; else shargv[1] = "-c"; shargv[2] = cmd; shargv[3] = NULL; av = shargv; argc = 0; bp = NULL; mav = NULL; } else { /* * No meta-characters, so no need to exec a shell. Break the command * into words to form an argument vector we can execute. */ mav = brk_string(cmd, &argc, TRUE, &bp); if (mav == NULL) { useShell = 1; goto again; } av = (const char **)mav; } local = TRUE; #ifdef USE_META if (useMeta) { meta_compat_start(); } #endif /* * Fork and execute the single command. If the fork fails, we abort. */ cpid = vFork(); if (cpid < 0) { Fatal("Could not fork"); } if (cpid == 0) { Check_Cwd(av); Var_ExportVars(); #ifdef USE_META if (useMeta) { meta_compat_child(); } #endif if (local) (void)execvp(av[0], (char *const *)UNCONST(av)); else (void)execv(av[0], (char *const *)UNCONST(av)); execError("exec", av[0]); _exit(1); } if (mav) free(mav); if (bp) free(bp); Lst_Replace(cmdNode, NULL); #ifdef USE_META if (useMeta) { meta_compat_parent(); } #endif /* * The child is off and running. Now all we can do is wait... */ while (1) { while ((retstat = wait(&reason)) != cpid) { if (retstat > 0) JobReapChild(retstat, reason, FALSE); /* not ours? */ if (retstat == -1 && errno != EINTR) { break; } } if (retstat > -1) { if (WIFSTOPPED(reason)) { status = WSTOPSIG(reason); /* stopped */ } else if (WIFEXITED(reason)) { status = WEXITSTATUS(reason); /* exited */ #if defined(USE_META) && defined(USE_FILEMON_ONCE) if (useMeta) { meta_cmd_finish(NULL); } #endif if (status != 0) { if (DEBUG(ERROR)) { fprintf(debug_file, "\n*** Failed target: %s\n*** Failed command: ", gn->name); for (cp = cmd; *cp; ) { if (isspace((unsigned char)*cp)) { fprintf(debug_file, " "); while (isspace((unsigned char)*cp)) cp++; } else { fprintf(debug_file, "%c", *cp); cp++; } } fprintf(debug_file, "\n"); } printf("*** Error code %d", status); } } else { status = WTERMSIG(reason); /* signaled */ printf("*** Signal %d", status); } if (!WIFEXITED(reason) || (status != 0)) { if (errCheck) { #ifdef USE_META if (useMeta) { meta_job_error(NULL, gn, 0, status); } #endif gn->made = ERROR; if (keepgoing) { /* * Abort the current target, but let others * continue. */ printf(" (continuing)\n"); } } else { /* * Continue executing commands for this target. * If we return 0, this will happen... */ printf(" (ignored)\n"); status = 0; } } break; } else { Fatal("error in wait: %d: %s", retstat, strerror(errno)); /*NOTREACHED*/ } } free(cmdStart); return (status); }
/*- * MainParseArgs -- * Parse a given argument vector. Called from main() and from * Main_ParseArgLine() when the .MAKEFLAGS target is used. * * XXX: Deal with command line overriding .MAKEFLAGS in makefile * * Results: * None * * Side Effects: * Various global and local flags will be set depending on the flags * given */ static void MainParseArgs(int argc, char **argv) { char *p; int c = '?'; int arginc; char *argvalue; const char *getopt_def; char *optscan; Boolean inOption, dashDash = FALSE; char found_path[MAXPATHLEN + 1]; /* for searching for sys.mk */ #define OPTFLAGS "BC:D:I:J:NST:V:WXd:ef:ij:km:nqrstw" /* Can't actually use getopt(3) because rescanning is not portable */ getopt_def = OPTFLAGS; rearg: inOption = FALSE; optscan = NULL; while(argc > 1) { char *getopt_spec; if(!inOption) optscan = argv[1]; c = *optscan++; arginc = 0; if(inOption) { if(c == '\0') { ++argv; --argc; inOption = FALSE; continue; } } else { if (c != '-' || dashDash) break; inOption = TRUE; c = *optscan++; } /* '-' found at some earlier point */ getopt_spec = strchr(getopt_def, c); if(c != '\0' && getopt_spec != NULL && getopt_spec[1] == ':') { /* -<something> found, and <something> should have an arg */ inOption = FALSE; arginc = 1; argvalue = optscan; if(*argvalue == '\0') { if (argc < 3) goto noarg; argvalue = argv[2]; arginc = 2; } } else { argvalue = NULL; } switch(c) { case '\0': arginc = 1; inOption = FALSE; break; case 'B': compatMake = TRUE; Var_Append(MAKEFLAGS, "-B", VAR_GLOBAL); Var_Set(MAKE_MODE, "compat", VAR_GLOBAL, 0); break; case 'C': if (chdir(argvalue) == -1) { (void)fprintf(stderr, "%s: chdir %s: %s\n", progname, argvalue, strerror(errno)); exit(1); } if (getcwd(curdir, MAXPATHLEN) == NULL) { (void)fprintf(stderr, "%s: %s.\n", progname, strerror(errno)); exit(2); } ignorePWD = TRUE; break; case 'D': if (argvalue == NULL || argvalue[0] == 0) goto noarg; Var_Set(argvalue, "1", VAR_GLOBAL, 0); Var_Append(MAKEFLAGS, "-D", VAR_GLOBAL); Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL); break; case 'I': if (argvalue == NULL) goto noarg; Parse_AddIncludeDir(argvalue); Var_Append(MAKEFLAGS, "-I", VAR_GLOBAL); Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL); break; case 'J': if (argvalue == NULL) goto noarg; if (sscanf(argvalue, "%d,%d", &jp_0, &jp_1) != 2) { (void)fprintf(stderr, "%s: internal error -- J option malformed (%s)\n", progname, argvalue); usage(); } if ((fcntl(jp_0, F_GETFD, 0) < 0) || (fcntl(jp_1, F_GETFD, 0) < 0)) { #if 0 (void)fprintf(stderr, "%s: ###### warning -- J descriptors were closed!\n", progname); exit(2); #endif jp_0 = -1; jp_1 = -1; compatMake = TRUE; } else { Var_Append(MAKEFLAGS, "-J", VAR_GLOBAL); Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL); jobServer = TRUE; } break; case 'N': noExecute = TRUE; noRecursiveExecute = TRUE; Var_Append(MAKEFLAGS, "-N", VAR_GLOBAL); break; case 'S': keepgoing = FALSE; Var_Append(MAKEFLAGS, "-S", VAR_GLOBAL); break; case 'T': if (argvalue == NULL) goto noarg; tracefile = bmake_strdup(argvalue); Var_Append(MAKEFLAGS, "-T", VAR_GLOBAL); Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL); break; case 'V': if (argvalue == NULL) goto noarg; printVars = TRUE; (void)Lst_AtEnd(variables, argvalue); Var_Append(MAKEFLAGS, "-V", VAR_GLOBAL); Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL); break; case 'W': parseWarnFatal = TRUE; break; case 'X': varNoExportEnv = TRUE; Var_Append(MAKEFLAGS, "-X", VAR_GLOBAL); break; case 'd': if (argvalue == NULL) goto noarg; /* If '-d-opts' don't pass to children */ if (argvalue[0] == '-') argvalue++; else { Var_Append(MAKEFLAGS, "-d", VAR_GLOBAL); Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL); } parse_debug_options(argvalue); break; case 'e': checkEnvFirst = TRUE; Var_Append(MAKEFLAGS, "-e", VAR_GLOBAL); break; case 'f': if (argvalue == NULL) goto noarg; (void)Lst_AtEnd(makefiles, argvalue); break; case 'i': ignoreErrors = TRUE; Var_Append(MAKEFLAGS, "-i", VAR_GLOBAL); break; case 'j': if (argvalue == NULL) goto noarg; forceJobs = TRUE; maxJobs = strtol(argvalue, &p, 0); if (*p != '\0' || maxJobs < 1) { (void)fprintf(stderr, "%s: illegal argument to -j -- must be positive integer!\n", progname); exit(1); } Var_Append(MAKEFLAGS, "-j", VAR_GLOBAL); Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL); Var_Set(".MAKE.JOBS", argvalue, VAR_GLOBAL, 0); maxJobTokens = maxJobs; break; case 'k': keepgoing = TRUE; Var_Append(MAKEFLAGS, "-k", VAR_GLOBAL); break; case 'm': if (argvalue == NULL) goto noarg; /* look for magic parent directory search string */ if (strncmp(".../", argvalue, 4) == 0) { if (!Dir_FindHereOrAbove(curdir, argvalue+4, found_path, sizeof(found_path))) break; /* nothing doing */ (void)Dir_AddDir(sysIncPath, found_path); } else { (void)Dir_AddDir(sysIncPath, argvalue); } Var_Append(MAKEFLAGS, "-m", VAR_GLOBAL); Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL); break; case 'n': noExecute = TRUE; Var_Append(MAKEFLAGS, "-n", VAR_GLOBAL); break; case 'q': queryFlag = TRUE; /* Kind of nonsensical, wot? */ Var_Append(MAKEFLAGS, "-q", VAR_GLOBAL); break; case 'r': noBuiltins = TRUE; Var_Append(MAKEFLAGS, "-r", VAR_GLOBAL); break; case 's': beSilent = TRUE; Var_Append(MAKEFLAGS, "-s", VAR_GLOBAL); break; case 't': touchFlag = TRUE; Var_Append(MAKEFLAGS, "-t", VAR_GLOBAL); break; case 'w': enterFlag = TRUE; Var_Append(MAKEFLAGS, "-w", VAR_GLOBAL); break; case '-': dashDash = TRUE; break; default: case '?': #ifndef MAKE_NATIVE fprintf(stderr, "getopt(%s) -> %d (%c)\n", OPTFLAGS, c, c); #endif usage(); } argv += arginc; argc -= arginc; } oldVars = TRUE; /* * See if the rest of the arguments are variable assignments and * perform them if so. Else take them to be targets and stuff them * on the end of the "create" list. */ for (; argc > 1; ++argv, --argc) if (Parse_IsVar(argv[1])) { Parse_DoVar(argv[1], VAR_CMD); } else { if (!*argv[1]) Punt("illegal (null) argument."); if (*argv[1] == '-' && !dashDash) goto rearg; (void)Lst_AtEnd(create, bmake_strdup(argv[1])); } return; noarg: (void)fprintf(stderr, "%s: option requires an argument -- %c\n", progname, c); usage(); }
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; }
/*- *----------------------------------------------------------------------- * 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); }
For * For_Eval(const char *line) { const char *ptr = line; const char *wrd; char *sub; const char *endVar; For *arg; unsigned long n; while (ISSPACE(*ptr)) ptr++; /* Parse loop. */ arg = emalloc(sizeof(*arg)); arg->nvars = 0; Lst_Init(&arg->vars); for (;;) { /* Grab the variables. */ for (wrd = ptr; *ptr && !ISSPACE(*ptr); ptr++) continue; if (ptr - wrd == 0) { Parse_Error(PARSE_FATAL, "Syntax error in for"); return 0; } endVar = ptr++; while (ISSPACE(*ptr)) ptr++; /* End of variable list ? */ if (endVar - wrd == 2 && wrd[0] == 'i' && wrd[1] == 'n') break; Lst_AtEnd(&arg->vars, Var_NewLoopVar(wrd, endVar)); arg->nvars++; } if (arg->nvars == 0) { Parse_Error(PARSE_FATAL, "Missing variable in for"); return 0; } /* Make a list with the remaining words. */ sub = Var_Subst(ptr, NULL, false); if (DEBUG(FOR)) { LstNode ln; (void)fprintf(stderr, "For: Iterator "); for (ln = Lst_First(&arg->vars); ln != NULL; ln = Lst_Adv(ln)) (void)fprintf(stderr, "%s ", Var_LoopVarName(Lst_Datum(ln))); (void)fprintf(stderr, "List %s\n", sub); } Lst_Init(&arg->lst); n = build_words_list(&arg->lst, sub); free(sub); if (arg->nvars != 1 && n % arg->nvars != 0) { LstNode ln; Parse_Error(PARSE_FATAL, "Wrong number of items in for loop"); (void)fprintf(stderr, "%lu items for %d variables:", n, arg->nvars); for (ln = Lst_First(&arg->lst); ln != NULL; ln = Lst_Adv(ln)) { char *p = Lst_Datum(ln); (void)fprintf(stderr, " %s", p); } (void)fprintf(stderr, "\n"); return 0; } arg->lineno = Parse_Getlineno(); arg->level = 1; Buf_Init(&arg->buf, 0); return arg; }
/*- * MainParseArgs -- * Parse a given argument vector. Called from main() and from * Main_ParseArgLine() when the MAKEFLAGS target is used. * * XXX: Deal with command line overriding MAKEFLAGS in makefile * * Results: * None * * Side Effects: * Various global and local flags will be set depending on the flags * given */ static void MainParseArgs(int argc, char **argv) { int c; string_t argstr; optind = 1; /* since we're called more than once */ rearg: while((c = getopt(argc, argv, "D:I:L:NPSd:ef:ij:knqrstv")) != EOF) { switch(c) { case 'D': argstr = string_create(optarg); Var_Set(argstr, string_create("1"), VAR_GLOBAL); Var_Append(sMAKEFLAGS, string_create("-D"), VAR_GLOBAL); Var_Append(sMAKEFLAGS, argstr, VAR_GLOBAL); string_deref(argstr); break; case 'I': argstr = string_create(optarg); Parse_AddIncludeDir(argstr); Var_Append(sMAKEFLAGS, string_create("-I"), VAR_GLOBAL); Var_Append(sMAKEFLAGS, argstr, VAR_GLOBAL); string_deref(argstr); break; case 'L': maxLocal = atoi(optarg); Var_Append(sMAKEFLAGS, string_create("-L"), VAR_GLOBAL); argstr = string_create(optarg); Var_Append(sMAKEFLAGS, argstr, VAR_GLOBAL); string_deref(argstr); break; case 'N': noisy = TRUE; Var_Append(sMAKEFLAGS, string_create("-N"), VAR_GLOBAL); break; case 'S': keepgoing = FALSE; Var_Append(sMAKEFLAGS, string_create("-S"), VAR_GLOBAL); break; case 'd': { char *modules = optarg; for (; *modules; ++modules) switch (*modules) { case 'A': debug = ~0; break; case 'a': debug |= DEBUG_ARCH; break; case 'c': debug |= DEBUG_COND; break; case 'd': debug |= DEBUG_DIR; break; case 'g': if (modules[1] == '1') { debug |= DEBUG_GRAPH1; ++modules; } else if (modules[1] == '2') { debug |= DEBUG_GRAPH2; ++modules; } break; case 'j': debug |= DEBUG_JOB; break; case 'm': debug |= DEBUG_MAKE; break; case 's': debug |= DEBUG_SUFF; break; case 't': debug |= DEBUG_TARG; break; case 'v': debug |= DEBUG_VAR; break; } Var_Append(sMAKEFLAGS, string_create("-d"), VAR_GLOBAL); argstr = string_create(optarg); Var_Append(sMAKEFLAGS, argstr, VAR_GLOBAL); string_deref(argstr); break; } case 'e': checkEnvFirst = TRUE; Var_Append(sMAKEFLAGS, string_create("-e"), VAR_GLOBAL); break; case 'f': (void)Lst_AtEnd(makefiles, (ClientData)optarg); break; case 'i': ignoreErrors = TRUE; Var_Append(sMAKEFLAGS, string_create("-i"), VAR_GLOBAL); break; case 'j': maxJobs = atoi(optarg); Var_Append(sMAKEFLAGS, string_create("-j"), VAR_GLOBAL); argstr = string_create(optarg); Var_Append(sMAKEFLAGS, argstr, VAR_GLOBAL); string_deref(argstr); break; case 'k': keepgoing = TRUE; Var_Append(sMAKEFLAGS, string_create("-k"), VAR_GLOBAL); break; case 'n': noExecute = TRUE; Var_Append(sMAKEFLAGS, string_create("-n"), VAR_GLOBAL); break; case 'q': queryFlag = TRUE; /* Kind of nonsensical, wot? */ Var_Append(sMAKEFLAGS, string_create("-q"), VAR_GLOBAL); break; case 'r': noBuiltins = TRUE; Var_Append(sMAKEFLAGS, string_create("-r"), VAR_GLOBAL); break; case 's': beSilent = TRUE; Var_Append(sMAKEFLAGS, string_create("-s"), VAR_GLOBAL); break; case 't': touchFlag = TRUE; Var_Append(sMAKEFLAGS, string_create("-t"), VAR_GLOBAL); break; case 'v': ui_print_revision(); exit(2); default: case '?': usage(); } } oldVars = TRUE; /* * See if the rest of the arguments are variable assignments and * perform them if so. Else take them to be targets and stuff them * on the end of the "create" list. */ for (argv += optind, argc -= optind; *argv; ++argv, --argc) if (Parse_DoVar(*argv, VAR_CMD) != 0) { if (!**argv) Punt("illegal (null) argument."); if (**argv == '-') { optind = 0; goto rearg; } (void)Lst_AtEnd(create, (ClientData)string_create(*argv)); } }
/*- *--------------------------------------------------------------------- * ParseDoSrc -- * Given the name of a source, figure out if it is an attribute * and apply it to the targets if it is. Else decide if there is * some attribute which should be applied *to* the source because * of some special target and apply it if so. Otherwise, make the * source be a child of the targets in the list 'targets' * * Side Effects: * Operator bits may be added to the list of targets or to the source. * The targets may have a new source added to their lists of children. *--------------------------------------------------------------------- */ static void ParseDoSrc( struct growableArray *targets, struct growableArray *sources, int tOp, /* operator (if any) from special targets */ const char *src, /* name of the source to handle */ const char *esrc) { GNode *gn = Targ_FindNodei(src, esrc, TARG_CREATE); if ((gn->special & SPECIAL_SOURCE) != 0) { if (gn->special_op) { Array_FindP(targets, ParseDoOp, gn->special_op); return; } else { assert((gn->special & SPECIAL_MASK) == SPECIAL_WAIT); waiting++; return; } } switch (specType) { case SPECIAL_MAIN: /* * If we have noted the existence of a .MAIN, it means we need * to add the sources of said target to the list of things * to create. Note that this will only be invoked if the user * didn't specify a target on the command line. This is to * allow #ifmake's to succeed, or something... */ Lst_AtEnd(create, gn->name); /* * Add the name to the .TARGETS variable as well, so the user * can employ that, if desired. */ Var_Append(".TARGETS", gn->name); return; case SPECIAL_ORDER: /* * Create proper predecessor/successor links between the * previous source and the current one. */ if (predecessor != NULL) { Lst_AtEnd(&predecessor->successors, gn); Lst_AtEnd(&gn->preds, predecessor); } predecessor = gn; break; default: /* * In the case of a source that was the object of a :: operator, * the attribute is applied to all of its instances (as kept in * the 'cohorts' list of the node) or all the cohorts are linked * to all the targets. */ apply_op(targets, tOp, gn); if ((gn->type & OP_OPMASK) == OP_DOUBLEDEP) { LstNode ln; for (ln=Lst_First(&gn->cohorts); ln != NULL; ln = Lst_Adv(ln)){ apply_op(targets, tOp, (GNode *)Lst_Datum(ln)); } } break; } gn->order = waiting; Array_AtEnd(sources, gn); if (waiting) Array_Find(sources, ParseAddDep, gn); }
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; }
/*- *----------------------------------------------------------------------- * ArchStatMember -- * Locate a member of an archive, given the path of the archive and * the path of the desired member. * * Input: * archive Path to the archive * member Name of member. If it is a path, only the last * component is used. * hash TRUE if archive should be hashed if not already so. * * Results: * A pointer to the current struct ar_hdr structure for the member. Note * That no position is returned, so this is not useful for touching * archive members. This is mostly because we have no assurances that * The archive will remain constant after we read all the headers, so * there's not much point in remembering the position... * * Side Effects: * *----------------------------------------------------------------------- */ static struct ar_hdr * ArchStatMember(char *archive, char *member, Boolean hash) { FILE * arch; /* Stream to archive */ int size; /* Size of archive member */ char *cp; /* Useful character pointer */ char magic[SARMAG]; LstNode ln; /* Lst member containing archive descriptor */ Arch *ar; /* Archive descriptor */ Hash_Entry *he; /* Entry containing member's description */ struct ar_hdr arh; /* archive-member header for reading archive */ char memName[MAXPATHLEN+1]; /* Current member name while hashing. */ /* * Because of space constraints and similar things, files are archived * using their final path components, not the entire thing, so we need * to point 'member' to the final component, if there is one, to make * the comparisons easier... */ cp = strrchr(member, '/'); if (cp != NULL) { member = cp + 1; } ln = Lst_Find(archives, archive, ArchFindArchive); if (ln != NULL) { ar = (Arch *)Lst_Datum(ln); he = Hash_FindEntry(&ar->members, member); if (he != NULL) { return ((struct ar_hdr *)Hash_GetValue(he)); } else { /* Try truncated name */ char copy[AR_MAX_NAME_LEN+1]; size_t len = strlen(member); if (len > AR_MAX_NAME_LEN) { len = AR_MAX_NAME_LEN; strncpy(copy, member, AR_MAX_NAME_LEN); copy[AR_MAX_NAME_LEN] = '\0'; } if ((he = Hash_FindEntry(&ar->members, copy)) != NULL) return ((struct ar_hdr *)Hash_GetValue(he)); return NULL; } } if (!hash) { /* * Caller doesn't want the thing hashed, just use ArchFindMember * to read the header for the member out and close down the stream * again. Since the archive is not to be hashed, we assume there's * no need to allocate extra room for the header we're returning, * so just declare it static. */ static struct ar_hdr sarh; arch = ArchFindMember(archive, member, &sarh, "r"); if (arch == NULL) { return NULL; } else { fclose(arch); return (&sarh); } } /* * We don't have this archive on the list yet, so we want to find out * everything that's in it and cache it so we can get at it quickly. */ arch = fopen(archive, "r"); if (arch == NULL) { return NULL; } /* * We use the ARMAG string to make sure this is an archive we * can handle... */ if ((fread(magic, SARMAG, 1, arch) != 1) || (strncmp(magic, ARMAG, SARMAG) != 0)) { fclose(arch); return NULL; } ar = bmake_malloc(sizeof(Arch)); ar->name = bmake_strdup(archive); ar->fnametab = NULL; ar->fnamesize = 0; Hash_InitTable(&ar->members, -1); memName[AR_MAX_NAME_LEN] = '\0'; while (fread((char *)&arh, sizeof(struct ar_hdr), 1, arch) == 1) { if (strncmp( arh.AR_FMAG, ARFMAG, sizeof(arh.AR_FMAG)) != 0) { /* * The header is bogus, so the archive is bad * and there's no way we can recover... */ goto badarch; } else { /* * We need to advance the stream's pointer to the start of the * next header. Files are padded with newlines to an even-byte * boundary, so we need to extract the size of the file from the * 'size' field of the header and round it up during the seek. */ arh.AR_SIZE[sizeof(arh.AR_SIZE)-1] = '\0'; size = (int)strtol(arh.AR_SIZE, NULL, 10); (void)strncpy(memName, arh.AR_NAME, sizeof(arh.AR_NAME)); for (cp = &memName[AR_MAX_NAME_LEN]; *cp == ' '; cp--) { continue; } cp[1] = '\0'; #ifdef SVR4ARCHIVES /* * svr4 names are slash terminated. Also svr4 extended AR format. */ if (memName[0] == '/') { /* * svr4 magic mode; handle it */ switch (ArchSVR4Entry(ar, memName, size, arch)) { case -1: /* Invalid data */ goto badarch; case 0: /* List of files entry */ continue; default: /* Got the entry */ break; } } else { if (cp[0] == '/') cp[0] = '\0'; } #endif #ifdef AR_EFMT1 /* * BSD 4.4 extended AR format: #1/<namelen>, with name as the * first <namelen> bytes of the file */ if (strncmp(memName, AR_EFMT1, sizeof(AR_EFMT1) - 1) == 0 && isdigit((unsigned char)memName[sizeof(AR_EFMT1) - 1])) { unsigned int elen = atoi(&memName[sizeof(AR_EFMT1)-1]); if (elen > MAXPATHLEN) goto badarch; if (fread(memName, elen, 1, arch) != 1) goto badarch; memName[elen] = '\0'; if (fseek(arch, -elen, SEEK_CUR) != 0) goto badarch; if (DEBUG(ARCH) || DEBUG(MAKE)) { fprintf(debug_file, "ArchStat: Extended format entry for %s\n", memName); } } #endif he = Hash_CreateEntry(&ar->members, memName, NULL); Hash_SetValue(he, bmake_malloc(sizeof(struct ar_hdr))); memcpy(Hash_GetValue(he), &arh, sizeof(struct ar_hdr)); } if (fseek(arch, (size + 1) & ~1, SEEK_CUR) != 0) goto badarch; } fclose(arch); (void)Lst_AtEnd(archives, ar); /* * Now that the archive has been read and cached, we can look into * the hash table to find the desired member's header. */ he = Hash_FindEntry(&ar->members, member); if (he != NULL) { return ((struct ar_hdr *)Hash_GetValue(he)); } else { return NULL; } badarch: fclose(arch); Hash_DeleteTable(&ar->members); free(ar->fnametab); free(ar); return NULL; }
/*- *----------------------------------------------------------------------- * 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); }
/*- * MainParseArgs -- * Parse a given argument vector. Called from main() and from * Main_ParseArgLine() when the .MAKEFLAGS target is used. * * XXX: Deal with command line overriding .MAKEFLAGS in makefile * * Results: * None * * Side Effects: * Various global and local flags will be set depending on the flags * given */ static void MainParseArgs(int argc, char **argv) { char *p; int c; int arginc; char *argvalue; const char *getopt_def; char *optscan; Boolean inOption, dashDash = FALSE; char found_path[MAXPATHLEN + 1]; /* for searching for sys.mk */ #ifdef REMOTE # define OPTFLAGS "BD:I:J:L:NPST:V:WXd:ef:ij:km:nqrst" #else # define OPTFLAGS "BD:I:J:NPST:V:WXd:ef:ij:km:nqrst" #endif #undef optarg #define optarg argvalue /* Can't actually use getopt(3) because rescanning is not portable */ getopt_def = OPTFLAGS; rearg: inOption = FALSE; optscan = NULL; while(argc > 1) { char *getopt_spec; if(!inOption) optscan = argv[1]; c = *optscan++; arginc = 0; if(inOption) { if(c == '\0') { ++argv; --argc; inOption = FALSE; continue; } } else { if (c != '-' || dashDash) break; inOption = TRUE; c = *optscan++; } /* '-' found at some earlier point */ getopt_spec = strchr(getopt_def, c); if(c != '\0' && getopt_spec != NULL && getopt_spec[1] == ':') { /* -<something> found, and <something> should have an arg */ inOption = FALSE; arginc = 1; argvalue = optscan; if(*argvalue == '\0') { if (argc < 3) { (void)fprintf(stderr, "%s: option requires " "an argument -- %c\n", progname, c); usage(); } argvalue = argv[2]; arginc = 2; } } else { argvalue = NULL; } switch(c) { case '\0': arginc = 1; inOption = FALSE; break; case 'B': compatMake = TRUE; Var_Append(MAKEFLAGS, "-B", VAR_GLOBAL); break; case 'D': Var_Set(optarg, "1", VAR_GLOBAL, 0); Var_Append(MAKEFLAGS, "-D", VAR_GLOBAL); Var_Append(MAKEFLAGS, optarg, VAR_GLOBAL); break; case 'I': Parse_AddIncludeDir(optarg); Var_Append(MAKEFLAGS, "-I", VAR_GLOBAL); Var_Append(MAKEFLAGS, optarg, VAR_GLOBAL); break; case 'J': if (sscanf(optarg, "%d,%d", &job_pipe[0], &job_pipe[1]) != 2) { /* backslash to avoid trigraph ??) */ (void)fprintf(stderr, "%s: internal error -- J option malformed (%s?\?)\n", progname, optarg); usage(); } if ((fcntl(job_pipe[0], F_GETFD, 0) < 0) || (fcntl(job_pipe[1], F_GETFD, 0) < 0)) { #if 0 (void)fprintf(stderr, "%s: warning -- J descriptors were closed!\n", progname); #endif job_pipe[0] = -1; job_pipe[1] = -1; compatMake = TRUE; } else { Var_Append(MAKEFLAGS, "-J", VAR_GLOBAL); Var_Append(MAKEFLAGS, optarg, VAR_GLOBAL); jobServer = TRUE; } break; #ifdef REMOTE case 'L': maxLocal = strtol(optarg, &p, 0); if (*p != '\0' || maxLocal < 1) { (void)fprintf(stderr, "%s: illegal argument to -L -- must be positive integer!\n", progname); exit(1); } Var_Append(MAKEFLAGS, "-L", VAR_GLOBAL); Var_Append(MAKEFLAGS, optarg, VAR_GLOBAL); break; #endif case 'N': noExecute = TRUE; noRecursiveExecute = TRUE; Var_Append(MAKEFLAGS, "-N", VAR_GLOBAL); break; case 'P': usePipes = FALSE; Var_Append(MAKEFLAGS, "-P", VAR_GLOBAL); break; case 'S': keepgoing = FALSE; Var_Append(MAKEFLAGS, "-S", VAR_GLOBAL); break; case 'T': tracefile = estrdup(optarg); Var_Append(MAKEFLAGS, "-T", VAR_GLOBAL); Var_Append(MAKEFLAGS, optarg, VAR_GLOBAL); break; case 'V': printVars = TRUE; (void)Lst_AtEnd(variables, (ClientData)optarg); Var_Append(MAKEFLAGS, "-V", VAR_GLOBAL); Var_Append(MAKEFLAGS, optarg, VAR_GLOBAL); break; case 'W': parseWarnFatal = TRUE; break; case 'X': varNoExportEnv = TRUE; Var_Append(MAKEFLAGS, "-X", VAR_GLOBAL); break; case 'd': { char *modules = optarg; for (; *modules; ++modules) switch (*modules) { case 'A': debug = ~0; break; case 'a': debug |= DEBUG_ARCH; break; case 'c': debug |= DEBUG_COND; break; case 'd': debug |= DEBUG_DIR; break; case 'e': debug |= DEBUG_ERROR; break; case 'f': debug |= DEBUG_FOR; break; case 'g': if (modules[1] == '1') { debug |= DEBUG_GRAPH1; ++modules; } else if (modules[1] == '2') { debug |= DEBUG_GRAPH2; ++modules; } else if (modules[1] == '3') { debug |= DEBUG_GRAPH3; ++modules; } break; case 'j': debug |= DEBUG_JOB; break; case 'm': debug |= DEBUG_MAKE; break; case 'n': debug |= DEBUG_SCRIPT; break; case 's': debug |= DEBUG_SUFF; break; case 't': debug |= DEBUG_TARG; break; case 'v': debug |= DEBUG_VAR; break; case 'x': debug |= DEBUG_SHELL; break; default: (void)fprintf(stderr, "%s: illegal argument to d option -- %c\n", progname, *modules); usage(); } Var_Append(MAKEFLAGS, "-d", VAR_GLOBAL); Var_Append(MAKEFLAGS, optarg, VAR_GLOBAL); break; } case 'e': checkEnvFirst = TRUE; Var_Append(MAKEFLAGS, "-e", VAR_GLOBAL); break; case 'f': (void)Lst_AtEnd(makefiles, (ClientData)optarg); break; case 'i': ignoreErrors = TRUE; Var_Append(MAKEFLAGS, "-i", VAR_GLOBAL); break; case 'j': forceJobs = TRUE; maxJobs = strtol(optarg, &p, 0); if (*p != '\0' || maxJobs < 1) { (void)fprintf(stderr, "%s: illegal argument to -j -- must be positive integer!\n", progname); exit(1); } #ifndef REMOTE maxLocal = maxJobs; #endif Var_Append(MAKEFLAGS, "-j", VAR_GLOBAL); Var_Append(MAKEFLAGS, optarg, VAR_GLOBAL); break; case 'k': keepgoing = TRUE; Var_Append(MAKEFLAGS, "-k", VAR_GLOBAL); break; case 'm': /* look for magic parent directory search string */ if (strncmp(".../", optarg, 4) == 0) { if (!Dir_FindHereOrAbove(curdir, optarg+4, found_path, sizeof(found_path))) break; /* nothing doing */ (void)Dir_AddDir(sysIncPath, found_path); } else { (void)Dir_AddDir(sysIncPath, optarg); } Var_Append(MAKEFLAGS, "-m", VAR_GLOBAL); Var_Append(MAKEFLAGS, optarg, VAR_GLOBAL); break; case 'n': noExecute = TRUE; Var_Append(MAKEFLAGS, "-n", VAR_GLOBAL); break; case 'q': queryFlag = TRUE; /* Kind of nonsensical, wot? */ Var_Append(MAKEFLAGS, "-q", VAR_GLOBAL); break; case 'r': noBuiltins = TRUE; Var_Append(MAKEFLAGS, "-r", VAR_GLOBAL); break; case 's': beSilent = TRUE; Var_Append(MAKEFLAGS, "-s", VAR_GLOBAL); break; case 't': touchFlag = TRUE; Var_Append(MAKEFLAGS, "-t", VAR_GLOBAL); break; case '-': dashDash = TRUE; break; default: case '?': #ifndef MAKE_NATIVE fprintf(stderr, "getopt(%s) -> %d (%c)\n", OPTFLAGS, c, c); #endif usage(); } argv += arginc; argc -= arginc; } oldVars = TRUE; /* * See if the rest of the arguments are variable assignments and * perform them if so. Else take them to be targets and stuff them * on the end of the "create" list. */ for (; argc > 1; ++argv, --argc) if (Parse_IsVar(argv[1])) { Parse_DoVar(argv[1], VAR_CMD); } else { if (!*argv[1]) Punt("illegal (null) argument."); if (*argv[1] == '-' && !dashDash) goto rearg; (void)Lst_AtEnd(create, (ClientData)estrdup(argv[1])); } }
/*- *----------------------------------------------------------------------- * JobExec -- * Execute the shell for the given job. Called from JobStart * * Side Effects: * A shell is executed, outputs is altered and the Job structure added * to the job table. *----------------------------------------------------------------------- */ static void JobExec(Job *job) { pid_t cpid; /* ID of new child */ struct job_pid *p; int fds[4]; int *fdout = fds; int *fderr = fds+2; int i; banner(job, stdout); setup_engine(1); /* Create the pipe by which we'll get the shell's output. */ if (pipe(fdout) == -1) Punt("Cannot create pipe: %s", strerror(errno)); if (pipe(fderr) == -1) Punt("Cannot create pipe: %s", strerror(errno)); block_signals(); if ((cpid = fork()) == -1) { Punt("Cannot fork"); unblock_signals(); } else if (cpid == 0) { supervise_jobs = false; /* standard pipe code to route stdout and stderr */ close(fdout[0]); if (dup2(fdout[1], 1) == -1) Punt("Cannot dup2(outPipe): %s", strerror(errno)); if (fdout[1] != 1) close(fdout[1]); close(fderr[0]); if (dup2(fderr[1], 2) == -1) Punt("Cannot dup2(errPipe): %s", strerror(errno)); if (fderr[1] != 2) close(fderr[1]); /* * We want to switch the child into a different process family * so we can kill it and all its descendants in one fell swoop, * by killing its process family, but not commit suicide. */ (void)setpgid(0, getpid()); if (random_delay) if (!(nJobs == 1 && no_jobs_left())) usleep(random() % random_delay); setup_all_signals(SigHandler, SIG_DFL); unblock_signals(); /* this exits directly */ run_gnode_parallel(job->node); /*NOTREACHED*/ } else { supervise_jobs = true; job->pid = cpid; /* we set the current position in the buffers to the beginning * and mark another stream to watch in the outputs mask */ for (i = 0; i < 2; i++) prepare_pipe(&job->in[i], fds+2*i); } /* * Now the job is actually running, add it to the table. */ nJobs++; Lst_AtEnd(&runningJobs, job); if (job->flags & JOB_IS_EXPENSIVE) expensive_job = true; p = emalloc(sizeof(struct job_pid)); p->pid = cpid; Lst_AtEnd(&job_pids, p); job->p = Lst_Last(&job_pids); unblock_signals(); if (DEBUG(JOB)) { LstNode ln; (void)fprintf(stdout, "Running %ld (%s)\n", (long)cpid, job->node->name); for (ln = Lst_First(&job->node->commands); ln != NULL ; ln = Lst_Adv(ln)) fprintf(stdout, "\t%s\n", (char *)Lst_Datum(ln)); (void)fflush(stdout); } }