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.c1019
1 files changed, 544 insertions, 475 deletions
diff --git a/contrib/bmake/parse.c b/contrib/bmake/parse.c
index 398c594f523e..ecc77366d2d7 100644
--- a/contrib/bmake/parse.c
+++ b/contrib/bmake/parse.c
@@ -1,4 +1,4 @@
-/* $NetBSD: parse.c,v 1.670 2022/04/18 16:09:05 sjg Exp $ */
+/* $NetBSD: parse.c,v 1.753 2025/06/28 22:39:27 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
@@ -91,7 +91,7 @@
* Parse_Error Report a parse error, a warning or an informational
* message.
*
- * Parse_MainName Returns a list of the single main target to create.
+ * Parse_MainName Populate the list of targets to create.
*/
#include <sys/types.h>
@@ -105,27 +105,22 @@
#include <stdint.h>
#endif
-#ifdef HAVE_MMAP
-#include <sys/mman.h>
-
-#ifndef MAP_COPY
-#define MAP_COPY MAP_PRIVATE
-#endif
-#ifndef MAP_FILE
-#define MAP_FILE 0
-#endif
-#endif
-
#include "dir.h"
#include "job.h"
#include "pathnames.h"
/* "@(#)parse.c 8.3 (Berkeley) 3/19/94" */
-MAKE_RCSID("$NetBSD: parse.c,v 1.670 2022/04/18 16:09:05 sjg Exp $");
+MAKE_RCSID("$NetBSD: parse.c,v 1.753 2025/06/28 22:39:27 rillig Exp $");
-/*
- * A file being read.
- */
+/* Detects a multiple-inclusion guard in a makefile. */
+typedef enum {
+ GS_START, /* at the beginning of the file */
+ GS_COND, /* after the guard condition */
+ GS_DONE, /* after the closing .endif */
+ GS_NO /* the file is not guarded */
+} GuardState;
+
+/* A file being parsed. */
typedef struct IncludedFile {
FStr name; /* absolute or relative to the cwd */
unsigned lineno; /* 1-based */
@@ -135,14 +130,18 @@ typedef struct IncludedFile {
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 */
+ unsigned condMinDepth; /* depth of nested 'if' directives, at the
+ * beginning of the file */
bool depending; /* state of doing_depend on EOF */
Buffer buf; /* the file's content or the body of the .for
* loop; either empty or ends with '\n' */
- char *buf_ptr; /* next char to be read */
+ char *buf_ptr; /* next char to be read from buf */
char *buf_end; /* buf_end[-1] == '\n' */
+ GuardState guardState;
+ Guard *guard;
+
struct ForLoop *forLoop;
} IncludedFile;
@@ -164,6 +163,7 @@ typedef enum ParseSpecial {
SP_NOMETA, /* .NOMETA */
SP_NOMETA_CMP, /* .NOMETA_CMP */
SP_NOPATH, /* .NOPATH */
+ SP_NOREADONLY, /* .NOREADONLY */
SP_NOT, /* Not special */
SP_NOTPARALLEL, /* .NOTPARALLEL or .NO_PARALLEL */
SP_NULL, /* .NULL; not mentioned in the manual page */
@@ -172,15 +172,15 @@ typedef enum ParseSpecial {
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 */
-#endif
SP_PRECIOUS, /* .PRECIOUS */
+ SP_READONLY, /* .READONLY */
SP_SHELL, /* .SHELL */
SP_SILENT, /* .SILENT */
SP_SINGLESHELL, /* .SINGLESHELL; not mentioned in the manual page */
SP_STALE, /* .STALE */
SP_SUFFIXES, /* .SUFFIXES */
+ SP_SYSPATH, /* .SYSPATH */
SP_WAIT /* .WAIT */
} ParseSpecial;
@@ -223,9 +223,9 @@ static GNodeList *targets;
#ifdef CLEANUP
/*
* 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.
+ * with duplicate values. 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 = LST_INIT;
#endif
@@ -236,7 +236,7 @@ static StringList targCmds = LST_INIT;
*/
static GNode *order_pred;
-static int parseErrors = 0;
+int parseErrors;
/*
* The include chain of makefiles. At index 0 is the top-level makefile from
@@ -253,10 +253,7 @@ SearchPath *defSysIncPath; /* default for sysIncPath */
/*
* The parseKeywords table is searched using binary search when deciding
- * if a target or source is special. The 'spec' field is the ParseSpecial
- * type of the keyword (SP_NOT if the keyword isn't special as a target) while
- * the 'op' field is the operator to apply to the list of targets if the
- * keyword is used as a source ("0" if the keyword isn't special as a source)
+ * if a target or source is special.
*/
static const struct {
const char name[17];
@@ -284,6 +281,7 @@ static const struct {
{ ".NOMETA", SP_NOMETA, OP_NOMETA },
{ ".NOMETA_CMP", SP_NOMETA_CMP, OP_NOMETA_CMP },
{ ".NOPATH", SP_NOPATH, OP_NOPATH },
+ { ".NOREADONLY", SP_NOREADONLY, OP_NONE },
{ ".NOTMAIN", SP_ATTRIBUTE, OP_NOTMAIN },
{ ".NOTPARALLEL", SP_NOTPARALLEL, OP_NONE },
{ ".NO_PARALLEL", SP_NOTPARALLEL, OP_NONE },
@@ -294,16 +292,16 @@ static const struct {
{ ".PARALLEL", SP_PARALLEL, OP_NONE },
{ ".PATH", SP_PATH, OP_NONE },
{ ".PHONY", SP_PHONY, OP_PHONY },
-#ifdef POSIX
{ ".POSIX", SP_POSIX, OP_NONE },
-#endif
{ ".PRECIOUS", SP_PRECIOUS, OP_PRECIOUS },
+ { ".READONLY", SP_READONLY, OP_NONE },
{ ".RECURSIVE", SP_ATTRIBUTE, OP_MAKE },
{ ".SHELL", SP_SHELL, OP_NONE },
{ ".SILENT", SP_SILENT, OP_SILENT },
{ ".SINGLESHELL", SP_SINGLESHELL, OP_NONE },
{ ".STALE", SP_STALE, OP_NONE },
{ ".SUFFIXES", SP_SUFFIXES, OP_NONE },
+ { ".SYSPATH", SP_SYSPATH, OP_NONE },
{ ".USE", SP_ATTRIBUTE, OP_USE },
{ ".USEBEFORE", SP_ATTRIBUTE, OP_USEBEFORE },
{ ".WAIT", SP_WAIT, OP_NONE },
@@ -311,21 +309,47 @@ static const struct {
enum PosixState posix_state = PS_NOT_YET;
+static HashTable /* full file name -> Guard */ guards;
+
+
+static List *
+Lst_New(void)
+{
+ List *list = bmake_malloc(sizeof *list);
+ Lst_Init(list);
+ return list;
+}
+
+static void
+Lst_Free(List *list)
+{
+
+ Lst_Done(list);
+ free(list);
+}
+
static IncludedFile *
GetInclude(size_t i)
{
+ assert(i < includes.len);
return Vector_Get(&includes, i);
}
-/* The file that is currently being read. */
+/* The makefile or the body of a .for loop that is currently being read. */
static IncludedFile *
CurFile(void)
{
return GetInclude(includes.len - 1);
}
+unsigned
+CurFile_CondMinDepth(void)
+{
+ return CurFile()->condMinDepth;
+}
+
static Buffer
-loadfile(const char *path, int fd)
+LoadFile(const char *path, int fd)
{
ssize_t n;
Buffer buf;
@@ -348,7 +372,7 @@ loadfile(const char *path, int fd)
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));
+ Error("%s: %s", path, strerror(errno));
exit(2); /* Not 1 so -q can distinguish error */
}
if (n == 0)
@@ -358,30 +382,53 @@ loadfile(const char *path, int fd)
}
assert(buf.len <= buf.cap);
- if (!Buf_EndsWith(&buf, '\n'))
+ if (buf.len > 0 && !Buf_EndsWith(&buf, '\n'))
Buf_AddByte(&buf, '\n');
return buf; /* may not be null-terminated */
}
+const char *
+GetParentStackTrace(void)
+{
+ static bool initialized;
+ static const char *parentStackTrace;
+
+ if (!initialized) {
+ const char *env = getenv("MAKE_STACK_TRACE");
+ parentStackTrace = env == NULL ? NULL
+ : env[0] == '\t' ? bmake_strdup(env)
+ : strcmp(env, "yes") == 0 ? bmake_strdup("")
+ : NULL;
+ initialized = true;
+ }
+ return parentStackTrace;
+}
+
/*
* 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)
+char *
+GetStackTrace(bool includingInnermost)
{
+ const char *parentStackTrace;
+ Buffer buffer, *buf = &buffer;
const IncludedFile *entries;
size_t i, n;
+ bool hasDetails;
- entries = GetInclude(0);
+ Buf_Init(buf);
+ hasDetails = EvalStack_Details(buf);
n = includes.len;
if (n == 0)
- return;
+ goto add_parent_stack_trace;
- if (!includingInnermost && entries[n - 1].forLoop == NULL)
+ entries = GetInclude(0);
+ if (!includingInnermost && !(hasDetails && n > 1)
+ && entries[n - 1].forLoop == NULL)
n--; /* already in the diagnostic */
for (i = n; i-- > 0;) {
@@ -389,19 +436,57 @@ PrintStackTrace(bool includingInnermost)
const char *fname = entry->name.str;
char dirbuf[MAXPATHLEN + 1];
- if (fname[0] != '/' && strcmp(fname, "(stdin)") != 0)
- fname = realpath(fname, dirbuf);
+ if (fname[0] != '/' && strcmp(fname, "(stdin)") != 0) {
+ const char *realPath = realpath(fname, dirbuf);
+ if (realPath != NULL)
+ fname = realPath;
+ }
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);
+ Buf_AddStr(buf, "\tin .for loop from ");
+ Buf_AddStr(buf, fname);
+ Buf_AddStr(buf, ":");
+ Buf_AddInt(buf, (int)entry->forHeadLineno);
+ Buf_AddStr(buf, " with ");
+ Buf_AddStr(buf, details);
+ Buf_AddStr(buf, "\n");
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);
+ } else {
+ Buf_AddStr(buf, "\tin ");
+ Buf_AddStr(buf, fname);
+ Buf_AddStr(buf, ":");
+ Buf_AddInt(buf, (int)entry->lineno);
+ Buf_AddStr(buf, "\n");
+ }
}
+
+add_parent_stack_trace:
+ parentStackTrace = GetParentStackTrace();
+ if ((makelevel > 0 && (n > 0 || !includingInnermost))
+ || parentStackTrace != NULL) {
+ Buf_AddStr(buf, "\tin ");
+ Buf_AddStr(buf, progname);
+ Buf_AddStr(buf, " in directory \"");
+ Buf_AddStr(buf, curdir);
+ Buf_AddStr(buf, "\"\n");
+ }
+
+ if (parentStackTrace != NULL)
+ Buf_AddStr(buf, parentStackTrace);
+
+ return Buf_DoneData(buf);
+}
+
+void
+PrintStackTrace(bool includingInnermost)
+{
+ char *stackTrace = GetStackTrace(includingInnermost);
+ fprintf(stderr, "%s", stackTrace);
+ fflush(stderr);
+ free(stackTrace);
}
/* Check if the current character is escaped on the current line. */
@@ -415,8 +500,8 @@ IsEscaped(const char *line, const char *p)
}
/*
- * Add the filename and lineno to the GNode so that we remember where it
- * was first defined.
+ * Remember the location (filename and lineno) where the last command was
+ * added or where the node was mentioned in a .depend file.
*/
static void
RememberLocation(GNode *gn)
@@ -452,13 +537,25 @@ FindKeyword(const char *str)
}
void
-PrintLocation(FILE *f, bool useVars, const char *fname, unsigned lineno)
+PrintLocation(FILE *f, bool useVars, const GNode *gn)
{
char dirbuf[MAXPATHLEN + 1];
FStr dir, base;
+ const char *fname;
+ unsigned lineno;
+
+ if (gn != NULL) {
+ fname = gn->fname;
+ lineno = gn->lineno;
+ } else if (includes.len > 0) {
+ IncludedFile *curFile = CurFile();
+ fname = curFile->name.str;
+ lineno = curFile->lineno;
+ } else
+ return;
if (!useVars || fname[0] == '/' || strcmp(fname, "(stdin)") == 0) {
- (void)fprintf(f, "\"%s\" line %u: ", fname, lineno);
+ (void)fprintf(f, "%s:%u: ", fname, lineno);
return;
}
@@ -472,22 +569,21 @@ PrintLocation(FILE *f, bool useVars, const char *fname, unsigned lineno)
if (base.str == NULL)
base.str = str_basename(fname);
- (void)fprintf(f, "\"%s/%s\" line %u: ", dir.str, base.str, lineno);
+ (void)fprintf(f, "%s/%s:%u: ", dir.str, base.str, lineno);
FStr_Done(&base);
FStr_Done(&dir);
}
-static void MAKE_ATTR_PRINTFLIKE(6, 0)
-ParseVErrorInternal(FILE *f, bool useVars, const char *fname, unsigned lineno,
+static void MAKE_ATTR_PRINTFLIKE(5, 0)
+ParseVErrorInternal(FILE *f, bool useVars, const GNode *gn,
ParseErrorLevel level, const char *fmt, va_list ap)
{
static bool fatal_warning_error_printed = false;
(void)fprintf(f, "%s: ", progname);
- if (fname != NULL)
- PrintLocation(f, useVars, fname, lineno);
+ PrintLocation(f, useVars, gn);
if (level == PARSE_WARNING)
(void)fprintf(f, "warning: ");
(void)vfprintf(f, fmt, ap);
@@ -504,31 +600,32 @@ ParseVErrorInternal(FILE *f, bool useVars, const char *fname, unsigned lineno,
parseErrors++;
}
- if (DEBUG(PARSE))
+ if (level == PARSE_FATAL || DEBUG(PARSE)
+ || (gn == NULL && includes.len == 0 /* see PrintLocation */))
PrintStackTrace(false);
}
-static void MAKE_ATTR_PRINTFLIKE(4, 5)
-ParseErrorInternal(const char *fname, unsigned lineno,
+static void MAKE_ATTR_PRINTFLIKE(3, 4)
+ParseErrorInternal(const GNode *gn,
ParseErrorLevel level, const char *fmt, ...)
{
va_list ap;
(void)fflush(stdout);
va_start(ap, fmt);
- ParseVErrorInternal(stderr, false, fname, lineno, level, fmt, ap);
+ ParseVErrorInternal(stderr, false, gn, level, fmt, ap);
va_end(ap);
if (opts.debug_file != stdout && opts.debug_file != stderr) {
va_start(ap, fmt);
- ParseVErrorInternal(opts.debug_file, false, fname, lineno,
+ ParseVErrorInternal(opts.debug_file, false, gn,
level, fmt, ap);
va_end(ap);
}
}
/*
- * Print a parse error message, including location information.
+ * Print a 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).
@@ -539,26 +636,15 @@ void
Parse_Error(ParseErrorLevel level, const char *fmt, ...)
{
va_list ap;
- const char *fname;
- unsigned lineno;
-
- if (includes.len == 0) {
- fname = NULL;
- lineno = 0;
- } else {
- IncludedFile *curFile = CurFile();
- fname = curFile->name.str;
- lineno = curFile->lineno;
- }
(void)fflush(stdout);
va_start(ap, fmt);
- ParseVErrorInternal(stderr, true, fname, lineno, level, fmt, ap);
+ ParseVErrorInternal(stderr, true, NULL, level, fmt, ap);
va_end(ap);
if (opts.debug_file != stdout && opts.debug_file != stderr) {
va_start(ap, fmt);
- ParseVErrorInternal(opts.debug_file, true, fname, lineno,
+ ParseVErrorInternal(opts.debug_file, true, NULL,
level, fmt, ap);
va_end(ap);
}
@@ -580,7 +666,7 @@ HandleMessage(ParseErrorLevel level, const char *levelName, const char *umsg)
return;
}
- (void)Var_Subst(umsg, SCOPE_CMDLINE, VARE_WANTRES, &xmsg);
+ xmsg = Var_Subst(umsg, SCOPE_CMDLINE, VARE_EVAL);
/* TODO: handle errors */
Parse_Error(level, "%s", xmsg);
@@ -594,8 +680,7 @@ HandleMessage(ParseErrorLevel level, const char *levelName, const char *umsg)
/*
* 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.
+ * versa.
*/
static void
LinkSource(GNode *pgn, GNode *cgn, bool isSpecial)
@@ -606,12 +691,15 @@ LinkSource(GNode *pgn, GNode *cgn, bool isSpecial)
Lst_Append(&pgn->children, cgn);
pgn->unmade++;
- /* Special targets like .END don't need any children. */
+ /*
+ * Special targets like .END do not need to be informed once the child
+ * target has been made.
+ */
if (!isSpecial)
Lst_Append(&cgn->parents, pgn);
if (DEBUG(PARSE)) {
- debug_printf("# LinkSource: added child %s - %s\n",
+ debug_printf("Target \"%s\" depends on \"%s\"\n",
pgn->name, cgn->name);
Targ_PrintNode(pgn, 0);
Targ_PrintNode(cgn, 0);
@@ -644,11 +732,10 @@ TryApplyDependencyOperator(GNode *gn, GNodeType op)
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.
+ * If the node was on the left-hand side of a '::' operator,
+ * create a new node 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
@@ -661,13 +748,13 @@ TryApplyDependencyOperator(GNode *gn, GNodeType 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;
+ gn->type |= op & (unsigned)~OP_OPMASK;
cohort = Targ_NewInternalNode(gn->name);
if (doing_depend)
RememberLocation(cohort);
/*
- * Make the cohort invisible as well to avoid duplicating it
+ * Make the cohort invisible 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.
@@ -680,13 +767,9 @@ TryApplyDependencyOperator(GNode *gn, GNodeType op)
cohort->centurion = gn;
gn->unmade_cohorts++;
snprintf(cohort->cohort_num, sizeof cohort->cohort_num, "#%d",
- (unsigned int)gn->unmade_cohorts % 1000000);
+ (unsigned)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;
+ gn->type |= op; /* preserve any previous flags */
}
return true;
@@ -703,22 +786,22 @@ ApplyDependencyOperator(GNodeType op)
}
/*
- * We add a .WAIT node in the dependency list. After any dynamic dependencies
+ * 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).
+ * Give each .WAIT node a unique name (mainly for diagnostics).
*/
static void
ApplyDependencySourceWait(bool isSpecial)
{
static unsigned wait_number = 0;
- char wait_src[16];
+ char name[6 + 10 + 1];
GNode *gn;
- snprintf(wait_src, sizeof wait_src, ".WAIT_%u", ++wait_number);
- gn = Targ_NewInternalNode(wait_src);
+ snprintf(name, sizeof name, ".WAIT_%u", ++wait_number);
+ gn = Targ_NewInternalNode(name);
if (doing_depend)
RememberLocation(gn);
gn->type = OP_WAIT | OP_PHONY | OP_DEPENDS | OP_NOTMAIN;
@@ -792,9 +875,7 @@ ApplyDependencySourceOrder(const char *src)
Targ_PrintNode(gn, 0);
}
}
- /*
- * The current source now becomes the predecessor for the next one.
- */
+ /* The current source now becomes the predecessor for the next one. */
order_pred = gn;
}
@@ -852,7 +933,8 @@ MaybeUpdateMainTarget(void)
for (ln = targets->first; ln != NULL; ln = ln->next) {
GNode *gn = ln->datum;
if (GNode_IsMainCandidate(gn)) {
- DEBUG1(MAKE, "Setting main node to \"%s\"\n", gn->name);
+ DEBUG1(MAKE, "Setting main node to \"%s\"\n",
+ gn->name);
mainNode = gn;
return;
}
@@ -860,14 +942,10 @@ MaybeUpdateMainTarget(void)
}
static void
-InvalidLineType(const char *line)
+InvalidLineType(const char *line, const char *unexpanded_line)
{
- if (strncmp(line, "<<<<<<", 6) == 0 ||
- strncmp(line, ">>>>>>", 6) == 0)
- Parse_Error(PARSE_FATAL,
- "Makefile appears to contain unresolved CVS/RCS/??? merge conflicts");
- else if (line[0] == '.') {
- const char *dirstart = line + 1;
+ if (unexpanded_line[0] == '.') {
+ const char *dirstart = unexpanded_line + 1;
const char *dirend;
cpp_skip_whitespace(&dirstart);
dirend = dirstart;
@@ -875,40 +953,33 @@ InvalidLineType(const char *line)
dirend++;
Parse_Error(PARSE_FATAL, "Unknown directive \"%.*s\"",
(int)(dirend - dirstart), dirstart);
- } else
- Parse_Error(PARSE_FATAL, "Invalid line type");
+ } else if (strcmp(line, unexpanded_line) == 0)
+ Parse_Error(PARSE_FATAL, "Invalid line \"%s\"", line);
+ else
+ Parse_Error(PARSE_FATAL,
+ "Invalid line \"%s\", expanded to \"%s\"",
+ unexpanded_line, line);
}
static void
ParseDependencyTargetWord(char **pp, const char *lstart)
{
- const char *cp = *pp;
+ const char *p = *pp;
- while (*cp != '\0') {
- if ((ch_isspace(*cp) || *cp == '!' || *cp == ':' ||
- *cp == '(') &&
- !IsEscaped(lstart, cp))
+ while (*p != '\0') {
+ if ((ch_isspace(*p) || *p == '!' || *p == ':' || *p == '(')
+ && !IsEscaped(lstart, p))
break;
- if (*cp == '$') {
- /*
- * Must be a dynamic source (would have been expanded
- * 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.
- */
- FStr val;
-
- (void)Var_Parse(&cp, SCOPE_CMDLINE,
- VARE_PARSE_ONLY, &val);
+ if (*p == '$') {
+ FStr val = Var_Parse(&p, SCOPE_CMDLINE, VARE_PARSE);
+ /* TODO: handle errors */
FStr_Done(&val);
} else
- cp++;
+ p++;
}
- *pp += cp - *pp;
+ *pp += p - *pp;
}
/*
@@ -927,6 +998,11 @@ HandleDependencyTargetSpecial(const char *targetName,
*inout_paths = Lst_New();
Lst_Append(*inout_paths, &dirSearchPath);
break;
+ case SP_SYSPATH:
+ if (*inout_paths == NULL)
+ *inout_paths = Lst_New();
+ Lst_Append(*inout_paths, sysIncPath);
+ break;
case SP_MAIN:
/*
* Allow targets from the command line to override the
@@ -987,7 +1063,7 @@ HandleDependencyTargetPath(const char *suffixName,
path = Suff_GetPath(suffixName);
if (path == NULL) {
Parse_Error(PARSE_FATAL,
- "Suffix '%s' not defined (yet)", suffixName);
+ "Suffix \"%s\" not defined (yet)", suffixName);
return false;
}
@@ -1037,27 +1113,34 @@ HandleDependencyTarget(const char *targetName,
}
static void
-HandleDependencyTargetMundane(char *targetName)
+HandleSingleDependencyTargetMundane(const char *name)
{
- StringList targetNames = LST_INIT;
+ GNode *gn = Suff_IsTransform(name)
+ ? Suff_AddTransform(name)
+ : Targ_GetNode(name);
+ if (doing_depend)
+ RememberLocation(gn);
+
+ Lst_Append(targets, gn);
+}
+static void
+HandleDependencyTargetMundane(const char *targetName)
+{
if (Dir_HasWildcards(targetName)) {
+ StringList targetNames = LST_INIT;
+
SearchPath *emptyPath = SearchPath_New();
SearchPath_Expand(emptyPath, targetName, &targetNames);
SearchPath_Free(emptyPath);
- } else
- Lst_Append(&targetNames, targetName);
- while (!Lst_IsEmpty(&targetNames)) {
- char *targName = Lst_Dequeue(&targetNames);
- GNode *gn = Suff_IsTransform(targName)
- ? Suff_AddTransform(targName)
- : Targ_GetNode(targName);
- if (doing_depend)
- RememberLocation(gn);
-
- Lst_Append(targets, gn);
- }
+ while (!Lst_IsEmpty(&targetNames)) {
+ char *targName = Lst_Dequeue(&targetNames);
+ HandleSingleDependencyTargetMundane(targName);
+ free(targName);
+ }
+ } else
+ HandleSingleDependencyTargetMundane(targetName);
}
static void
@@ -1073,8 +1156,12 @@ SkipExtraTargets(char **pp, const char *lstart)
warning = true;
p++;
}
- if (warning)
- Parse_Error(PARSE_WARNING, "Extra target ignored");
+ if (warning) {
+ const char *start = *pp;
+ cpp_skip_whitespace(&start);
+ Parse_Error(PARSE_WARNING, "Extra target \"%.*s\" ignored",
+ (int)(p - start), start);
+ }
*pp += p - *pp;
}
@@ -1113,22 +1200,94 @@ ParseDependencyOp(char **pp)
{
if (**pp == '!')
return (*pp)++, OP_FORCE;
- if ((*pp)[1] == ':')
+ if (**pp == ':' && (*pp)[1] == ':')
return *pp += 2, OP_DOUBLEDEP;
- else
+ else if (**pp == ':')
return (*pp)++, OP_DEPENDS;
+ else
+ return OP_NONE;
}
static void
-ClearPaths(SearchPathList *paths)
+ClearPaths(ParseSpecial special, SearchPathList *paths)
{
if (paths != NULL) {
SearchPathListNode *ln;
for (ln = paths->first; ln != NULL; ln = ln->next)
SearchPath_Clear(ln->datum);
}
+ if (special == SP_SYSPATH)
+ Dir_SetSYSPATH();
+ else
+ Dir_SetPATH();
+}
+
+static char *
+FindInDirOfIncludingFile(const char *file)
+{
+ char *fullname, *incdir, *slash, *newName;
+ int i;
- Dir_SetPATH();
+ fullname = NULL;
+ incdir = bmake_strdup(CurFile()->name.str);
+ 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);
+ return fullname;
+}
+
+static char *
+FindInQuotPath(const char *file)
+{
+ const char *suff;
+ SearchPath *suffPath;
+ char *fullname;
+
+ fullname = FindInDirOfIncludingFile(file);
+ if (fullname == NULL &&
+ (suff = strrchr(file, '.')) != NULL &&
+ (suffPath = Suff_GetPath(suff)) != NULL)
+ fullname = Dir_FindFile(file, suffPath);
+ if (fullname == NULL)
+ fullname = Dir_FindFile(file, parseIncPath);
+ if (fullname == NULL)
+ fullname = Dir_FindFile(file, &dirSearchPath);
+ return fullname;
+}
+
+static bool
+SkipGuarded(const char *fullname)
+{
+ Guard *guard = HashTable_FindValue(&guards, fullname);
+ if (guard != NULL && guard->kind == GK_VARIABLE
+ && GNode_ValueDirect(SCOPE_GLOBAL, guard->name) != NULL)
+ goto skip;
+ if (guard != NULL && guard->kind == GK_TARGET
+ && Targ_FindNode(guard->name) != NULL)
+ goto skip;
+ return false;
+
+skip:
+ DEBUG2(PARSE, "Skipping '%s' because '%s' is defined\n",
+ fullname, guard->name);
+ return true;
}
/*
@@ -1146,80 +1305,17 @@ IncludeFile(const char *file, bool isSystem, bool depinc, bool silent)
{
Buffer buf;
char *fullname; /* full pathname of file */
- char *newName;
- char *slash, *incdir;
int fd;
- int i;
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.
- */
-
- incdir = bmake_strdup(CurFile()->name.str);
- 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 (fullname == NULL && !isSystem)
+ fullname = FindInQuotPath(file);
- 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) {
- /*
- * Look for it on the system path
- */
SearchPath *path = Lst_IsEmpty(&sysIncPath->dirs)
? defSysIncPath : sysIncPath;
- fullname = Dir_FindFile(file, path);
+ fullname = Dir_FindInclude(file, path);
}
if (fullname == NULL) {
@@ -1228,21 +1324,22 @@ IncludeFile(const char *file, bool isSystem, bool depinc, bool silent)
return;
}
- /* Actually open the file... */
- fd = open(fullname, O_RDONLY);
- if (fd == -1) {
+ if (SkipGuarded(fullname))
+ goto done;
+
+ if ((fd = open(fullname, O_RDONLY)) == -1) {
if (!silent)
Parse_Error(PARSE_FATAL, "Cannot open %s", fullname);
- free(fullname);
- return;
+ goto done;
}
- buf = loadfile(fullname, fd);
+ buf = LoadFile(fullname, fd);
(void)close(fd);
Parse_PushInput(fullname, 1, 0, buf, NULL);
if (depinc)
doing_depend = depinc; /* only turn it on */
+done:
free(fullname);
}
@@ -1264,9 +1361,9 @@ HandleDependencySourcesEmpty(ParseSpecial special, SearchPathList *paths)
opts.silent = true;
break;
case SP_PATH:
- ClearPaths(paths);
+ case SP_SYSPATH:
+ ClearPaths(special, paths);
break;
-#ifdef POSIX
case SP_POSIX:
if (posix_state == PS_NOW_OR_NEVER) {
/*
@@ -1275,10 +1372,10 @@ HandleDependencySourcesEmpty(ParseSpecial special, SearchPathList *paths)
* otherwise it is an extension.
*/
Global_Set("%POSIX", "1003.2");
+ posix_state = PS_SET;
IncludeFile("posix.mk", true, false, true);
}
break;
-#endif
default:
break;
}
@@ -1308,6 +1405,7 @@ ParseDependencySourceSpecial(ParseSpecial special, const char *word,
Suff_AddSuffix(word);
break;
case SP_PATH:
+ case SP_SYSPATH:
AddToPaths(word, paths);
break;
case SP_INCLUDES:
@@ -1316,12 +1414,18 @@ ParseDependencySourceSpecial(ParseSpecial special, const char *word,
case SP_LIBS:
Suff_AddLib(word);
break;
+ case SP_NOREADONLY:
+ Var_ReadOnly(word, false);
+ break;
case SP_NULL:
Suff_SetNull(word);
break;
case SP_OBJDIR:
Main_SetObjdir(false, "%s", word);
break;
+ case SP_READONLY:
+ Var_ReadOnly(word, true);
+ break;
default:
break;
}
@@ -1332,7 +1436,7 @@ ApplyDependencyTarget(char *name, char *nameEnd, ParseSpecial *inout_special,
GNodeType *inout_targetAttr,
SearchPathList **inout_paths)
{
- char savec = *nameEnd;
+ char savedNameEnd = *nameEnd;
*nameEnd = '\0';
if (!HandleDependencyTarget(name, inout_special,
@@ -1342,33 +1446,35 @@ ApplyDependencyTarget(char *name, char *nameEnd, ParseSpecial *inout_special,
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);
+ Parse_Error(PARSE_WARNING, "Extra target \"%s\" ignored",
+ name);
- *nameEnd = savec;
+ *nameEnd = savedNameEnd;
return true;
}
static bool
-ParseDependencyTargets(char **inout_cp,
+ParseDependencyTargets(char **pp,
const char *lstart,
ParseSpecial *inout_special,
GNodeType *inout_targetAttr,
- SearchPathList **inout_paths)
+ SearchPathList **inout_paths,
+ const char *unexpanded_line)
{
- char *cp = *inout_cp;
+ char *p = *pp;
for (;;) {
- char *tgt = cp;
+ char *tgt = p;
- ParseDependencyTargetWord(&cp, lstart);
+ ParseDependencyTargetWord(&p, lstart);
/*
* If the word is followed by a left parenthesis, it's the
* name of one or more files inside an archive.
*/
- if (!IsEscaped(lstart, cp) && *cp == '(') {
- cp = tgt;
- if (!Arch_ParseArchive(&cp, targets, SCOPE_CMDLINE)) {
+ if (!IsEscaped(lstart, p) && *p == '(') {
+ p = tgt;
+ if (!Arch_ParseArchive(&p, targets, SCOPE_CMDLINE)) {
Parse_Error(PARSE_FATAL,
"Error in archive specification: \"%s\"",
tgt);
@@ -1377,27 +1483,27 @@ ParseDependencyTargets(char **inout_cp,
continue;
}
- if (*cp == '\0') {
- InvalidLineType(lstart);
+ if (*p == '\0') {
+ InvalidLineType(lstart, unexpanded_line);
return false;
}
- if (!ApplyDependencyTarget(tgt, cp, inout_special,
+ if (!ApplyDependencyTarget(tgt, p, inout_special,
inout_targetAttr, inout_paths))
return false;
if (*inout_special != SP_NOT && *inout_special != SP_PATH)
- SkipExtraTargets(&cp, lstart);
+ SkipExtraTargets(&p, lstart);
else
- pp_skip_whitespace(&cp);
+ pp_skip_whitespace(&p);
- if (*cp == '\0')
+ if (*p == '\0')
break;
- if ((*cp == '!' || *cp == ':') && !IsEscaped(lstart, cp))
+ if ((*p == '!' || *p == ':') && !IsEscaped(lstart, p))
break;
}
- *inout_cp = cp;
+ *pp = p;
return true;
}
@@ -1405,17 +1511,17 @@ static void
ParseDependencySourcesSpecial(char *start,
ParseSpecial special, SearchPathList *paths)
{
- char savec;
while (*start != '\0') {
+ char savedEnd;
char *end = start;
while (*end != '\0' && !ch_isspace(*end))
end++;
- savec = *end;
+ savedEnd = *end;
*end = '\0';
ParseDependencySourceSpecial(special, start, paths);
- *end = savec;
- if (savec != '\0')
+ *end = savedEnd;
+ if (savedEnd != '\0')
end++;
pp_skip_whitespace(&end);
start = end;
@@ -1444,17 +1550,13 @@ ParseDependencySourcesMundane(char *start,
* 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(
+ bool targetVarsEnabled = GetBooleanExpr(
"${.MAKE.TARGET_LOCAL_VARIABLES}", true);
- if (target_vars)
+ if (targetVarsEnabled)
LinkVarToTargets(&var);
free(var.varname);
- if (target_vars)
+ if (targetVarsEnabled)
return true;
}
@@ -1533,10 +1635,16 @@ ParseDependencySources(char *p, GNodeType targetAttr,
return;
}
- /* Now go for the sources. */
- if (special == SP_SUFFIXES || special == SP_PATH ||
- special == SP_INCLUDES || special == SP_LIBS ||
- special == SP_NULL || special == SP_OBJDIR) {
+ switch (special) {
+ case SP_INCLUDES:
+ case SP_LIBS:
+ case SP_NOREADONLY:
+ case SP_NULL:
+ case SP_OBJDIR:
+ case SP_PATH:
+ case SP_READONLY:
+ case SP_SUFFIXES:
+ case SP_SYSPATH:
ParseDependencySourcesSpecial(p, special, *inout_paths);
if (*inout_paths != NULL) {
Lst_Free(*inout_paths);
@@ -1544,10 +1652,14 @@ ParseDependencySources(char *p, GNodeType targetAttr,
}
if (special == SP_PATH)
Dir_SetPATH();
- } else {
+ if (special == SP_SYSPATH)
+ Dir_SetSYSPATH();
+ break;
+ default:
assert(*inout_paths == NULL);
if (!ParseDependencySourcesMundane(p, special, targetAttr))
return;
+ break;
}
MaybeUpdateMainTarget();
@@ -1574,10 +1686,10 @@ ParseDependencySources(char *p, GNodeType targetAttr,
* Transformation rules such as '.c.o' are also handled here, see
* Suff_AddTransform.
*
- * Upon return, the value of the line is unspecified.
+ * Upon return, the value of expandedLine is unspecified.
*/
static void
-ParseDependency(char *line)
+ParseDependency(char *expandedLine, const char *unexpandedLine)
{
char *p;
SearchPathList *paths; /* search paths to alter when parsing a list
@@ -1586,20 +1698,27 @@ ParseDependency(char *line)
ParseSpecial special; /* in special targets, the children are
* linked as children of the parent but not
* vice versa */
+ GNodeType op;
- DEBUG1(PARSE, "ParseDependency(%s)\n", line);
- p = line;
+ DEBUG1(PARSE, "ParseDependency(%s)\n", expandedLine);
+ p = expandedLine;
paths = NULL;
targetAttr = OP_NONE;
special = SP_NOT;
- if (!ParseDependencyTargets(&p, line, &special, &targetAttr, &paths))
+ if (!ParseDependencyTargets(&p, expandedLine, &special, &targetAttr,
+ &paths, unexpandedLine))
goto out;
if (!Lst_IsEmpty(targets))
CheckSpecialMundaneMixture(special);
- ApplyDependencyOperator(ParseDependencyOp(&p));
+ op = ParseDependencyOp(&p);
+ if (op == OP_NONE) {
+ InvalidLineType(expandedLine, unexpandedLine);
+ goto out;
+ }
+ ApplyDependencyOperator(op);
pp_skip_whitespace(&p);
@@ -1639,7 +1758,6 @@ AdjustVarassignOp(const char *name, const char *nameEnd, const char *op,
} else {
type = VAR_NORMAL;
-#ifdef SUNSHCMD
while (op > name && ch_isspace(op[-1]))
op--;
@@ -1647,7 +1765,6 @@ AdjustVarassignOp(const char *name, const char *nameEnd, const char *op,
op -= 3;
type = VAR_SHELL;
}
-#endif
}
va.varname = bmake_strsedup(name, nameEnd < op ? nameEnd : op);
@@ -1687,10 +1804,7 @@ Parse_IsVar(const char *p, VarAssign *out_var)
nameStart = p;
firstSpace = NULL;
- /*
- * Scan for one of the assignment operators outside a variable
- * expansion.
- */
+ /* Scan for one of the assignment operators outside an expression. */
while (*p != '\0') {
char ch = *p++;
if (ch == '(' || ch == '{') {
@@ -1712,12 +1826,10 @@ Parse_IsVar(const char *p, VarAssign *out_var)
if (ch == '\0')
return false;
-#ifdef SUNSHCMD
if (ch == ':' && p[0] == 's' && p[1] == 'h') {
p += 2;
continue;
}
-#endif
if (ch == '=')
eq = p - 1;
else if (*p == '=' &&
@@ -1742,16 +1854,14 @@ Parse_IsVar(const char *p, VarAssign *out_var)
* Check for syntax errors such as unclosed expressions or unknown modifiers.
*/
static void
-VarCheckSyntax(VarAssignOp type, const char *uvalue, GNode *scope)
+VarCheckSyntax(VarAssignOp op, const char *uvalue, GNode *scope)
{
if (opts.strict) {
- if (type != VAR_SUBST && strchr(uvalue, '$') != NULL) {
- char *expandedValue;
-
- (void)Var_Subst(uvalue, scope, VARE_PARSE_ONLY,
- &expandedValue);
+ if (op != VAR_SUBST && strchr(uvalue, '$') != NULL) {
+ char *parsedValue = Var_Subst(uvalue,
+ scope, VARE_PARSE);
/* TODO: handle errors */
- free(expandedValue);
+ free(parsedValue);
}
}
}
@@ -1764,7 +1874,7 @@ VarAssign_EvalSubst(GNode *scope, const char *name, const char *uvalue,
char *evalue;
/*
- * make sure that we set the variable the first time to nothing
+ * Make sure that we set the variable the first time to nothing
* so that it gets substituted.
*
* TODO: Add a test that demonstrates why this code is needed,
@@ -1775,7 +1885,8 @@ VarAssign_EvalSubst(GNode *scope, const char *name, const char *uvalue,
if (!Var_ExistsExpand(scope, name))
Var_SetExpand(scope, name, "");
- (void)Var_Subst(uvalue, scope, VARE_KEEP_DOLLAR_UNDEF, &evalue);
+ evalue = Var_Subst(uvalue, scope,
+ VARE_EVAL_KEEP_DOLLAR_AND_UNDEFINED);
/* TODO: handle errors */
Var_SetExpand(scope, name, evalue);
@@ -1792,7 +1903,7 @@ VarAssign_EvalShell(const char *name, const char *uvalue, GNode *scope,
char *output, *error;
cmd = FStr_InitRefer(uvalue);
- Var_Expand(&cmd, SCOPE_CMDLINE, VARE_UNDEFERR);
+ Var_Expand(&cmd, SCOPE_CMDLINE, VARE_EVAL);
output = Cmd_Exec(cmd.str, &error);
Var_SetExpand(scope, name, output);
@@ -1844,7 +1955,7 @@ VarAssign_Eval(const char *name, VarAssignOp op, const char *uvalue,
static void
VarAssignSpecial(const char *name, const char *avalue)
{
- if (strcmp(name, MAKEOVERRIDES) == 0)
+ if (strcmp(name, ".MAKEOVERRIDES") == 0)
Main_ExportMAKEFLAGS(false); /* re-export MAKEFLAGS */
else if (strcmp(name, ".CURDIR") == 0) {
/*
@@ -1854,9 +1965,9 @@ VarAssignSpecial(const char *name, const char *avalue)
*/
Dir_InitCur(avalue);
Dir_SetPATH();
- } else if (strcmp(name, MAKE_JOB_PREFIX) == 0)
+ } else if (strcmp(name, ".MAKE.JOB.PREFIX") == 0)
Job_SetPrefix();
- else if (strcmp(name, MAKE_EXPORTED) == 0)
+ else if (strcmp(name, ".MAKE.EXPORTED") == 0)
Var_ExportVars(avalue);
}
@@ -1875,7 +1986,7 @@ Parse_Var(VarAssign *var, GNode *scope)
/*
- * See if the command possibly calls a sub-make by using the variable
+ * See if the command possibly calls a sub-make by using the
* expressions ${.MAKE}, ${MAKE} or the plain word "make".
*/
static bool
@@ -1914,16 +2025,10 @@ MaybeSubMake(const char *cmd)
return false;
}
-/*
- * Append the command to the target node.
- *
- * The node may be marked as a submake node if the command is determined to
- * be that.
- */
+/* Append the command to the target node. */
static void
GNode_AddCommand(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;
@@ -1934,36 +2039,16 @@ GNode_AddCommand(GNode *gn, char *cmd)
gn->type |= OP_SUBMAKE;
RememberLocation(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: %u ignored",
- gn->name, gn->fname, gn->lineno);
-#else
Parse_Error(PARSE_WARNING,
"duplicate script for target \"%s\" ignored",
gn->name);
- ParseErrorInternal(gn->fname, gn->lineno, PARSE_WARNING,
+ ParseErrorInternal(gn, 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.
- */
-void
-Parse_AddIncludeDir(const char *dir)
-{
- (void)SearchPath_Add(parseIncPath, dir);
-}
-
-
-/*
* Parse a directive like '.include' or '.-include'.
*
* .include "user-makefile.mk"
@@ -1982,29 +2067,25 @@ ParseInclude(char *directive)
if (*p != '"' && *p != '<') {
Parse_Error(PARSE_FATAL,
- ".include filename must be delimited by '\"' or '<'");
+ ".include filename must be delimited by \"\" or <>");
return;
}
- if (*p++ == '<')
- endc = '>';
- else
- endc = '"';
+ endc = *p++ == '<' ? '>' : '"';
file = FStr_InitRefer(p);
- /* Skip to matching delimiter */
while (*p != '\0' && *p != endc)
p++;
if (*p != endc) {
Parse_Error(PARSE_FATAL,
- "Unclosed .include filename. '%c' expected", endc);
+ "Unclosed .include filename, \"%c\" expected", endc);
return;
}
*p = '\0';
- Var_Expand(&file, SCOPE_CMDLINE, VARE_WANTRES);
+ Var_Expand(&file, SCOPE_CMDLINE, VARE_EVAL);
IncludeFile(file.str, endc == '>', directive[0] == 'd', silent);
FStr_Done(&file);
}
@@ -2120,8 +2201,8 @@ VarContainsWord(const char *varname, const char *word)
static void
TrackInput(const char *name)
{
- if (!VarContainsWord(MAKE_MAKEFILES, name))
- Global_Append(MAKE_MAKEFILES, name);
+ if (!VarContainsWord(".MAKE.MAKEFILES", name))
+ Global_Append(".MAKE.MAKEFILES", name);
}
@@ -2137,8 +2218,8 @@ Parse_PushInput(const char *name, unsigned lineno, unsigned readLines,
else
TrackInput(name);
- DEBUG3(PARSE, "Parse_PushInput: %s %s, line %u\n",
- forLoop != NULL ? ".for loop in": "file", name, lineno);
+ DEBUG3(PARSE, "Parse_PushInput: %s%s:%u\n",
+ forLoop != NULL ? ".for loop in ": "", name, lineno);
curFile = Vector_Push(&includes);
curFile->name = FStr_InitOwn(bmake_strdup(name));
@@ -2148,6 +2229,8 @@ Parse_PushInput(const char *name, unsigned lineno, unsigned readLines,
curFile->forBodyReadLines = readLines;
curFile->buf = buf;
curFile->depending = doing_depend; /* restore this on EOF */
+ curFile->guardState = forLoop == NULL ? GS_START : GS_NO;
+ curFile->guard = NULL;
curFile->forLoop = forLoop;
if (forLoop != NULL && !For_NextIteration(forLoop, &curFile->buf))
@@ -2155,7 +2238,7 @@ Parse_PushInput(const char *name, unsigned lineno, unsigned readLines,
curFile->buf_ptr = curFile->buf.data;
curFile->buf_end = curFile->buf.data + curFile->buf.len;
- curFile->cond_depth = Cond_save_depth();
+ curFile->condMinDepth = cond_depth;
SetParseFile(name);
}
@@ -2174,7 +2257,6 @@ IsInclude(const char *dir, bool sysv)
}
-#ifdef SYSVINCLUDE
/* Check if the line is a SYSV include directive. */
static bool
IsSysVInclude(const char *line)
@@ -2202,7 +2284,7 @@ IsSysVInclude(const char *line)
static void
ParseTraditionalInclude(char *line)
{
- char *cp; /* current position in file spec */
+ char *p; /* current position in file spec */
bool done = false;
bool silent = line[0] != 'i';
char *file = line + (silent ? 8 : 7);
@@ -2212,16 +2294,16 @@ ParseTraditionalInclude(char *line)
pp_skip_whitespace(&file);
- (void)Var_Subst(file, SCOPE_CMDLINE, VARE_WANTRES, &all_files);
+ all_files = Var_Subst(file, SCOPE_CMDLINE, VARE_EVAL);
/* TODO: handle errors */
- for (file = all_files; !done; file = cp + 1) {
+ for (file = all_files; !done; file = p + 1) {
/* Skip to end of line or next whitespace */
- for (cp = file; *cp != '\0' && !ch_isspace(*cp); cp++)
+ for (p = file; *p != '\0' && !ch_isspace(*p); p++)
continue;
- if (*cp != '\0')
- *cp = '\0';
+ if (*p != '\0')
+ *p = '\0';
else
done = true;
@@ -2230,9 +2312,7 @@ ParseTraditionalInclude(char *line)
free(all_files);
}
-#endif
-#ifdef GMAKEEXPORT
/* Parse "export <variable>=<value>", and actually export it. */
static void
ParseGmakeExport(char *line)
@@ -2257,18 +2337,16 @@ ParseGmakeExport(char *line)
/*
* Expand the value before putting it in the environment.
*/
- (void)Var_Subst(value, SCOPE_CMDLINE, VARE_WANTRES, &value);
+ value = Var_Subst(value, SCOPE_CMDLINE, VARE_EVAL);
/* TODO: handle errors */
setenv(variable, value, 1);
free(value);
}
-#endif
/*
- * 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.
+ * When the end of the current file or .for loop is reached, continue reading
+ * the previous file at the previous location.
*
* Results:
* true to continue parsing, i.e. it had only reached the end of an
@@ -2288,11 +2366,20 @@ ParseEOF(void)
return true;
}
- /*
- * Ensure the makefile (or .for loop) didn't have mismatched
- * conditionals.
- */
- Cond_restore_depth(curFile->cond_depth);
+ Cond_EndFile();
+
+ if (curFile->guardState == GS_DONE) {
+ HashEntry *he = HashTable_CreateEntry(&guards,
+ curFile->name.str, NULL);
+ if (he->value != NULL) {
+ free(((Guard *)he->value)->name);
+ free(he->value);
+ }
+ HashEntry_Set(he, curFile->guard);
+ } else if (curFile->guard != NULL) {
+ free(curFile->guard->name);
+ free(curFile->guard);
+ }
FStr_Done(&curFile->name);
Buf_Done(&curFile->buf);
@@ -2310,7 +2397,7 @@ ParseEOF(void)
}
curFile = CurFile();
- DEBUG2(PARSE, "ParseEOF: returning to file %s, line %u\n",
+ DEBUG2(PARSE, "ParseEOF: returning to %s:%u\n",
curFile->name.str, curFile->readLines + 1);
SetParseFile(curFile->name.str);
@@ -2353,7 +2440,7 @@ ParseRawLine(IncludedFile *curFile, char **out_line, char **out_line_end,
ch = *p;
if (ch == '\0' || (ch == '\\' && p[1] == '\0')) {
Parse_Error(PARSE_FATAL, "Zero byte read from file");
- return PRLR_ERROR;
+ exit(2);
}
/* Treat next character after '\' as literal. */
@@ -2528,23 +2615,9 @@ SkipIrrelevantBranches(void)
{
const char *line;
- while ((line = ReadLowLevelLine(LK_DOT)) != NULL) {
+ 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.
- *
- * 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 false;
}
@@ -2583,9 +2656,9 @@ ParseForLoop(const char *line)
/*
* 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.
+ * Empty lines, .if and .for are handled by this function, while variable
+ * assignments, other directives, dependency lines and shell commands are
+ * handled by the caller.
*
* Return a line without trailing whitespace, or NULL for EOF. The returned
* string will be freed at the end of including the file.
@@ -2594,20 +2667,38 @@ static char *
ReadHighLevelLine(void)
{
char *line;
+ CondResult condResult;
for (;;) {
+ IncludedFile *curFile = CurFile();
line = ReadLowLevelLine(LK_NONEMPTY);
if (posix_state == PS_MAYBE_NEXT_LINE)
posix_state = PS_NOW_OR_NEVER;
- else
+ else if (posix_state != PS_SET)
posix_state = PS_TOO_LATE;
if (line == NULL)
return NULL;
+ DEBUG3(PARSE, "Parsing %s:%u: %s\n",
+ curFile->name.str, curFile->lineno, line);
+ if (curFile->guardState != GS_NO
+ && ((curFile->guardState == GS_START && line[0] != '.')
+ || curFile->guardState == GS_DONE))
+ curFile->guardState = GS_NO;
if (line[0] != '.')
return line;
- switch (Cond_EvalLine(line)) {
+ condResult = Cond_EvalLine(line);
+ if (curFile->guardState == GS_START) {
+ Guard *guard;
+ if (condResult != CR_ERROR
+ && (guard = Cond_ExtractGuard(line)) != NULL) {
+ curFile->guardState = GS_COND;
+ curFile->guard = guard;
+ } else
+ curFile->guardState = GS_NO;
+ }
+ switch (condResult) {
case CR_FALSE: /* May also mean a syntax error. */
if (!SkipIrrelevantBranches())
return NULL;
@@ -2649,6 +2740,13 @@ FinishDependencyGroup(void)
targets = NULL;
}
+#ifdef CLEANUP
+void Parse_RegisterCommand(char *cmd)
+{
+ Lst_Append(&targCmds, cmd);
+}
+#endif
+
/* Add the command to each target from the current dependency spec. */
static void
ParseLine_ShellCommand(const char *p)
@@ -2671,12 +2769,28 @@ ParseLine_ShellCommand(const char *p)
GNode *gn = ln->datum;
GNode_AddCommand(gn, cmd);
}
-#ifdef CLEANUP
- Lst_Append(&targCmds, cmd);
-#endif
+ Parse_RegisterCommand(cmd);
}
}
+static void
+HandleBreak(const char *arg)
+{
+ IncludedFile *curFile = CurFile();
+
+ if (arg[0] != '\0')
+ Parse_Error(PARSE_FATAL,
+ "The .break directive does not take arguments");
+
+ if (curFile->forLoop != NULL) {
+ /* pretend we reached EOF */
+ For_Break(curFile->forLoop);
+ cond_depth = CurFile_CondMinDepth();
+ ParseEOF();
+ } else
+ Parse_Error(PARSE_FATAL, "break outside of for loop");
+}
+
/*
* See if the line starts with one of the known directives, and if so, handle
* the directive.
@@ -2684,31 +2798,35 @@ ParseLine_ShellCommand(const char *p)
static bool
ParseDirective(char *line)
{
- char *cp = line + 1;
+ char *p = line + 1;
const char *arg;
Substring dir;
- pp_skip_whitespace(&cp);
- if (IsInclude(cp, false)) {
- ParseInclude(cp);
+ pp_skip_whitespace(&p);
+ if (IsInclude(p, false)) {
+ ParseInclude(p);
return true;
}
- dir.start = cp;
- while (ch_islower(*cp) || *cp == '-')
- cp++;
- dir.end = cp;
+ dir.start = p;
+ while (ch_islower(*p) || *p == '-')
+ p++;
+ dir.end = p;
- if (*cp != '\0' && !ch_isspace(*cp))
+ if (*p != '\0' && !ch_isspace(*p))
return false;
- pp_skip_whitespace(&cp);
- arg = cp;
+ pp_skip_whitespace(&p);
+ arg = p;
- if (Substring_Equals(dir, "undef"))
+ if (Substring_Equals(dir, "break"))
+ HandleBreak(arg);
+ else if (Substring_Equals(dir, "undef"))
Var_Undef(arg);
else if (Substring_Equals(dir, "export"))
Var_Export(VEM_PLAIN, arg);
+ else if (Substring_Equals(dir, "export-all"))
+ Var_Export(VEM_ALL, arg);
else if (Substring_Equals(dir, "export-env"))
Var_Export(VEM_ENV, arg);
else if (Substring_Equals(dir, "export-literal"))
@@ -2742,10 +2860,27 @@ Parse_VarAssign(const char *line, bool finishDependencyGroup, GNode *scope)
return true;
}
+void
+Parse_GuardElse(void)
+{
+ IncludedFile *curFile = CurFile();
+ if (cond_depth == curFile->condMinDepth + 1)
+ curFile->guardState = GS_NO;
+}
+
+void
+Parse_GuardEndif(void)
+{
+ IncludedFile *curFile = CurFile();
+ if (cond_depth == curFile->condMinDepth
+ && curFile->guardState == GS_COND)
+ curFile->guardState = GS_DONE;
+}
+
static char *
FindSemicolon(char *p)
{
- int level = 0;
+ int depth = 0;
for (; *p != '\0'; p++) {
if (*p == '\\' && p[1] != '\0') {
@@ -2754,31 +2889,21 @@ FindSemicolon(char *p)
}
if (*p == '$' && (p[1] == '(' || p[1] == '{'))
- level++;
- else if (level > 0 && (*p == ')' || *p == '}'))
- level--;
- else if (level == 0 && *p == ';')
+ depth++;
+ else if (depth > 0 && (*p == ')' || *p == '}'))
+ depth--;
+ else if (depth == 0 && *p == ';')
break;
}
return p;
}
-/*
- * dependency -> [target...] op [source...] [';' command]
- * op -> ':' | '::' | '!'
- */
static void
ParseDependencyLine(char *line)
{
- VarEvalMode emode;
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 skip over ';' inside substitution patterns.
- */
{
char *semicolon = FindSemicolon(line);
if (*semicolon != '\0') {
@@ -2788,41 +2913,7 @@ ParseDependencyLine(char *line)
}
}
- /*
- * 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.
- */
- emode = opts.strict ? VARE_WANTRES : VARE_UNDEFERR;
- (void)Var_Subst(line, SCOPE_CMDLINE, emode, &expanded_line);
+ expanded_line = Var_Subst(line, SCOPE_CMDLINE, VARE_EVAL);
/* TODO: handle errors */
/* Need a fresh list for the target nodes */
@@ -2830,7 +2921,7 @@ ParseDependencyLine(char *line)
Lst_Free(targets);
targets = Lst_New();
- ParseDependency(expanded_line);
+ ParseDependency(expanded_line, line);
free(expanded_line);
if (shellcmd != NULL)
@@ -2840,13 +2931,6 @@ ParseDependencyLine(char *line)
static void
ParseLine(char *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'.
- */
if (line[0] == '.' && ParseDirective(line))
return;
@@ -2855,26 +2939,16 @@ ParseLine(char *line)
return;
}
-#ifdef SYSVINCLUDE
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;
}
-#endif
if (Parse_VarAssign(line, true, SCOPE_GLOBAL))
return;
@@ -2884,17 +2958,14 @@ ParseLine(char *line)
ParseDependencyLine(line);
}
-/*
- * Parse a top-level makefile, incorporating its content into the global
- * dependency graph.
- */
+/* Interpret a top-level makefile. */
void
Parse_File(const char *name, int fd)
{
char *line;
Buffer buf;
- buf = loadfile(name, fd != -1 ? fd : STDIN_FILENO);
+ buf = LoadFile(name, fd != -1 ? fd : STDIN_FILENO);
if (fd != -1)
(void)close(fd);
@@ -2904,11 +2975,8 @@ Parse_File(const char *name, int fd)
do {
while ((line = ReadHighLevelLine()) != NULL) {
- DEBUG2(PARSE, "Parsing line %u: %s\n",
- CurFile()->lineno, line);
ParseLine(line);
}
- /* Reached EOF, but it may be just EOF of an include file. */
} while (ParseEOF());
FinishDependencyGroup();
@@ -2932,28 +3000,35 @@ Parse_Init(void)
sysIncPath = SearchPath_New();
defSysIncPath = SearchPath_New();
Vector_Init(&includes, sizeof(IncludedFile));
+ HashTable_Init(&guards);
}
+#ifdef CLEANUP
/* Clean up the parsing module. */
void
Parse_End(void)
{
-#ifdef CLEANUP
- Lst_DoneCall(&targCmds, free);
+ HashIter hi;
+
+ Lst_DoneFree(&targCmds);
assert(targets == NULL);
SearchPath_Free(defSysIncPath);
SearchPath_Free(sysIncPath);
SearchPath_Free(parseIncPath);
assert(includes.len == 0);
Vector_Done(&includes);
-#endif
+ HashIter_Init(&hi, &guards);
+ while (HashIter_Next(&hi)) {
+ Guard *guard = hi.entry->value;
+ free(guard->name);
+ free(guard);
+ }
+ HashTable_Done(&guards);
}
+#endif
-/*
- * Return a list containing the single main target to create.
- * If no such target exists, we Punt with an obnoxious error message.
- */
+/* Populate the list with the single main target to create, or error out. */
void
Parse_MainName(GNodeList *mainList)
{
@@ -2966,9 +3041,3 @@ Parse_MainName(GNodeList *mainList)
Global_Append(".TARGETS", mainNode->name);
}
-
-int
-Parse_NumErrors(void)
-{
- return parseErrors;
-}