summaryrefslogtreecommitdiff
path: root/parse.c
diff options
context:
space:
mode:
Diffstat (limited to 'parse.c')
-rw-r--r--parse.c3754
1 files changed, 1812 insertions, 1942 deletions
diff --git a/parse.c b/parse.c
index ee065dd709dc7..9efa5efc627bb 100644
--- a/parse.c
+++ b/parse.c
@@ -1,4 +1,4 @@
-/* $NetBSD: parse.c,v 1.275 2020/09/01 17:38:26 rillig Exp $ */
+/* $NetBSD: parse.c,v 1.420 2020/11/01 00:24:57 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
@@ -68,71 +68,41 @@
* SUCH DAMAGE.
*/
-#ifndef MAKE_NATIVE
-static char rcsid[] = "$NetBSD: parse.c,v 1.275 2020/09/01 17:38:26 rillig Exp $";
-#else
-#include <sys/cdefs.h>
-#ifndef lint
-#if 0
-static char sccsid[] = "@(#)parse.c 8.3 (Berkeley) 3/19/94";
-#else
-__RCSID("$NetBSD: parse.c,v 1.275 2020/09/01 17:38:26 rillig Exp $");
-#endif
-#endif /* not lint */
-#endif
-
-/*-
- * 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.
+/*
+ * Parsing of makefiles.
*
- * 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.
+ * Parse_File is the main entry point and controls most of the other
+ * functions in this module.
*
- * 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.
+ * The directories for the .include "..." directive are kept in
+ * 'parseIncPath', while those for .include <...> are kept in 'sysIncPath'.
+ * The targets currently being defined are kept in 'targets'.
*
* Interface:
- * Parse_Init Initialization function which must be
- * called before anything else in this module
- * is used.
+ * Parse_Init Initialize the module
+ *
+ * Parse_End Clean up the module
*
- * Parse_End Cleanup the module
+ * Parse_File Parse a top-level makefile. Included files are
+ * handled by Parse_include_file though.
*
- * 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 Return 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_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 Report a parse error, a warning or an informational
+ * message.
*
- * 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.
+ * Parse_MainName Returns a list of the main target to create.
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <stdarg.h>
-#include <stdio.h>
#include "make.h"
-#include "dir.h"
-#include "job.h"
-#include "pathnames.h"
#ifdef HAVE_STDINT_H
#include <stdint.h>
@@ -149,82 +119,79 @@ __RCSID("$NetBSD: parse.c,v 1.275 2020/09/01 17:38:26 rillig Exp $");
#endif
#endif
+#include "dir.h"
+#include "job.h"
+#include "pathnames.h"
+
+/* "@(#)parse.c 8.3 (Berkeley) 3/19/94" */
+MAKE_RCSID("$NetBSD: parse.c,v 1.420 2020/11/01 00:24:57 rillig Exp $");
+
/* types and constants */
/*
* Structure for a file being read ("included file")
*/
typedef struct IFile {
- char *fname; /* name of file */
- int lineno; /* current line number in file */
- int first_lineno; /* line number of start of text */
- int cond_depth; /* 'if' nesting when file opened */
- Boolean depending; /* state of doing_depend on EOF */
- char *P_str; /* point to base of string buffer */
- char *P_ptr; /* point to next char of string buffer */
- char *P_end; /* point to the end of string buffer */
- char *(*nextbuf)(void *, size_t *); /* Function to get more data */
- void *nextbuf_arg; /* Opaque arg for nextbuf() */
- struct loadedfile *lf; /* loadedfile object, if any */
+ char *fname; /* name of file */
+ Boolean fromForLoop; /* simulated .include by the .for loop */
+ int lineno; /* current line number in file */
+ int first_lineno; /* line number of start of text */
+ unsigned int cond_depth; /* 'if' nesting when file opened */
+ Boolean depending; /* state of doing_depend on EOF */
+
+ /* The buffer from which the file's content is read. */
+ char *buf_freeIt;
+ char *buf_ptr; /* next char to be read */
+ char *buf_end;
+
+ char *(*nextbuf)(void *, size_t *); /* Function to get more data */
+ void *nextbuf_arg; /* Opaque arg for nextbuf() */
+ struct loadedfile *lf; /* loadedfile object, if any */
} IFile;
-
-/*
- * 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 0
-
/*
* Tokens for target attributes
*/
-typedef enum {
- Begin, /* .BEGIN */
- Default, /* .DEFAULT */
- DeleteOnError, /* .DELETE_ON_ERROR */
- End, /* .END */
- dotError, /* .ERROR */
- Ignore, /* .IGNORE */
- Includes, /* .INCLUDES */
- Interrupt, /* .INTERRUPT */
- Libs, /* .LIBS */
- Meta, /* .META */
- MFlags, /* .MFLAGS or .MAKEFLAGS */
- Main, /* .MAIN and we don't have anything user-specified to
- * make */
- NoExport, /* .NOEXPORT */
- NoMeta, /* .NOMETA */
- NoMetaCmp, /* .NOMETA_CMP */
- NoPath, /* .NOPATH */
- Not, /* Not special */
- NotParallel, /* .NOTPARALLEL */
- Null, /* .NULL */
- ExObjdir, /* .OBJDIR */
- Order, /* .ORDER */
- Parallel, /* .PARALLEL */
- ExPath, /* .PATH */
- Phony, /* .PHONY */
+typedef enum ParseSpecial {
+ SP_ATTRIBUTE, /* Generic attribute */
+ SP_BEGIN, /* .BEGIN */
+ SP_DEFAULT, /* .DEFAULT */
+ SP_DELETE_ON_ERROR, /* .DELETE_ON_ERROR */
+ SP_END, /* .END */
+ SP_ERROR, /* .ERROR */
+ SP_IGNORE, /* .IGNORE */
+ SP_INCLUDES, /* .INCLUDES; not mentioned in the manual page */
+ SP_INTERRUPT, /* .INTERRUPT */
+ SP_LIBS, /* .LIBS; not mentioned in the manual page */
+ SP_MAIN, /* .MAIN and we don't have anything user-specified to
+ * make */
+ SP_META, /* .META */
+ SP_MFLAGS, /* .MFLAGS or .MAKEFLAGS */
+ SP_NOMETA, /* .NOMETA */
+ SP_NOMETA_CMP, /* .NOMETA_CMP */
+ SP_NOPATH, /* .NOPATH */
+ SP_NOT, /* Not special */
+ SP_NOTPARALLEL, /* .NOTPARALLEL or .NO_PARALLEL */
+ SP_NULL, /* .NULL; not mentioned in the manual page */
+ SP_OBJDIR, /* .OBJDIR */
+ SP_ORDER, /* .ORDER */
+ SP_PARALLEL, /* .PARALLEL; not mentioned in the manual page */
+ SP_PATH, /* .PATH or .PATH.suffix */
+ SP_PHONY, /* .PHONY */
#ifdef POSIX
- Posix, /* .POSIX */
+ SP_POSIX, /* .POSIX; not mentioned in the manual page */
#endif
- Precious, /* .PRECIOUS */
- ExShell, /* .SHELL */
- Silent, /* .SILENT */
- SingleShell, /* .SINGLESHELL */
- Stale, /* .STALE */
- Suffixes, /* .SUFFIXES */
- Wait, /* .WAIT */
- Attribute /* Generic attribute */
+ SP_PRECIOUS, /* .PRECIOUS */
+ SP_SHELL, /* .SHELL */
+ SP_SILENT, /* .SILENT */
+ SP_SINGLESHELL, /* .SINGLESHELL; not mentioned in the manual page */
+ SP_STALE, /* .STALE */
+ SP_SUFFIXES, /* .SUFFIXES */
+ SP_WAIT /* .WAIT */
} ParseSpecial;
-/*
- * Other tokens
- */
-#define LPAREN '('
-#define RPAREN ')'
-
+typedef List SearchPathList;
+typedef ListNode SearchPathListNode;
/* result data */
@@ -236,33 +203,29 @@ static GNode *mainNode;
/* eval state */
-/* targets we're working on */
-static Lst targets;
+/* During parsing, the targets from the left-hand side of the currently
+ * active dependency line, or NULL if the current line does not belong to a
+ * dependency line, for example because it is a variable assignment.
+ *
+ * See unit-tests/deptgt.mk, keyword "parse.c:targets". */
+static GNodeList *targets;
#ifdef CLEANUP
-/* command lines for targets */
-static Lst targCmds;
+/* All shell commands for all targets, in no particular order and possibly
+ * with duplicates. Kept in a separate list since the commands from .USE or
+ * .USEBEFORE nodes are shared with other GNodes, thereby giving up the
+ * easily understandable ownership over the allocated strings. */
+static StringList *targCmds;
#endif
/*
- * 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
- */
-static ParseSpecial specType;
-
-/*
* Predecessor node for handling .ORDER. Initialized to NULL when .ORDER
* seen, then set to each successive source on the line.
*/
-static GNode *predecessor;
+static GNode *order_pred;
/* parser state */
-/* true if currently in a dependency line or its commands */
-static Boolean inLine;
-
/* number of fatal errors */
static int fatals = 0;
@@ -270,105 +233,125 @@ static int fatals = 0;
* Variables for doing includes
*/
-/* current file being read */
-static IFile *curFile;
+/* The include chain of makefiles. At the bottom is the top-level makefile
+ * from the command line, and on top of that, there are the included files or
+ * .for loops, up to and including the current file.
+ *
+ * This data could be used to print stack traces on parse errors. As of
+ * 2020-09-14, this is not done though. It seems quite simple to print the
+ * tuples (fname:lineno:fromForLoop), from top to bottom. This simple idea is
+ * made complicated by the fact that the .for loops also use this stack for
+ * storing information.
+ *
+ * The lineno fields of the IFiles with fromForLoop == TRUE look confusing,
+ * which is demonstrated by the test 'include-main.mk'. They seem sorted
+ * backwards since they tell the number of completely parsed lines, which for
+ * a .for loop is right after the terminating .endfor. To compensate for this
+ * confusion, there is another field first_lineno pointing at the start of the
+ * .for loop, 1-based for human consumption.
+ *
+ * To make the stack trace intuitive, the entry below the first .for loop must
+ * be ignored completely since neither its lineno nor its first_lineno is
+ * useful. Instead, the topmost of each chain of .for loop needs to be
+ * printed twice, once with its first_lineno and once with its lineno.
+ *
+ * As of 2020-10-28, using the above rules, the stack trace for the .info line
+ * in include-subsub.mk would be:
+ *
+ * includes[5]: include-subsub.mk:4
+ * (lineno, from an .include)
+ * includes[4]: include-sub.mk:32
+ * (lineno, from a .for loop below an .include)
+ * includes[4]: include-sub.mk:31
+ * (first_lineno, from a .for loop, lineno == 32)
+ * includes[3]: include-sub.mk:30
+ * (first_lineno, from a .for loop, lineno == 33)
+ * includes[2]: include-sub.mk:29
+ * (first_lineno, from a .for loop, lineno == 34)
+ * includes[1]: include-sub.mk:35
+ * (not printed since it is below a .for loop)
+ * includes[0]: include-main.mk:27
+ */
+static Vector /* of IFile */ includes;
-/* stack of IFiles generated by .includes */
-static Lst includes;
+static IFile *
+GetInclude(size_t i)
+{
+ return Vector_Get(&includes, i);
+}
-/* include paths (lists of directories) */
-Lst parseIncPath; /* dirs for "..." includes */
-Lst sysIncPath; /* dirs for <...> includes */
-Lst defIncPath; /* default for sysIncPath */
+/* The file that is currently being read. */
+static IFile *
+CurFile(void)
+{
+ return GetInclude(includes.len - 1);
+}
+
+/* include paths */
+SearchPath *parseIncPath; /* dirs for "..." includes */
+SearchPath *sysIncPath; /* dirs for <...> includes */
+SearchPath *defSysIncPath; /* default for sysIncPath */
/* parser tables */
/*
* 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
+ * type of the keyword (SP_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 const struct {
- const char *name; /* Name of keyword */
- ParseSpecial spec; /* Type when used as a target */
- int op; /* Operator when used as a source */
+ const char *name; /* Name of keyword */
+ ParseSpecial spec; /* Type when used as a target */
+ GNodeType op; /* Operator when used as a source */
} parseKeywords[] = {
-{ ".BEGIN", Begin, 0 },
-{ ".DEFAULT", Default, 0 },
-{ ".DELETE_ON_ERROR", DeleteOnError, 0 },
-{ ".END", End, 0 },
-{ ".ERROR", dotError, 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 },
-{ ".MADE", Attribute, OP_MADE },
-{ ".MAIN", Main, 0 },
-{ ".MAKE", Attribute, OP_MAKE },
-{ ".MAKEFLAGS", MFlags, 0 },
-{ ".META", Meta, OP_META },
-{ ".MFLAGS", MFlags, 0 },
-{ ".NOMETA", NoMeta, OP_NOMETA },
-{ ".NOMETA_CMP", NoMetaCmp, OP_NOMETA_CMP },
-{ ".NOPATH", NoPath, OP_NOPATH },
-{ ".NOTMAIN", Attribute, OP_NOTMAIN },
-{ ".NOTPARALLEL", NotParallel, 0 },
-{ ".NO_PARALLEL", NotParallel, 0 },
-{ ".NULL", Null, 0 },
-{ ".OBJDIR", ExObjdir, 0 },
-{ ".OPTIONAL", Attribute, OP_OPTIONAL },
-{ ".ORDER", Order, 0 },
-{ ".PARALLEL", Parallel, 0 },
-{ ".PATH", ExPath, 0 },
-{ ".PHONY", Phony, OP_PHONY },
+ { ".BEGIN", SP_BEGIN, 0 },
+ { ".DEFAULT", SP_DEFAULT, 0 },
+ { ".DELETE_ON_ERROR", SP_DELETE_ON_ERROR, 0 },
+ { ".END", SP_END, 0 },
+ { ".ERROR", SP_ERROR, 0 },
+ { ".EXEC", SP_ATTRIBUTE, OP_EXEC },
+ { ".IGNORE", SP_IGNORE, OP_IGNORE },
+ { ".INCLUDES", SP_INCLUDES, 0 },
+ { ".INTERRUPT", SP_INTERRUPT, 0 },
+ { ".INVISIBLE", SP_ATTRIBUTE, OP_INVISIBLE },
+ { ".JOIN", SP_ATTRIBUTE, OP_JOIN },
+ { ".LIBS", SP_LIBS, 0 },
+ { ".MADE", SP_ATTRIBUTE, OP_MADE },
+ { ".MAIN", SP_MAIN, 0 },
+ { ".MAKE", SP_ATTRIBUTE, OP_MAKE },
+ { ".MAKEFLAGS", SP_MFLAGS, 0 },
+ { ".META", SP_META, OP_META },
+ { ".MFLAGS", SP_MFLAGS, 0 },
+ { ".NOMETA", SP_NOMETA, OP_NOMETA },
+ { ".NOMETA_CMP", SP_NOMETA_CMP, OP_NOMETA_CMP },
+ { ".NOPATH", SP_NOPATH, OP_NOPATH },
+ { ".NOTMAIN", SP_ATTRIBUTE, OP_NOTMAIN },
+ { ".NOTPARALLEL", SP_NOTPARALLEL, 0 },
+ { ".NO_PARALLEL", SP_NOTPARALLEL, 0 },
+ { ".NULL", SP_NULL, 0 },
+ { ".OBJDIR", SP_OBJDIR, 0 },
+ { ".OPTIONAL", SP_ATTRIBUTE, OP_OPTIONAL },
+ { ".ORDER", SP_ORDER, 0 },
+ { ".PARALLEL", SP_PARALLEL, 0 },
+ { ".PATH", SP_PATH, 0 },
+ { ".PHONY", SP_PHONY, OP_PHONY },
#ifdef POSIX
-{ ".POSIX", Posix, 0 },
+ { ".POSIX", SP_POSIX, 0 },
#endif
-{ ".PRECIOUS", Precious, OP_PRECIOUS },
-{ ".RECURSIVE", Attribute, OP_MAKE },
-{ ".SHELL", ExShell, 0 },
-{ ".SILENT", Silent, OP_SILENT },
-{ ".SINGLESHELL", SingleShell, 0 },
-{ ".STALE", Stale, 0 },
-{ ".SUFFIXES", Suffixes, 0 },
-{ ".USE", Attribute, OP_USE },
-{ ".USEBEFORE", Attribute, OP_USEBEFORE },
-{ ".WAIT", Wait, 0 },
+ { ".PRECIOUS", SP_PRECIOUS, OP_PRECIOUS },
+ { ".RECURSIVE", SP_ATTRIBUTE, OP_MAKE },
+ { ".SHELL", SP_SHELL, 0 },
+ { ".SILENT", SP_SILENT, OP_SILENT },
+ { ".SINGLESHELL", SP_SINGLESHELL, 0 },
+ { ".STALE", SP_STALE, 0 },
+ { ".SUFFIXES", SP_SUFFIXES, 0 },
+ { ".USE", SP_ATTRIBUTE, OP_USE },
+ { ".USEBEFORE", SP_ATTRIBUTE, OP_USEBEFORE },
+ { ".WAIT", SP_WAIT, 0 },
};
-/* local functions */
-
-static int ParseIsEscaped(const char *, const char *);
-static void ParseErrorInternal(const char *, size_t, int, const char *, ...)
- MAKE_ATTR_PRINTFLIKE(4,5);
-static void ParseVErrorInternal(FILE *, const char *, size_t, int, const char *, va_list)
- MAKE_ATTR_PRINTFLIKE(5, 0);
-static int ParseFindKeyword(const char *);
-static int ParseLinkSrc(void *, void *);
-static int ParseDoOp(void *, void *);
-static void ParseDoSrc(int, const char *);
-static int ParseFindMain(void *, void *);
-static int ParseAddDir(void *, void *);
-static int ParseClearPath(void *, void *);
-static void ParseDoDependency(char *);
-static int ParseAddCmd(void *, void *);
-static void ParseHasCommands(void *);
-static void ParseDoInclude(char *);
-static void ParseSetParseFile(const char *);
-static void ParseSetIncludedFile(void);
-#ifdef GMAKEEXPORT
-static void ParseGmakeExport(char *);
-#endif
-static int ParseEOF(void);
-static char *ParseReadLine(void);
-static void ParseFinishLine(void);
-static void ParseMark(GNode *);
-
/* file loader */
struct loadedfile {
@@ -379,9 +362,6 @@ struct loadedfile {
Boolean used; /* XXX: have we used the data yet */
};
-/*
- * Constructor/destructor for loadedfile
- */
static struct loadedfile *
loadedfile_create(const char *path)
{
@@ -457,58 +437,28 @@ load_getsize(int fd, size_t *ret)
return FALSE;
}
- *ret = (size_t) st.st_size;
+ *ret = (size_t)st.st_size;
return TRUE;
}
-/*
- * Read in a file.
- *
- * Until the path search logic can be moved under here instead of
- * being in the caller in another source file, we need to have the fd
- * passed in already open. Bleh.
- *
- * If the path is NULL use stdin and (to insure against fd leaks)
- * assert that the caller passed in -1.
- */
-static struct loadedfile *
-loadfile(const char *path, int fd)
-{
- struct loadedfile *lf;
#ifdef HAVE_MMAP
- static long pagesize = 0;
-#endif
- ssize_t result;
- size_t bufpos;
-
- lf = loadedfile_create(path);
-
- if (path == NULL) {
- assert(fd == -1);
- fd = STDIN_FILENO;
- } else {
-#if 0 /* notyet */
- fd = open(path, O_RDONLY);
- if (fd < 0) {
- ...
- Error("%s: %s", path, strerror(errno));
- exit(1);
- }
-#endif
- }
+static Boolean
+loadedfile_mmap(struct loadedfile *lf, int fd)
+{
+ static unsigned long pagesize = 0;
-#ifdef HAVE_MMAP
if (load_getsize(fd, &lf->len)) {
+
/* found a size, try mmap */
#ifdef _SC_PAGESIZE
if (pagesize == 0)
- pagesize = sysconf(_SC_PAGESIZE);
+ pagesize = (unsigned long)sysconf(_SC_PAGESIZE);
#endif
- if (pagesize <= 0) {
+ if (pagesize == 0 || pagesize == (unsigned long)-1) {
pagesize = 0x1000;
}
/* round size up to a page */
- lf->maplen = pagesize * ((lf->len + pagesize - 1)/pagesize);
+ lf->maplen = pagesize * ((lf->len + pagesize - 1) / pagesize);
/*
* XXX hack for dealing with empty files; remove when
@@ -535,10 +485,51 @@ loadfile(const char *path, int fd)
lf->maplen = 0;
lf->buf = b;
}
- goto done;
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+#endif
+
+/*
+ * Read in a file.
+ *
+ * Until the path search logic can be moved under here instead of
+ * being in the caller in another source file, we need to have the fd
+ * passed in already open. Bleh.
+ *
+ * If the path is NULL use stdin and (to insure against fd leaks)
+ * assert that the caller passed in -1.
+ */
+static struct loadedfile *
+loadfile(const char *path, int fd)
+{
+ struct loadedfile *lf;
+ ssize_t result;
+ size_t bufpos;
+
+ lf = loadedfile_create(path);
+
+ if (path == NULL) {
+ assert(fd == -1);
+ fd = STDIN_FILENO;
+ } else {
+#if 0 /* notyet */
+ fd = open(path, O_RDONLY);
+ if (fd < 0) {
+ ...
+ Error("%s: %s", path, strerror(errno));
+ exit(1);
}
+#endif
}
+
+#ifdef HAVE_MMAP
+ if (loadedfile_mmap(lf, fd))
+ goto done;
#endif
+
/* cannot mmap; load the traditional way */
lf->maplen = 0;
@@ -566,7 +557,7 @@ loadfile(const char *path, int fd)
if (result == 0) {
break;
}
- bufpos += result;
+ bufpos += (size_t)result;
}
assert(bufpos <= lf->len);
lf->len = bufpos;
@@ -591,22 +582,11 @@ done:
/* old code */
-/*-
- *----------------------------------------------------------------------
- * ParseIsEscaped --
- * Check if the current character is escaped on the current line
- *
- * Results:
- * 0 if the character is not backslash escaped, 1 otherwise
- *
- * Side Effects:
- * None
- *----------------------------------------------------------------------
- */
-static int
+/* Check if the current character is escaped on the current line. */
+static Boolean
ParseIsEscaped(const char *line, const char *c)
{
- int active = 0;
+ Boolean active = FALSE;
for (;;) {
if (line == c)
return active;
@@ -616,32 +596,29 @@ ParseIsEscaped(const char *line, const char *c)
}
}
-/*-
- *----------------------------------------------------------------------
- * ParseFindKeyword --
- * Look in the table of keywords for one matching the given string.
- *
- * Input:
- * str String to find
- *
- * Results:
- * The index of the keyword, or -1 if it isn't there.
- *
- * Side Effects:
- * None
- *----------------------------------------------------------------------
- */
+/* Add the filename and lineno to the GNode so that we remember where it
+ * was first defined. */
+static void
+ParseMark(GNode *gn)
+{
+ IFile *curFile = CurFile();
+ gn->fname = curFile->fname;
+ gn->lineno = curFile->lineno;
+}
+
+/* Look in the table of keywords for one matching the given string.
+ * Return the index of the keyword, or -1 if it isn't there. */
static int
ParseFindKeyword(const char *str)
{
- int start, end, cur;
- int diff;
+ int start, end, cur;
+ int diff;
start = 0;
- end = (sizeof(parseKeywords)/sizeof(parseKeywords[0])) - 1;
+ end = sizeof parseKeywords / sizeof parseKeywords[0] - 1;
do {
- cur = start + ((end - start) / 2);
+ cur = start + (end - start) / 2;
diff = strcmp(str, parseKeywords[cur].name);
if (diff == 0) {
@@ -655,89 +632,72 @@ ParseFindKeyword(const char *str)
return -1;
}
-/*-
- * ParseVErrorInternal --
- * 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 */
static void
-ParseVErrorInternal(FILE *f, const char *cfname, size_t clineno, int type,
- const char *fmt, va_list ap)
+PrintLocation(FILE *f, const char *filename, size_t lineno)
{
- static Boolean fatal_warning_error_printed = FALSE;
char dirbuf[MAXPATHLEN+1];
+ const char *dir, *base;
+ void *dir_freeIt, *base_freeIt;
- (void)fprintf(f, "%s: ", progname);
+ if (*filename == '/' || strcmp(filename, "(stdin)") == 0) {
+ (void)fprintf(f, "\"%s\" line %zu: ", filename, lineno);
+ return;
+ }
- if (cfname != NULL) {
- (void)fprintf(f, "\"");
- if (*cfname != '/' && strcmp(cfname, "(stdin)") != 0) {
- char *cp, *cp2;
- const char *dir, *fname;
-
- /*
- * Nothing is more annoying than not knowing
- * which Makefile is the culprit; we try ${.PARSEDIR}
- * and apply realpath(3) if not absolute.
- */
- dir = Var_Value(".PARSEDIR", VAR_GLOBAL, &cp);
- if (dir == NULL)
- dir = ".";
- if (*dir != '/') {
- dir = realpath(dir, dirbuf);
- }
- fname = Var_Value(".PARSEFILE", VAR_GLOBAL, &cp2);
- if (fname == NULL) {
- if ((fname = strrchr(cfname, '/')))
- fname++;
- else
- fname = cfname;
- }
- (void)fprintf(f, "%s/%s", dir, fname);
- bmake_free(cp2);
- bmake_free(cp);
- } else
- (void)fprintf(f, "%s", cfname);
+ /* Find out which makefile is the culprit.
+ * We try ${.PARSEDIR} and apply realpath(3) if not absolute. */
+
+ dir = Var_Value(".PARSEDIR", VAR_GLOBAL, &dir_freeIt);
+ if (dir == NULL)
+ dir = ".";
+ if (*dir != '/')
+ dir = realpath(dir, dirbuf);
- (void)fprintf(f, "\" line %d: ", (int)clineno);
+ base = Var_Value(".PARSEFILE", VAR_GLOBAL, &base_freeIt);
+ if (base == NULL) {
+ const char *slash = strrchr(filename, '/');
+ base = slash != NULL ? slash + 1 : filename;
}
+
+ (void)fprintf(f, "\"%s/%s\" line %zu: ", dir, base, lineno);
+ bmake_free(base_freeIt);
+ bmake_free(dir_freeIt);
+}
+
+/* Print a parse error message, including location information.
+ *
+ * Increment "fatals" if the level is PARSE_FATAL, and continue parsing
+ * until the end of the current top-level makefile, then exit (see
+ * Parse_File). */
+static void
+ParseVErrorInternal(FILE *f, const char *cfname, size_t clineno,
+ ParseErrorLevel type, const char *fmt, va_list ap)
+{
+ static Boolean fatal_warning_error_printed = FALSE;
+
+ (void)fprintf(f, "%s: ", progname);
+
+ if (cfname != NULL)
+ PrintLocation(f, cfname, clineno);
if (type == PARSE_WARNING)
(void)fprintf(f, "warning: ");
(void)vfprintf(f, fmt, ap);
(void)fprintf(f, "\n");
(void)fflush(f);
+
if (type == PARSE_INFO)
return;
- if (type == PARSE_FATAL || parseWarnFatal)
- fatals += 1;
- if (parseWarnFatal && !fatal_warning_error_printed) {
+ if (type == PARSE_FATAL || opts.parseWarnFatal)
+ fatals++;
+ if (opts.parseWarnFatal && !fatal_warning_error_printed) {
Error("parsing warnings being treated as errors");
fatal_warning_error_printed = TRUE;
}
}
-/*-
- * ParseErrorInternal --
- * Error function
- *
- * Results:
- * None
- *
- * Side Effects:
- * None
- */
-/* VARARGS */
static void
-ParseErrorInternal(const char *cfname, size_t clineno, int type,
- const char *fmt, ...)
+ParseErrorInternal(const char *cfname, size_t clineno, ParseErrorLevel type,
+ const char *fmt, ...)
{
va_list ap;
@@ -746,38 +706,32 @@ ParseErrorInternal(const char *cfname, size_t clineno, int type,
ParseVErrorInternal(stderr, cfname, clineno, type, fmt, ap);
va_end(ap);
- if (debug_file != stderr && debug_file != stdout) {
+ if (opts.debug_file != stderr && opts.debug_file != stdout) {
va_start(ap, fmt);
- ParseVErrorInternal(debug_file, cfname, clineno, type, fmt, ap);
+ ParseVErrorInternal(opts.debug_file, cfname, clineno, type,
+ fmt, ap);
va_end(ap);
}
}
-/*-
- * Parse_Error --
- * External interface to ParseErrorInternal; uses the default filename
- * Line number.
+/* External interface to ParseErrorInternal; uses the default filename and
+ * line number.
*
- * Results:
- * None
- *
- * Side Effects:
- * None
- */
-/* VARARGS */
+ * Fmt is given without a trailing newline. */
void
-Parse_Error(int type, const char *fmt, ...)
+Parse_Error(ParseErrorLevel type, const char *fmt, ...)
{
va_list ap;
const char *fname;
size_t lineno;
- if (curFile == NULL) {
+ if (includes.len == 0) {
fname = NULL;
lineno = 0;
} else {
+ IFile *curFile = CurFile();
fname = curFile->fname;
- lineno = curFile->lineno;
+ lineno = (size_t)curFile->lineno;
}
va_start(ap, fmt);
@@ -785,136 +739,95 @@ Parse_Error(int type, const char *fmt, ...)
ParseVErrorInternal(stderr, fname, lineno, type, fmt, ap);
va_end(ap);
- if (debug_file != stderr && debug_file != stdout) {
+ if (opts.debug_file != stderr && opts.debug_file != stdout) {
va_start(ap, fmt);
- ParseVErrorInternal(debug_file, fname, lineno, type, fmt, ap);
+ ParseVErrorInternal(opts.debug_file, fname, lineno, type,
+ fmt, ap);
va_end(ap);
}
}
-/*
- * ParseMessage
- * Parse a .info .warning or .error directive
+/* Parse a .info .warning or .error directive.
*
- * The input is the line minus the ".". We substitute
- * variables, print the message and exit(1) (for .error) or just print
- * a warning if the directive is malformed.
+ * The input is the line minus the ".". We substitute variables, print the
+ * message and exit(1) (for .error) or just print a warning if the directive
+ * is malformed.
*/
static Boolean
-ParseMessage(char *line)
+ParseMessage(const char *directive)
{
- int mtype;
+ const char *p = directive;
+ int mtype = *p == 'i' ? PARSE_INFO :
+ *p == 'w' ? PARSE_WARNING : PARSE_FATAL;
+ char *arg;
- switch(*line) {
- case 'i':
- mtype = PARSE_INFO;
- break;
- case 'w':
- mtype = PARSE_WARNING;
- break;
- case 'e':
- mtype = PARSE_FATAL;
- break;
- default:
- Parse_Error(PARSE_WARNING, "invalid syntax: \".%s\"", line);
- return FALSE;
- }
+ while (ch_isalpha(*p))
+ p++;
+ if (!ch_isspace(*p))
+ return FALSE; /* missing argument */
- while (isalpha((unsigned char)*line))
- line++;
- if (!isspace((unsigned char)*line))
- return FALSE; /* not for us */
- while (isspace((unsigned char)*line))
- line++;
+ cpp_skip_whitespace(&p);
+ (void)Var_Subst(p, VAR_CMDLINE, VARE_WANTRES, &arg);
+ /* TODO: handle errors */
- line = Var_Subst(line, VAR_CMD, VARE_WANTRES);
- Parse_Error(mtype, "%s", line);
- free(line);
+ Parse_Error(mtype, "%s", arg);
+ free(arg);
if (mtype == PARSE_FATAL) {
- /* Terminate immediately. */
+ PrintOnError(NULL, NULL);
exit(1);
}
return TRUE;
}
-/*-
- *---------------------------------------------------------------------
- * 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.
- *
- * Input:
- * pgnp The parent node
- * cgpn The child node
- *
- * Results:
- * Always = 0
+/* Add the child to the parent's children.
*
- * 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 int
-ParseLinkSrc(void *pgnp, void *cgnp)
+ * Additionally, add the parent to the child's parents, but only if the
+ * target is not special. An example for such a special target is .END,
+ * which does not need to be informed once the child target has been made. */
+static void
+LinkSource(GNode *pgn, GNode *cgn, Boolean isSpecial)
{
- GNode *pgn = (GNode *)pgnp;
- GNode *cgn = (GNode *)cgnp;
-
if ((pgn->type & OP_DOUBLEDEP) && !Lst_IsEmpty(pgn->cohorts))
- pgn = LstNode_Datum(Lst_Last(pgn->cohorts));
+ pgn = pgn->cohorts->last->datum;
+
Lst_Append(pgn->children, cgn);
- if (specType == Not)
+ pgn->unmade++;
+
+ /* Special targets like .END don't need any children. */
+ if (!isSpecial)
Lst_Append(cgn->parents, pgn);
- pgn->unmade += 1;
+
if (DEBUG(PARSE)) {
- fprintf(debug_file, "# %s: added child %s - %s\n", __func__,
- pgn->name, cgn->name);
+ debug_printf("# %s: added child %s - %s\n",
+ __func__, pgn->name, cgn->name);
Targ_PrintNode(pgn, 0);
Targ_PrintNode(cgn, 0);
}
- 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.
- *
- * Input:
- * gnp The node to which the operator is to be applied
- * opp The operator to apply
- *
- * Results:
- * Always 0
- *
- * Side Effects:
- * The type field of the node is altered to reflect any new bits in
- * the op.
- *---------------------------------------------------------------------
- */
-static int
-ParseDoOp(void *gnp, void *opp)
+/* Add the node to each target from the current dependency group. */
+static void
+LinkToTargets(GNode *gn, Boolean isSpecial)
+{
+ GNodeListNode *ln;
+ for (ln = targets->first; ln != NULL; ln = ln->next)
+ LinkSource(ln->datum, gn, isSpecial);
+}
+
+static Boolean
+TryApplyDependencyOperator(GNode *gn, GNodeType op)
{
- GNode *gn = (GNode *)gnp;
- int op = *(int *)opp;
/*
- * 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 the node occurred on the left-hand side of a dependency and the
+ * operator also defines a dependency, they must match.
*/
- if (((op & OP_OPMASK) != (gn->type & OP_OPMASK)) &&
- !OP_NOP(gn->type) && !OP_NOP(op))
+ if ((op & OP_OPMASK) && (gn->type & OP_OPMASK) &&
+ ((op & OP_OPMASK) != (gn->type & OP_OPMASK)))
{
Parse_Error(PARSE_FATAL, "Inconsistent operator for %s", gn->name);
- return 1;
+ return FALSE;
}
if (op == OP_DOUBLEDEP && (gn->type & OP_OPMASK) == OP_DOUBLEDEP) {
@@ -926,7 +839,7 @@ ParseDoOp(void *gnp, void *opp)
* and the new instance is linked to all parents of the initial
* instance.
*/
- GNode *cohort;
+ GNode *cohort;
/*
* Propagate copied bits to the initial node. They'll be propagated
@@ -934,7 +847,7 @@ ParseDoOp(void *gnp, void *opp)
*/
gn->type |= op & ~OP_OPMASK;
- cohort = Targ_FindNode(gn->name, TARG_NOHASH);
+ cohort = Targ_NewInternalNode(gn->name);
if (doing_depend)
ParseMark(cohort);
/*
@@ -947,9 +860,9 @@ ParseDoOp(void *gnp, void *opp)
cohort->type = op | OP_INVISIBLE;
Lst_Append(gn->cohorts, cohort);
cohort->centurion = gn;
- gn->unmade_cohorts += 1;
+ gn->unmade_cohorts++;
snprintf(cohort->cohort_num, sizeof cohort->cohort_num, "#%d",
- gn->unmade_cohorts);
+ (unsigned int)gn->unmade_cohorts % 1000000);
} else {
/*
* We don't want to nuke any previous flags (whatever they were) so we
@@ -958,47 +871,34 @@ ParseDoOp(void *gnp, void *opp)
gn->type |= op;
}
- return 0;
+ return TRUE;
}
-/*-
- *---------------------------------------------------------------------
- * 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'
- *
- * Input:
- * tOp operator (if any) from special targets
- * src name of the source to handle
- *
- * 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 void
-ParseDoSrc(int tOp, const char *src)
+ApplyDependencyOperator(GNodeType op)
+{
+ GNodeListNode *ln;
+ for (ln = targets->first; ln != NULL; ln = ln->next)
+ if (!TryApplyDependencyOperator(ln->datum, op))
+ break;
+}
+
+static Boolean
+ParseDoSrcKeyword(const char *src, ParseSpecial specType)
{
- GNode *gn = NULL;
static int wait_number = 0;
char wait_src[16];
+ GNode *gn;
- if (*src == '.' && isupper ((unsigned char)src[1])) {
+ if (*src == '.' && ch_isupper(src[1])) {
int keywd = ParseFindKeyword(src);
if (keywd != -1) {
int op = parseKeywords[keywd].op;
if (op != 0) {
- if (targets != NULL)
- Lst_ForEach(targets, ParseDoOp, &op);
- return;
+ ApplyDependencyOperator(op);
+ return TRUE;
}
- if (parseKeywords[keywd].spec == Wait) {
+ if (parseKeywords[keywd].spec == SP_WAIT) {
/*
* We add a .WAIT node in the dependency list.
* After any dynamic dependencies (and filename globbing)
@@ -1009,257 +909,591 @@ ParseDoSrc(int tOp, const char *src)
* We give each .WAIT node a unique name (mainly for diag).
*/
snprintf(wait_src, sizeof wait_src, ".WAIT_%u", ++wait_number);
- gn = Targ_FindNode(wait_src, TARG_NOHASH);
+ gn = Targ_NewInternalNode(wait_src);
if (doing_depend)
ParseMark(gn);
gn->type = OP_WAIT | OP_PHONY | OP_DEPENDS | OP_NOTMAIN;
- if (targets != NULL)
- Lst_ForEach(targets, ParseLinkSrc, gn);
- return;
+ LinkToTargets(gn, specType != SP_NOT);
+ return TRUE;
}
}
}
+ return FALSE;
+}
- switch (specType) {
- case 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...
- */
- Lst_Append(create, bmake_strdup(src));
- /*
- * Add the name to the .TARGETS variable as well, so the user can
- * employ that, if desired.
- */
- Var_Append(".TARGETS", src, VAR_GLOBAL);
+static void
+ParseDoSrcMain(const char *src)
+{
+ /*
+ * 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...
+ */
+ Lst_Append(opts.create, bmake_strdup(src));
+ /*
+ * Add the name to the .TARGETS variable as well, so the user can
+ * employ that, if desired.
+ */
+ Var_Append(".TARGETS", src, VAR_GLOBAL);
+}
+
+static void
+ParseDoSrcOrder(const char *src)
+{
+ GNode *gn;
+ /*
+ * Create proper predecessor/successor links between the previous
+ * source and the current one.
+ */
+ gn = Targ_GetNode(src);
+ if (doing_depend)
+ ParseMark(gn);
+ if (order_pred != NULL) {
+ Lst_Append(order_pred->order_succ, gn);
+ Lst_Append(gn->order_pred, order_pred);
+ if (DEBUG(PARSE)) {
+ debug_printf("# %s: added Order dependency %s - %s\n",
+ __func__, order_pred->name, gn->name);
+ Targ_PrintNode(order_pred, 0);
+ Targ_PrintNode(gn, 0);
+ }
+ }
+ /*
+ * The current source now becomes the predecessor for the next one.
+ */
+ order_pred = gn;
+}
+
+static void
+ParseDoSrcOther(const char *src, GNodeType tOp, ParseSpecial specType)
+{
+ GNode *gn;
+
+ /*
+ * 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.
+ */
+
+ /* Find/create the 'src' node and attach to all targets */
+ gn = Targ_GetNode(src);
+ if (doing_depend)
+ ParseMark(gn);
+ if (tOp) {
+ gn->type |= tOp;
+ } else {
+ LinkToTargets(gn, specType != SP_NOT);
+ }
+}
+
+/* Given the name of a source in a dependency line, figure out if it is an
+ * attribute (such as .SILENT) 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 (such as .PHONY) and apply it if so.
+ * Otherwise, make the source a child of the targets in the list 'targets'.
+ *
+ * Input:
+ * tOp operator (if any) from special targets
+ * src name of the source to handle
+ */
+static void
+ParseDoSrc(GNodeType tOp, const char *src, ParseSpecial specType)
+{
+ if (ParseDoSrcKeyword(src, specType))
return;
- case Order:
- /*
- * Create proper predecessor/successor links between the previous
- * source and the current one.
- */
- gn = Targ_FindNode(src, TARG_CREATE);
+ if (specType == SP_MAIN)
+ ParseDoSrcMain(src);
+ else if (specType == SP_ORDER)
+ ParseDoSrcOrder(src);
+ else
+ ParseDoSrcOther(src, tOp, specType);
+}
+
+/* If we have yet to decide on a main target to make, in the absence of any
+ * user input, we want the first target on the first dependency line that is
+ * actually a real target (i.e. isn't a .USE or .EXEC rule) to be made. */
+static void
+FindMainTarget(void)
+{
+ GNodeListNode *ln;
+
+ if (mainNode != NULL)
+ return;
+
+ for (ln = targets->first; ln != NULL; ln = ln->next) {
+ GNode *gn = ln->datum;
+ if (!(gn->type & OP_NOTARGET)) {
+ mainNode = gn;
+ Targ_SetMain(gn);
+ return;
+ }
+ }
+}
+
+/*
+ * We got to the end of the line while we were still looking at targets.
+ *
+ * Ending a dependency line without an operator is a Bozo no-no. As a
+ * heuristic, this is also often triggered by undetected conflicts from
+ * cvs/rcs merges.
+ */
+static void
+ParseErrorNoDependency(const char *lstart)
+{
+ if ((strncmp(lstart, "<<<<<<", 6) == 0) ||
+ (strncmp(lstart, "======", 6) == 0) ||
+ (strncmp(lstart, ">>>>>>", 6) == 0))
+ Parse_Error(PARSE_FATAL,
+ "Makefile appears to contain unresolved cvs/rcs/??? merge conflicts");
+ else if (lstart[0] == '.') {
+ const char *dirstart = lstart + 1;
+ const char *dirend;
+ cpp_skip_whitespace(&dirstart);
+ dirend = dirstart;
+ while (ch_isalnum(*dirend) || *dirend == '-')
+ dirend++;
+ Parse_Error(PARSE_FATAL, "Unknown directive \"%.*s\"",
+ (int)(dirend - dirstart), dirstart);
+ } else
+ Parse_Error(PARSE_FATAL, "Need an operator");
+}
+
+static void
+ParseDependencyTargetWord(/*const*/ char **pp, const char *lstart)
+{
+ /*const*/ char *cp = *pp;
+
+ while (*cp != '\0') {
+ if ((ch_isspace(*cp) || *cp == '!' || *cp == ':' || *cp == '(') &&
+ !ParseIsEscaped(lstart, cp))
+ break;
+
+ 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.
+ */
+ const char *nested_p = cp;
+ const char *nested_val;
+ void *freeIt;
+
+ (void)Var_Parse(&nested_p, VAR_CMDLINE, VARE_UNDEFERR|VARE_WANTRES,
+ &nested_val, &freeIt);
+ /* TODO: handle errors */
+ free(freeIt);
+ cp += nested_p - cp;
+ } else
+ cp++;
+ }
+
+ *pp = cp;
+}
+
+/*
+ * 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.
+ * .PHONY The list of targets
+ * .NOPATH Don't search for file in the path
+ * .STALE
+ * .BEGIN
+ * .END
+ * .ERROR
+ * .DELETE_ON_ERROR
+ * .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 NULL
+ */
+static void
+ParseDoDependencyTargetSpecial(ParseSpecial *inout_specType,
+ const char *line,
+ SearchPathList **inout_paths)
+{
+ switch (*inout_specType) {
+ case SP_PATH:
+ if (*inout_paths == NULL) {
+ *inout_paths = Lst_New();
+ }
+ Lst_Append(*inout_paths, dirSearchPath);
+ break;
+ case SP_MAIN:
+ if (!Lst_IsEmpty(opts.create)) {
+ *inout_specType = SP_NOT;
+ }
+ break;
+ case SP_BEGIN:
+ case SP_END:
+ case SP_STALE:
+ case SP_ERROR:
+ case SP_INTERRUPT: {
+ GNode *gn = Targ_GetNode(line);
if (doing_depend)
ParseMark(gn);
- if (predecessor != NULL) {
- Lst_Append(predecessor->order_succ, gn);
- Lst_Append(gn->order_pred, predecessor);
- if (DEBUG(PARSE)) {
- fprintf(debug_file, "# %s: added Order dependency %s - %s\n",
- __func__, predecessor->name, gn->name);
- Targ_PrintNode(predecessor, 0);
- Targ_PrintNode(gn, 0);
- }
+ gn->type |= OP_NOTMAIN|OP_SPECIAL;
+ Lst_Append(targets, gn);
+ break;
+ }
+ case SP_DEFAULT: {
+ GNode *gn = Targ_NewGN(".DEFAULT");
+ gn->type |= OP_NOTMAIN|OP_TRANSFORM;
+ Lst_Append(targets, gn);
+ DEFAULT = gn;
+ break;
+ }
+ case SP_DELETE_ON_ERROR:
+ deleteOnError = TRUE;
+ break;
+ case SP_NOTPARALLEL:
+ opts.maxJobs = 1;
+ break;
+ case SP_SINGLESHELL:
+ opts.compatMake = TRUE;
+ break;
+ case SP_ORDER:
+ order_pred = NULL;
+ break;
+ default:
+ break;
+ }
+}
+
+/*
+ * .PATH<suffix> has to be handled specially.
+ * Call on the suffix module to give us a path to modify.
+ */
+static Boolean
+ParseDoDependencyTargetPath(const char *line, SearchPathList **inout_paths)
+{
+ SearchPath *path;
+
+ path = Suff_GetPath(&line[5]);
+ if (path == NULL) {
+ Parse_Error(PARSE_FATAL,
+ "Suffix '%s' not defined (yet)",
+ &line[5]);
+ return FALSE;
+ } else {
+ if (*inout_paths == NULL) {
+ *inout_paths = Lst_New();
}
+ Lst_Append(*inout_paths, path);
+ }
+ return TRUE;
+}
+
+/*
+ * See if it's a special target and if so set specType to match it.
+ */
+static Boolean
+ParseDoDependencyTarget(const char *line, ParseSpecial *inout_specType,
+ GNodeType *out_tOp, SearchPathList **inout_paths)
+{
+ int keywd;
+
+ if (!(*line == '.' && ch_isupper(line[1])))
+ return TRUE;
+
+ /*
+ * See if the target is a special target that must have it
+ * or its sources handled specially.
+ */
+ keywd = ParseFindKeyword(line);
+ if (keywd != -1) {
+ if (*inout_specType == SP_PATH && parseKeywords[keywd].spec != SP_PATH) {
+ Parse_Error(PARSE_FATAL, "Mismatched special targets");
+ return FALSE;
+ }
+
+ *inout_specType = parseKeywords[keywd].spec;
+ *out_tOp = parseKeywords[keywd].op;
+
+ ParseDoDependencyTargetSpecial(inout_specType, line, inout_paths);
+
+ } else if (strncmp(line, ".PATH", 5) == 0) {
+ *inout_specType = SP_PATH;
+ if (!ParseDoDependencyTargetPath(line, inout_paths))
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static void
+ParseDoDependencyTargetMundane(char *line, StringList *curTargs)
+{
+ if (Dir_HasWildcards(line)) {
/*
- * The current source now becomes the predecessor for the next one.
+ * 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...
*/
- predecessor = gn;
- break;
+ SearchPath *emptyPath = Lst_New();
- default:
+ Dir_Expand(line, emptyPath, curTargs);
+
+ Lst_Destroy(emptyPath, Dir_Destroy);
+ } 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.
+ * No wildcards, but we want to avoid code duplication,
+ * so create a list with the word on it.
*/
+ Lst_Append(curTargs, line);
+ }
+
+ /* Apply the targets. */
- /* Find/create the 'src' node and attach to all targets */
- gn = Targ_FindNode(src, TARG_CREATE);
+ while (!Lst_IsEmpty(curTargs)) {
+ char *targName = Lst_Dequeue(curTargs);
+ GNode *gn = Suff_IsTransform(targName)
+ ? Suff_AddTransform(targName)
+ : Targ_GetNode(targName);
if (doing_depend)
ParseMark(gn);
- if (tOp) {
- gn->type |= tOp;
- } else {
- if (targets != NULL)
- Lst_ForEach(targets, ParseLinkSrc, gn);
+
+ Lst_Append(targets, gn);
+ }
+}
+
+static void
+ParseDoDependencyTargetExtraWarn(char **pp, const char *lstart)
+{
+ Boolean warning = FALSE;
+ char *cp = *pp;
+
+ while (*cp && (ParseIsEscaped(lstart, cp) ||
+ (*cp != '!' && *cp != ':'))) {
+ if (ParseIsEscaped(lstart, cp) ||
+ (*cp != ' ' && *cp != '\t')) {
+ warning = TRUE;
}
+ cp++;
+ }
+ if (warning) {
+ Parse_Error(PARSE_WARNING, "Extra target ignored");
+ }
+ *pp = cp;
+}
+
+static void
+ParseDoDependencyCheckSpec(ParseSpecial specType)
+{
+ switch (specType) {
+ default:
+ Parse_Error(PARSE_WARNING,
+ "Special and mundane targets don't mix. Mundane ones ignored");
+ break;
+ case SP_DEFAULT:
+ case SP_STALE:
+ case SP_BEGIN:
+ case SP_END:
+ case SP_ERROR:
+ case SP_INTERRUPT:
+ /*
+ * These four create nodes on which to hang commands, so
+ * targets shouldn't be empty...
+ */
+ case SP_NOT:
+ /*
+ * Nothing special here -- targets can be empty if it wants.
+ */
break;
}
}
-/*-
- *-----------------------------------------------------------------------
- * 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.
- *
- * Input:
- * gnp Node to examine
- *
- * Results:
- * 0 if main not found yet, 1 if it is.
- *
- * Side Effects:
- * mainNode is changed and Targ_SetMain is called.
- *
- *-----------------------------------------------------------------------
- */
-static int
-ParseFindMain(void *gnp, void *dummy MAKE_ATTR_UNUSED)
+static Boolean
+ParseDoDependencyParseOp(char **pp, const char *lstart, GNodeType *out_op)
{
- GNode *gn = (GNode *)gnp;
- if (!(gn->type & OP_NOTARGET)) {
- mainNode = gn;
- Targ_SetMain(gn);
- return 1;
- } else {
- return 0;
+ const char *cp = *pp;
+
+ if (*cp == '!') {
+ *out_op = OP_FORCE;
+ (*pp)++;
+ return TRUE;
+ }
+
+ if (*cp == ':') {
+ if (cp[1] == ':') {
+ *out_op = OP_DOUBLEDEP;
+ (*pp) += 2;
+ } else {
+ *out_op = OP_DEPENDS;
+ (*pp)++;
+ }
+ return TRUE;
+ }
+
+ {
+ const char *msg = lstart[0] == '.' ? "Unknown directive"
+ : "Missing dependency operator";
+ Parse_Error(PARSE_FATAL, "%s", msg);
+ return FALSE;
}
}
-/*-
- *-----------------------------------------------------------------------
- * ParseAddDir --
- * Front-end for Dir_AddDir to make sure Lst_ForEach keeps going
- *
- * Results:
- * === 0
- *
- * Side Effects:
- * See Dir_AddDir.
- *
- *-----------------------------------------------------------------------
- */
-static int
-ParseAddDir(void *path, void *name)
+static void
+ClearPaths(SearchPathList *paths)
{
- (void)Dir_AddDir((Lst) path, (char *)name);
- return 0;
+ if (paths != NULL) {
+ SearchPathListNode *ln;
+ for (ln = paths->first; ln != NULL; ln = ln->next)
+ Dir_ClearPath(ln->datum);
+ }
+
+ Dir_SetPATH();
}
-/*-
- *-----------------------------------------------------------------------
- * ParseClearPath --
- * Front-end for Dir_ClearPath to make sure Lst_ForEach keeps going
- *
- * Results:
- * === 0
- *
- * Side Effects:
- * See Dir_ClearPath
- *
- *-----------------------------------------------------------------------
- */
-static int
-ParseClearPath(void *path, void *dummy MAKE_ATTR_UNUSED)
+static void
+ParseDoDependencySourcesEmpty(ParseSpecial specType, SearchPathList *paths)
{
- Dir_ClearPath((Lst) path);
- return 0;
+ switch (specType) {
+ case SP_SUFFIXES:
+ Suff_ClearSuffixes();
+ break;
+ case SP_PRECIOUS:
+ allPrecious = TRUE;
+ break;
+ case SP_IGNORE:
+ opts.ignoreErrors = TRUE;
+ break;
+ case SP_SILENT:
+ opts.beSilent = TRUE;
+ break;
+ case SP_PATH:
+ ClearPaths(paths);
+ break;
+#ifdef POSIX
+ case SP_POSIX:
+ Var_Set("%POSIX", "1003.2", VAR_GLOBAL);
+ break;
+#endif
+ default:
+ break;
+ }
}
-/*-
- *---------------------------------------------------------------------
- * ParseDoDependency --
- * Parse the dependency line in line.
+static void
+AddToPaths(const char *dir, SearchPathList *paths)
+{
+ if (paths != NULL) {
+ SearchPathListNode *ln;
+ for (ln = paths->first; ln != NULL; ln = ln->next)
+ (void)Dir_AddDir(ln->datum, dir);
+ }
+}
+
+/*
+ * If the target was one that doesn't take files as its sources
+ * but takes something like suffixes, we take each
+ * space-separated word on the line as a something and deal
+ * with it accordingly.
*
- * Input:
- * line the line to parse
+ * If the target was .SUFFIXES, we take each source as a
+ * suffix and add it to the list of suffixes maintained by the
+ * Suff module.
*
- * Results:
- * None
+ * If the target was a .PATH, we add the source as a directory
+ * to search on the search path.
*
- * Side Effects:
- * The nodes of the sources are linked as children to the nodes of the
- * targets. Some nodes may be created.
+ * If it was .INCLUDES, the source is taken to be the suffix of
+ * files which will be #included and whose search path should
+ * be present in the .INCLUDES variable.
*
- * 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.
- *---------------------------------------------------------------------
+ * If it was .LIBS, the source is taken to be the suffix of
+ * files which are considered libraries and whose search path
+ * should be present in the .LIBS variable.
+ *
+ * If it was .NULL, the source is the suffix to use when a file
+ * has no valid suffix.
+ *
+ * If it was .OBJDIR, the source is a new definition for .OBJDIR,
+ * and will cause make to do a new chdir to that path.
*/
static void
-ParseDoDependency(char *line)
+ParseDoDependencySourceSpecial(ParseSpecial specType, char *word,
+ SearchPathList *paths)
{
- char *cp; /* our current position */
- GNode *gn = NULL; /* a general purpose temporary node */
- 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 archive source names after
- * expansion */
- Lst curTargs; /* list of target names to be found and added
- * to the targets list */
- char *lstart = line;
-
- if (DEBUG(PARSE))
- fprintf(debug_file, "ParseDoDependency(%s)\n", line);
- tOp = 0;
-
- specType = Not;
- paths = NULL;
-
- curTargs = Lst_Init();
+ switch (specType) {
+ case SP_SUFFIXES:
+ Suff_AddSuffix(word, &mainNode);
+ break;
+ case SP_PATH:
+ AddToPaths(word, paths);
+ break;
+ case SP_INCLUDES:
+ Suff_AddInclude(word);
+ break;
+ case SP_LIBS:
+ Suff_AddLib(word);
+ break;
+ case SP_NULL:
+ Suff_SetNull(word);
+ break;
+ case SP_OBJDIR:
+ Main_SetObjdir("%s", word);
+ break;
+ default:
+ break;
+ }
+}
- /*
- * First, grind through the targets.
- */
+static Boolean
+ParseDoDependencyTargets(char **inout_cp,
+ char **inout_line,
+ const char *lstart,
+ ParseSpecial *inout_specType,
+ GNodeType *inout_tOp,
+ SearchPathList **inout_paths,
+ StringList *curTargs)
+{
+ char *cp = *inout_cp;
+ char *line = *inout_line;
+ char savec;
- do {
+ for (;;) {
/*
* Here LINE points to the beginning of the next word, and
* LSTART points to the actual beginning of the line.
*/
/* Find the end of the next word. */
- for (cp = line; *cp && (ParseIsEscaped(lstart, cp) ||
- !(isspace((unsigned char)*cp) ||
- *cp == '!' || *cp == ':' || *cp == LPAREN));
- 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;
- void *freeIt;
-
- (void)Var_Parse(cp, VAR_CMD, VARE_UNDEFERR|VARE_WANTRES,
- &length, &freeIt);
- free(freeIt);
- cp += length - 1;
- }
- }
+ cp = line;
+ ParseDependencyTargetWord(&cp, lstart);
/*
* If the word is followed by a left parenthesis, it's the
* name of an object file inside an archive (ar file).
*/
- if (!ParseIsEscaped(lstart, cp) && *cp == LPAREN) {
+ if (!ParseIsEscaped(lstart, cp) && *cp == '(') {
/*
* Archives must be handled specially to make sure the OP_ARCHV
* flag is set in their 'type' field, for one thing, and because
@@ -1270,10 +1504,10 @@ ParseDoDependency(char *line)
* went well and FALSE if there was an error in the
* specification. On error, line should remain untouched.
*/
- if (!Arch_ParseArchive(&line, targets, VAR_CMD)) {
+ if (!Arch_ParseArchive(&line, targets, VAR_CMDLINE)) {
Parse_Error(PARSE_FATAL,
- "Error in archive specification: \"%s\"", line);
- goto out;
+ "Error in archive specification: \"%s\"", line);
+ return FALSE;
} else {
/* Done with this word; on to the next. */
cp = line;
@@ -1282,198 +1516,25 @@ ParseDoDependency(char *line)
}
if (!*cp) {
- /*
- * We got to the end of the line while we were still
- * looking at targets.
- *
- * Ending a dependency line without an operator is a Bozo
- * no-no. As a heuristic, this is also often triggered by
- * undetected conflicts from cvs/rcs merges.
- */
- if ((strncmp(line, "<<<<<<", 6) == 0) ||
- (strncmp(line, "======", 6) == 0) ||
- (strncmp(line, ">>>>>>", 6) == 0))
- Parse_Error(PARSE_FATAL,
- "Makefile appears to contain unresolved cvs/rcs/??? merge conflicts");
- else if (lstart[0] == '.') {
- const char *dirstart = lstart + 1;
- const char *dirend;
- while (isspace((unsigned char)*dirstart))
- dirstart++;
- dirend = dirstart;
- while (isalnum((unsigned char)*dirend) || *dirend == '-')
- dirend++;
- Parse_Error(PARSE_FATAL, "Unknown directive \"%.*s\"",
- (int)(dirend - dirstart), dirstart);
- } else
- Parse_Error(PARSE_FATAL, "Need an operator");
- goto out;
+ ParseErrorNoDependency(lstart);
+ return FALSE;
}
/* Insert a null terminator. */
savec = *cp;
*cp = '\0';
- /*
- * Got the word. See if it's a special target and if so set
- * specType to match it.
- */
- if (*line == '.' && isupper ((unsigned char)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");
- goto out;
- }
-
- 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.
- * .PHONY The list of targets
- * .NOPATH Don't search for file in the path
- * .STALE
- * .BEGIN
- * .END
- * .ERROR
- * .DELETE_ON_ERROR
- * .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 NULL
- */
- switch (specType) {
- case ExPath:
- if (paths == NULL) {
- paths = Lst_Init();
- }
- Lst_Append(paths, dirSearchPath);
- break;
- case Main:
- if (!Lst_IsEmpty(create)) {
- specType = Not;
- }
- break;
- case Begin:
- case End:
- case Stale:
- case dotError:
- case Interrupt:
- gn = Targ_FindNode(line, TARG_CREATE);
- if (doing_depend)
- ParseMark(gn);
- gn->type |= OP_NOTMAIN|OP_SPECIAL;
- Lst_Append(targets, gn);
- break;
- case Default:
- gn = Targ_NewGN(".DEFAULT");
- gn->type |= (OP_NOTMAIN|OP_TRANSFORM);
- Lst_Append(targets, gn);
- DEFAULT = gn;
- break;
- case DeleteOnError:
- deleteOnError = TRUE;
- break;
- case NotParallel:
- maxJobs = 1;
- break;
- case SingleShell:
- compatMake = TRUE;
- break;
- case Order:
- predecessor = NULL;
- 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 == NULL) {
- Parse_Error(PARSE_FATAL,
- "Suffix '%s' not defined (yet)",
- &line[5]);
- goto out;
- } else {
- if (paths == NULL) {
- paths = Lst_Init();
- }
- Lst_Append(paths, path);
- }
- }
- }
+ if (!ParseDoDependencyTarget(line, inout_specType, inout_tOp,
+ inout_paths))
+ return FALSE;
/*
* 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();
-
- 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.
- */
- Lst_Append(curTargs, line);
- }
-
- /* Apply the targets. */
-
- while(!Lst_IsEmpty(curTargs)) {
- char *targName = Lst_Dequeue(curTargs);
-
- if (!Suff_IsTransform (targName)) {
- gn = Targ_FindNode(targName, TARG_CREATE);
- } else {
- gn = Suff_AddTransform(targName);
- }
- if (doing_depend)
- ParseMark(gn);
-
- Lst_Append(targets, gn);
- }
- } else if (specType == ExPath && *line != '.' && *line != '\0') {
+ if (*inout_specType == SP_NOT && *line != '\0') {
+ ParseDoDependencyTargetMundane(line, curTargs);
+ } else if (*inout_specType == SP_PATH && *line != '.' && *line != '\0') {
Parse_Error(PARSE_WARNING, "Extra target (%s) ignored", line);
}
@@ -1484,28 +1545,150 @@ ParseDoDependency(char *line)
* 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 warning = FALSE;
-
- while (*cp && (ParseIsEscaped(lstart, cp) ||
- (*cp != '!' && *cp != ':'))) {
- if (ParseIsEscaped(lstart, cp) ||
- (*cp != ' ' && *cp != '\t')) {
- warning = TRUE;
- }
- cp++;
+ if (*inout_specType != SP_NOT && *inout_specType != SP_PATH) {
+ ParseDoDependencyTargetExtraWarn(&cp, lstart);
+ } else {
+ pp_skip_whitespace(&cp);
+ }
+ line = cp;
+ if (*line == '\0')
+ break;
+ if ((*line == '!' || *line == ':') && !ParseIsEscaped(lstart, line))
+ break;
+ }
+
+ *inout_cp = cp;
+ *inout_line = line;
+ return TRUE;
+}
+
+static void
+ParseDoDependencySourcesSpecial(char *start, char *end,
+ ParseSpecial specType, SearchPathList *paths)
+{
+ char savec;
+
+ while (*start) {
+ while (*end && !ch_isspace(*end))
+ end++;
+ savec = *end;
+ *end = '\0';
+ ParseDoDependencySourceSpecial(specType, start, paths);
+ *end = savec;
+ if (savec != '\0')
+ end++;
+ pp_skip_whitespace(&end);
+ start = end;
+ }
+}
+
+static Boolean
+ParseDoDependencySourcesMundane(char *start, char *end,
+ ParseSpecial specType, GNodeType tOp)
+{
+ while (*start) {
+ /*
+ * The targets take real sources, so we must beware of archive
+ * specifications (i.e. things with left parentheses in them)
+ * and handle them accordingly.
+ */
+ for (; *end && !ch_isspace(*end); end++) {
+ if (*end == '(' && end > start && end[-1] != '$') {
+ /*
+ * Only stop for a left parenthesis if it isn't at the
+ * start of a word (that'll be for variable changes
+ * later) and isn't preceded by a dollar sign (a dynamic
+ * source).
+ */
+ break;
+ }
+ }
+
+ if (*end == '(') {
+ GNodeList *sources = Lst_New();
+ if (!Arch_ParseArchive(&start, sources, VAR_CMDLINE)) {
+ Parse_Error(PARSE_FATAL,
+ "Error in source archive spec \"%s\"", start);
+ return FALSE;
}
- if (warning) {
- Parse_Error(PARSE_WARNING, "Extra target ignored");
+
+ while (!Lst_IsEmpty(sources)) {
+ GNode *gn = Lst_Dequeue(sources);
+ ParseDoSrc(tOp, gn->name, specType);
}
+ Lst_Free(sources);
+ end = start;
} else {
- while (*cp && isspace ((unsigned char)*cp)) {
- cp++;
+ if (*end) {
+ *end = '\0';
+ end++;
}
+
+ ParseDoSrc(tOp, start, specType);
}
- line = cp;
- } while (*line && (ParseIsEscaped(lstart, line) ||
- (*line != '!' && *line != ':')));
+ pp_skip_whitespace(&end);
+ start = end;
+ }
+ return TRUE;
+}
+
+/* Parse a dependency line consisting of targets, followed by a dependency
+ * operator, optionally followed by sources.
+ *
+ * The nodes of the sources are linked as children to the nodes of the
+ * targets. Nodes are created as necessary.
+ *
+ * The operator is applied to each node in the global 'targets' list,
+ * which is where the nodes found for the targets are kept, by means of
+ * the ParseDoOp function.
+ *
+ * The sources are parsed in much the same way as the targets, except
+ * that 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 and sources such as .PHONY or .PRECIOUS are handled
+ * specially. These are the ones detailed by the specType variable.
+ *
+ * The storing of transformation rules such as '.c.o' 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 void
+ParseDoDependency(char *line)
+{
+ char *cp; /* our current position */
+ GNodeType op; /* the operator on the line */
+ SearchPathList *paths; /* search paths to alter when parsing
+ * a list of .PATH targets */
+ int tOp; /* operator from special target */
+ StringList *curTargs; /* target names to be found and added
+ * to the targets list */
+ char *lstart = line;
+
+ /*
+ * specType contains the SPECial TYPE of the current target. It is SP_NOT
+ * if the target is unspecial. If it *is* special, however, the children
+ * are linked as children of the parent but not vice versa.
+ */
+ ParseSpecial specType = SP_NOT;
+
+ DEBUG1(PARSE, "ParseDoDependency(%s)\n", line);
+ tOp = 0;
+
+ paths = NULL;
+
+ curTargs = Lst_New();
+
+ /*
+ * First, grind through the targets.
+ */
+ if (!ParseDoDependencyTargets(&cp, &line, lstart, &specType, &tOp, &paths,
+ curTargs))
+ goto out;
/*
* Don't need the list of target names anymore...
@@ -1513,58 +1696,21 @@ ParseDoDependency(char *line)
Lst_Free(curTargs);
curTargs = NULL;
- if (targets != NULL && !Lst_IsEmpty(targets)) {
- switch(specType) {
- default:
- Parse_Error(PARSE_WARNING, "Special and mundane targets don't mix. Mundane ones ignored");
- break;
- case Default:
- case Stale:
- case Begin:
- case End:
- case dotError:
- 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;
- }
- }
+ if (!Lst_IsEmpty(targets))
+ ParseDoDependencyCheckSpec(specType);
/*
- * Have now parsed all the target names. Must parse the operator next. The
- * result is left in op .
+ * Have now parsed all the target names. Must parse the operator next.
*/
- if (*cp == '!') {
- op = OP_FORCE;
- } else if (*cp == ':') {
- if (cp[1] == ':') {
- op = OP_DOUBLEDEP;
- cp++;
- } else {
- op = OP_DEPENDS;
- }
- } else {
- Parse_Error(PARSE_FATAL, lstart[0] == '.' ? "Unknown directive"
- : "Missing dependency operator");
+ if (!ParseDoDependencyParseOp(&cp, lstart, &op))
goto out;
- }
-
- /* Advance beyond the operator */
- cp++;
/*
* Apply the operator to the target. This is how we remember which
* operator a target was defined with. It fails if the operator
* used isn't consistent across all references.
*/
- if (targets != NULL)
- Lst_ForEach(targets, ParseDoOp, &op);
+ ApplyDependencyOperator(op);
/*
* Onward to the sources.
@@ -1572,9 +1718,7 @@ ParseDoDependency(char *line)
* LINE will now point to the first source word, if any, or the
* end of the string if not.
*/
- while (*cp && isspace ((unsigned char)*cp)) {
- cp++;
- }
+ pp_skip_whitespace(&cp);
line = cp;
/*
@@ -1587,33 +1731,8 @@ ParseDoDependency(char *line)
* 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:
- if (paths != NULL)
- Lst_ForEach(paths, ParseClearPath, NULL);
- Dir_SetPATH();
- break;
-#ifdef POSIX
- case Posix:
- Var_Set("%POSIX", "1003.2", VAR_GLOBAL);
- break;
-#endif
- default:
- break;
- }
- } else if (specType == MFlags) {
+ ParseDoDependencySourcesEmpty(specType, paths);
+ } else if (specType == SP_MFLAGS) {
/*
* Call on functions in main.c to deal with these arguments and
* set the initial character to a null-character so the loop to
@@ -1621,153 +1740,38 @@ ParseDoDependency(char *line)
*/
Main_ParseArgLine(line);
*line = '\0';
- } else if (specType == ExShell) {
+ } else if (specType == SP_SHELL) {
if (!Job_ParseShell(line)) {
Parse_Error(PARSE_FATAL, "improper shell specification");
goto out;
}
*line = '\0';
- } else if (specType == NotParallel || specType == SingleShell ||
- specType == DeleteOnError) {
+ } else if (specType == SP_NOTPARALLEL || specType == SP_SINGLESHELL ||
+ specType == SP_DELETE_ON_ERROR) {
*line = '\0';
}
/*
* NOW GO FOR THE SOURCES
*/
- if (specType == Suffixes || specType == ExPath ||
- specType == Includes || specType == Libs ||
- specType == Null || specType == ExObjdir)
+ if (specType == SP_SUFFIXES || specType == SP_PATH ||
+ specType == SP_INCLUDES || specType == SP_LIBS ||
+ specType == SP_NULL || specType == SP_OBJDIR)
{
- while (*line) {
- /*
- * If the target was one that doesn't take files as its sources
- * but takes something like suffixes, we take each
- * space-separated word on the line as a something and deal
- * with it accordingly.
- *
- * If the target was .SUFFIXES, we take each source as a
- * suffix and add it to the list of suffixes maintained by the
- * Suff module.
- *
- * If the target was a .PATH, we add the source as a directory
- * to search on the search path.
- *
- * If it was .INCLUDES, the source is taken to be the suffix of
- * files which will be #included and whose search path should
- * be present in the .INCLUDES variable.
- *
- * If it was .LIBS, the source is taken to be the suffix of
- * files which are considered libraries and whose search path
- * should be present in the .LIBS variable.
- *
- * If it was .NULL, the source is the suffix to use when a file
- * has no valid suffix.
- *
- * If it was .OBJDIR, the source is a new definition for .OBJDIR,
- * and will cause make to do a new chdir to that path.
- */
- while (*cp && !isspace ((unsigned char)*cp)) {
- cp++;
- }
- savec = *cp;
- *cp = '\0';
- switch (specType) {
- case Suffixes:
- Suff_AddSuffix(line, &mainNode);
- break;
- case ExPath:
- if (paths != NULL)
- Lst_ForEach(paths, ParseAddDir, line);
- break;
- case Includes:
- Suff_AddInclude(line);
- break;
- case Libs:
- Suff_AddLib(line);
- break;
- case Null:
- Suff_SetNull(line);
- break;
- case ExObjdir:
- Main_SetObjdir("%s", line);
- break;
- default:
- break;
- }
- *cp = savec;
- if (savec != '\0') {
- cp++;
- }
- while (*cp && isspace ((unsigned char)*cp)) {
- cp++;
- }
- line = cp;
- }
+ ParseDoDependencySourcesSpecial(line, cp, specType, paths);
if (paths) {
Lst_Free(paths);
paths = NULL;
}
- if (specType == ExPath)
+ if (specType == SP_PATH)
Dir_SetPATH();
} else {
assert(paths == NULL);
- while (*line) {
- /*
- * The targets take real sources, so we must beware of archive
- * specifications (i.e. things with left parentheses in them)
- * and handle them accordingly.
- */
- for (; *cp && !isspace ((unsigned char)*cp); cp++) {
- if (*cp == LPAREN && cp > line && cp[-1] != '$') {
- /*
- * Only stop for a left parenthesis if it isn't at the
- * start of a word (that'll be for variable changes
- * later) and isn't preceded by a dollar sign (a dynamic
- * source).
- */
- break;
- }
- }
-
- if (*cp == LPAREN) {
- sources = Lst_Init();
- if (!Arch_ParseArchive(&line, sources, VAR_CMD)) {
- Parse_Error(PARSE_FATAL,
- "Error in source archive spec \"%s\"", line);
- goto out;
- }
-
- while (!Lst_IsEmpty(sources)) {
- gn = Lst_Dequeue(sources);
- ParseDoSrc(tOp, gn->name);
- }
- Lst_Free(sources);
- cp = line;
- } else {
- if (*cp) {
- *cp = '\0';
- cp += 1;
- }
-
- ParseDoSrc(tOp, line);
- }
- while (*cp && isspace ((unsigned char)*cp)) {
- cp++;
- }
- line = cp;
- }
+ if (!ParseDoDependencySourcesMundane(line, cp, specType, tOp))
+ goto out;
}
- if (mainNode == NULL && targets != NULL) {
- /*
- * If we have yet to decide on a main target to make, in the
- * absence of any user input, we want the first target on
- * the first dependency line that is actually a real target
- * (i.e. isn't a .USE or .EXEC rule) to be made.
- */
- Lst_ForEach(targets, ParseFindMain, NULL);
- }
+ FindMainTarget();
out:
if (paths != NULL)
@@ -1776,42 +1780,94 @@ out:
Lst_Free(curTargs);
}
-/*-
- *---------------------------------------------------------------------
- * Parse_IsVar --
- * Return TRUE if the passed line is a variable assignment. A variable
- * assignment consists of a single word followed by optional whitespace
- * followed by either a += or an = operator.
- * This function is used both by the Parse_File function and main when
- * parsing the command-line arguments.
- *
- * Input:
- * line the line to check
+typedef struct VarAssignParsed {
+ const char *nameStart; /* unexpanded */
+ const char *nameEnd; /* before operator adjustment */
+ const char *eq; /* the '=' of the assignment operator */
+} VarAssignParsed;
+
+/* Determine the assignment operator and adjust the end of the variable
+ * name accordingly. */
+static void
+AdjustVarassignOp(const VarAssignParsed *pvar, const char *value,
+ VarAssign *out_var)
+{
+ const char *op = pvar->eq;
+ const char * const name = pvar->nameStart;
+ VarAssignOp type;
+
+ if (op > name && op[-1] == '+') {
+ type = VAR_APPEND;
+ op--;
+
+ } else if (op > name && op[-1] == '?') {
+ op--;
+ type = VAR_DEFAULT;
+
+ } else if (op > name && op[-1] == ':') {
+ op--;
+ type = VAR_SUBST;
+
+ } else if (op > name && op[-1] == '!') {
+ op--;
+ type = VAR_SHELL;
+
+ } else {
+ type = VAR_NORMAL;
+#ifdef SUNSHCMD
+ while (op > name && ch_isspace(op[-1]))
+ op--;
+
+ if (op >= name + 3 && op[-3] == ':' && op[-2] == 's' && op[-1] == 'h') {
+ type = VAR_SHELL;
+ op -= 3;
+ }
+#endif
+ }
+
+ {
+ const char *nameEnd = pvar->nameEnd < op ? pvar->nameEnd : op;
+ out_var->varname = bmake_strsedup(pvar->nameStart, nameEnd);
+ out_var->op = type;
+ out_var->value = value;
+ }
+}
+
+/* Parse a variable assignment, consisting of a single-word variable name,
+ * optional whitespace, an assignment operator, optional whitespace and the
+ * variable value.
*
- * Results:
- * TRUE if it is. FALSE if it ain't
+ * Note: There is a lexical ambiguity with assignment modifier characters
+ * in variable names. This routine interprets the character before the =
+ * as a modifier. Therefore, an assignment like
+ * C++=/usr/bin/CC
+ * is interpreted as "C+ +=" instead of "C++ =".
*
- * Side Effects:
- * none
- *---------------------------------------------------------------------
- */
+ * Used for both lines in a file and command line arguments. */
Boolean
-Parse_IsVar(char *line)
+Parse_IsVar(const char *p, VarAssign *out_var)
{
- Boolean wasSpace = FALSE; /* set TRUE if found a space */
+ VarAssignParsed pvar;
+ const char *firstSpace = NULL;
char ch;
int level = 0;
-#define ISEQOPERATOR(c) \
- (((c) == '+') || ((c) == ':') || ((c) == '?') || ((c) == '!'))
- /*
- * Skip to variable name
- */
- while (*line == ' ' || *line == '\t')
- line++;
+ /* Skip to variable name */
+ while (*p == ' ' || *p == '\t')
+ p++;
+
+ /* During parsing, the '+' of the '+=' operator is initially parsed
+ * as part of the variable name. It is later corrected, as is the ':sh'
+ * modifier. Of these two (nameEnd and op), the earlier one determines the
+ * actual end of the variable name. */
+ pvar.nameStart = p;
+#ifdef CLEANUP
+ pvar.nameEnd = NULL;
+ pvar.eq = NULL;
+#endif
/* Scan for one of the assignment operators outside a variable expansion */
- while ((ch = *line++) != 0) {
+ while ((ch = *p++) != 0) {
if (ch == '(' || ch == '{') {
level++;
continue;
@@ -1820,233 +1876,200 @@ Parse_IsVar(char *line)
level--;
continue;
}
+
if (level != 0)
continue;
- while (ch == ' ' || ch == '\t') {
- ch = *line++;
- wasSpace = TRUE;
- }
+
+ if (ch == ' ' || ch == '\t')
+ if (firstSpace == NULL)
+ firstSpace = p - 1;
+ while (ch == ' ' || ch == '\t')
+ ch = *p++;
+
#ifdef SUNSHCMD
- if (ch == ':' && strncmp(line, "sh", 2) == 0) {
- line += 2;
+ if (ch == ':' && strncmp(p, "sh", 2) == 0) {
+ p += 2;
continue;
}
#endif
- if (ch == '=')
+ if (ch == '=') {
+ pvar.eq = p - 1;
+ pvar.nameEnd = firstSpace != NULL ? firstSpace : p - 1;
+ cpp_skip_whitespace(&p);
+ AdjustVarassignOp(&pvar, p, out_var);
return TRUE;
- if (*line == '=' && ISEQOPERATOR(ch))
+ }
+ if (*p == '=' && (ch == '+' || ch == ':' || ch == '?' || ch == '!')) {
+ pvar.eq = p;
+ pvar.nameEnd = firstSpace != NULL ? firstSpace : p;
+ p++;
+ cpp_skip_whitespace(&p);
+ AdjustVarassignOp(&pvar, p, out_var);
return TRUE;
- if (wasSpace)
+ }
+ if (firstSpace != NULL)
return FALSE;
}
return FALSE;
}
-/*-
- *---------------------------------------------------------------------
- * Parse_DoVar --
- * Take the variable assignment in the passed line and do it in the
- * global context.
- *
- * Note: There is a lexical ambiguity with assignment modifier characters
- * in variable names. This routine interprets the character before the =
- * as a modifier. Therefore, an assignment like
- * C++=/usr/bin/CC
- * is interpreted as "C+ +=" instead of "C++ =".
- *
- * Input:
- * line a line guaranteed to be a variable assignment.
- * This reduces error checks
- * ctxt Context in which to do the assignment
- *
- * Results:
- * none
- *
- * Side Effects:
- * the variable structure of the given variable name is altered in the
- * global context.
- *---------------------------------------------------------------------
- */
-void
-Parse_DoVar(char *line, GNode *ctxt)
-{
- char *cp; /* pointer into line */
- enum {
- VAR_SUBST, VAR_APPEND, VAR_SHELL, VAR_NORMAL
- } type; /* Type of assignment */
- char *opc; /* ptr to operator character to
- * null-terminate the variable name */
- Boolean freeCp = FALSE; /* TRUE if cp needs to be freed,
- * i.e. if any variable expansion was
- * performed */
- int depth;
-
- /*
- * Skip to variable name
- */
- while (*line == ' ' || *line == '\t')
- line++;
-
- /*
- * Skip to operator character, nulling out whitespace as we go
- * XXX Rather than counting () and {} we should look for $ and
- * then expand the variable.
- */
- for (depth = 0, cp = line; depth > 0 || *cp != '='; cp++) {
- if (*cp == '(' || *cp == '{') {
- depth++;
- continue;
- }
- if (*cp == ')' || *cp == '}') {
- depth--;
- continue;
- }
- if (depth == 0 && isspace ((unsigned char)*cp)) {
- *cp = '\0';
+static void
+VarCheckSyntax(VarAssignOp type, const char *uvalue, GNode *ctxt)
+{
+ if (DEBUG(LINT)) {
+ if (type != VAR_SUBST && strchr(uvalue, '$') != NULL) {
+ /* Check for syntax errors such as unclosed expressions or
+ * unknown modifiers. */
+ char *expandedValue;
+
+ (void)Var_Subst(uvalue, ctxt, VARE_NONE, &expandedValue);
+ /* TODO: handle errors */
+ free(expandedValue);
}
}
- opc = cp-1; /* operator is the previous character */
- *cp++ = '\0'; /* nuke the = */
+}
+static void
+VarAssign_EvalSubst(const char *name, const char *uvalue, GNode *ctxt,
+ const char **out_avalue, void **out_avalue_freeIt)
+{
+ const char *avalue = uvalue;
+ char *evalue;
/*
- * Check operator type
+ * Allow variables in the old value to be undefined, but leave their
+ * expressions alone -- this is done by forcing oldVars to be false.
+ * XXX: This can cause recursive variables, but that's not hard to do,
+ * and this allows someone to do something like
+ *
+ * CFLAGS = $(.INCLUDES)
+ * CFLAGS := -I.. $(CFLAGS)
+ *
+ * And not get an error.
*/
- switch (*opc) {
- case '+':
- type = VAR_APPEND;
- *opc = '\0';
- break;
-
- case '?':
- /*
- * If the variable already has a value, we don't do anything.
- */
- *opc = '\0';
- if (Var_Exists(line, ctxt)) {
- return;
- } else {
- type = VAR_NORMAL;
- }
- break;
-
- case ':':
- type = VAR_SUBST;
- *opc = '\0';
- break;
-
- case '!':
- type = VAR_SHELL;
- *opc = '\0';
- break;
+ Boolean oldOldVars = oldVars;
- default:
-#ifdef SUNSHCMD
- while (opc > line && *opc != ':')
- opc--;
+ oldVars = FALSE;
- if (strncmp(opc, ":sh", 3) == 0) {
- type = VAR_SHELL;
- *opc = '\0';
- break;
- }
-#endif
- type = VAR_NORMAL;
- break;
- }
+ /*
+ * make sure that we set the variable the first time to nothing
+ * so that it gets substituted!
+ */
+ if (!Var_Exists(name, ctxt))
+ Var_Set(name, "", ctxt);
- while (isspace((unsigned char)*cp))
- cp++;
+ (void)Var_Subst(uvalue, ctxt, VARE_WANTRES|VARE_ASSIGN, &evalue);
+ /* TODO: handle errors */
+ oldVars = oldOldVars;
+ avalue = evalue;
+ Var_Set(name, avalue, ctxt);
- if (DEBUG(LINT)) {
- if (type != VAR_SUBST && strchr(cp, '$') != NULL) {
- /* sanity check now */
- char *cp2;
+ *out_avalue = avalue;
+ *out_avalue_freeIt = evalue;
+}
- cp2 = Var_Subst(cp, ctxt, VARE_ASSIGN);
- free(cp2);
- }
+static void
+VarAssign_EvalShell(const char *name, const char *uvalue, GNode *ctxt,
+ const char **out_avalue, void **out_avalue_freeIt)
+{
+ const char *cmd, *errfmt;
+ char *cmdOut;
+ void *cmd_freeIt = NULL;
+
+ cmd = uvalue;
+ if (strchr(cmd, '$') != NULL) {
+ char *ecmd;
+ (void)Var_Subst(cmd, VAR_CMDLINE, VARE_UNDEFERR | VARE_WANTRES, &ecmd);
+ /* TODO: handle errors */
+ cmd = cmd_freeIt = ecmd;
}
- if (type == VAR_APPEND) {
- Var_Append(line, cp, ctxt);
- } else if (type == VAR_SUBST) {
- /*
- * Allow variables in the old value to be undefined, but leave their
- * invocation alone -- this is done by forcing oldVars to be false.
- * XXX: This can cause recursive variables, but that's not hard to do,
- * and this allows someone to do something like
- *
- * CFLAGS = $(.INCLUDES)
- * CFLAGS := -I.. $(CFLAGS)
- *
- * And not get an error.
- */
- Boolean oldOldVars = oldVars;
-
- oldVars = FALSE;
-
- /*
- * make sure that we set the variable the first time to nothing
- * so that it gets substituted!
- */
- if (!Var_Exists(line, ctxt))
- Var_Set(line, "", ctxt);
+ cmdOut = Cmd_Exec(cmd, &errfmt);
+ Var_Set(name, cmdOut, ctxt);
+ *out_avalue = *out_avalue_freeIt = cmdOut;
- cp = Var_Subst(cp, ctxt, VARE_WANTRES|VARE_ASSIGN);
- oldVars = oldOldVars;
- freeCp = TRUE;
+ if (errfmt)
+ Parse_Error(PARSE_WARNING, errfmt, cmd);
- Var_Set(line, cp, ctxt);
- } else if (type == VAR_SHELL) {
- char *res;
- const char *error;
+ free(cmd_freeIt);
+}
- if (strchr(cp, '$') != NULL) {
- /*
- * There's a dollar sign in the command, so perform variable
- * expansion on the whole thing. The resulting string will need
- * freeing when we're done.
- */
- cp = Var_Subst(cp, VAR_CMD, VARE_UNDEFERR|VARE_WANTRES);
- freeCp = TRUE;
+/* Perform a variable assignment.
+ *
+ * The actual value of the variable is returned in *out_avalue and
+ * *out_avalue_freeIt. Especially for VAR_SUBST and VAR_SHELL this can differ
+ * from the literal value.
+ *
+ * Return whether the assignment was actually done. The assignment is only
+ * skipped if the operator is '?=' and the variable already exists. */
+static Boolean
+VarAssign_Eval(const char *name, VarAssignOp op, const char *uvalue,
+ GNode *ctxt, const char **out_avalue, void **out_avalue_freeIt)
+{
+ const char *avalue = uvalue;
+ void *avalue_freeIt = NULL;
+
+ if (op == VAR_APPEND) {
+ Var_Append(name, uvalue, ctxt);
+ } else if (op == VAR_SUBST) {
+ VarAssign_EvalSubst(name, uvalue, ctxt, &avalue, &avalue_freeIt);
+ } else if (op == VAR_SHELL) {
+ VarAssign_EvalShell(name, uvalue, ctxt, &avalue, &avalue_freeIt);
+ } else {
+ if (op == VAR_DEFAULT && Var_Exists(name, ctxt)) {
+ *out_avalue_freeIt = NULL;
+ return FALSE;
}
- res = Cmd_Exec(cp, &error);
- Var_Set(line, res, ctxt);
- free(res);
-
- if (error)
- Parse_Error(PARSE_WARNING, error, cp);
- } else {
- /*
- * Normal assignment -- just do it.
- */
- Var_Set(line, cp, ctxt);
+ /* Normal assignment -- just do it. */
+ Var_Set(name, uvalue, ctxt);
}
- if (strcmp(line, MAKEOVERRIDES) == 0)
+
+ *out_avalue = avalue;
+ *out_avalue_freeIt = avalue_freeIt;
+ return TRUE;
+}
+
+static void
+VarAssignSpecial(const char *name, const char *avalue)
+{
+ if (strcmp(name, MAKEOVERRIDES) == 0)
Main_ExportMAKEFLAGS(FALSE); /* re-export MAKEFLAGS */
- else if (strcmp(line, ".CURDIR") == 0) {
+ else if (strcmp(name, ".CURDIR") == 0) {
/*
- * Somone is being (too?) clever...
+ * Someone is being (too?) clever...
* Let's pretend they know what they are doing and
- * re-initialize the 'cur' Path.
+ * re-initialize the 'cur' CachedDir.
*/
- Dir_InitCur(cp);
+ Dir_InitCur(avalue);
Dir_SetPATH();
- } else if (strcmp(line, MAKE_JOB_PREFIX) == 0) {
+ } else if (strcmp(name, MAKE_JOB_PREFIX) == 0) {
Job_SetPrefix();
- } else if (strcmp(line, MAKE_EXPORTED) == 0) {
- Var_Export(cp, FALSE);
+ } else if (strcmp(name, MAKE_EXPORTED) == 0) {
+ Var_Export(avalue, FALSE);
}
- if (freeCp)
- free(cp);
+}
+
+/* Perform the variable variable assignment in the given context. */
+void
+Parse_DoVar(VarAssign *var, GNode *ctxt)
+{
+ const char *avalue; /* actual value (maybe expanded) */
+ void *avalue_freeIt;
+
+ VarCheckSyntax(var->op, var->value, ctxt);
+ if (VarAssign_Eval(var->varname, var->op, var->value, ctxt,
+ &avalue, &avalue_freeIt))
+ VarAssignSpecial(var->varname, avalue);
+
+ free(avalue_freeIt);
+ free(var->varname);
}
/*
* ParseMaybeSubMake --
- * Scan the command string to see if it a possible submake node
+ * Scan the command string to see if it a possible submake node
* Input:
* cmd the command to scan
* Results:
@@ -2067,41 +2090,27 @@ ParseMaybeSubMake(const char *cmd)
MKV("$(.MAKE)"),
MKV("make"),
};
- for (i = 0; i < sizeof(vals)/sizeof(vals[0]); i++) {
+ for (i = 0; i < sizeof vals / sizeof vals[0]; i++) {
char *ptr;
if ((ptr = strstr(cmd, vals[i].name)) == NULL)
continue;
- if ((ptr == cmd || !isalnum((unsigned char)ptr[-1]))
- && !isalnum((unsigned char)ptr[vals[i].len]))
+ if ((ptr == cmd || !ch_isalnum(ptr[-1]))
+ && !ch_isalnum(ptr[vals[i].len]))
return TRUE;
}
return FALSE;
}
-/*-
- * ParseAddCmd --
- * Lst_ForEach function to add a command line to all targets
- *
- * Input:
- * gnp the node to which the command is to be added
- * cmd the command to add
+/* Append the command to the target node.
*
- * Results:
- * Always 0
- *
- * Side Effects:
- * A new element is added to the commands list of the node,
- * and the node can be marked as a submake node if the command is
- * determined to be that.
- */
-static int
-ParseAddCmd(void *gnp, void *cmd)
+ * The node may be marked as a submake node if the command is determined to
+ * be that. */
+static void
+ParseAddCmd(GNode *gn, char *cmd)
{
- GNode *gn = (GNode *)gnp;
-
/* Add to last (ie current) cohort for :: targets */
- if ((gn->type & OP_DOUBLEDEP) && !Lst_IsEmpty(gn->cohorts))
- gn = LstNode_Datum(Lst_Last(gn->cohorts));
+ if ((gn->type & OP_DOUBLEDEP) && gn->cohorts->last != NULL)
+ gn = gn->cohorts->last->datum;
/* if target already supplied, ignore commands */
if (!(gn->type & OP_HAS_COMMANDS)) {
@@ -2110,7 +2119,7 @@ ParseAddCmd(void *gnp, void *cmd)
gn->type |= OP_SUBMAKE;
ParseMark(gn);
} else {
-#ifdef notyet
+#if 0
/* XXX: We cannot do this until we fix the tree */
Lst_Append(gn->commands, cmd);
Parse_Error(PARSE_WARNING,
@@ -2119,95 +2128,39 @@ ParseAddCmd(void *gnp, void *cmd)
gn->name, gn->fname, gn->lineno);
#else
Parse_Error(PARSE_WARNING,
- "duplicate script for target \"%s\" ignored",
- gn->name);
- ParseErrorInternal(gn->fname, gn->lineno, PARSE_WARNING,
- "using previous script for \"%s\" defined here",
- gn->name);
+ "duplicate script for target \"%s\" ignored",
+ gn->name);
+ ParseErrorInternal(gn->fname, (size_t)gn->lineno, PARSE_WARNING,
+ "using previous script for \"%s\" defined here",
+ gn->name);
#endif
}
- return 0;
}
-/*-
- *-----------------------------------------------------------------------
- * ParseHasCommands --
- * Callback procedure for Parse_File when destroying the list of
- * targets on the last dependency line. Marks a target as already
- * having commands if it does, to keep from having shell commands
- * on multiple dependency lines.
- *
- * Input:
- * gnp Node to examine
- *
- * Results:
- * None
- *
- * Side Effects:
- * OP_HAS_COMMANDS may be set for the target.
- *
- *-----------------------------------------------------------------------
- */
-static void
-ParseHasCommands(void *gnp)
-{
- GNode *gn = (GNode *)gnp;
- if (!Lst_IsEmpty(gn->commands)) {
- gn->type |= OP_HAS_COMMANDS;
- }
-}
-
-/*-
- *-----------------------------------------------------------------------
- * Parse_AddIncludeDir --
- * Add a directory to the path searched for included makefiles
- * bracketed by double-quotes. Used by functions in main.c
- *
- * Input:
- * dir The name of the directory to add
- *
- * Results:
- * None.
- *
- * Side Effects:
- * The directory is appended to the list.
- *
- *-----------------------------------------------------------------------
- */
+/* Add a directory to the path searched for included makefiles bracketed
+ * by double-quotes. */
void
-Parse_AddIncludeDir(char *dir)
+Parse_AddIncludeDir(const char *dir)
{
(void)Dir_AddDir(parseIncPath, dir);
}
-/*-
- *---------------------------------------------------------------------
- * ParseDoInclude --
- * Push to another file.
- *
- * The input is the line minus the `.'. A file spec is a string
- * enclosed in <> or "". The former is looked for only in sysIncPath.
- * The latter in . and the directories specified by -I command line
- * options
- *
- * Results:
- * None
+/* Push to another file.
*
- * Side Effects:
- * A structure is added to the includes Lst and readProc, lineno,
- * fname and curFILE are altered for the new file
- *---------------------------------------------------------------------
+ * The input is the line minus the '.'. A file spec is a string enclosed in
+ * <> or "". The <> file is looked for only in sysIncPath. The "" file is
+ * first searched in the parsedir and then in the directories specified by
+ * the -I command line options.
*/
-
static void
Parse_include_file(char *file, Boolean isSystem, Boolean depinc, int silent)
{
struct loadedfile *lf;
- char *fullname; /* full pathname of file */
- char *newName;
- char *prefEnd, *incdir;
- int fd;
- int i;
+ char *fullname; /* full pathname of file */
+ char *newName;
+ char *prefEnd, *incdir;
+ int fd;
+ int i;
/*
* Now we know the file's name and its search path, we attempt to
@@ -2225,7 +2178,7 @@ Parse_include_file(char *file, Boolean isSystem, Boolean depinc, int silent)
* we can locate the beast.
*/
- incdir = bmake_strdup(curFile->fname);
+ incdir = bmake_strdup(CurFile()->fname);
prefEnd = strrchr(incdir, '/');
if (prefEnd != NULL) {
*prefEnd = '\0';
@@ -2252,7 +2205,7 @@ Parse_include_file(char *file, Boolean isSystem, Boolean depinc, int silent)
* If we have a suffix specific path we should use that.
*/
char *suff;
- Lst suffPath = NULL;
+ SearchPath *suffPath = NULL;
if ((suff = strrchr(file, '.'))) {
suffPath = Suff_GetPath(suff);
@@ -2274,8 +2227,8 @@ Parse_include_file(char *file, Boolean isSystem, Boolean depinc, int silent)
/*
* Look for it on the system path
*/
- fullname = Dir_FindFile(file,
- Lst_IsEmpty(sysIncPath) ? defIncPath : sysIncPath);
+ SearchPath *path = Lst_IsEmpty(sysIncPath) ? defSysIncPath : sysIncPath;
+ fullname = Dir_FindFile(file, path);
}
if (fullname == NULL) {
@@ -2296,21 +2249,20 @@ Parse_include_file(char *file, Boolean isSystem, Boolean depinc, int silent)
/* load it */
lf = loadfile(fullname, fd);
- ParseSetIncludedFile();
/* Start reading from this file next */
Parse_SetInput(fullname, 0, -1, loadedfile_nextbuf, lf);
- curFile->lf = lf;
+ CurFile()->lf = lf;
if (depinc)
- doing_depend = depinc; /* only turn it on */
+ doing_depend = depinc; /* only turn it on */
}
static void
ParseDoInclude(char *line)
{
- char endc; /* the character which ends the file spec */
- char *cp; /* current position in file spec */
- int silent = *line != 'i';
- char *file = &line[7 + silent];
+ char endc; /* the character which ends the file spec */
+ char *cp; /* current position in file spec */
+ int silent = *line != 'i';
+ char *file = line + (silent ? 8 : 7);
/* Skip to delimiter character so we know where to look */
while (*file == ' ' || *file == '\t')
@@ -2318,7 +2270,7 @@ ParseDoInclude(char *line)
if (*file != '"' && *file != '<') {
Parse_Error(PARSE_FATAL,
- ".include filename must be delimited by '\"' or '<'");
+ ".include filename must be delimited by '\"' or '<'");
return;
}
@@ -2339,8 +2291,8 @@ ParseDoInclude(char *line)
if (*cp != endc) {
Parse_Error(PARSE_FATAL,
- "Unclosed %cinclude filename. '%c' expected",
- '.', endc);
+ "Unclosed %cinclude filename. '%c' expected",
+ '.', endc);
return;
}
*cp = '\0';
@@ -2349,153 +2301,147 @@ ParseDoInclude(char *line)
* Substitute for any variables in the file name before trying to
* find the thing.
*/
- file = Var_Subst(file, VAR_CMD, VARE_WANTRES);
+ (void)Var_Subst(file, VAR_CMDLINE, VARE_WANTRES, &file);
+ /* TODO: handle errors */
Parse_include_file(file, endc == '>', *line == 'd', silent);
free(file);
}
-
-/*-
- *---------------------------------------------------------------------
- * ParseSetIncludedFile --
- * Set the .INCLUDEDFROMFILE variable to the contents of .PARSEFILE
- * and the .INCLUDEDFROMDIR variable to the contents of .PARSEDIR
- *
- * Results:
- * None
- *
- * Side Effects:
- * The .INCLUDEDFROMFILE variable is overwritten by the contents
- * of .PARSEFILE and the .INCLUDEDFROMDIR variable is overwriten
- * by the contents of .PARSEDIR
- *---------------------------------------------------------------------
- */
+/* Split filename into dirname + basename, then assign these to the
+ * given variables. */
static void
-ParseSetIncludedFile(void)
+SetFilenameVars(const char *filename, const char *dirvar, const char *filevar)
{
- const char *pf, *pd;
- char *pf_freeIt, *pd_freeIt;
+ const char *slash, *dirname, *basename;
+ void *freeIt;
- pf = Var_Value(".PARSEFILE", VAR_GLOBAL, &pf_freeIt);
- Var_Set(".INCLUDEDFROMFILE", pf, VAR_GLOBAL);
- pd = Var_Value(".PARSEDIR", VAR_GLOBAL, &pd_freeIt);
- Var_Set(".INCLUDEDFROMDIR", pd, VAR_GLOBAL);
+ slash = strrchr(filename, '/');
+ if (slash == NULL) {
+ dirname = curdir;
+ basename = filename;
+ freeIt = NULL;
+ } else {
+ dirname = freeIt = bmake_strsedup(filename, slash);
+ basename = slash + 1;
+ }
- if (DEBUG(PARSE))
- fprintf(debug_file, "%s: ${.INCLUDEDFROMDIR} = `%s' "
- "${.INCLUDEDFROMFILE} = `%s'\n", __func__, pd, pf);
+ Var_Set(dirvar, dirname, VAR_GLOBAL);
+ Var_Set(filevar, basename, VAR_GLOBAL);
- bmake_free(pf_freeIt);
- bmake_free(pd_freeIt);
+ DEBUG5(PARSE, "%s: ${%s} = `%s' ${%s} = `%s'\n",
+ __func__, dirvar, dirname, filevar, basename);
+ free(freeIt);
}
-/*-
- *---------------------------------------------------------------------
- * ParseSetParseFile --
- * Set the .PARSEDIR and .PARSEFILE variables to the dirname and
- * basename of the given filename
- *
- * Results:
- * None
+
+/* Return the immediately including file.
*
- * Side Effects:
- * The .PARSEDIR and .PARSEFILE variables are overwritten by the
- * dirname and basename of the given filename.
- *---------------------------------------------------------------------
- */
+ * This is made complicated since the .for loop is implemented as a special
+ * kind of .include; see For_Run. */
+static const char *
+GetActuallyIncludingFile(void)
+{
+ size_t i;
+ const IFile *incs = GetInclude(0);
+
+ for (i = includes.len; i >= 2; i--)
+ if (!incs[i - 1].fromForLoop)
+ return incs[i - 2].fname;
+ return NULL;
+}
+
+/* Set .PARSEDIR, .PARSEFILE, .INCLUDEDFROMDIR and .INCLUDEDFROMFILE. */
static void
ParseSetParseFile(const char *filename)
{
- char *slash, *dirname;
- const char *pd, *pf;
+ const char *including;
- slash = strrchr(filename, '/');
- if (slash == NULL) {
- Var_Set(".PARSEDIR", pd = curdir, VAR_GLOBAL);
- Var_Set(".PARSEFILE", pf = filename, VAR_GLOBAL);
- dirname = NULL;
+ SetFilenameVars(filename, ".PARSEDIR", ".PARSEFILE");
+
+ including = GetActuallyIncludingFile();
+ if (including != NULL) {
+ SetFilenameVars(including,
+ ".INCLUDEDFROMDIR", ".INCLUDEDFROMFILE");
} else {
- dirname = bmake_strsedup(filename, slash);
- Var_Set(".PARSEDIR", pd = dirname, VAR_GLOBAL);
- Var_Set(".PARSEFILE", pf = slash + 1, VAR_GLOBAL);
+ Var_Delete(".INCLUDEDFROMDIR", VAR_GLOBAL);
+ Var_Delete(".INCLUDEDFROMFILE", VAR_GLOBAL);
}
- if (DEBUG(PARSE))
- fprintf(debug_file, "%s: ${.PARSEDIR} = `%s' ${.PARSEFILE} = `%s'\n",
- __func__, pd, pf);
- free(dirname);
}
-/*
- * Track the makefiles we read - so makefiles can
- * set dependencies on them.
- * Avoid adding anything more than once.
- */
+static Boolean
+StrContainsWord(const char *str, const char *word)
+{
+ size_t strLen = strlen(str);
+ size_t wordLen = strlen(word);
+ const char *p, *end;
+
+ if (strLen < wordLen)
+ return FALSE; /* str is too short to contain word */
+
+ end = str + strLen - wordLen;
+ for (p = str; p != NULL; p = strchr(p, ' ')) {
+ if (*p == ' ')
+ p++;
+ if (p > end)
+ return FALSE; /* cannot contain word */
+
+ if (memcmp(p, word, wordLen) == 0 &&
+ (p[wordLen] == '\0' || p[wordLen] == ' '))
+ return TRUE;
+ }
+ return FALSE;
+}
+/* XXX: Searching through a set of words with this linear search is
+ * inefficient for variables that contain thousands of words. */
+static Boolean
+VarContainsWord(const char *varname, const char *word)
+{
+ void *val_freeIt;
+ const char *val = Var_Value(varname, VAR_GLOBAL, &val_freeIt);
+ Boolean found = val != NULL && StrContainsWord(val, word);
+ bmake_free(val_freeIt);
+ return found;
+}
+
+/* Track the makefiles we read - so makefiles can set dependencies on them.
+ * Avoid adding anything more than once. */
static void
ParseTrackInput(const char *name)
{
- char *fp = NULL;
-
- const char *old = Var_Value(MAKE_MAKEFILES, VAR_GLOBAL, &fp);
- if (old) {
- size_t name_len = strlen(name);
- const char *ep = old + strlen(old) - name_len;
- /* does it contain name? */
- for (; old != NULL; old = strchr(old, ' ')) {
- if (*old == ' ')
- old++;
- if (old >= ep)
- break; /* cannot contain name */
- if (memcmp(old, name, name_len) == 0
- && (old[name_len] == 0 || old[name_len] == ' '))
- goto cleanup;
- }
- }
- Var_Append (MAKE_MAKEFILES, name, VAR_GLOBAL);
- cleanup:
- bmake_free(fp);
+ if (!VarContainsWord(MAKE_MAKEFILES, name))
+ Var_Append(MAKE_MAKEFILES, name, VAR_GLOBAL);
}
-/*-
- *---------------------------------------------------------------------
- * Parse_setInput --
- * Start Parsing from the given source
+/* Start Parsing from the given source.
*
- * Results:
- * None
- *
- * Side Effects:
- * A structure is added to the includes Lst and readProc, lineno,
- * fname and curFile are altered for the new file
- *---------------------------------------------------------------------
- */
+ * The given file is added to the includes stack. */
void
Parse_SetInput(const char *name, int line, int fd,
- char *(*nextbuf)(void *, size_t *), void *arg)
+ char *(*nextbuf)(void *, size_t *), void *arg)
{
+ IFile *curFile;
char *buf;
size_t len;
+ Boolean fromForLoop = name == NULL;
- if (name == NULL)
- name = curFile->fname;
+ if (fromForLoop)
+ name = CurFile()->fname;
else
ParseTrackInput(name);
if (DEBUG(PARSE))
- fprintf(debug_file, "%s: file %s, line %d, fd %d, nextbuf %p, arg %p\n",
- __func__, name, line, fd, nextbuf, arg);
+ debug_printf("%s: file %s, line %d, fd %d, nextbuf %s, arg %p\n",
+ __func__, name, line, fd,
+ nextbuf == loadedfile_nextbuf ? "loadedfile" : "other",
+ arg);
if (fd == -1 && nextbuf == NULL)
/* sanity */
return;
- if (curFile != NULL)
- /* Save exiting file info */
- Lst_Prepend(includes, curFile);
-
- /* Allocate and fill in new structure */
- curFile = bmake_malloc(sizeof *curFile);
+ curFile = Vector_Push(&includes);
/*
* Once the previous state has been saved, we can get down to reading
@@ -2504,6 +2450,7 @@ Parse_SetInput(const char *name, int line, int fd,
* place.
*/
curFile->fname = bmake_strdup(name);
+ curFile->fromForLoop = fromForLoop;
curFile->lineno = line;
curFile->first_lineno = line;
curFile->nextbuf = nextbuf;
@@ -2522,58 +2469,31 @@ Parse_SetInput(const char *name, int line, int fd,
free(curFile);
return;
}
- curFile->P_str = buf;
- curFile->P_ptr = buf;
- curFile->P_end = buf+len;
+ curFile->buf_freeIt = buf;
+ curFile->buf_ptr = buf;
+ curFile->buf_end = buf + len;
curFile->cond_depth = Cond_save_depth();
ParseSetParseFile(name);
}
-/*-
- *-----------------------------------------------------------------------
- * IsInclude --
- * Check if the line is an include directive
- *
- * Results:
- * TRUE if it is.
- *
- * Side Effects:
- * None
- *
- *-----------------------------------------------------------------------
- */
+/* Check if the directive is an include directive. */
static Boolean
-IsInclude(const char *line, Boolean sysv)
+IsInclude(const char *dir, Boolean sysv)
{
- static const char inc[] = "include";
- static const size_t inclen = sizeof(inc) - 1;
-
- /* 'd' is not valid for sysv */
- int o = strchr(sysv ? "s-" : "ds-", *line) != NULL;
+ if (dir[0] == 's' || dir[0] == '-' || (dir[0] == 'd' && !sysv))
+ dir++;
- if (strncmp(line + o, inc, inclen) != 0)
+ if (strncmp(dir, "include", 7) != 0)
return FALSE;
/* Space is not mandatory for BSD .include */
- return !sysv || isspace((unsigned char)line[inclen + o]);
+ return !sysv || ch_isspace(dir[7]);
}
#ifdef SYSVINCLUDE
-/*-
- *-----------------------------------------------------------------------
- * IsSysVInclude --
- * Check if the line is a SYSV include directive
- *
- * Results:
- * TRUE if it is.
- *
- * Side Effects:
- * None
- *
- *-----------------------------------------------------------------------
- */
+/* Check if the line is a SYSV include directive. */
static Boolean
IsSysVInclude(const char *line)
{
@@ -2582,13 +2502,13 @@ IsSysVInclude(const char *line)
if (!IsInclude(line, TRUE))
return FALSE;
- /* Avoid interpeting a dependency line as an include */
+ /* Avoid interpreting a dependency line as an include */
for (p = line; (p = strchr(p, ':')) != NULL;) {
if (*++p == '\0') {
/* end of line -> dependency */
return FALSE;
}
- if (*p == ':' || isspace((unsigned char)*p)) {
+ if (*p == ':' || ch_isspace(*p)) {
/* :: operator or ': ' -> dependency */
return FALSE;
}
@@ -2596,56 +2516,35 @@ IsSysVInclude(const char *line)
return TRUE;
}
-/*-
- *---------------------------------------------------------------------
- * ParseTraditionalInclude --
- * Push to another file.
- *
- * The input is the current line. The file name(s) are
- * following the "include".
- *
- * Results:
- * None
- *
- * Side Effects:
- * A structure is added to the includes Lst and readProc, lineno,
- * fname and curFILE are altered for the new file
- *---------------------------------------------------------------------
- */
+/* Push to another file. The line points to the word "include". */
static void
ParseTraditionalInclude(char *line)
{
- char *cp; /* current position in file spec */
- int done = 0;
- int silent = line[0] != 'i';
- char *file = &line[silent + 7];
- char *all_files;
+ char *cp; /* current position in file spec */
+ int done = 0;
+ int silent = line[0] != 'i';
+ char *file = line + (silent ? 8 : 7);
+ char *all_files;
- if (DEBUG(PARSE)) {
- fprintf(debug_file, "%s: %s\n", __func__, file);
- }
+ DEBUG2(PARSE, "%s: %s\n", __func__, file);
- /*
- * Skip over whitespace
- */
- while (isspace((unsigned char)*file))
- file++;
+ pp_skip_whitespace(&file);
/*
* Substitute for any variables in the file name before trying to
* find the thing.
*/
- all_files = Var_Subst(file, VAR_CMD, VARE_WANTRES);
+ (void)Var_Subst(file, VAR_CMDLINE, VARE_WANTRES, &all_files);
+ /* TODO: handle errors */
if (*file == '\0') {
- Parse_Error(PARSE_FATAL,
- "Filename missing from \"include\"");
+ Parse_Error(PARSE_FATAL, "Filename missing from \"include\"");
goto out;
}
for (file = all_files; !done; file = cp + 1) {
/* Skip to end of line or next whitespace */
- for (cp = file; *cp && !isspace((unsigned char) *cp); cp++)
+ for (cp = file; *cp && !ch_isspace(*cp); cp++)
continue;
if (*cp)
@@ -2661,130 +2560,104 @@ out:
#endif
#ifdef GMAKEEXPORT
-/*-
- *---------------------------------------------------------------------
- * ParseGmakeExport --
- * Parse export <variable>=<value>
- *
- * And set the environment with it.
- *
- * Results:
- * None
- *
- * Side Effects:
- * None
- *---------------------------------------------------------------------
- */
+/* Parse "export <variable>=<value>", and actually export it. */
static void
ParseGmakeExport(char *line)
{
- char *variable = &line[6];
- char *value;
+ char *variable = line + 6;
+ char *value;
- if (DEBUG(PARSE)) {
- fprintf(debug_file, "%s: %s\n", __func__, variable);
- }
+ DEBUG2(PARSE, "%s: %s\n", __func__, variable);
- /*
- * Skip over whitespace
- */
- while (isspace((unsigned char)*variable))
- variable++;
+ pp_skip_whitespace(&variable);
for (value = variable; *value && *value != '='; value++)
continue;
if (*value != '=') {
Parse_Error(PARSE_FATAL,
- "Variable/Value missing from \"export\"");
+ "Variable/Value missing from \"export\"");
return;
}
- *value++ = '\0'; /* terminate variable */
+ *value++ = '\0'; /* terminate variable */
/*
* Expand the value before putting it in the environment.
*/
- value = Var_Subst(value, VAR_CMD, VARE_WANTRES);
+ (void)Var_Subst(value, VAR_CMDLINE, VARE_WANTRES, &value);
+ /* TODO: handle errors */
+
setenv(variable, value, 1);
free(value);
}
#endif
-/*-
- *---------------------------------------------------------------------
- * ParseEOF --
- * Called when EOF is reached in the current file. If we were reading
- * an include file, the includes stack is popped and things set up
- * to go back to reading the previous file at the previous location.
+/* Called when EOF is reached in the current file. If we were reading an
+ * include file, the includes stack is popped and things set up to go back
+ * to reading the previous file at the previous location.
*
* Results:
- * CONTINUE if there's more to do. DONE if not.
- *
- * Side Effects:
- * The old curFILE, is closed. The includes list is shortened.
- * lineno, curFILE, and fname are changed if CONTINUE is returned.
- *---------------------------------------------------------------------
+ * TRUE to continue parsing, i.e. it had only reached the end of an
+ * included file, FALSE if the main file has been parsed completely.
*/
-static int
+static Boolean
ParseEOF(void)
{
char *ptr;
size_t len;
+ IFile *curFile = CurFile();
assert(curFile->nextbuf != NULL);
doing_depend = curFile->depending; /* restore this */
/* get next input buffer, if any */
ptr = curFile->nextbuf(curFile->nextbuf_arg, &len);
- curFile->P_ptr = ptr;
- curFile->P_str = ptr;
- curFile->P_end = ptr + len;
+ curFile->buf_ptr = ptr;
+ curFile->buf_freeIt = ptr;
+ curFile->buf_end = ptr + len;
curFile->lineno = curFile->first_lineno;
if (ptr != NULL) {
/* Iterate again */
- return CONTINUE;
+ return TRUE;
}
/* Ensure the makefile (or loop) didn't have mismatched conditionals */
Cond_restore_depth(curFile->cond_depth);
if (curFile->lf != NULL) {
- loadedfile_destroy(curFile->lf);
- curFile->lf = NULL;
+ loadedfile_destroy(curFile->lf);
+ curFile->lf = NULL;
}
/* Dispose of curFile info */
/* Leak curFile->fname because all the gnodes have pointers to it */
- free(curFile->P_str);
- free(curFile);
+ free(curFile->buf_freeIt);
+ Vector_Pop(&includes);
- if (Lst_IsEmpty(includes)) {
- curFile = NULL;
+ if (includes.len == 0) {
/* We've run out of input */
Var_Delete(".PARSEDIR", VAR_GLOBAL);
Var_Delete(".PARSEFILE", VAR_GLOBAL);
Var_Delete(".INCLUDEDFROMDIR", VAR_GLOBAL);
Var_Delete(".INCLUDEDFROMFILE", VAR_GLOBAL);
- return DONE;
+ return FALSE;
}
- curFile = Lst_Dequeue(includes);
- if (DEBUG(PARSE))
- fprintf(debug_file, "ParseEOF: returning to file %s, line %d\n",
- curFile->fname, curFile->lineno);
+ curFile = CurFile();
+ DEBUG2(PARSE, "ParseEOF: returning to file %s, line %d\n",
+ curFile->fname, curFile->lineno);
- /* Restore the PARSEDIR/PARSEFILE variables */
ParseSetParseFile(curFile->fname);
- return CONTINUE;
+ return TRUE;
}
#define PARSE_RAW 1
#define PARSE_SKIP 2
static char *
-ParseGetLine(int flags, int *length)
+ParseGetLine(int flags)
{
- IFile *cf = curFile;
+ IFile *cf = CurFile();
char *ptr;
char ch;
char *line;
@@ -2796,29 +2669,32 @@ ParseGetLine(int flags, int *length)
/* Loop through blank lines and comment lines */
for (;;) {
cf->lineno++;
- line = cf->P_ptr;
+ line = cf->buf_ptr;
ptr = line;
line_end = line;
escaped = NULL;
comment = NULL;
for (;;) {
- if (cf->P_end != NULL && ptr == cf->P_end) {
+ /* XXX: can buf_end ever be null? */
+ if (cf->buf_end != NULL && ptr == cf->buf_end) {
/* end of buffer */
ch = 0;
break;
}
ch = *ptr;
if (ch == 0 || (ch == '\\' && ptr[1] == 0)) {
- if (cf->P_end == NULL)
+ /* XXX: can buf_end ever be null? */
+ if (cf->buf_end == NULL)
/* End of string (aka for loop) data */
break;
/* see if there is more we can parse */
- while (ptr++ < cf->P_end) {
+ while (ptr++ < cf->buf_end) {
if ((ch = *ptr) == '\n') {
if (ptr > line && ptr[-1] == '\\')
continue;
Parse_Error(PARSE_WARNING,
- "Zero byte read from file, skipping rest of line.");
+ "Zero byte read from file, "
+ "skipping rest of line.");
break;
}
}
@@ -2852,13 +2728,13 @@ ParseGetLine(int flags, int *length)
ptr++;
if (ch == '\n')
break;
- if (!isspace((unsigned char)ch))
+ if (!ch_isspace(ch))
/* We are not interested in trailing whitespace */
line_end = ptr;
}
/* Save next 'to be processed' location */
- cf->P_ptr = ptr;
+ cf->buf_ptr = ptr;
/* Check we have a non-comment, non-blank line */
if (line_end == line || comment == line) {
@@ -2874,7 +2750,6 @@ ParseGetLine(int flags, int *length)
if (flags & PARSE_RAW) {
/* Leave '\' (etc) in line buffer (eg 'for' lines) */
- *length = line_end - line;
return line;
}
@@ -2894,10 +2769,8 @@ ParseGetLine(int flags, int *length)
}
/* If we didn't see a '\\' then the in-situ data is fine */
- if (escaped == NULL) {
- *length = line_end - line;
+ if (escaped == NULL)
return line;
- }
/* Remove escapes from '\n' and '#' */
tp = ptr = escaped;
@@ -2936,36 +2809,30 @@ ParseGetLine(int flags, int *length)
}
/* Delete any trailing spaces - eg from empty continuations */
- while (tp > escaped && isspace((unsigned char)tp[-1]))
+ while (tp > escaped && ch_isspace(tp[-1]))
tp--;
*tp = 0;
- *length = tp - line;
return line;
}
-/*-
- *---------------------------------------------------------------------
- * ParseReadLine --
- * Read an entire line from the input file. Called only by Parse_File.
+/* Read an entire line from the input file. Called only by Parse_File.
*
* Results:
- * A line w/o its newline
+ * A line without its newline.
*
* Side Effects:
* Only those associated with reading a character
- *---------------------------------------------------------------------
*/
static char *
ParseReadLine(void)
{
- char *line; /* Result */
- int lineLength; /* Length of result */
- int lineno; /* Saved line # */
- int rval;
+ char *line; /* Result */
+ int lineno; /* Saved line # */
+ int rval;
for (;;) {
- line = ParseGetLine(0, &lineLength);
+ line = ParseGetLine(0);
if (line == NULL)
return NULL;
@@ -2976,12 +2843,12 @@ ParseReadLine(void)
* The line might be a conditional. Ask the conditional module
* about it and act accordingly
*/
- switch (Cond_Eval(line)) {
+ switch (Cond_EvalLine(line)) {
case COND_SKIP:
/* Skip to next conditional that evaluates to COND_PARSE. */
do {
- line = ParseGetLine(PARSE_SKIP, &lineLength);
- } while (line && Cond_Eval(line) != COND_PARSE);
+ line = ParseGetLine(PARSE_SKIP);
+ } while (line && Cond_EvalLine(line) != COND_PARSE);
if (line == NULL)
break;
continue;
@@ -2997,13 +2864,13 @@ ParseReadLine(void)
/* Syntax error - error printed, ignore line */
continue;
/* Start of a .for loop */
- lineno = curFile->lineno;
+ lineno = CurFile()->lineno;
/* Accumulate loop lines until matching .endfor */
do {
- line = ParseGetLine(PARSE_RAW, &lineLength);
+ line = ParseGetLine(PARSE_RAW);
if (line == NULL) {
Parse_Error(PARSE_FATAL,
- "Unexpected end of file in for loop.");
+ "Unexpected end of file in for loop.");
break;
}
} while (For_Accum(line));
@@ -3016,309 +2883,325 @@ ParseReadLine(void)
}
}
-/*-
- *-----------------------------------------------------------------------
- * ParseFinishLine --
- * Handle the end of a dependency group.
- *
- * Results:
- * Nothing.
- *
- * Side Effects:
- * inLine set FALSE. 'targets' list destroyed.
- *
- *-----------------------------------------------------------------------
- */
static void
-ParseFinishLine(void)
+FinishDependencyGroup(void)
{
- if (inLine) {
- if (targets != NULL) {
- Lst_ForEach(targets, Suff_EndTransform, NULL);
- Lst_Destroy(targets, ParseHasCommands);
+ if (targets != NULL) {
+ GNodeListNode *ln;
+ for (ln = targets->first; ln != NULL; ln = ln->next) {
+ GNode *gn = ln->datum;
+
+ Suff_EndTransform(gn);
+
+ /* Mark the target as already having commands if it does, to
+ * keep from having shell commands on multiple dependency lines. */
+ if (!Lst_IsEmpty(gn->commands))
+ gn->type |= OP_HAS_COMMANDS;
}
+
+ Lst_Free(targets);
targets = NULL;
- inLine = FALSE;
}
}
+/* Add the command to each target from the current dependency spec. */
+static void
+ParseLine_ShellCommand(const char *p)
+{
+ cpp_skip_whitespace(&p);
+ if (*p == '\0')
+ return; /* skip empty commands */
-/*-
- *---------------------------------------------------------------------
- * Parse_File --
- * Parse a file into its component parts, incorporating it into the
- * current dependency graph. This is the main function and controls
- * almost every other function in this module
- *
- * Input:
- * name the name of the file being read
- * fd Open file to makefile to parse
- *
- * Results:
- * None
- *
- * Side Effects:
- * closes fd.
- * Loads. Nodes are added to the list of all targets, nodes and links
- * are added to the dependency graph. etc. etc. etc.
- *---------------------------------------------------------------------
- */
-void
-Parse_File(const char *name, int fd)
+ if (targets == NULL) {
+ Parse_Error(PARSE_FATAL, "Unassociated shell command \"%s\"", p);
+ return;
+ }
+
+ {
+ char *cmd = bmake_strdup(p);
+ GNodeListNode *ln;
+
+ for (ln = targets->first; ln != NULL; ln = ln->next) {
+ GNode *gn = ln->datum;
+ ParseAddCmd(gn, cmd);
+ }
+#ifdef CLEANUP
+ Lst_Append(targCmds, cmd);
+#endif
+ }
+}
+
+static Boolean
+ParseDirective(char *line)
{
- char *cp; /* pointer into the line */
- char *line; /* the line we're working on */
- struct loadedfile *lf;
+ char *cp;
- lf = loadfile(name, fd);
+ if (*line == '.') {
+ /*
+ * Lines that begin with the special character may be
+ * include or undef directives.
+ * On the other hand they can be suffix rules (.c.o: ...)
+ * or just dependencies for filenames that start '.'.
+ */
+ cp = line + 1;
+ pp_skip_whitespace(&cp);
+ if (IsInclude(cp, FALSE)) {
+ ParseDoInclude(cp);
+ return TRUE;
+ }
+ if (strncmp(cp, "undef", 5) == 0) {
+ const char *varname;
+ cp += 5;
+ pp_skip_whitespace(&cp);
+ varname = cp;
+ for (; !ch_isspace(*cp) && *cp != '\0'; cp++)
+ continue;
+ *cp = '\0';
+ Var_Delete(varname, VAR_GLOBAL);
+ /* TODO: undefine all variables, not only the first */
+ /* TODO: use Str_Words, like everywhere else */
+ return TRUE;
+ } else if (strncmp(cp, "export", 6) == 0) {
+ cp += 6;
+ pp_skip_whitespace(&cp);
+ Var_Export(cp, TRUE);
+ return TRUE;
+ } else if (strncmp(cp, "unexport", 8) == 0) {
+ Var_UnExport(cp);
+ return TRUE;
+ } else if (strncmp(cp, "info", 4) == 0 ||
+ strncmp(cp, "error", 5) == 0 ||
+ strncmp(cp, "warning", 7) == 0) {
+ if (ParseMessage(cp))
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
- inLine = FALSE;
- fatals = 0;
+static Boolean
+ParseVarassign(const char *line)
+{
+ VarAssign var;
+ if (Parse_IsVar(line, &var)) {
+ FinishDependencyGroup();
+ Parse_DoVar(&var, VAR_GLOBAL);
+ return TRUE;
+ }
+ return FALSE;
+}
- if (name == NULL)
- name = "(stdin)";
+static char *
+FindSemicolon(char *p)
+{
+ int level = 0;
- Parse_SetInput(name, 0, -1, loadedfile_nextbuf, lf);
- curFile->lf = lf;
+ for (; *p != '\0'; p++) {
+ if (*p == '\\' && p[1] != '\0') {
+ p++;
+ continue;
+ }
- do {
- for (; (line = ParseReadLine()) != NULL; ) {
- if (DEBUG(PARSE))
- fprintf(debug_file, "ParseReadLine (%d): '%s'\n",
- curFile->lineno, line);
- if (*line == '.') {
- /*
- * Lines that begin with the special character may be
- * include or undef directives.
- * On the other hand they can be suffix rules (.c.o: ...)
- * or just dependencies for filenames that start '.'.
- */
- for (cp = line + 1; isspace((unsigned char)*cp); cp++) {
- continue;
- }
- if (IsInclude(cp, FALSE)) {
- ParseDoInclude(cp);
- continue;
- }
- if (strncmp(cp, "undef", 5) == 0) {
- char *cp2;
- for (cp += 5; isspace((unsigned char) *cp); cp++)
- continue;
- for (cp2 = cp; !isspace((unsigned char) *cp2) &&
- *cp2 != '\0'; cp2++)
- continue;
- *cp2 = '\0';
- Var_Delete(cp, VAR_GLOBAL);
- continue;
- } else if (strncmp(cp, "export", 6) == 0) {
- for (cp += 6; isspace((unsigned char) *cp); cp++)
- continue;
- Var_Export(cp, TRUE);
- continue;
- } else if (strncmp(cp, "unexport", 8) == 0) {
- Var_UnExport(cp);
- continue;
- } else if (strncmp(cp, "info", 4) == 0 ||
- strncmp(cp, "error", 5) == 0 ||
- strncmp(cp, "warning", 7) == 0) {
- if (ParseMessage(cp))
- continue;
- }
- }
+ if (*p == '$' && (p[1] == '(' || p[1] == '{')) {
+ level++;
+ continue;
+ }
- if (*line == '\t') {
- /*
- * If a line starts with a tab, it can only hope to be
- * a creation command.
- */
- cp = line + 1;
- shellCommand:
- for (; isspace ((unsigned char)*cp); cp++) {
- continue;
- }
- if (*cp) {
- if (!inLine)
- Parse_Error(PARSE_FATAL,
- "Unassociated shell command \"%s\"",
- cp);
- /*
- * So long as it's not a blank line and we're actually
- * in a dependency spec, add the command to the list of
- * commands of all targets in the dependency spec
- */
- if (targets) {
- cp = bmake_strdup(cp);
- Lst_ForEach(targets, ParseAddCmd, cp);
-#ifdef CLEANUP
- Lst_Append(targCmds, cp);
-#endif
- }
- }
- continue;
- }
+ if (level > 0 && (*p == ')' || *p == '}')) {
+ level--;
+ continue;
+ }
+
+ if (level == 0 && *p == ';') {
+ break;
+ }
+ }
+ return p;
+}
+
+/* dependency -> target... op [source...]
+ * op -> ':' | '::' | '!' */
+static void
+ParseDependency(char *line)
+{
+ VarEvalFlags eflags;
+ char *expanded_line;
+ const char *shellcmd = NULL;
+
+ /*
+ * For some reason - probably to make the parser impossible -
+ * a ';' can be used to separate commands from dependencies.
+ * Attempt to avoid ';' inside substitution patterns.
+ */
+ {
+ char *semicolon = FindSemicolon(line);
+ if (*semicolon != '\0') {
+ /* Terminate the dependency list at the ';' */
+ *semicolon = '\0';
+ shellcmd = semicolon + 1;
+ }
+ }
+
+ /*
+ * We now know it's a dependency line so it needs to have all
+ * variables expanded before being parsed.
+ *
+ * XXX: Ideally the dependency line would first be split into
+ * its left-hand side, dependency operator and right-hand side,
+ * and then each side would be expanded on its own. This would
+ * allow for the left-hand side to allow only defined variables
+ * and to allow variables on the right-hand side to be undefined
+ * as well.
+ *
+ * Parsing the line first would also prevent that targets
+ * generated from variable expressions are interpreted as the
+ * dependency operator, such as in "target${:U:} middle: source",
+ * in which the middle is interpreted as a source, not a target.
+ */
+
+ /* In lint mode, allow undefined variables to appear in
+ * dependency lines.
+ *
+ * Ideally, only the right-hand side would allow undefined
+ * variables since it is common to have no dependencies.
+ * Having undefined variables on the left-hand side is more
+ * unusual though. Since both sides are expanded in a single
+ * pass, there is not much choice what to do here.
+ *
+ * In normal mode, it does not matter whether undefined
+ * variables are allowed or not since as of 2020-09-14,
+ * Var_Parse does not print any parse errors in such a case.
+ * It simply returns the special empty string var_Error,
+ * which cannot be detected in the result of Var_Subst. */
+ eflags = DEBUG(LINT) ? VARE_WANTRES : VARE_UNDEFERR | VARE_WANTRES;
+ (void)Var_Subst(line, VAR_CMDLINE, eflags, &expanded_line);
+ /* TODO: handle errors */
+
+ /* Need a fresh list for the target nodes */
+ if (targets != NULL)
+ Lst_Free(targets);
+ targets = Lst_New();
+
+ ParseDoDependency(expanded_line);
+ free(expanded_line);
+
+ if (shellcmd != NULL)
+ ParseLine_ShellCommand(shellcmd);
+}
+
+static void
+ParseLine(char *line)
+{
+ if (ParseDirective(line))
+ return;
+
+ if (*line == '\t') {
+ ParseLine_ShellCommand(line + 1);
+ return;
+ }
#ifdef SYSVINCLUDE
- if (IsSysVInclude(line)) {
- /*
- * It's an S3/S5-style "include".
- */
- ParseTraditionalInclude(line);
- continue;
- }
+ if (IsSysVInclude(line)) {
+ /*
+ * It's an S3/S5-style "include".
+ */
+ ParseTraditionalInclude(line);
+ return;
+ }
#endif
+
#ifdef GMAKEEXPORT
- if (strncmp(line, "export", 6) == 0 &&
- isspace((unsigned char) line[6]) &&
- strchr(line, ':') == NULL) {
- /*
- * It's a Gmake "export".
- */
- ParseGmakeExport(line);
- continue;
- }
+ if (strncmp(line, "export", 6) == 0 && ch_isspace(line[6]) &&
+ strchr(line, ':') == NULL) {
+ /*
+ * It's a Gmake "export".
+ */
+ ParseGmakeExport(line);
+ return;
+ }
#endif
- if (Parse_IsVar(line)) {
- ParseFinishLine();
- Parse_DoVar(line, VAR_GLOBAL);
- continue;
- }
-#ifndef POSIX
- /*
- * To make life easier on novices, if the line is indented we
- * first make sure the line has a dependency operator in it.
- * If it doesn't have an operator and we're in a dependency
- * line's script, we assume it's actually a shell command
- * and add it to the current list of targets.
- */
- cp = line;
- if (isspace((unsigned char) line[0])) {
- while (isspace((unsigned char) *cp))
- cp++;
- while (*cp && (ParseIsEscaped(line, cp) ||
- *cp != ':' && *cp != '!')) {
- cp++;
- }
- if (*cp == '\0') {
- if (inLine) {
- Parse_Error(PARSE_WARNING,
- "Shell command needs a leading tab");
- goto shellCommand;
- }
- }
- }
-#endif
- ParseFinishLine();
+ if (ParseVarassign(line))
+ return;
- /*
- * For some reason - probably to make the parser impossible -
- * a ';' can be used to separate commands from dependencies.
- * Attempt to avoid ';' inside substitution patterns.
- */
- {
- int level = 0;
+ FinishDependencyGroup();
- for (cp = line; *cp != 0; cp++) {
- if (*cp == '\\' && cp[1] != 0) {
- cp++;
- continue;
- }
- if (*cp == '$' &&
- (cp[1] == '(' || cp[1] == '{')) {
- level++;
- continue;
- }
- if (level > 0) {
- if (*cp == ')' || *cp == '}') {
- level--;
- continue;
- }
- } else if (*cp == ';') {
- break;
- }
- }
- }
- if (*cp != 0)
- /* Terminate the dependency list at the ';' */
- *cp++ = 0;
- else
- cp = NULL;
+ ParseDependency(line);
+}
- /*
- * We now know it's a dependency line so it needs to have all
- * variables expanded before being parsed. Tell the variable
- * module to complain if some variable is undefined...
- */
- line = Var_Subst(line, VAR_CMD, VARE_UNDEFERR|VARE_WANTRES);
+/* Parse a top-level makefile into its component parts, incorporating them
+ * into the global dependency graph.
+ *
+ * Input:
+ * name The name of the file being read
+ * fd The open file to parse; will be closed at the end
+ */
+void
+Parse_File(const char *name, int fd)
+{
+ char *line; /* the line we're working on */
+ struct loadedfile *lf;
- /*
- * Need a non-circular list for the target nodes
- */
- if (targets != NULL)
- Lst_Free(targets);
+ lf = loadfile(name, fd);
+
+ assert(targets == NULL);
+ fatals = 0;
- targets = Lst_Init();
- inLine = TRUE;
+ if (name == NULL)
+ name = "(stdin)";
- ParseDoDependency(line);
- free(line);
+ Parse_SetInput(name, 0, -1, loadedfile_nextbuf, lf);
+ CurFile()->lf = lf;
- /* If there were commands after a ';', add them now */
- if (cp != NULL) {
- goto shellCommand;
- }
+ do {
+ while ((line = ParseReadLine()) != NULL) {
+ DEBUG2(PARSE, "ParseReadLine (%d): '%s'\n",
+ CurFile()->lineno, line);
+ ParseLine(line);
}
/*
* Reached EOF, but it may be just EOF of an include file...
*/
- } while (ParseEOF() == CONTINUE);
+ } while (ParseEOF());
+
+ FinishDependencyGroup();
if (fatals) {
(void)fflush(stdout);
(void)fprintf(stderr,
- "%s: Fatal errors encountered -- cannot continue",
- progname);
+ "%s: Fatal errors encountered -- cannot continue",
+ progname);
PrintOnError(NULL, NULL);
exit(1);
}
}
-/*-
- *---------------------------------------------------------------------
- * Parse_Init --
- * initialize the parsing module
- *
- * Results:
- * none
- *
- * Side Effects:
- * the parseIncPath list is initialized...
- *---------------------------------------------------------------------
- */
+/* Initialize the parsing module. */
void
Parse_Init(void)
{
mainNode = NULL;
- parseIncPath = Lst_Init();
- sysIncPath = Lst_Init();
- defIncPath = Lst_Init();
- includes = Lst_Init();
+ parseIncPath = Lst_New();
+ sysIncPath = Lst_New();
+ defSysIncPath = Lst_New();
+ Vector_Init(&includes, sizeof(IFile));
#ifdef CLEANUP
- targCmds = Lst_Init();
+ targCmds = Lst_New();
#endif
}
+/* Clean up the parsing module. */
void
Parse_End(void)
{
#ifdef CLEANUP
Lst_Destroy(targCmds, free);
- if (targets)
- Lst_Free(targets);
- Lst_Destroy(defIncPath, Dir_Destroy);
+ assert(targets == NULL);
+ Lst_Destroy(defSysIncPath, Dir_Destroy);
Lst_Destroy(sysIncPath, Dir_Destroy);
Lst_Destroy(parseIncPath, Dir_Destroy);
- Lst_Free(includes); /* Should be empty now */
+ assert(includes.len == 0);
+ Vector_Done(&includes);
#endif
}
@@ -3337,12 +3220,12 @@ Parse_End(void)
*
*-----------------------------------------------------------------------
*/
-Lst
+GNodeList *
Parse_MainName(void)
{
- Lst mainList; /* result list */
+ GNodeList *mainList;
- mainList = Lst_Init();
+ mainList = Lst_New();
if (mainNode == NULL) {
Punt("no target to make.");
@@ -3350,27 +3233,14 @@ Parse_MainName(void)
} else if (mainNode->type & OP_DOUBLEDEP) {
Lst_Append(mainList, mainNode);
Lst_AppendAll(mainList, mainNode->cohorts);
- }
- else
+ } else
Lst_Append(mainList, mainNode);
Var_Append(".TARGETS", mainNode->name, VAR_GLOBAL);
return mainList;
}
-/*-
- *-----------------------------------------------------------------------
- * ParseMark --
- * Add the filename and lineno to the GNode so that we remember
- * where it was first defined.
- *
- * Side Effects:
- * None.
- *
- *-----------------------------------------------------------------------
- */
-static void
-ParseMark(GNode *gn)
+int
+Parse_GetFatals(void)
{
- gn->fname = curFile->fname;
- gn->lineno = curFile->lineno;
+ return fatals;
}