📄 parse.c
字号:
(void)Lst_AtEnd(predecessor->successors, (ClientData)gn); (void)Lst_AtEnd(gn->preds, (ClientData)predecessor); } /* * The current source now becomes the predecessor for the next one. */ predecessor = gn; } else { /* * If the source is not an attribute, we need to find/create * a node for it. After that we can apply any operator to it * from a special target or link it to its parents, as * appropriate. * * 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. */ gn = Targ_FindNode (src, TARG_CREATE); if (tOp) { gn->type |= tOp; } else { Lst_ForEach (targets, ParseLinkSrc, (ClientData)gn); } if ((gn->type & OP_OPMASK) == OP_DOUBLEDEP) { register GNode *cohort; register LstNode ln; for (ln=Lst_First(gn->cohorts); ln != NILLNODE; ln = Lst_Succ(ln)){ cohort = (GNode *)Lst_Datum(ln); if (tOp) { cohort->type |= tOp; } else { Lst_ForEach(targets, ParseLinkSrc, (ClientData)cohort); } } } }}/*- *----------------------------------------------------------------------- * ParseFindMain -- * Find a real target in the list and set it to be the main one. * Called by ParseDoDependency when a main target hasn't been found * yet. * * Results: * 0 if main not found yet, 1 if it is. * * Side Effects: * mainNode is changed and Targ_SetMain is called. * *----------------------------------------------------------------------- */static intParseFindMain(gn) GNode *gn; /* Node to examine */{ if ((gn->type & (OP_NOTMAIN|OP_USE|OP_EXEC|OP_TRANSFORM)) == 0) { mainNode = gn; Targ_SetMain(gn); return (1); } else { return (0); }}/*- *----------------------------------------------------------------------- * ParseAddDir -- * Front-end for Dir_AddDir to make sure Lst_ForEach keeps going * * Results: * === 0 * * Side Effects: * See Dir_AddDir. * *----------------------------------------------------------------------- */static intParseAddDir(path, name) Lst path; char *name;{ Dir_AddDir(path, name); return(0);}/*- *----------------------------------------------------------------------- * ParseClearPath -- * Front-end for Dir_ClearPath to make sure Lst_ForEach keeps going * * Results: * === 0 * * Side Effects: * See Dir_ClearPath * *----------------------------------------------------------------------- */static intParseClearPath(path) Lst path;{ Dir_ClearPath(path); return(0);}/*- *--------------------------------------------------------------------- * ParseDoDependency -- * Parse the dependency line in line. * * Results: * None * * Side Effects: * The nodes of the sources are linked as children to the nodes of the * targets. Some nodes may be created. * * We parse a dependency line by first extracting words from the line and * finding nodes in the list of all targets with that name. This is done * until a character is encountered which is an operator character. Currently * these are only ! and :. At this point the operator is parsed and the * pointer into the line advanced until the first source is encountered. * The parsed operator is applied to each node in the 'targets' list, * which is where the nodes found for the targets are kept, by means of * the ParseDoOp function. * The sources are read in much the same way as the targets were except * that now they are expanded using the wildcarding scheme of the C-Shell * and all instances of the resulting words in the list of all targets * are found. Each of the resulting nodes is then linked to each of the * targets as one of its children. * Certain targets are handled specially. These are the ones detailed * by the specType variable. * The storing of transformation rules is also taken care of here. * A target is recognized as a transformation rule by calling * Suff_IsTransform. If it is a transformation rule, its node is gotten * from the suffix module via Suff_AddTransform rather than the standard * Targ_FindNode in the target module. *--------------------------------------------------------------------- */static voidParseDoDependency (line) char *line; /* the line to parse */{ register char *cp; /* our current position */ register GNode *gn; /* a general purpose temporary node */ register int op; /* the operator on the line */ char savec; /* a place to save a character */ Lst paths; /* List of search paths to alter when parsing * a list of .PATH targets */ int tOp; /* operator from special target */ Lst sources; /* list of source names after expansion */ Lst curTargs; /* list of target names to be found and added * to the targets list */ tOp = 0; specType = Not; paths = (Lst)NULL; curTargs = Lst_Init(FALSE); do { for (cp = line; *cp && !isspace (*cp) && (*cp != '!') && (*cp != ':') && (*cp != '('); cp++) { if (*cp == '$') { /* * Must be a dynamic source (would have been expanded * otherwise), so call the Var module to parse the puppy * so we can safely advance beyond it...There should be * no errors in this, as they would have been discovered * in the initial Var_Subst and we wouldn't be here. */ int length; Boolean freeIt; char *result; result=Var_Parse(cp, VAR_CMD, TRUE, &length, &freeIt); if (freeIt) { free(result); } cp += length-1; } continue; } if (*cp == '(') { /* * Archives must be handled specially to make sure the OP_ARCHV * flag is set in their 'type' field, for one thing, and because * things like "archive(file1.o file2.o file3.o)" are permissible. * Arch_ParseArchive will set 'line' to be the first non-blank * after the archive-spec. It creates/finds nodes for the members * and places them on the given list, returning SUCCESS if all * went well and FAILURE if there was an error in the * specification. On error, line should remain untouched. */ if (Arch_ParseArchive (&line, targets, VAR_CMD) != SUCCESS) { Parse_Error (PARSE_FATAL, "Error in archive specification: \"%s\"", line); return; } else { continue; } } savec = *cp; if (!*cp) { /* * Ending a dependency line without an operator is a Bozo * no-no */ Parse_Error (PARSE_FATAL, "Need an operator"); return; } *cp = '\0'; /* * Have a word in line. See if it's a special target and set * specType to match it. */ if (*line == '.' && isupper (line[1])) { /* * See if the target is a special target that must have it * or its sources handled specially. */ int keywd = ParseFindKeyword(line); if (keywd != -1) { if (specType == ExPath && parseKeywords[keywd].spec != ExPath) { Parse_Error(PARSE_FATAL, "Mismatched special targets"); return; } specType = parseKeywords[keywd].spec; tOp = parseKeywords[keywd].op; /* * Certain special targets have special semantics: * .PATH Have to set the dirSearchPath * variable too * .MAIN Its sources are only used if * nothing has been specified to * create. * .DEFAULT Need to create a node to hang * commands on, but we don't want * it in the graph, nor do we want * it to be the Main Target, so we * create it, set OP_NOTMAIN and * add it to the list, setting * DEFAULT to the new node for * later use. We claim the node is * A transformation rule to make * life easier later, when we'll * use Make_HandleUse to actually * apply the .DEFAULT commands. * .BEGIN * .END * .INTERRUPT Are not to be considered the * main target. * .NOTPARALLEL Make only one target at a time. * .SINGLESHELL Create a shell for each command. * .ORDER Must set initial predecessor to NIL */ switch (specType) { case ExPath: if (paths == NULL) { paths = Lst_Init(FALSE); } (void)Lst_AtEnd(paths, (ClientData)dirSearchPath); break; case Main: if (!Lst_IsEmpty(create)) { specType = Not; } break; case Begin: case End: case Interrupt: gn = Targ_FindNode(line, TARG_CREATE); gn->type |= OP_NOTMAIN; (void)Lst_AtEnd(targets, (ClientData)gn); break; case Default: gn = Targ_NewGN(".DEFAULT"); gn->type |= (OP_NOTMAIN|OP_TRANSFORM); (void)Lst_AtEnd(targets, (ClientData)gn); DEFAULT = gn; break; case NotParallel: { extern int maxJobs; maxJobs = 1; break; } case SingleShell: compatMake = 1; break; case Order: predecessor = NILGNODE; break; default: break; } } else if (strncmp (line, ".PATH", 5) == 0) { /* * .PATH<suffix> has to be handled specially. * Call on the suffix module to give us a path to * modify. */ Lst path; specType = ExPath; path = Suff_GetPath (&line[5]); if (path == NILLST) { Parse_Error (PARSE_FATAL, "Suffix '%s' not defined (yet)", &line[5]); return; } else { if (paths == (Lst)NULL) { paths = Lst_Init(FALSE); } (void)Lst_AtEnd(paths, (ClientData)path); } } } /* * Have word in line. Get or create its node and stick it at * the end of the targets list */ if ((specType == Not) && (*line != '\0')) { if (Dir_HasWildcards(line)) { /* * Targets are to be sought only in the current directory, * so create an empty path for the thing. Note we need to * use Dir_Destroy in the destruction of the path as the * Dir module could have added a directory to the path... */ Lst emptyPath = Lst_Init(FALSE); Dir_Expand(line, emptyPath, curTargs); Lst_Destroy(emptyPath, Dir_Destroy); } else { /* * No wildcards, but we want to avoid code duplication, * so create a list with the word on it. */ (void)Lst_AtEnd(curTargs, (ClientData)line); } while(!Lst_IsEmpty(curTargs)) { char *targName = (char *)Lst_DeQueue(curTargs); if (!Suff_IsTransform (targName)) { gn = Targ_FindNode (targName, TARG_CREATE); } else { gn = Suff_AddTransform (targName); } (void)Lst_AtEnd (targets, (ClientData)gn); } } else if (specType == ExPath && *line != '.' && *line != '\0') { Parse_Error(PARSE_WARNING, "Extra target (%s) ignored", line); } *cp = savec; /* * If it is a special type and not .PATH, it's the only target we * allow on this line... */ if (specType != Not && specType != ExPath) { Boolean warn = FALSE; while ((*cp != '!') && (*cp != ':') && *cp) { if (*cp != ' ' && *cp != '\t') { warn = TRUE; } cp++; } if (warn) { Parse_Error(PARSE_WARNING, "Extra target ignored"); } } else { while (*cp && isspace (*cp)) { cp++; } } line = cp; } while ((*line != '!') && (*line != ':') && *line); /* * Don't need the list of target names anymore... */ Lst_Destroy(curTargs, NOFREE); if (!Lst_IsEmpty(targets)) { switch(specType) { default: Parse_Error(PARSE_WARNING, "Special and mundane targets don't mix. Mundane ones ignored"); break; case Default: case Begin: case End: case Interrupt: /* * These four create nodes on which to hang commands, so * targets shouldn't be empty... */ case Not: /* * Nothing special here -- targets can be empty if it wants. */ break; } } /* * Have now parsed all the target names. Must parse the operator next. The * result is left in op . */ if (*cp == '!') { op = OP_FORCE; } else if (*cp == ':') { if (cp[1] == ':') { op = OP_DOUBLEDEP; cp++; } else { op = OP_DEPENDS; } } else { Parse_Error (PARSE_FATAL, "Missing dependency operator"); return; } cp++; /* Advance beyond operator */ Lst_ForEach (targets, ParseDoOp, (ClientData)op); /* * Get to the first source */ while (*cp && isspace (*cp)) { cp++; } line = cp; /* * Several special targets take different actions if present with no * sources: * a .SUFFIXES line with no sources clears out all old suffixes * a .PRECIOUS line makes all targets precious * a .IGNORE line ignores errors for all targets * a .SILENT line creates silence when making all targets * a .PATH removes all directories from the search path(s). */ if (!*line) { switch (specType) { case Suffixes: Suff_ClearSuffixes (); break; case Precious: allPrecious = TRUE; break; case Ignore: ignoreErrors = TRUE; break; case Silent: beSilent = TRUE; break; case ExPath: Lst_ForEach(paths, ParseClearPath, (ClientData)NULL); break; default: break; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -