dmake/dmake/unix/gno/rulparse.c

1313 lines
39 KiB
C

/* RCS -- $Header: /u2/dvadura/src/generic/dmake/src/RCS/rulparse.c,v 1.1 1992/01/24 03:27:57 dvadura Exp $
-- SYNOPSIS -- perform semantic analysis on input
--
-- DESCRIPTION
-- This code performs semantic analysis on the input, and builds
-- the complex internal datastructure that is used to represent
-- the user makefile.
--
-- AUTHOR
-- Dennis Vadura, dvadura@watdragon.uwaterloo.ca
-- CS DEPT, University of Waterloo, Waterloo, Ont., Canada
--
-- COPYRIGHT
-- Copyright (c) 1990 by Dennis Vadura. All rights reserved.
--
-- This program is free software; you can redistribute it and/or
-- modify it under the terms of the GNU General Public License
-- (version 1), as published by the Free Software Foundation, and
-- found in the file 'LICENSE' included with this distribution.
--
-- This program is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warrant of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-- GNU General Public License for more details.
--
-- You should have received a copy of the GNU General Public License
-- along with this program; if not, write to the Free Software
-- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
--
-- LOG
-- $Log: rulparse.c,v $
* Revision 1.1 1992/01/24 03:27:57 dvadura
* dmake Version 3.8, Initial revision
*
*/
#include "extern.h"
/* prototypes for local functions */
static void _add_global_prereq ANSI((CELLPTR));
static int _add_root ANSI((CELLPTR));
static void _build_graph ANSI((int, CELLPTR, CELLPTR));
static char* _build_meta ANSI((char*));
static int _do_magic ANSI((int, char*, CELLPTR, CELLPTR, t_attr, char*));
static void _do_special ANSI((int, int, t_attr,char*,CELLPTR,CELLPTR,int*));
static int _do_targets ANSI((int, t_attr, char*, CELLPTR, CELLPTR));
static t_attr _is_attribute ANSI((char*));
static int _is_special ANSI((char*));
static char* _is_magic ANSI((char*));
static int _is_percent ANSI((char*));
static CELLPTR _make_multi ANSI((CELLPTR));
static CELLPTR _replace_cell ANSI((CELLPTR,CELLPTR,CELLPTR));
static void _set_attributes ANSI((t_attr, char*, CELLPTR ));
static void _stick_at_head ANSI((CELLPTR, CELLPTR));
static void _set_global_attr ANSI((t_attr));
/* static variables that must persist across invocation of Parse_rule_def */
static CELLPTR _sv_targets = NIL(CELL);
static STRINGPTR _sv_rules = NIL(STRING);
static STRINGPTR _sv_crule = NIL(STRING);
static CELLPTR _sv_edgel = NIL(CELL);
static LINKPTR _sv_glb_prq = NIL(LINK);
static int _sp_target = FALSE;
static t_attr _sv_attr;
static int _sv_flag;
static int _sv_op;
static char *_sv_setdir;
static char _sv_globprq_only = 0;
/* Define for global attribute mask */
#define A_GLOB (A_PRECIOUS | A_SILENT | A_IGNORE | A_EPILOG | A_SWAP |\
A_SHELL | A_PROLOG | A_NOINFER | A_SEQ | A_MKSARGS )
PUBLIC int
Parse_rule_def( int *state )/*
=========================
Parse the rule definition contained in Buffer, and modify the state
if appropriate. The function returns 0, if the definition is found to
be an illegal rule definition, and it returns 1 if it is a rule definition.
*/
{
TKSTR input; /* input string struct for token search */
CELLPTR targets; /* list of targets if any */
CELLPTR prereq; /* list of prereq if any */
CELLPTR prereqtail; /* tail of prerequisite list */
CELLPTR cp; /* temporary cell pointer for list making */
char *result; /* temporary storage for result */
char *tok; /* temporary pointer for tokens */
char *set_dir; /* value of setdir attribute */
char *brk; /* break char list for Get_token */
char *firstrcp; /* first recipe line, from ; in rule line */
t_attr attr; /* sum of attribute flags for current tgts*/
t_attr at; /* temp place to keep an attribute code */
int op; /* rule operator */
int special; /* indicate special targets in rule */
int percent; /* indicate percent rule target */
int mixed_glob_prq; /* indicate mixed %-rule prereq possible */
DB_ENTER( "Parse_rule_def" );
op = 0;
attr = 0;
special = 0;
percent = 0;
set_dir = NIL( char );
targets = NIL(CELL);
prereq = NIL(CELL);
prereqtail = NIL(CELL);
mixed_glob_prq = 0;
/*
* Check to see if the line is of the form:
* targets : prerequisites; first recipe line
* If so remember the first_recipe part of the line.
*/
firstrcp = strchr( Buffer, ';' );
if( firstrcp != NIL( char ) ) {
*firstrcp++ = 0;
firstrcp = _strspn( firstrcp, " \t" );
}
result = Expand( Buffer );
for( brk=strchr(result,'\\'); brk != NIL(char); brk=strchr(brk,'\\') )
if( brk[1] == '\n' )
*brk = ' ';
else
brk++;
DB_PRINT( "par", ("Scanning: [%s]", result) );
SET_TOKEN( &input, result );
brk = ":-^!";
Def_targets = TRUE;
/* Scan the input rule line collecting targets, the operator, and any
* prerequisites. Stop when we run out of targets and prerequisites. */
while( *(tok = Get_token( &input, brk, TRUE )) != '\0' )
if( !op ) {
/* we are scanning targets and attributes
* check to see if token is an operator. */
op = Rule_op( tok );
if( !op ) {
/* define a new cell, or get old cell */
cp = Def_cell( tok );
DB_PRINT( "par", ("tg_cell [%s]", tok) );
if((at = _is_attribute( tok ))) {
/* Logically OR the attributes specified into one main
* ATTRIBUTE mask. */
if( at == A_SETDIR )
if( set_dir != NIL( char ) )
Warning( "Multiple .SETDIR attribute ignored" );
else
set_dir = _strdup( tok );
attr |= at;
}
else {
int tmp;
tmp = _is_special( tok );
if( _is_percent( tok ) ) percent++;
if( percent )
if( targets != NIL(CELL) )
Fatal( "Multiple targets are not allowed in %% rules" );
else
cp->ce_flag |= F_PERCENT;
if( special )
Fatal( "Special target must appear alone", tok );
else if( !(cp->ce_flag & F_MARK) ) {
cp->ce_link = targets; /* targets are stacked in this list*/
cp->ce_flag |= F_MARK | F_EXPLICIT;
targets = cp;
special = tmp;
}
else if( !(cp->ce_attr & A_LIBRARY) )
Warning("Duplicate entry [%s] in target list",cp->CE_NAME);
}
}
else {
/* found an operator so empty out break list
* and clear mark bits on target list, setting them all to F_USED */
brk = "";
for( cp=targets; cp != NIL(CELL); cp=cp->ce_link ) {
cp->ce_flag ^= F_MARK;
cp->ce_flag |= F_USED;
}
Def_targets = FALSE;
}
}
else {
/* Scanning prerequisites so build the prerequisite list. We use
* F_MARK flag to make certain we have only a single copy of the
* prerequisite in the list */
cp = Def_cell( tok );
if( _is_percent( tok ) ) {
if( !percent && !attr )
Fatal( "Syntax error in %% rule, missing %% target");
mixed_glob_prq = 1;
}
if( cp->ce_flag & F_USED ) {
if( cp->ce_attr & A_COMPOSITE )
continue;
else
Fatal( "Detected circular dependency in graph at [%s]",
cp->CE_NAME );
}
else if( !(cp->ce_flag & F_MARK) ) {
DB_PRINT( "par", ("pq_cell [%s]", tok) );
cp->ce_flag |= F_MARK;
if( prereqtail == NIL(CELL) ) /* keep prereq's in order */
prereq = cp;
else
prereqtail->ce_link = cp;
prereqtail = cp;
cp->ce_link = NIL(CELL);
}
else if( !(cp->ce_attr & A_LIBRARY) )
Warning("Duplicate entry [%s] in prerequisite list",cp->CE_NAME);
}
/* endwhile */
/* Check to see if we have a percent rule that has only global
* prerequisites. If so then set the flag so that later on, we don't issue
* an error if such targets supply an empty set of rules. */
if( percent && !mixed_glob_prq && (prereq != NIL(CELL)) )
_sv_globprq_only = 1;
/* It's ok to have targets with attributes, and no prerequisites, but it's
* not ok to have no targets and no attributes, or no operator */
if( !op ) {
CLEAR_TOKEN( &input );
DB_PRINT( "par", ("Not a rule [%s]", Buffer) );
DB_RETURN( 0 );
}
if( !attr && targets == NIL(CELL) ) {
Fatal( "Missing targets or attributes in rule" );
if( set_dir != NIL( char )) FREE( set_dir );
DB_RETURN( 0 );
}
/* We have established we have a legal rules line, so we must process it.
* In doing so we must handle any special targets. Special targets must
* appear alone possibly accompanied by attributes.
* NOTE: special != 0 ==> targets != NIL(CELL) */
if( prereqtail != NIL(CELL) ) prereqtail->ce_link = NIL(CELL);
/* Clear out MARK bits used in duplicate checking. I originally wanted
* to do this as the lists get processed but that got too error prone
* so I bit the bullit and added these two loops. */
for( cp=prereq; cp != NIL(CELL); cp=cp->ce_link ) cp->ce_flag &= ~F_MARK;
for( cp=targets; cp != NIL(CELL); cp=cp->ce_link ) cp->ce_flag &= ~F_USED;
/* Check to see if the previous rule line was bound if, not the call
* Bind_rules_to_targets to go and bind the line */
if( _sv_rules != NIL(STRING) ) Bind_rules_to_targets( F_DEFAULT );
/* Add the first recipe line to the list */
if( firstrcp != NIL( char ) )
Add_recipe_to_list( firstrcp, TRUE, FALSE );
/* Save these prior to calling _do_targets, since _build_graph needs the
* _sv_setdir value for matching edges. */
_sv_op = op;
_sv_setdir = set_dir;
if( special )
_do_special( special, op, attr, set_dir, targets, prereq, state );
else
*state = _do_targets( op, attr, set_dir, targets, prereq );
DB_RETURN( 1 );
}
PUBLIC int
Rule_op( char *op )/*
================
Check the passed in op string and map it to one of the rule operators */
{
int ret = 0;
DB_ENTER( "rule_op" );
if( *op == TGT_DEP_SEP ) {
ret = R_OP_CL;
op++;
/* All rule operations begin with a :, but may include any one of the
* four modifiers. In order for the rule to be properly mapped we must
* check for each of the modifiers in turn, building up our return bit
* string. */
while( *op && ret )
switch( *op ) {
case ':': ret |= R_OP_DCL; op++; break;
case '!': ret |= R_OP_BG; op++; break;
case '^': ret |= R_OP_UP; op++; break;
case '-': ret |= R_OP_MI; op++; break;
default : ret = 0; /* an invalid modifier, chuck whole string */
}
if( *op != '\0' ) ret = 0;
}
DB_RETURN( ret );
}
PUBLIC void
Add_recipe_to_list( char *rule, int white_too, int no_check )/*
=================================================
Take the provided string and add it to the list of recipe lines
we are saving to be added to the list of targets we have built
previously. If white_too == TRUE add the rule EVEN IF it contains only
whitespace. */
{
DB_ENTER( "Add_recipe_to_list" );
if( rule != NIL( char ) && (*rule != '\0' || white_too) ) {
DB_PRINT( "par", ("Adding recipe [%s]", rule) );
_sv_crule = Def_recipe( rule, _sv_crule, white_too, no_check );
if( _sv_rules == NIL(STRING) )
_sv_rules = _sv_crule;
}
DB_VOID_RETURN;
}
PUBLIC void
Bind_rules_to_targets( int flag )/*
===============================
Take the rules we have defined and bind them with proper attributes
to the targets that were previously defined in the parse. The
attributes that get passed here are merged with those that are were
previously defined. (namely F_SINGLE) */
{
CELLPTR tg; /* pointer to current target in list */
LINKPTR lp; /* pointer to link cell */
int magic; /* TRUE if target is .xxx.yyy form */
int tflag; /* TRUE if we assigned targets here */
DB_ENTER( "Bind_rules_to_targets" );
/* This line is needed since Parse may call us twice when the last
* GROUP rule appears at the end of file. In this case the rules
* have already been bound and we want to ignore them. */
if( _sv_targets == NIL(CELL) ) { DB_VOID_RETURN; }
tflag = FALSE;
flag |= (_sv_flag & F_SINGLE);
for( tg = _sv_targets; tg != NIL(CELL); tg = tg->ce_link ) {
DB_PRINT( "par", ("Binding to %s, %04x", tg->CE_NAME, tg->ce_flag) );
magic = tg->ce_flag & F_PERCENT;
/* Check to see if we had a rule of the form '%.o : a.h b.h ; xxx'
* In which case we must build a NULL prq node to hold the recipe */
if( _sv_globprq_only && (_sv_rules != NIL(STRING)) )
_build_graph( _sv_op, tg, NIL(CELL) );
/* NOTE: For targets that are magic we ignore any previously defined
* rules. ie. We throw away the old definition and use the new. */
if( !(tg->ce_flag & F_MULTI) && !magic && (tg->CE_RECIPE != NIL(STRING))
&& !_sp_target && (_sv_rules != NIL(STRING)) )
Fatal( "Multiply defined recipe for target %s", tg->CE_NAME );
if( (magic || _sp_target) && (_sv_rules == NIL(STRING)) &&
!(tg->ce_flag & F_SPECIAL) && !_sv_globprq_only )
Warning( "Empty recipe for special target %s", tg->CE_NAME );
if( magic ) {
CELLPTR ep;
for( ep=_sv_edgel; ep != NIL(CELL); ep=ep->ce_link ) {
_set_attributes( _sv_attr, _sv_setdir, ep );
ep->ce_flag |= (F_TARGET|flag);
if( _sv_rules != NIL(STRING) ) {
ep->ce_recipe = _sv_rules;
ep->ce_indprq = _sv_glb_prq;
}
}
}
else {
tg->ce_attr |= _sv_attr;
tg->ce_flag |= flag;
if( _sv_rules != NIL(STRING) ) {
tg->ce_recipe = _sv_rules;
tg->ce_flag |= F_RULES | F_TARGET;
/* Bind the current set of prerequisites as belonging to the
* original recipe given for the target */
for( lp=tg->ce_prq; lp != NIL(LINK); lp = lp->cl_next )
if( !(lp->cl_flag & F_USED) ) lp->cl_flag |= F_TARGET;
}
else for( lp=tg->ce_prq; lp != NIL(LINK); lp = lp->cl_next )
lp->cl_flag |= F_USED;
}
tflag |= _add_root(tg);
}
if( tflag ) Target = TRUE;
if( _sv_setdir ) FREE(_sv_setdir);
_sv_rules = NIL(STRING);
_sv_crule = NIL(STRING);
_sv_targets = NIL(CELL);
_sv_glb_prq = NIL(LINK);
_sv_edgel = NIL(CELL);
_sp_target = FALSE;
_sv_globprq_only = 0;
DB_VOID_RETURN;
}
PUBLIC int
Set_group_attributes( char *list )/*
==============================
Scan list looking for the standard @ and - (as in recipe line defs)
and set the flags accordingly so that they apply when we bind the
rules to the appropriate targets. */
{
int res = (*_strspn(list,"@-%+ \t") == '[');
if( res ) _sv_attr |= Rcp_attribute(list);
return(res);
}
static void
_do_special(
/*
==================================================================
Process a special target. So far the only special targets we have
are those recognized by the _is_special function.
target is always only a single special target.
NOTE: For the cases of .IMPORT, and .INCLUDE, the cells created by the
parser are never freed. This is due to the fact that it is too much
trouble to get them out of the hash table once they are defined, and
if they are per chance used again it will be ok, anyway, since the
cell is not really used by the code below. */
int special,
int op,
t_attr attr,
char *set_dir,
CELLPTR target,
CELLPTR prereq,
int *state)
{
HASHPTR hp; /* pointer to macro def cell */
CELLPTR cp; /* temporary pointer into cells list */
CELLPTR dp; /* pointer to directory dir cell */
LINKPTR lp; /* pointer at prerequisite list */
char *dir; /* current dir to prepend */
char *path; /* resulting path to try to read */
char *name; /* File name for processing a .INCLUDE */
char *tmp; /* temporary string pointer */
FILE *fil; /* File descriptor returned by Openfile */
DB_ENTER( "_do_special" );
target->ce_flag = F_SPECIAL; /* mark the target as special */
switch( special ) {
case ST_EXPORT:
for( ; prereq != NIL(CELL); prereq = prereq->ce_link ) {
DB_PRINT( "par", ("Exporting [%s]", prereq->CE_NAME) );
hp = GET_MACRO( prereq->CE_NAME );
if( hp != NIL(HASH) ) {
char *tmpstr = hp->ht_value;
if( tmpstr == NIL(char) ) tmpstr = "";
if( Write_env_string( prereq->CE_NAME, tmpstr ) != 0 )
Warning( "Could not export %s", prereq->CE_NAME );
}
}
break;
case ST_IMPORT:
for( ; prereq != NIL(CELL); prereq = prereq->ce_link ) {
char *tmpstr;
DB_PRINT( "par", ("Importing [%s]", prereq->CE_NAME) );
if( strcmp(prereq->CE_NAME, ".EVERYTHING") == 0 ) {
t_attr sattr = Glob_attr;
Glob_attr |= A_SILENT;
ReadEnvironment();
Glob_attr = sattr;
}
else {
tmpstr = Read_env_string( prereq->CE_NAME );
if( tmpstr != NIL(char) )
Def_macro( prereq->CE_NAME,tmpstr,M_EXPANDED | M_LITERAL);
else
if( !((Glob_attr | attr) & A_IGNORE) )
Fatal("Imported macro `%s' not found",prereq->CE_NAME);
}
}
attr &= ~A_IGNORE;
break;
case ST_INCLUDE:
{
int ignore = (((Glob_attr | attr) & A_IGNORE) != 0);
int pushed = FALSE;
LINKPTR prqlnk = NIL(LINK);
LINKPTR prqlst = NIL(LINK);
if( prereq == NIL(CELL) ) Fatal( "No .INCLUDE file(s) specified" );
dp = Def_cell( ".INCLUDEDIRS" );
if( (attr & A_SETDIR) && *(dir = strchr(set_dir, '=')+1) )
pushed = Push_dir( dir, ".INCLUDE", ignore );
for( cp=prereq; cp != NIL(CELL); cp = cp->ce_link ) {
LINKPTR ltmp;
TALLOC(ltmp, 1, LINK);
ltmp->cl_prq = cp;
if( prqlnk == NIL(LINK) )
prqlst = ltmp;
else
prqlnk->cl_next = ltmp;
prqlnk = ltmp;
}
for( ; prqlst != NIL(LINK); FREE(prqlst), prqlst=prqlnk ) {
prqlnk = prqlst->cl_next;
cp = prqlst->cl_prq;
name = cp->CE_NAME;
if( *name == '<' ) {
/* We have a file name enclosed in <....>
* so get rid of the <> arround the file name */
name++;
if( (tmp = strrchr( name, '>' )) != NIL( char ) )
*tmp = 0;
if( If_root_path( name ) )
fil = Openfile( name, FALSE, FALSE );
else
fil = NIL(FILE);
}
else
fil = Openfile( name, FALSE, FALSE );
if( fil == NIL(FILE) ) { /*if true ==> not found in current dir*/
/* Now we must scan the list of prerequisites for .INCLUDEDIRS
* looking for the file in each of the specified directories.
* if we don't find it then we issue an error. The error
* message is suppressed if the .IGNORE attribute of attr is
* set. If a file is found we call Parse on the file to
* perform the parse and then continue on from where we left
* off. */
for(lp=dp->CE_PRQ; lp && fil == NIL(FILE); lp=lp->cl_next) {
dir = lp->cl_prq->CE_NAME;
if( strchr(dir, '$') ) dir = Expand(dir);
path = Build_path( dir, name );
DB_PRINT( "par", ("Trying to include [%s]", path) );
fil = Openfile( path, FALSE, FALSE );
if( dir != lp->cl_prq->CE_NAME ) FREE(dir);
}
}
if( fil != NIL(FILE) )
Parse( fil );
else if( !((Glob_attr | attr) & A_IGNORE) )
Fatal( "Include file %s, not found", name );
}
if( pushed ) Pop_dir(FALSE);
attr &= ~(A_IGNORE|A_SETDIR);
}
break;
case ST_SOURCE:
/* case ST_SUFFIXES: */
if( prereq != NIL(CELL) )
_do_targets( op & (R_OP_CL | R_OP_MI | R_OP_UP), attr, set_dir,
target, prereq );
else {
/* The old semantics of .SOURCE were that an empty list of
* prerequisites clears the .SOURCE list. So we must implement
* that here as a clearout prerequisite operation. Since this is
* a standard operation with the :- opcode we can simply call the
* proper routine with the target cell and it should do the trick
*/
if( op == R_OP_CL || (op & R_OP_MI) )
Clear_prerequisites( target );
}
op &= ~(R_OP_MI | R_OP_UP);
break;
case ST_KEEP:
if( Keep_state != NIL(char) ) break;
Def_macro( ".KEEP_STATE", "_state.mk", M_EXPANDED );
break;
case ST_REST:
/* The rest of the special targets can all take rules, as such they
* must be able to affect the state of the parser. */
{
int s_targ = Target;
Target = TRUE;
_sp_target = TRUE;
*state = _do_targets( op, attr, set_dir, target, prereq );
Target = s_targ;
target->ce_flag |= F_TARGET;
attr = A_DEFAULT;
op = R_OP_CL;
}
break;
default:break;
}
if( op != R_OP_CL ) Warning( "Modifier(s) for operator ignored" );
if( attr != A_DEFAULT ) Warning( "Extra attributes ignored" );
DB_VOID_RETURN;
}
static int
_do_targets(
/*================================================= */
int op,
t_attr attr,
char *set_dir,
CELLPTR targets,
CELLPTR prereq)
{
CELLPTR tg1; /* temporary target pointer */
CELLPTR tp1; /* temporary prerequisite pointer */
char *p; /* temporary char pointer */
CELLPTR prev_cell; /* pointer for .UPDATEALL processing */
CELLPTR first_cell; /* pointer for .UPDATEALL processing */
int update; /* A_UPDATEALL attribute flag */
int smagic = 0; /* collective amount of magic :-) */
int tflag = FALSE; /* set to TRUE if we add target to root */
DB_ENTER( "_do_targets" );
if((update = ((attr & A_UPDATEALL) != 0)) )
if( targets == NIL(CELL) )
Fatal( ".UPDATEALL attribute requires non-empty list of targets" );
first_cell = prev_cell = NIL(CELL);
for( tg1 = targets; tg1 != NIL(CELL); tg1 = tg1->ce_link ) {
/* Check each target. Check for inconsistencies between :: and : rule
* sets. :: may follow either : or :: but not the reverse.
*
* Any targets that contain :: rules are represented by a prerequisite
* list hanging off the main target cell where each of the prerequisites
* is a copy of the target cell but is not entered into the hash table.
*/
int magic = (tg1->ce_flag & F_PERCENT) && !(tg1->ce_flag & F_MAGIC);
smagic |= magic;
if( !(op & R_OP_DCL ) && (tg1->ce_flag & F_MULTI) && !magic )
Fatal( "Inconsistency in inference rules for %s", tg1->CE_NAME );
if( magic )
do {
_build_graph( op, tg1, prereq );
if( prereq != NIL(CELL) ) prereq = prereq->ce_link;
} while( prereq != NIL(CELL) );
else if( !(tg1->ce_flag & F_SPECIAL) &&
(prereq == NIL(CELL)) &&
(p = _is_magic( tg1->CE_NAME )) != NIL(char))
smagic |= _do_magic( op, p, tg1, prereq, attr, set_dir );
else if( op & R_OP_DCL ) {
CELLPTR tmp_cell = _make_multi(tg1);
tflag |= _add_root(tg1);
targets = _replace_cell( targets, tg1, tmp_cell );
tg1 = tmp_cell;
}
if( !magic ) _set_attributes( attr, set_dir, tg1 );
if( update ) {
if( smagic ) Fatal( ".UPDATEALL attribute not legal in meta rule" );
/* Check this as it would break another circular .UPATEALL list if
* we blindly assign it and it is part of another list already. */
if( tg1->ce_all != NIL(CELL) )
Fatal( "Target [%s] appears on multiple .UPDATEALL lists" );
tg1->ce_all = prev_cell;
if( prev_cell == NIL(CELL) ) first_cell = tg1;
prev_cell = tg1;
}
/* Build the proper prerequisite list of the target. If the `-',
* modifier was used clear the prerequisite list before adding any
* new prerequisites. Else add them to the head/tail as appropriate.
*
* If the target has F_PERCENT set then no prerequisites are used. */
if( !(tg1->ce_flag & F_PERCENT) ) {
if( op & R_OP_MI ) Clear_prerequisites( tg1 );
if( (op & R_OP_UP) && (tg1->ce_prq != NIL(LINK)) )
_stick_at_head( tg1, prereq );
else for( tp1=prereq; tp1 != NIL(CELL); tp1 = tp1->ce_link )
Add_prerequisite( tg1, tp1, FALSE, FALSE );
}
else if( op & (R_OP_MI | R_OP_UP) )
Warning( "Modifier(s) `^!' for %-meta target ignored" );
}
if( tflag ) Target = TRUE;
if( first_cell != NIL(CELL) ) first_cell->ce_all = prev_cell;
/* Check to see if we have NO targets but some attributes. IF so then
* apply all of the attributes to the complete list of prerequisites.
* Cannot happen for F_PERCENT targets. (ie. in that case targets is always
* not NIL) */
if( (targets == NIL(CELL)) && attr )
if( prereq != NIL(CELL) )
for( tp1=prereq; tp1 != NIL(CELL); tp1 = tp1->ce_link )
_set_attributes( attr, set_dir, tp1 );
else
_set_global_attr( attr );
/* Fix up the ce_link pointers so that when we go to attach a recipe in
* Bind_targets to rules we get the right thing if it's an .UPDATEALL ::
* recipe */
if( update ) {
for( tp1=NIL(CELL),tg1=prev_cell; tg1!=first_cell; tg1=tg1->ce_all ) {
tg1->ce_link = tp1;
tp1 = tg1;
}
tg1->ce_link = tp1;
targets = first_cell;
}
/* Now that we have built the lists of targets, the parser must parse the
* rules if there are any. However we must start the rule list with the
* rule specified as via the ; kludge, if there is one */
_sv_targets = targets;
_sv_attr = attr;
_sv_flag = ((op & R_OP_BG) ? F_SINGLE : F_DEFAULT);
DB_RETURN( RULE_SCAN );
}
static int
_do_magic(
/*
=====================================================
This function takes a magic target of the form .<chars>.<chars> or
.<chars> and builds the appropriate % rules for that target.
The function builds the % rule, `%.o : %.c' from .c.o, and
`%.a :' from .a */
int op,
char *dot,
CELLPTR target,
CELLPTR prereq,
t_attr attr,
char *set_dir)
{
CELLPTR tg;
CELLPTR prq;
char *tmp, *tmp2;
DB_ENTER( "_do_magic" );
if( prereq != NIL(CELL) )
Warning( "Ignoring prerequisites of old style meta-target" );
if( dot == target->CE_NAME ) { /* its of the form .a */
tg = Def_cell( "%" ); /* ==> no prerequisite */
tmp = _build_meta( target->CE_NAME );
prq = Def_cell( tmp );
FREE( tmp );
_build_graph( op, tg, prq );
}
else {
tmp = _build_meta( dot );
tg = Def_cell( tmp );
FREE( tmp );
tmp = _build_meta( tmp2 = _substr( target->CE_NAME, dot ) );
prq = Def_cell( tmp );
FREE( tmp );
FREE( tmp2 );
_build_graph( op, tg, prq );
}
tg->ce_flag |= F_PERCENT;
target->ce_flag |= (F_MAGIC|F_PERCENT);
_set_attributes( attr, set_dir, tg );
DB_RETURN(1);
}
static CELLPTR
_replace_cell( CELLPTR lst, CELLPTR cell, CELLPTR rep )
{
register CELLPTR tp;
if( lst == cell ) {
rep->ce_link = lst->ce_link;
lst = rep;
}
else {
for( tp=lst; tp->ce_link != cell; tp=tp->ce_link );
rep->ce_link = tp->ce_link->ce_link;
tp->ce_link = rep;
}
return(lst);
}
static char *
_build_meta( char *name )/*
=====================
Check to see if the name is of the form .c~ if so and if Augmake
translation is enabled then return s.%.c, else return %.suff, where if the
suffix ends in '~' then leave it be.*/
{
char *tmp;
int test = Augmake ? name[strlen(name)-1] == '~' : 0;
tmp = _strjoin( test ? "s.%" : "%", name, -1, FALSE);
if( test ) tmp[ strlen(tmp)-1 ] = '\0';
return(tmp);
}
static void
_build_graph( int op, CELLPTR target, CELLPTR prereq )/*
====================================
This function is called to build the graph for the % rule given by
target : prereq cell combination. This function assumes that target
is a % target and that prereq is a single % prerequisite. R_OP_CL
rules replace existing rules if any, only R_OP_CL works for meta-rules.
%.o :: %.c is meaningless.
It also assumes that target cell has F_PERCENT set already. */
{
LINKPTR edl;
CELLPTR edge;
int match;
DB_ENTER( "_build_graph" );
DB_PRINT( "%", ("Building graph for [%s : %s]", target->CE_NAME,
(prereq == NIL(CELL)) ? "" : prereq->CE_NAME) );
if( prereq != NIL(CELL) ) {
char *name = prereq->CE_NAME;
int len = strlen(name);
if( *name == '\'' && name[len-1]=='\'' ){
_add_global_prereq( prereq );
name[len-1] = '\0';
strcpy(name, name+1);
DB_VOID_RETURN;
}
}
/* Search the list of prerequisites for the current target and see if
* any of them match the current %-meta : prereq pair. NOTE that %-metas
* are built as if they were F_MULTI targets. */
match = FALSE;
for( edl=target->ce_prq; edl != NIL(LINK); edl=edl->cl_next ) {
edge = edl->cl_prq;
DB_PRINT( "%", ("Trying to match [%s]", edge?edge->CE_NAME:"(nil)") );
if( (!edge->ce_prq && !prereq)
|| ( edge->ce_prq
&& (edge->ce_prq->cl_prq == prereq)
&& ( (edge->ce_dir == _sv_setdir)
|| ( edge->ce_dir
&& _sv_setdir
&& !strcmp(edge->ce_dir,strchr(_sv_setdir,'=')+1)
)
)
)
) {
match = TRUE;
break;
}
}
if( match ) {
/* match is TRUE hence, we found an edge joining the target and the
* prerequisite so reset the new edge's how values to reflect the new
* recipe etc. */
DB_PRINT( "%", ("It's an old edge") );
edge->ce_dir = NIL(char);
edge->ce_flag &= (F_PERCENT|F_MAGIC|F_DFA);
edge->ce_attr &= A_NOINFER;
}
else {
DB_PRINT( "%", ("Adding a new edge") );
if( !(target->ce_flag & F_DFA) ) {
Add_nfa( target->CE_NAME );
target->ce_flag |= F_DFA;
}
edge = _make_multi(target);
if( prereq ) Add_prerequisite(edge, prereq, FALSE, TRUE);
}
if( op & R_OP_DCL )
Warning( "'::' operator for meta-target '%s' ignored, ':' operator assumed.",
target->CE_NAME );
edge->ce_link = _sv_edgel;
_sv_edgel = edge;
_sv_globprq_only = 0;
DB_VOID_RETURN;
}
static CELLPTR
_make_multi( CELLPTR tg )
{
CELLPTR cp;
/* This first handle the case when a : foo ; exists prior to seeing
* a :: fee; */
if( !(tg->ce_flag & F_MULTI) && (tg->ce_prq || tg->ce_recipe) ) {
TALLOC(cp, 1, CELL);
*cp = *tg;
tg->ce_prq = NIL(LINK);
tg->ce_flag |= F_RULES|F_MULTI|F_TARGET;
tg->ce_attr |= A_SEQ;
tg->ce_recipe = NIL(STRING);
tg->ce_dir = NIL(char);
cp->ce_count = ++tg->ce_index;
Add_prerequisite(tg, cp, FALSE, TRUE);
}
TALLOC(cp, 1, CELL);
*cp = *tg;
if( !(tg->ce_flag & F_MULTI) ) {
tg->ce_prq = NIL(LINK);
tg->ce_flag |= F_RULES|F_MULTI|F_TARGET;
tg->ce_attr |= A_SEQ;
tg->ce_recipe = NIL(STRING);
tg->ce_dir = NIL(char);
}
else {
cp->ce_flag &= ~(F_RULES|F_MULTI);
cp->ce_attr &= ~A_SEQ;
cp->ce_prq = NIL(LINK);
cp->ce_index = 0;
}
cp->ce_count = ++tg->ce_index;
cp->ce_flag |= F_TARGET;
Add_prerequisite(tg, cp, FALSE, TRUE);
return(cp);
}
static void
_add_global_prereq( CELLPTR pq )/*
==========================
Prerequisite is a non-% prerequisite for a %-rule target, add it to
the target's list of global prerequsites to add on match */
{
register LINKPTR ln;
TALLOC( ln, 1, LINK );
ln->cl_next = _sv_glb_prq;
ln->cl_prq = pq;
_sv_glb_prq = ln;
}
static void
_set_attributes(
/*
======================================
Set the appropriate attributes for a cell */
t_attr attr,
char *set_dir,
CELLPTR cp)
{
char *dir;
DB_ENTER( "_set_attributes" );
/* If .SETDIR attribute is set then we have at least .SETDIR= in the
* set_dir string. So go and fishout what is at the end of the =.
* If not set and not NULL then propagate it to the target cell. */
if( attr & A_SETDIR ) {
dir = strchr( set_dir, '=' ) + 1;
if( cp->ce_dir )
Warning( "Multiple .SETDIR for %s ignored", cp->CE_NAME );
else
if( *dir ) cp->ce_dir = _strdup(dir);
}
cp->ce_attr |= attr; /* set rest of attributes for target */
DB_VOID_RETURN;
}
static void
_set_global_attr( t_attr attr )/*
==========================
Handle the setting of the global attribute functions based on
The attribute flags set in attr. */
{
t_attr flag;
/* Some compilers can't handle a switch on a long, and t_attr is now a long
* integer on some systems. foey! */
for( flag = MAX_ATTR; flag; flag >>= 1 )
if( flag & attr )
if( flag == A_PRECIOUS) Def_macro(".PRECIOUS", "y", M_EXPANDED);
else if( flag == A_SILENT) Def_macro(".SILENT", "y", M_EXPANDED);
else if( flag == A_IGNORE ) Def_macro(".IGNORE", "y", M_EXPANDED);
else if( flag == A_EPILOG ) Def_macro(".EPILOG", "y", M_EXPANDED);
else if( flag == A_PROLOG ) Def_macro(".PROLOG", "y", M_EXPANDED);
else if( flag == A_NOINFER ) Def_macro(".NOINFER", "y", M_EXPANDED);
else if( flag == A_SEQ ) Def_macro(".SEQUENTIAL","y", M_EXPANDED);
else if( flag == A_SHELL ) Def_macro(".USESHELL", "y", M_EXPANDED);
else if( flag == A_MKSARGS ) Def_macro(".MKSARGS", "y", M_EXPANDED);
else if( flag == A_SWAP ) Def_macro(".SWAP", "y", M_EXPANDED);
attr &= ~A_GLOB;
if( attr ) Warning( "Non global attribute(s) ignored" );
}
static void
_stick_at_head(
/*
==========================
Add the prerequisite list to the head of the existing prerequisite
list */
CELLPTR cp, /* cell for target node */
CELLPTR pq) /* list of prerequisites to add */
{
DB_ENTER( "_stick_at_head" );
if( pq->ce_link != NIL(CELL) ) _stick_at_head( cp, pq->ce_link );
Add_prerequisite( cp, pq, TRUE, FALSE );
DB_VOID_RETURN;
}
static t_attr
_is_attribute( char *name )/*
=======================
Check the passed name against the list of valid attributes and return the
attribute index if it is, else return 0, indicating the name is not a valid
attribute. The present attributes are defined in dmake.h as A_xxx #defines,
with the corresponding makefile specification: (note they must be named
exactly as defined below)
Valid attributes are: .IGNORE, .SETDIR=, .SILENT, .PRECIOUS, .LIBRARY,
.EPILOG, .PROLOG, .LIBRARYM, .SYMBOL, .UPDATEALL,
.USESHELL, .NOINFER, .PHONY, .SWAP, .SEQUENTIAL
.NOSTATE, .MKSARGS
NOTE: The strcmp's are OK since at most three are ever executed for any
one attribute check, and that happens only when we can be fairly
certain we have an attribute. */
{
t_attr attr = 0;
DB_ENTER( "_is_attribute" );
if( *name++ == '.' )
switch( *name )
{
case 'E': attr = (strcmp(name, "EPILOG")) ? 0 : A_EPILOG; break;
case 'I': attr = (strcmp(name, "IGNORE")) ? 0 : A_IGNORE; break;
case 'L': attr = (strcmp(name, "LIBRARY")) ? 0 : A_LIBRARY; break;
case 'M': attr = (strcmp(name, "MKSARGS")) ? 0 : A_MKSARGS; break;
case 'N':
if( !strcmp(name, "NOINFER") ) attr = A_NOINFER;
else if( !strcmp(name, "NOSTATE")) attr = A_NOSTATE;
else attr = 0;
break;
case 'U':
if( !strcmp(name, "UPDATEALL") ) attr = A_UPDATEALL;
else if( !strcmp(name, "USESHELL")) attr = A_SHELL;
else attr = 0;
break;
case 'P':
if( !strcmp(name, "PRECIOUS") ) attr = A_PRECIOUS;
else if( !strcmp(name, "PROLOG") ) attr = A_PROLOG;
else if( !strcmp(name, "PHONY") ) attr = A_PHONY;
else attr = 0;
break;
case 'S':
if( !strncmp(name, "SETDIR=", 7) ) attr = A_SETDIR;
else if( !strcmp(name, "SILENT") ) attr = A_SILENT;
else if( !strcmp(name, "SYMBOL") ) attr = A_SYMBOL;
else if( !strcmp(name, "SEQUENTIAL")) attr = A_SEQ;
else if( !strcmp(name, "SWAP")) attr = A_SWAP;
else attr = 0;
break;
}
DB_RETURN( attr );
}
static int
_is_special( char *tg )/*
===================
This function returns TRUE if the name passed in represents a special
target, otherwise it returns false. A special target is one that has
a special meaning to dmake, and may require processing at the time that
it is parsed.
Current Special targets are:
.GROUPPROLOG .GROUPEPILOG .INCLUDE .IMPORT
.EXPORT .SOURCE .SUFFIXES .ERROR
.INCLUDEDIRS .MAKEFILES .REMOVE .KEEP_STATE
*/
{
DB_ENTER( "_is_special" );
if( *tg++ != '.' ) DB_RETURN( 0 );
switch( *tg )
{
case 'I':
if( !strcmp( tg, "IMPORT" ) ) DB_RETURN( ST_IMPORT );
else if( !strcmp( tg, "INCLUDE" ) ) DB_RETURN( ST_INCLUDE );
else if( !strcmp( tg, "INCLUDEDIRS" )) DB_RETURN( ST_REST );
break;
case 'M':
if( !strcmp( tg, "MAKEFILES" ) ) DB_RETURN( ST_REST );
break;
case 'E':
if( !strcmp( tg, "ERROR" ) ) DB_RETURN( ST_REST );
else if( !strcmp( tg, "EXPORT" ) ) DB_RETURN( ST_EXPORT );
break;
case 'G':
if( !strcmp( tg, "GROUPPROLOG" )) DB_RETURN( ST_REST );
else if( !strcmp( tg, "GROUPEPILOG" )) DB_RETURN( ST_REST );
break;
case 'K':
if( !strcmp( tg, "KEEP_STATE" ) ) DB_RETURN( ST_KEEP );
break;
case 'R':
if( !strcmp( tg, "REMOVE" ) ) DB_RETURN( ST_REST );
break;
case 'S':
if( !strncmp( tg, "SOURCE", 6 ) ) DB_RETURN( ST_SOURCE );
else if( !strncmp(tg, "SUFFIXES", 8 )) DB_RETURN( ST_SOURCE );
break;
}
DB_RETURN( 0 );
}
static int
_is_percent( char *np )/*
===================
return TRUE if np points at a string containing a % sign */
{
return( (strchr(np,'%') && (*np != '\'' && np[strlen(np)-1] != '\'')) ?
TRUE : FALSE );
}
static char *
_is_magic( char *np )/*
=================
return TRUE if np points at a string of the form
.<chars>.<chars> or .<chars>
where chars are only alpha characters.
NOTE: reject target if it begins with ./ or ../ */
{
register char *n;
n = np;
if( *n != '.' ) return( NIL(char) );
if (strchr(DirBrkStr, *(n+1))!=NULL || *(n+1) == '.' )
return (NIL(char));
for( n++; isgraph(*n) && (*n != '.'); n++ );
if( *n != '\0' ) {
if( *n != '.' ) return( NIL(char) );
for( np = n++; isgraph( *n ) && (*n != '.'); n++ );
if( *n != '\0' ) return( NIL(char) );
}
else if( !Augmake )
return( NIL(char) );
/* np points at the second . of .<chars>.<chars> string.
* if the special target is of the form .<chars> then np points at the
* first . in the token. */
return( np );
}
static int
_add_root( CELLPTR tg)
{
int res = FALSE;
if( !Target && !(tg->ce_flag & (F_SPECIAL|F_PERCENT)) ) {
Add_prerequisite( Root, tg, FALSE, TRUE );
tg->ce_flag |= F_TARGET;
tg->ce_attr |= A_FRINGE;
res = TRUE;
}
return(res);
}