📄 parse.c
字号:
/* * Copyright (c) 1988, 1989, 1990, 1993 * The Regents of the University of California. All rights reserved. * Copyright (c) 1989 by Berkeley Softworks * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */#ifndef lintstatic char sccsid[] = "@(#)parse.c 8.3 (Berkeley) 3/19/94";#endif /* not lint *//*- * parse.c -- * Functions to parse a makefile. * * One function, Parse_Init, must be called before any functions * in this module are used. After that, the function Parse_File is the * main entry point and controls most of the other functions in this * module. * * Most important structures are kept in Lsts. Directories for * the #include "..." function are kept in the 'parseIncPath' Lst, while * those for the #include <...> are kept in the 'sysIncPath' Lst. The * targets currently being defined are kept in the 'targets' Lst. * * The variables 'fname' and 'lineno' are used to track the name * of the current file and the line number in that file so that error * messages can be more meaningful. * * Interface: * Parse_Init Initialization function which must be * called before anything else in this module * is used. * * Parse_File Function used to parse a makefile. It must * be given the name of the file, which should * already have been opened, and a function * to call to read a character from the file. * * Parse_IsVar Returns TRUE if the given line is a * variable assignment. Used by MainParseArgs * to determine if an argument is a target * or a variable assignment. Used internally * for pretty much the same thing... * * Parse_Error Function called when an error occurs in * parsing. Used by the variable and * conditional modules. * Parse_MainName Returns a Lst of the main target to create. */#if __STDC__#include <stdarg.h>#else#include <varargs.h>#endif#include <stdio.h>#include <ctype.h>#include <errno.h>#include <sys/wait.h>#include "make.h"#include "hash.h"#include "dir.h"#include "job.h"#include "buf.h"#include "pathnames.h"/* * These values are returned by ParseEOF to tell Parse_File whether to * CONTINUE parsing, i.e. it had only reached the end of an include file, * or if it's DONE. */#define CONTINUE 1#define DONE 0static Lst targets; /* targets we're working on */static Boolean inLine; /* true if currently in a dependency * line or its commands */typedef struct { char *str; char *ptr;} PTR;static char *fname; /* name of current file (for errors) */static int lineno; /* line number in current file */static FILE *curFILE = NULL; /* current makefile */static PTR *curPTR = NULL; /* current makefile */static int fatals = 0;static GNode *mainNode; /* The main target to create. This is the * first target on the first dependency * line in the first makefile *//* * Definitions for handling #include specifications */typedef struct IFile { char *fname; /* name of previous file */ int lineno; /* saved line number */ FILE * F; /* the open stream */ PTR * p; /* the char pointer */} IFile;static Lst includes; /* stack of IFiles generated by * #includes */Lst parseIncPath; /* list of directories for "..." includes */Lst sysIncPath; /* list of directories for <...> includes *//*- * specType contains the SPECial TYPE of the current target. It is * Not if the target is unspecial. If it *is* special, however, the children * are linked as children of the parent but not vice versa. This variable is * set in ParseDoDependency */typedef enum { Begin, /* .BEGIN */ Default, /* .DEFAULT */ End, /* .END */ Ignore, /* .IGNORE */ Includes, /* .INCLUDES */ Interrupt, /* .INTERRUPT */ Libs, /* .LIBS */ MFlags, /* .MFLAGS or .MAKEFLAGS */ Main, /* .MAIN and we don't have anything user-specified to * make */ NoExport, /* .NOEXPORT */ Not, /* Not special */ NotParallel, /* .NOTPARALELL */ Null, /* .NULL */ Order, /* .ORDER */ ExPath, /* .PATH */ Precious, /* .PRECIOUS */ ExShell, /* .SHELL */ Silent, /* .SILENT */ SingleShell, /* .SINGLESHELL */ Suffixes, /* .SUFFIXES */ Attribute /* Generic attribute */} ParseSpecial;static ParseSpecial specType;/* * Predecessor node for handling .ORDER. Initialized to NILGNODE when .ORDER * seen, then set to each successive source on the line. */static GNode *predecessor;/* * The parseKeywords table is searched using binary search when deciding * if a target or source is special. The 'spec' field is the ParseSpecial * type of the keyword ("Not" if the keyword isn't special as a target) while * the 'op' field is the operator to apply to the list of targets if the * keyword is used as a source ("0" if the keyword isn't special as a source) */static struct { char *name; /* Name of keyword */ ParseSpecial spec; /* Type when used as a target */ int op; /* Operator when used as a source */} parseKeywords[] = {{ ".BEGIN", Begin, 0 },{ ".DEFAULT", Default, 0 },{ ".OPTIONAL", Attribute, OP_OPTIONAL },{ ".END", End, 0 },{ ".EXEC", Attribute, OP_EXEC },{ ".IGNORE", Ignore, OP_IGNORE },{ ".INCLUDES", Includes, 0 },{ ".INTERRUPT", Interrupt, 0 },{ ".INVISIBLE", Attribute, OP_INVISIBLE },{ ".JOIN", Attribute, OP_JOIN },{ ".LIBS", Libs, 0 },{ ".MAIN", Main, 0 },{ ".MAKE", Attribute, OP_MAKE },{ ".MAKEFLAGS", MFlags, 0 },{ ".MFLAGS", MFlags, 0 },{ ".NOTMAIN", Attribute, OP_NOTMAIN },{ ".NOTPARALLEL", NotParallel, 0 },{ ".NULL", Null, 0 },{ ".ORDER", Order, 0 },{ ".PATH", ExPath, 0 },{ ".PRECIOUS", Precious, OP_PRECIOUS },{ ".RECURSIVE", Attribute, OP_MAKE },{ ".SHELL", ExShell, 0 },{ ".SILENT", Silent, OP_SILENT },{ ".SINGLESHELL", SingleShell, 0 },{ ".SUFFIXES", Suffixes, 0 },{ ".USE", Attribute, OP_USE },};static int ParseFindKeyword __P((char *));static int ParseLinkSrc __P((GNode *, GNode *));static int ParseDoOp __P((GNode *, int));static void ParseDoSrc __P((int, char *));static int ParseFindMain __P((GNode *));static int ParseAddDir __P((Lst, char *));static int ParseClearPath __P((Lst));static void ParseDoDependency __P((char *));static int ParseAddCmd __P((GNode *, char *));static int ParseReadc __P((void));static void ParseUnreadc __P((int));static int ParseHasCommands __P((GNode *));static void ParseDoInclude __P((char *));#ifdef SYSVINCLUDEstatic void ParseTraditionalInclude __P((char *));#endifstatic int ParseEOF __P((int));static char *ParseReadLine __P((void));static char *ParseSkipLine __P((int));static void ParseFinishLine __P((void));/*- *---------------------------------------------------------------------- * ParseFindKeyword -- * Look in the table of keywords for one matching the given string. * * Results: * The index of the keyword, or -1 if it isn't there. * * Side Effects: * None *---------------------------------------------------------------------- */static intParseFindKeyword (str) char *str; /* String to find */{ register int start, end, cur; register int diff; start = 0; end = (sizeof(parseKeywords)/sizeof(parseKeywords[0])) - 1; do { cur = start + ((end - start) / 2); diff = strcmp (str, parseKeywords[cur].name); if (diff == 0) { return (cur); } else if (diff < 0) { end = cur - 1; } else { start = cur + 1; } } while (start <= end); return (-1);}/*- * Parse_Error -- * Error message abort function for parsing. Prints out the context * of the error (line number and file) as well as the message with * two optional arguments. * * Results: * None * * Side Effects: * "fatals" is incremented if the level is PARSE_FATAL. *//* VARARGS */void#if __STDC__Parse_Error(int type, const char *fmt, ...)#elseParse_Error(va_alist) va_dcl#endif{ va_list ap;#if __STDC__ va_start(ap, fmt);#else int type; /* Error type (PARSE_WARNING, PARSE_FATAL) */ char *fmt; va_start(ap); type = va_arg(ap, int); fmt = va_arg(ap, char *);#endif (void)fprintf(stderr, "\"%s\", line %d: ", fname, lineno); if (type == PARSE_WARNING) (void)fprintf(stderr, "warning: "); (void)vfprintf(stderr, fmt, ap); va_end(ap); (void)fprintf(stderr, "\n"); (void)fflush(stderr); if (type == PARSE_FATAL) fatals += 1;}/*- *--------------------------------------------------------------------- * ParseLinkSrc -- * Link the parent node to its new child. Used in a Lst_ForEach by * ParseDoDependency. If the specType isn't 'Not', the parent * isn't linked as a parent of the child. * * Results: * Always = 0 * * 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 intParseLinkSrc (pgn, cgn) GNode *pgn; /* The parent node */ GNode *cgn; /* The child node */{ if (Lst_Member (pgn->children, (ClientData)cgn) == NILLNODE) { (void)Lst_AtEnd (pgn->children, (ClientData)cgn); if (specType == Not) { (void)Lst_AtEnd (cgn->parents, (ClientData)pgn); } pgn->unmade += 1; } return (0);}/*- *--------------------------------------------------------------------- * ParseDoOp -- * Apply the parsed operator to the given target node. Used in a * Lst_ForEach 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. * * Results: * Always 0 * * Side Effects: * The type field of the node is altered to reflect any new bits in * the op. *--------------------------------------------------------------------- */static intParseDoOp (gn, op) GNode *gn; /* The node to which the operator is to be * applied */ int op; /* The operator to apply */{ /* * 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 operator for %s", gn->name); return (1); } 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. */ register 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. */ Lst_ForEach(gn->parents, ParseLinkSrc, (ClientData)cohort); cohort->type = OP_DOUBLEDEP|OP_INVISIBLE; (void)Lst_AtEnd(gn->cohorts, (ClientData)cohort); /* * Replace the node in the targets list with the new copy */ ln = Lst_Member(targets, (ClientData)gn); Lst_Replace(ln, (ClientData)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 (0);}/*- *--------------------------------------------------------------------- * 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' * * Results: * None * * 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 voidParseDoSrc (tOp, src) int tOp; /* operator (if any) from special targets */ char *src; /* name of the source to handle */{ int op; /* operator (if any) from special source */ GNode *gn; op = 0; if (*src == '.' && isupper (src[1])) { int keywd = ParseFindKeyword(src); if (keywd != -1) { op = parseKeywords[keywd].op; } } if (op != 0) { Lst_ForEach (targets, ParseDoOp, (ClientData)op); } else if (specType == 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. The string 'src' is likely to be free, so we * must make a new copy of it. 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... */ (void) Lst_AtEnd (create, (ClientData)strdup(src)); /* * Add the name to the .TARGETS variable as well, so the user cna * employ that, if desired. */ Var_Append(".TARGETS", src, VAR_GLOBAL); } else if (specType == Order) { /* * Create proper predecessor/successor links between the previous * source and the current one. */ gn = Targ_FindNode(src, TARG_CREATE); if (predecessor != NILGNODE) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -