aboutsummaryrefslogtreecommitdiff
path: root/parse.c
diff options
context:
space:
mode:
Diffstat (limited to 'parse.c')
-rw-r--r--parse.c1608
1 files changed, 624 insertions, 984 deletions
diff --git a/parse.c b/parse.c
index 6a310c74ba5c..3faeafe1c739 100644
--- a/parse.c
+++ b/parse.c
@@ -1,4 +1,4 @@
-/* $NetBSD: parse.c,v 1.574 2021/12/12 15:44:41 rillig Exp $ */
+/* $NetBSD: parse.c,v 1.662 2022/02/05 00:37:19 sjg Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
@@ -74,10 +74,6 @@
* Parse_File is the main entry point and controls most of the other
* functions in this module.
*
- * The directories for the .include "..." directive are kept in
- * 'parseIncPath', while those for .include <...> are kept in 'sysIncPath'.
- * The targets currently being defined are kept in 'targets'.
- *
* Interface:
* Parse_Init Initialize the module
*
@@ -86,15 +82,16 @@
* Parse_File Parse a top-level makefile. Included files are
* handled by IncludeFile instead.
*
- * Parse_IsVar Return true if the given line is a variable
- * assignment. Used by MainParseArgs to determine if
- * an argument is a target or a variable assignment.
- * Used internally for pretty much the same thing.
+ * Parse_VarAssign
+ * Try to parse the given line as a variable assignment.
+ * Used by MainParseArgs to determine if an argument is
+ * a target or a variable assignment. Used internally
+ * for pretty much the same thing.
*
* Parse_Error Report a parse error, a warning or an informational
* message.
*
- * Parse_MainName Returns a list of the main target to create.
+ * Parse_MainName Returns a list of the single main target to create.
*/
#include <sys/types.h>
@@ -124,40 +121,32 @@
#include "pathnames.h"
/* "@(#)parse.c 8.3 (Berkeley) 3/19/94" */
-MAKE_RCSID("$NetBSD: parse.c,v 1.574 2021/12/12 15:44:41 rillig Exp $");
-
-/* types and constants */
+MAKE_RCSID("$NetBSD: parse.c,v 1.662 2022/02/05 00:37:19 sjg Exp $");
/*
- * Structure for a file being read ("included file")
+ * A file being read.
*/
-typedef struct IFile {
- char *fname; /* name of file (relative? absolute?) */
- bool fromForLoop; /* simulated .include by the .for loop */
- int lineno; /* current line number in file */
- int first_lineno; /* line number of start of text */
+typedef struct IncludedFile {
+ FStr name; /* absolute or relative to the cwd */
+ unsigned lineno; /* 1-based */
+ unsigned readLines; /* the number of physical lines that have
+ * been read from the file */
+ unsigned forHeadLineno; /* 1-based */
+ unsigned forBodyReadLines; /* the number of physical lines that have
+ * been read from the file above the body of
+ * the .for loop */
unsigned int cond_depth; /* 'if' nesting when file opened */
- bool depending; /* state of doing_depend on EOF */
+ bool depending; /* state of doing_depend on EOF */
- /*
- * The buffer from which the file's content is read. The buffer
- * always ends with '\n', the buffer is not null-terminated, that is,
- * buf_end[0] is already out of bounds.
- */
- char *buf_freeIt;
+ Buffer buf; /* the file's content or the body of the .for
+ * loop; always ends with '\n' */
char *buf_ptr; /* next char to be read */
char *buf_end; /* buf_end[-1] == '\n' */
- /* Function to read more data, with a single opaque argument. */
- ReadMoreProc readMore;
- void *readMoreArg;
-
- struct loadedfile *lf; /* loadedfile object, if any */
-} IFile;
+ struct ForLoop *forLoop;
+} IncludedFile;
-/*
- * Tokens for target attributes
- */
+/* Special attributes for target nodes. */
typedef enum ParseSpecial {
SP_ATTRIBUTE, /* Generic attribute */
SP_BEGIN, /* .BEGIN */
@@ -169,8 +158,7 @@ typedef enum ParseSpecial {
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_MAIN, /* .MAIN and no user-specified targets to make */
SP_META, /* .META */
SP_MFLAGS, /* .MFLAGS or .MAKEFLAGS */
SP_NOMETA, /* .NOMETA */
@@ -199,15 +187,29 @@ typedef enum ParseSpecial {
typedef List SearchPathList;
typedef ListNode SearchPathListNode;
-/* result data */
+
+typedef enum VarAssignOp {
+ VAR_NORMAL, /* = */
+ VAR_APPEND, /* += */
+ VAR_DEFAULT, /* ?= */
+ VAR_SUBST, /* := */
+ VAR_SHELL /* != or :sh= */
+} VarAssignOp;
+
+typedef struct VarAssign {
+ char *varname; /* unexpanded */
+ VarAssignOp op;
+ const char *value; /* unexpanded */
+} VarAssign;
+
+static bool Parse_IsVar(const char *, VarAssign *);
+static void Parse_Var(VarAssign *, GNode *);
/*
- * The main target to create. This is the first target on the first
- * dependency line in the first makefile.
+ * The target to be made if no targets are specified in the command line.
+ * This is the first target defined in any of the makefiles.
*/
-static GNode *mainNode;
-
-/* eval state */
+GNode *mainNode;
/*
* During parsing, the targets from the left-hand side of the currently
@@ -230,48 +232,25 @@ static StringList targCmds = LST_INIT;
/*
* Predecessor node for handling .ORDER. Initialized to NULL when .ORDER
- * seen, then set to each successive source on the line.
+ * is seen, then set to each successive source on the line.
*/
static GNode *order_pred;
-/* parser state */
-
-/* number of fatal errors */
static int parseErrors = 0;
/*
- * Variables for doing includes
- */
-
-/*
* The include chain of makefiles. At index 0 is the top-level makefile from
* the command line, followed by the included files or .for loops, up to and
* including the current file.
*
* See PrintStackTrace for how to interpret the data.
*/
-static Vector /* of IFile */ includes;
-
-static IFile *
-GetInclude(size_t i)
-{
- return Vector_Get(&includes, i);
-}
-
-/* The file that is currently being read. */
-static IFile *
-CurFile(void)
-{
- return GetInclude(includes.len - 1);
-}
+static Vector /* of IncludedFile */ includes;
-/* include paths */
SearchPath *parseIncPath; /* directories for "..." includes */
SearchPath *sysIncPath; /* directories for <...> includes */
SearchPath *defSysIncPath; /* default for sysIncPath */
-/* parser tables */
-
/*
* The parseKeywords table is searched using binary search when deciding
* if a target or source is special. The 'spec' field is the ParseSpecial
@@ -280,9 +259,9 @@ 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[17]; /* Name of keyword */
- ParseSpecial spec; /* Type when used as a target */
- GNodeType op; /* Operator when used as a source */
+ const char name[17];
+ ParseSpecial special; /* when used as a target */
+ GNodeType targetAttr; /* when used as a source */
} parseKeywords[] = {
{ ".BEGIN", SP_BEGIN, OP_NONE },
{ ".DEFAULT", SP_DEFAULT, OP_NONE },
@@ -330,120 +309,36 @@ static const struct {
{ ".WAIT", SP_WAIT, OP_NONE },
};
-/* file loader */
-
-struct loadedfile {
- char *buf; /* contents buffer */
- size_t len; /* length of contents */
- bool 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(char *buf, size_t buflen)
-{
- struct loadedfile *lf;
-
- lf = bmake_malloc(sizeof *lf);
- lf->buf = buf;
- lf->len = buflen;
- lf->used = false;
- return lf;
-}
-
-static void
-loadedfile_destroy(struct loadedfile *lf)
-{
- free(lf->buf);
- free(lf);
-}
-/*
- * 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_readMore(void *x, size_t *len)
+static IncludedFile *
+GetInclude(size_t i)
{
- struct loadedfile *lf = x;
-
- if (lf->used)
- return NULL;
-
- lf->used = true;
- *len = lf->len;
- return lf->buf;
+ return Vector_Get(&includes, i);
}
-/*
- * Try to get the size of a file.
- */
-static bool
-load_getsize(int fd, size_t *ret)
+/* The file that is currently being read. */
+static IncludedFile *
+CurFile(void)
{
- struct stat st;
-
- if (fstat(fd, &st) < 0)
- return false;
-
- if (!S_ISREG(st.st_mode))
- return false;
-
- /*
- * 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 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 > 0x3fffffff)
- return false;
-
- *ret = (size_t)st.st_size;
- return true;
+ return GetInclude(includes.len - 1);
}
-/*
- * Read in a file.
- *
- * Until the path search logic can be moved under here instead of
- * being in the caller in another source file, we need to have the fd
- * passed in already open. Bleh.
- *
- * If the path is NULL, use stdin.
- */
-static struct loadedfile *
+static Buffer
loadfile(const char *path, int fd)
{
ssize_t n;
Buffer buf;
- size_t filesize;
-
+ size_t bufSize;
+ struct stat st;
- if (path == NULL) {
- assert(fd == -1);
- fd = STDIN_FILENO;
- }
-
- 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);
+ bufSize = fstat(fd, &st) == 0 && S_ISREG(st.st_mode) &&
+ st.st_size > 0 && st.st_size < 1024 * 1024 * 1024
+ ? (size_t)st.st_size : 1024;
+ Buf_InitSize(&buf, bufSize);
for (;;) {
- assert(buf.len <= buf.cap);
if (buf.len == buf.cap) {
- if (buf.cap > 0x1fffffff) {
- errno = EFBIG;
+ if (buf.cap >= 512 * 1024 * 1024) {
Error("%s: file too large", path);
exit(2); /* Not 1 so -q can distinguish error */
}
@@ -465,79 +360,57 @@ loadfile(const char *path, int fd)
if (!Buf_EndsWith(&buf, '\n'))
Buf_AddByte(&buf, '\n');
- if (path != NULL)
- close(fd);
-
- {
- struct loadedfile *lf = loadedfile_create(buf.data, buf.len);
- Buf_DoneData(&buf);
- return lf;
- }
+ return buf; /* may not be null-terminated */
}
-static void
-PrintStackTrace(void)
+/*
+ * Print the current chain of .include and .for directives. In Parse_Fatal
+ * or other functions that already print the location, includingInnermost
+ * would be redundant, but in other cases like Error or Fatal it needs to be
+ * included.
+ */
+void
+PrintStackTrace(bool includingInnermost)
{
- const IFile *entries;
+ const IncludedFile *entries;
size_t i, n;
- if (!(DEBUG(PARSE)))
- return;
-
entries = GetInclude(0);
n = includes.len;
if (n == 0)
return;
- n--; /* This entry is already in the diagnostic. */
- /*
- * For the IFiles with fromForLoop, lineno seems to be sorted
- * backwards. This is because lineno is the number of completely
- * parsed lines, which for a .for loop is right after the
- * corresponding .endfor. The intuitive line number comes from
- * first_lineno instead, which points at the start of the .for loop.
- *
- * To make the stack trace intuitive, the entry below each chain of
- * .for loop entries must be ignored completely since neither its
- * lineno nor its first_lineno is useful. Instead, the topmost of
- * each chain of .for loop entries needs to be printed twice, once
- * with its first_lineno and once with its lineno.
- */
+ if (!includingInnermost && entries[n - 1].forLoop == NULL)
+ n--; /* already in the diagnostic */
for (i = n; i-- > 0;) {
- const IFile *entry = entries + i;
- const char *fname = entry->fname;
- bool printLineno;
+ const IncludedFile *entry = entries + i;
+ const char *fname = entry->name.str;
char dirbuf[MAXPATHLEN + 1];
if (fname[0] != '/' && strcmp(fname, "(stdin)") != 0)
fname = realpath(fname, dirbuf);
- printLineno = !entry->fromForLoop;
- if (i + 1 < n && entries[i + 1].fromForLoop == printLineno)
- printLineno = entry->fromForLoop;
-
- if (printLineno)
- debug_printf("\tin .include from %s:%d\n",
- fname, entry->lineno);
- if (entry->fromForLoop)
- debug_printf("\tin .for loop from %s:%d\n",
- fname, entry->first_lineno);
+ if (entry->forLoop != NULL) {
+ char *details = ForLoop_Details(entry->forLoop);
+ debug_printf("\tin .for loop from %s:%u with %s\n",
+ fname, entry->forHeadLineno, details);
+ free(details);
+ } else if (i + 1 < n && entries[i + 1].forLoop != NULL) {
+ /* entry->lineno is not a useful line number */
+ } else
+ debug_printf("\tin %s:%u\n", fname, entry->lineno);
}
}
/* Check if the current character is escaped on the current line. */
static bool
-ParseIsEscaped(const char *line, const char *c)
+IsEscaped(const char *line, const char *p)
{
- bool active = false;
- for (;;) {
- if (line == c)
- return active;
- if (*--c != '\\')
- return active;
- active = !active;
- }
+ bool escaped = false;
+ while (p > line && *--p == '\\')
+ escaped = !escaped;
+ return escaped;
}
/*
@@ -547,8 +420,8 @@ ParseIsEscaped(const char *line, const char *c)
static void
RememberLocation(GNode *gn)
{
- IFile *curFile = CurFile();
- gn->fname = curFile->fname;
+ IncludedFile *curFile = CurFile();
+ gn->fname = Str_Intern(curFile->name.str);
gn->lineno = curFile->lineno;
}
@@ -557,12 +430,12 @@ RememberLocation(GNode *gn)
* Return the index of the keyword, or -1 if it isn't there.
*/
static int
-ParseFindKeyword(const char *str)
+FindKeyword(const char *str)
{
int start = 0;
int end = sizeof parseKeywords / sizeof parseKeywords[0] - 1;
- do {
+ while (start <= end) {
int curr = start + (end - start) / 2;
int diff = strcmp(str, parseKeywords[curr].name);
@@ -572,25 +445,22 @@ ParseFindKeyword(const char *str)
end = curr - 1;
else
start = curr + 1;
- } while (start <= end);
+ }
return -1;
}
-static void
-PrintLocation(FILE *f, const char *fname, size_t lineno)
+void
+PrintLocation(FILE *f, bool useVars, const char *fname, unsigned lineno)
{
char dirbuf[MAXPATHLEN + 1];
FStr dir, base;
- if (*fname == '/' || strcmp(fname, "(stdin)") == 0) {
- (void)fprintf(f, "\"%s\" line %u: ", fname, (unsigned)lineno);
+ if (!useVars || fname[0] == '/' || strcmp(fname, "(stdin)") == 0) {
+ (void)fprintf(f, "\"%s\" line %u: ", fname, lineno);
return;
}
- /* Find out which makefile is the culprit.
- * We try ${.PARSEDIR} and apply realpath(3) if not absolute. */
-
dir = Var_Value(SCOPE_GLOBAL, ".PARSEDIR");
if (dir.str == NULL)
dir.str = ".";
@@ -601,15 +471,14 @@ PrintLocation(FILE *f, const char *fname, size_t lineno)
if (base.str == NULL)
base.str = str_basename(fname);
- (void)fprintf(f, "\"%s/%s\" line %u: ",
- dir.str, base.str, (unsigned)lineno);
+ (void)fprintf(f, "\"%s/%s\" line %u: ", dir.str, base.str, lineno);
FStr_Done(&base);
FStr_Done(&dir);
}
-static void
-ParseVErrorInternal(FILE *f, const char *fname, size_t lineno,
+static void MAKE_ATTR_PRINTFLIKE(6, 0)
+ParseVErrorInternal(FILE *f, bool useVars, const char *fname, unsigned lineno,
ParseErrorLevel type, const char *fmt, va_list ap)
{
static bool fatal_warning_error_printed = false;
@@ -617,42 +486,42 @@ ParseVErrorInternal(FILE *f, const char *fname, size_t lineno,
(void)fprintf(f, "%s: ", progname);
if (fname != NULL)
- PrintLocation(f, fname, lineno);
+ PrintLocation(f, useVars, fname, lineno);
if (type == PARSE_WARNING)
(void)fprintf(f, "warning: ");
(void)vfprintf(f, fmt, ap);
(void)fprintf(f, "\n");
(void)fflush(f);
- if (type == PARSE_INFO)
- goto print_stack_trace;
- if (type == PARSE_WARNING && !opts.parseWarnFatal)
- goto print_stack_trace;
- parseErrors++;
- if (type == PARSE_WARNING && !fatal_warning_error_printed) {
- Error("parsing warnings being treated as errors");
- fatal_warning_error_printed = true;
+ if (type == PARSE_FATAL)
+ parseErrors++;
+ if (type == PARSE_WARNING && opts.parseWarnFatal) {
+ if (!fatal_warning_error_printed) {
+ Error("parsing warnings being treated as errors");
+ fatal_warning_error_printed = true;
+ }
+ parseErrors++;
}
-print_stack_trace:
- PrintStackTrace();
+ if (DEBUG(PARSE))
+ PrintStackTrace(false);
}
-static void
-ParseErrorInternal(const char *fname, size_t lineno,
+static void MAKE_ATTR_PRINTFLIKE(4, 5)
+ParseErrorInternal(const char *fname, unsigned lineno,
ParseErrorLevel type, const char *fmt, ...)
{
va_list ap;
(void)fflush(stdout);
va_start(ap, fmt);
- ParseVErrorInternal(stderr, fname, lineno, type, fmt, ap);
+ ParseVErrorInternal(stderr, false, fname, lineno, type, fmt, ap);
va_end(ap);
- if (opts.debug_file != stderr && opts.debug_file != stdout) {
+ if (opts.debug_file != stdout && opts.debug_file != stderr) {
va_start(ap, fmt);
- ParseVErrorInternal(opts.debug_file, fname, lineno, type,
- fmt, ap);
+ ParseVErrorInternal(opts.debug_file, false, fname, lineno,
+ type, fmt, ap);
va_end(ap);
}
}
@@ -670,37 +539,37 @@ Parse_Error(ParseErrorLevel type, const char *fmt, ...)
{
va_list ap;
const char *fname;
- size_t lineno;
+ unsigned lineno;
if (includes.len == 0) {
fname = NULL;
lineno = 0;
} else {
- IFile *curFile = CurFile();
- fname = curFile->fname;
- lineno = (size_t)curFile->lineno;
+ IncludedFile *curFile = CurFile();
+ fname = curFile->name.str;
+ lineno = curFile->lineno;
}
- va_start(ap, fmt);
(void)fflush(stdout);
- ParseVErrorInternal(stderr, fname, lineno, type, fmt, ap);
+ va_start(ap, fmt);
+ ParseVErrorInternal(stderr, true, fname, lineno, type, fmt, ap);
va_end(ap);
- if (opts.debug_file != stderr && opts.debug_file != stdout) {
+ if (opts.debug_file != stdout && opts.debug_file != stderr) {
va_start(ap, fmt);
- ParseVErrorInternal(opts.debug_file, fname, lineno, type,
- fmt, ap);
+ ParseVErrorInternal(opts.debug_file, true, fname, lineno,
+ type, fmt, ap);
va_end(ap);
}
}
/*
- * Parse and handle an .info, .warning or .error directive.
- * For an .error directive, immediately exit.
+ * Handle an .info, .warning or .error directive. For an .error directive,
+ * exit immediately.
*/
static void
-ParseMessage(ParseErrorLevel level, const char *levelName, const char *umsg)
+HandleMessage(ParseErrorLevel level, const char *levelName, const char *umsg)
{
char *xmsg;
@@ -717,17 +586,15 @@ ParseMessage(ParseErrorLevel level, const char *levelName, const char *umsg)
free(xmsg);
if (level == PARSE_FATAL) {
- PrintOnError(NULL, NULL);
+ PrintOnError(NULL, "\n");
exit(1);
}
}
/*
- * 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.
+ * Add the child to the parent's children, and for non-special targets, vice
+ * versa. Special targets such as .END do not need to be informed once the
+ * child target has been made.
*/
static void
LinkSource(GNode *pgn, GNode *cgn, bool isSpecial)
@@ -743,8 +610,8 @@ LinkSource(GNode *pgn, GNode *cgn, bool isSpecial)
Lst_Append(&cgn->parents, pgn);
if (DEBUG(PARSE)) {
- debug_printf("# %s: added child %s - %s\n",
- __func__, pgn->name, cgn->name);
+ debug_printf("# LinkSource: added child %s - %s\n",
+ pgn->name, cgn->name);
Targ_PrintNode(pgn, 0);
Targ_PrintNode(cgn, 0);
}
@@ -843,9 +710,9 @@ ApplyDependencyOperator(GNodeType op)
* We give each .WAIT node a unique name (mainly for diagnostics).
*/
static void
-ParseDependencySourceWait(bool isSpecial)
+ApplyDependencySourceWait(bool isSpecial)
{
- static int wait_number = 0;
+ static unsigned wait_number = 0;
char wait_src[16];
GNode *gn;
@@ -859,41 +726,41 @@ ParseDependencySourceWait(bool isSpecial)
}
static bool
-ParseDependencySourceKeyword(const char *src, ParseSpecial specType)
+ApplyDependencySourceKeyword(const char *src, ParseSpecial special)
{
int keywd;
- GNodeType op;
+ GNodeType targetAttr;
if (*src != '.' || !ch_isupper(src[1]))
return false;
- keywd = ParseFindKeyword(src);
+ keywd = FindKeyword(src);
if (keywd == -1)
return false;
- op = parseKeywords[keywd].op;
- if (op != OP_NONE) {
- ApplyDependencyOperator(op);
+ targetAttr = parseKeywords[keywd].targetAttr;
+ if (targetAttr != OP_NONE) {
+ ApplyDependencyOperator(targetAttr);
return true;
}
- if (parseKeywords[keywd].spec == SP_WAIT) {
- ParseDependencySourceWait(specType != SP_NOT);
+ if (parseKeywords[keywd].special == SP_WAIT) {
+ ApplyDependencySourceWait(special != SP_NOT);
return true;
}
return false;
}
+/*
+ * In a line like ".MAIN: source1 source2", add all sources to the list of
+ * things to create, but only if the user didn't specify a target on the
+ * command line and .MAIN occurs for the first time.
+ *
+ * See HandleDependencyTargetSpecial, branch SP_MAIN.
+ * See unit-tests/cond-func-make-main.mk.
+ */
static void
-ParseDependencySourceMain(const char *src)
+ApplyDependencySourceMain(const char *src)
{
- /*
- * In a line like ".MAIN: source1 source2", add all sources to the
- * list of things to create, but only if the user didn't specify a
- * target on the command line and .MAIN occurs for the first time.
- *
- * See ParseDependencyTargetSpecial, 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
@@ -903,7 +770,7 @@ ParseDependencySourceMain(const char *src)
}
static void
-ParseDependencySourceOrder(const char *src)
+ApplyDependencySourceOrder(const char *src)
{
GNode *gn;
/*
@@ -917,8 +784,9 @@ ParseDependencySourceOrder(const char *src)
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);
+ debug_printf(
+ "# .ORDER forces '%s' to be made before '%s'\n",
+ order_pred->name, gn->name);
Targ_PrintNode(order_pred, 0);
Targ_PrintNode(gn, 0);
}
@@ -929,56 +797,42 @@ ParseDependencySourceOrder(const char *src)
order_pred = gn;
}
+/* The source is not an attribute, so find/create a node for it. */
static void
-ParseDependencySourceOther(const char *src, GNodeType tOp,
- ParseSpecial specType)
+ApplyDependencySourceOther(const char *src, GNodeType targetAttr,
+ ParseSpecial special)
{
GNode *gn;
- /*
- * The source is not an attribute, so find/create a node for it.
- * After that, 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)
RememberLocation(gn);
- if (tOp != OP_NONE)
- gn->type |= tOp;
+ if (targetAttr != OP_NONE)
+ gn->type |= targetAttr;
else
- LinkToTargets(gn, specType != SP_NOT);
+ LinkToTargets(gn, special != 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
+ * attribute (such as .SILENT) and if so, apply it to all targets. Otherwise
* decide if there is some attribute which should be applied *to* the source
* because of some special target (such as .PHONY) and apply it if so.
- * Otherwise, make the source a child of the targets in the list 'targets'.
- *
- * Input:
- * tOp operator (if any) from special targets
- * src name of the source to handle
+ * Otherwise, make the source a child of the targets.
*/
static void
-ParseDependencySource(GNodeType tOp, const char *src, ParseSpecial specType)
+ApplyDependencySource(GNodeType targetAttr, const char *src,
+ ParseSpecial special)
{
- if (ParseDependencySourceKeyword(src, specType))
+ if (ApplyDependencySourceKeyword(src, special))
return;
- if (specType == SP_MAIN)
- ParseDependencySourceMain(src);
- else if (specType == SP_ORDER)
- ParseDependencySourceOrder(src);
+ if (special == SP_MAIN)
+ ApplyDependencySourceMain(src);
+ else if (special == SP_ORDER)
+ ApplyDependencySourceOrder(src);
else
- ParseDependencySourceOther(src, tOp, specType);
+ ApplyDependencySourceOther(src, targetAttr, special);
}
/*
@@ -987,7 +841,7 @@ ParseDependencySource(GNodeType tOp, const char *src, ParseSpecial specType)
* actually a real target (i.e. isn't a .USE or .EXEC rule) to be made.
*/
static void
-FindMainTarget(void)
+MaybeUpdateMainTarget(void)
{
GNodeListNode *ln;
@@ -996,32 +850,24 @@ FindMainTarget(void)
for (ln = targets->first; ln != NULL; ln = ln->next) {
GNode *gn = ln->datum;
- if (!(gn->type & OP_NOTARGET)) {
+ if (GNode_IsMainCandidate(gn)) {
DEBUG1(MAKE, "Setting main node to \"%s\"\n", gn->name);
mainNode = gn;
- Targ_SetMain(gn);
return;
}
}
}
-/*
- * We got to the end of the line while we were still looking at targets.
- *
- * Ending a dependency line without an operator is a Bozo no-no. As a
- * heuristic, this is also often triggered by undetected conflicts from
- * cvs/rcs merges.
- */
static void
-ParseErrorNoDependency(const char *lstart)
+InvalidLineType(const char *line)
{
- if ((strncmp(lstart, "<<<<<<", 6) == 0) ||
- (strncmp(lstart, "======", 6) == 0) ||
- (strncmp(lstart, ">>>>>>", 6) == 0))
+ if (strncmp(line, "<<<<<<", 6) == 0 ||
+ strncmp(line, "======", 6) == 0 ||
+ strncmp(line, ">>>>>>", 6) == 0)
Parse_Error(PARSE_FATAL,
"Makefile appears to contain unresolved CVS/RCS/??? merge conflicts");
- else if (lstart[0] == '.') {
- const char *dirstart = lstart + 1;
+ else if (line[0] == '.') {
+ const char *dirstart = line + 1;
const char *dirend;
cpp_skip_whitespace(&dirstart);
dirend = dirstart;
@@ -1034,39 +880,35 @@ ParseErrorNoDependency(const char *lstart)
}
static void
-ParseDependencyTargetWord(const char **pp, const char *lstart)
+ParseDependencyTargetWord(char **pp, const char *lstart)
{
const char *cp = *pp;
while (*cp != '\0') {
if ((ch_isspace(*cp) || *cp == '!' || *cp == ':' ||
*cp == '(') &&
- !ParseIsEscaped(lstart, cp))
+ !IsEscaped(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.
+ * otherwise).
*
* 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;
+ FStr val;
- (void)Var_Parse(&nested_p, SCOPE_CMDLINE,
- VARE_PARSE_ONLY, &nested_val);
- /* TODO: handle errors */
- FStr_Done(&nested_val);
- cp += nested_p - cp;
+ (void)Var_Parse(&cp, SCOPE_CMDLINE,
+ VARE_PARSE_ONLY, &val);
+ FStr_Done(&val);
} else
cp++;
}
- *pp = cp;
+ *pp += cp - *pp;
}
/*
@@ -1075,11 +917,11 @@ ParseDependencyTargetWord(const char **pp, const char *lstart)
* See the tests deptgt-*.mk.
*/
static void
-ParseDependencyTargetSpecial(ParseSpecial *inout_specType,
- const char *targetName,
- SearchPathList **inout_paths)
+HandleDependencyTargetSpecial(const char *targetName,
+ ParseSpecial *inout_special,
+ SearchPathList **inout_paths)
{
- switch (*inout_specType) {
+ switch (*inout_special) {
case SP_PATH:
if (*inout_paths == NULL)
*inout_paths = Lst_New();
@@ -1091,7 +933,7 @@ ParseDependencyTargetSpecial(ParseSpecial *inout_specType,
* .MAIN node.
*/
if (!Lst_IsEmpty(&opts.create))
- *inout_specType = SP_NOT;
+ *inout_special = SP_NOT;
break;
case SP_BEGIN:
case SP_END:
@@ -1136,13 +978,9 @@ ParseDependencyTargetSpecial(ParseSpecial *inout_specType,
}
}
-/*
- * .PATH<suffix> has to be handled specially.
- * Call on the suffix module to give us a path to modify.
- */
static bool
-ParseDependencyTargetPath(const char *suffixName,
- SearchPathList **inout_paths)
+HandleDependencyTargetPath(const char *suffixName,
+ SearchPathList **inout_paths)
{
SearchPath *path;
@@ -1160,13 +998,12 @@ ParseDependencyTargetPath(const char *suffixName,
return true;
}
-/*
- * See if it's a special target and if so set specType to match it.
- */
+/* See if it's a special target and if so set inout_special to match it. */
static bool
-ParseDependencyTarget(const char *targetName,
- ParseSpecial *inout_specType,
- GNodeType *out_tOp, SearchPathList **inout_paths)
+HandleDependencyTarget(const char *targetName,
+ ParseSpecial *inout_special,
+ GNodeType *inout_targetAttr,
+ SearchPathList **inout_paths)
{
int keywd;
@@ -1177,55 +1014,42 @@ ParseDependencyTarget(const char *targetName,
* See if the target is a special target that must have it
* or its sources handled specially.
*/
- keywd = ParseFindKeyword(targetName);
+ keywd = FindKeyword(targetName);
if (keywd != -1) {
- if (*inout_specType == SP_PATH &&
- parseKeywords[keywd].spec != SP_PATH) {
+ if (*inout_special == SP_PATH &&
+ parseKeywords[keywd].special != SP_PATH) {
Parse_Error(PARSE_FATAL, "Mismatched special targets");
return false;
}
- *inout_specType = parseKeywords[keywd].spec;
- *out_tOp = parseKeywords[keywd].op;
+ *inout_special = parseKeywords[keywd].special;
+ *inout_targetAttr = parseKeywords[keywd].targetAttr;
- ParseDependencyTargetSpecial(inout_specType, targetName,
+ HandleDependencyTargetSpecial(targetName, inout_special,
inout_paths);
} else if (strncmp(targetName, ".PATH", 5) == 0) {
- *inout_specType = SP_PATH;
- if (!ParseDependencyTargetPath(targetName + 5, inout_paths))
+ *inout_special = SP_PATH;
+ if (!HandleDependencyTargetPath(targetName + 5, inout_paths))
return false;
}
return true;
}
static void
-ParseDependencyTargetMundane(char *targetName, StringList *curTargs)
+HandleDependencyTargetMundane(char *targetName)
{
+ StringList targetNames = LST_INIT;
+
if (Dir_HasWildcards(targetName)) {
- /*
- * 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();
-
- SearchPath_Expand(emptyPath, targetName, curTargs);
-
+ SearchPath_Expand(emptyPath, targetName, &targetNames);
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, targetName);
- }
-
- /* Apply the targets. */
+ } else
+ Lst_Append(&targetNames, targetName);
- while (!Lst_IsEmpty(curTargs)) {
- char *targName = Lst_Dequeue(curTargs);
+ while (!Lst_IsEmpty(&targetNames)) {
+ char *targName = Lst_Dequeue(&targetNames);
GNode *gn = Suff_IsTransform(targName)
? Suff_AddTransform(targName)
: Targ_GetNode(targName);
@@ -1237,33 +1061,28 @@ ParseDependencyTargetMundane(char *targetName, StringList *curTargs)
}
static void
-ParseDependencyTargetExtraWarn(char **pp, const char *lstart)
+SkipExtraTargets(char **pp, const char *lstart)
{
bool warning = false;
- char *cp = *pp;
+ const char *p = *pp;
- while (*cp != '\0') {
- if (!ParseIsEscaped(lstart, cp) && (*cp == '!' || *cp == ':'))
+ while (*p != '\0') {
+ if (!IsEscaped(lstart, p) && (*p == '!' || *p == ':'))
break;
- if (ParseIsEscaped(lstart, cp) || (*cp != ' ' && *cp != '\t'))
+ if (IsEscaped(lstart, p) || (*p != ' ' && *p != '\t'))
warning = true;
- cp++;
+ p++;
}
if (warning)
Parse_Error(PARSE_WARNING, "Extra target ignored");
- *pp = cp;
+ *pp += p - *pp;
}
static void
-ParseDependencyCheckSpec(ParseSpecial specType)
+CheckSpecialMundaneMixture(ParseSpecial special)
{
- switch (specType) {
- default:
- Parse_Error(PARSE_WARNING,
- "Special and mundane targets don't mix. "
- "Mundane ones ignored");
- break;
+ switch (special) {
case SP_DEFAULT:
case SP_STALE:
case SP_BEGIN:
@@ -1275,7 +1094,14 @@ ParseDependencyCheckSpec(ParseSpecial specType)
* shouldn't be empty.
*/
case SP_NOT:
- /* Nothing special here -- targets can be empty if it wants. */
+ /*
+ * Nothing special here -- targets can be empty if it wants.
+ */
+ break;
+ default:
+ Parse_Error(PARSE_WARNING,
+ "Special and mundane targets don't mix. "
+ "Mundane ones ignored");
break;
}
}
@@ -1284,34 +1110,15 @@ ParseDependencyCheckSpec(ParseSpecial specType)
* In a dependency line like 'targets: sources' or 'targets! sources', parse
* the operator ':', '::' or '!' from between the targets and the sources.
*/
-static bool
-ParseDependencyOp(char **pp, const char *lstart, GNodeType *out_op)
+static GNodeType
+ParseDependencyOp(char **pp)
{
- const char *cp = *pp;
-
- if (*cp == '!') {
- *out_op = OP_FORCE;
- (*pp)++;
- return true;
- }
-
- if (*cp == ':') {
- if (cp[1] == ':') {
- *out_op = OP_DOUBLEDEP;
- (*pp) += 2;
- } else {
- *out_op = OP_DEPENDS;
- (*pp)++;
- }
- return true;
- }
-
- {
- const char *msg = lstart[0] == '.'
- ? "Unknown directive" : "Missing dependency operator";
- Parse_Error(PARSE_FATAL, "%s", msg);
- return false;
- }
+ if (**pp == '!')
+ return (*pp)++, OP_FORCE;
+ if ((*pp)[1] == ':')
+ return *pp += 2, OP_DOUBLEDEP;
+ else
+ return (*pp)++, OP_DEPENDS;
}
static void
@@ -1326,19 +1133,11 @@ ClearPaths(SearchPathList *paths)
Dir_SetPATH();
}
-/*
- * 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).
- */
+/* Handle a "dependency" line like '.SPECIAL:' without any sources. */
static void
-ParseDependencySourcesEmpty(ParseSpecial specType, SearchPathList *paths)
+HandleDependencySourcesEmpty(ParseSpecial special, SearchPathList *paths)
{
- switch (specType) {
+ switch (special) {
case SP_SUFFIXES:
Suff_ClearSuffixes();
break;
@@ -1349,7 +1148,7 @@ ParseDependencySourcesEmpty(ParseSpecial specType, SearchPathList *paths)
opts.ignoreErrors = true;
break;
case SP_SILENT:
- opts.beSilent = true;
+ opts.silent = true;
break;
case SP_PATH:
ClearPaths(paths);
@@ -1375,39 +1174,17 @@ AddToPaths(const char *dir, SearchPathList *paths)
}
/*
- * If the target was one that doesn't take files as its sources
- * but takes something like suffixes, we take each
- * space-separated word on the line as a something and deal
- * with it accordingly.
- *
- * If the target was .SUFFIXES, we take each source as a
- * suffix and add it to the list of suffixes maintained by the
- * Suff module.
- *
- * If the target was a .PATH, we add the source as a directory
- * to search on the search path.
- *
- * If it was .INCLUDES, the source is taken to be the suffix of
- * files which will be #included and whose search path should
- * be present in the .INCLUDES variable.
- *
- * If it was .LIBS, the source is taken to be the suffix of
- * files which are considered libraries and whose search path
- * should be present in the .LIBS variable.
- *
- * If it was .NULL, the source is the suffix to use when a file
- * has no valid suffix.
- *
- * If it was .OBJDIR, the source is a new definition for .OBJDIR,
- * and will cause make to do a new chdir to that path.
+ * If the target was one that doesn't take files as its sources but takes
+ * something like suffixes, we take each space-separated word on the line as
+ * a something and deal with it accordingly.
*/
static void
-ParseDependencySourceSpecial(ParseSpecial specType, char *word,
+ParseDependencySourceSpecial(ParseSpecial special, const char *word,
SearchPathList *paths)
{
- switch (specType) {
+ switch (special) {
case SP_SUFFIXES:
- Suff_AddSuffix(word, &mainNode);
+ Suff_AddSuffix(word);
break;
case SP_PATH:
AddToPaths(word, paths);
@@ -1430,121 +1207,92 @@ ParseDependencySourceSpecial(ParseSpecial specType, char *word,
}
static bool
+ApplyDependencyTarget(char *name, char *nameEnd, ParseSpecial *inout_special,
+ GNodeType *inout_targetAttr,
+ SearchPathList **inout_paths)
+{
+ char savec = *nameEnd;
+ *nameEnd = '\0';
+
+ if (!HandleDependencyTarget(name, inout_special,
+ inout_targetAttr, inout_paths))
+ return false;
+
+ if (*inout_special == SP_NOT && *name != '\0')
+ HandleDependencyTargetMundane(name);
+ else if (*inout_special == SP_PATH && *name != '.' && *name != '\0')
+ Parse_Error(PARSE_WARNING, "Extra target (%s) ignored", name);
+
+ *nameEnd = savec;
+ return true;
+}
+
+static bool
ParseDependencyTargets(char **inout_cp,
- char **inout_line,
const char *lstart,
- ParseSpecial *inout_specType,
- GNodeType *inout_tOp,
- SearchPathList **inout_paths,
- StringList *curTargs)
+ ParseSpecial *inout_special,
+ GNodeType *inout_targetAttr,
+ SearchPathList **inout_paths)
{
- char *cp;
- char *tgt = *inout_line;
- char savec;
- const char *p;
+ char *cp = *inout_cp;
for (;;) {
- /*
- * Here LINE points to the beginning of the next word, and
- * LSTART points to the actual beginning of the line.
- */
+ char *tgt = cp;
- /* Find the end of the next word. */
- cp = tgt;
- p = cp;
- ParseDependencyTargetWord(&p, lstart);
- cp += p - cp;
+ ParseDependencyTargetWord(&cp, lstart);
/*
* If the word is followed by a left parenthesis, it's the
- * name of an object file inside an archive (ar file).
+ * name of one or more files inside an archive.
*/
- 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, SCOPE_CMDLINE)) {
+ if (!IsEscaped(lstart, cp) && *cp == '(') {
+ cp = tgt;
+ if (!Arch_ParseArchive(&cp, targets, SCOPE_CMDLINE)) {
Parse_Error(PARSE_FATAL,
"Error in archive specification: \"%s\"",
tgt);
return false;
}
-
- cp = tgt;
continue;
}
if (*cp == '\0') {
- ParseErrorNoDependency(lstart);
+ InvalidLineType(lstart);
return false;
}
- /* Insert a null terminator. */
- savec = *cp;
- *cp = '\0';
-
- if (!ParseDependencyTarget(tgt, inout_specType, inout_tOp,
- inout_paths))
+ if (!ApplyDependencyTarget(tgt, cp, inout_special,
+ inout_targetAttr, inout_paths))
return false;
- /*
- * 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')
- ParseDependencyTargetMundane(tgt, curTargs);
- else if (*inout_specType == SP_PATH && *tgt != '.' &&
- *tgt != '\0')
- Parse_Error(PARSE_WARNING, "Extra target (%s) ignored",
- tgt);
-
- /* Don't need the inserted null terminator any more. */
- *cp = savec;
-
- /*
- * 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)
- ParseDependencyTargetExtraWarn(&cp, lstart);
+ if (*inout_special != SP_NOT && *inout_special != SP_PATH)
+ SkipExtraTargets(&cp, lstart);
else
pp_skip_whitespace(&cp);
- tgt = cp;
- if (*tgt == '\0')
+ if (*cp == '\0')
break;
- if ((*tgt == '!' || *tgt == ':') &&
- !ParseIsEscaped(lstart, tgt))
+ if ((*cp == '!' || *cp == ':') && !IsEscaped(lstart, cp))
break;
}
*inout_cp = cp;
- *inout_line = tgt;
return true;
}
static void
-ParseDependencySourcesSpecial(char *start, char *end,
- ParseSpecial specType, SearchPathList *paths)
+ParseDependencySourcesSpecial(char *start,
+ ParseSpecial special, SearchPathList *paths)
{
char savec;
while (*start != '\0') {
+ char *end = start;
while (*end != '\0' && !ch_isspace(*end))
end++;
savec = *end;
*end = '\0';
- ParseDependencySourceSpecial(specType, start, paths);
+ ParseDependencySourceSpecial(special, start, paths);
*end = savec;
if (savec != '\0')
end++;
@@ -1553,11 +1301,42 @@ ParseDependencySourcesSpecial(char *start, char *end,
}
}
+static void
+LinkVarToTargets(VarAssign *var)
+{
+ GNodeListNode *ln;
+
+ for (ln = targets->first; ln != NULL; ln = ln->next)
+ Parse_Var(var, ln->datum);
+}
+
static bool
-ParseDependencySourcesMundane(char *start, char *end,
- ParseSpecial specType, GNodeType tOp)
+ParseDependencySourcesMundane(char *start,
+ ParseSpecial special, GNodeType targetAttr)
{
while (*start != '\0') {
+ char *end = start;
+ VarAssign var;
+
+ /*
+ * Check for local variable assignment,
+ * rest of the line is the value.
+ */
+ if (Parse_IsVar(start, &var)) {
+ /*
+ * Check if this makefile has disabled
+ * setting local variables.
+ */
+ bool target_vars = GetBooleanExpr(
+ "${.MAKE.TARGET_LOCAL_VARIABLES}", true);
+
+ if (target_vars)
+ LinkVarToTargets(&var);
+ free(var.varname);
+ if (target_vars)
+ return true;
+ }
+
/*
* The targets take real sources, so we must beware of archive
* specifications (i.e. things with left parentheses in them)
@@ -1588,7 +1367,8 @@ ParseDependencySourcesMundane(char *start, char *end,
while (!Lst_IsEmpty(&sources)) {
GNode *gn = Lst_Dequeue(&sources);
- ParseDependencySource(tOp, gn->name, specType);
+ ApplyDependencySource(targetAttr, gn->name,
+ special);
}
Lst_Done(&sources);
end = start;
@@ -1598,7 +1378,7 @@ ParseDependencySourcesMundane(char *start, char *end,
end++;
}
- ParseDependencySource(tOp, start, specType);
+ ApplyDependencySource(targetAttr, start, special);
}
pp_skip_whitespace(&end);
start = end;
@@ -1607,54 +1387,49 @@ ParseDependencySourcesMundane(char *start, char *end,
}
/*
- * In a dependency line like 'targets: sources', parse the sources.
+ * From a dependency line like 'targets: sources', parse the sources.
*
* See the tests depsrc-*.mk.
*/
static void
-ParseDependencySources(char *line, char *cp, GNodeType tOp,
- ParseSpecial specType, SearchPathList **inout_paths)
+ParseDependencySources(char *p, GNodeType targetAttr,
+ ParseSpecial special, SearchPathList **inout_paths)
{
- if (line[0] == '\0') {
- ParseDependencySourcesEmpty(specType, *inout_paths);
- } else if (specType == SP_MFLAGS) {
- Main_ParseArgLine(line);
- /*
- * Set the initial character to a null-character so the loop
- * to get sources won't get anything.
- */
- *line = '\0';
- } else if (specType == SP_SHELL) {
- if (!Job_ParseShell(line)) {
+ if (*p == '\0') {
+ HandleDependencySourcesEmpty(special, *inout_paths);
+ } else if (special == SP_MFLAGS) {
+ Main_ParseArgLine(p);
+ return;
+ } else if (special == SP_SHELL) {
+ if (!Job_ParseShell(p)) {
Parse_Error(PARSE_FATAL,
"improper shell specification");
return;
}
- *line = '\0';
- } else if (specType == SP_NOTPARALLEL || specType == SP_SINGLESHELL ||
- specType == SP_DELETE_ON_ERROR) {
- *line = '\0';
+ return;
+ } else if (special == SP_NOTPARALLEL || special == SP_SINGLESHELL ||
+ special == SP_DELETE_ON_ERROR) {
+ return;
}
/* Now go for the sources. */
- if (specType == SP_SUFFIXES || specType == SP_PATH ||
- specType == SP_INCLUDES || specType == SP_LIBS ||
- specType == SP_NULL || specType == SP_OBJDIR) {
- ParseDependencySourcesSpecial(line, cp, specType,
- *inout_paths);
+ if (special == SP_SUFFIXES || special == SP_PATH ||
+ special == SP_INCLUDES || special == SP_LIBS ||
+ special == SP_NULL || special == SP_OBJDIR) {
+ ParseDependencySourcesSpecial(p, special, *inout_paths);
if (*inout_paths != NULL) {
Lst_Free(*inout_paths);
*inout_paths = NULL;
}
- if (specType == SP_PATH)
+ if (special == SP_PATH)
Dir_SetPATH();
} else {
assert(*inout_paths == NULL);
- if (!ParseDependencySourcesMundane(line, cp, specType, tOp))
+ if (!ParseDependencySourcesMundane(p, special, targetAttr))
return;
}
- FindMainTarget();
+ MaybeUpdateMainTarget();
}
/*
@@ -1665,8 +1440,7 @@ ParseDependencySources(char *line, char *cp, GNodeType tOp,
* targets. Nodes are created as necessary.
*
* The operator is applied to each node in the global 'targets' list,
- * which is where the nodes found for the targets are kept, by means of
- * the ParseOp function.
+ * which is where the nodes found for the targets are kept.
*
* The sources are parsed in much the same way as the targets, except
* that they are expanded using the wildcarding scheme of the C-Shell,
@@ -1674,109 +1448,61 @@ ParseDependencySources(char *line, char *cp, GNodeType tOp,
* nodes is then linked to each of the targets as one of its children.
*
* Certain targets and sources such as .PHONY or .PRECIOUS are handled
- * specially. These are the ones detailed by the specType variable.
+ * specially, see ParseSpecial.
*
- * The storing of transformation rules such as '.c.o' is also taken care of
- * here. A target is recognized as a transformation rule by calling
- * Suff_IsTransform. If it is a transformation rule, its node is gotten
- * from the suffix module via Suff_AddTransform rather than the standard
- * Targ_FindNode in the target module.
+ * Transformation rules such as '.c.o' are also handled here, see
+ * Suff_AddTransform.
*
* Upon return, the value of the line is unspecified.
*/
static void
ParseDependency(char *line)
{
- char *cp; /* our current position */
- GNodeType op; /* the operator on the line */
- SearchPathList *paths; /* search paths to alter when parsing
- * a list of .PATH targets */
- GNodeType tOp; /* operator from special target */
- /* target names to be found and added to the targets list */
- StringList curTargs = LST_INIT;
- 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;
+ char *p;
+ SearchPathList *paths; /* search paths to alter when parsing a list
+ * of .PATH targets */
+ GNodeType targetAttr; /* from special sources */
+ ParseSpecial special; /* in special targets, the children are
+ * linked as children of the parent but not
+ * vice versa */
DEBUG1(PARSE, "ParseDependency(%s)\n", line);
- tOp = OP_NONE;
-
+ p = line;
paths = NULL;
+ targetAttr = OP_NONE;
+ special = SP_NOT;
- /*
- * First, grind through the targets.
- */
- /* XXX: don't use 'line' as an iterator variable */
- if (!ParseDependencyTargets(&cp, &line, lstart, &specType, &tOp,
- &paths, &curTargs))
+ if (!ParseDependencyTargets(&p, line, &special, &targetAttr, &paths))
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))
- ParseDependencyCheckSpec(specType);
-
- /*
- * Have now parsed all the target names. Must parse the operator next.
- */
- if (!ParseDependencyOp(&cp, lstart, &op))
- goto out;
+ CheckSpecialMundaneMixture(special);
- /*
- * 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);
+ ApplyDependencyOperator(ParseDependencyOp(&p));
- /*
- * 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 */
+ pp_skip_whitespace(&p);
- ParseDependencySources(line, cp, tOp, specType, &paths);
+ ParseDependencySources(p, targetAttr, special, &paths);
out:
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 */
-} VarAssignParsed;
-
/*
* Determine the assignment operator and adjust the end of the variable
* name accordingly.
*/
-static void
-AdjustVarassignOp(const VarAssignParsed *pvar, const char *value,
- VarAssign *out_var)
+static VarAssign
+AdjustVarassignOp(const char *name, const char *nameEnd, const char *op,
+ const char *value)
{
- const char *op = pvar->eq;
- const char *const name = pvar->nameStart;
VarAssignOp type;
+ VarAssign va;
if (op > name && op[-1] == '+') {
- type = VAR_APPEND;
op--;
+ type = VAR_APPEND;
} else if (op > name && op[-1] == '?') {
op--;
@@ -1796,20 +1522,17 @@ AdjustVarassignOp(const VarAssignParsed *pvar, const char *value,
while (op > name && ch_isspace(op[-1]))
op--;
- if (op >= name + 3 && op[-3] == ':' && op[-2] == 's' &&
- op[-1] == 'h') {
- type = VAR_SHELL;
+ if (op - name >= 3 && memcmp(op - 3, ":sh", 3) == 0) {
op -= 3;
+ type = VAR_SHELL;
}
#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;
- }
+ va.varname = bmake_strsedup(name, nameEnd < op ? nameEnd : op);
+ va.op = type;
+ va.value = value;
+ return va;
}
/*
@@ -1825,11 +1548,10 @@ AdjustVarassignOp(const VarAssignParsed *pvar, const char *value,
*
* Used for both lines in a file and command line arguments.
*/
-bool
+static bool
Parse_IsVar(const char *p, VarAssign *out_var)
{
- VarAssignParsed pvar;
- const char *firstSpace = NULL;
+ const char *nameStart, *nameEnd, *firstSpace, *eq;
int level = 0;
cpp_skip_hspace(&p); /* Skip to variable name */
@@ -1837,14 +1559,12 @@ Parse_IsVar(const char *p, VarAssign *out_var)
/*
* 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
+ * ':sh' modifier. Of these two (nameEnd and eq), the earlier one
* determines the actual end of the variable name.
*/
- pvar.nameStart = p;
-#ifdef CLEANUP
- pvar.nameEnd = NULL;
- pvar.eq = NULL;
-#endif
+
+ nameStart = p;
+ firstSpace = NULL;
/*
* Scan for one of the assignment operators outside a variable
@@ -1864,36 +1584,34 @@ Parse_IsVar(const char *p, VarAssign *out_var)
if (level != 0)
continue;
- if (ch == ' ' || ch == '\t')
- if (firstSpace == NULL)
- firstSpace = p - 1;
+ if ((ch == ' ' || ch == '\t') && firstSpace == NULL)
+ firstSpace = p - 1;
while (ch == ' ' || ch == '\t')
ch = *p++;
+ if (ch == '\0')
+ return false;
#ifdef SUNSHCMD
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 (firstSpace != NULL)
+ if (ch == '=')
+ eq = p - 1;
+ else if (*p == '=' &&
+ (ch == '+' || ch == ':' || ch == '?' || ch == '!'))
+ eq = p;
+ else if (firstSpace != NULL)
return false;
+ else
+ continue;
+
+ nameEnd = firstSpace != NULL ? firstSpace : eq;
+ p = eq + 1;
+ cpp_skip_whitespace(&p);
+ *out_var = AdjustVarassignOp(nameStart, nameEnd, eq, p);
+ return true;
}
return false;
@@ -1917,6 +1635,7 @@ VarCheckSyntax(VarAssignOp type, const char *uvalue, GNode *scope)
}
}
+/* Perform a variable assignment that uses the operator ':='. */
static void
VarAssign_EvalSubst(GNode *scope, const char *name, const char *uvalue,
FStr *out_avalue)
@@ -1929,6 +1648,8 @@ VarAssign_EvalSubst(GNode *scope, const char *name, const char *uvalue,
*
* TODO: Add a test that demonstrates why this code is needed,
* apart from making the debug log longer.
+ *
+ * XXX: The variable name is expanded up to 3 times.
*/
if (!Var_ExistsExpand(scope, name))
Var_SetExpand(scope, name, "");
@@ -1941,29 +1662,24 @@ VarAssign_EvalSubst(GNode *scope, const char *name, const char *uvalue,
*out_avalue = FStr_InitOwn(evalue);
}
+/* Perform a variable assignment that uses the operator '!='. */
static void
VarAssign_EvalShell(const char *name, const char *uvalue, GNode *scope,
FStr *out_avalue)
{
FStr cmd;
- const char *errfmt;
- char *cmdOut;
+ char *output, *error;
cmd = FStr_InitRefer(uvalue);
- if (strchr(cmd.str, '$') != NULL) {
- char *expanded;
- (void)Var_Subst(cmd.str, SCOPE_CMDLINE, VARE_UNDEFERR,
- &expanded);
- /* TODO: handle errors */
- cmd = FStr_InitOwn(expanded);
- }
-
- cmdOut = Cmd_Exec(cmd.str, &errfmt);
- Var_SetExpand(scope, name, cmdOut);
- *out_avalue = FStr_InitOwn(cmdOut);
+ Var_Expand(&cmd, SCOPE_CMDLINE, VARE_UNDEFERR);
- if (errfmt != NULL)
- Parse_Error(PARSE_WARNING, errfmt, cmd.str);
+ output = Cmd_Exec(cmd.str, &error);
+ Var_SetExpand(scope, name, output);
+ *out_avalue = FStr_InitOwn(output);
+ if (error != NULL) {
+ Parse_Error(PARSE_WARNING, "%s", error);
+ free(error);
+ }
FStr_Done(&cmd);
}
@@ -1992,6 +1708,7 @@ VarAssign_Eval(const char *name, VarAssignOp op, const char *uvalue,
else if (op == VAR_SHELL)
VarAssign_EvalShell(name, uvalue, scope, &avalue);
else {
+ /* XXX: The variable name is expanded up to 2 times. */
if (op == VAR_DEFAULT && Var_ExistsExpand(scope, name))
return false;
@@ -2007,7 +1724,7 @@ static void
VarAssignSpecial(const char *name, const char *avalue)
{
if (strcmp(name, MAKEOVERRIDES) == 0)
- Main_ExportMAKEFLAGS(false); /* re-export MAKEFLAGS */
+ Main_ExportMAKEFLAGS(false); /* re-export MAKEFLAGS */
else if (strcmp(name, ".CURDIR") == 0) {
/*
* Someone is being (too?) clever...
@@ -2022,19 +1739,17 @@ VarAssignSpecial(const char *name, const char *avalue)
Var_ExportVars(avalue);
}
-/* Perform the variable variable assignment in the given scope. */
-void
+/* Perform the variable assignment in the given scope. */
+static void
Parse_Var(VarAssign *var, GNode *scope)
{
- FStr avalue; /* actual value (maybe expanded) */
+ FStr avalue; /* actual value (maybe expanded) */
VarCheckSyntax(var->op, var->value, scope);
if (VarAssign_Eval(var->varname, var->op, var->value, scope, &avalue)) {
VarAssignSpecial(var->varname, avalue.str);
FStr_Done(&avalue);
}
-
- free(var->varname);
}
@@ -2052,7 +1767,7 @@ MaybeSubMake(const char *cmd)
char endc;
/* XXX: What if progname != "make"? */
- if (p[0] == 'm' && p[1] == 'a' && p[2] == 'k' && p[3] == 'e')
+ if (strncmp(p, "make", 4) == 0)
if (start == cmd || !ch_isalnum(p[-1]))
if (!ch_isalnum(p[4]))
return true;
@@ -2072,9 +1787,8 @@ MaybeSubMake(const char *cmd)
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;
+ if (strncmp(p, "MAKE", 4) == 0 && p[4] == endc)
+ return true;
}
return false;
}
@@ -2086,7 +1800,7 @@ MaybeSubMake(const char *cmd)
* be that.
*/
static void
-ParseAddCmd(GNode *gn, char *cmd)
+GNode_AddCommand(GNode *gn, char *cmd)
{
/* Add to last (ie current) cohort for :: targets */
if ((gn->type & OP_DOUBLEDEP) && gn->cohorts.last != NULL)
@@ -2104,13 +1818,13 @@ ParseAddCmd(GNode *gn, char *cmd)
Lst_Append(&gn->commands, cmd);
Parse_Error(PARSE_WARNING,
"overriding commands for target \"%s\"; "
- "previous commands defined at %s: %d ignored",
+ "previous commands defined at %s: %u ignored",
gn->name, gn->fname, gn->lineno);
#else
Parse_Error(PARSE_WARNING,
"duplicate script for target \"%s\" ignored",
gn->name);
- ParseErrorInternal(gn->fname, (size_t)gn->lineno, PARSE_WARNING,
+ ParseErrorInternal(gn->fname, gn->lineno, PARSE_WARNING,
"using previous script for \"%s\" defined here",
gn->name);
#endif
@@ -2140,7 +1854,7 @@ Parse_AddIncludeDir(const char *dir)
static void
IncludeFile(const char *file, bool isSystem, bool depinc, bool silent)
{
- struct loadedfile *lf;
+ Buffer buf;
char *fullname; /* full pathname of file */
char *newName;
char *slash, *incdir;
@@ -2158,7 +1872,7 @@ IncludeFile(const char *file, bool isSystem, bool depinc, bool silent)
* we can locate the file.
*/
- incdir = bmake_strdup(CurFile()->fname);
+ incdir = bmake_strdup(CurFile()->name.str);
slash = strrchr(incdir, '/');
if (slash != NULL) {
*slash = '\0';
@@ -2233,15 +1947,13 @@ IncludeFile(const char *file, bool isSystem, bool depinc, bool silent)
return;
}
- /* load it */
- lf = loadfile(fullname, fd);
+ buf = loadfile(fullname, fd);
+ (void)close(fd);
- /* Start reading from this file next */
- Parse_PushInput(fullname, 0, -1, loadedfile_readMore, lf);
- CurFile()->lf = lf;
+ Parse_PushInput(fullname, 1, 0, buf, NULL);
if (depinc)
doing_depend = depinc; /* only turn it on */
- /* TODO: consider free(fullname); */
+ free(fullname);
}
/*
@@ -2285,13 +1997,7 @@ ParseInclude(char *directive)
*p = '\0';
- if (strchr(file.str, '$') != NULL) {
- char *xfile;
- Var_Subst(file.str, SCOPE_CMDLINE, VARE_WANTRES, &xfile);
- /* TODO: handle errors */
- file = FStr_InitOwn(xfile);
- }
-
+ Var_Expand(&file, SCOPE_CMDLINE, VARE_WANTRES);
IncludeFile(file.str, endc == '>', directive[0] == 'd', silent);
FStr_Done(&file);
}
@@ -2318,8 +2024,8 @@ SetFilenameVars(const char *filename, const char *dirvar, const char *filevar)
Global_Set(dirvar, dirname.str);
Global_Set(filevar, basename);
- DEBUG5(PARSE, "%s: ${%s} = `%s' ${%s} = `%s'\n",
- __func__, dirvar, dirname.str, filevar, basename);
+ DEBUG4(PARSE, "SetFilenameVars: ${%s} = `%s' ${%s} = `%s'\n",
+ dirvar, dirname.str, filevar, basename);
FStr_Done(&dirname);
}
@@ -2333,17 +2039,17 @@ static const char *
GetActuallyIncludingFile(void)
{
size_t i;
- const IFile *incs = GetInclude(0);
+ const IncludedFile *incs = GetInclude(0);
for (i = includes.len; i >= 2; i--)
- if (!incs[i - 1].fromForLoop)
- return incs[i - 2].fname;
+ if (incs[i - 1].forLoop == NULL)
+ return incs[i - 2].name.str;
return NULL;
}
/* Set .PARSEDIR, .PARSEFILE, .INCLUDEDFROMDIR and .INCLUDEDFROMFILE. */
static void
-ParseSetParseFile(const char *filename)
+SetParseFile(const char *filename)
{
const char *including;
@@ -2364,17 +2070,16 @@ StrContainsWord(const char *str, const char *word)
{
size_t strLen = strlen(str);
size_t wordLen = strlen(word);
- const char *p, *end;
+ const char *p;
if (strLen < wordLen)
- return false; /* str is too short to contain word */
+ return false;
- end = str + strLen - wordLen;
for (p = str; p != NULL; p = strchr(p, ' ')) {
if (*p == ' ')
p++;
- if (p > end)
- return false; /* cannot contain word */
+ if (p > str + strLen - wordLen)
+ return false;
if (memcmp(p, word, wordLen) == 0 &&
(p[wordLen] == '\0' || p[wordLen] == ' '))
@@ -2406,67 +2111,45 @@ VarContainsWord(const char *varname, const char *word)
* of makefiles that have been loaded.
*/
static void
-ParseTrackInput(const char *name)
+TrackInput(const char *name)
{
if (!VarContainsWord(MAKE_MAKEFILES, name))
Global_Append(MAKE_MAKEFILES, name);
}
-/*
- * Start parsing from the given source.
- *
- * The given file is added to the includes stack.
- */
+/* Parse from the given buffer, later return to the current file. */
void
-Parse_PushInput(const char *name, int lineno, int fd,
- ReadMoreProc readMore, void *readMoreArg)
+Parse_PushInput(const char *name, unsigned lineno, unsigned readLines,
+ Buffer buf, struct ForLoop *forLoop)
{
- IFile *curFile;
- char *buf;
- size_t len;
- bool fromForLoop = name == NULL;
+ IncludedFile *curFile;
- if (fromForLoop)
- name = CurFile()->fname;
+ if (forLoop != NULL)
+ name = CurFile()->name.str;
else
- ParseTrackInput(name);
+ TrackInput(name);
- DEBUG3(PARSE, "Parse_PushInput: %s %s, line %d\n",
- readMore == loadedfile_readMore ? "file" : ".for loop in",
- name, lineno);
-
- if (fd == -1 && readMore == NULL)
- /* sanity */
- return;
+ DEBUG3(PARSE, "Parse_PushInput: %s %s, line %u\n",
+ forLoop != NULL ? ".for loop in": "file", name, lineno);
curFile = Vector_Push(&includes);
- curFile->fname = bmake_strdup(name);
- curFile->fromForLoop = fromForLoop;
+ curFile->name = FStr_InitOwn(bmake_strdup(name));
curFile->lineno = lineno;
- curFile->first_lineno = lineno;
- curFile->readMore = readMore;
- curFile->readMoreArg = readMoreArg;
- curFile->lf = NULL;
+ curFile->readLines = readLines;
+ curFile->forHeadLineno = lineno;
+ curFile->forBodyReadLines = readLines;
+ curFile->buf = buf;
curFile->depending = doing_depend; /* restore this on EOF */
+ curFile->forLoop = forLoop;
- 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;
+ if (forLoop != NULL && !For_NextIteration(forLoop, &curFile->buf))
+ abort(); /* see For_Run */
+ curFile->buf_ptr = curFile->buf.data;
+ curFile->buf_end = curFile->buf.data + curFile->buf.len;
curFile->cond_depth = Cond_save_depth();
- ParseSetParseFile(name);
+ SetParseFile(name);
}
/* Check if the directive is an include directive. */
@@ -2518,22 +2201,13 @@ ParseTraditionalInclude(char *line)
char *file = line + (silent ? 8 : 7);
char *all_files;
- DEBUG2(PARSE, "%s: %s\n", __func__, file);
+ DEBUG1(PARSE, "ParseTraditionalInclude: %s\n", file);
pp_skip_whitespace(&file);
- /*
- * Substitute for any variables in the file name before trying to
- * find the thing.
- */
(void)Var_Subst(file, SCOPE_CMDLINE, VARE_WANTRES, &all_files);
/* TODO: handle errors */
- 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 != '\0' && !ch_isspace(*cp); cp++)
@@ -2546,7 +2220,7 @@ ParseTraditionalInclude(char *line)
IncludeFile(file, false, false, silent);
}
-out:
+
free(all_files);
}
#endif
@@ -2559,7 +2233,7 @@ ParseGmakeExport(char *line)
char *variable = line + 6;
char *value;
- DEBUG2(PARSE, "%s: %s\n", __func__, variable);
+ DEBUG1(PARSE, "ParseGmakeExport: %s\n", variable);
pp_skip_whitespace(&variable);
@@ -2596,33 +2270,27 @@ ParseGmakeExport(char *line)
static bool
ParseEOF(void)
{
- char *ptr;
- size_t len;
- IFile *curFile = CurFile();
-
- assert(curFile->readMore != NULL);
+ IncludedFile *curFile = CurFile();
- 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 */
+ doing_depend = curFile->depending;
+ if (curFile->forLoop != NULL &&
+ For_NextIteration(curFile->forLoop, &curFile->buf)) {
+ curFile->buf_ptr = curFile->buf.data;
+ curFile->buf_end = curFile->buf.data + curFile->buf.len;
+ curFile->readLines = curFile->forBodyReadLines;
+ return true;
+ }
- /* Ensure the makefile (or loop) didn't have mismatched conditionals */
+ /*
+ * Ensure the makefile (or .for 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);
+ FStr_Done(&curFile->name);
+ Buf_Done(&curFile->buf);
+ if (curFile->forLoop != NULL)
+ ForLoop_Free(curFile->forLoop);
Vector_Pop(&includes);
if (includes.len == 0) {
@@ -2635,10 +2303,10 @@ ParseEOF(void)
}
curFile = CurFile();
- DEBUG2(PARSE, "ParseEOF: returning to file %s, line %d\n",
- curFile->fname, curFile->lineno);
+ DEBUG2(PARSE, "ParseEOF: returning to file %s, line %u\n",
+ curFile->name.str, curFile->readLines + 1);
- ParseSetParseFile(curFile->fname);
+ SetParseFile(curFile->name.str);
return true;
}
@@ -2654,18 +2322,18 @@ typedef enum ParseRawLineResult {
* the line is not null-terminated.
*/
static ParseRawLineResult
-ParseRawLine(IFile *curFile, char **out_line, char **out_line_end,
- char **out_firstBackslash, char **out_firstComment)
+ParseRawLine(IncludedFile *curFile, char **out_line, char **out_line_end,
+ char **out_firstBackslash, char **out_commentLineEnd)
{
char *line = curFile->buf_ptr;
char *buf_end = curFile->buf_end;
char *p = line;
char *line_end = line;
char *firstBackslash = NULL;
- char *firstComment = NULL;
+ char *commentLineEnd = NULL;
ParseRawLineResult res = PRLR_LINE;
- curFile->lineno++;
+ curFile->readLines++;
for (;;) {
char ch;
@@ -2676,8 +2344,7 @@ ParseRawLine(IFile *curFile, char **out_line, char **out_line_end,
}
ch = *p;
- if (ch == '\0' ||
- (ch == '\\' && p + 1 < buf_end && p[1] == '\0')) {
+ if (ch == '\0' || (ch == '\\' && p[1] == '\0')) {
Parse_Error(PARSE_FATAL, "Zero byte read from file");
return PRLR_ERROR;
}
@@ -2687,7 +2354,7 @@ ParseRawLine(IFile *curFile, char **out_line, char **out_line_end,
if (firstBackslash == NULL)
firstBackslash = p;
if (p[1] == '\n') {
- curFile->lineno++;
+ curFile->readLines++;
if (p + 2 == buf_end) {
line_end = p;
*line_end = '\n';
@@ -2705,9 +2372,9 @@ ParseRawLine(IFile *curFile, char **out_line, char **out_line_end,
* Remember the first '#' for comment stripping, unless
* the previous char was '[', as in the modifier ':[#]'.
*/
- if (ch == '#' && firstComment == NULL &&
+ if (ch == '#' && commentLineEnd == NULL &&
!(p > line && p[-1] == '['))
- firstComment = line_end;
+ commentLineEnd = line_end;
p++;
if (ch == '\n')
@@ -2718,11 +2385,11 @@ ParseRawLine(IFile *curFile, char **out_line, char **out_line_end,
line_end = p;
}
- *out_line = line;
curFile->buf_ptr = p;
+ *out_line = line;
*out_line_end = line_end;
*out_firstBackslash = firstBackslash;
- *out_firstComment = firstComment;
+ *out_commentLineEnd = commentLineEnd;
return res;
}
@@ -2733,7 +2400,7 @@ ParseRawLine(IFile *curFile, char **out_line, char **out_line_end,
static void
UnescapeBackslash(char *line, char *start)
{
- char *src = start;
+ const char *src = start;
char *dst = start;
char *spaceStart = line;
@@ -2748,35 +2415,24 @@ UnescapeBackslash(char *line, char *start)
ch = *src++;
if (ch == '\0') {
- /* Delete '\\' at end of buffer */
+ /* Delete '\\' at the end of the buffer. */
dst--;
break;
}
- /* Delete '\\' from before '#' on non-command lines */
- if (ch == '#' && line[0] != '\t') {
+ /* Delete '\\' from before '#' on non-command lines. */
+ if (ch == '#' && line[0] != '\t')
*dst++ = ch;
- continue;
- }
-
- if (ch != '\n') {
- /* Leave '\\' in buffer for later */
+ else if (ch == '\n') {
+ cpp_skip_hspace(&src);
+ *dst++ = ' ';
+ } else {
+ /* Leave '\\' in the buffer for later. */
*dst++ = '\\';
- /*
- * Make sure we don't delete an escaped ' ' from the
- * line end.
- */
- spaceStart = dst + 1;
*dst++ = ch;
- continue;
+ /* Keep an escaped ' ' at the line end. */
+ spaceStart = dst;
}
-
- /*
- * Escaped '\n' -- replace following whitespace with a single
- * ' '.
- */
- pp_skip_hspace(&src);
- *dst++ = ' ';
}
/* Delete any trailing spaces - eg from empty continuations */
@@ -2785,13 +2441,13 @@ UnescapeBackslash(char *line, char *start)
*dst = '\0';
}
-typedef enum GetLineMode {
+typedef enum LineKind {
/*
* 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,
+ LK_NONEMPTY,
/*
* Return the next line, even if it is empty or a comment.
@@ -2800,7 +2456,7 @@ typedef enum GetLineMode {
* Used in .for loops to collect the body of the loop while waiting
* for the corresponding .endfor.
*/
- GLM_FOR_BODY,
+ LK_FOR_BODY,
/*
* Return the next line that starts with a dot.
@@ -2810,29 +2466,34 @@ typedef enum GetLineMode {
* Used in .if directives to skip over irrelevant branches while
* waiting for the corresponding .endif.
*/
- GLM_DOT
-} GetLineMode;
+ LK_DOT
+} LineKind;
-/* Return the next "interesting" logical line from the current file. */
+/*
+ * Return the next "interesting" logical line from the current file. The
+ * returned string will be freed at the end of including the file.
+ */
static char *
-ParseGetLine(GetLineMode mode)
+ReadLowLevelLine(LineKind kind)
{
- IFile *curFile = CurFile();
+ IncludedFile *curFile = CurFile();
+ ParseRawLineResult res;
char *line;
char *line_end;
char *firstBackslash;
- char *firstComment;
+ char *commentLineEnd;
for (;;) {
- ParseRawLineResult res = ParseRawLine(curFile,
- &line, &line_end, &firstBackslash, &firstComment);
+ curFile->lineno = curFile->readLines + 1;
+ res = ParseRawLine(curFile,
+ &line, &line_end, &firstBackslash, &commentLineEnd);
if (res == PRLR_ERROR)
return NULL;
- if (line_end == line || firstComment == line) {
+ if (line == line_end || line == commentLineEnd) {
if (res == PRLR_EOF)
return NULL;
- if (mode != GLM_FOR_BODY)
+ if (kind != LK_FOR_BODY)
continue;
}
@@ -2840,59 +2501,53 @@ ParseGetLine(GetLineMode mode)
assert(ch_isspace(*line_end));
*line_end = '\0';
- if (mode == GLM_FOR_BODY)
+ if (kind == LK_FOR_BODY)
return line; /* Don't join the physical lines. */
- if (mode == GLM_DOT && line[0] != '.')
+ if (kind == LK_DOT && line[0] != '.')
continue;
break;
}
- /* Brutally ignore anything after a non-escaped '#' in non-commands. */
- if (firstComment != NULL && line[0] != '\t')
- *firstComment = '\0';
-
- /* If we didn't see a '\\' then the in-situ data is fine. */
- if (firstBackslash == NULL)
- return line;
-
- /* Remove escapes from '\n' and '#' */
- UnescapeBackslash(line, firstBackslash);
-
+ if (commentLineEnd != NULL && line[0] != '\t')
+ *commentLineEnd = '\0';
+ if (firstBackslash != NULL)
+ UnescapeBackslash(line, firstBackslash);
return line;
}
static bool
-ParseSkippedBranches(void)
+SkipIrrelevantBranches(void)
{
- char *line;
+ const char *line;
- while ((line = ParseGetLine(GLM_DOT)) != NULL) {
- if (Cond_EvalLine(line) == COND_PARSE)
- break;
+ while ((line = ReadLowLevelLine(LK_DOT)) != NULL) {
+ if (Cond_EvalLine(line) == CR_TRUE)
+ return true;
/*
- * TODO: Check for typos in .elif directives
- * such as .elsif or .elseif.
+ * 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
- * ParseDependencyLine can, and to prevent code
- * duplication, these would need to be called
- * with a flag called onlyCheckSyntax.
+ * This check will probably duplicate some of the code in
+ * ParseLine. Most of the code there cannot apply, only
+ * ParseVarassign and ParseDependencyLine can, and to prevent
+ * code duplication, these would need to be called with a
+ * flag called onlyCheckSyntax.
*
* See directive-elif.mk for details.
*/
}
- return line != NULL;
+ return false;
}
static bool
ParseForLoop(const char *line)
{
int rval;
- int firstLineno;
+ unsigned forHeadLineno;
+ unsigned bodyReadLines;
+ int forLevel;
rval = For_Eval(line);
if (rval == 0)
@@ -2900,21 +2555,22 @@ ParseForLoop(const char *line)
if (rval < 0)
return true; /* Syntax error - error printed, ignore line */
- firstLineno = CurFile()->lineno;
+ forHeadLineno = CurFile()->lineno;
+ bodyReadLines = CurFile()->readLines;
- /* Accumulate loop lines until matching .endfor */
+ /* Accumulate the loop body until the matching '.endfor'. */
+ forLevel = 1;
do {
- line = ParseGetLine(GLM_FOR_BODY);
+ line = ReadLowLevelLine(LK_FOR_BODY);
if (line == NULL) {
Parse_Error(PARSE_FATAL,
"Unexpected end of file in .for loop");
break;
}
- } while (For_Accum(line));
+ } while (For_Accum(line, &forLevel));
- For_Run(firstLineno); /* Stash each iteration as a new 'input file' */
-
- return true; /* Read next line from for-loop buffer */
+ For_Run(forHeadLineno, bodyReadLines);
+ return true;
}
/*
@@ -2924,35 +2580,30 @@ ParseForLoop(const char *line)
* 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,
- * or NULL.
+ * Return a line without trailing whitespace, or NULL for EOF. The returned
+ * string will be freed at the end of including the file.
*/
static char *
-ParseReadLine(void)
+ReadHighLevelLine(void)
{
char *line;
for (;;) {
- line = ParseGetLine(GLM_NONEMPTY);
+ line = ReadLowLevelLine(LK_NONEMPTY);
if (line == NULL)
return NULL;
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:
- if (!ParseSkippedBranches())
+ case CR_FALSE: /* May also mean a syntax error. */
+ if (!SkipIrrelevantBranches())
return NULL;
continue;
- case COND_PARSE:
+ case CR_TRUE:
continue;
- case COND_INVALID: /* Not a conditional line */
+ case CR_ERROR: /* Not a conditional line */
if (ParseForLoop(line))
continue;
break;
@@ -3007,7 +2658,7 @@ ParseLine_ShellCommand(const char *p)
for (ln = targets->first; ln != NULL; ln = ln->next) {
GNode *gn = ln->datum;
- ParseAddCmd(gn, cmd);
+ GNode_AddCommand(gn, cmd);
}
#ifdef CLEANUP
Lst_Append(&targCmds, cmd);
@@ -3033,7 +2684,7 @@ ParseDirective(char *line)
}
dir.start = cp;
- while (ch_isalpha(*cp) || *cp == '-')
+ while (ch_islower(*cp) || *cp == '-')
cp++;
dir.end = cp;
@@ -3043,47 +2694,40 @@ ParseDirective(char *line)
pp_skip_whitespace(&cp);
arg = cp;
- if (Substring_Equals(dir, "undef")) {
+ if (Substring_Equals(dir, "undef"))
Var_Undef(arg);
- return true;
- } else if (Substring_Equals(dir, "export")) {
+ else if (Substring_Equals(dir, "export"))
Var_Export(VEM_PLAIN, arg);
- return true;
- } else if (Substring_Equals(dir, "export-env")) {
+ else if (Substring_Equals(dir, "export-env"))
Var_Export(VEM_ENV, arg);
- return true;
- } else if (Substring_Equals(dir, "export-literal")) {
+ else if (Substring_Equals(dir, "export-literal"))
Var_Export(VEM_LITERAL, arg);
- return true;
- } else if (Substring_Equals(dir, "unexport")) {
+ else if (Substring_Equals(dir, "unexport"))
Var_UnExport(false, arg);
- return true;
- } else if (Substring_Equals(dir, "unexport-env")) {
+ else if (Substring_Equals(dir, "unexport-env"))
Var_UnExport(true, arg);
- return true;
- } else if (Substring_Equals(dir, "info")) {
- ParseMessage(PARSE_INFO, "info", arg);
- return true;
- } else if (Substring_Equals(dir, "warning")) {
- ParseMessage(PARSE_WARNING, "warning", arg);
- return true;
- } else if (Substring_Equals(dir, "error")) {
- ParseMessage(PARSE_FATAL, "error", arg);
- return true;
- }
- return false;
+ else if (Substring_Equals(dir, "info"))
+ HandleMessage(PARSE_INFO, "info", arg);
+ else if (Substring_Equals(dir, "warning"))
+ HandleMessage(PARSE_WARNING, "warning", arg);
+ else if (Substring_Equals(dir, "error"))
+ HandleMessage(PARSE_FATAL, "error", arg);
+ else
+ return false;
+ return true;
}
-static bool
-ParseVarassign(const char *line)
+bool
+Parse_VarAssign(const char *line, bool finishDependencyGroup, GNode *scope)
{
VarAssign var;
if (!Parse_IsVar(line, &var))
return false;
-
- FinishDependencyGroup();
- Parse_Var(&var, SCOPE_GLOBAL);
+ if (finishDependencyGroup)
+ FinishDependencyGroup();
+ Parse_Var(&var, scope);
+ free(var.varname);
return true;
}
@@ -3109,7 +2753,7 @@ FindSemicolon(char *p)
}
/*
- * dependency -> target... op [source...] [';' command]
+ * dependency -> [target...] op [source...] [';' command]
* op -> ':' | '::' | '!'
*/
static void
@@ -3122,7 +2766,7 @@ ParseDependencyLine(char *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.
+ * Attempt to skip over ';' inside substitution patterns.
*/
{
char *semicolon = FindSemicolon(line);
@@ -3150,20 +2794,22 @@ ParseDependencyLine(char *line)
* in which the middle is interpreted as a source, not a target.
*/
- /* In lint mode, allow undefined variables to appear in
- * dependency lines.
+ /*
+ * 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.
+ * 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. */
+ * 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.
+ */
emode = opts.strict ? VARE_WANTRES : VARE_UNDEFERR;
(void)Var_Subst(line, SCOPE_CMDLINE, emode, &expanded_line);
/* TODO: handle errors */
@@ -3219,7 +2865,7 @@ ParseLine(char *line)
}
#endif
- if (ParseVarassign(line))
+ if (Parse_VarAssign(line, true, SCOPE_GLOBAL))
return;
FinishDependencyGroup();
@@ -3230,30 +2876,24 @@ ParseLine(char *line)
/*
* Parse a top-level makefile, incorporating its content into the global
* dependency graph.
- *
- * Input:
- * name The name of the file being read
- * fd The open file to parse; will be closed at the end
*/
void
Parse_File(const char *name, int fd)
{
- char *line; /* the line we're working on */
- struct loadedfile *lf;
+ char *line;
+ Buffer buf;
- lf = loadfile(name, fd);
+ buf = loadfile(name, fd != -1 ? fd : STDIN_FILENO);
+ if (fd != -1)
+ (void)close(fd);
assert(targets == NULL);
- if (name == NULL)
- name = "(stdin)";
-
- Parse_PushInput(name, 0, -1, loadedfile_readMore, lf);
- CurFile()->lf = lf;
+ Parse_PushInput(name, 1, 0, buf, NULL);
do {
- while ((line = ParseReadLine()) != NULL) {
- DEBUG2(PARSE, "ParseReadLine (%d): '%s'\n",
+ while ((line = ReadHighLevelLine()) != NULL) {
+ DEBUG2(PARSE, "Parsing line %u: %s\n",
CurFile()->lineno, line);
ParseLine(line);
}
@@ -3265,9 +2905,9 @@ Parse_File(const char *name, int fd)
if (parseErrors != 0) {
(void)fflush(stdout);
(void)fprintf(stderr,
- "%s: Fatal errors encountered -- cannot continue",
+ "%s: Fatal errors encountered -- cannot continue\n",
progname);
- PrintOnError(NULL, NULL);
+ PrintOnError(NULL, "");
exit(1);
}
}
@@ -3280,7 +2920,7 @@ Parse_Init(void)
parseIncPath = SearchPath_New();
sysIncPath = SearchPath_New();
defSysIncPath = SearchPath_New();
- Vector_Init(&includes, sizeof(IFile));
+ Vector_Init(&includes, sizeof(IncludedFile));
}
/* Clean up the parsing module. */