diff options
author | bsiegert <bsiegert@pkgsrc.org> | 2011-06-18 22:17:41 +0000 |
---|---|---|
committer | bsiegert <bsiegert@pkgsrc.org> | 2011-06-18 22:17:41 +0000 |
commit | 7cb16bb66575467ac59352068eaec344242b439a (patch) | |
tree | c8f5dd7d540977ec601c02e7d04c13e80115f641 /devel/bmake/files/parse.c | |
parent | 92219014db9b676e01e78bbe73c3877f94f2a471 (diff) | |
download | pkgsrc-7cb16bb66575467ac59352068eaec344242b439a.tar.gz |
Import bmake-20110606. Many changes, among them:
- unit-tests/modts now works on MirBSD
- meta mode
- ApplyModifiers: when we parse a variable which is not the entire modifier
string, or not followed by ':', do not consider it as containing modifiers.
- when long modifiers fail to match, check sysV style.
- :hash - cheap 32bit hash of value
- :localtime, :gmtime - use value as format string for strftime.
- fix for use after free() in CondDoExists().
- boot-strap (TOOL_DIFF): aparently at least on linux distro
formats the output of 'type' differently - so eat any "()"
- correct sysV substitution handling of empty lhs and variable
- correct exists() check for dir with trailing /
- correct handling of modifiers for non-existant variables during evaluation
of conditionals.
- fix for incorrect .PARSEDIR when .OBJDIR is re-computed after makefiles
have been read.
- fix example of :? modifier in man page.
- sigcompat.c: convert to ansi so we can use higher warning levels.
- parse.c: SunOS 5.8 at least does not have MAP_FILE
- use mmap(2) if available, for reading makefiles
- to ensure unit-tests results match, need to control LC_ALL as well as LANG.
- if stale dependency is an IMPSRC, search via .PATH
- machine.sh: like os.sh, allow for uname -p producing useless drivel
- boot-strap: document configure knobs for meta and filemon.
Diffstat (limited to 'devel/bmake/files/parse.c')
-rw-r--r-- | devel/bmake/files/parse.c | 522 |
1 files changed, 382 insertions, 140 deletions
diff --git a/devel/bmake/files/parse.c b/devel/bmake/files/parse.c index 5e549f0c6c4..40a14cba1a6 100644 --- a/devel/bmake/files/parse.c +++ b/devel/bmake/files/parse.c @@ -1,4 +1,4 @@ -/* $NetBSD: parse.c,v 1.1.1.8 2010/09/07 14:12:02 joerg Exp $ */ +/* $NetBSD: parse.c,v 1.1.1.9 2011/06/18 22:18:06 bsiegert Exp $ */ /* * Copyright (c) 1988, 1989, 1990, 1993 @@ -69,14 +69,14 @@ */ #ifndef MAKE_NATIVE -static char rcsid[] = "$NetBSD: parse.c,v 1.1.1.8 2010/09/07 14:12:02 joerg Exp $"; +static char rcsid[] = "$NetBSD: parse.c,v 1.1.1.9 2011/06/18 22:18:06 bsiegert Exp $"; #else #include <sys/cdefs.h> #ifndef lint #if 0 static char sccsid[] = "@(#)parse.c 8.3 (Berkeley) 3/19/94"; #else -__RCSID("$NetBSD: parse.c,v 1.1.1.8 2010/09/07 14:12:02 joerg Exp $"); +__RCSID("$NetBSD: parse.c,v 1.1.1.9 2011/06/18 22:18:06 bsiegert Exp $"); #endif #endif /* not lint */ #endif @@ -123,12 +123,22 @@ __RCSID("$NetBSD: parse.c,v 1.1.1.8 2010/09/07 14:12:02 joerg Exp $"); * Parse_MainName Returns a Lst of the main target to create. */ +#include <sys/types.h> +#include <sys/stat.h> +#include <assert.h> #include <ctype.h> #include <errno.h> #include <fcntl.h> #include <stdarg.h> #include <stdio.h> +#ifndef MAP_FILE +#define MAP_FILE 0 +#endif +#ifndef MAP_COPY +#define MAP_COPY MAP_PRIVATE +#endif + #include "make.h" #include "hash.h" #include "dir.h" @@ -136,56 +146,47 @@ __RCSID("$NetBSD: parse.c,v 1.1.1.8 2010/09/07 14:12:02 joerg Exp $"); #include "buf.h" #include "pathnames.h" -/* - * These values are returned by ParseEOF to tell Parse_File whether to - * CONTINUE parsing, i.e. it had only reached the end of an include file, - * or if it's DONE. - */ -#define CONTINUE 1 -#define DONE 0 -static Lst targets; /* targets we're working on */ -#ifdef CLEANUP -static Lst targCmds; /* command lines for targets */ +#ifdef HAVE_MMAP +#include <sys/mman.h> + +#ifndef MAP_COPY +#define MAP_COPY MAP_PRIVATE #endif -static Boolean inLine; /* true if currently in a dependency - * line or its commands */ -static int fatals = 0; +#ifndef MAP_FILE +#define MAP_FILE 0 +#endif +#endif + +//////////////////////////////////////////////////////////// +// types and constants -static GNode *mainNode; /* The main target to create. This is the - * first target on the first dependency - * line in the first makefile */ +/* + * Structure for a file being read ("included file") + */ typedef struct IFile { const char *fname; /* name of file */ int lineno; /* current line number in file */ int first_lineno; /* line number of start of text */ - int fd; /* the open file */ int cond_depth; /* 'if' nesting when file opened */ char *P_str; /* point to base of string buffer */ char *P_ptr; /* point to next char of string buffer */ char *P_end; /* point to the end of string buffer */ - int P_buflen; /* current size of file buffer */ - char *(*nextbuf)(void *); /* Function to get more data */ + char *(*nextbuf)(void *, size_t *); /* Function to get more data */ void *nextbuf_arg; /* Opaque arg for nextbuf() */ + struct loadedfile *lf; /* loadedfile object, if any */ } IFile; -#define IFILE_BUFLEN 0x8000 -static IFile *curFile; - /* - * Definitions for handling #include specifications + * These values are returned by ParseEOF to tell Parse_File whether to + * CONTINUE parsing, i.e. it had only reached the end of an include file, + * or if it's DONE. */ +#define CONTINUE 1 +#define DONE 0 -static Lst includes; /* stack of IFiles generated by .includes */ -Lst parseIncPath; /* list of directories for "..." includes */ -Lst sysIncPath; /* list of directories for <...> includes */ -Lst defIncPath; /* default directories for <...> includes */ - -/*- - * specType contains the SPECial TYPE of the current target. It is - * Not if the target is unspecial. If it *is* special, however, the children - * are linked as children of the parent but not vice versa. This variable is - * set in ParseDoDependency +/* + * Tokens for target attributes */ typedef enum { Begin, /* .BEGIN */ @@ -196,10 +197,13 @@ typedef enum { Includes, /* .INCLUDES */ Interrupt, /* .INTERRUPT */ Libs, /* .LIBS */ + Meta, /* .META */ MFlags, /* .MFLAGS or .MAKEFLAGS */ Main, /* .MAIN and we don't have anything user-specified to * make */ NoExport, /* .NOEXPORT */ + NoMeta, /* .NOMETA */ + NoMetaCmp, /* .NOMETA_CMP */ NoPath, /* .NOPATH */ Not, /* Not special */ NotParallel, /* .NOTPARALLEL */ @@ -221,16 +225,74 @@ typedef enum { Attribute /* Generic attribute */ } ParseSpecial; +/* + * Other tokens + */ +#define LPAREN '(' +#define RPAREN ')' + + +//////////////////////////////////////////////////////////// +// result data + +/* + * The main target to create. This is the first target on the first + * dependency line in the first makefile. + */ +static GNode *mainNode; + +//////////////////////////////////////////////////////////// +// eval state + +/* targets we're working on */ +static Lst targets; + +#ifdef CLEANUP +/* command lines for targets */ +static Lst targCmds; +#endif + +/* + * specType contains the SPECial TYPE of the current target. It is + * Not if the target is unspecial. If it *is* special, however, the children + * are linked as children of the parent but not vice versa. This variable is + * set in ParseDoDependency + */ static ParseSpecial specType; -#define LPAREN '(' -#define RPAREN ')' /* * Predecessor node for handling .ORDER. Initialized to NULL when .ORDER * seen, then set to each successive source on the line. */ static GNode *predecessor; +//////////////////////////////////////////////////////////// +// parser state + +/* true if currently in a dependency line or its commands */ +static Boolean inLine; + +/* number of fatal errors */ +static int fatals = 0; + +/* + * Variables for doing includes + */ + +/* current file being read */ +static IFile *curFile; + +/* stack of IFiles generated by .includes */ +static Lst includes; + +/* include paths (lists of directories) */ +Lst parseIncPath; /* dirs for "..." includes */ +Lst sysIncPath; /* dirs for <...> includes */ +Lst defIncPath; /* 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 @@ -238,7 +300,7 @@ static GNode *predecessor; * the 'op' field is the operator to apply to the list of targets if the * keyword is used as a source ("0" if the keyword isn't special as a source) */ -static struct { +static const struct { const char *name; /* Name of keyword */ ParseSpecial spec; /* Type when used as a target */ int op; /* Operator when used as a source */ @@ -258,7 +320,10 @@ static struct { { ".MAIN", Main, 0 }, { ".MAKE", Attribute, OP_MAKE }, { ".MAKEFLAGS", MFlags, 0 }, +{ ".META", Meta, OP_META }, { ".MFLAGS", MFlags, 0 }, +{ ".NOMETA", NoMeta, OP_NOMETA }, +{ ".NOMETA_CMP", NoMetaCmp, OP_NOMETA_CMP }, { ".NOPATH", NoPath, OP_NOPATH }, { ".NOTMAIN", Attribute, OP_NOTMAIN }, { ".NOTPARALLEL", NotParallel, 0 }, @@ -284,6 +349,9 @@ static struct { { ".WAIT", Wait, 0 }, }; +//////////////////////////////////////////////////////////// +// local functions + static int ParseIsEscaped(const char *, const char *); static void ParseErrorInternal(const char *, size_t, int, const char *, ...) __attribute__((__format__(__printf__, 4, 5))); @@ -309,8 +377,216 @@ static char *ParseReadLine(void); static void ParseFinishLine(void); static void ParseMark(GNode *); -extern int maxJobs; +//////////////////////////////////////////////////////////// +// file loader +struct loadedfile { + const char *path; /* name, for error reports */ + char *buf; /* contents buffer */ + size_t len; /* length of contents */ + size_t maplen; /* length of mmap area, or 0 */ + Boolean used; /* XXX: have we used the data yet */ +}; + +/* + * Constructor/destructor for loadedfile + */ +static struct loadedfile * +loadedfile_create(const char *path) +{ + struct loadedfile *lf; + + lf = bmake_malloc(sizeof(*lf)); + lf->path = (path == NULL ? "(stdin)" : path); + lf->buf = NULL; + lf->len = 0; + lf->maplen = 0; + lf->used = FALSE; + return lf; +} + +static void +loadedfile_destroy(struct loadedfile *lf) +{ + if (lf->buf != NULL) { + if (lf->maplen > 0) { +#ifdef HAVE_MMAP + munmap(lf->buf, lf->maplen); +#endif + } else { + free(lf->buf); + } + } + free(lf); +} + +/* + * nextbuf() operation for loadedfile, as needed by the weird and twisted + * logic below. Once that's cleaned up, we can get rid of lf->used... + */ +static char * +loadedfile_nextbuf(void *x, size_t *len) +{ + struct loadedfile *lf = x; + + if (lf->used) { + return NULL; + } + lf->used = TRUE; + *len = lf->len; + return lf->buf; +} + +/* + * Try to get the size of a file. + */ +static ReturnStatus +load_getsize(int fd, size_t *ret) +{ + struct stat st; + + if (fstat(fd, &st) < 0) { + return FAILURE; + } + + if (!S_ISREG(st.st_mode)) { + return FAILURE; + } + + /* + * st_size is an off_t, which is 64 bits signed; *ret is + * size_t, which might be 32 bits unsigned or 64 bits + * unsigned. Rather than being elaborate, just punt on + * files that are more than 2^31 bytes. We should never + * see a makefile that size in practice... + * + * While we're at it reject negative sizes too, just in case. + */ + if (st.st_size < 0 || st.st_size > 0x7fffffff) { + return FAILURE; + } + + *ret = (size_t) st.st_size; + return SUCCESS; +} + +/* + * Read in a file. + * + * Until the path search logic can be moved under here instead of + * being in the caller in another source file, we need to have the fd + * passed in already open. Bleh. + * + * If the path is NULL use stdin and (to insure against fd leaks) + * assert that the caller passed in -1. + */ +static struct loadedfile * +loadfile(const char *path, int fd) +{ + struct loadedfile *lf; +#ifdef HAVE_MMAP + long pagesize; +#endif + ssize_t result; + size_t bufpos; + + lf = loadedfile_create(path); + + if (path == NULL) { + assert(fd == -1); + fd = STDIN_FILENO; + } else { +#if 0 /* notyet */ + fd = open(path, O_RDONLY); + if (fd < 0) { + ... + Error("%s: %s", path, strerror(errno)); + exit(1); + } +#endif + } + +#ifdef HAVE_MMAP + if (load_getsize(fd, &lf->len) == SUCCESS) { + /* found a size, try mmap */ + pagesize = sysconf(_SC_PAGESIZE); + if (pagesize <= 0) { + pagesize = 0x1000; + } + /* round size up to a page */ + lf->maplen = pagesize * ((lf->len + pagesize - 1)/pagesize); + + /* + * XXX hack for dealing with empty files; remove when + * we're no longer limited by interfacing to the old + * logic elsewhere in this file. + */ + if (lf->maplen == 0) { + lf->maplen = pagesize; + } + + /* + * FUTURE: remove PROT_WRITE when the parser no longer + * needs to scribble on the input. + */ + lf->buf = mmap(NULL, lf->maplen, PROT_READ|PROT_WRITE, + MAP_FILE|MAP_COPY, fd, 0); + if (lf->buf != MAP_FAILED) { + /* succeeded */ + if (lf->len == lf->maplen && lf->buf[lf->len - 1] != '\n') { + char *b = malloc(lf->len + 1); + b[lf->len] = '\n'; + memcpy(b, lf->buf, lf->len++); + munmap(lf->buf, lf->maplen); + lf->maplen = 0; + lf->buf = b; + } + goto done; + } + } +#endif + /* cannot mmap; load the traditional way */ + + lf->maplen = 0; + lf->len = 1024; + lf->buf = bmake_malloc(lf->len); + + bufpos = 0; + while (1) { + assert(bufpos <= lf->len); + if (bufpos == lf->len) { + lf->len *= 2; + lf->buf = bmake_realloc(lf->buf, lf->len); + } + result = read(fd, lf->buf + bufpos, lf->len - bufpos); + if (result < 0) { + Error("%s: read error: %s", path, strerror(errno)); + exit(1); + } + if (result == 0) { + break; + } + bufpos += result; + } + assert(bufpos <= lf->len); + lf->len = bufpos; + + /* truncate malloc region to actual length (maybe not useful) */ + if (lf->len > 0) { + lf->buf = bmake_realloc(lf->buf, lf->len); + } + +#ifdef HAVE_MMAP +done: +#endif + if (path != NULL) { + close(fd); + } + return lf; +} + +//////////////////////////////////////////////////////////// +// old code /*- *---------------------------------------------------------------------- @@ -404,7 +680,7 @@ ParseVErrorInternal(FILE *f, const char *cfname, size_t clineno, int type, const char *dir; /* - * Nothing is more anoying than not knowing + * Nothing is more annoying than not knowing * which Makefile is the culprit. */ dir = Var_Value(".PARSEDIR", VAR_GLOBAL, &cp); @@ -1786,6 +2062,7 @@ Parse_AddIncludeDir(char *dir) static void Parse_include_file(char *file, Boolean isSystem, int silent) { + struct loadedfile *lf; char *fullname; /* full pathname of file */ char *newName; char *prefEnd, *incdir; @@ -1876,8 +2153,12 @@ Parse_include_file(char *file, Boolean isSystem, int silent) return; } + /* load it */ + lf = loadfile(fullname, fd); + /* Start reading from this file next */ - Parse_SetInput(fullname, 0, fd, NULL, NULL); + Parse_SetInput(fullname, 0, -1, loadedfile_nextbuf, lf); + curFile->lf = lf; } static void @@ -1955,7 +2236,7 @@ ParseSetParseFile(const char *filename) slash = strrchr(filename, '/'); if (slash == NULL) { - Var_Set(".PARSEDIR", ".", VAR_GLOBAL, 0); + Var_Set(".PARSEDIR", curdir, VAR_GLOBAL, 0); Var_Set(".PARSEFILE", filename, VAR_GLOBAL, 0); } else { len = slash - filename; @@ -2014,9 +2295,11 @@ ParseTrackInput(const char *name) *--------------------------------------------------------------------- */ void -Parse_SetInput(const char *name, int line, int fd, char *(*nextbuf)(void *), void *arg) +Parse_SetInput(const char *name, int line, int fd, + char *(*nextbuf)(void *, size_t *), void *arg) { char *buf; + size_t len; if (name == NULL) name = curFile->fname; @@ -2047,33 +2330,22 @@ Parse_SetInput(const char *name, int line, int fd, char *(*nextbuf)(void *), voi curFile->fname = name; curFile->lineno = line; curFile->first_lineno = line; - curFile->fd = fd; curFile->nextbuf = nextbuf; curFile->nextbuf_arg = arg; + curFile->lf = NULL; - if (nextbuf == NULL) { - /* - * Allocate a 32k data buffer (as stdio seems to). - * Set pointers so that first ParseReadc has to do a file read. - */ - buf = bmake_malloc(IFILE_BUFLEN); - buf[0] = 0; - curFile->P_str = buf; - curFile->P_ptr = buf; - curFile->P_end = buf; - curFile->P_buflen = IFILE_BUFLEN; - } else { - /* Get first block of input data */ - buf = curFile->nextbuf(curFile->nextbuf_arg); - if (buf == NULL) { - /* Was all a waste of time ... */ - free(curFile); - return; - } - curFile->P_str = buf; - curFile->P_ptr = buf; - curFile->P_end = NULL; + assert(nextbuf != NULL); + + /* Get first block of input data */ + buf = curFile->nextbuf(curFile->nextbuf_arg, &len); + if (buf == NULL) { + /* Was all a waste of time ... */ + free(curFile); + return; } + curFile->P_str = buf; + curFile->P_ptr = buf; + curFile->P_end = buf+len; curFile->cond_depth = Cond_save_depth(); ParseSetParseFile(name); @@ -2162,26 +2434,31 @@ static int ParseEOF(void) { char *ptr; - - if (curFile->nextbuf != NULL) { - /* eg .for loop data, get next iteration */ - ptr = curFile->nextbuf(curFile->nextbuf_arg); - curFile->P_ptr = ptr; - curFile->P_str = ptr; - curFile->lineno = curFile->first_lineno; - if (ptr != NULL) { - /* Iterate again */ - return CONTINUE; - } + size_t len; + + assert(curFile->nextbuf != NULL); + + /* get next input buffer, if any */ + ptr = curFile->nextbuf(curFile->nextbuf_arg, &len); + curFile->P_ptr = ptr; + curFile->P_str = ptr; + curFile->P_end = ptr + len; + curFile->lineno = curFile->first_lineno; + if (ptr != NULL) { + /* Iterate again */ + return CONTINUE; } /* Ensure the makefile (or loop) didn't have mismatched conditionals */ Cond_restore_depth(curFile->cond_depth); + if (curFile->lf != NULL) { + loadedfile_destroy(curFile->lf); + curFile->lf = NULL; + } + /* Dispose of curFile info */ /* Leak curFile->fname because all the gnodes have pointers to it */ - if (curFile->fd != -1) - close(curFile->fd); free(curFile->P_str); free(curFile); @@ -2195,8 +2472,8 @@ ParseEOF(void) } if (DEBUG(PARSE)) - fprintf(debug_file, "ParseEOF: returning to file %s, line %d, fd %d\n", - curFile->fname, curFile->lineno, curFile->fd); + fprintf(debug_file, "ParseEOF: returning to file %s, line %d\n", + curFile->fname, curFile->lineno); /* Restore the PARSEDIR/PARSEFILE variables */ ParseSetParseFile(curFile->fname); @@ -2217,7 +2494,6 @@ ParseGetLine(int flags, int *length) char *escaped; char *comment; char *tp; - int len, dist; /* Loop through blank lines and comment lines */ for (;;) { @@ -2228,67 +2504,25 @@ ParseGetLine(int flags, int *length) escaped = NULL; comment = NULL; for (;;) { + if (cf->P_end != NULL && ptr == cf->P_end) { + /* end of buffer */ + ch = 0; + break; + } ch = *ptr; if (ch == 0 || (ch == '\\' && ptr[1] == 0)) { if (cf->P_end == NULL) /* End of string (aka for loop) data */ break; - /* End of data read from file, read more data */ - if (ptr != cf->P_end && (ch != '\\' || ptr + 1 != cf->P_end)) { - Parse_Error(PARSE_FATAL, "Zero byte read from file"); - return NULL; - } - /* Move existing data to (near) start of file buffer */ - len = cf->P_end - cf->P_ptr; - tp = cf->P_str + 32; - memmove(tp, cf->P_ptr, len); - dist = cf->P_ptr - tp; - /* Update all pointers to reflect moved data */ - ptr -= dist; - line -= dist; - line_end -= dist; - if (escaped) - escaped -= dist; - if (comment) - comment -= dist; - cf->P_ptr = tp; - tp += len; - cf->P_end = tp; - /* Try to read more data from file into buffer space */ - len = cf->P_str + cf->P_buflen - tp - 32; - if (len <= 0) { - /* We need a bigger buffer to hold this line */ - tp = bmake_realloc(cf->P_str, cf->P_buflen + IFILE_BUFLEN); - cf->P_ptr = cf->P_ptr - cf->P_str + tp; - cf->P_end = cf->P_end - cf->P_str + tp; - ptr = ptr - cf->P_str + tp; - line = line - cf->P_str + tp; - line_end = line_end - cf->P_str + tp; - if (escaped) - escaped = escaped - cf->P_str + tp; - if (comment) - comment = comment - cf->P_str + tp; - cf->P_str = tp; - tp = cf->P_end; - len += IFILE_BUFLEN; - cf->P_buflen += IFILE_BUFLEN; - } - len = read(cf->fd, tp, len); - if (len <= 0) { - if (len < 0) { - Parse_Error(PARSE_FATAL, "Makefile read error: %s", - strerror(errno)); - return NULL; - } - /* End of file */ + if (cf->nextbuf != NULL) { + /* + * End of this buffer; return EOF and outer logic + * will get the next one. (eww) + */ break; } - /* 0 terminate the data, and update end pointer */ - tp += len; - cf->P_end = tp; - *tp = 0; - /* Process newly read characters */ - continue; + Parse_Error(PARSE_FATAL, "Zero byte read from file"); + return NULL; } if (ch == '\\') { @@ -2459,7 +2693,7 @@ ParseReadLine(void) line = ParseGetLine(PARSE_RAW, &lineLength); if (line == NULL) { Parse_Error(PARSE_FATAL, - "Unexpected end of file in for loop.\n"); + "Unexpected end of file in for loop."); break; } } while (For_Accum(line)); @@ -2522,11 +2756,19 @@ Parse_File(const char *name, int fd) { char *cp; /* pointer into the line */ char *line; /* the line we're working on */ + struct loadedfile *lf; + + lf = loadfile(name, fd); inLine = FALSE; fatals = 0; - Parse_SetInput(name, 0, fd, NULL, NULL); + if (name == NULL) { + name = "(stdin)"; + } + + Parse_SetInput(name, 0, -1, loadedfile_nextbuf, lf); + curFile->lf = lf; do { for (; (line = ParseReadLine()) != NULL; ) { @@ -2719,7 +2961,7 @@ Parse_File(const char *name, int fd) if (fatals) { (void)fflush(stdout); (void)fprintf(stderr, - "%s: Fatal errors encountered -- cannot continue\n", + "%s: Fatal errors encountered -- cannot continue", progname); PrintOnError(NULL, NULL); exit(1); |