version 1.58, 2000/11/24 14:27:20 |
version 1.59, 2001/05/03 13:41:08 |
|
|
|
/* $OpenPackages$ */ |
/* $OpenBSD$ */ |
/* $OpenBSD$ */ |
/* $NetBSD: parse.c,v 1.29 1997/03/10 21:20:04 christos Exp $ */ |
/* $NetBSD: parse.c,v 1.29 1997/03/10 21:20:04 christos Exp $ */ |
|
|
/* |
/* |
|
* Copyright (c) 1999 Marc Espie. |
|
* |
|
* Extensive code changes for the OpenBSD project. |
|
* |
|
* Redistribution and use in source and binary forms, with or without |
|
* modification, are permitted provided that the following conditions |
|
* are met: |
|
* 1. Redistributions of source code must retain the above copyright |
|
* notice, this list of conditions and the following disclaimer. |
|
* 2. Redistributions in binary form must reproduce the above copyright |
|
* notice, this list of conditions and the following disclaimer in the |
|
* documentation and/or other materials provided with the distribution. |
|
* |
|
* THIS SOFTWARE IS PROVIDED BY THE OPENBSD PROJECT AND CONTRIBUTORS |
|
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OPENBSD |
|
* PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
*/ |
|
/* |
* Copyright (c) 1988, 1989, 1990, 1993 |
* Copyright (c) 1988, 1989, 1990, 1993 |
* The Regents of the University of California. All rights reserved. |
* The Regents of the University of California. All rights reserved. |
* Copyright (c) 1989 by Berkeley Softworks |
* Copyright (c) 1989 by Berkeley Softworks |
|
|
* messages can be more meaningful. |
* messages can be more meaningful. |
* |
* |
* Interface: |
* Interface: |
* Parse_Init Initialization function which must be |
* Parse_Init Initialization function which must be |
* called before anything else in this module |
* called before anything else in this module |
* is used. |
* is used. |
* |
* |
* Parse_End Cleanup the module |
* Parse_End Cleanup the module |
* |
* |
* Parse_File Function used to parse a makefile. It must |
* Parse_File Function used to parse a makefile. It must |
* be given the name of the file, which should |
* be given the name of the file, which should |
* already have been opened, and a function |
* already have been opened, and a function |
* to call to read a character from the file. |
* to call to read a character from the file. |
* |
* |
* Parse_IsVar Returns TRUE if the given line is a |
* Parse_IsVar Returns TRUE if the given line is a |
* variable assignment. Used by MainParseArgs |
* variable assignment. Used by MainParseArgs |
* to determine if an argument is a target |
* to determine if an argument is a target |
* or a variable assignment. Used internally |
* or a variable assignment. Used internally |
* for pretty much the same thing... |
* for pretty much the same thing... |
* |
* |
* Parse_Error Function called when an error occurs in |
* Parse_Error Function called when an error occurs in |
* parsing. Used by the variable and |
* parsing. Used by the variable and |
* conditional modules. |
* conditional modules. |
* Parse_MainName Returns a Lst of the main target to create. |
* Parse_MainName Returns a Lst of the main target to create. |
*/ |
*/ |
|
|
#ifdef __STDC__ |
#ifdef __STDC__ |
|
|
#else |
#else |
#include <varargs.h> |
#include <varargs.h> |
#endif |
#endif |
|
#include <assert.h> |
#include <stddef.h> |
#include <stddef.h> |
#include <stdio.h> |
#include <stdio.h> |
#include <ctype.h> |
#include <ctype.h> |
|
|
#endif |
#endif |
#endif /* not lint */ |
#endif /* not lint */ |
|
|
static LIST targets; /* targets we're working on */ |
LIST parseIncPath; /* list of directories for "..." includes */ |
|
LIST sysIncPath; /* list of directories for <...> includes */ |
|
|
|
static LIST targets; /* targets we're working on */ |
#ifdef CLEANUP |
#ifdef CLEANUP |
static LIST targCmds; /* command lines for targets */ |
static LIST targCmds; /* command lines for targets */ |
|
static LIST fileNames; |
#endif |
#endif |
static Boolean inLine; /* true if currently in a dependency |
|
* line or its commands */ |
|
static GNode *mainNode; /* The main target to create. This is the |
static GNode *mainNode; /* The main target to create. This is the |
* first target on the first dependency |
* first target on the first dependency |
* line in the first makefile */ |
* line in the first makefile */ |
LIST parseIncPath; /* list of directories for "..." includes */ |
|
LIST sysIncPath; /* list of directories for <...> includes */ |
|
|
|
/*- |
/*- |
* specType contains the SPECial TYPE of the current target. It is |
* specType contains the SPECial TYPE of the current target. It is |
* Not if the target is unspecial. If it *is* special, however, the children |
* Not if the target is unspecial. If it *is* special, however, the children |
|
|
* set in ParseDoDependency |
* set in ParseDoDependency |
*/ |
*/ |
typedef enum { |
typedef enum { |
Begin, /* .BEGIN */ |
Begin, /* .BEGIN */ |
Default, /* .DEFAULT */ |
Default, /* .DEFAULT */ |
End, /* .END */ |
End, /* .END */ |
Ignore, /* .IGNORE */ |
Ignore, /* .IGNORE */ |
Includes, /* .INCLUDES */ |
Includes, /* .INCLUDES */ |
Interrupt, /* .INTERRUPT */ |
Interrupt, /* .INTERRUPT */ |
|
|
NoPath, /* .NOPATH */ |
NoPath, /* .NOPATH */ |
Not, /* Not special */ |
Not, /* Not special */ |
NotParallel, /* .NOTPARALELL */ |
NotParallel, /* .NOTPARALELL */ |
Null, /* .NULL */ |
Null, /* .NULL */ |
Order, /* .ORDER */ |
Order, /* .ORDER */ |
Parallel, /* .PARALLEL */ |
Parallel, /* .PARALLEL */ |
ExPath, /* .PATH */ |
ExPath, /* .PATH */ |
Phony, /* .PHONY */ |
Phony, /* .PHONY */ |
|
|
* keyword is used as a source ("0" if the keyword isn't special as a source) |
* keyword is used as a source ("0" if the keyword isn't special as a source) |
*/ |
*/ |
static struct { |
static struct { |
char *name; /* Name of keyword */ |
char *name; /* Name of keyword */ |
ParseSpecial spec; /* Type when used as a target */ |
ParseSpecial spec; /* Type when used as a target */ |
int op; /* Operator when used as a source */ |
int op; /* Operator when used as a source */ |
} parseKeywords[] = { |
} parseKeywords[] = { |
{ ".BEGIN", Begin, 0 }, |
{ ".BEGIN", Begin, 0 }, |
{ ".DEFAULT", Default, 0 }, |
{ ".DEFAULT", Default, 0 }, |
{ ".END", End, 0 }, |
{ ".END", End, 0 }, |
{ ".EXEC", Attribute, OP_EXEC }, |
{ ".EXEC", Attribute, OP_EXEC }, |
{ ".IGNORE", Ignore, OP_IGNORE }, |
{ ".IGNORE", Ignore, OP_IGNORE }, |
{ ".INCLUDES", Includes, 0 }, |
{ ".INCLUDES", Includes, 0 }, |
{ ".INTERRUPT", Interrupt, 0 }, |
{ ".INTERRUPT", Interrupt, 0 }, |
{ ".INVISIBLE", Attribute, OP_INVISIBLE }, |
{ ".INVISIBLE", Attribute, OP_INVISIBLE }, |
{ ".JOIN", Attribute, OP_JOIN }, |
{ ".JOIN", Attribute, OP_JOIN }, |
{ ".LIBS", Libs, 0 }, |
{ ".LIBS", Libs, 0 }, |
{ ".MADE", Attribute, OP_MADE }, |
{ ".MADE", Attribute, OP_MADE }, |
{ ".MAIN", Main, 0 }, |
{ ".MAIN", Main, 0 }, |
{ ".MAKE", Attribute, OP_MAKE }, |
{ ".MAKE", Attribute, OP_MAKE }, |
{ ".MAKEFLAGS", MFlags, 0 }, |
{ ".MAKEFLAGS", MFlags, 0 }, |
{ ".MFLAGS", MFlags, 0 }, |
{ ".MFLAGS", MFlags, 0 }, |
#if 0 /* basic scaffolding for NOPATH, not working yet */ |
#if 0 /* basic scaffolding for NOPATH, not working yet */ |
{ ".NOPATH", NoPath, OP_NOPATH }, |
{ ".NOPATH", NoPath, OP_NOPATH }, |
#endif |
#endif |
{ ".NOTMAIN", Attribute, OP_NOTMAIN }, |
{ ".NOTMAIN", Attribute, OP_NOTMAIN }, |
{ ".NOTPARALLEL", NotParallel, 0 }, |
{ ".NOTPARALLEL", NotParallel, 0 }, |
{ ".NO_PARALLEL", NotParallel, 0 }, |
{ ".NO_PARALLEL", NotParallel, 0 }, |
{ ".NULL", Null, 0 }, |
{ ".NULL", Null, 0 }, |
{ ".OPTIONAL", Attribute, OP_OPTIONAL }, |
{ ".OPTIONAL", Attribute, OP_OPTIONAL }, |
{ ".ORDER", Order, 0 }, |
{ ".ORDER", Order, 0 }, |
{ ".PARALLEL", Parallel, 0 }, |
{ ".PARALLEL", Parallel, 0 }, |
{ ".PATH", ExPath, 0 }, |
{ ".PATH", ExPath, 0 }, |
{ ".PHONY", Phony, OP_PHONY }, |
{ ".PHONY", Phony, OP_PHONY }, |
{ ".PRECIOUS", Precious, OP_PRECIOUS }, |
{ ".PRECIOUS", Precious, OP_PRECIOUS }, |
{ ".RECURSIVE", Attribute, OP_MAKE }, |
{ ".RECURSIVE", Attribute, OP_MAKE }, |
{ ".SHELL", ExShell, 0 }, |
{ ".SHELL", ExShell, 0 }, |
{ ".SILENT", Silent, OP_SILENT }, |
{ ".SILENT", Silent, OP_SILENT }, |
{ ".SINGLESHELL", SingleShell, 0 }, |
{ ".SINGLESHELL", SingleShell, 0 }, |
{ ".SUFFIXES", Suffixes, 0 }, |
{ ".SUFFIXES", Suffixes, 0 }, |
{ ".USE", Attribute, OP_USE }, |
{ ".USE", Attribute, OP_USE }, |
{ ".WAIT", Wait, 0 }, |
{ ".WAIT", Wait, 0 }, |
}; |
}; |
|
|
static int ParseFindKeyword __P((char *)); |
static int ParseFindKeyword(const char *); |
static void ParseLinkSrc __P((void *, void *)); |
static void ParseLinkSrc(GNode *, GNode *); |
static int ParseDoOp __P((void *, void *)); |
static int ParseDoOp(void *, void *); |
static int ParseAddDep __P((void *, void *)); |
static int ParseAddDep(void *, void *); |
static void ParseDoSrc __P((int, char *, Lst)); |
static void ParseDoSrc(int, const char *, Lst); |
static int ParseFindMain __P((void *, void *)); |
static int ParseFindMain(void *, void *); |
static void ParseAddDir __P((void *, void *)); |
static void ParseAddDir(void *, void *); |
static void ParseClearPath __P((void *)); |
static void ParseClearPath(void *); |
static void ParseDoDependency __P((char *)); |
static void ParseDoDependency(char *); |
static void ParseAddCmd __P((void *, void *)); |
static void ParseAddCmd(void *, void *); |
static void ParseHasCommands __P((void *)); |
static void ParseHasCommands(void *); |
static void ParseDoInclude __P((char *)); |
static void ParseDoInclude(char *); |
#ifdef SYSVINCLUDE |
static void ParseTraditionalInclude(char *); |
static void ParseTraditionalInclude __P((char *)); |
static void ParseConditionalInclude(char *); |
#endif |
static void ParseLookupIncludeFile(char *, char *, Boolean, Boolean); |
static void ParseLookupIncludeFile __P((char *, char *, Boolean)); |
#define ParseGetLoopLine(linebuf) ParseGetLine(linebuf, "for loop") |
static void ParseFinishLine __P((void)); |
static void ParseFinishDependency(void); |
|
static Boolean ParseIsCond(Buffer, Buffer, char *); |
|
static char *strip_comments(Buffer, const char *); |
|
|
|
static void ParseDoCommands(const char *); |
|
static const char *find_op1(const char *); |
|
static const char *find_op2(const char *); |
|
|
/*- |
/*- |
*---------------------------------------------------------------------- |
*---------------------------------------------------------------------- |
* ParseFindKeyword -- |
* ParseFindKeyword -- |
|
|
* |
* |
* Results: |
* Results: |
* The index of the keyword, or -1 if it isn't there. |
* The index of the keyword, or -1 if it isn't there. |
* |
|
* Side Effects: |
|
* None |
|
*---------------------------------------------------------------------- |
*---------------------------------------------------------------------- |
*/ |
*/ |
static int |
static int |
ParseFindKeyword (str) |
ParseFindKeyword(str) |
char *str; /* String to find */ |
const char *str; /* String to find */ |
{ |
{ |
register int start, |
int start, |
end, |
end, |
cur; |
cur; |
register int diff; |
int diff; |
|
|
start = 0; |
start = 0; |
end = (sizeof(parseKeywords)/sizeof(parseKeywords[0])) - 1; |
end = (sizeof(parseKeywords)/sizeof(parseKeywords[0])) - 1; |
|
|
do { |
do { |
cur = start + ((end - start) / 2); |
cur = start + (end - start) / 2; |
diff = strcmp (str, parseKeywords[cur].name); |
diff = strcmp(str, parseKeywords[cur].name); |
|
|
if (diff == 0) { |
if (diff == 0) { |
return (cur); |
return cur; |
} else if (diff < 0) { |
} else if (diff < 0) { |
end = cur - 1; |
end = cur - 1; |
} else { |
} else { |
start = cur + 1; |
start = cur + 1; |
} |
} |
} while (start <= end); |
} while (start <= end); |
return (-1); |
return -1; |
} |
} |
|
|
/*- |
/*- |
*--------------------------------------------------------------------- |
*--------------------------------------------------------------------- |
* ParseLinkSrc -- |
* ParseLinkSrc -- |
* Link the parent node to its new child. Used in a Lst_ForEach by |
* Link the parent node to its new child. Used by |
* ParseDoDependency. If the specType isn't 'Not', the parent |
* ParseDoDependency. If the specType isn't 'Not', the parent |
* isn't linked as a parent of the child. |
* isn't linked as a parent of the child. |
* |
* |
|
|
*--------------------------------------------------------------------- |
*--------------------------------------------------------------------- |
*/ |
*/ |
static void |
static void |
ParseLinkSrc(pgnp, cgnp) |
ParseLinkSrc(pgn, cgn) |
void *pgnp; /* The parent node */ |
GNode *pgn; /* The parent node */ |
void *cgnp; /* The child node */ |
GNode *cgn; /* The child node */ |
{ |
{ |
GNode *pgn = (GNode *)pgnp; |
if (Lst_AddNew(&pgn->children, cgn) == SUCCESS) { |
GNode *cgn = (GNode *)cgnp; |
|
if (Lst_Member(&pgn->children, cgn) == NULL) { |
|
Lst_AtEnd(&pgn->children, cgn); |
|
if (specType == Not) |
if (specType == Not) |
Lst_AtEnd(&cgn->parents, pgn); |
Lst_AtEnd(&cgn->parents, pgn); |
pgn->unmade += 1; |
pgn->unmade++; |
} |
} |
} |
} |
|
|
|
|
* been found and their operator parsed. If the previous and new |
* been found and their operator parsed. If the previous and new |
* operators are incompatible, a major error is taken. |
* operators are incompatible, a major error is taken. |
* |
* |
* Results: |
|
* 0 if a problem, 1 if ok. |
|
* |
|
* Side Effects: |
* Side Effects: |
* The type field of the node is altered to reflect any new bits in |
* The type field of the node is altered to reflect any new bits in |
* the op. |
* the op. |
*--------------------------------------------------------------------- |
*--------------------------------------------------------------------- |
*/ |
*/ |
static int |
static int |
ParseDoOp (gnp, opp) |
ParseDoOp(gnp, opp) |
void *gnp; /* The node to which the operator is to be |
void *gnp; /* The node to which the operator is to be |
* applied */ |
* applied */ |
void *opp; /* The operator to apply */ |
void *opp; /* The operator to apply */ |
{ |
{ |
GNode *gn = (GNode *) gnp; |
GNode *gn = (GNode *)gnp; |
int op = *(int *) opp; |
int op = *(int *)opp; |
/* |
/* |
* If the dependency mask of the operator and the node don't match and |
* If the dependency mask of the operator and the node don't match and |
* the node has actually had an operator applied to it before, and |
* the node has actually had an operator applied to it before, and |
|
|
return 0; |
return 0; |
} |
} |
|
|
if ((op == OP_DOUBLEDEP) && ((gn->type & OP_OPMASK) == OP_DOUBLEDEP)) { |
if (op == OP_DOUBLEDEP && ((gn->type & OP_OPMASK) == OP_DOUBLEDEP)) { |
/* |
/* If the node was the object of a :: operator, we need to create a |
* If the node was the object of a :: operator, we need to create a |
|
* new instance of it for the children and commands on this dependency |
* new instance of it for the children and commands on this dependency |
* line. The new instance is placed on the 'cohorts' list of the |
* line. The new instance is placed on the 'cohorts' list of the |
* initial one (note the initial one is not on its own cohorts list) |
* initial one (note the initial one is not on its own cohorts list) |
* and the new instance is linked to all parents of the initial |
* and the new instance is linked to all parents of the initial |
* instance. |
* instance. */ |
*/ |
GNode *cohort; |
register GNode *cohort; |
LstNode ln; |
LstNode ln; |
|
|
|
cohort = Targ_NewGN(gn->name, NULL); |
cohort = Targ_NewGN(gn->name, NULL); |
/* |
/* Duplicate links to parents so graph traversal is simple. Perhaps |
* Duplicate links to parents so graph traversal is simple. Perhaps |
|
* some type bits should be duplicated? |
* some type bits should be duplicated? |
* |
* |
* Make the cohort invisible as well to avoid duplicating it into |
* Make the cohort invisible as well to avoid duplicating it into |
* other variables. True, parents of this target won't tend to do |
* other variables. True, parents of this target won't tend to do |
* anything with their local variables, but better safe than |
* anything with their local variables, but better safe than |
* sorry. |
* sorry. */ |
*/ |
for (ln = Lst_First(&gn->parents); ln != NULL; ln = Lst_Adv(ln)) |
Lst_ForEach(&gn->parents, ParseLinkSrc, cohort); |
ParseLinkSrc((GNode *)Lst_Datum(ln), cohort); |
cohort->type = OP_DOUBLEDEP|OP_INVISIBLE; |
cohort->type = OP_DOUBLEDEP|OP_INVISIBLE; |
Lst_AtEnd(&gn->cohorts, cohort); |
Lst_AtEnd(&gn->cohorts, cohort); |
|
|
/* |
/* Replace the node in the targets list with the new copy */ |
* Replace the node in the targets list with the new copy |
|
*/ |
|
ln = Lst_Member(&targets, gn); |
ln = Lst_Member(&targets, gn); |
Lst_Replace(ln, cohort); |
Lst_Replace(ln, cohort); |
gn = cohort; |
gn = cohort; |
} |
} |
/* |
/* We don't want to nuke any previous flags (whatever they were) so we |
* We don't want to nuke any previous flags (whatever they were) so we |
* just OR the new operator into the old. */ |
* just OR the new operator into the old |
|
*/ |
|
gn->type |= op; |
gn->type |= op; |
|
|
return 1; |
return 1; |
} |
} |
|
|
/*- |
/*- |
*--------------------------------------------------------------------- |
*--------------------------------------------------------------------- |
* ParseAddDep -- |
* ParseAddDep -- |
* Check if the pair of GNodes given needs to be synchronized. |
* Check if the pair of GNodes given needs to be synchronized. |
* This has to be when two nodes are on different sides of a |
* This has to be when two nodes are on different sides of a |
* .WAIT directive. |
* .WAIT directive. |
* |
* |
* Results: |
* Results: |
* Returns 0 if the two targets need to be ordered, 1 otherwise. |
* Returns 0 if the two targets need to be ordered, 1 otherwise. |
* If it returns 0, the search can stop |
* If it returns 0, the search can stop. |
* |
* |
* Side Effects: |
* Side Effects: |
* A dependency can be added between the two nodes. |
* A dependency can be added between the two nodes. |
|
|
void *pp; |
void *pp; |
void *sp; |
void *sp; |
{ |
{ |
GNode *p = (GNode *) pp; |
GNode *p = (GNode *)pp; |
GNode *s = (GNode *) sp; |
GNode *s = (GNode *)sp; |
|
|
if (p->order < s->order) { |
if (p->order < s->order) { |
/* |
/* XXX: This can cause loops, and loops can cause unmade targets, |
* XXX: This can cause loops, and loops can cause unmade targets, |
|
* but checking is tedious, and the debugging output can show the |
* but checking is tedious, and the debugging output can show the |
* problem |
* problem. */ |
*/ |
|
Lst_AtEnd(&p->successors, s); |
Lst_AtEnd(&p->successors, s); |
Lst_AtEnd(&s->preds, p); |
Lst_AtEnd(&s->preds, p); |
return 1; |
return 1; |
|
|
* of some special target and apply it if so. Otherwise, make the |
* of some special target and apply it if so. Otherwise, make the |
* source be a child of the targets in the list 'targets' |
* source be a child of the targets in the list 'targets' |
* |
* |
* Results: |
|
* None |
|
* |
|
* Side Effects: |
* Side Effects: |
* Operator bits may be added to the list of targets or to the source. |
* Operator bits may be added to the list of targets or to the source. |
* The targets may have a new source added to their lists of children. |
* The targets may have a new source added to their lists of children. |
*--------------------------------------------------------------------- |
*--------------------------------------------------------------------- |
*/ |
*/ |
static void |
static void |
ParseDoSrc (tOp, src, allsrc) |
ParseDoSrc(tOp, src, allsrc) |
int tOp; /* operator (if any) from special targets */ |
int tOp; /* operator (if any) from special targets */ |
char *src; /* name of the source to handle */ |
const char *src; /* name of the source to handle */ |
Lst allsrc; /* List of all sources to wait for */ |
Lst allsrc; /* List of all sources to wait for */ |
|
|
{ |
{ |
GNode *gn = NULL; |
GNode *gn = NULL; |
|
|
if (*src == '.' && isupper (src[1])) { |
if (*src == '.' && isupper(src[1])) { |
int keywd = ParseFindKeyword(src); |
int keywd = ParseFindKeyword(src); |
if (keywd != -1) { |
if (keywd != -1) { |
int op = parseKeywords[keywd].op; |
int op = parseKeywords[keywd].op; |
|
|
* Create proper predecessor/successor links between the previous |
* Create proper predecessor/successor links between the previous |
* source and the current one. |
* source and the current one. |
*/ |
*/ |
gn = Targ_FindNode(src, TARG_CREATE); |
gn = Targ_FindNode(src, NULL, TARG_CREATE); |
if (predecessor != NULL) { |
if (predecessor != NULL) { |
Lst_AtEnd(&predecessor->successors, gn); |
Lst_AtEnd(&predecessor->successors, gn); |
Lst_AtEnd(&gn->preds, predecessor); |
Lst_AtEnd(&gn->preds, predecessor); |
|
|
* the 'cohorts' list of the node) or all the cohorts are linked |
* the 'cohorts' list of the node) or all the cohorts are linked |
* to all the targets. |
* to all the targets. |
*/ |
*/ |
gn = Targ_FindNode (src, TARG_CREATE); |
gn = Targ_FindNode(src, NULL, TARG_CREATE); |
if (tOp) { |
if (tOp) { |
gn->type |= tOp; |
gn->type |= tOp; |
} else { |
} else { |
Lst_ForEach(&targets, ParseLinkSrc, gn); |
LstNode ln; |
|
|
|
for (ln = Lst_First(&targets); ln != NULL; ln = Lst_Adv(ln)) |
|
ParseLinkSrc((GNode *)Lst_Datum(ln), gn); |
} |
} |
if ((gn->type & OP_OPMASK) == OP_DOUBLEDEP) { |
if ((gn->type & OP_OPMASK) == OP_DOUBLEDEP) { |
register GNode *cohort; |
GNode *cohort; |
register LstNode ln; |
LstNode ln; |
|
|
for (ln=Lst_First(&gn->cohorts); ln != NULL; ln = Lst_Adv(ln)){ |
for (ln=Lst_First(&gn->cohorts); ln != NULL; ln = Lst_Adv(ln)){ |
cohort = (GNode *)Lst_Datum(ln); |
cohort = (GNode *)Lst_Datum(ln); |
if (tOp) { |
if (tOp) { |
cohort->type |= tOp; |
cohort->type |= tOp; |
} else { |
} else { |
Lst_ForEach(&targets, ParseLinkSrc, cohort); |
LstNode ln; |
|
|
|
for (ln = Lst_First(&targets); ln != NULL; ln = Lst_Adv(ln)) |
|
ParseLinkSrc((GNode *)Lst_Datum(ln), cohort); |
} |
} |
} |
} |
} |
} |
|
|
|
|
gn->order = waiting; |
gn->order = waiting; |
Lst_AtEnd(allsrc, gn); |
Lst_AtEnd(allsrc, gn); |
if (waiting) |
if (waiting) { |
Lst_Find(allsrc, ParseAddDep, gn); |
Lst_Find(allsrc, ParseAddDep, gn); |
|
} |
} |
} |
|
|
/*- |
/*- |
|
|
* |
* |
* Side Effects: |
* Side Effects: |
* mainNode is changed and Targ_SetMain is called. |
* mainNode is changed and Targ_SetMain is called. |
* |
|
*----------------------------------------------------------------------- |
*----------------------------------------------------------------------- |
*/ |
*/ |
static int |
static int |
ParseFindMain(gnp, dummy) |
ParseFindMain(gnp, dummy) |
void *gnp; /* Node to examine */ |
void *gnp; /* Node to examine */ |
void *dummy UNUSED; |
void *dummy UNUSED; |
{ |
{ |
GNode *gn = (GNode *) gnp; |
GNode *gn = (GNode *)gnp; |
if ((gn->type & OP_NOTARGET) == 0) { |
if ((gn->type & OP_NOTARGET) == 0) { |
mainNode = gn; |
mainNode = gn; |
Targ_SetMain(gn); |
Targ_SetMain(gn); |
return 0; |
return 0; |
} else |
} else { |
return 1; |
return 1; |
|
} |
} |
} |
|
|
/*- |
/*- |
|
|
* |
* |
* Side Effects: |
* Side Effects: |
* See Dir_AddDir. |
* See Dir_AddDir. |
* |
|
*----------------------------------------------------------------------- |
*----------------------------------------------------------------------- |
*/ |
*/ |
static void |
static void |
|
|
/*- |
/*- |
*----------------------------------------------------------------------- |
*----------------------------------------------------------------------- |
* ParseClearPath -- |
* ParseClearPath -- |
* Front-end for Dir_ClearPath to make sure Lst_ForEach keeps going |
* Reinit path to an empty path |
* |
|
* Side Effects: |
|
* See Dir_ClearPath |
|
* |
|
*----------------------------------------------------------------------- |
*----------------------------------------------------------------------- |
*/ |
*/ |
static void |
static void |
ParseClearPath(path) |
ParseClearPath(p) |
void *path; |
void *p; |
{ |
{ |
Dir_ClearPath((Lst)path); |
Lst path = (Lst)p; |
|
|
|
Lst_Destroy(path, Dir_Destroy); |
|
Lst_Init(path); |
} |
} |
|
|
/*- |
/*- |
|
|
* ParseDoDependency -- |
* ParseDoDependency -- |
* Parse the dependency line in line. |
* Parse the dependency line in line. |
* |
* |
* Results: |
|
* None |
|
* |
|
* Side Effects: |
* Side Effects: |
* The nodes of the sources are linked as children to the nodes of the |
* The nodes of the sources are linked as children to the nodes of the |
* targets. Some nodes may be created. |
* targets. Some nodes may be created. |
|
|
* until a character is encountered which is an operator character. Currently |
* until a character is encountered which is an operator character. Currently |
* these are only ! and :. At this point the operator is parsed and the |
* these are only ! and :. At this point the operator is parsed and the |
* pointer into the line advanced until the first source is encountered. |
* pointer into the line advanced until the first source is encountered. |
* The parsed operator is applied to each node in the 'targets' list, |
* The parsed operator is applied to each node in the 'targets' list, |
* which is where the nodes found for the targets are kept, by means of |
* which is where the nodes found for the targets are kept, by means of |
* the ParseDoOp function. |
* the ParseDoOp function. |
* The sources are read in much the same way as the targets were except |
* The sources are read in much the same way as the targets were except |
|
|
*--------------------------------------------------------------------- |
*--------------------------------------------------------------------- |
*/ |
*/ |
static void |
static void |
ParseDoDependency (line) |
ParseDoDependency(line) |
char *line; /* the line to parse */ |
char *line; /* the line to parse */ |
{ |
{ |
char *cp; /* our current position */ |
char *cp; /* our current position */ |
GNode *gn; /* a general purpose temporary node */ |
GNode *gn; /* a general purpose temporary node */ |
int op; /* the operator on the line */ |
int op; /* the operator on the line */ |
char savec; /* a place to save a character */ |
char savec; /* a place to save a character */ |
LIST paths; /* List of search paths to alter when parsing |
LIST paths; /* List of search paths to alter when parsing |
* a list of .PATH targets */ |
* a list of .PATH targets */ |
int tOp; /* operator from special target */ |
int tOp; /* operator from special target */ |
LIST curTargs; /* list of target names to be found and added |
LIST curTargs; /* list of target names to be found and added |
* to the targets list */ |
* to the targets list */ |
LIST curSrcs; /* list of sources in order */ |
LIST curSrcs; /* list of sources in order */ |
|
|
|
|
Lst_Init(&curSrcs); |
Lst_Init(&curSrcs); |
|
|
do { |
do { |
for (cp = line; |
for (cp = line; *cp && !isspace(*cp) && *cp != '(';) |
*cp && !isspace (*cp) && (*cp != '('); |
if (*cp == '$') |
cp++) |
/* Must be a dynamic source (would have been expanded |
{ |
|
/* |
|
* We don't want to end a word on ':' or '!' if there is a |
|
* better match later on in the string. By "better" I mean |
|
* one that is followed by whitespace. This allows the user |
|
* to have targets like: |
|
* fie::fi:fo: fum |
|
* where "fie::fi:fo" is the target. In real life this is used |
|
* for perl5 library man pages where "::" separates an object |
|
* from its class. Ie: "File::Spec::Unix". This behaviour |
|
* is also consistent with other versions of make. |
|
*/ |
|
if (*cp == '!' || *cp == ':') { |
|
char *p = cp + 1; |
|
|
|
if (*p == '\0') |
|
break; /* no chance, not enough room */ |
|
/* |
|
* Only end the word on ':' or '!' if there is not |
|
* a match later on followed by whitespace. |
|
*/ |
|
while ((p = strchr(p + 1, *cp)) && !isspace(*(p + 1))) |
|
; |
|
if (!p || !isspace(*(p + 1))) |
|
break; |
|
} else if (*cp == '$') { |
|
/* |
|
* Must be a dynamic source (would have been expanded |
|
* otherwise), so call the Var module to parse the puppy |
* otherwise), so call the Var module to parse the puppy |
* so we can safely advance beyond it...There should be |
* so we can safely advance beyond it...There should be |
* no errors in this, as they would have been discovered |
* no errors in this, as they would have been discovered |
* in the initial Var_Subst and we wouldn't be here. |
* in the initial Var_Subst and we wouldn't be here. */ |
*/ |
cp += Var_ParseSkip(cp, NULL, NULL); |
size_t length; |
else { |
Boolean freeIt; |
/* We don't want to end a word on ':' or '!' if there is a |
char *result; |
* better match later on in the string. By "better" I mean |
|
* one that is followed by whitespace. This allows the user |
|
* to have targets like: |
|
* fie::fi:fo: fum |
|
* where "fie::fi:fo" is the target. In real life this is used |
|
* for perl5 library man pages where "::" separates an object |
|
* from its class. Ie: "File::Spec::Unix". This behaviour |
|
* is also consistent with other versions of make. */ |
|
if (*cp == '!' || *cp == ':') { |
|
char *p = cp + 1; |
|
|
result=Var_Parse(cp, NULL, TRUE, &length, &freeIt); |
if (*cp == ':' && *p == ':') |
|
p++; |
|
|
if (freeIt) { |
/* Found the best match already. */ |
free(result); |
if (isspace(*p) || *p == '\0') |
|
break; |
|
|
|
do { |
|
p += strcspn(p, "!:"); |
|
if (*p == '\0') |
|
break; |
|
p++; |
|
} while (!isspace(*p)); |
|
|
|
/* No better match later on... */ |
|
if (*p == '\0') |
|
break; |
|
|
} |
} |
cp += length-1; |
cp++; |
} |
} |
continue; |
|
} |
|
if (*cp == '(') { |
if (*cp == '(') { |
/* |
/* Archives must be handled specially to make sure the OP_ARCHV |
* Archives must be handled specially to make sure the OP_ARCHV |
|
* flag is set in their 'type' field, for one thing, and because |
* flag is set in their 'type' field, for one thing, and because |
* things like "archive(file1.o file2.o file3.o)" are permissible. |
* things like "archive(file1.o file2.o file3.o)" are permissible. |
* Arch_ParseArchive will set 'line' to be the first non-blank |
* Arch_ParseArchive will set 'line' to be the first non-blank |
* after the archive-spec. It creates/finds nodes for the members |
* after the archive-spec. It creates/finds nodes for the members |
* and places them on the given list, returning SUCCESS if all |
* and places them on the given list, returning SUCCESS if all |
* went well and FAILURE if there was an error in the |
* went well and FAILURE if there was an error in the |
* specification. On error, line should remain untouched. |
* specification. On error, line should remain untouched. */ |
*/ |
|
if (Arch_ParseArchive(&line, &targets, NULL) != SUCCESS) { |
if (Arch_ParseArchive(&line, &targets, NULL) != SUCCESS) { |
Parse_Error (PARSE_FATAL, |
Parse_Error(PARSE_FATAL, |
"Error in archive specification: \"%s\"", line); |
"Error in archive specification: \"%s\"", line); |
return; |
return; |
} else { |
} else { |
|
|
} |
} |
savec = *cp; |
savec = *cp; |
|
|
if (!*cp) { |
if (*cp == '\0') { |
/* |
/* Ending a dependency line without an operator is a Bozo no-no */ |
* Ending a dependency line without an operator is a Bozo |
Parse_Error(PARSE_FATAL, "Need an operator"); |
* no-no |
|
*/ |
|
Parse_Error (PARSE_FATAL, "Need an operator"); |
|
return; |
return; |
} |
} |
*cp = '\0'; |
*cp = '\0'; |
/* |
/* Have a word in line. See if it's a special target and set |
* Have a word in line. See if it's a special target and set |
* specType to match it. */ |
* specType to match it. |
if (*line == '.' && isupper(line[1])) { |
*/ |
/* See if the target is a special target that must have it |
if (*line == '.' && isupper (line[1])) { |
* or its sources handled specially. */ |
/* |
|
* See if the target is a special target that must have it |
|
* or its sources handled specially. |
|
*/ |
|
int keywd = ParseFindKeyword(line); |
int keywd = ParseFindKeyword(line); |
if (keywd != -1) { |
if (keywd != -1) { |
if (specType == ExPath && parseKeywords[keywd].spec != ExPath) { |
if (specType == ExPath && parseKeywords[keywd].spec != ExPath) { |
|
|
* .MAIN Its sources are only used if |
* .MAIN Its sources are only used if |
* nothing has been specified to |
* nothing has been specified to |
* create. |
* create. |
* .DEFAULT Need to create a node to hang |
* .DEFAULT Need to create a node to hang |
* commands on, but we don't want |
* commands on, but we don't want |
* it in the graph, nor do we want |
* it in the graph, nor do we want |
* it to be the Main Target, so we |
* it to be the Main Target, so we |
|
|
* add it to the list, setting |
* add it to the list, setting |
* DEFAULT to the new node for |
* DEFAULT to the new node for |
* later use. We claim the node is |
* later use. We claim the node is |
* A transformation rule to make |
* A transformation rule to make |
* life easier later, when we'll |
* life easier later, when we'll |
* use Make_HandleUse to actually |
* use Make_HandleUse to actually |
* apply the .DEFAULT commands. |
* apply the .DEFAULT commands. |
* .PHONY The list of targets |
* .PHONY The list of targets |
* .NOPATH Don't search for file in the path |
* .NOPATH Don't search for file in the path |
* .BEGIN |
* .BEGIN |
* .END |
* .END |
* .INTERRUPT Are not to be considered the |
* .INTERRUPT Are not to be considered the |
* main target. |
* main target. |
* .NOTPARALLEL Make only one target at a time. |
* .NOTPARALLEL Make only one target at a time. |
* .SINGLESHELL Create a shell for each command. |
* .SINGLESHELL Create a shell for each command. |
* .ORDER Must set initial predecessor to NULL |
* .ORDER Must set initial predecessor to NULL |
*/ |
*/ |
switch (specType) { |
switch (specType) { |
case ExPath: |
case ExPath: |
|
|
case Begin: |
case Begin: |
case End: |
case End: |
case Interrupt: |
case Interrupt: |
gn = Targ_FindNode(line, TARG_CREATE); |
gn = Targ_FindNode(line, NULL, TARG_CREATE); |
gn->type |= OP_NOTMAIN; |
gn->type |= OP_NOTMAIN; |
Lst_AtEnd(&targets, gn); |
Lst_AtEnd(&targets, gn); |
break; |
break; |
case Default: |
case Default: |
gn = Targ_NewGN(".DEFAULT", NULL); |
gn = Targ_NewGN(".DEFAULT", NULL); |
gn->type |= (OP_NOTMAIN|OP_TRANSFORM); |
gn->type |= OP_NOTMAIN|OP_TRANSFORM; |
Lst_AtEnd(&targets, gn); |
Lst_AtEnd(&targets, gn); |
DEFAULT = gn; |
DEFAULT = gn; |
break; |
break; |
|
|
default: |
default: |
break; |
break; |
} |
} |
} else if (strncmp (line, ".PATH", 5) == 0) { |
} else if (strncmp(line, ".PATH", 5) == 0) { |
/* |
/* |
* .PATH<suffix> has to be handled specially. |
* .PATH<suffix> has to be handled specially. |
* Call on the suffix module to give us a path to |
* Call on the suffix module to give us a path to |
* modify. |
* modify. |
*/ |
*/ |
Lst path; |
Lst path; |
|
|
specType = ExPath; |
specType = ExPath; |
path = Suff_GetPath(&line[5]); |
path = Suff_GetPath(&line[5]); |
|
|
"Suffix '%s' not defined (yet)", |
"Suffix '%s' not defined (yet)", |
&line[5]); |
&line[5]); |
return; |
return; |
} else |
} else { |
Lst_AtEnd(&paths, path); |
Lst_AtEnd(&paths, path); |
|
} |
} |
} |
} |
} |
|
|
|
|
* Have word in line. Get or create its node and stick it at |
* Have word in line. Get or create its node and stick it at |
* the end of the targets list |
* the end of the targets list |
*/ |
*/ |
if ((specType == Not) && (*line != '\0')) { |
if (specType == Not && *line != '\0') { |
char *targName; |
char *targName; |
|
|
if (Dir_HasWildcards(line)) { |
if (Dir_HasWildcards(line)) { |
|
|
* Dir module could have added a directory to the path... |
* Dir module could have added a directory to the path... |
*/ |
*/ |
LIST emptyPath; |
LIST emptyPath; |
|
|
Lst_Init(&emptyPath); |
|
|
|
|
Lst_Init(&emptyPath); |
Dir_Expand(line, &emptyPath, &curTargs); |
Dir_Expand(line, &emptyPath, &curTargs); |
|
|
Lst_Destroy(&emptyPath, Dir_Destroy); |
Lst_Destroy(&emptyPath, Dir_Destroy); |
} else { |
} else { |
/* |
/* |
|
|
Lst_AtEnd(&curTargs, line); |
Lst_AtEnd(&curTargs, line); |
} |
} |
|
|
while((targName = (char *)Lst_DeQueue(&curTargs)) != NULL) { |
while ((targName = (char *)Lst_DeQueue(&curTargs)) != NULL) { |
if (!Suff_IsTransform (targName)) { |
if (!Suff_IsTransform(targName)) { |
gn = Targ_FindNode (targName, TARG_CREATE); |
gn = Targ_FindNode(targName, NULL, TARG_CREATE); |
} else { |
} else { |
gn = Suff_AddTransform (targName); |
gn = Suff_AddTransform(targName); |
} |
} |
|
|
if (gn != NULL) |
if (gn != NULL) |
|
|
if (specType != Not && specType != ExPath) { |
if (specType != Not && specType != ExPath) { |
Boolean warn = FALSE; |
Boolean warn = FALSE; |
|
|
while ((*cp != '!') && (*cp != ':') && *cp) { |
while (*cp != '!' && *cp != ':' && *cp) { |
if (*cp != ' ' && *cp != '\t') { |
if (*cp != ' ' && *cp != '\t') { |
warn = TRUE; |
warn = TRUE; |
} |
} |
|
|
Parse_Error(PARSE_WARNING, "Extra target ignored"); |
Parse_Error(PARSE_WARNING, "Extra target ignored"); |
} |
} |
} else { |
} else { |
while (*cp && isspace (*cp)) { |
while (*cp && isspace(*cp)) { |
cp++; |
cp++; |
} |
} |
} |
} |
line = cp; |
line = cp; |
} while ((*line != '!') && (*line != ':') && *line); |
} while (*line != '!' && *line != ':' && *line); |
|
|
/* Don't need the list of target names any more */ |
/* |
|
* Don't need the list of target names anymore... |
|
*/ |
Lst_Destroy(&curTargs, NOFREE); |
Lst_Destroy(&curTargs, NOFREE); |
|
|
if (!Lst_IsEmpty(&targets)) { |
if (!Lst_IsEmpty(&targets)) { |
switch(specType) { |
switch (specType) { |
default: |
default: |
Parse_Error(PARSE_WARNING, "Special and mundane targets don't mix. Mundane ones ignored"); |
Parse_Error(PARSE_WARNING, "Special and mundane targets don't mix. Mundane ones ignored"); |
break; |
break; |
|
|
case Begin: |
case Begin: |
case End: |
case End: |
case Interrupt: |
case Interrupt: |
/* |
/* These four create nodes on which to hang commands, so |
* These four create nodes on which to hang commands, so |
* targets shouldn't be empty... */ |
* targets shouldn't be empty... |
|
*/ |
|
case Not: |
case Not: |
/* |
/* Nothing special here -- targets can be empty if it wants. */ |
* Nothing special here -- targets can be empty if it wants. |
|
*/ |
|
break; |
break; |
} |
} |
} |
} |
|
|
/* |
/* Have now parsed all the target names. Must parse the operator next. The |
* Have now parsed all the target names. Must parse the operator next. The |
* result is left in op . */ |
* result is left in op . |
|
*/ |
|
if (*cp == '!') { |
if (*cp == '!') { |
op = OP_FORCE; |
op = OP_FORCE; |
} else if (*cp == ':') { |
} else if (*cp == ':') { |
|
|
op = OP_DEPENDS; |
op = OP_DEPENDS; |
} |
} |
} else { |
} else { |
Parse_Error (PARSE_FATAL, "Missing dependency operator"); |
Parse_Error(PARSE_FATAL, "Missing dependency operator"); |
return; |
return; |
} |
} |
|
|
|
|
/* |
/* |
* Get to the first source |
* Get to the first source |
*/ |
*/ |
while (*cp && isspace (*cp)) { |
while (*cp && isspace(*cp)) { |
cp++; |
cp++; |
} |
} |
line = cp; |
line = cp; |
|
|
if (!*line) { |
if (!*line) { |
switch (specType) { |
switch (specType) { |
case Suffixes: |
case Suffixes: |
Suff_ClearSuffixes (); |
Suff_ClearSuffixes(); |
break; |
break; |
case Precious: |
case Precious: |
allPrecious = TRUE; |
allPrecious = TRUE; |
|
|
* set the initial character to a null-character so the loop to |
* set the initial character to a null-character so the loop to |
* get sources won't get anything |
* get sources won't get anything |
*/ |
*/ |
Main_ParseArgLine (line); |
Main_ParseArgLine(line); |
*line = '\0'; |
*line = '\0'; |
} else if (specType == ExShell) { |
} else if (specType == ExShell) { |
if (Job_ParseShell (line) != SUCCESS) { |
if (Job_ParseShell(line) != SUCCESS) { |
Parse_Error (PARSE_FATAL, "improper shell specification"); |
Parse_Error(PARSE_FATAL, "improper shell specification"); |
return; |
return; |
} |
} |
*line = '\0'; |
*line = '\0'; |
} else if ((specType == NotParallel) || (specType == SingleShell)) { |
} else if (specType == NotParallel || specType == SingleShell) { |
*line = '\0'; |
*line = '\0'; |
} |
} |
|
|
/* |
/* |
* NOW GO FOR THE SOURCES |
* NOW GO FOR THE SOURCES |
*/ |
*/ |
if ((specType == Suffixes) || (specType == ExPath) || |
if (specType == Suffixes || specType == ExPath || |
(specType == Includes) || (specType == Libs) || |
specType == Includes || specType == Libs || |
(specType == Null)) |
specType == Null) { |
{ |
|
while (*line) { |
while (*line) { |
/* |
/* |
* If the target was one that doesn't take files as its sources |
* If the target was one that doesn't take files as its sources |
|
|
* has no valid suffix. |
* has no valid suffix. |
*/ |
*/ |
char savec; |
char savec; |
while (*cp && !isspace (*cp)) { |
while (*cp && !isspace(*cp)) { |
cp++; |
cp++; |
} |
} |
savec = *cp; |
savec = *cp; |
*cp = '\0'; |
*cp = '\0'; |
switch (specType) { |
switch (specType) { |
case Suffixes: |
case Suffixes: |
Suff_AddSuffix (line); |
Suff_AddSuffix(line); |
break; |
break; |
case ExPath: |
case ExPath: |
Lst_ForEach(&paths, ParseAddDir, line); |
Lst_ForEach(&paths, ParseAddDir, line); |
break; |
break; |
case Includes: |
case Includes: |
Suff_AddInclude (line); |
Suff_AddInclude(line); |
break; |
break; |
case Libs: |
case Libs: |
Suff_AddLib (line); |
Suff_AddLib(line); |
break; |
break; |
case Null: |
case Null: |
Suff_SetNull (line); |
Suff_SetNull(line); |
break; |
break; |
default: |
default: |
break; |
break; |
|
|
if (savec != '\0') { |
if (savec != '\0') { |
cp++; |
cp++; |
} |
} |
while (*cp && isspace (*cp)) { |
while (*cp && isspace(*cp)) { |
cp++; |
cp++; |
} |
} |
line = cp; |
line = cp; |
|
|
* specifications (i.e. things with left parentheses in them) |
* specifications (i.e. things with left parentheses in them) |
* and handle them accordingly. |
* and handle them accordingly. |
*/ |
*/ |
while (*cp && !isspace (*cp)) { |
while (*cp && !isspace(*cp)) { |
if ((*cp == '(') && (cp > line) && (cp[-1] != '$')) { |
if (*cp == '(' && cp > line && cp[-1] != '$') { |
/* |
/* |
* Only stop for a left parenthesis if it isn't at the |
* Only stop for a left parenthesis if it isn't at the |
* start of a word (that'll be for variable changes |
* start of a word (that'll be for variable changes |
|
|
|
|
if (*cp == '(') { |
if (*cp == '(') { |
GNode *gn; |
GNode *gn; |
LIST sources; /* list of archive source names after |
LIST sources; /* list of archive source names after |
* expansion */ |
* expansion */ |
|
|
Lst_Init(&sources); |
Lst_Init(&sources); |
if (Arch_ParseArchive(&line, &sources, NULL) != SUCCESS) { |
if (Arch_ParseArchive(&line, &sources, NULL) != SUCCESS) { |
Parse_Error (PARSE_FATAL, |
Parse_Error(PARSE_FATAL, |
"Error in source archive spec \"%s\"", line); |
"Error in source archive spec \"%s\"", line); |
return; |
return; |
} |
} |
|
|
while ((gn = (GNode *)Lst_DeQueue(&sources)) != NULL) |
while ((gn = (GNode *)Lst_DeQueue(&sources)) != NULL) |
ParseDoSrc(tOp, gn->name, &curSrcs); |
ParseDoSrc(tOp, gn->name, &curSrcs); |
Lst_Destroy(&sources, NOFREE); |
|
cp = line; |
cp = line; |
} else { |
} else { |
if (*cp) { |
if (*cp) { |
|
|
|
|
ParseDoSrc(tOp, line, &curSrcs); |
ParseDoSrc(tOp, line, &curSrcs); |
} |
} |
while (*cp && isspace (*cp)) { |
while (*cp && isspace(*cp)) { |
cp++; |
cp++; |
} |
} |
line = cp; |
line = cp; |
|
|
} |
} |
|
|
if (mainNode == NULL) { |
if (mainNode == NULL) { |
/* |
/* If we have yet to decide on a main target to make, in the |
* If we have yet to decide on a main target to make, in the |
|
* absence of any user input, we want the first target on |
* absence of any user input, we want the first target on |
* the first dependency line that is actually a real target |
* the first dependency line that is actually a real target |
* (i.e. isn't a .USE or .EXEC rule) to be made. |
* (i.e. isn't a .USE or .EXEC rule) to be made. */ |
*/ |
|
Lst_Find(&targets, ParseFindMain, NULL); |
Lst_Find(&targets, ParseFindMain, NULL); |
} |
} |
|
|
|
|
|
|
/*- |
/*- |
*--------------------------------------------------------------------- |
*--------------------------------------------------------------------- |
* Parse_IsVar -- |
* Parse_IsVar -- |
* Return TRUE if the passed line is a variable assignment. A variable |
* Return TRUE if the passed line is a variable assignment. A variable |
* assignment consists of a single word followed by optional whitespace |
* assignment consists of a single word followed by optional whitespace |
* followed by either a += or an = operator. |
* followed by either a += or an = operator. |
|
|
* |
* |
* Results: |
* Results: |
* TRUE if it is. FALSE if it ain't |
* TRUE if it is. FALSE if it ain't |
* |
|
* Side Effects: |
|
* none |
|
*--------------------------------------------------------------------- |
*--------------------------------------------------------------------- |
*/ |
*/ |
Boolean |
Boolean |
Parse_IsVar (line) |
Parse_IsVar(line) |
register char *line; /* the line to check */ |
char *line; /* the line to check */ |
{ |
{ |
register Boolean wasSpace = FALSE; /* set TRUE if found a space */ |
Boolean wasSpace = FALSE; /* set TRUE if found a space */ |
register Boolean haveName = FALSE; /* Set TRUE if have a variable name */ |
Boolean haveName = FALSE; /* Set TRUE if have a variable name */ |
int level = 0; |
int level = 0; |
#define ISEQOPERATOR(c) \ |
#define ISEQOPERATOR(c) \ |
(((c) == '+') || ((c) == ':') || ((c) == '?') || ((c) == '!')) |
((c) == '+' || (c) == ':' || (c) == '?' || (c) == '!') |
|
|
/* |
|
* Skip to variable name |
|
*/ |
|
for (;(*line == ' ') || (*line == '\t'); line++) |
|
continue; |
|
|
|
for (; *line != '=' || level != 0; line++) |
for (; *line != '=' || level != 0; line++) |
switch (*line) { |
switch (*line) { |
case '\0': |
case '\0': |
/* |
/* end-of-line -- can't be a variable assignment. */ |
* end-of-line -- can't be a variable assignment. |
|
*/ |
|
return FALSE; |
return FALSE; |
|
|
case ' ': |
case ' ': |
|
|
default: |
default: |
if (wasSpace && haveName) { |
if (wasSpace && haveName) { |
if (ISEQOPERATOR(*line)) { |
if (ISEQOPERATOR(*line)) { |
/* |
/* We must have a finished word. */ |
* We must have a finished word |
|
*/ |
|
if (level != 0) |
if (level != 0) |
return FALSE; |
return FALSE; |
|
|
/* |
/* When an = operator [+?!:] is found, the next |
* When an = operator [+?!:] is found, the next |
|
* character must be an = or it ain't a valid |
* character must be an = or it ain't a valid |
* assignment. |
* assignment. */ |
*/ |
|
if (line[1] == '=') |
if (line[1] == '=') |
return haveName; |
return haveName; |
#ifdef SUNSHCMD |
/* This is a shell command. */ |
/* |
if (FEATURES(FEATURE_SUNSHCMD) && |
* This is a shell command |
strncmp(line, ":sh", 3) == 0) |
*/ |
|
if (strncmp(line, ":sh", 3) == 0) |
|
return haveName; |
return haveName; |
#endif |
|
} |
} |
/* |
/* This is the start of another word, so not assignment. */ |
* This is the start of another word, so not assignment. |
|
*/ |
|
return FALSE; |
return FALSE; |
} |
} |
else { |
else { |
|
|
return haveName; |
return haveName; |
} |
} |
|
|
|
static const char * |
|
find_op1(p) |
|
const char *p; |
|
{ |
|
for(;; p++) { |
|
if (*p == '=' || isspace(*p) || *p == '$') |
|
break; |
|
if (p[1] == '=' && (*p == '?' || *p == ':' || *p == '!' || *p == '+')) |
|
break; |
|
if (p[0] == ':' && p[1] == 's' && p[2] == 'h') |
|
break; |
|
} |
|
return p; |
|
} |
|
|
|
static const char * |
|
find_op2(p) |
|
const char *p; |
|
{ |
|
for(;; p++) { |
|
if (*p == '=' || isspace(*p) || *p == '$') |
|
break; |
|
if (p[1] == '=' && (*p == '?' || *p == ':' || *p == '!' || *p == '+')) |
|
break; |
|
} |
|
return p; |
|
} |
|
|
/*- |
/*- |
*--------------------------------------------------------------------- |
*--------------------------------------------------------------------- |
* Parse_DoVar -- |
* Parse_DoVar -- |
* Take the variable assignment in the passed line and do it in the |
* Take the variable assignment in the passed line and do it in the |
* global context. |
* global context. |
* |
* |
|
|
* C++=/usr/bin/CC |
* C++=/usr/bin/CC |
* is interpreted as "C+ +=" instead of "C++ =". |
* is interpreted as "C+ +=" instead of "C++ =". |
* |
* |
* Results: |
|
* none |
|
* |
|
* Side Effects: |
* Side Effects: |
* the variable structure of the given variable name is altered in the |
* the variable structure of the given variable name is altered in the |
* global context. |
* global context. |
*--------------------------------------------------------------------- |
*--------------------------------------------------------------------- |
*/ |
*/ |
void |
void |
Parse_DoVar (line, ctxt) |
Parse_DoVar(line, ctxt) |
char *line; /* a line guaranteed to be a variable |
const char *line; /* a line guaranteed to be a variable |
* assignment. This reduces error checks */ |
* assignment. This reduces error checks */ |
GSymT *ctxt; /* Context in which to do the assignment */ |
GSymT *ctxt; /* Context in which to do the assignment */ |
{ |
{ |
char *cp; /* pointer into line */ |
const char *end; |
|
const char *arg; |
enum { |
enum { |
VAR_SUBST, VAR_APPEND, VAR_SHELL, VAR_NORMAL |
VAR_SUBST, VAR_APPEND, VAR_SHELL, VAR_NORMAL |
} type; /* Type of assignment */ |
} type; /* Type of assignment */ |
char *opc; /* ptr to operator character to |
struct Name name; |
* null-terminate the variable name */ |
|
/* |
|
* Avoid clobbered variable warnings by forcing the compiler |
|
* to ``unregister'' variables |
|
*/ |
|
#if __GNUC__ |
|
(void) &cp; |
|
(void) &line; |
|
#endif |
|
|
|
/* |
end = Var_Name_Get(line, &name, (SymTable *)ctxt, TRUE, |
* Skip to variable name |
FEATURES(FEATURE_SUNSHCMD) ? find_op1 : find_op2); |
*/ |
|
while ((*line == ' ') || (*line == '\t')) { |
|
line++; |
|
} |
|
|
|
/* |
while (isspace(*end)) |
* Skip to operator character, nulling out whitespace as we go |
end++; |
*/ |
|
for (cp = line + 1; *cp != '='; cp++) { |
|
if (isspace (*cp)) { |
|
*cp = '\0'; |
|
} |
|
} |
|
opc = cp-1; /* operator is the previous character */ |
|
*cp++ = '\0'; /* nuke the = */ |
|
|
|
/* |
/* Check operator type. */ |
* Check operator type |
switch (*end) { |
*/ |
|
switch (*opc) { |
|
case '+': |
case '+': |
type = VAR_APPEND; |
type = VAR_APPEND; |
*opc = '\0'; |
|
break; |
break; |
|
|
case '?': |
case '?': |
/* |
/* If the variable already has a value, we don't do anything. */ |
* If the variable already has a value, we don't do anything. |
if (Var_Value_interval(name.s, name.e) != NULL) { |
*/ |
Var_Name_Free(&name); |
*opc = '\0'; |
|
if (Var_Exists(line, ctxt)) { |
|
return; |
return; |
} else { |
|
type = VAR_NORMAL; |
|
} |
} |
|
type = VAR_NORMAL; |
break; |
break; |
|
|
case ':': |
case ':': |
type = VAR_SUBST; |
type = VAR_SUBST; |
*opc = '\0'; |
|
break; |
break; |
|
|
case '!': |
case '!': |
type = VAR_SHELL; |
type = VAR_SHELL; |
*opc = '\0'; |
|
break; |
break; |
|
|
default: |
default: |
#ifdef SUNSHCMD |
if (FEATURES(FEATURE_SUNSHCMD) && strncmp(end, ":sh", 3) == 0) |
while (*opc != ':') |
|
if (--opc < line) |
|
break; |
|
|
|
if (strncmp(opc, ":sh", 3) == 0) { |
|
type = VAR_SHELL; |
type = VAR_SHELL; |
*opc = '\0'; |
else |
break; |
type = VAR_NORMAL; |
} |
|
#endif |
|
type = VAR_NORMAL; |
|
break; |
break; |
} |
} |
|
|
while (isspace (*cp)) { |
/* Find operator itself and go over it. */ |
cp++; |
arg = end; |
} |
while (*arg != '=') |
|
arg++; |
|
arg++; |
|
while (isspace(*arg)) |
|
arg++; |
|
|
if (type == VAR_APPEND) { |
if (type == VAR_APPEND) |
Var_Append (line, cp, ctxt); |
Var_Append_interval(name.s, name.e, arg, ctxt); |
} else if (type == VAR_SUBST) { |
else if (type == VAR_SUBST) { |
|
char *sub; |
/* |
/* |
* Allow variables in the old value to be undefined, but leave their |
* Allow variables in the old value to be undefined, but leave their |
* invocation alone -- this is done by forcing oldVars to be false. |
* invocation alone -- this is done by forcing oldVars to be false. |
|
|
* |
* |
* And not get an error. |
* And not get an error. |
*/ |
*/ |
Boolean oldOldVars = oldVars; |
Boolean oldOldVars = oldVars; |
|
|
oldVars = FALSE; |
oldVars = FALSE; |
cp = Var_Subst(cp, (SymTable *)ctxt, FALSE); |
/* ensure the variable is set to something to avoid `variable |
|
* is recursive' errors. */ |
|
if (Var_Value_interval(name.s, name.e) == NULL) |
|
Var_Set_interval(name.s, name.e, "", ctxt); |
|
|
|
sub = Var_Subst(arg, (SymTable *)ctxt, FALSE); |
oldVars = oldOldVars; |
oldVars = oldOldVars; |
|
|
Var_Set(line, cp, ctxt); |
Var_Set_interval(name.s, name.e, sub, ctxt); |
free(cp); |
free(sub); |
} else if (type == VAR_SHELL) { |
} else if (type == VAR_SHELL) { |
Boolean freeCmd = FALSE; /* TRUE if the command needs to be freed, i.e. |
|
* if any variable expansion was performed */ |
|
char *res, *err; |
char *res, *err; |
|
|
if (strchr(cp, '$') != NULL) { |
if (strchr(arg, '$') != NULL) { |
/* |
char *sub; |
* There's a dollar sign in the command, so perform variable |
/* There's a dollar sign in the command, so perform variable |
* expansion on the whole thing. The resulting string will need |
* expansion on the whole thing. */ |
* freeing when we're done, so set freeCmd to TRUE. |
sub = Var_Subst(arg, NULL, TRUE); |
*/ |
res = Cmd_Exec(sub, &err); |
cp = Var_Subst(cp, NULL, TRUE); |
free(sub); |
freeCmd = TRUE; |
} else |
} |
res = Cmd_Exec(arg, &err); |
|
|
res = Cmd_Exec(cp, &err); |
Var_Set_interval(name.s, name.e, res, ctxt); |
Var_Set(line, res, ctxt); |
|
free(res); |
free(res); |
|
|
if (err) |
if (err) |
Parse_Error(PARSE_WARNING, err, cp); |
Parse_Error(PARSE_WARNING, err, arg); |
|
|
if (freeCmd) |
} else |
free(cp); |
/* Normal assignment -- just do it. */ |
} else { |
Var_Set_interval(name.s, name.e, arg, ctxt); |
/* |
Var_Name_Free(&name); |
* Normal assignment -- just do it. |
|
*/ |
|
Var_Set(line, cp, ctxt); |
|
} |
|
} |
} |
|
|
|
|
/*- |
/*- |
* ParseAddCmd -- |
* ParseAddCmd -- |
* Lst_ForEach function to add a command line to all targets |
* Lst_ForEach function to add a command line to all targets |
* |
* |
* Side Effects: |
* Side Effects: |
|
|
* having commands if it does, to keep from having shell commands |
* having commands if it does, to keep from having shell commands |
* on multiple dependency lines. |
* on multiple dependency lines. |
* |
* |
* Results: |
|
* None |
|
* |
|
* Side Effects: |
* Side Effects: |
* OP_HAS_COMMANDS may be set for the target. |
* OP_HAS_COMMANDS may be set for the target. |
* |
|
*----------------------------------------------------------------------- |
*----------------------------------------------------------------------- |
*/ |
*/ |
static void |
static void |
ParseHasCommands(gnp) |
ParseHasCommands(gnp) |
void *gnp; /* Node to examine */ |
void *gnp; /* Node to examine */ |
{ |
{ |
GNode *gn = (GNode *) gnp; |
GNode *gn = (GNode *)gnp; |
if (!Lst_IsEmpty(&gn->commands)) { |
if (!Lst_IsEmpty(&gn->commands)) { |
gn->type |= OP_HAS_COMMANDS; |
gn->type |= OP_HAS_COMMANDS; |
} |
} |
|
|
* Parse_AddIncludeDir -- |
* Parse_AddIncludeDir -- |
* Add a directory to the path searched for included makefiles |
* Add a directory to the path searched for included makefiles |
* bracketed by double-quotes. Used by functions in main.c |
* bracketed by double-quotes. Used by functions in main.c |
* |
|
* Results: |
|
* None. |
|
* |
|
* Side Effects: |
|
* The directory is appended to the list. |
|
* |
|
*----------------------------------------------------------------------- |
*----------------------------------------------------------------------- |
*/ |
*/ |
void |
void |
Parse_AddIncludeDir(dir) |
Parse_AddIncludeDir(dir) |
char *dir; /* The name of the directory to add */ |
const char *dir; /* The name of the directory to add */ |
{ |
{ |
Dir_AddDir(&parseIncPath, dir, NULL); |
Dir_AddDir(&parseIncPath, dir, NULL); |
} |
} |
|
|
* options |
* options |
* |
* |
* Side Effects: |
* Side Effects: |
* old parse context is pushed on the stack, new file becomes |
* old parse context is pushed on the stack, new file becomes |
* current context. |
* current context. |
*--------------------------------------------------------------------- |
*--------------------------------------------------------------------- |
*/ |
*/ |
static void |
static void |
ParseDoInclude(file) |
ParseDoInclude(file) |
char *file; /* file specification */ |
char *file; /* file specification */ |
{ |
{ |
char endc; /* the character which ends the file spec */ |
char endc; /* the character which ends the file spec */ |
char *cp; /* current position in file spec */ |
char *cp; /* current position in file spec */ |
Boolean isSystem; /* TRUE if makefile is a system makefile */ |
Boolean isSystem; /* TRUE if makefile is a system makefile */ |
|
|
/* Skip to delimiter character so we know where to look */ |
/* Skip to delimiter character so we know where to look. */ |
while (*file == ' ' || *file == '\t') |
while (*file == ' ' || *file == '\t') |
file++; |
file++; |
|
|
if (*file != '"' && *file != '<') { |
if (*file != '"' && *file != '<') { |
Parse_Error (PARSE_FATAL, |
Parse_Error(PARSE_FATAL, |
".include filename must be delimited by '\"' or '<'"); |
".include filename must be delimited by '\"' or '<'"); |
return; |
return; |
} |
} |
|
|
endc = '"'; |
endc = '"'; |
} |
} |
|
|
/* Skip to matching delimiter */ |
/* Skip to matching delimiter. */ |
for (cp = ++file; *cp && *cp != endc; cp++) { |
for (cp = ++file; *cp != endc; cp++) { |
if (*cp == '\0') { |
if (*cp == '\0') { |
Parse_Error(PARSE_FATAL, |
Parse_Error(PARSE_FATAL, |
"Unclosed %cinclude filename. '%c' expected", |
"Unclosed %cinclude filename. '%c' expected", |
'.', endc); |
'.', endc); |
return; |
return; |
} |
} |
} |
} |
ParseLookupIncludeFile(file, cp, isSystem); |
ParseLookupIncludeFile(file, cp, isSystem, TRUE); |
} |
} |
|
|
#ifdef SYSVINCLUDE |
|
/*- |
/*- |
*--------------------------------------------------------------------- |
*--------------------------------------------------------------------- |
* ParseTraditionalInclude -- |
* ParseTraditionalInclude -- |
|
|
* the string following the "include". |
* the string following the "include". |
* |
* |
* Side Effects: |
* Side Effects: |
* old parse context is pushed on the stack, new file becomes |
* old parse context is pushed on the stack, new file becomes |
* current context. |
* current context. |
|
* |
|
* XXX May wish to support multiple files and wildcards ? |
*--------------------------------------------------------------------- |
*--------------------------------------------------------------------- |
*/ |
*/ |
static void |
static void |
ParseTraditionalInclude(file) |
ParseTraditionalInclude(file) |
char *file; /* file specification */ |
char *file; /* file specification */ |
{ |
{ |
char *cp; /* current position in file spec */ |
char *cp; /* current position in file spec */ |
|
|
/* Skip over whitespace */ |
/* Skip over whitespace. */ |
while (*file == ' ' || *file == '\t') |
while (isspace(*file)) |
file++; |
file++; |
|
|
if (*file == '\0') { |
if (*file == '\0') { |
Parse_Error (PARSE_FATAL, |
Parse_Error(PARSE_FATAL, |
"Filename missing from \"include\""); |
"Filename missing from \"include\""); |
return; |
return; |
} |
} |
|
/* Skip to end of line or next whitespace. */ |
|
for (cp = file; *cp != '\0' && !isspace(*cp);) |
|
cp++; |
|
|
/* Skip to end of line or next whitespace */ |
ParseLookupIncludeFile(file, cp, TRUE, TRUE); |
|
} |
|
|
|
/*- |
|
*--------------------------------------------------------------------- |
|
* ParseConditionalInclude -- |
|
* May push to another file. |
|
* |
|
* No error if the file does not exist. |
|
* See ParseTraditionalInclude otherwise. |
|
*--------------------------------------------------------------------- |
|
*/ |
|
static void |
|
ParseConditionalInclude(file) |
|
char *file; /* file specification */ |
|
{ |
|
char *cp; /* current position in file spec */ |
|
|
|
/* Skip over whitespace. */ |
|
while (isspace(*file)) |
|
file++; |
|
if (*file == '\0') { |
|
Parse_Error(PARSE_FATAL, |
|
"Filename missing from \"include\""); |
|
return; |
|
} |
|
/* Skip to end of line or next whitespace. */ |
for (cp = file; *cp != '\0' && !isspace(*cp);) |
for (cp = file; *cp != '\0' && !isspace(*cp);) |
cp++; |
cp++; |
|
|
ParseLookupIncludeFile(file, cp, TRUE); |
ParseLookupIncludeFile(file, cp, TRUE, FALSE); |
} |
} |
#endif |
|
|
|
/* Common part to lookup and read an include file. */ |
/* Common part to lookup and read an include file. */ |
static void |
static void |
ParseLookupIncludeFile(spec, endSpec, isSystem) |
ParseLookupIncludeFile(spec, endSpec, isSystem, errIfNotFound) |
char *spec; |
char *spec; |
char *endSpec; |
char *endSpec; |
Boolean isSystem; |
Boolean isSystem; |
|
Boolean errIfNotFound; |
{ |
{ |
char *file; |
char *file; |
char *fullname; |
char *fullname; |
char endc; |
char endc; |
|
|
/* Substitute for any variables in the file name before trying to |
/* Substitute for any variables in the file name before trying to |
* find the thing. */ |
* find the thing. */ |
endc = *endSpec; |
endc = *endSpec; |
*endSpec = '\0'; |
*endSpec = '\0'; |
file = Var_Subst(spec, NULL, FALSE); |
file = Var_Subst(spec, NULL, FALSE); |
|
|
|
|
/* Handle non-system non-absolute files... */ |
/* Handle non-system non-absolute files... */ |
if (!isSystem && file[0] != '/') { |
if (!isSystem && file[0] != '/') { |
/* ... by first searching relative to the including file's |
/* ... by first searching relative to the including file's |
* location. We don't want to cd there, of course, so we |
* location. We don't want to cd there, of course, so we |
* just tack on the old file's leading path components |
* just tack on the old file's leading path components |
* and call Dir_FindFile to see if we can locate the beast. */ |
* and call Dir_FindFile to see if we can locate the beast. */ |
char *slash; |
char *slash; |
|
|
slash = strrchr(Parse_Getfilename(), '/'); |
slash = strrchr(Parse_Getfilename(), '/'); |
if (slash != NULL) { |
if (slash != NULL) { |
|
|
} |
} |
} |
} |
|
|
/* Now look first on the -I search path, then on the .PATH |
/* Now look first on the -I search path, then on the .PATH |
* search path, if not found in a -I directory. |
* search path, if not found in a -I directory. |
* XXX: Suffix specific? */ |
* XXX: Suffix specific? */ |
if (fullname == NULL) |
if (fullname == NULL) |
fullname = Dir_FindFile(file, &parseIncPath); |
fullname = Dir_FindFile(file, &parseIncPath); |
if (fullname == NULL) |
if (fullname == NULL) |
fullname = Dir_FindFile(file, &dirSearchPath); |
fullname = Dir_FindFile(file, &dirSearchPath); |
|
|
if (fullname == NULL) |
if (fullname == NULL) |
fullname = Dir_FindFile(file, &sysIncPath); |
fullname = Dir_FindFile(file, &sysIncPath); |
|
|
if (fullname == NULL) |
if (fullname == NULL && errIfNotFound) |
Parse_Error(PARSE_FATAL, "Could not find %s", file); |
Parse_Error(PARSE_FATAL, "Could not find %s", file); |
|
|
|
|
free(file); |
free(file); |
|
|
if (fullname != NULL) { |
if (fullname != NULL) { |
FILE *f; |
FILE *f; |
|
|
f = fopen(fullname, "r"); |
f = fopen(fullname, "r"); |
if (f == NULL) { |
if (f == NULL && errIfNotFound) { |
Parse_Error(PARSE_FATAL, "Cannot open %s", fullname); |
Parse_Error(PARSE_FATAL, "Cannot open %s", fullname); |
} else { |
} else { |
/* Once we find the absolute path to the file, we push the current |
/* Once we find the absolute path to the file, we push the current |
* stream to the includes stack, and start reading from the new |
* stream to the includes stack, and start reading from the new |
* file. We set up the file name to be its absolute name so that |
* file. We set up the file name to be its absolute name so that |
* error messages are informative. */ |
* error messages are informative. */ |
Parse_FromFile(fullname, f); |
Parse_FromFile(fullname, f); |
} |
} |
} |
} |
} |
} |
|
|
|
|
|
|
|
|
|
/* Strip comments from the line. May return either a copy of the line, or |
|
* the line itself. */ |
|
static char * |
|
strip_comments(copy, line) |
|
Buffer copy; |
|
const char *line; |
|
{ |
|
const char *comment; |
|
const char *p; |
|
|
|
comment = strchr(line, '#'); |
|
assert(comment != line); |
|
if (comment == NULL) |
|
return (char *)line; |
|
else { |
|
Buf_Reset(copy); |
|
|
|
for (p = line; *p != '\0'; p++) { |
|
if (*p == '\\') { |
|
if (p[1] == '#') { |
|
Buf_AddInterval(copy, line, p); |
|
Buf_AddChar(copy, '#'); |
|
line = p+2; |
|
} |
|
if (p[1] != '\0') |
|
p++; |
|
} else if (*p == '#') |
|
break; |
|
} |
|
Buf_AddInterval(copy, line, p); |
|
Buf_KillTrailingSpaces(copy); |
|
return Buf_Retrieve(copy); |
|
} |
|
} |
|
|
|
static Boolean |
|
ParseIsCond(linebuf, copy, line) |
|
Buffer linebuf; |
|
Buffer copy; |
|
char *line; |
|
{ |
|
|
|
char *stripped; |
|
|
|
while (*line != '\0' && isspace(*line)) |
|
line++; |
|
|
|
/* The line might be a conditional. Ask the conditional module |
|
* about it and act accordingly. */ |
|
switch (Cond_Eval(line)) { |
|
case COND_SKIP: |
|
/* Skip to next conditional that evaluates to COND_PARSE. */ |
|
do { |
|
line = ParseSkipGetLine(linebuf); |
|
if (line != NULL) { |
|
while (*line != '\0' && isspace(*line)) |
|
line++; |
|
stripped = strip_comments(copy, line); |
|
} |
|
} while (line != NULL && Cond_Eval(stripped) != COND_PARSE); |
|
/* FALLTHROUGH */ |
|
case COND_PARSE: |
|
return TRUE; |
|
default: |
|
break; |
|
} |
|
|
|
{ |
|
For *loop; |
|
|
|
loop = For_Eval(line); |
|
if (loop != NULL) { |
|
Boolean ok; |
|
do { |
|
/* Find the matching endfor. */ |
|
line = ParseGetLoopLine(linebuf); |
|
if (line == NULL) { |
|
Parse_Error(PARSE_FATAL, |
|
"Unexpected end of file in for loop.\n"); |
|
return FALSE; |
|
} |
|
ok = For_Accumulate(loop, line); |
|
} while (ok); |
|
For_Run(loop); |
|
return TRUE; |
|
} |
|
} |
|
|
|
if (strncmp(line, "include", 7) == 0) { |
|
ParseDoInclude(line + 7); |
|
return TRUE; |
|
} else if (strncmp(line, "undef", 5) == 0) { |
|
char *cp; |
|
|
|
line+=5; |
|
while (*line != '\0' && isspace(*line)) |
|
line++; |
|
for (cp = line; !isspace(*cp) && *cp != '\0';) |
|
cp++; |
|
*cp = '\0'; |
|
Var_Delete(line); |
|
return TRUE; |
|
} |
|
return FALSE; |
|
} |
|
|
/*- |
/*- |
*----------------------------------------------------------------------- |
*----------------------------------------------------------------------- |
* ParseFinishLine -- |
* ParseFinishDependency -- |
* Handle the end of a dependency group. |
* Handle the end of a dependency group. |
* |
* |
* Side Effects: |
* Side Effects: |
* inLine set FALSE. 'targets' list destroyed. |
* 'targets' list destroyed. |
* |
* |
*----------------------------------------------------------------------- |
*----------------------------------------------------------------------- |
*/ |
*/ |
static void |
static void |
ParseFinishLine() |
ParseFinishDependency() |
{ |
{ |
if (inLine) { |
Lst_Every(&targets, Suff_EndTransform); |
Lst_Every(&targets, Suff_EndTransform); |
Lst_Destroy(&targets, ParseHasCommands); |
Lst_Destroy(&targets, ParseHasCommands); |
|
Lst_Init(&targets); |
|
inLine = FALSE; |
|
} |
|
} |
} |
|
|
|
static void |
|
ParseDoCommands(line) |
|
const char *line; |
|
{ |
|
/* add the command to the list of |
|
* commands of all targets in the dependency spec */ |
|
char *cmd = estrdup(line); |
|
|
|
Lst_ForEach(&targets, ParseAddCmd, cmd); |
|
#ifdef CLEANUP |
|
Lst_AtEnd(&targCmds, cmd); |
|
#endif |
|
} |
|
|
/*- |
/*- |
*--------------------------------------------------------------------- |
*--------------------------------------------------------------------- |
* Parse_File -- |
* Parse_File -- |
|
|
* current dependency graph. This is the main function and controls |
* current dependency graph. This is the main function and controls |
* almost every other function in this module |
* almost every other function in this module |
* |
* |
* Results: |
|
* None |
|
* |
|
* Side Effects: |
* Side Effects: |
* Loads. Nodes are added to the list of all targets, nodes and links |
* Loads. Nodes are added to the list of all targets, nodes and links |
* are added to the dependency graph. etc. etc. etc. |
* are added to the dependency graph. etc. etc. etc. |
|
|
*/ |
*/ |
void |
void |
Parse_File(name, stream) |
Parse_File(name, stream) |
char *name; /* the name of the file being read */ |
char *name; /* the name of the file being read */ |
FILE * stream; /* Stream open to makefile to parse */ |
FILE *stream; /* Stream open to makefile to parse */ |
{ |
{ |
register char *cp, /* pointer into the line */ |
char *cp, /* pointer into the line */ |
*line; /* the line we're working on */ |
*line; /* the line we're working on */ |
|
Boolean inDependency; /* true if currently in a dependency |
|
* line or its commands */ |
|
|
inLine = FALSE; |
BUFFER buf; |
|
BUFFER copy; |
|
|
|
Buf_Init(&buf, MAKE_BSIZE); |
|
Buf_Init(©, MAKE_BSIZE); |
|
inDependency = FALSE; |
Parse_FromFile(name, stream); |
Parse_FromFile(name, stream); |
do { |
|
while ((line = ParseReadLine ()) != NULL) { |
|
if (*line == '.') { |
|
/* |
|
* Lines that begin with the special character are either |
|
* include or undef directives. |
|
*/ |
|
for (cp = line + 1; isspace (*cp); cp++) { |
|
continue; |
|
} |
|
if (strncmp (cp, "include", 7) == 0) { |
|
ParseDoInclude (cp + 7); |
|
goto nextLine; |
|
} else if (strncmp(cp, "undef", 5) == 0) { |
|
char *cp2; |
|
for (cp += 5; isspace((unsigned char) *cp); cp++) { |
|
continue; |
|
} |
|
|
|
for (cp2 = cp; !isspace((unsigned char) *cp2) && |
do { |
(*cp2 != '\0'); cp2++) { |
while ((line = ParseReadLine(&buf)) != NULL) { |
continue; |
|
} |
|
|
|
*cp2 = '\0'; |
|
|
|
Var_Delete(cp, VAR_GLOBAL); |
|
goto nextLine; |
|
} |
|
} |
|
if (*line == '#') { |
|
/* If we're this far, the line must be a comment. */ |
|
goto nextLine; |
|
} |
|
|
|
if (*line == '\t') { |
if (*line == '\t') { |
/* |
if (inDependency) |
* If a line starts with a tab, it can only hope to be |
ParseDoCommands(line+1); |
* a creation command. |
else |
*/ |
Parse_Error(PARSE_FATAL, |
#ifndef POSIX |
"Unassociated shell command \"%s\"", |
shellCommand: |
line); |
#endif |
|
for (cp = line + 1; isspace (*cp); cp++) { |
|
continue; |
|
} |
|
if (*cp) { |
|
if (inLine) { |
|
/* |
|
* So long as it's not a blank line and we're actually |
|
* in a dependency spec, add the command to the list of |
|
* commands of all targets in the dependency spec |
|
*/ |
|
Lst_ForEach(&targets, ParseAddCmd, cp); |
|
#ifdef CLEANUP |
|
Lst_AtEnd(&targCmds, line); |
|
#endif |
|
continue; |
|
} else { |
|
Parse_Error (PARSE_FATAL, |
|
"Unassociated shell command \"%s\"", |
|
cp); |
|
} |
|
} |
|
#ifdef SYSVINCLUDE |
|
} else if (strncmp (line, "include", 7) == 0 && |
|
isspace((unsigned char) line[7]) && |
|
strchr(line, ':') == NULL) { |
|
/* |
|
* It's an S3/S5-style "include". |
|
*/ |
|
ParseTraditionalInclude (line + 7); |
|
goto nextLine; |
|
#endif |
|
} else if (Parse_IsVar (line)) { |
|
ParseFinishLine(); |
|
Parse_DoVar (line, VAR_GLOBAL); |
|
} else { |
} else { |
/* |
char *stripped; |
* We now know it's a dependency line so it needs to have all |
stripped = strip_comments(©, line); |
* variables expanded before being parsed. Tell the variable |
if (*stripped == '.' && ParseIsCond(&buf, ©, stripped+1)) |
* module to complain if some variable is undefined... |
; |
* To make life easier on novices, if the line is indented we |
else if (FEATURES(FEATURE_SYSVINCLUDE) && |
* first make sure the line has a dependency operator in it. |
strncmp(stripped, "include", 7) == 0 && |
* If it doesn't have an operator and we're in a dependency |
isspace(stripped[7]) && |
* line's script, we assume it's actually a shell command |
strchr(stripped, ':') == NULL) { |
* and add it to the current list of targets. |
/* It's an S3/S5-style "include". */ |
*/ |
ParseTraditionalInclude(stripped + 7); |
#ifndef POSIX |
} else if (FEATURES(FEATURE_CONDINCLUDE) && |
Boolean nonSpace = FALSE; |
(*stripped == '-' || *stripped == 's') && |
#endif |
strncmp(stripped+1, "include", 7) == 0 && |
|
isspace(stripped[8]) && |
cp = line; |
strchr(stripped, ':') == NULL) { |
if (isspace((unsigned char) line[0])) { |
ParseConditionalInclude(stripped+8); |
while ((*cp != '\0') && isspace((unsigned char) *cp)) { |
|
cp++; |
|
} |
|
if (*cp == '\0') { |
|
goto nextLine; |
|
} |
|
#ifndef POSIX |
|
while ((*cp != ':') && (*cp != '!') && (*cp != '\0')) { |
|
nonSpace = TRUE; |
|
cp++; |
|
} |
|
#endif |
|
} |
|
|
|
#ifndef POSIX |
|
if (*cp == '\0') { |
|
if (inLine) { |
|
Parse_Error (PARSE_WARNING, |
|
"Shell command needs a leading tab"); |
|
goto shellCommand; |
|
} else if (nonSpace) { |
|
Parse_Error (PARSE_FATAL, "Missing operator"); |
|
} |
|
} else { |
} else { |
#endif |
char *dep; |
ParseFinishLine(); |
|
|
|
cp = Var_Subst(line, NULL, TRUE); |
if (inDependency) |
free (line); |
ParseFinishDependency(); |
line = cp; |
if (Parse_IsVar(stripped)) { |
|
inDependency = FALSE; |
|
Parse_DoVar(stripped, VAR_GLOBAL); |
|
} else { |
|
size_t pos; |
|
char *end; |
|
|
/* Need a new list for the target nodes */ |
/* Need a new list for the target nodes. */ |
Lst_Destroy(&targets, NOFREE); |
Lst_Init(&targets); |
Lst_Init(&targets); |
inDependency = TRUE; |
inLine = TRUE; |
|
|
|
ParseDoDependency (line); |
dep = NULL; |
#ifndef POSIX |
/* First we need to find eventual dependencies */ |
|
pos = strcspn(stripped, ":!"); |
|
/* go over :!, and find ; */ |
|
if (stripped[pos] != '\0' && |
|
(end = strchr(stripped+pos+1, ';')) != NULL) { |
|
if (line != stripped) |
|
/* find matching ; in original... The |
|
* original might be slightly longer. */ |
|
dep = strchr(line+(end-stripped), ';'); |
|
else |
|
dep = end; |
|
/* kill end of line. */ |
|
*end = '\0'; |
|
} |
|
/* We now know it's a dependency line so it needs to |
|
* have all variables expanded before being parsed. |
|
* Tell the variable module to complain if some |
|
* variable is undefined... */ |
|
cp = Var_Subst(stripped, NULL, TRUE); |
|
ParseDoDependency(cp); |
|
free(cp); |
|
|
|
/* Parse dependency if it's not empty. */ |
|
if (dep != NULL) { |
|
do { |
|
dep++; |
|
} while (isspace(*dep)); |
|
if (*dep != '\0') |
|
ParseDoCommands(dep); |
|
} |
|
} |
} |
} |
#endif |
|
} |
} |
|
|
nextLine: |
|
|
|
free (line); |
|
} |
} |
} while (Parse_NextFile()); |
} while (Parse_NextFile()); |
|
|
/* Make sure conditionals are clean */ |
if (inDependency) |
|
ParseFinishDependency(); |
|
/* Make sure conditionals are clean. */ |
Cond_End(); |
Cond_End(); |
|
|
Finish_Errors(); |
Finish_Errors(); |
|
Buf_Destroy(&buf); |
|
Buf_Destroy(©); |
} |
} |
|
|
/*- |
/*- |
|
|
#ifdef CLEANUP |
#ifdef CLEANUP |
Lst_Destroy(&targCmds, (SimpleProc)free); |
Lst_Destroy(&targCmds, (SimpleProc)free); |
Lst_Destroy(&fileNames, (SimpleProc)free); |
Lst_Destroy(&fileNames, (SimpleProc)free); |
Lst_Delete(&targets, NOFREE); |
Lst_Destroy(&targets, NOFREE); |
Lst_Destroy(&sysIncPath, Dir_Destroy); |
Lst_Destroy(&sysIncPath, Dir_Destroy); |
Lst_Destroy(&parseIncPath, Dir_Destroy); |
Lst_Destroy(&parseIncPath, Dir_Destroy); |
LowParse_End(); |
LowParse_End(); |
|
|
* Return a Lst of the main target to create for main()'s sake. If |
* Return a Lst of the main target to create for main()'s sake. If |
* no such target exists, we Punt with an obnoxious error message. |
* no such target exists, we Punt with an obnoxious error message. |
* |
* |
* Side Effects: |
* Side effect: |
* Add the node to create to the list. |
* Add the node to create to the list. |
* |
|
*----------------------------------------------------------------------- |
*----------------------------------------------------------------------- |
*/ |
*/ |
void |
void |
Parse_MainName(listmain) |
Parse_MainName(listmain) |
Lst listmain; /* result list */ |
Lst listmain; /* result list */ |
{ |
{ |
|
|
if (mainNode == NULL) |
if (mainNode == NULL) { |
Punt ("no target to make."); |
Punt("no target to make."); |
/*NOTREACHED*/ |
/*NOTREACHED*/ |
else if (mainNode->type & OP_DOUBLEDEP) { |
} else if (mainNode->type & OP_DOUBLEDEP) { |
Lst_AtEnd(listmain, mainNode); |
Lst_AtEnd(listmain, mainNode); |
Lst_Concat(listmain, &mainNode->cohorts); |
Lst_Concat(listmain, &mainNode->cohorts); |
} |
} |
else |
else |
Lst_AtEnd(listmain, mainNode); |
Lst_AtEnd(listmain, mainNode); |
} |
} |
|
|