version 1.91, 2007/09/17 11:32:25 |
version 1.92, 2007/09/17 12:42:09 |
|
|
#include "parsevar.h" |
#include "parsevar.h" |
#include "stats.h" |
#include "stats.h" |
#include "garray.h" |
#include "garray.h" |
|
#include "node_int.h" |
|
#include "nodehashconsts.h" |
|
|
|
|
|
/* gsources and gtargets should be local to some functions, but they're |
|
* set as persistent arrays for performance reasons. |
|
*/ |
static struct growableArray gsources, gtargets; |
static struct growableArray gsources, gtargets; |
#define SOURCES_SIZE 128 |
#define SOURCES_SIZE 128 |
#define TARGETS_SIZE 32 |
#define TARGETS_SIZE 32 |
|
|
* first target on the first dependency |
* first target on the first dependency |
* line in the first makefile */ |
* line in the first makefile */ |
/*- |
/*- |
* 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 |
* SPECIAL_NONE if the target is unspecial. If it *is* special, however, |
* are linked as children of the parent but not vice versa. This variable is |
* the children are linked as children of the parent but not vice versa. |
* set in ParseDoDependency |
* This variable is set in ParseDoDependency |
*/ |
*/ |
typedef enum { |
|
Begin, /* .BEGIN */ |
|
Default, /* .DEFAULT */ |
|
End, /* .END */ |
|
Ignore, /* .IGNORE */ |
|
Includes, /* .INCLUDES */ |
|
Interrupt, /* .INTERRUPT */ |
|
Libs, /* .LIBS */ |
|
MFlags, /* .MFLAGS or .MAKEFLAGS */ |
|
Main, /* .MAIN and we don't have anything user-specified to |
|
* make */ |
|
NoExport, /* .NOEXPORT */ |
|
NoPath, /* .NOPATH */ |
|
Not, /* Not special */ |
|
NotParallel, /* .NOTPARALELL */ |
|
Null, /* .NULL */ |
|
Order, /* .ORDER */ |
|
Parallel, /* .PARALLEL */ |
|
ExPath, /* .PATH */ |
|
Phony, /* .PHONY */ |
|
Precious, /* .PRECIOUS */ |
|
Silent, /* .SILENT */ |
|
SingleShell, /* .SINGLESHELL */ |
|
Suffixes, /* .SUFFIXES */ |
|
Wait, /* .WAIT */ |
|
Attribute /* Generic attribute */ |
|
} ParseSpecial; |
|
|
|
static ParseSpecial specType; |
static int specType; |
static int waiting; |
static int waiting; |
|
|
/* |
/* |
|
|
*/ |
*/ |
static GNode *predecessor; |
static GNode *predecessor; |
|
|
/* |
|
* The parseKeywords table is searched using binary search when deciding |
|
* if a target or source is special. The 'spec' field is the ParseSpecial |
|
* type of the keyword ("Not" if the keyword isn't special as a target) while |
|
* the 'op' field is the operator to apply to the list of targets if the |
|
* keyword is used as a source ("0" if the keyword isn't special as a source) |
|
*/ |
|
static struct { |
|
char *name; /* Name of keyword */ |
|
ParseSpecial spec; /* Type when used as a target */ |
|
int op; /* Operator when used as a source */ |
|
} parseKeywords[] = { |
|
{ ".BEGIN", Begin, 0 }, |
|
{ ".DEFAULT", Default, 0 }, |
|
{ ".END", End, 0 }, |
|
{ ".EXEC", Attribute, OP_EXEC }, |
|
{ ".IGNORE", Ignore, OP_IGNORE }, |
|
{ ".INCLUDES", Includes, 0 }, |
|
{ ".INTERRUPT", Interrupt, 0 }, |
|
{ ".INVISIBLE", Attribute, OP_INVISIBLE }, |
|
{ ".JOIN", Attribute, OP_JOIN }, |
|
{ ".LIBS", Libs, 0 }, |
|
{ ".MADE", Attribute, OP_MADE }, |
|
{ ".MAIN", Main, 0 }, |
|
{ ".MAKE", Attribute, OP_MAKE }, |
|
{ ".MAKEFLAGS", MFlags, 0 }, |
|
{ ".MFLAGS", MFlags, 0 }, |
|
#if 0 /* basic scaffolding for NOPATH, not working yet */ |
|
{ ".NOPATH", NoPath, OP_NOPATH }, |
|
#endif |
|
{ ".NOTMAIN", Attribute, OP_NOTMAIN }, |
|
{ ".NOTPARALLEL", NotParallel, 0 }, |
|
{ ".NO_PARALLEL", NotParallel, 0 }, |
|
{ ".NULL", Null, 0 }, |
|
{ ".OPTIONAL", Attribute, OP_OPTIONAL }, |
|
{ ".ORDER", Order, 0 }, |
|
{ ".PARALLEL", Parallel, 0 }, |
|
{ ".PATH", ExPath, 0 }, |
|
{ ".PHONY", Phony, OP_PHONY }, |
|
{ ".PRECIOUS", Precious, OP_PRECIOUS }, |
|
{ ".RECURSIVE", Attribute, OP_MAKE }, |
|
{ ".SILENT", Silent, OP_SILENT }, |
|
{ ".SINGLESHELL", SingleShell, 0 }, |
|
{ ".SUFFIXES", Suffixes, 0 }, |
|
{ ".USE", Attribute, OP_USE }, |
|
{ ".WAIT", Wait, 0 }, |
|
}; |
|
|
|
static int ParseFindKeyword(const char *); |
|
static void ParseLinkSrc(GNode *, GNode *); |
static void ParseLinkSrc(GNode *, GNode *); |
static int ParseDoOp(GNode *, int); |
static int ParseDoOp(GNode **, int); |
static int ParseAddDep(GNode *, GNode *); |
static int ParseAddDep(GNode *, GNode *); |
static void ParseDoSrc(int, const char *); |
static void ParseDoSrc(struct growableArray *, struct growableArray *, int, |
|
const char *, const char *); |
static int ParseFindMain(void *, void *); |
static int ParseFindMain(void *, void *); |
static void ParseAddDir(void *, void *); |
|
static void ParseClearPath(void *); |
static void ParseClearPath(void *); |
|
|
static void add_target_node(const char *); |
static void add_target_node(const char *, const char *); |
static void add_target_nodes(const char *); |
static void add_target_nodes(const char *, const char *); |
static void ParseDoDependency(char *); |
static void apply_op(struct growableArray *, int, GNode *); |
|
static void ParseDoDependency(const char *); |
static void ParseAddCmd(void *, void *); |
static void ParseAddCmd(void *, void *); |
static void ParseHasCommands(void *); |
static void ParseHasCommands(void *); |
static bool handle_poison(const char *); |
static bool handle_poison(const char *); |
|
|
static void lookup_sysv_include(const char *, const char *); |
static void lookup_sysv_include(const char *, const char *); |
static void lookup_conditional_include(const char *, const char *); |
static void lookup_conditional_include(const char *, const char *); |
static bool parse_as_special_line(Buffer, Buffer, const char *); |
static bool parse_as_special_line(Buffer, Buffer, const char *); |
|
|
|
static const char *parse_do_targets(Lst, int *, const char *); |
static void parse_target_line(struct growableArray *, const char *, |
static void parse_target_line(struct growableArray *, const char *, |
const char *); |
const char *); |
|
|
/*- |
static void finish_commands(struct growableArray *); |
*---------------------------------------------------------------------- |
static void parse_commands(struct growableArray *, const char *); |
* ParseFindKeyword -- |
static void create_special_nodes(void); |
* Look in the table of keywords for one matching the given string. |
static bool found_delimiter(const char *); |
* |
static int handle_special_targets(Lst); |
* Results: |
|
* The index of the keyword, or -1 if it isn't there. |
|
*---------------------------------------------------------------------- |
|
*/ |
|
static int |
|
ParseFindKeyword(const char *str) /* keyword to look up */ |
|
{ |
|
int start, |
|
end, |
|
cur; |
|
int diff; |
|
|
|
start = 0; |
#define SPECIAL_EXEC 4 |
end = (sizeof(parseKeywords)/sizeof(parseKeywords[0])) - 1; |
#define SPECIAL_IGNORE 5 |
|
#define SPECIAL_INCLUDES 6 |
|
#define SPECIAL_INVISIBLE 8 |
|
#define SPECIAL_JOIN 9 |
|
#define SPECIAL_LIBS 10 |
|
#define SPECIAL_MADE 11 |
|
#define SPECIAL_MAIN 12 |
|
#define SPECIAL_MAKE 13 |
|
#define SPECIAL_MFLAGS 14 |
|
#define SPECIAL_NOTMAIN 15 |
|
#define SPECIAL_NOTPARALLEL 16 |
|
#define SPECIAL_NULL 17 |
|
#define SPECIAL_OPTIONAL 18 |
|
#define SPECIAL_ORDER 19 |
|
#define SPECIAL_PARALLEL 20 |
|
#define SPECIAL_PHONY 22 |
|
#define SPECIAL_PRECIOUS 23 |
|
#define SPECIAL_SILENT 25 |
|
#define SPECIAL_SINGLESHELL 26 |
|
#define SPECIAL_SUFFIXES 27 |
|
#define SPECIAL_USE 28 |
|
#define SPECIAL_WAIT 29 |
|
#define SPECIAL_NOPATH 30 |
|
#define SPECIAL_ERROR 31 |
|
|
do { |
|
cur = start + (end - start) / 2; |
|
diff = strcmp(str, parseKeywords[cur].name); |
|
|
|
if (diff == 0) { |
#define P(k) k, sizeof(k), K_##k |
return cur; |
|
} else if (diff < 0) { |
static struct { |
end = cur - 1; |
const char *keyword; |
} else { |
size_t sz; |
start = cur + 1; |
uint32_t hv; |
|
int type; |
|
int special_op; |
|
} specials[] = { |
|
{ P(NODE_EXEC), SPECIAL_EXEC | SPECIAL_TARGETSOURCE, OP_EXEC, }, |
|
{ P(NODE_IGNORE), SPECIAL_IGNORE | SPECIAL_TARGETSOURCE, OP_IGNORE, }, |
|
{ P(NODE_INCLUDES), SPECIAL_INCLUDES | SPECIAL_TARGET, 0, }, |
|
{ P(NODE_INVISIBLE),SPECIAL_INVISIBLE | SPECIAL_TARGETSOURCE,OP_INVISIBLE, }, |
|
{ P(NODE_JOIN), SPECIAL_JOIN | SPECIAL_TARGETSOURCE, OP_JOIN, }, |
|
{ P(NODE_LIBS), SPECIAL_LIBS | SPECIAL_TARGET, 0, }, |
|
{ P(NODE_MADE), SPECIAL_MADE | SPECIAL_TARGETSOURCE, OP_MADE, }, |
|
{ P(NODE_MAIN), SPECIAL_MAIN | SPECIAL_TARGET, 0, }, |
|
{ P(NODE_MAKE), SPECIAL_MAKE | SPECIAL_TARGETSOURCE, OP_MAKE, }, |
|
{ P(NODE_MAKEFLAGS), SPECIAL_MFLAGS | SPECIAL_TARGET, 0, }, |
|
{ P(NODE_MFLAGS), SPECIAL_MFLAGS | SPECIAL_TARGET, 0, }, |
|
{ P(NODE_NOTMAIN), SPECIAL_NOTMAIN | SPECIAL_TARGETSOURCE, OP_NOTMAIN, }, |
|
{ P(NODE_NOTPARALLEL),SPECIAL_NOTPARALLEL | SPECIAL_TARGET, 0, }, |
|
{ P(NODE_NO_PARALLEL),SPECIAL_NOTPARALLEL | SPECIAL_TARGET, 0, }, |
|
{ P(NODE_NULL), SPECIAL_NULL | SPECIAL_TARGET, 0, }, |
|
{ P(NODE_OPTIONAL), SPECIAL_OPTIONAL | SPECIAL_TARGETSOURCE,OP_OPTIONAL, }, |
|
{ P(NODE_ORDER), SPECIAL_ORDER | SPECIAL_TARGET, 0, }, |
|
{ P(NODE_PARALLEL), SPECIAL_PARALLEL | SPECIAL_TARGET, 0, }, |
|
{ P(NODE_PATH), SPECIAL_PATH | SPECIAL_TARGET, 0, }, |
|
{ P(NODE_PHONY), SPECIAL_PHONY | SPECIAL_TARGETSOURCE, OP_PHONY, }, |
|
{ P(NODE_PRECIOUS), SPECIAL_PRECIOUS | SPECIAL_TARGETSOURCE,OP_PRECIOUS, }, |
|
{ P(NODE_RECURSIVE),SPECIAL_MAKE | SPECIAL_TARGETSOURCE, OP_MAKE, }, |
|
{ P(NODE_SILENT), SPECIAL_SILENT | SPECIAL_TARGETSOURCE, OP_SILENT, }, |
|
{ P(NODE_SINGLESHELL),SPECIAL_SINGLESHELL | SPECIAL_TARGET, 0, }, |
|
{ P(NODE_SUFFIXES), SPECIAL_SUFFIXES | SPECIAL_TARGET, 0, }, |
|
{ P(NODE_USE), SPECIAL_USE | SPECIAL_TARGETSOURCE, OP_USE, }, |
|
{ P(NODE_WAIT), SPECIAL_WAIT | SPECIAL_TARGETSOURCE, 0 }, |
|
#if 0 |
|
{ P(NODE_NOPATH), SPECIAL_NOPATH, }, |
|
#endif |
|
}; |
|
|
|
#undef P |
|
|
|
static void |
|
create_special_nodes() |
|
{ |
|
unsigned int i; |
|
|
|
for (i = 0; i < sizeof(specials)/sizeof(specials[0]); i++) { |
|
GNode *gn = Targ_FindNodeh(specials[i].keyword, |
|
specials[i].sz, specials[i].hv, TARG_CREATE); |
|
gn->special = specials[i].type; |
|
gn->special_op = specials[i].special_op; |
} |
} |
} while (start <= end); |
|
return -1; |
|
} |
} |
|
|
/*- |
/*- |
*--------------------------------------------------------------------- |
*--------------------------------------------------------------------- |
* ParseLinkSrc -- |
* ParseLinkSrc -- |
|
|
GNode *pgn, /* The parent node */ |
GNode *pgn, /* The parent node */ |
GNode *cgn) /* The child node */ |
GNode *cgn) /* The child node */ |
{ |
{ |
if (Lst_AddNew(&pgn->children, cgn)) { |
if (Lst_AddNew(&pgn->children, cgn)) { |
if (specType == Not) |
if (specType == SPECIAL_NONE) |
Lst_AtEnd(&cgn->parents, pgn); |
Lst_AtEnd(&cgn->parents, pgn); |
pgn->unmade++; |
pgn->unmade++; |
} |
} |
} |
} |
|
|
/*- |
/*- |
*--------------------------------------------------------------------- |
*--------------------------------------------------------------------- |
* ParseDoOp -- |
* ParseDoOp -- |
* Apply the parsed operator to the given target node. Used in a |
* Apply the parsed operator to the given target node. Used in a |
* Lst_Find call by ParseDoDependency once all targets have |
* Array_Find call by ParseDoDependency once all targets have |
* 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. |
* |
* |
|
|
*/ |
*/ |
static int |
static int |
ParseDoOp( |
ParseDoOp( |
GNode *gn, /* The node to which the operator is to be applied */ |
GNode **gnp, |
int op) /* The operator to apply */ |
int op) /* The operator to apply */ |
{ |
{ |
/* |
GNode *gn = *gnp; |
* 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 |
* If the dependency mask of the operator and the node don't match and |
* the operator actually has some dependency information in it, complain. |
* the node has actually had an operator applied to it before, and |
*/ |
* the operator actually has some dependency information in it, complain. |
if (((op & OP_OPMASK) != (gn->type & OP_OPMASK)) && |
*/ |
!OP_NOP(gn->type) && !OP_NOP(op)) { |
if (((op & OP_OPMASK) != (gn->type & OP_OPMASK)) && |
Parse_Error(PARSE_FATAL, |
!OP_NOP(gn->type) && !OP_NOP(op)) { |
"Inconsistent operator for %s", gn->name); |
Parse_Error(PARSE_FATAL, "Inconsistent operator for %s", gn->name); |
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 |
/* If the node was the object of a :: operator, we need to create a |
* create a new instance of it for the children and commands on |
* new instance of it for the children and commands on this dependency |
* this dependency line. The new instance is placed on the |
* line. The new instance is placed on the 'cohorts' list of the |
* 'cohorts' list of the initial one (note the initial one is |
* initial one (note the initial one is not on its own cohorts list) |
* not on its own cohorts list) and the new instance is linked |
* and the new instance is linked to all parents of the initial |
* to all parents of the initial instance. */ |
* instance. */ |
GNode *cohort; |
GNode *cohort; |
LstNode ln; |
LstNode ln; |
unsigned int i; |
|
|
|
cohort = Targ_NewGN(gn->name); |
cohort = Targ_NewGN(gn->name); |
/* Duplicate links to parents so graph traversal is simple. |
/* Duplicate links to parents so graph traversal is simple. Perhaps |
* Perhaps some type bits should be duplicated? |
* some type bits should be duplicated? |
* |
* |
* Make the cohort invisible as well to avoid duplicating it |
* Make the cohort invisible as well to avoid duplicating it into |
* into other variables. True, parents of this target won't |
* other variables. True, parents of this target won't tend to do |
* tend to do anything with their local variables, but better |
* anything with their local variables, but better safe than |
* safe than sorry. */ |
* sorry. */ |
for (ln = Lst_First(&gn->parents); ln != NULL; ln = Lst_Adv(ln)) |
for (ln = Lst_First(&gn->parents); ln != NULL; ln = Lst_Adv(ln)) |
ParseLinkSrc((GNode *)Lst_Datum(ln), 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 */ |
for (i = 0; i < gtargets.n; i++) |
*gnp = cohort; |
if (gtargets.a[i] == gn) |
gn = cohort; |
break; |
} |
gtargets.a[i] = cohort; |
/* We don't want to nuke any previous flags (whatever they were) so we |
gn = cohort; |
* just OR the new operator into the old. */ |
} |
gn->type |= op; |
/* We don't want to nuke any previous flags (whatever they were) so we |
return 1; |
* just OR the new operator into the old. */ |
|
gn->type |= op; |
|
return 1; |
|
} |
} |
|
|
/*- |
/*- |
|
|
static int |
static int |
ParseAddDep(GNode *p, GNode *s) |
ParseAddDep(GNode *p, GNode *s) |
{ |
{ |
if (p->order < s->order) { |
if (p->order < s->order) { |
/* XXX: This can cause loops, and loops can cause unmade |
/* XXX: This can cause loops, and loops can cause unmade targets, |
* targets, but checking is tedious, and the debugging output |
* but checking is tedious, and the debugging output can show the |
* 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; |
} |
} |
|
else |
|
return 0; |
|
} |
|
|
|
static void |
|
apply_op(struct growableArray *targets, int op, GNode *gn) |
|
{ |
|
if (op) |
|
gn->type |= op; |
else |
else |
return 0; |
Array_ForEach(targets, ParseLinkSrc, gn); |
} |
} |
|
|
|
|
/*- |
/*- |
*--------------------------------------------------------------------- |
*--------------------------------------------------------------------- |
* ParseDoSrc -- |
* ParseDoSrc -- |
|
|
*/ |
*/ |
static void |
static void |
ParseDoSrc( |
ParseDoSrc( |
|
struct growableArray *targets, |
|
struct growableArray *sources, |
int tOp, /* operator (if any) from special targets */ |
int tOp, /* operator (if any) from special targets */ |
const char *src) /* name of the source to handle */ |
const char *src, /* name of the source to handle */ |
|
const char *esrc) |
{ |
{ |
GNode *gn = NULL; |
GNode *gn = Targ_FindNodei(src, esrc, TARG_CREATE); |
|
if ((gn->special & SPECIAL_SOURCE) != 0) { |
if (*src == '.' && isupper(src[1])) { |
if (gn->special_op) { |
int keywd = ParseFindKeyword(src); |
Array_FindP(targets, ParseDoOp, gn->special_op); |
if (keywd != -1) { |
return; |
int op = parseKeywords[keywd].op; |
} else { |
if (op != 0) { |
assert((gn->special & SPECIAL_MASK) == SPECIAL_WAIT); |
Array_Find(>argets, ParseDoOp, op); |
waiting++; |
return; |
return; |
} |
|
if (parseKeywords[keywd].spec == Wait) { |
|
waiting++; |
|
return; |
|
} |
|
} |
} |
} |
} |
|
|
switch (specType) { |
switch (specType) { |
case Main: |
case SPECIAL_MAIN: |
/* |
/* |
* If we have noted the existence of a .MAIN, it means we need |
* If we have noted the existence of a .MAIN, it means we need |
* to add the sources of said target to the list of things |
* to add the sources of said target to the list of things |
* to create. The string 'src' is likely to be freed, so we |
* to create. Note that this will only be invoked if the user |
* must make a new copy of it. Note that this will only be |
* didn't specify a target on the command line. This is to |
* invoked if the user didn't specify a target on the command |
* allow #ifmake's to succeed, or something... |
* line. This is to allow #ifmake's to succeed, or something... |
|
*/ |
*/ |
Lst_AtEnd(create, estrdup(src)); |
Lst_AtEnd(create, gn->name); |
/* |
/* |
* Add the name to the .TARGETS variable as well, so the user |
* Add the name to the .TARGETS variable as well, so the user |
* can employ that, if desired. |
* can employ that, if desired. |
*/ |
*/ |
Var_Append(".TARGETS", src); |
Var_Append(".TARGETS", gn->name); |
return; |
return; |
|
|
case Order: |
case SPECIAL_ORDER: |
/* |
/* |
* Create proper predecessor/successor links between the |
* Create proper predecessor/successor links between the |
* previous source and the current one. |
* previous source and the current one. |
*/ |
*/ |
gn = Targ_FindNode(src, 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 current source now becomes the predecessor for the next |
|
* one. |
|
*/ |
|
predecessor = gn; |
predecessor = gn; |
break; |
break; |
|
|
default: |
default: |
/* |
/* |
* If the source is not an attribute, we need to find/create |
|
* a node for it. After that we can apply any operator to it |
|
* from a special target or link it to its parents, as |
|
* appropriate. |
|
* |
|
* In the case of a source that was the object of a :: operator, |
* In the case of a source that was the object of a :: operator, |
* the attribute is applied to all of its instances (as kept in |
* the attribute is applied to all of its instances (as kept in |
* 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); |
apply_op(targets, tOp, gn); |
if (tOp) { |
|
gn->type |= tOp; |
|
} else { |
|
Array_ForEach(>argets, ParseLinkSrc, gn); |
|
} |
|
if ((gn->type & OP_OPMASK) == OP_DOUBLEDEP) { |
if ((gn->type & OP_OPMASK) == OP_DOUBLEDEP) { |
GNode *cohort; |
|
LstNode ln; |
LstNode ln; |
|
|
for (ln=Lst_First(&gn->cohorts); ln != NULL; |
for (ln=Lst_First(&gn->cohorts); ln != NULL; |
ln = Lst_Adv(ln)){ |
ln = Lst_Adv(ln)){ |
cohort = (GNode *)Lst_Datum(ln); |
apply_op(targets, tOp, |
if (tOp) { |
(GNode *)Lst_Datum(ln)); |
cohort->type |= tOp; |
|
} else { |
|
Array_ForEach(>argets, ParseLinkSrc, |
|
cohort); |
|
} |
|
} |
} |
} |
} |
break; |
break; |
} |
} |
|
|
gn->order = waiting; |
gn->order = waiting; |
Array_AtEnd(&gsources, gn); |
Array_AtEnd(sources, gn); |
if (waiting) { |
if (waiting) |
Array_Find(&gsources, ParseAddDep, gn); |
Array_Find(sources, ParseAddDep, gn); |
} |
|
} |
} |
|
|
/*- |
/*- |
|
|
* 1 if main not found yet, 0 if it is. |
* 1 if main not found yet, 0 if it is. |
* |
* |
* Side Effects: |
* Side Effects: |
* mainNode is changed and Targ_SetMain is called. |
* mainNode is changed and. |
*----------------------------------------------------------------------- |
*----------------------------------------------------------------------- |
*/ |
*/ |
static int |
static int |
|
|
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 && gn->special == SPECIAL_NONE) { |
mainNode = gn; |
mainNode = gn; |
Targ_SetMain(gn); |
return 0; |
return 0; |
} else { |
} else { |
return 1; |
return 1; |
} |
} |
|
} |
} |
|
|
/*- |
/*- |
*----------------------------------------------------------------------- |
*----------------------------------------------------------------------- |
* ParseAddDir -- |
|
* Front-end for Dir_AddDir to make sure Lst_ForEach keeps going |
|
* |
|
* Side Effects: |
|
* See Dir_AddDir. |
|
*----------------------------------------------------------------------- |
|
*/ |
|
static void |
|
ParseAddDir(void *path, void *name) |
|
{ |
|
Dir_AddDir((Lst)path, (char *)name); |
|
} |
|
|
|
/*- |
|
*----------------------------------------------------------------------- |
|
* ParseClearPath -- |
* ParseClearPath -- |
* Reinit path to an empty path |
* Reinit path to an empty path |
*----------------------------------------------------------------------- |
*----------------------------------------------------------------------- |
|
|
static void |
static void |
ParseClearPath(void *p) |
ParseClearPath(void *p) |
{ |
{ |
Lst path = (Lst)p; |
Lst path = (Lst)p; |
|
|
Lst_Destroy(path, Dir_Destroy); |
Lst_Destroy(path, Dir_Destroy); |
Lst_Init(path); |
Lst_Init(path); |
} |
} |
|
|
static void |
static void |
add_target_node(const char *line) |
add_target_node(const char *line, const char *end) |
{ |
{ |
GNode *gn; |
GNode *gn; |
|
|
if (!Suff_IsTransform(line)) |
gn = Suff_ParseAsTransform(line, end); |
gn = Targ_FindNode(line, TARG_CREATE); |
|
else |
|
gn = Suff_AddTransform(line); |
|
|
|
|
if (gn == NULL) { |
|
gn = Targ_FindNodei(line, end, TARG_CREATE); |
|
gn->type &= ~OP_DUMMY; |
|
} |
|
|
if (gn != NULL) |
if (gn != NULL) |
Array_AtEnd(>argets, gn); |
Array_AtEnd(>argets, gn); |
} |
} |
|
|
static void |
static void |
add_target_nodes(const char *line) |
add_target_nodes(const char *line, const char *end) |
{ |
{ |
|
|
if (Dir_HasWildcards(line)) { |
if (Dir_HasWildcardsi(line, end)) { |
/* |
/* |
* Targets are to be sought only in the current directory, |
* Targets are to be sought only in the current directory, |
* so create an empty path for the thing. Note we need to |
* so create an empty path for the thing. Note we need to |
|
|
|
|
Lst_Init(&emptyPath); |
Lst_Init(&emptyPath); |
Lst_Init(&curTargs); |
Lst_Init(&curTargs); |
Dir_Expand(line, &emptyPath, &curTargs); |
Dir_Expandi(line, end, &emptyPath, &curTargs); |
Lst_Destroy(&emptyPath, Dir_Destroy); |
Lst_Destroy(&emptyPath, Dir_Destroy); |
while ((targName = (char *)Lst_DeQueue(&curTargs)) != NULL) { |
while ((targName = (char *)Lst_DeQueue(&curTargs)) != NULL) { |
add_target_node(targName); |
add_target_node(targName, targName + strlen(targName)); |
} |
} |
Lst_Destroy(&curTargs, NOFREE); |
Lst_Destroy(&curTargs, NOFREE); |
} else { |
} else { |
add_target_node(line); |
add_target_node(line, end); |
} |
} |
} |
} |
|
|
|
/* special target line check: a proper delimiter is a ':' or '!', but |
|
* we don't want to end a target on such a character if there is a better |
|
* match later on. |
|
* 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. |
|
*/ |
|
static bool |
|
found_delimiter(const char *s) |
|
{ |
|
if (*s == '!' || *s == ':') { |
|
const char *p = s + 1; |
|
|
|
if (*s == ':' && *p == ':') |
|
p++; |
|
|
|
/* Found the best match already. */ |
|
if (isspace(*p) || *p == '\0') |
|
return true; |
|
|
|
do { |
|
p += strcspn(p, "!:"); |
|
if (*p == '\0') |
|
break; |
|
p++; |
|
} while (!isspace(*p)); |
|
|
|
/* No better match later on... */ |
|
if (*p == '\0') |
|
return true; |
|
} |
|
return false; |
|
} |
|
|
|
static const char * |
|
parse_do_targets(Lst paths, int *op, const char *line) |
|
{ |
|
const char *cp; |
|
|
|
do { |
|
for (cp = line; *cp && !isspace(*cp) && *cp != '(';) { |
|
if (*cp == '$') |
|
/* Must be a dynamic source (would have been |
|
* expanded otherwise), so call the Var module |
|
* to parse the puppy so we can safely advance |
|
* beyond it...There should be no errors in |
|
* this, as they would have been discovered in |
|
* the initial Var_Subst and we wouldn't be |
|
* here. */ |
|
Var_ParseSkip(&cp, NULL); |
|
else { |
|
if (found_delimiter(cp)) |
|
break; |
|
cp++; |
|
} |
|
} |
|
|
|
if (*cp == '(') { |
|
LIST temp; |
|
Lst_Init(&temp); |
|
/* Archives must be handled specially to make sure the |
|
* OP_ARCHV flag is set in their 'type' field, for one |
|
* thing, and because things like "archive(file1.o |
|
* file2.o file3.o)" are permissible. |
|
* Arch_ParseArchive will set 'line' to be the first |
|
* non-blank after the archive-spec. It creates/finds |
|
* nodes for the members and places them on the given |
|
* list, returning true if all went well and false if |
|
* there was an error in the specification. On error, |
|
* line should remain untouched. */ |
|
if (!Arch_ParseArchive(&line, &temp, NULL)) { |
|
Parse_Error(PARSE_FATAL, |
|
"Error in archive specification: \"%s\"", |
|
line); |
|
return NULL; |
|
} else { |
|
AppendList2Array(&temp, >argets); |
|
Lst_Destroy(&temp, NOFREE); |
|
cp = line; |
|
continue; |
|
} |
|
} |
|
if (*cp == '\0') { |
|
/* Ending a dependency line without an operator is a |
|
* Bozo no-no */ |
|
Parse_Error(PARSE_FATAL, "Need an operator"); |
|
return NULL; |
|
} |
|
/* |
|
* Have word in line. Get or create its nodes and stick it at |
|
* the end of the targets list |
|
*/ |
|
if (*line != '\0') |
|
add_target_nodes(line, cp); |
|
|
|
while (isspace(*cp)) |
|
cp++; |
|
line = cp; |
|
} while (*line != '!' && *line != ':' && *line); |
|
*op = handle_special_targets(paths); |
|
return cp; |
|
} |
|
|
|
static void |
|
dump_targets() |
|
{ |
|
size_t i; |
|
for (i = 0; i < gtargets.n; i++) |
|
fprintf(stderr, "%s", gtargets.a[i]->name); |
|
fprintf(stderr, "\n"); |
|
} |
|
|
|
static int |
|
handle_special_targets(Lst paths) |
|
{ |
|
size_t i; |
|
int seen_path = 0; |
|
int seen_special = 0; |
|
int seen_normal = 0; |
|
int type; |
|
|
|
for (i = 0; i < gtargets.n; i++) { |
|
type = gtargets.a[i]->special; |
|
if ((type & SPECIAL_MASK) == SPECIAL_PATH) { |
|
seen_path++; |
|
Lst_AtEnd(paths, find_suffix_path(gtargets.a[i])); |
|
} else if ((type & SPECIAL_TARGET) != 0) |
|
seen_special++; |
|
else |
|
seen_normal++; |
|
} |
|
if ((seen_path != 0) + (seen_special != 0) + (seen_normal != 0) > 1) { |
|
Parse_Error(PARSE_FATAL, "Wrong mix of special targets"); |
|
dump_targets(); |
|
specType = SPECIAL_ERROR; |
|
return 0; |
|
} |
|
if (seen_normal != 0) { |
|
specType = SPECIAL_NONE; |
|
return 0; |
|
} |
|
else if (seen_path != 0) { |
|
specType = SPECIAL_PATH; |
|
return 0; |
|
} else if (seen_special == 0) { |
|
specType = SPECIAL_NONE; |
|
return 0; |
|
} else if (seen_special != 1) { |
|
Parse_Error(PARSE_FATAL, "Mixing special targets is not allowed"); |
|
dump_targets(); |
|
return 0; |
|
} else if (seen_special == 1) { |
|
specType = gtargets.a[0]->special & SPECIAL_MASK; |
|
switch (specType) { |
|
case SPECIAL_MAIN: |
|
if (!Lst_IsEmpty(create)) { |
|
specType = SPECIAL_NONE; |
|
} |
|
break; |
|
case SPECIAL_NOTPARALLEL: |
|
{ |
|
extern int maxJobs; |
|
|
|
maxJobs = 1; |
|
break; |
|
} |
|
case SPECIAL_SINGLESHELL: |
|
compatMake = 1; |
|
break; |
|
case SPECIAL_ORDER: |
|
predecessor = NULL; |
|
break; |
|
default: |
|
break; |
|
} |
|
return gtargets.a[0]->special_op; |
|
} else { |
|
/* we're allowed to have 0 target */ |
|
specType = SPECIAL_NONE; |
|
return 0; |
|
} |
|
} |
|
|
/*- |
/*- |
*--------------------------------------------------------------------- |
*--------------------------------------------------------------------- |
* ParseDoDependency -- |
* ParseDoDependency -- |
|
|
*--------------------------------------------------------------------- |
*--------------------------------------------------------------------- |
*/ |
*/ |
static void |
static void |
ParseDoDependency(char *line) /* the line to parse */ |
ParseDoDependency(const char *line) /* the line to parse */ |
{ |
{ |
char *cp; /* our current position */ |
const char *cp; /* our current position */ |
GNode *gn; /* a general purpose temporary node */ |
int op; /* the operator on the line */ |
int op; /* the operator on the line */ |
LIST paths; /* List of search paths to alter when parsing |
char savec; /* a place to save a character */ |
* a list of .PATH targets */ |
LIST paths; /* List of search paths to alter when parsing |
int tOp; /* operator from special target */ |
* a list of .PATH targets */ |
|
int tOp; /* operator from special target */ |
|
tOp = 0; |
|
|
|
specType = Not; |
|
waiting = 0; |
|
Lst_Init(&paths); |
|
|
|
Array_Reset(&gsources); |
waiting = 0; |
|
Lst_Init(&paths); |
|
|
do { |
Array_Reset(&gsources); |
for (cp = line; *cp && !isspace(*cp) && *cp != '(';) |
|
if (*cp == '$') |
|
/* Must be a dynamic source (would have been expanded |
|
* otherwise), so call the Var module to parse the puppy |
|
* so we can safely advance beyond it...There should be |
|
* no errors in this, as they would have been discovered |
|
* in the initial Var_Subst and we wouldn't be here. */ |
|
Var_ParseSkip(&cp, NULL); |
|
else { |
|
/* 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 (*cp == ':' && *p == ':') |
cp = parse_do_targets(&paths, &tOp, line); |
p++; |
if (cp == NULL || specType == SPECIAL_ERROR) |
|
return; |
|
|
/* Found the best match already. */ |
/* Have now parsed all the target names. Must parse the operator next. |
if (isspace(*p) || *p == '\0') |
* The result is left in op . */ |
break; |
if (*cp == '!') { |
|
op = OP_FORCE; |
do { |
} else if (*cp == ':') { |
p += strcspn(p, "!:"); |
if (cp[1] == ':') { |
if (*p == '\0') |
op = OP_DOUBLEDEP; |
break; |
cp++; |
p++; |
} else { |
} while (!isspace(*p)); |
op = OP_DEPENDS; |
|
|
/* No better match later on... */ |
|
if (*p == '\0') |
|
break; |
|
|
|
} |
} |
cp++; |
} else { |
} |
Parse_Error(PARSE_FATAL, "Missing dependency operator"); |
if (*cp == '(') { |
|
LIST temp; |
|
Lst_Init(&temp); |
|
/* Archives must be handled specially to make sure the OP_ARCHV |
|
* flag is set in their 'type' field, for one thing, and because |
|
* things like "archive(file1.o file2.o file3.o)" are permissible. |
|
* Arch_ParseArchive will set 'line' to be the first non-blank |
|
* after the archive-spec. It creates/finds nodes for the members |
|
* and places them on the given list, returning true if all |
|
* went well and false if there was an error in the |
|
* specification. On error, line should remain untouched. */ |
|
if (!Arch_ParseArchive(&line, &temp, NULL)) { |
|
Parse_Error(PARSE_FATAL, |
|
"Error in archive specification: \"%s\"", line); |
|
return; |
return; |
} else { |
|
AppendList2Array(&temp, >argets); |
|
Lst_Destroy(&temp, NOFREE); |
|
continue; |
|
} |
|
} |
} |
savec = *cp; |
|
|
|
if (*cp == '\0') { |
cp++; /* Advance beyond operator */ |
/* Ending a dependency line without an operator is a Bozo no-no */ |
|
Parse_Error(PARSE_FATAL, "Need an operator"); |
|
return; |
|
} |
|
*cp = '\0'; |
|
/* Have a word in line. See if it's a special target and set |
|
* specType to match it. */ |
|
if (*line == '.' && isupper(line[1])) { |
|
/* See if the target is a special target that must have it |
|
* or its sources handled specially. */ |
|
int keywd = ParseFindKeyword(line); |
|
if (keywd != -1) { |
|
if (specType == ExPath && parseKeywords[keywd].spec != ExPath) { |
|
Parse_Error(PARSE_FATAL, "Mismatched special targets"); |
|
return; |
|
} |
|
|
|
specType = parseKeywords[keywd].spec; |
Array_FindP(>argets, ParseDoOp, op); |
tOp = parseKeywords[keywd].op; |
|
|
|
/* |
/* |
* Certain special targets have special semantics: |
* Get to the first source |
* .PATH Have to set the defaultPath |
*/ |
* variable too |
while (isspace(*cp)) |
* .MAIN Its sources are only used if |
cp++; |
* nothing has been specified to |
line = cp; |
* create. |
|
* .DEFAULT Need to create a node to hang |
/* |
* commands on, but we don't want |
* Several special targets take different actions if present with no |
* it in the graph, nor do we want |
* sources: |
* it to be the Main Target, so we |
* a .SUFFIXES line with no sources clears out all old suffixes |
* create it, set OP_NOTMAIN and |
* a .PRECIOUS line makes all targets precious |
* add it to the list, setting |
* a .IGNORE line ignores errors for all targets |
* DEFAULT to the new node for |
* a .SILENT line creates silence when making all targets |
* later use. We claim the node is |
* a .PATH removes all directories from the search path(s). |
* A transformation rule to make |
*/ |
* life easier later, when we'll |
if (!*line) { |
* use Make_HandleUse to actually |
|
* apply the .DEFAULT commands. |
|
* .PHONY The list of targets |
|
* .NOPATH Don't search for file in the path |
|
* .BEGIN |
|
* .END |
|
* .INTERRUPT Are not to be considered the |
|
* main target. |
|
* .NOTPARALLEL Make only one target at a time. |
|
* .SINGLESHELL Create a shell for each command. |
|
* .ORDER Must set initial predecessor to NULL |
|
*/ |
|
switch (specType) { |
switch (specType) { |
case ExPath: |
case SPECIAL_SUFFIXES: |
Lst_AtEnd(&paths, defaultPath); |
Suff_ClearSuffixes(); |
break; |
break; |
case Main: |
case SPECIAL_PRECIOUS: |
if (!Lst_IsEmpty(create)) { |
allPrecious = true; |
specType = Not; |
|
} |
|
break; |
break; |
case Begin: |
case SPECIAL_IGNORE: |
case End: |
ignoreErrors = true; |
case Interrupt: |
|
gn = Targ_FindNode(line, TARG_CREATE); |
|
gn->type |= OP_NOTMAIN; |
|
Array_AtEnd(>argets, gn); |
|
break; |
break; |
case Default: |
case SPECIAL_SILENT: |
gn = Targ_NewGN(".DEFAULT"); |
beSilent = true; |
gn->type |= OP_NOTMAIN|OP_TRANSFORM; |
|
Array_AtEnd(>argets, gn); |
|
DEFAULT = gn; |
|
break; |
break; |
case NotParallel: |
case SPECIAL_PATH: |
{ |
Lst_Every(&paths, ParseClearPath); |
extern int maxJobs; |
|
|
|
maxJobs = 1; |
|
break; |
break; |
} |
default: |
case SingleShell: |
|
compatMake = 1; |
|
break; |
break; |
case Order: |
|
predecessor = NULL; |
|
break; |
|
default: |
|
break; |
|
} |
} |
} else if (strncmp(line, ".PATH", 5) == 0) { |
} else if (specType == SPECIAL_MFLAGS) { |
/* |
/*Call on functions in main.c to deal with these arguments */ |
* .PATH<suffix> has to be handled specially. |
Main_ParseArgLine(line); |
* Call on the suffix module to give us a path to |
return; |
* modify. |
} else if (specType == SPECIAL_NOTPARALLEL || |
*/ |
specType == SPECIAL_SINGLESHELL) { |
Lst path; |
return; |
|
|
specType = ExPath; |
|
path = Suff_GetPath(&line[5]); |
|
if (path == NULL) { |
|
Parse_Error(PARSE_FATAL, |
|
"Suffix '%s' not defined (yet)", |
|
&line[5]); |
|
return; |
|
} else { |
|
Lst_AtEnd(&paths, path); |
|
} |
|
} |
|
} |
} |
|
|
/* |
/* |
* Have word in line. Get or create its node and stick it at |
* NOW GO FOR THE SOURCES |
* the end of the targets list |
|
*/ |
*/ |
if (specType == Not && *line != '\0') { |
if (specType == SPECIAL_SUFFIXES || specType == SPECIAL_PATH || |
add_target_nodes(line); |
specType == SPECIAL_INCLUDES || specType == SPECIAL_LIBS || |
} else if (specType == ExPath && *line != '.' && *line != '\0') |
specType == SPECIAL_NULL) { |
Parse_Error(PARSE_WARNING, "Extra target (%s) ignored", line); |
while (*line) { |
|
/* |
|
* If the target was one that doesn't take files as its |
|
* sources but takes something like suffixes, we take each |
|
* space-separated word on the line as a something and deal |
|
* with it accordingly. |
|
* |
|
* If the target was .SUFFIXES, we take each source as a |
|
* suffix and add it to the list of suffixes maintained by |
|
* the Suff module. |
|
* |
|
* If the target was a .PATH, we add the source as a |
|
* directory to search on the search path. |
|
* |
|
* If it was .INCLUDES, the source is taken to be the |
|
* suffix of files which will be #included and whose search |
|
* path should be present in the .INCLUDES variable. |
|
* |
|
* If it was .LIBS, the source is taken to be the suffix of |
|
* files which are considered libraries and whose search |
|
* path should be present in the .LIBS variable. |
|
* |
|
* If it was .NULL, the source is the suffix to use when a |
|
* file has no valid suffix. |
|
*/ |
|
while (*cp && !isspace(*cp)) |
|
cp++; |
|
switch (specType) { |
|
case SPECIAL_SUFFIXES: |
|
Suff_AddSuffixi(line, cp); |
|
break; |
|
case SPECIAL_PATH: |
|
{ |
|
LstNode ln; |
|
|
*cp = savec; |
for (ln = Lst_First(&paths); ln != NULL; |
/* |
ln = Lst_Adv(ln)) |
* If it is a special type and not .PATH, it's the only target we |
Dir_AddDiri((Lst)Lst_Datum(ln), line, cp); |
* allow on this line... |
break; |
*/ |
} |
if (specType != Not && specType != ExPath) { |
case SPECIAL_INCLUDES: |
bool warn = false; |
Suff_AddIncludei(line, cp); |
|
break; |
while (*cp != '!' && *cp != ':' && *cp) { |
case SPECIAL_LIBS: |
if (*cp != ' ' && *cp != '\t') { |
Suff_AddLibi(line, cp); |
warn = true; |
break; |
|
case SPECIAL_NULL: |
|
Suff_SetNulli(line, cp); |
|
break; |
|
default: |
|
break; |
|
} |
|
if (*cp != '\0') |
|
cp++; |
|
while (isspace(*cp)) |
|
cp++; |
|
line = cp; |
} |
} |
cp++; |
Lst_Destroy(&paths, NOFREE); |
} |
|
if (warn) { |
|
Parse_Error(PARSE_WARNING, "Extra target ignored"); |
|
} |
|
} else { |
} else { |
while (isspace(*cp)) { |
while (*line) { |
cp++; |
/* |
} |
* The targets take real sources, so we must beware of |
} |
* archive specifications (i.e. things with left |
line = cp; |
* parentheses in them) and handle them accordingly. |
} while (*line != '!' && *line != ':' && *line); |
*/ |
|
while (*cp && !isspace(*cp)) { |
|
if (*cp == '(' && cp > line && cp[-1] != '$') { |
|
/* |
|
* Only stop for a left parenthesis if |
|
* it isn't at the start of a word |
|
* (that'll be for variable changes |
|
* later) and isn't preceded by a |
|
* dollar sign (a dynamic source). |
|
*/ |
|
break; |
|
} else { |
|
cp++; |
|
} |
|
} |
|
|
if (!Array_IsEmpty(>argets)) { |
if (*cp == '(') { |
switch (specType) { |
GNode *gn; |
default: |
LIST sources; /* list of archive source |
Parse_Error(PARSE_WARNING, "Special and mundane targets don't mix. Mundane ones ignored"); |
* names after expansion */ |
break; |
|
case Default: |
|
case Begin: |
|
case End: |
|
case Interrupt: |
|
/* These four create nodes on which to hang commands, so |
|
* targets shouldn't be empty... */ |
|
case Not: |
|
/* Nothing special here -- targets can be empty if it wants. */ |
|
break; |
|
} |
|
} |
|
|
|
/* Have now parsed all the target names. Must parse the operator next. The |
Lst_Init(&sources); |
* result is left in op . */ |
if (!Arch_ParseArchive(&line, &sources, NULL)) { |
if (*cp == '!') { |
Parse_Error(PARSE_FATAL, |
op = OP_FORCE; |
"Error in source archive spec \"%s\"", |
} else if (*cp == ':') { |
line); |
if (cp[1] == ':') { |
return; |
op = OP_DOUBLEDEP; |
} |
cp++; |
|
} else { |
|
op = OP_DEPENDS; |
|
} |
|
} else { |
|
Parse_Error(PARSE_FATAL, "Missing dependency operator"); |
|
return; |
|
} |
|
|
|
cp++; /* Advance beyond operator */ |
while ((gn = (GNode *)Lst_DeQueue(&sources)) != |
|
NULL) |
|
ParseDoSrc(>argets, &gsources, tOp, |
|
gn->name, NULL); |
|
cp = line; |
|
} else { |
|
const char *endSrc = cp; |
|
|
Array_Find(>argets, ParseDoOp, op); |
ParseDoSrc(>argets, &gsources, tOp, line, |
|
endSrc); |
/* |
if (*cp) |
* Get to the first source |
cp++; |
*/ |
} |
while (isspace(*cp)) { |
while (isspace(*cp)) |
cp++; |
cp++; |
} |
line = cp; |
line = cp; |
|
|
|
/* |
|
* Several special targets take different actions if present with no |
|
* sources: |
|
* a .SUFFIXES line with no sources clears out all old suffixes |
|
* a .PRECIOUS line makes all targets precious |
|
* a .IGNORE line ignores errors for all targets |
|
* a .SILENT line creates silence when making all targets |
|
* a .PATH removes all directories from the search path(s). |
|
*/ |
|
if (!*line) { |
|
switch (specType) { |
|
case Suffixes: |
|
Suff_ClearSuffixes(); |
|
break; |
|
case Precious: |
|
allPrecious = true; |
|
break; |
|
case Ignore: |
|
ignoreErrors = true; |
|
break; |
|
case Silent: |
|
beSilent = true; |
|
break; |
|
case ExPath: |
|
Lst_Every(&paths, ParseClearPath); |
|
break; |
|
default: |
|
break; |
|
} |
|
} else if (specType == MFlags) { |
|
/* |
|
* Call on functions in main.c to deal with these arguments and |
|
* set the initial character to a null-character so the loop to |
|
* get sources won't get anything |
|
*/ |
|
Main_ParseArgLine(line); |
|
*line = '\0'; |
|
} else if (specType == NotParallel || specType == SingleShell) { |
|
*line = '\0'; |
|
} |
|
|
|
/* |
|
* NOW GO FOR THE SOURCES |
|
*/ |
|
if (specType == Suffixes || specType == ExPath || |
|
specType == Includes || specType == Libs || |
|
specType == Null) { |
|
while (*line) { |
|
/* |
|
* If the target was one that doesn't take files as its sources |
|
* but takes something like suffixes, we take each |
|
* space-separated word on the line as a something and deal |
|
* with it accordingly. |
|
* |
|
* If the target was .SUFFIXES, we take each source as a |
|
* suffix and add it to the list of suffixes maintained by the |
|
* Suff module. |
|
* |
|
* If the target was a .PATH, we add the source as a directory |
|
* to search on the search path. |
|
* |
|
* If it was .INCLUDES, the source is taken to be the suffix of |
|
* files which will be #included and whose search path should |
|
* be present in the .INCLUDES variable. |
|
* |
|
* If it was .LIBS, the source is taken to be the suffix of |
|
* files which are considered libraries and whose search path |
|
* should be present in the .LIBS variable. |
|
* |
|
* If it was .NULL, the source is the suffix to use when a file |
|
* has no valid suffix. |
|
*/ |
|
char savec; |
|
while (*cp && !isspace(*cp)) { |
|
cp++; |
|
} |
|
savec = *cp; |
|
*cp = '\0'; |
|
switch (specType) { |
|
case Suffixes: |
|
Suff_AddSuffix(line); |
|
break; |
|
case ExPath: |
|
Lst_ForEach(&paths, ParseAddDir, line); |
|
break; |
|
case Includes: |
|
Suff_AddInclude(line); |
|
break; |
|
case Libs: |
|
Suff_AddLib(line); |
|
break; |
|
case Null: |
|
Suff_SetNull(line); |
|
break; |
|
default: |
|
break; |
|
} |
|
*cp = savec; |
|
if (savec != '\0') { |
|
cp++; |
|
} |
|
while (isspace(*cp)) { |
|
cp++; |
|
} |
|
line = cp; |
|
} |
|
Lst_Destroy(&paths, NOFREE); |
|
} else { |
|
while (*line) { |
|
/* |
|
* The targets take real sources, so we must beware of archive |
|
* specifications (i.e. things with left parentheses in them) |
|
* and handle them accordingly. |
|
*/ |
|
while (*cp && !isspace(*cp)) { |
|
if (*cp == '(' && cp > line && cp[-1] != '$') { |
|
/* |
|
* Only stop for a left parenthesis if it isn't at the |
|
* start of a word (that'll be for variable changes |
|
* later) and isn't preceded by a dollar sign (a dynamic |
|
* source). |
|
*/ |
|
break; |
|
} else { |
|
cp++; |
|
} |
} |
} |
|
|
|
if (*cp == '(') { |
|
GNode *gn; |
|
LIST sources; /* list of archive source names after |
|
* expansion */ |
|
|
|
Lst_Init(&sources); |
|
if (!Arch_ParseArchive(&line, &sources, NULL)) { |
|
Parse_Error(PARSE_FATAL, |
|
"Error in source archive spec \"%s\"", line); |
|
return; |
|
} |
|
|
|
while ((gn = (GNode *)Lst_DeQueue(&sources)) != NULL) |
|
ParseDoSrc(tOp, gn->name); |
|
cp = line; |
|
} else { |
|
if (*cp) { |
|
*cp = '\0'; |
|
cp++; |
|
} |
|
|
|
ParseDoSrc(tOp, line); |
|
} |
|
while (isspace(*cp)) { |
|
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. */ |
Array_Find(>argets, ParseFindMain, NULL); |
Array_Find(>argets, ParseFindMain, NULL); |
} |
} |
|
|
/* Finally, destroy the list of sources. */ |
|
} |
} |
|
|
/*- |
/*- |
|
|
ParseHasCommands(void *gnp) /* Node to examine */ |
ParseHasCommands(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; |
} |
|
} |
} |
|
|
|
|
|
|
/* Strip comments from line. Build a copy in buffer if necessary, */ |
/* Strip comments from line. Build a copy in buffer if necessary, */ |
static char * |
static char * |
strip_comments(Buffer copy, const char *line) |
strip_comments(Buffer copy, const char *line) |
|
|
} |
} |
} |
} |
|
|
|
|
static bool |
static bool |
handle_for_loop(Buffer linebuf, const char *line) |
handle_for_loop(Buffer linebuf, const char *line) |
{ |
{ |
|
|
return true; |
return true; |
} |
} |
|
|
|
/* global hub for the construct */ |
static bool |
static bool |
handle_bsd_command(Buffer linebuf, Buffer copy, const char *line) |
handle_bsd_command(Buffer linebuf, Buffer copy, const char *line) |
{ |
{ |
|
|
static void |
static void |
finish_commands(struct growableArray *targets) |
finish_commands(struct growableArray *targets) |
{ |
{ |
Array_Every(targets, Suff_EndTransform); |
|
Array_Every(targets, ParseHasCommands); |
Array_Every(targets, ParseHasCommands); |
Array_Reset(targets); |
Array_Reset(targets); |
} |
} |
|
|
mainNode = NULL; |
mainNode = NULL; |
Static_Lst_Init(userIncludePath); |
Static_Lst_Init(userIncludePath); |
Static_Lst_Init(systemIncludePath); |
Static_Lst_Init(systemIncludePath); |
Array_Init(&gsources, SOURCES_SIZE); |
|
Array_Init(>argets, TARGETS_SIZE); |
Array_Init(>argets, TARGETS_SIZE); |
|
Array_Init(&gsources, SOURCES_SIZE); |
|
create_special_nodes(); |
|
|
LowParse_Init(); |
LowParse_Init(); |
#ifdef CLEANUP |
#ifdef CLEANUP |
|
|
Parse_MainName(Lst listmain) /* result list */ |
Parse_MainName(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); |
} |
} |
|
|