aboutsummaryrefslogtreecommitdiff
path: root/contrib/bmake/parse.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/bmake/parse.c')
-rw-r--r--contrib/bmake/parse.c4058
1 files changed, 2084 insertions, 1974 deletions
diff --git a/contrib/bmake/parse.c b/contrib/bmake/parse.c
index d7bd65645aca..aca1015ab30d 100644
--- a/contrib/bmake/parse.c
+++ b/contrib/bmake/parse.c
@@ -1,4 +1,4 @@
-/* $NetBSD: parse.c,v 1.443 2020/11/16 21:39:22 rillig Exp $ */
+/* $NetBSD: parse.c,v 1.526 2021/01/10 21:20:46 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
@@ -124,7 +124,7 @@
#include "pathnames.h"
/* "@(#)parse.c 8.3 (Berkeley) 3/19/94" */
-MAKE_RCSID("$NetBSD: parse.c,v 1.443 2020/11/16 21:39:22 rillig Exp $");
+MAKE_RCSID("$NetBSD: parse.c,v 1.526 2021/01/10 21:20:46 rillig Exp $");
/* types and constants */
@@ -132,62 +132,64 @@ MAKE_RCSID("$NetBSD: parse.c,v 1.443 2020/11/16 21:39:22 rillig Exp $");
* Structure for a file being read ("included file")
*/
typedef struct IFile {
- char *fname; /* name of file (relative? absolute?) */
- 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 */
+ char *fname; /* name of file (relative? absolute?) */
+ 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;
+
+ /* Function to read more data, with a single opaque argument. */
+ ReadMoreProc readMore;
+ void *readMoreArg;
+
+ struct loadedfile *lf; /* loadedfile object, if any */
} IFile;
/*
* Tokens for target attributes
*/
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 */
+ 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 */
+ /* .MAIN and we don't have anything user-specified to make */
+ SP_MAIN,
+ 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
- SP_POSIX, /* .POSIX; not mentioned in the manual page */
+ SP_POSIX, /* .POSIX; not mentioned in the manual page */
#endif
- 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 */
+ 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;
typedef List SearchPathList;
@@ -203,19 +205,23 @@ static GNode *mainNode;
/* eval state */
-/* During parsing, the targets from the left-hand side of the currently
+/*
+ * 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". */
+ * See unit-tests/deptgt.mk, keyword "parse.c:targets".
+ */
static GNodeList *targets;
#ifdef CLEANUP
-/* All shell commands for all targets, in no particular order and possibly
+/*
+ * 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;
+ * easily understandable ownership over the allocated strings.
+ */
+static StringList targCmds = LST_INIT;
#endif
/*
@@ -233,7 +239,8 @@ static int fatals = 0;
* Variables for doing includes
*/
-/* The include chain of makefiles. At the bottom is the top-level makefile
+/*
+ * 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.
*
@@ -277,14 +284,14 @@ static Vector /* of IFile */ includes;
static IFile *
GetInclude(size_t i)
{
- return Vector_Get(&includes, i);
+ return Vector_Get(&includes, i);
}
/* The file that is currently being read. */
static IFile *
CurFile(void)
{
- return GetInclude(includes.len - 1);
+ return GetInclude(includes.len - 1);
}
/* include paths */
@@ -302,78 +309,76 @@ SearchPath *defSysIncPath; /* default for sysIncPath */
* 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 */
- GNodeType 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", SP_BEGIN, 0 },
- { ".DEFAULT", SP_DEFAULT, 0 },
- { ".DELETE_ON_ERROR", SP_DELETE_ON_ERROR, 0 },
- { ".END", SP_END, 0 },
- { ".ERROR", SP_ERROR, 0 },
+ { ".BEGIN", SP_BEGIN, OP_NONE },
+ { ".DEFAULT", SP_DEFAULT, OP_NONE },
+ { ".DELETE_ON_ERROR", SP_DELETE_ON_ERROR, OP_NONE },
+ { ".END", SP_END, OP_NONE },
+ { ".ERROR", SP_ERROR, OP_NONE },
{ ".EXEC", SP_ATTRIBUTE, OP_EXEC },
{ ".IGNORE", SP_IGNORE, OP_IGNORE },
- { ".INCLUDES", SP_INCLUDES, 0 },
- { ".INTERRUPT", SP_INTERRUPT, 0 },
+ { ".INCLUDES", SP_INCLUDES, OP_NONE },
+ { ".INTERRUPT", SP_INTERRUPT, OP_NONE },
{ ".INVISIBLE", SP_ATTRIBUTE, OP_INVISIBLE },
{ ".JOIN", SP_ATTRIBUTE, OP_JOIN },
- { ".LIBS", SP_LIBS, 0 },
+ { ".LIBS", SP_LIBS, OP_NONE },
{ ".MADE", SP_ATTRIBUTE, OP_MADE },
- { ".MAIN", SP_MAIN, 0 },
+ { ".MAIN", SP_MAIN, OP_NONE },
{ ".MAKE", SP_ATTRIBUTE, OP_MAKE },
- { ".MAKEFLAGS", SP_MFLAGS, 0 },
+ { ".MAKEFLAGS", SP_MFLAGS, OP_NONE },
{ ".META", SP_META, OP_META },
- { ".MFLAGS", SP_MFLAGS, 0 },
+ { ".MFLAGS", SP_MFLAGS, OP_NONE },
{ ".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 },
+ { ".NOTPARALLEL", SP_NOTPARALLEL, OP_NONE },
+ { ".NO_PARALLEL", SP_NOTPARALLEL, OP_NONE },
+ { ".NULL", SP_NULL, OP_NONE },
+ { ".OBJDIR", SP_OBJDIR, OP_NONE },
{ ".OPTIONAL", SP_ATTRIBUTE, OP_OPTIONAL },
- { ".ORDER", SP_ORDER, 0 },
- { ".PARALLEL", SP_PARALLEL, 0 },
- { ".PATH", SP_PATH, 0 },
+ { ".ORDER", SP_ORDER, OP_NONE },
+ { ".PARALLEL", SP_PARALLEL, OP_NONE },
+ { ".PATH", SP_PATH, OP_NONE },
{ ".PHONY", SP_PHONY, OP_PHONY },
#ifdef POSIX
- { ".POSIX", SP_POSIX, 0 },
+ { ".POSIX", SP_POSIX, OP_NONE },
#endif
{ ".PRECIOUS", SP_PRECIOUS, OP_PRECIOUS },
{ ".RECURSIVE", SP_ATTRIBUTE, OP_MAKE },
- { ".SHELL", SP_SHELL, 0 },
+ { ".SHELL", SP_SHELL, OP_NONE },
{ ".SILENT", SP_SILENT, OP_SILENT },
- { ".SINGLESHELL", SP_SINGLESHELL, 0 },
- { ".STALE", SP_STALE, 0 },
- { ".SUFFIXES", SP_SUFFIXES, 0 },
+ { ".SINGLESHELL", SP_SINGLESHELL, OP_NONE },
+ { ".STALE", SP_STALE, OP_NONE },
+ { ".SUFFIXES", SP_SUFFIXES, OP_NONE },
{ ".USE", SP_ATTRIBUTE, OP_USE },
{ ".USEBEFORE", SP_ATTRIBUTE, OP_USEBEFORE },
- { ".WAIT", SP_WAIT, 0 },
+ { ".WAIT", SP_WAIT, OP_NONE },
};
/* file loader */
struct loadedfile {
/* XXX: What is the lifetime of this path? Who manages the memory? */
- const char *path; /* name, for error reports */
- char *buf; /* contents buffer */
- size_t len; /* length of contents */
- size_t maplen; /* length of mmap area, or 0 */
- Boolean used; /* XXX: have we used the data yet */
+ const char *path; /* name, for error reports */
+ char *buf; /* contents buffer */
+ size_t len; /* length of contents */
+ Boolean used; /* XXX: have we used the data yet */
};
/* XXX: What is the lifetime of the path? Who manages the memory? */
static struct loadedfile *
-loadedfile_create(const char *path)
+loadedfile_create(const char *path, char *buf, size_t buflen)
{
struct loadedfile *lf;
lf = bmake_malloc(sizeof *lf);
lf->path = path == NULL ? "(stdin)" : path;
- lf->buf = NULL;
- lf->len = 0;
- lf->maplen = 0;
+ lf->buf = buf;
+ lf->len = buflen;
lf->used = FALSE;
return lf;
}
@@ -381,23 +386,16 @@ loadedfile_create(const char *path)
static void
loadedfile_destroy(struct loadedfile *lf)
{
- if (lf->buf != NULL) {
-#ifdef HAVE_MMAP
- if (lf->maplen > 0)
- munmap(lf->buf, lf->maplen);
- else
-#endif
- free(lf->buf);
- }
+ free(lf->buf);
free(lf);
}
/*
- * nextbuf() operation for loadedfile, as needed by the weird and twisted
- * logic below. Once that's cleaned up, we can get rid of lf->used...
+ * readMore() operation for loadedfile, as needed by the weird and twisted
+ * logic below. Once that's cleaned up, we can get rid of lf->used.
*/
static char *
-loadedfile_nextbuf(void *x, size_t *len)
+loadedfile_readMore(void *x, size_t *len)
{
struct loadedfile *lf = x;
@@ -427,68 +425,18 @@ load_getsize(int fd, size_t *ret)
* st_size is an off_t, which is 64 bits signed; *ret is
* size_t, which might be 32 bits unsigned or 64 bits
* unsigned. Rather than being elaborate, just punt on
- * files that are more than 2^31 bytes. We should never
- * see a makefile that size in practice...
+ * files that are more than 1 GiB. We should never
+ * see a makefile that size in practice.
*
* While we're at it reject negative sizes too, just in case.
*/
- if (st.st_size < 0 || st.st_size > 0x7fffffff)
+ if (st.st_size < 0 || st.st_size > 0x3fffffff)
return FALSE;
*ret = (size_t)st.st_size;
return TRUE;
}
-#ifdef HAVE_MMAP
-static Boolean
-loadedfile_mmap(struct loadedfile *lf, int fd)
-{
- static unsigned long pagesize = 0;
-
- if (!load_getsize(fd, &lf->len))
- return FALSE;
-
- /* found a size, try mmap */
-#ifdef _SC_PAGESIZE
- if (pagesize == 0)
- pagesize = (unsigned long)sysconf(_SC_PAGESIZE);
-#endif
- if (pagesize == 0 || pagesize == (unsigned long)-1)
- pagesize = 0x1000;
-
- /* round size up to a page */
- lf->maplen = pagesize * ((lf->len + pagesize - 1) / pagesize);
-
- /*
- * XXX hack for dealing with empty files; remove when
- * we're no longer limited by interfacing to the old
- * logic elsewhere in this file.
- */
- if (lf->maplen == 0)
- lf->maplen = pagesize;
-
- /*
- * FUTURE: remove PROT_WRITE when the parser no longer
- * needs to scribble on the input.
- */
- lf->buf = mmap(NULL, lf->maplen, PROT_READ|PROT_WRITE,
- MAP_FILE|MAP_COPY, fd, 0);
- if (lf->buf == MAP_FAILED)
- return FALSE;
-
- if (lf->len == lf->maplen && lf->buf[lf->len - 1] != '\n') {
- char *b = bmake_malloc(lf->len + 1);
- b[lf->len] = '\n';
- memcpy(b, lf->buf, lf->len++);
- munmap(lf->buf, lf->maplen);
- lf->maplen = 0;
- lf->buf = b;
- }
-
- return TRUE;
-}
-#endif
-
/*
* Read in a file.
*
@@ -501,79 +449,63 @@ loadedfile_mmap(struct loadedfile *lf, int fd)
static struct loadedfile *
loadfile(const char *path, int fd)
{
- struct loadedfile *lf;
- ssize_t result;
- size_t bufpos;
+ ssize_t n;
+ Buffer buf;
+ size_t filesize;
- 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;
- lf->len = 1024;
- lf->buf = bmake_malloc(lf->len);
+ if (load_getsize(fd, &filesize)) {
+ /*
+ * Avoid resizing the buffer later for no reason.
+ *
+ * At the same time leave space for adding a final '\n',
+ * just in case it is missing in the file.
+ */
+ filesize++;
+ } else
+ filesize = 1024;
+ Buf_InitSize(&buf, filesize);
- bufpos = 0;
for (;;) {
- assert(bufpos <= lf->len);
- if (bufpos == lf->len) {
- if (lf->len > SIZE_MAX/2) {
+ assert(buf.len <= buf.cap);
+ if (buf.len == buf.cap) {
+ if (buf.cap > 0x1fffffff) {
errno = EFBIG;
Error("%s: file too large", path);
- exit(1);
+ exit(2); /* Not 1 so -q can distinguish error */
}
- lf->len *= 2;
- lf->buf = bmake_realloc(lf->buf, lf->len);
+ Buf_Expand(&buf);
}
- assert(bufpos < lf->len);
- result = read(fd, lf->buf + bufpos, lf->len - bufpos);
- if (result < 0) {
+ assert(buf.len < buf.cap);
+ n = read(fd, buf.data + buf.len, buf.cap - buf.len);
+ if (n < 0) {
Error("%s: read error: %s", path, strerror(errno));
- exit(1);
+ exit(2); /* Not 1 so -q can distinguish error */
}
- if (result == 0)
+ if (n == 0)
break;
- bufpos += (size_t)result;
+ buf.len += (size_t)n;
}
- assert(bufpos <= lf->len);
- lf->len = bufpos;
+ assert(buf.len <= buf.cap);
- /* truncate malloc region to actual length (maybe not useful) */
- if (lf->len > 0) {
- /* as for mmap case, ensure trailing \n */
- if (lf->buf[lf->len - 1] != '\n')
- lf->len++;
- lf->buf = bmake_realloc(lf->buf, lf->len);
- lf->buf[lf->len - 1] = '\n';
- }
+ if (!Buf_EndsWith(&buf, '\n'))
+ Buf_AddByte(&buf, '\n');
-#ifdef HAVE_MMAP
-done:
-#endif
if (path != NULL)
close(fd);
- return lf;
+ {
+ struct loadedfile *lf = loadedfile_create(path,
+ buf.data, buf.len);
+ Buf_Destroy(&buf, FALSE);
+ return lf;
+ }
}
/* old code */
@@ -582,79 +514,82 @@ done:
static Boolean
ParseIsEscaped(const char *line, const char *c)
{
- Boolean active = FALSE;
- for (;;) {
- if (line == c)
- return active;
- if (*--c != '\\')
- return active;
- active = !active;
- }
+ Boolean active = FALSE;
+ for (;;) {
+ if (line == c)
+ return active;
+ if (*--c != '\\')
+ return active;
+ active = !active;
+ }
}
-/* Add the filename and lineno to the GNode so that we remember where it
- * was first defined. */
+/*
+ * 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;
+ 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. */
+/*
+ * 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 = 0;
- int end = sizeof parseKeywords / sizeof parseKeywords[0] - 1;
+ int start = 0;
+ int end = sizeof parseKeywords / sizeof parseKeywords[0] - 1;
- do {
- int cur = start + (end - start) / 2;
- int diff = strcmp(str, parseKeywords[cur].name);
+ do {
+ int curr = start + (end - start) / 2;
+ int diff = strcmp(str, parseKeywords[curr].name);
- if (diff == 0)
- return cur;
- if (diff < 0)
- end = cur - 1;
- else
- start = cur + 1;
- } while (start <= end);
+ if (diff == 0)
+ return curr;
+ if (diff < 0)
+ end = curr - 1;
+ else
+ start = curr + 1;
+ } while (start <= end);
- return -1;
+ return -1;
}
static void
PrintLocation(FILE *f, const char *fname, size_t lineno)
{
- char dirbuf[MAXPATHLEN+1];
- const char *dir, *base;
- void *dir_freeIt, *base_freeIt;
+ char dirbuf[MAXPATHLEN + 1];
+ FStr dir, base;
if (*fname == '/' || strcmp(fname, "(stdin)") == 0) {
- (void)fprintf(f, "\"%s\" line %zu: ", fname, lineno);
+ (void)fprintf(f, "\"%s\" line %u: ", fname, (unsigned)lineno);
return;
}
/* 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);
+ dir = Var_Value(".PARSEDIR", VAR_GLOBAL);
+ if (dir.str == NULL)
+ dir.str = ".";
+ if (dir.str[0] != '/')
+ dir.str = realpath(dir.str, dirbuf);
- base = Var_Value(".PARSEFILE", VAR_GLOBAL, &base_freeIt);
- if (base == NULL) {
- const char *slash = strrchr(fname, '/');
- base = slash != NULL ? slash + 1 : fname;
- }
+ base = Var_Value(".PARSEFILE", VAR_GLOBAL);
+ if (base.str == NULL)
+ base.str = str_basename(fname);
- (void)fprintf(f, "\"%s/%s\" line %zu: ", dir, base, lineno);
- bmake_free(base_freeIt);
- bmake_free(dir_freeIt);
+ (void)fprintf(f, "\"%s/%s\" line %u: ",
+ dir.str, base.str, (unsigned)lineno);
+
+ FStr_Done(&base);
+ FStr_Done(&dir);
}
static void
@@ -697,17 +632,19 @@ ParseErrorInternal(const char *fname, size_t lineno,
if (opts.debug_file != stderr && opts.debug_file != stdout) {
va_start(ap, fmt);
ParseVErrorInternal(opts.debug_file, fname, lineno, type,
- fmt, ap);
+ fmt, ap);
va_end(ap);
}
}
-/* Print a parse error message, including location information.
+/*
+ * Print a parse error message, including location information.
*
* If the level is PARSE_FATAL, continue parsing until the end of the
* current top-level makefile, then exit (see Parse_File).
*
- * Fmt is given without a trailing newline. */
+ * Fmt is given without a trailing newline.
+ */
void
Parse_Error(ParseErrorLevel type, const char *fmt, ...)
{
@@ -732,260 +669,278 @@ Parse_Error(ParseErrorLevel type, const char *fmt, ...)
if (opts.debug_file != stderr && opts.debug_file != stdout) {
va_start(ap, fmt);
ParseVErrorInternal(opts.debug_file, fname, lineno, type,
- fmt, ap);
+ fmt, ap);
va_end(ap);
}
}
-/* Parse and handle a .info, .warning or .error directive.
- * For an .error directive, immediately exit. */
-static Boolean
-ParseMessage(const char *directive)
+/*
+ * Parse and handle a .info, .warning or .error directive.
+ * For an .error directive, immediately exit.
+ */
+static void
+ParseMessage(ParseErrorLevel level, const char *levelName, const char *umsg)
{
- const char *p = directive;
- int mtype = *p == 'i' ? PARSE_INFO :
- *p == 'w' ? PARSE_WARNING : PARSE_FATAL;
- char *arg;
+ char *xmsg;
- while (ch_isalpha(*p))
- p++;
- if (!ch_isspace(*p))
- return FALSE; /* missing argument */
+ if (umsg[0] == '\0') {
+ Parse_Error(PARSE_FATAL, "Missing argument for \".%s\"",
+ levelName);
+ return;
+ }
- cpp_skip_whitespace(&p);
- (void)Var_Subst(p, VAR_CMDLINE, VARE_WANTRES, &arg);
- /* TODO: handle errors */
+ (void)Var_Subst(umsg, VAR_CMDLINE, VARE_WANTRES, &xmsg);
+ /* TODO: handle errors */
- Parse_Error(mtype, "%s", arg);
- free(arg);
+ Parse_Error(level, "%s", xmsg);
+ free(xmsg);
- if (mtype == PARSE_FATAL) {
- PrintOnError(NULL, NULL);
- exit(1);
- }
- return TRUE;
+ if (level == PARSE_FATAL) {
+ PrintOnError(NULL, NULL);
+ exit(1);
+ }
}
-/* Add the child to the parent's children.
+/*
+ * Add the child to the parent's children.
*
* 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. */
+ * which does not need to be informed once the child target has been made.
+ */
static void
LinkSource(GNode *pgn, GNode *cgn, Boolean isSpecial)
{
- if ((pgn->type & OP_DOUBLEDEP) && !Lst_IsEmpty(pgn->cohorts))
- pgn = pgn->cohorts->last->datum;
+ if ((pgn->type & OP_DOUBLEDEP) && !Lst_IsEmpty(&pgn->cohorts))
+ pgn = pgn->cohorts.last->datum;
- Lst_Append(pgn->children, cgn);
- pgn->unmade++;
+ Lst_Append(&pgn->children, cgn);
+ pgn->unmade++;
- /* Special targets like .END don't need any children. */
- if (!isSpecial)
- Lst_Append(cgn->parents, pgn);
+ /* Special targets like .END don't need any children. */
+ if (!isSpecial)
+ Lst_Append(&cgn->parents, pgn);
- if (DEBUG(PARSE)) {
- debug_printf("# %s: added child %s - %s\n",
- __func__, pgn->name, cgn->name);
- Targ_PrintNode(pgn, 0);
- Targ_PrintNode(cgn, 0);
- }
+ if (DEBUG(PARSE)) {
+ debug_printf("# %s: added child %s - %s\n",
+ __func__, pgn->name, cgn->name);
+ Targ_PrintNode(pgn, 0);
+ Targ_PrintNode(cgn, 0);
+ }
}
/* 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);
+ GNodeListNode *ln;
+
+ for (ln = targets->first; ln != NULL; ln = ln->next)
+ LinkSource(ln->datum, gn, isSpecial);
}
static Boolean
TryApplyDependencyOperator(GNode *gn, GNodeType op)
{
- /*
- * 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 & OP_OPMASK) != (gn->type & OP_OPMASK)))
- {
- Parse_Error(PARSE_FATAL, "Inconsistent operator for %s", gn->name);
- return FALSE;
- }
-
- if (op == OP_DOUBLEDEP && (gn->type & OP_OPMASK) == OP_DOUBLEDEP) {
/*
- * If the node was of the left-hand side of a '::' operator, we need
- * to create a new instance of it for the children and commands on
- * this dependency line since each of these dependency groups has its
- * own attributes and commands, separate from the others.
- *
- * The new instance is placed on the 'cohorts' list of the
- * initial one (note the initial one is not on its own cohorts list)
- * and the new instance is linked to all parents of the initial
- * instance.
+ * If the node occurred on the left-hand side of a dependency and the
+ * operator also defines a dependency, they must match.
*/
- GNode *cohort;
+ 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 FALSE;
+ }
- /*
- * Propagate copied bits to the initial node. They'll be propagated
- * back to the rest of the cohorts later.
- */
- gn->type |= op & ~OP_OPMASK;
+ if (op == OP_DOUBLEDEP && (gn->type & OP_OPMASK) == OP_DOUBLEDEP) {
+ /*
+ * If the node was of the left-hand side of a '::' operator,
+ * we need to create a new instance of it for the children
+ * and commands on this dependency line since each of these
+ * dependency groups has its own attributes and commands,
+ * separate from the others.
+ *
+ * The new instance is placed on the 'cohorts' list of the
+ * initial one (note the initial one is not on its own
+ * cohorts list) and the new instance is linked to all
+ * parents of the initial instance.
+ */
+ GNode *cohort;
- cohort = Targ_NewInternalNode(gn->name);
- if (doing_depend)
- ParseMark(cohort);
- /*
- * Make the cohort invisible as well to avoid duplicating it into
- * other variables. True, parents of this target won't tend to do
- * anything with their local variables, but better safe than
- * sorry. (I think this is pointless now, since the relevant list
- * traversals will no longer see this node anyway. -mycroft)
- */
- cohort->type = op | OP_INVISIBLE;
- Lst_Append(gn->cohorts, cohort);
- cohort->centurion = gn;
- gn->unmade_cohorts++;
- snprintf(cohort->cohort_num, sizeof cohort->cohort_num, "#%d",
- (unsigned int)gn->unmade_cohorts % 1000000);
- } else {
- /*
- * We don't want to nuke any previous flags (whatever they were) so we
- * just OR the new operator into the old.
- */
- gn->type |= op;
- }
+ /*
+ * Propagate copied bits to the initial node. They'll be
+ * propagated back to the rest of the cohorts later.
+ */
+ gn->type |= op & ~OP_OPMASK;
+
+ cohort = Targ_NewInternalNode(gn->name);
+ if (doing_depend)
+ ParseMark(cohort);
+ /*
+ * Make the cohort invisible as well to avoid duplicating it
+ * into other variables. True, parents of this target won't
+ * tend to do anything with their local variables, but better
+ * safe than sorry.
+ *
+ * (I think this is pointless now, since the relevant list
+ * traversals will no longer see this node anyway. -mycroft)
+ */
+ cohort->type = op | OP_INVISIBLE;
+ Lst_Append(&gn->cohorts, cohort);
+ cohort->centurion = gn;
+ gn->unmade_cohorts++;
+ snprintf(cohort->cohort_num, sizeof cohort->cohort_num, "#%d",
+ (unsigned int)gn->unmade_cohorts % 1000000);
+ } else {
+ /*
+ * We don't want to nuke any previous flags (whatever they
+ * were) so we just OR the new operator into the old.
+ */
+ gn->type |= op;
+ }
- return TRUE;
+ return TRUE;
}
static void
ApplyDependencyOperator(GNodeType op)
{
- GNodeListNode *ln;
- for (ln = targets->first; ln != NULL; ln = ln->next)
- if (!TryApplyDependencyOperator(ln->datum, op))
- break;
+ GNodeListNode *ln;
+
+ for (ln = targets->first; ln != NULL; ln = ln->next)
+ if (!TryApplyDependencyOperator(ln->datum, op))
+ break;
+}
+
+/*
+ * We add a .WAIT node in the dependency list. After any dynamic dependencies
+ * (and filename globbing) have happened, it is given a dependency on each
+ * previous child, back until the previous .WAIT node. The next child won't
+ * be scheduled until the .WAIT node is built.
+ *
+ * We give each .WAIT node a unique name (mainly for diagnostics).
+ */
+static void
+ParseDependencySourceWait(Boolean isSpecial)
+{
+ static int wait_number = 0;
+ char wait_src[16];
+ GNode *gn;
+
+ snprintf(wait_src, sizeof wait_src, ".WAIT_%u", ++wait_number);
+ gn = Targ_NewInternalNode(wait_src);
+ if (doing_depend)
+ ParseMark(gn);
+ gn->type = OP_WAIT | OP_PHONY | OP_DEPENDS | OP_NOTMAIN;
+ LinkToTargets(gn, isSpecial);
+
}
static Boolean
-ParseDoSrcKeyword(const char *src, ParseSpecial specType)
+ParseDependencySourceKeyword(const char *src, ParseSpecial specType)
{
- static int wait_number = 0;
- char wait_src[16];
- GNode *gn;
+ int keywd;
+ GNodeType op;
- if (*src == '.' && ch_isupper(src[1])) {
- int keywd = ParseFindKeyword(src);
- if (keywd != -1) {
- GNodeType op = parseKeywords[keywd].op;
- if (op != 0) {
+ if (*src != '.' || !ch_isupper(src[1]))
+ return FALSE;
+
+ keywd = ParseFindKeyword(src);
+ if (keywd == -1)
+ return FALSE;
+
+ op = parseKeywords[keywd].op;
+ if (op != OP_NONE) {
ApplyDependencyOperator(op);
return TRUE;
- }
- if (parseKeywords[keywd].spec == SP_WAIT) {
- /*
- * We add a .WAIT node in the dependency list.
- * After any dynamic dependencies (and filename globbing)
- * have happened, it is given a dependency on each
- * previous child, back until the previous .WAIT node.
- * The next child won't be scheduled until the .WAIT node
- * is built.
- * We give each .WAIT node a unique name (mainly for
- * diagnostics).
- */
- snprintf(wait_src, sizeof wait_src, ".WAIT_%u", ++wait_number);
- gn = Targ_NewInternalNode(wait_src);
- if (doing_depend)
- ParseMark(gn);
- gn->type = OP_WAIT | OP_PHONY | OP_DEPENDS | OP_NOTMAIN;
- LinkToTargets(gn, specType != SP_NOT);
+ }
+ if (parseKeywords[keywd].spec == SP_WAIT) {
+ ParseDependencySourceWait(specType != SP_NOT);
return TRUE;
- }
}
- }
- return FALSE;
+ return FALSE;
}
static void
-ParseDoSrcMain(const char *src)
-{
- /*
- * In a line like ".MAIN: source1 source2", it means we need to add
- * the sources of said target to the list of things to create.
- *
- * Note that this will only be invoked if the user didn't specify a
- * target on the command line. This is to allow .ifmake to succeed.
- *
- * XXX: Double-check all of the above comment.
- */
- 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);
+ParseDependencySourceMain(const char *src)
+{
+ /*
+ * In a line like ".MAIN: source1 source2", it means we need to add
+ * the sources of said target to the list of things to create.
+ *
+ * Note that this will only be invoked if the user didn't specify a
+ * target on the command line and the .MAIN occurs for the first time.
+ *
+ * See ParseDoDependencyTargetSpecial, branch SP_MAIN.
+ * See unit-tests/cond-func-make-main.mk.
+ */
+ 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);
+ParseDependencySourceOrder(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;
+ /*
+ * 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 != OP_NONE)
- 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
+ParseDependencySourceOther(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 != OP_NONE)
+ 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.
@@ -996,38 +951,41 @@ ParseDoSrcOther(const char *src, GNodeType tOp, ParseSpecial specType)
* src name of the source to handle
*/
static void
-ParseDoSrc(GNodeType tOp, const char *src, ParseSpecial specType)
+ParseDependencySource(GNodeType tOp, const char *src, ParseSpecial specType)
{
- if (ParseDoSrcKeyword(src, specType))
- return;
+ if (ParseDependencySourceKeyword(src, specType))
+ return;
- if (specType == SP_MAIN)
- ParseDoSrcMain(src);
- else if (specType == SP_ORDER)
- ParseDoSrcOrder(src);
- else
- ParseDoSrcOther(src, tOp, specType);
+ if (specType == SP_MAIN)
+ ParseDependencySourceMain(src);
+ else if (specType == SP_ORDER)
+ ParseDependencySourceOrder(src);
+ else
+ ParseDependencySourceOther(src, tOp, specType);
}
-/* If we have yet to decide on a main target to make, in the absence of any
+/*
+ * 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. */
+ * actually a real target (i.e. isn't a .USE or .EXEC rule) to be made.
+ */
static void
FindMainTarget(void)
{
- GNodeListNode *ln;
+ GNodeListNode *ln;
- if (mainNode != NULL)
- return;
+ 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;
+ for (ln = targets->first; ln != NULL; ln = ln->next) {
+ GNode *gn = ln->datum;
+ if (!(gn->type & OP_NOTARGET)) {
+ DEBUG1(MAKE, "Setting main node to \"%s\"\n", gn->name);
+ mainNode = gn;
+ Targ_SetMain(gn);
+ return;
+ }
}
- }
}
/*
@@ -1040,114 +998,121 @@ FindMainTarget(void)
static void
ParseErrorNoDependency(const char *lstart)
{
- if ((strncmp(lstart, "<<<<<<", 6) == 0) ||
- (strncmp(lstart, "======", 6) == 0) ||
- (strncmp(lstart, ">>>>>>", 6) == 0))
- Parse_Error(PARSE_FATAL,
+ 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\"",
+ 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");
+ } 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_WANTRES | VARE_UNDEFERR, &nested_val, &freeIt);
- /* TODO: handle errors */
- free(freeIt);
- cp += nested_p - cp;
- } else
- cp++;
- }
+ParseDependencyTargetWord(const char **pp, const char *lstart)
+{
+ const char *cp = *pp;
- *pp = cp;
+ 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;
+ FStr nested_val;
+
+ (void)Var_Parse(&nested_p, VAR_CMDLINE, VARE_NONE,
+ &nested_val);
+ /* TODO: handle errors */
+ FStr_Done(&nested_val);
+ cp += nested_p - cp;
+ } else
+ cp++;
+ }
+
+ *pp = cp;
}
/* Handle special targets like .PATH, .DEFAULT, .BEGIN, .ORDER. */
static void
ParseDoDependencyTargetSpecial(ParseSpecial *inout_specType,
- const char *line,
+ const char *line, /* XXX: bad name */
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:
- /* Allow targets from the command line to override the .MAIN node. */
- 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);
- gn->type |= OP_NOTMAIN|OP_SPECIAL;
- Lst_Append(targets, gn);
- break;
- }
- case SP_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. 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. */
- GNode *gn = GNode_New(".DEFAULT");
- gn->type |= OP_NOTMAIN|OP_TRANSFORM;
- Lst_Append(targets, gn);
- defaultNode = 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;
- }
+ switch (*inout_specType) {
+ case SP_PATH:
+ if (*inout_paths == NULL)
+ *inout_paths = Lst_New();
+ Lst_Append(*inout_paths, &dirSearchPath);
+ break;
+ case SP_MAIN:
+ /*
+ * Allow targets from the command line to override the
+ * .MAIN node.
+ */
+ 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);
+ gn->type |= OP_NOTMAIN | OP_SPECIAL;
+ Lst_Append(targets, gn);
+ break;
+ }
+ case SP_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. 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.
+ */
+ GNode *gn = GNode_New(".DEFAULT");
+ gn->type |= OP_NOTMAIN | OP_TRANSFORM;
+ Lst_Append(targets, gn);
+ defaultNode = 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;
+ }
}
/*
@@ -1155,221 +1120,225 @@ ParseDoDependencyTargetSpecial(ParseSpecial *inout_specType,
* Call on the suffix module to give us a path to modify.
*/
static Boolean
-ParseDoDependencyTargetPath(const char *line, SearchPathList **inout_paths)
+ParseDoDependencyTargetPath(const char *line, /* XXX: bad name */
+ SearchPathList **inout_paths)
{
- SearchPath *path;
+ SearchPath *path;
- path = Suff_GetPath(&line[5]);
- if (path == NULL) {
- Parse_Error(PARSE_FATAL,
- "Suffix '%s' not defined (yet)",
- &line[5]);
- return FALSE;
- }
+ path = Suff_GetPath(&line[5]);
+ if (path == NULL) {
+ Parse_Error(PARSE_FATAL,
+ "Suffix '%s' not defined (yet)", &line[5]);
+ return FALSE;
+ }
- if (*inout_paths == NULL)
- *inout_paths = Lst_New();
- Lst_Append(*inout_paths, path);
+ if (*inout_paths == NULL)
+ *inout_paths = Lst_New();
+ Lst_Append(*inout_paths, path);
- return TRUE;
+ 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,
+ParseDoDependencyTarget(const char *line, /* XXX: bad name */
+ ParseSpecial *inout_specType,
GNodeType *out_tOp, SearchPathList **inout_paths)
{
- int keywd;
+ int keywd;
- if (!(*line == '.' && ch_isupper(line[1])))
- return TRUE;
+ if (!(line[0] == '.' && 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;
- }
+ /*
+ * 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;
+ *inout_specType = parseKeywords[keywd].spec;
+ *out_tOp = parseKeywords[keywd].op;
- ParseDoDependencyTargetSpecial(inout_specType, line, inout_paths);
+ 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;
+ } 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)
+ParseDoDependencyTargetMundane(char *line, /* XXX: bad name */
+ StringList *curTargs)
{
- 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...
- */
- SearchPath *emptyPath = Lst_New();
+ 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...
+ */
+ SearchPath *emptyPath = SearchPath_New();
- Dir_Expand(line, emptyPath, curTargs);
+ 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);
- }
+ SearchPath_Free(emptyPath);
+ } 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. */
+ /* Apply the targets. */
- while (!Lst_IsEmpty(curTargs)) {
- char *targName = Lst_Dequeue(curTargs);
- GNode *gn = Suff_IsTransform(targName)
+ 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 (doing_depend)
+ ParseMark(gn);
- Lst_Append(targets, gn);
- }
+ Lst_Append(targets, gn);
+ }
}
static void
ParseDoDependencyTargetExtraWarn(char **pp, const char *lstart)
{
- Boolean warning = FALSE;
- char *cp = *pp;
+ Boolean warning = FALSE;
+ char *cp = *pp;
- while (*cp != '\0') {
- if (!ParseIsEscaped(lstart, cp) && (*cp == '!' || *cp == ':'))
- break;
- if (ParseIsEscaped(lstart, cp) || (*cp != ' ' && *cp != '\t'))
- warning = TRUE;
- cp++;
- }
- if (warning)
- Parse_Error(PARSE_WARNING, "Extra target ignored");
+ while (*cp != '\0') {
+ if (!ParseIsEscaped(lstart, cp) && (*cp == '!' || *cp == ':'))
+ break;
+ if (ParseIsEscaped(lstart, cp) || (*cp != ' ' && *cp != '\t'))
+ warning = TRUE;
+ cp++;
+ }
+ if (warning)
+ Parse_Error(PARSE_WARNING, "Extra target ignored");
- *pp = cp;
+ *pp = cp;
}
static void
ParseDoDependencyCheckSpec(ParseSpecial specType)
{
- switch (specType) {
- default:
- Parse_Error(PARSE_WARNING,
+ 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 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;
- }
+ break;
+ case SP_DEFAULT:
+ case SP_STALE:
+ case SP_BEGIN:
+ case SP_END:
+ case SP_ERROR:
+ case SP_INTERRUPT:
+ /*
+ * These 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;
+ }
}
static Boolean
ParseDoDependencyParseOp(char **pp, const char *lstart, GNodeType *out_op)
{
- const char *cp = *pp;
+ const char *cp = *pp;
- if (*cp == '!') {
- *out_op = OP_FORCE;
- (*pp)++;
- return TRUE;
- }
+ 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)++;
+ if (*cp == ':') {
+ if (cp[1] == ':') {
+ *out_op = OP_DOUBLEDEP;
+ (*pp) += 2;
+ } else {
+ *out_op = OP_DEPENDS;
+ (*pp)++;
+ }
+ return TRUE;
}
- return TRUE;
- }
- {
- const char *msg = lstart[0] == '.' ? "Unknown directive"
- : "Missing dependency operator";
- Parse_Error(PARSE_FATAL, "%s", msg);
- return FALSE;
- }
+ {
+ const char *msg = lstart[0] == '.'
+ ? "Unknown directive" : "Missing dependency operator";
+ Parse_Error(PARSE_FATAL, "%s", msg);
+ return FALSE;
+ }
}
static void
ClearPaths(SearchPathList *paths)
{
- if (paths != NULL) {
- SearchPathListNode *ln;
- for (ln = paths->first; ln != NULL; ln = ln->next)
- Dir_ClearPath(ln->datum);
- }
+ if (paths != NULL) {
+ SearchPathListNode *ln;
+ for (ln = paths->first; ln != NULL; ln = ln->next)
+ SearchPath_Clear(ln->datum);
+ }
- Dir_SetPATH();
+ Dir_SetPATH();
}
static void
ParseDoDependencySourcesEmpty(ParseSpecial specType, SearchPathList *paths)
{
- 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;
+ 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;
+ case SP_POSIX:
+ Var_Set("%POSIX", "1003.2", VAR_GLOBAL);
+ break;
#endif
- default:
- break;
- }
+ default:
+ break;
+ }
}
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 (paths != NULL) {
+ SearchPathListNode *ln;
+ for (ln = paths->first; ln != NULL; ln = ln->next)
+ (void)Dir_AddDir(ln->datum, dir);
+ }
}
/*
@@ -1403,28 +1372,28 @@ static void
ParseDoDependencySourceSpecial(ParseSpecial specType, char *word,
SearchPathList *paths)
{
- 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(FALSE, "%s", word);
- break;
- default:
- break;
- }
+ 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(FALSE, "%s", word);
+ break;
+ default:
+ break;
+ }
}
static Boolean
@@ -1436,163 +1405,175 @@ ParseDoDependencyTargets(char **inout_cp,
SearchPathList **inout_paths,
StringList *curTargs)
{
- char *cp = *inout_cp;
- char *line = *inout_line;
- char savec;
+ char *cp;
+ char *tgt = *inout_line;
+ char savec;
+ const char *p;
- for (;;) {
- /*
- * Here LINE points to the beginning of the next word, and
- * LSTART points to the actual beginning of the line.
- */
+ 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. */
- cp = line;
- ParseDependencyTargetWord(&cp, lstart);
+ /* Find the end of the next word. */
+ cp = tgt;
+ p = cp;
+ ParseDependencyTargetWord(&p, lstart);
+ cp += p - cp;
- /*
- * 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 == '(') {
- /*
- * Archives must be handled specially to make sure the OP_ARCHV
- * flag is set in their 'type' field, for one thing, and because
- * things like "archive(file1.o file2.o file3.o)" are permissible.
- * Arch_ParseArchive will set 'line' to be the first non-blank
- * after the archive-spec. It creates/finds nodes for the members
- * and places them on the given list, returning TRUE if all
- * went well and FALSE if there was an error in the
- * specification. On error, line should remain untouched.
- */
- if (!Arch_ParseArchive(&line, targets, VAR_CMDLINE)) {
- Parse_Error(PARSE_FATAL,
- "Error in archive specification: \"%s\"", line);
- return FALSE;
- } else {
- /* Done with this word; on to the next. */
- cp = line;
- continue;
- }
- }
+ /*
+ * 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 == '(') {
+ /*
+ * Archives must be handled specially to make sure the
+ * OP_ARCHV flag is set in their 'type' field, for one
+ * thing, and because things like "archive(file1.o
+ * file2.o file3.o)" are permissible.
+ *
+ * Arch_ParseArchive will set 'line' to be the first
+ * non-blank after the archive-spec. It creates/finds
+ * nodes for the members and places them on the given
+ * list, returning TRUE if all went well and FALSE if
+ * there was an error in the specification. On error,
+ * line should remain untouched.
+ */
+ if (!Arch_ParseArchive(&tgt, targets, VAR_CMDLINE)) {
+ Parse_Error(PARSE_FATAL,
+ "Error in archive specification: \"%s\"",
+ tgt);
+ return FALSE;
+ }
- if (!*cp) {
- ParseErrorNoDependency(lstart);
- return FALSE;
- }
+ cp = tgt;
+ continue;
+ }
- /* Insert a null terminator. */
- savec = *cp;
- *cp = '\0';
+ if (*cp == '\0') {
+ ParseErrorNoDependency(lstart);
+ return FALSE;
+ }
- if (!ParseDoDependencyTarget(line, inout_specType, inout_tOp,
- inout_paths))
- return FALSE;
+ /* Insert a null terminator. */
+ savec = *cp;
+ *cp = '\0';
- /*
- * Have word in line. Get or create its node and stick it at
- * the end of the targets list
- */
- 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);
+ if (!ParseDoDependencyTarget(tgt, inout_specType, inout_tOp,
+ inout_paths))
+ return FALSE;
- /* Don't need the inserted null terminator any more. */
- *cp = savec;
+ /*
+ * Have word in line. Get or create its node and stick it at
+ * the end of the targets list
+ */
+ if (*inout_specType == SP_NOT && *tgt != '\0')
+ ParseDoDependencyTargetMundane(tgt, curTargs);
+ else if (*inout_specType == SP_PATH && *tgt != '.' &&
+ *tgt != '\0')
+ Parse_Error(PARSE_WARNING, "Extra target (%s) ignored",
+ tgt);
- /*
- * If it is a special type and not .PATH, it's the only target we
- * allow on this line...
- */
- if (*inout_specType != SP_NOT && *inout_specType != SP_PATH)
- ParseDoDependencyTargetExtraWarn(&cp, lstart);
- else
- pp_skip_whitespace(&cp);
+ /* Don't need the inserted null terminator any more. */
+ *cp = savec;
- line = cp;
- if (*line == '\0')
- break;
- if ((*line == '!' || *line == ':') && !ParseIsEscaped(lstart, line))
- break;
- }
+ /*
+ * If it is a special type and not .PATH, it's the only target
+ * we allow on this line.
+ */
+ if (*inout_specType != SP_NOT && *inout_specType != SP_PATH)
+ ParseDoDependencyTargetExtraWarn(&cp, lstart);
+ else
+ pp_skip_whitespace(&cp);
- *inout_cp = cp;
- *inout_line = line;
- return TRUE;
+ tgt = cp;
+ if (*tgt == '\0')
+ break;
+ if ((*tgt == '!' || *tgt == ':') &&
+ !ParseIsEscaped(lstart, tgt))
+ break;
+ }
+
+ *inout_cp = cp;
+ *inout_line = tgt;
+ return TRUE;
}
static void
ParseDoDependencySourcesSpecial(char *start, char *end,
ParseSpecial specType, SearchPathList *paths)
{
- char savec;
+ 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;
- }
+ while (*start != '\0') {
+ while (*end != '\0' && !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 != '\0') {
- /*
- * 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] != '$') {
+ while (*start != '\0') {
/*
- * 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).
+ * The targets take real sources, so we must beware of archive
+ * specifications (i.e. things with left parentheses in them)
+ * and handle them accordingly.
*/
- break;
- }
- }
+ for (; *end != '\0' && !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;
- }
-
- while (!Lst_IsEmpty(sources)) {
- GNode *gn = Lst_Dequeue(sources);
- ParseDoSrc(tOp, gn->name, specType);
- }
- Lst_Free(sources);
- end = start;
- } else {
- if (*end) {
- *end = '\0';
- end++;
- }
+ if (*end == '(') {
+ GNodeList sources = LST_INIT;
+ if (!Arch_ParseArchive(&start, &sources, VAR_CMDLINE)) {
+ Parse_Error(PARSE_FATAL,
+ "Error in source archive spec \"%s\"",
+ start);
+ return FALSE;
+ }
+
+ while (!Lst_IsEmpty(&sources)) {
+ GNode *gn = Lst_Dequeue(&sources);
+ ParseDependencySource(tOp, gn->name, specType);
+ }
+ Lst_Done(&sources);
+ end = start;
+ } else {
+ if (*end != '\0') {
+ *end = '\0';
+ end++;
+ }
- ParseDoSrc(tOp, start, specType);
+ ParseDependencySource(tOp, start, specType);
+ }
+ pp_skip_whitespace(&end);
+ start = end;
}
- pp_skip_whitespace(&end);
- start = end;
- }
- return TRUE;
+ return TRUE;
}
-/* Parse a dependency line consisting of targets, followed by a dependency
+/*
+ * 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
@@ -1621,177 +1602,181 @@ ParseDoDependencySourcesMundane(char *start, char *end,
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
+ 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 */
- GNodeType 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 = OP_NONE;
-
- 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.
- * The targets themselves are now in the global variable 'targets'. */
- Lst_Free(curTargs);
- curTargs = NULL;
-
- if (!Lst_IsEmpty(targets))
- ParseDoDependencyCheckSpec(specType);
-
- /*
- * Have now parsed all the target names. Must parse the operator next.
- */
- if (!ParseDoDependencyParseOp(&cp, lstart, &op))
- goto out;
-
- /*
- * 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.
- */
- ApplyDependencyOperator(op);
-
- /*
- * Onward to the sources.
- *
- * LINE will now point to the first source word, if any, or the
- * end of the string if not.
- */
- pp_skip_whitespace(&cp);
- line = cp; /* XXX: 'line' is an inappropriate name */
-
- /*
- * Several special targets take different actions if present with no
- * sources:
- * a .SUFFIXES line with no sources clears out all old suffixes
- * a .PRECIOUS line makes all targets precious
- * a .IGNORE line ignores errors for all targets
- * a .SILENT line creates silence when making all targets
- * a .PATH removes all directories from the search path(s).
- */
- if (line[0] == '\0') {
- ParseDoDependencySourcesEmpty(specType, paths);
- } else if (specType == SP_MFLAGS) {
+ GNodeType tOp; /* operator from special target */
+ /* target names to be found and added to the targets list */
+ StringList curTargs = LST_INIT;
+ char *lstart = line;
+
/*
- * Call on functions in main.c to deal with these arguments and
- * set the initial character to a null-character so the loop to
- * get sources won't get anything
+ * 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.
*/
- Main_ParseArgLine(line);
- *line = '\0';
- } else if (specType == SP_SHELL) {
- if (!Job_ParseShell(line)) {
- Parse_Error(PARSE_FATAL, "improper shell specification");
- goto out;
- }
- *line = '\0';
- } else if (specType == SP_NOTPARALLEL || specType == SP_SINGLESHELL ||
- specType == SP_DELETE_ON_ERROR) {
- *line = '\0';
- }
-
- /* Now go for the sources. */
- if (specType == SP_SUFFIXES || specType == SP_PATH ||
- specType == SP_INCLUDES || specType == SP_LIBS ||
- specType == SP_NULL || specType == SP_OBJDIR)
- {
- ParseDoDependencySourcesSpecial(line, cp, specType, paths);
- if (paths) {
- Lst_Free(paths);
- paths = NULL;
- }
- if (specType == SP_PATH)
- Dir_SetPATH();
- } else {
- assert(paths == NULL);
- if (!ParseDoDependencySourcesMundane(line, cp, specType, tOp))
- goto out;
- }
-
- FindMainTarget();
+ ParseSpecial specType = SP_NOT;
+
+ DEBUG1(PARSE, "ParseDoDependency(%s)\n", line);
+ tOp = OP_NONE;
+
+ paths = NULL;
+
+ /*
+ * First, grind through the targets.
+ */
+ /* XXX: don't use line as an iterator variable */
+ if (!ParseDoDependencyTargets(&cp, &line, lstart, &specType, &tOp,
+ &paths, &curTargs))
+ goto out;
+
+ /*
+ * Don't need the list of target names anymore.
+ * The targets themselves are now in the global variable 'targets'.
+ */
+ Lst_Done(&curTargs);
+ Lst_Init(&curTargs);
+
+ if (!Lst_IsEmpty(targets))
+ ParseDoDependencyCheckSpec(specType);
+
+ /*
+ * Have now parsed all the target names. Must parse the operator next.
+ */
+ if (!ParseDoDependencyParseOp(&cp, lstart, &op))
+ goto out;
+
+ /*
+ * 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.
+ */
+ ApplyDependencyOperator(op);
+
+ /*
+ * Onward to the sources.
+ *
+ * LINE will now point to the first source word, if any, or the
+ * end of the string if not.
+ */
+ pp_skip_whitespace(&cp);
+ line = cp; /* XXX: 'line' is an inappropriate name */
+
+ /*
+ * Several special targets take different actions if present with no
+ * sources:
+ * a .SUFFIXES line with no sources clears out all old suffixes
+ * a .PRECIOUS line makes all targets precious
+ * a .IGNORE line ignores errors for all targets
+ * a .SILENT line creates silence when making all targets
+ * a .PATH removes all directories from the search path(s).
+ */
+ if (line[0] == '\0') {
+ 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
+ * get sources won't get anything
+ */
+ Main_ParseArgLine(line);
+ *line = '\0';
+ } else if (specType == SP_SHELL) {
+ if (!Job_ParseShell(line)) {
+ Parse_Error(PARSE_FATAL,
+ "improper shell specification");
+ goto out;
+ }
+ *line = '\0';
+ } else if (specType == SP_NOTPARALLEL || specType == SP_SINGLESHELL ||
+ specType == SP_DELETE_ON_ERROR) {
+ *line = '\0';
+ }
+
+ /* Now go for the sources. */
+ if (specType == SP_SUFFIXES || specType == SP_PATH ||
+ specType == SP_INCLUDES || specType == SP_LIBS ||
+ specType == SP_NULL || specType == SP_OBJDIR) {
+ ParseDoDependencySourcesSpecial(line, cp, specType, paths);
+ if (paths != NULL) {
+ Lst_Free(paths);
+ paths = NULL;
+ }
+ if (specType == SP_PATH)
+ Dir_SetPATH();
+ } else {
+ assert(paths == NULL);
+ if (!ParseDoDependencySourcesMundane(line, cp, specType, tOp))
+ goto out;
+ }
+
+ FindMainTarget();
out:
- if (paths != NULL)
- Lst_Free(paths);
- if (curTargs != NULL)
- Lst_Free(curTargs);
+ if (paths != NULL)
+ Lst_Free(paths);
+ Lst_Done(&curTargs);
}
typedef struct VarAssignParsed {
- const char *nameStart; /* unexpanded */
- const char *nameEnd; /* before operator adjustment */
- const char *eq; /* the '=' of the assignment operator */
+ 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. */
+/*
+ * 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;
+ const char *op = pvar->eq;
+ const char *const name = pvar->nameStart;
+ VarAssignOp type;
- if (op > name && op[-1] == '+') {
- type = VAR_APPEND;
- op--;
+ 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_DEFAULT;
- } else if (op > name && op[-1] == ':') {
- op--;
- type = VAR_SUBST;
+ } else if (op > name && op[-1] == ':') {
+ op--;
+ type = VAR_SUBST;
- } else if (op > name && op[-1] == '!') {
- op--;
- type = VAR_SHELL;
+ } else if (op > name && op[-1] == '!') {
+ op--;
+ type = VAR_SHELL;
- } else {
- type = VAR_NORMAL;
+ } else {
+ type = VAR_NORMAL;
#ifdef SUNSHCMD
- while (op > name && ch_isspace(op[-1]))
- op--;
+ 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;
- }
+ 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;
- }
+ {
+ 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,
+/*
+ * Parse a variable assignment, consisting of a single-word variable name,
* optional whitespace, an assignment operator, optional whitespace and the
* variable value.
*
@@ -1801,309 +1786,311 @@ AdjustVarassignOp(const VarAssignParsed *pvar, const char *value,
* C++=/usr/bin/CC
* is interpreted as "C+ +=" instead of "C++ =".
*
- * Used for both lines in a file and command line arguments. */
+ * Used for both lines in a file and command line arguments.
+ */
Boolean
Parse_IsVar(const char *p, VarAssign *out_var)
{
- VarAssignParsed pvar;
- const char *firstSpace = NULL;
- int level = 0;
+ VarAssignParsed pvar;
+ const char *firstSpace = NULL;
+ int level = 0;
- cpp_skip_hspace(&p); /* Skip to variable name */
+ cpp_skip_hspace(&p); /* Skip to variable name */
- /* 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;
+ /*
+ * 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;
+ pvar.nameEnd = NULL;
+ pvar.eq = NULL;
#endif
- /* Scan for one of the assignment operators outside a variable expansion */
- while (*p != '\0') {
- char ch = *p++;
- if (ch == '(' || ch == '{') {
- level++;
- continue;
- }
- if (ch == ')' || ch == '}') {
- level--;
- continue;
- }
+ /*
+ * Scan for one of the assignment operators outside a variable
+ * expansion.
+ */
+ while (*p != '\0') {
+ char ch = *p++;
+ if (ch == '(' || ch == '{') {
+ level++;
+ continue;
+ }
+ if (ch == ')' || ch == '}') {
+ level--;
+ continue;
+ }
- if (level != 0)
- continue;
+ if (level != 0)
+ continue;
- if (ch == ' ' || ch == '\t')
- if (firstSpace == NULL)
- firstSpace = p - 1;
- while (ch == ' ' || ch == '\t')
- ch = *p++;
+ if (ch == ' ' || ch == '\t')
+ if (firstSpace == NULL)
+ firstSpace = p - 1;
+ while (ch == ' ' || ch == '\t')
+ ch = *p++;
#ifdef SUNSHCMD
- if (ch == ':' && p[0] == 's' && p[1] == 'h') {
- p += 2;
- continue;
- }
+ if (ch == ':' && p[0] == 's' && p[1] == 'h') {
+ p += 2;
+ continue;
+ }
#endif
- 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 (*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 (ch == '=') {
+ pvar.eq = p - 1;
+ pvar.nameEnd = firstSpace != NULL ? firstSpace : p - 1;
+ cpp_skip_whitespace(&p);
+ AdjustVarassignOp(&pvar, p, out_var);
+ return TRUE;
+ }
+ 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 (firstSpace != NULL)
+ return FALSE;
}
- if (firstSpace != NULL)
- return FALSE;
- }
- return FALSE;
+ return FALSE;
}
+/*
+ * Check for syntax errors such as unclosed expressions or unknown modifiers.
+ */
static void
VarCheckSyntax(VarAssignOp type, const char *uvalue, GNode *ctxt)
{
- if (opts.lint) {
- if (type != VAR_SUBST && strchr(uvalue, '$') != NULL) {
- /* Check for syntax errors such as unclosed expressions or
- * unknown modifiers. */
- char *expandedValue;
+ if (opts.strict) {
+ if (type != VAR_SUBST && strchr(uvalue, '$') != NULL) {
+ char *expandedValue;
- (void)Var_Subst(uvalue, ctxt, VARE_NONE, &expandedValue);
- /* TODO: handle errors */
- free(expandedValue);
+ (void)Var_Subst(uvalue, ctxt, VARE_NONE,
+ &expandedValue);
+ /* TODO: handle errors */
+ free(expandedValue);
+ }
}
- }
}
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;
- Boolean savedPreserveUndefined = preserveUndefined;
-
- /* TODO: Can this assignment to preserveUndefined be moved further down
- * to the actually interesting Var_Subst call, without affecting any
- * edge cases?
- *
- * It might affect the implicit expansion of the variable name in the
- * Var_Exists and Var_Set calls, even though it's unlikely that anyone
- * cared about this edge case when adding this code. In addition,
- * variable assignments should not refer to any undefined variables in
- * the variable name. */
- preserveUndefined = TRUE;
-
- /*
- * 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);
-
- (void)Var_Subst(uvalue, ctxt, VARE_WANTRES|VARE_KEEP_DOLLAR, &evalue);
- /* TODO: handle errors */
- preserveUndefined = savedPreserveUndefined;
- avalue = evalue;
- Var_Set(name, avalue, ctxt);
-
- *out_avalue = avalue;
- *out_avalue_freeIt = evalue;
+ FStr *out_avalue)
+{
+ const char *avalue;
+ char *evalue;
+
+ /*
+ * 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);
+
+ (void)Var_Subst(uvalue, ctxt,
+ VARE_WANTRES | VARE_KEEP_DOLLAR | VARE_KEEP_UNDEF, &evalue);
+ /* TODO: handle errors */
+
+ avalue = evalue;
+ Var_Set(name, avalue, ctxt);
+
+ *out_avalue = (FStr){ avalue, evalue };
}
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_WANTRES | VARE_UNDEFERR, &ecmd);
- /* TODO: handle errors */
- cmd = cmd_freeIt = ecmd;
- }
+ FStr *out_avalue)
+{
+ FStr cmd;
+ const char *errfmt;
+ char *cmdOut;
+
+ cmd = FStr_InitRefer(uvalue);
+ if (strchr(cmd.str, '$') != NULL) {
+ char *expanded;
+ (void)Var_Subst(cmd.str, VAR_CMDLINE,
+ VARE_WANTRES | VARE_UNDEFERR, &expanded);
+ /* TODO: handle errors */
+ cmd = FStr_InitOwn(expanded);
+ }
- cmdOut = Cmd_Exec(cmd, &errfmt);
- Var_Set(name, cmdOut, ctxt);
- *out_avalue = *out_avalue_freeIt = cmdOut;
+ cmdOut = Cmd_Exec(cmd.str, &errfmt);
+ Var_Set(name, cmdOut, ctxt);
+ *out_avalue = FStr_InitOwn(cmdOut);
- if (errfmt)
- Parse_Error(PARSE_WARNING, errfmt, cmd);
+ if (errfmt != NULL)
+ Parse_Error(PARSE_WARNING, errfmt, cmd.str);
- free(cmd_freeIt);
+ FStr_Done(&cmd);
}
-/* Perform a variable assignment.
+/*
+ * 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. */
+ * 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;
+ GNode *ctxt, FStr *out_TRUE_avalue)
+{
+ FStr avalue = FStr_InitRefer(uvalue);
+
+ if (op == VAR_APPEND)
+ Var_Append(name, uvalue, ctxt);
+ else if (op == VAR_SUBST)
+ VarAssign_EvalSubst(name, uvalue, ctxt, &avalue);
+ else if (op == VAR_SHELL)
+ VarAssign_EvalShell(name, uvalue, ctxt, &avalue);
+ else {
+ if (op == VAR_DEFAULT && Var_Exists(name, ctxt))
+ return FALSE;
- 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;
+ /* Normal assignment -- just do it. */
+ Var_Set(name, uvalue, ctxt);
}
- /* Normal assignment -- just do it. */
- Var_Set(name, uvalue, ctxt);
- }
-
- *out_avalue = avalue;
- *out_avalue_freeIt = avalue_freeIt;
- return TRUE;
+ *out_TRUE_avalue = avalue;
+ 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(name, ".CURDIR") == 0) {
- /*
- * Someone is being (too?) clever...
- * Let's pretend they know what they are doing and
- * re-initialize the 'cur' CachedDir.
- */
- Dir_InitCur(avalue);
- Dir_SetPATH();
- } else if (strcmp(name, MAKE_JOB_PREFIX) == 0)
- Job_SetPrefix();
- else if (strcmp(name, MAKE_EXPORTED) == 0)
- Var_Export(avalue, FALSE);
+ if (strcmp(name, MAKEOVERRIDES) == 0)
+ Main_ExportMAKEFLAGS(FALSE); /* re-export MAKEFLAGS */
+ else if (strcmp(name, ".CURDIR") == 0) {
+ /*
+ * Someone is being (too?) clever...
+ * Let's pretend they know what they are doing and
+ * re-initialize the 'cur' CachedDir.
+ */
+ Dir_InitCur(avalue);
+ Dir_SetPATH();
+ } else if (strcmp(name, MAKE_JOB_PREFIX) == 0)
+ Job_SetPrefix();
+ else if (strcmp(name, MAKE_EXPORTED) == 0)
+ Var_ExportVars(avalue);
}
/* 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;
+ FStr avalue; /* actual value (maybe expanded) */
- VarCheckSyntax(var->op, var->value, ctxt);
- if (VarAssign_Eval(var->varname, var->op, var->value, ctxt,
- &avalue, &avalue_freeIt))
- VarAssignSpecial(var->varname, avalue);
+ VarCheckSyntax(var->op, var->value, ctxt);
+ if (VarAssign_Eval(var->varname, var->op, var->value, ctxt, &avalue)) {
+ VarAssignSpecial(var->varname, avalue.str);
+ FStr_Done(&avalue);
+ }
- free(avalue_freeIt);
- free(var->varname);
+ free(var->varname);
}
-/* See if the command possibly calls a sub-make by using the variable
- * expressions ${.MAKE}, ${MAKE} or the plain word "make". */
+/*
+ * See if the command possibly calls a sub-make by using the variable
+ * expressions ${.MAKE}, ${MAKE} or the plain word "make".
+ */
static Boolean
MaybeSubMake(const char *cmd)
{
- const char *start;
+ const char *start;
- for (start = cmd; *start != '\0'; start++) {
- const char *p = start;
- char endc;
+ for (start = cmd; *start != '\0'; start++) {
+ const char *p = start;
+ char endc;
- /* XXX: What if progname != "make"? */
- if (p[0] == 'm' && p[1] == 'a' && p[2] == 'k' && p[3] == 'e')
- if (start == cmd || !ch_isalnum(p[-1]))
- if (!ch_isalnum(p[4]))
- return TRUE;
+ /* XXX: What if progname != "make"? */
+ if (p[0] == 'm' && p[1] == 'a' && p[2] == 'k' && p[3] == 'e')
+ if (start == cmd || !ch_isalnum(p[-1]))
+ if (!ch_isalnum(p[4]))
+ return TRUE;
- if (*p != '$')
- continue;
- p++;
+ if (*p != '$')
+ continue;
+ p++;
- if (*p == '{')
- endc = '}';
- else if (*p == '(')
- endc = ')';
- else
- continue;
- p++;
+ if (*p == '{')
+ endc = '}';
+ else if (*p == '(')
+ endc = ')';
+ else
+ continue;
+ p++;
- if (*p == '.') /* Accept either ${.MAKE} or ${MAKE}. */
- p++;
+ if (*p == '.') /* Accept either ${.MAKE} or ${MAKE}. */
+ p++;
- if (p[0] == 'M' && p[1] == 'A' && p[2] == 'K' && p[3] == 'E')
- if (p[4] == endc)
- return TRUE;
- }
- return FALSE;
+ if (p[0] == 'M' && p[1] == 'A' && p[2] == 'K' && p[3] == 'E')
+ if (p[4] == endc)
+ return TRUE;
+ }
+ return FALSE;
}
-/* Append the command to the target node.
+/*
+ * Append the command to the target node.
*
* The node may be marked as a submake node if the command is determined to
- * be that. */
+ * be that.
+ */
static void
ParseAddCmd(GNode *gn, char *cmd)
{
- /* Add to last (ie current) cohort for :: targets */
- 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)) {
- Lst_Append(gn->commands, cmd);
- if (MaybeSubMake(cmd))
- gn->type |= OP_SUBMAKE;
- ParseMark(gn);
- } else {
+ /* Add to last (ie current) cohort for :: targets */
+ 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)) {
+ Lst_Append(&gn->commands, cmd);
+ if (MaybeSubMake(cmd))
+ gn->type |= OP_SUBMAKE;
+ ParseMark(gn);
+ } else {
#if 0
- /* XXX: We cannot do this until we fix the tree */
- Lst_Append(gn->commands, cmd);
- Parse_Error(PARSE_WARNING,
- "overriding commands for target \"%s\"; "
- "previous commands defined at %s: %d ignored",
- gn->name, gn->fname, gn->lineno);
+ /* XXX: We cannot do this until we fix the tree */
+ Lst_Append(&gn->commands, cmd);
+ Parse_Error(PARSE_WARNING,
+ "overriding commands for target \"%s\"; "
+ "previous commands defined at %s: %d ignored",
+ gn->name, gn->fname, gn->lineno);
#else
- Parse_Error(PARSE_WARNING,
+ Parse_Error(PARSE_WARNING,
"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);
+ ParseErrorInternal(gn->fname, (size_t)gn->lineno, PARSE_WARNING,
+ "using previous script for \"%s\" defined here",
+ gn->name);
#endif
- }
+ }
}
-/* Add a directory to the path searched for included makefiles bracketed
- * by double-quotes. */
+/*
+ * Add a directory to the path searched for included makefiles bracketed
+ * by double-quotes.
+ */
void
Parse_AddIncludeDir(const char *dir)
{
- (void)Dir_AddDir(parseIncPath, dir);
+ (void)Dir_AddDir(parseIncPath, dir);
}
-/* Handle one of the .[-ds]include directives by remembering the current file
+/*
+ * Handle one of the .[-ds]include directives by remembering the current file
* and pushing the included file on the stack. After the included file has
* finished, parsing continues with the including file; see Parse_SetInput
* and ParseEOF.
@@ -2115,314 +2102,331 @@ Parse_AddIncludeDir(const char *dir)
static void
Parse_include_file(char *file, Boolean isSystem, Boolean depinc, Boolean silent)
{
- struct loadedfile *lf;
- char *fullname; /* full pathname of file */
- char *newName;
- char *slash, *incdir;
- int fd;
- int i;
+ struct loadedfile *lf;
+ char *fullname; /* full pathname of file */
+ char *newName;
+ char *slash, *incdir;
+ int fd;
+ int i;
- fullname = file[0] == '/' ? bmake_strdup(file) : NULL;
+ fullname = file[0] == '/' ? bmake_strdup(file) : NULL;
- if (fullname == NULL && !isSystem) {
- /*
- * Include files contained in double-quotes are first searched
- * relative to the including file's location. We don't want to
- * cd there, of course, so we just tack on the old file's
- * leading path components and call Dir_FindFile to see if
- * we can locate the file.
- */
+ if (fullname == NULL && !isSystem) {
+ /*
+ * Include files contained in double-quotes are first searched
+ * relative to the including file's location. We don't want to
+ * cd there, of course, so we just tack on the old file's
+ * leading path components and call Dir_FindFile to see if
+ * we can locate the file.
+ */
- incdir = bmake_strdup(CurFile()->fname);
- slash = strrchr(incdir, '/');
- if (slash != NULL) {
- *slash = '\0';
- /* Now do lexical processing of leading "../" on the filename */
- for (i = 0; strncmp(file + i, "../", 3) == 0; i += 3) {
- slash = strrchr(incdir + 1, '/');
- if (slash == NULL || strcmp(slash, "/..") == 0)
- break;
- *slash = '\0';
- }
- newName = str_concat3(incdir, "/", file + i);
- fullname = Dir_FindFile(newName, parseIncPath);
- if (fullname == NULL)
- fullname = Dir_FindFile(newName, dirSearchPath);
- free(newName);
- }
- free(incdir);
+ incdir = bmake_strdup(CurFile()->fname);
+ slash = strrchr(incdir, '/');
+ if (slash != NULL) {
+ *slash = '\0';
+ /*
+ * Now do lexical processing of leading "../" on the
+ * filename.
+ */
+ for (i = 0; strncmp(file + i, "../", 3) == 0; i += 3) {
+ slash = strrchr(incdir + 1, '/');
+ if (slash == NULL || strcmp(slash, "/..") == 0)
+ break;
+ *slash = '\0';
+ }
+ newName = str_concat3(incdir, "/", file + i);
+ fullname = Dir_FindFile(newName, parseIncPath);
+ if (fullname == NULL)
+ fullname = Dir_FindFile(newName,
+ &dirSearchPath);
+ free(newName);
+ }
+ free(incdir);
+
+ if (fullname == NULL) {
+ /*
+ * Makefile wasn't found in same directory as included
+ * makefile.
+ *
+ * Search for it first on the -I search path, then on
+ * the .PATH search path, if not found in a -I
+ * directory. If we have a suffix-specific path, we
+ * should use that.
+ */
+ const char *suff;
+ SearchPath *suffPath = NULL;
+
+ if ((suff = strrchr(file, '.')) != NULL) {
+ suffPath = Suff_GetPath(suff);
+ if (suffPath != NULL)
+ fullname = Dir_FindFile(file, suffPath);
+ }
+ if (fullname == NULL) {
+ fullname = Dir_FindFile(file, parseIncPath);
+ if (fullname == NULL)
+ fullname = Dir_FindFile(file,
+ &dirSearchPath);
+ }
+ }
+ }
+ /* Looking for a system file or file still not found */
if (fullname == NULL) {
- /*
- * Makefile wasn't found in same directory as included makefile.
- * Search for it first on the -I search path,
- * then on the .PATH search path, if not found in a -I directory.
- * If we have a suffix specific path we should use that.
- */
- const char *suff;
- SearchPath *suffPath = NULL;
-
- if ((suff = strrchr(file, '.'))) {
- suffPath = Suff_GetPath(suff);
- if (suffPath != NULL)
- fullname = Dir_FindFile(file, suffPath);
- }
- if (fullname == NULL) {
- fullname = Dir_FindFile(file, parseIncPath);
- if (fullname == NULL)
- fullname = Dir_FindFile(file, dirSearchPath);
- }
- }
- }
-
- /* Looking for a system file or file still not found */
- if (fullname == NULL) {
- /*
- * Look for it on the system path
- */
- SearchPath *path = Lst_IsEmpty(sysIncPath) ? defSysIncPath : sysIncPath;
- fullname = Dir_FindFile(file, path);
- }
-
- if (fullname == NULL) {
- if (!silent)
- Parse_Error(PARSE_FATAL, "Could not find %s", file);
- return;
- }
-
- /* Actually open the file... */
- fd = open(fullname, O_RDONLY);
- if (fd == -1) {
- if (!silent)
- Parse_Error(PARSE_FATAL, "Cannot open %s", fullname);
- free(fullname);
- return;
- }
-
- /* load it */
- lf = loadfile(fullname, fd);
-
- /* Start reading from this file next */
- Parse_SetInput(fullname, 0, -1, loadedfile_nextbuf, lf);
- CurFile()->lf = lf;
- if (depinc)
- doing_depend = depinc; /* only turn it on */
+ /*
+ * Look for it on the system path
+ */
+ SearchPath *path = Lst_IsEmpty(sysIncPath) ? defSysIncPath
+ : sysIncPath;
+ fullname = Dir_FindFile(file, path);
+ }
+
+ if (fullname == NULL) {
+ if (!silent)
+ Parse_Error(PARSE_FATAL, "Could not find %s", file);
+ return;
+ }
+
+ /* Actually open the file... */
+ fd = open(fullname, O_RDONLY);
+ if (fd == -1) {
+ if (!silent)
+ Parse_Error(PARSE_FATAL, "Cannot open %s", fullname);
+ free(fullname);
+ return;
+ }
+
+ /* load it */
+ lf = loadfile(fullname, fd);
+
+ /* Start reading from this file next */
+ Parse_SetInput(fullname, 0, -1, loadedfile_readMore, lf);
+ CurFile()->lf = lf;
+ if (depinc)
+ doing_depend = depinc; /* only turn it on */
}
static void
-ParseDoInclude(char *line)
+ParseDoInclude(char *line /* XXX: bad name */)
{
- char endc; /* the character which ends the file spec */
- char *cp; /* current position in file spec */
- Boolean silent = *line != 'i';
- char *file = line + (silent ? 8 : 7);
+ char endc; /* the character which ends the file spec */
+ char *cp; /* current position in file spec */
+ Boolean silent = line[0] != 'i';
+ char *file = line + (silent ? 8 : 7);
- /* Skip to delimiter character so we know where to look */
- pp_skip_hspace(&file);
+ /* Skip to delimiter character so we know where to look */
+ pp_skip_hspace(&file);
- if (*file != '"' && *file != '<') {
- Parse_Error(PARSE_FATAL,
+ if (*file != '"' && *file != '<') {
+ Parse_Error(PARSE_FATAL,
".include filename must be delimited by '\"' or '<'");
- return;
- }
-
- /*
- * Set the search path on which to find the include file based on the
- * characters which bracket its name. Angle-brackets imply it's
- * a system Makefile while double-quotes imply it's a user makefile
- */
- if (*file == '<')
- endc = '>';
- else
- endc = '"';
-
- /* Skip to matching delimiter */
- for (cp = ++file; *cp && *cp != endc; cp++)
- continue;
-
- if (*cp != endc) {
- Parse_Error(PARSE_FATAL,
+ return;
+ }
+
+ /*
+ * Set the search path on which to find the include file based on the
+ * characters which bracket its name. Angle-brackets imply it's
+ * a system Makefile while double-quotes imply it's a user makefile
+ */
+ if (*file == '<')
+ endc = '>';
+ else
+ endc = '"';
+
+ /* Skip to matching delimiter */
+ for (cp = ++file; *cp != '\0' && *cp != endc; cp++)
+ continue;
+
+ if (*cp != endc) {
+ Parse_Error(PARSE_FATAL,
"Unclosed .include filename. '%c' expected", endc);
- return;
- }
+ return;
+ }
- *cp = '\0';
+ *cp = '\0';
- /*
- * Substitute for any variables in the filename before trying to
- * find the file.
- */
- (void)Var_Subst(file, VAR_CMDLINE, VARE_WANTRES, &file);
- /* TODO: handle errors */
+ /*
+ * Substitute for any variables in the filename before trying to
+ * find the file.
+ */
+ (void)Var_Subst(file, VAR_CMDLINE, VARE_WANTRES, &file);
+ /* TODO: handle errors */
- Parse_include_file(file, endc == '>', *line == 'd', silent);
- free(file);
+ Parse_include_file(file, endc == '>', line[0] == 'd', silent);
+ free(file);
}
-/* Split filename into dirname + basename, then assign these to the
- * given variables. */
+/*
+ * Split filename into dirname + basename, then assign these to the
+ * given variables.
+ */
static void
SetFilenameVars(const char *filename, const char *dirvar, const char *filevar)
{
- const char *slash, *dirname, *basename;
- void *freeIt;
+ const char *slash, *dirname, *basename;
+ void *freeIt;
- slash = strrchr(filename, '/');
- if (slash == NULL) {
- dirname = curdir;
- basename = filename;
- freeIt = NULL;
- } else {
- dirname = freeIt = bmake_strsedup(filename, slash);
- basename = slash + 1;
- }
+ slash = strrchr(filename, '/');
+ if (slash == NULL) {
+ dirname = curdir;
+ basename = filename;
+ freeIt = NULL;
+ } else {
+ dirname = freeIt = bmake_strsedup(filename, slash);
+ basename = slash + 1;
+ }
- Var_Set(dirvar, dirname, VAR_GLOBAL);
- Var_Set(filevar, basename, VAR_GLOBAL);
+ Var_Set(dirvar, dirname, VAR_GLOBAL);
+ Var_Set(filevar, basename, VAR_GLOBAL);
- DEBUG5(PARSE, "%s: ${%s} = `%s' ${%s} = `%s'\n",
- __func__, dirvar, dirname, filevar, basename);
- free(freeIt);
+ DEBUG5(PARSE, "%s: ${%s} = `%s' ${%s} = `%s'\n",
+ __func__, dirvar, dirname, filevar, basename);
+ free(freeIt);
}
-/* Return the immediately including file.
+/*
+ * Return the immediately including file.
*
* This is made complicated since the .for loop is implemented as a special
- * kind of .include; see For_Run. */
+ * kind of .include; see For_Run.
+ */
static const char *
GetActuallyIncludingFile(void)
{
- size_t i;
- const IFile *incs = GetInclude(0);
+ 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;
+ 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)
{
- const char *including;
+ const char *including;
- SetFilenameVars(filename, ".PARSEDIR", ".PARSEFILE");
+ SetFilenameVars(filename, ".PARSEDIR", ".PARSEFILE");
- including = GetActuallyIncludingFile();
- if (including != NULL) {
- SetFilenameVars(including,
- ".INCLUDEDFROMDIR", ".INCLUDEDFROMFILE");
- } else {
- Var_Delete(".INCLUDEDFROMDIR", VAR_GLOBAL);
- Var_Delete(".INCLUDEDFROMFILE", VAR_GLOBAL);
- }
+ including = GetActuallyIncludingFile();
+ if (including != NULL) {
+ SetFilenameVars(including,
+ ".INCLUDEDFROMDIR", ".INCLUDEDFROMFILE");
+ } else {
+ Var_Delete(".INCLUDEDFROMDIR", VAR_GLOBAL);
+ Var_Delete(".INCLUDEDFROMFILE", VAR_GLOBAL);
+ }
}
static Boolean
StrContainsWord(const char *str, const char *word)
{
- size_t strLen = strlen(str);
- size_t wordLen = strlen(word);
- const char *p, *end;
+ 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 */
+ 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 */
+ 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;
+ 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. */
-/* XXX: The paths in this list don't seem to be normalized in any way. */
+/*
+ * XXX: Searching through a set of words with this linear search is
+ * inefficient for variables that contain thousands of words.
+ *
+ * XXX: The paths in this list don't seem to be normalized in any way.
+ */
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;
+ FStr val = Var_Value(varname, VAR_GLOBAL);
+ Boolean found = val.str != NULL && StrContainsWord(val.str, word);
+ FStr_Done(&val);
+ return found;
}
-/* Track the makefiles we read - so makefiles can set dependencies on them.
+/*
+ * Track the makefiles we read - so makefiles can set dependencies on them.
* Avoid adding anything more than once.
*
* Time complexity: O(n) per call, in total O(n^2), where n is the number
- * of makefiles that have been loaded. */
+ * of makefiles that have been loaded.
+ */
static void
ParseTrackInput(const char *name)
{
- if (!VarContainsWord(MAKE_MAKEFILES, name))
- Var_Append(MAKE_MAKEFILES, name, VAR_GLOBAL);
+ if (!VarContainsWord(MAKE_MAKEFILES, name))
+ Var_Append(MAKE_MAKEFILES, name, VAR_GLOBAL);
}
-/* Start parsing from the given source.
+/*
+ * Start parsing from the given source.
*
- * The given file is added to the includes stack. */
+ * 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)
-{
- IFile *curFile;
- char *buf;
- size_t len;
- Boolean fromForLoop = name == NULL;
-
- if (fromForLoop)
- name = CurFile()->fname;
- else
- ParseTrackInput(name);
-
- if (DEBUG(PARSE))
- 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;
-
- curFile = Vector_Push(&includes);
- curFile->fname = bmake_strdup(name);
- curFile->fromForLoop = fromForLoop;
- curFile->lineno = line;
- curFile->first_lineno = line;
- curFile->nextbuf = nextbuf;
- curFile->nextbuf_arg = arg;
- curFile->lf = NULL;
- curFile->depending = doing_depend; /* restore this on EOF */
-
- assert(nextbuf != NULL);
-
- /* Get first block of input data */
- buf = curFile->nextbuf(curFile->nextbuf_arg, &len);
- if (buf == NULL) {
- /* Was all a waste of time ... */
- if (curFile->fname)
- free(curFile->fname);
- free(curFile);
- return;
- }
- curFile->buf_freeIt = buf;
- curFile->buf_ptr = buf;
- curFile->buf_end = buf + len;
-
- curFile->cond_depth = Cond_save_depth();
- ParseSetParseFile(name);
+Parse_SetInput(const char *name, int lineno, int fd,
+ ReadMoreProc readMore, void *readMoreArg)
+{
+ IFile *curFile;
+ char *buf;
+ size_t len;
+ Boolean fromForLoop = name == NULL;
+
+ if (fromForLoop)
+ name = CurFile()->fname;
+ else
+ ParseTrackInput(name);
+
+ DEBUG3(PARSE, "Parse_SetInput: %s %s, line %d\n",
+ readMore == loadedfile_readMore ? "file" : ".for loop in",
+ name, lineno);
+
+ if (fd == -1 && readMore == NULL)
+ /* sanity */
+ return;
+
+ curFile = Vector_Push(&includes);
+ curFile->fname = bmake_strdup(name);
+ curFile->fromForLoop = fromForLoop;
+ curFile->lineno = lineno;
+ curFile->first_lineno = lineno;
+ curFile->readMore = readMore;
+ curFile->readMoreArg = readMoreArg;
+ curFile->lf = NULL;
+ curFile->depending = doing_depend; /* restore this on EOF */
+
+ assert(readMore != NULL);
+
+ /* Get first block of input data */
+ buf = curFile->readMore(curFile->readMoreArg, &len);
+ if (buf == NULL) {
+ /* Was all a waste of time ... */
+ if (curFile->fname != NULL)
+ free(curFile->fname);
+ free(curFile);
+ return;
+ }
+ curFile->buf_freeIt = buf;
+ curFile->buf_ptr = buf;
+ curFile->buf_end = buf + len;
+
+ curFile->cond_depth = Cond_save_depth();
+ ParseSetParseFile(name);
}
/* Check if the directive is an include directive. */
@@ -2468,42 +2472,42 @@ IsSysVInclude(const char *line)
static void
ParseTraditionalInclude(char *line)
{
- char *cp; /* current position in file spec */
- Boolean done = FALSE;
- Boolean silent = line[0] != 'i';
- char *file = line + (silent ? 8 : 7);
- char *all_files;
+ char *cp; /* current position in file spec */
+ Boolean done = FALSE;
+ Boolean silent = line[0] != 'i';
+ char *file = line + (silent ? 8 : 7);
+ char *all_files;
- DEBUG2(PARSE, "%s: %s\n", __func__, file);
+ DEBUG2(PARSE, "%s: %s\n", __func__, file);
- pp_skip_whitespace(&file);
+ pp_skip_whitespace(&file);
- /*
- * Substitute for any variables in the file name before trying to
- * find the thing.
- */
- (void)Var_Subst(file, VAR_CMDLINE, VARE_WANTRES, &all_files);
- /* TODO: handle errors */
+ /*
+ * Substitute for any variables in the file name before trying to
+ * find the thing.
+ */
+ (void)Var_Subst(file, VAR_CMDLINE, VARE_WANTRES, &all_files);
+ /* TODO: handle errors */
- if (*file == '\0') {
- Parse_Error(PARSE_FATAL, "Filename missing from \"include\"");
- goto out;
- }
+ if (*file == '\0') {
+ 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 && !ch_isspace(*cp); cp++)
- continue;
+ for (file = all_files; !done; file = cp + 1) {
+ /* Skip to end of line or next whitespace */
+ for (cp = file; *cp != '\0' && !ch_isspace(*cp); cp++)
+ continue;
- if (*cp != '\0')
- *cp = '\0';
- else
- done = TRUE;
+ if (*cp != '\0')
+ *cp = '\0';
+ else
+ done = TRUE;
- Parse_include_file(file, FALSE, FALSE, silent);
- }
+ Parse_include_file(file, FALSE, FALSE, silent);
+ }
out:
- free(all_files);
+ free(all_files);
}
#endif
@@ -2512,35 +2516,36 @@ out:
static void
ParseGmakeExport(char *line)
{
- char *variable = line + 6;
- char *value;
+ char *variable = line + 6;
+ char *value;
- DEBUG2(PARSE, "%s: %s\n", __func__, variable);
+ DEBUG2(PARSE, "%s: %s\n", __func__, variable);
- pp_skip_whitespace(&variable);
+ pp_skip_whitespace(&variable);
- for (value = variable; *value && *value != '='; value++)
- continue;
+ for (value = variable; *value != '\0' && *value != '='; value++)
+ continue;
- if (*value != '=') {
- Parse_Error(PARSE_FATAL,
+ if (*value != '=') {
+ Parse_Error(PARSE_FATAL,
"Variable/Value missing from \"export\"");
- return;
- }
- *value++ = '\0'; /* terminate variable */
+ return;
+ }
+ *value++ = '\0'; /* terminate variable */
- /*
- * Expand the value before putting it in the environment.
- */
- (void)Var_Subst(value, VAR_CMDLINE, VARE_WANTRES, &value);
- /* TODO: handle errors */
+ /*
+ * Expand the value before putting it in the environment.
+ */
+ (void)Var_Subst(value, VAR_CMDLINE, VARE_WANTRES, &value);
+ /* TODO: handle errors */
- setenv(variable, value, 1);
- free(value);
+ setenv(variable, value, 1);
+ free(value);
}
#endif
-/* Called when EOF is reached in the current file. If we were reading an
+/*
+ * Called when EOF is reached in the current file. If we were reading an
* include file or a .for loop, the includes stack is popped and things set
* up to go back to reading the previous file at the previous location.
*
@@ -2551,528 +2556,644 @@ ParseGmakeExport(char *line)
static Boolean
ParseEOF(void)
{
- char *ptr;
- size_t len;
- IFile *curFile = CurFile();
+ char *ptr;
+ size_t len;
+ IFile *curFile = CurFile();
- assert(curFile->nextbuf != NULL);
+ assert(curFile->readMore != NULL);
- doing_depend = curFile->depending; /* restore this */
- /* get next input buffer, if any */
- ptr = curFile->nextbuf(curFile->nextbuf_arg, &len);
- curFile->buf_ptr = ptr;
- curFile->buf_freeIt = ptr;
- curFile->buf_end = ptr + len; /* XXX: undefined behavior if ptr == NULL */
- curFile->lineno = curFile->first_lineno;
- if (ptr != NULL)
- return TRUE; /* Iterate again */
+ doing_depend = curFile->depending; /* restore this */
+ /* get next input buffer, if any */
+ ptr = curFile->readMore(curFile->readMoreArg, &len);
+ curFile->buf_ptr = ptr;
+ curFile->buf_freeIt = ptr;
+ curFile->buf_end = ptr == NULL ? NULL : ptr + len;
+ curFile->lineno = curFile->first_lineno;
+ if (ptr != NULL)
+ return TRUE; /* Iterate again */
- /* Ensure the makefile (or loop) didn't have mismatched conditionals */
- Cond_restore_depth(curFile->cond_depth);
+ /* 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;
- }
-
- /* Dispose of curFile info */
- /* Leak curFile->fname because all the gnodes have pointers to it */
- free(curFile->buf_freeIt);
- Vector_Pop(&includes);
-
- 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 FALSE;
- }
+ if (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->buf_freeIt);
+ Vector_Pop(&includes);
+
+ 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 FALSE;
+ }
- curFile = CurFile();
- DEBUG2(PARSE, "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);
- ParseSetParseFile(curFile->fname);
- return TRUE;
+ ParseSetParseFile(curFile->fname);
+ return TRUE;
}
-typedef enum GetLineMode {
- PARSE_NORMAL,
- PARSE_RAW,
- PARSE_SKIP
-} GetLineMode;
+typedef enum ParseRawLineResult {
+ PRLR_LINE,
+ PRLR_EOF,
+ PRLR_ERROR
+} ParseRawLineResult;
-static char *
-ParseGetLine(GetLineMode mode)
+/*
+ * Parse until the end of a line, taking into account lines that end with
+ * backslash-newline.
+ */
+static ParseRawLineResult
+ParseRawLine(IFile *curFile, char **out_line, char **out_line_end,
+ char **out_firstBackslash, char **out_firstComment)
{
- IFile *cf = CurFile();
- char *ptr;
- char ch;
- char *line;
- char *line_end;
- char *escaped;
- char *comment;
- char *tp;
-
- /* Loop through blank lines and comment lines */
- for (;;) {
- cf->lineno++;
- line = cf->buf_ptr;
- ptr = line;
- line_end = line;
- escaped = NULL;
- comment = NULL;
+ char *line = curFile->buf_ptr;
+ char *p = line;
+ char *line_end = line;
+ char *firstBackslash = NULL;
+ char *firstComment = NULL;
+ ParseRawLineResult res = PRLR_LINE;
+
+ curFile->lineno++;
+
for (;;) {
- /* 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')) {
- /* 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->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.");
+ char ch;
+
+ if (p == curFile->buf_end) {
+ res = PRLR_EOF;
break;
- }
}
- /* XXX: Can cf->nextbuf ever be NULL? */
- if (cf->nextbuf != NULL) {
- /*
- * End of this buffer; return EOF and outer logic
- * will get the next one. (eww)
- */
- break;
+
+ ch = *p;
+ if (ch == '\0' ||
+ (ch == '\\' && p + 1 < curFile->buf_end && p[1] == '\0')) {
+ Parse_Error(PARSE_FATAL, "Zero byte read from file");
+ return PRLR_ERROR;
}
- Parse_Error(PARSE_FATAL, "Zero byte read from file");
- return NULL;
- }
-
- if (ch == '\\') {
- /* Don't treat next character as special, remember first one */
- if (escaped == NULL)
- escaped = ptr;
- if (ptr[1] == '\n')
- cf->lineno++;
- ptr += 2;
- line_end = ptr;
- continue;
- }
- if (ch == '#' && comment == NULL) {
- /* Remember first '#' for comment stripping */
- /* Unless previous char was '[', as in modifier :[#] */
- if (!(ptr > line && ptr[-1] == '['))
- comment = line_end;
- }
- ptr++;
- if (ch == '\n')
- break;
- if (!ch_isspace(ch))
- /* We are not interested in trailing whitespace */
- line_end = ptr;
- }
- /* Save next 'to be processed' location */
- cf->buf_ptr = ptr;
+ /* Treat next character after '\' as literal. */
+ if (ch == '\\') {
+ if (firstBackslash == NULL)
+ firstBackslash = p;
+ if (p[1] == '\n') {
+ curFile->lineno++;
+ if (p + 2 == curFile->buf_end) {
+ line_end = p;
+ *line_end = '\n';
+ p += 2;
+ continue;
+ }
+ }
+ p += 2;
+ line_end = p;
+ assert(p <= curFile->buf_end);
+ continue;
+ }
- /* Check we have a non-comment, non-blank line */
- if (line_end == line || comment == line) {
- if (ch == '\0')
- /* At end of file */
- return NULL;
- /* Parse another line */
- continue;
- }
+ /*
+ * Remember the first '#' for comment stripping, unless
+ * the previous char was '[', as in the modifier ':[#]'.
+ */
+ if (ch == '#' && firstComment == NULL &&
+ !(p > line && p[-1] == '['))
+ firstComment = line_end;
- /* We now have a line of data */
- *line_end = '\0';
+ p++;
+ if (ch == '\n')
+ break;
- if (mode == PARSE_RAW) {
- /* Leave '\' (etc) in line buffer (eg 'for' lines) */
- return line;
+ /* We are not interested in trailing whitespace. */
+ if (!ch_isspace(ch))
+ line_end = p;
}
- if (mode == PARSE_SKIP) {
- /* Completely ignore non-directives */
- if (line[0] != '.')
- continue;
- /* We could do more of the .else/.elif/.endif checks here */
+ *out_line = line;
+ curFile->buf_ptr = p;
+ *out_line_end = line_end;
+ *out_firstBackslash = firstBackslash;
+ *out_firstComment = firstComment;
+ return res;
+}
+
+/*
+ * Beginning at start, unescape '\#' to '#' and replace backslash-newline
+ * with a single space.
+ */
+static void
+UnescapeBackslash(char *line, char *start)
+{
+ char *src = start;
+ char *dst = start;
+ char *spaceStart = line;
+
+ for (;;) {
+ char ch = *src++;
+ if (ch != '\\') {
+ if (ch == '\0')
+ break;
+ *dst++ = ch;
+ continue;
+ }
+
+ ch = *src++;
+ if (ch == '\0') {
+ /* Delete '\\' at end of buffer */
+ dst--;
+ break;
+ }
+
+ /* Delete '\\' from before '#' on non-command lines */
+ if (ch == '#' && line[0] != '\t') {
+ *dst++ = ch;
+ continue;
+ }
+
+ if (ch != '\n') {
+ /* Leave '\\' in buffer for later */
+ *dst++ = '\\';
+ /*
+ * Make sure we don't delete an escaped ' ' from the
+ * line end.
+ */
+ spaceStart = dst + 1;
+ *dst++ = ch;
+ continue;
+ }
+
+ /*
+ * Escaped '\n' -- replace following whitespace with a single
+ * ' '.
+ */
+ pp_skip_hspace(&src);
+ *dst++ = ' ';
}
- break;
- }
- /* Brutally ignore anything after a non-escaped '#' in non-commands */
- if (comment != NULL && line[0] != '\t') {
- line_end = comment;
- *line_end = '\0';
- }
+ /* Delete any trailing spaces - eg from empty continuations */
+ while (dst > spaceStart && ch_isspace(dst[-1]))
+ dst--;
+ *dst = '\0';
+}
- /* If we didn't see a '\\' then the in-situ data is fine */
- if (escaped == NULL)
- return line;
+typedef enum GetLineMode {
+ /*
+ * Return the next line that is neither empty nor a comment.
+ * Backslash line continuations are folded into a single space.
+ * A trailing comment, if any, is discarded.
+ */
+ GLM_NONEMPTY,
- /* Remove escapes from '\n' and '#' */
- tp = ptr = escaped;
- escaped = line;
- for (; ; *tp++ = ch) {
- ch = *ptr++;
- if (ch != '\\') {
- if (ch == '\0')
+ /*
+ * Return the next line, even if it is empty or a comment.
+ * Preserve backslash-newline to keep the line numbers correct.
+ *
+ * Used in .for loops to collect the body of the loop while waiting
+ * for the corresponding .endfor.
+ */
+ GLM_FOR_BODY,
+
+ /*
+ * Return the next line that starts with a dot.
+ * Backslash line continuations are folded into a single space.
+ * A trailing comment, if any, is discarded.
+ *
+ * Used in .if directives to skip over irrelevant branches while
+ * waiting for the corresponding .endif.
+ */
+ GLM_DOT
+} GetLineMode;
+
+/* Return the next "interesting" logical line from the current file. */
+static char *
+ParseGetLine(GetLineMode mode)
+{
+ IFile *curFile = CurFile();
+ char *line;
+ char *line_end;
+ char *firstBackslash;
+ char *firstComment;
+
+ /* Loop through blank lines and comment lines */
+ for (;;) {
+ ParseRawLineResult res = ParseRawLine(curFile,
+ &line, &line_end, &firstBackslash, &firstComment);
+ if (res == PRLR_ERROR)
+ return NULL;
+
+ if (line_end == line || firstComment == line) {
+ if (res == PRLR_EOF)
+ return NULL;
+ if (mode != GLM_FOR_BODY)
+ continue;
+ }
+
+ /* We now have a line of data */
+ assert(ch_isspace(*line_end));
+ *line_end = '\0';
+
+ if (mode == GLM_FOR_BODY)
+ return line; /* Don't join the physical lines. */
+
+ if (mode == GLM_DOT && line[0] != '.')
+ continue;
break;
- continue;
}
- ch = *ptr++;
- if (ch == '\0') {
- /* Delete '\\' at end of buffer */
- tp--;
- break;
- }
+ /* Brutally ignore anything after a non-escaped '#' in non-commands. */
+ if (firstComment != NULL && line[0] != '\t')
+ *firstComment = '\0';
- if (ch == '#' && line[0] != '\t')
- /* Delete '\\' from before '#' on non-command lines */
- continue;
+ /* If we didn't see a '\\' then the in-situ data is fine. */
+ if (firstBackslash == NULL)
+ return line;
- if (ch != '\n') {
- /* Leave '\\' in buffer for later */
- *tp++ = '\\';
- /* Make sure we don't delete an escaped ' ' from the line end */
- escaped = tp + 1;
- continue;
+ /* Remove escapes from '\n' and '#' */
+ UnescapeBackslash(line, firstBackslash);
+
+ return line;
+}
+
+static Boolean
+ParseSkippedBranches(void)
+{
+ char *line;
+
+ while ((line = ParseGetLine(GLM_DOT)) != NULL) {
+ if (Cond_EvalLine(line) == COND_PARSE)
+ break;
+ /*
+ * TODO: Check for typos in .elif directives
+ * such as .elsif or .elseif.
+ *
+ * This check will probably duplicate some of
+ * the code in ParseLine. Most of the code
+ * there cannot apply, only ParseVarassign and
+ * ParseDependency can, and to prevent code
+ * duplication, these would need to be called
+ * with a flag called onlyCheckSyntax.
+ *
+ * See directive-elif.mk for details.
+ */
}
- /* Escaped '\n' -- replace following whitespace with a single ' '. */
- pp_skip_hspace(&ptr);
- ch = ' ';
- }
+ return line != NULL;
+}
+
+static Boolean
+ParseForLoop(const char *line)
+{
+ int rval;
+ int firstLineno;
+
+ rval = For_Eval(line);
+ if (rval == 0)
+ return FALSE; /* Not a .for line */
+ if (rval < 0)
+ return TRUE; /* Syntax error - error printed, ignore line */
+
+ firstLineno = CurFile()->lineno;
+
+ /* Accumulate loop lines until matching .endfor */
+ do {
+ line = ParseGetLine(GLM_FOR_BODY);
+ if (line == NULL) {
+ Parse_Error(PARSE_FATAL,
+ "Unexpected end of file in for loop.");
+ break;
+ }
+ } while (For_Accum(line));
- /* Delete any trailing spaces - eg from empty continuations */
- while (tp > escaped && ch_isspace(tp[-1]))
- tp--;
+ For_Run(firstLineno); /* Stash each iteration as a new 'input file' */
- *tp = '\0';
- return line;
+ return TRUE; /* Read next line from for-loop buffer */
}
-/* Read an entire line from the input file. Called only by Parse_File.
+/*
+ * Read an entire line from the input file.
+ *
+ * Empty lines, .if and .for are completely handled by this function,
+ * leaving only variable assignments, other directives, dependency lines
+ * and shell commands to the caller.
*
* Results:
- * A line without its newline and without any trailing whitespace.
+ * A line without its newline and without any trailing whitespace,
+ * or NULL.
*/
static char *
ParseReadLine(void)
{
- char *line; /* Result */
- int lineno; /* Saved line # */
- int rval;
+ char *line;
- for (;;) {
- line = ParseGetLine(PARSE_NORMAL);
- if (line == NULL)
- return NULL;
+ for (;;) {
+ line = ParseGetLine(GLM_NONEMPTY);
+ if (line == NULL)
+ return NULL;
- if (line[0] != '.')
- return line;
+ if (line[0] != '.')
+ return line;
- /*
- * The line might be a conditional. Ask the conditional module
- * about it and act accordingly
- */
- switch (Cond_EvalLine(line)) {
- case COND_SKIP:
- /* Skip to next conditional that evaluates to COND_PARSE. */
- do {
- line = ParseGetLine(PARSE_SKIP);
- } while (line && Cond_EvalLine(line) != COND_PARSE);
- if (line == NULL)
- break;
- continue;
- case COND_PARSE:
- continue;
- case COND_INVALID: /* Not a conditional line */
- /* Check for .for loops */
- rval = For_Eval(line);
- if (rval == 0)
- /* Not a .for line */
- break;
- if (rval < 0)
- /* Syntax error - error printed, ignore line */
- continue;
- /* Start of a .for loop */
- lineno = CurFile()->lineno;
- /* Accumulate loop lines until matching .endfor */
- do {
- line = ParseGetLine(PARSE_RAW);
- if (line == NULL) {
- Parse_Error(PARSE_FATAL,
- "Unexpected end of file in for loop.");
- break;
+ /*
+ * The line might be a conditional. Ask the conditional module
+ * about it and act accordingly
+ */
+ switch (Cond_EvalLine(line)) {
+ case COND_SKIP:
+ if (!ParseSkippedBranches())
+ return NULL;
+ continue;
+ case COND_PARSE:
+ continue;
+ case COND_INVALID: /* Not a conditional line */
+ if (ParseForLoop(line))
+ continue;
+ break;
}
- } while (For_Accum(line));
- /* Stash each iteration as a new 'input file' */
- For_Run(lineno);
- /* Read next line from for-loop buffer */
- continue;
+ return line;
}
- return line;
- }
}
static void
FinishDependencyGroup(void)
{
- GNodeListNode *ln;
+ GNodeListNode *ln;
- if (targets == NULL)
- return;
+ if (targets == NULL)
+ return;
- for (ln = targets->first; ln != NULL; ln = ln->next) {
- GNode *gn = ln->datum;
+ for (ln = targets->first; ln != NULL; ln = ln->next) {
+ GNode *gn = ln->datum;
- Suff_EndTransform(gn);
+ 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;
- }
+ /*
+ * 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;
+ Lst_Free(targets);
+ targets = NULL;
}
/* 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 */
+ cpp_skip_whitespace(&p);
+ if (*p == '\0')
+ return; /* skip empty commands */
- if (targets == NULL) {
- Parse_Error(PARSE_FATAL, "Unassociated shell command \"%s\"", p);
- return;
- }
+ if (targets == NULL) {
+ Parse_Error(PARSE_FATAL,
+ "Unassociated shell command \"%s\"", p);
+ return;
+ }
- {
- char *cmd = bmake_strdup(p);
- GNodeListNode *ln;
+ {
+ char *cmd = bmake_strdup(p);
+ GNodeListNode *ln;
- for (ln = targets->first; ln != NULL; ln = ln->next) {
- GNode *gn = ln->datum;
- ParseAddCmd(gn, cmd);
- }
+ for (ln = targets->first; ln != NULL; ln = ln->next) {
+ GNode *gn = ln->datum;
+ ParseAddCmd(gn, cmd);
+ }
#ifdef CLEANUP
- Lst_Append(targCmds, cmd);
+ Lst_Append(&targCmds, cmd);
#endif
- }
+ }
}
+MAKE_INLINE Boolean
+IsDirective(const char *dir, size_t dirlen, const char *name)
+{
+ return dirlen == strlen(name) && memcmp(dir, name, dirlen) == 0;
+}
+
+/*
+ * See if the line starts with one of the known directives, and if so, handle
+ * the directive.
+ */
static Boolean
ParseDirective(char *line)
{
- char *cp;
+ char *cp = line + 1;
+ const char *dir, *arg;
+ size_t dirlen;
- if (*line == '.') {
- /*
- * Lines that begin with '.' can be pretty much anything:
- * - directives like '.include' or '.if',
- * - suffix rules like '.c.o:',
- * - dependencies for filenames that start with '.',
- * - variable assignments like '.tmp=value'.
- */
- 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))
+ ParseDoInclude(cp);
return TRUE;
}
- }
- return FALSE;
+
+ dir = cp;
+ while (ch_isalpha(*cp) || *cp == '-')
+ cp++;
+ dirlen = (size_t)(cp - dir);
+
+ if (*cp != '\0' && !ch_isspace(*cp))
+ return FALSE;
+
+ pp_skip_whitespace(&cp);
+ arg = cp;
+
+ if (IsDirective(dir, dirlen, "undef")) {
+ Var_Undef(cp);
+ return TRUE;
+ } else if (IsDirective(dir, dirlen, "export")) {
+ Var_Export(VEM_PLAIN, arg);
+ return TRUE;
+ } else if (IsDirective(dir, dirlen, "export-env")) {
+ Var_Export(VEM_ENV, arg);
+ return TRUE;
+ } else if (IsDirective(dir, dirlen, "export-literal")) {
+ Var_Export(VEM_LITERAL, arg);
+ return TRUE;
+ } else if (IsDirective(dir, dirlen, "unexport")) {
+ Var_UnExport(FALSE, arg);
+ return TRUE;
+ } else if (IsDirective(dir, dirlen, "unexport-env")) {
+ Var_UnExport(TRUE, arg);
+ return TRUE;
+ } else if (IsDirective(dir, dirlen, "info")) {
+ ParseMessage(PARSE_INFO, "info", arg);
+ return TRUE;
+ } else if (IsDirective(dir, dirlen, "warning")) {
+ ParseMessage(PARSE_WARNING, "warning", arg);
+ return TRUE;
+ } else if (IsDirective(dir, dirlen, "error")) {
+ ParseMessage(PARSE_FATAL, "error", arg);
+ return TRUE;
+ }
+ return FALSE;
}
static Boolean
ParseVarassign(const char *line)
{
- VarAssign var;
+ VarAssign var;
- if (!Parse_IsVar(line, &var))
- return FALSE;
+ if (!Parse_IsVar(line, &var))
+ return FALSE;
- FinishDependencyGroup();
- Parse_DoVar(&var, VAR_GLOBAL);
- return TRUE;
+ FinishDependencyGroup();
+ Parse_DoVar(&var, VAR_GLOBAL);
+ return TRUE;
}
static char *
FindSemicolon(char *p)
{
- int level = 0;
+ int level = 0;
- for (; *p != '\0'; p++) {
- if (*p == '\\' && p[1] != '\0') {
- p++;
- continue;
- }
+ for (; *p != '\0'; p++) {
+ if (*p == '\\' && p[1] != '\0') {
+ p++;
+ continue;
+ }
- if (*p == '$' && (p[1] == '(' || p[1] == '{'))
- level++;
- else if (level > 0 && (*p == ')' || *p == '}'))
- level--;
- else if (level == 0 && *p == ';')
- break;
- }
- return p;
+ if (*p == '$' && (p[1] == '(' || p[1] == '{'))
+ level++;
+ else if (level > 0 && (*p == ')' || *p == '}'))
+ level--;
+ else if (level == 0 && *p == ';')
+ break;
+ }
+ return p;
}
-/* dependency -> target... op [source...]
- * op -> ':' | '::' | '!' */
+/*
+ * 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 optional 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 = opts.lint ? VARE_WANTRES : VARE_WANTRES | VARE_UNDEFERR;
- (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();
+ VarEvalFlags eflags;
+ char *expanded_line;
+ const char *shellcmd = NULL;
- ParseDoDependency(expanded_line);
- free(expanded_line);
+ /*
+ * 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.
+ */
- if (shellcmd != NULL)
- ParseLine_ShellCommand(shellcmd);
+ /* 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 optional 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 = opts.strict ? VARE_WANTRES : VARE_WANTRES | VARE_UNDEFERR;
+ (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;
+ /*
+ * Lines that begin with '.' can be pretty much anything:
+ * - directives like '.include' or '.if',
+ * - suffix rules like '.c.o:',
+ * - dependencies for filenames that start with '.',
+ * - variable assignments like '.tmp=value'.
+ */
+ if (line[0] == '.' && ParseDirective(line))
+ return;
- if (*line == '\t') {
- ParseLine_ShellCommand(line + 1);
- return;
- }
+ if (line[0] == '\t') {
+ ParseLine_ShellCommand(line + 1);
+ return;
+ }
#ifdef SYSVINCLUDE
- if (IsSysVInclude(line)) {
- /*
- * It's an S3/S5-style "include".
- */
- ParseTraditionalInclude(line);
- return;
- }
+ if (IsSysVInclude(line)) {
+ /*
+ * It's an S3/S5-style "include".
+ */
+ ParseTraditionalInclude(line);
+ return;
+ }
#endif
#ifdef GMAKEEXPORT
- if (strncmp(line, "export", 6) == 0 && ch_isspace(line[6]) &&
- strchr(line, ':') == NULL) {
- /*
- * It's a Gmake "export".
- */
- ParseGmakeExport(line);
- return;
- }
+ if (strncmp(line, "export", 6) == 0 && ch_isspace(line[6]) &&
+ strchr(line, ':') == NULL) {
+ /*
+ * It's a Gmake "export".
+ */
+ ParseGmakeExport(line);
+ return;
+ }
#endif
- if (ParseVarassign(line))
- return;
+ if (ParseVarassign(line))
+ return;
- FinishDependencyGroup();
+ FinishDependencyGroup();
- ParseDependency(line);
+ ParseDependency(line);
}
-/* Parse a top-level makefile, incorporating its content into the global
+/*
+ * Parse a top-level makefile, incorporating its content into the global
* dependency graph.
*
* Input:
@@ -3082,54 +3203,49 @@ ParseLine(char *line)
void
Parse_File(const char *name, int fd)
{
- char *line; /* the line we're working on */
- struct loadedfile *lf;
+ char *line; /* the line we're working on */
+ struct loadedfile *lf;
- lf = loadfile(name, fd);
+ lf = loadfile(name, fd);
- assert(targets == NULL);
+ assert(targets == NULL);
- if (name == NULL)
- name = "(stdin)";
+ if (name == NULL)
+ name = "(stdin)";
- Parse_SetInput(name, 0, -1, loadedfile_nextbuf, lf);
- CurFile()->lf = lf;
+ Parse_SetInput(name, 0, -1, loadedfile_readMore, lf);
+ CurFile()->lf = lf;
- do {
- while ((line = ParseReadLine()) != NULL) {
- DEBUG2(PARSE, "ParseReadLine (%d): '%s'\n",
- CurFile()->lineno, line);
- ParseLine(line);
+ 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());
+
+ FinishDependencyGroup();
+
+ if (fatals != 0) {
+ (void)fflush(stdout);
+ (void)fprintf(stderr,
+ "%s: Fatal errors encountered -- cannot continue",
+ progname);
+ PrintOnError(NULL, NULL);
+ exit(1);
}
- /*
- * Reached EOF, but it may be just EOF of an include file...
- */
- } while (ParseEOF());
-
- FinishDependencyGroup();
-
- if (fatals != 0) {
- (void)fflush(stdout);
- (void)fprintf(stderr,
- "%s: Fatal errors encountered -- cannot continue",
- progname);
- PrintOnError(NULL, NULL);
- exit(1);
- }
}
/* Initialize the parsing module. */
void
Parse_Init(void)
{
- mainNode = NULL;
- parseIncPath = Lst_New();
- sysIncPath = Lst_New();
- defSysIncPath = Lst_New();
- Vector_Init(&includes, sizeof(IFile));
-#ifdef CLEANUP
- targCmds = Lst_New();
-#endif
+ mainNode = NULL;
+ parseIncPath = SearchPath_New();
+ sysIncPath = SearchPath_New();
+ defSysIncPath = SearchPath_New();
+ Vector_Init(&includes, sizeof(IFile));
}
/* Clean up the parsing module. */
@@ -3137,13 +3253,13 @@ void
Parse_End(void)
{
#ifdef CLEANUP
- Lst_Destroy(targCmds, free);
- assert(targets == NULL);
- Lst_Destroy(defSysIncPath, Dir_Destroy);
- Lst_Destroy(sysIncPath, Dir_Destroy);
- Lst_Destroy(parseIncPath, Dir_Destroy);
- assert(includes.len == 0);
- Vector_Done(&includes);
+ Lst_DoneCall(&targCmds, free);
+ assert(targets == NULL);
+ SearchPath_Free(defSysIncPath);
+ SearchPath_Free(sysIncPath);
+ SearchPath_Free(parseIncPath);
+ assert(includes.len == 0);
+ Vector_Done(&includes);
#endif
}
@@ -3152,29 +3268,23 @@ Parse_End(void)
* Return a list containing the single main target to create.
* If no such target exists, we Punt with an obnoxious error message.
*/
-GNodeList *
-Parse_MainName(void)
+void
+Parse_MainName(GNodeList *mainList)
{
- GNodeList *mainList;
-
- mainList = Lst_New();
-
- if (mainNode == NULL)
- Punt("no target to make.");
+ if (mainNode == NULL)
+ Punt("no target to make.");
- if (mainNode->type & OP_DOUBLEDEP) {
- Lst_Append(mainList, mainNode);
- Lst_AppendAll(mainList, mainNode->cohorts);
- } else
- Lst_Append(mainList, mainNode);
-
- Var_Append(".TARGETS", mainNode->name, VAR_GLOBAL);
+ if (mainNode->type & OP_DOUBLEDEP) {
+ Lst_Append(mainList, mainNode);
+ Lst_AppendAll(mainList, &mainNode->cohorts);
+ } else
+ Lst_Append(mainList, mainNode);
- return mainList;
+ Var_Append(".TARGETS", mainNode->name, VAR_GLOBAL);
}
int
Parse_GetFatals(void)
{
- return fatals;
+ return fatals;
}