* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
* Copyright (c) 1988, 1989 by Adam de Boor
* Copyright (c) 1989 by Berkeley Softworks
* This code is derived from software contributed to Berkeley by
* Redistribution and use in source and binary forms are permitted
* provided that: (1) source distributions retain this entire copyright
* notice and comment, and (2) distributions including binaries display
* the following acknowledgement: ``This product includes software
* developed by the University of California, Berkeley and its contributors''
* in the documentation or other materials provided with the distribution
* and in all advertising materials mentioning features or use of this
* software. Neither the name of the University nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
static char sccsid
[] = "@(#)targ.c 5.7 (Berkeley) 6/1/90";
* Functions for maintaining the Lst allTargets. Target nodes are
* kept in two structures: a Lst, maintained by the list library, and a
* hash table, maintained by the hash library.
* Targ_Init Initialization procedure.
* Targ_NewGN Create a new GNode for the passed target
* (string). The node is *not* placed in the
* hash table, though all its fields are
* Targ_FindNode Find the node for a given target, creating
* and storing it if it doesn't exist and the
* flags are right (TARG_CREATE)
* Targ_FindList Given a list of names, find nodes for all
* of them. If a name doesn't exist and the
* TARG_NOCREATE flag was given, an error message
* is printed. Else, if a name doesn't exist,
* Targ_Ignore Return TRUE if errors should be ignored when
* creating the given target.
* Targ_Silent Return TRUE if we should be silent when
* creating the given target.
* Targ_Precious Return TRUE if the target is precious and
* should not be removed if we are interrupted.
* Targ_PrintGraph Print out the entire graphm all variables
* and statistics for the directory cache. Should
* print something for suffixes, too, but...
static Lst allTargets
; /* the list of all targets found so far */
static Hash_Table targets
; /* a hash table of same */
#define HTSIZE 191 /* initial size of hash table */
*-----------------------------------------------------------------------
* The allTargets list and the targets hash table are initialized
*-----------------------------------------------------------------------
allTargets
= Lst_Init (FALSE
);
Hash_InitTable (&targets
, HTSIZE
, HASH_STRING_KEYS
);
*-----------------------------------------------------------------------
* Create and initialize a new graph node
* An initialized graph node with the name field filled with a copy
*-----------------------------------------------------------------------
char *name
; /* the name to stick in the new node */
gn
= (GNode
*) emalloc (sizeof (GNode
));
gn
->name
= strdup (name
);
if (name
[0] == '-' && name
[1] == 'l') {
gn
->mtime
= gn
->cmtime
= 0;
gn
->iParents
= Lst_Init (FALSE
);
gn
->cohorts
= Lst_Init (FALSE
);
gn
->parents
= Lst_Init (FALSE
);
gn
->children
= Lst_Init (FALSE
);
gn
->successors
= Lst_Init(FALSE
);
gn
->preds
= Lst_Init(FALSE
);
gn
->context
= Lst_Init (FALSE
);
gn
->commands
= Lst_Init (FALSE
);
*-----------------------------------------------------------------------
* Find a node in the list using the given name for matching
* The node in the list if it was. If it wasn't, return NILGNODE of
* flags was TARG_NOCREATE or the newly created and initialized node
* Sometimes a node is created and added to the list
*-----------------------------------------------------------------------
Targ_FindNode (name
, flags
)
char *name
; /* the name to find */
int flags
; /* flags governing events when target not
GNode
*gn
; /* node in that element */
Hash_Entry
*he
; /* New or used hash entry for node */
Boolean isNew
; /* Set TRUE if Hash_CreateEntry had to create */
/* an entry for the node */
if (flags
& TARG_CREATE
) {
he
= Hash_CreateEntry (&targets
, name
, &isNew
);
(void) Lst_AtEnd (allTargets
, (ClientData
)gn
);
he
= Hash_FindEntry (&targets
, name
);
if (he
== (Hash_Entry
*) NULL
) {
return ((GNode
*) Hash_GetValue (he
));
*-----------------------------------------------------------------------
* Make a complete list of GNodes from the given list of names
* A complete list of graph nodes corresponding to all instances of all
* If flags is TARG_CREATE, nodes will be created for all names in
* names which do not yet have graph nodes. If flags is TARG_NOCREATE,
* an error message will be printed for each name which can't be found.
* -----------------------------------------------------------------------
Targ_FindList (names
, flags
)
Lst names
; /* list of names to find */
int flags
; /* flags used if no node is found for a given
Lst nodes
; /* result list */
register LstNode ln
; /* name list element */
register GNode
*gn
; /* node in tLn */
nodes
= Lst_Init (FALSE
);
if (Lst_Open (names
) == FAILURE
) {
while ((ln
= Lst_Next (names
)) != NILLNODE
) {
name
= (char *)Lst_Datum(ln
);
gn
= Targ_FindNode (name
, flags
);
* Note: Lst_AtEnd must come before the Lst_Concat so the nodes
* are added to the list in the order in which they were
* encountered in the makefile.
(void) Lst_AtEnd (nodes
, (ClientData
)gn
);
if (gn
->type
& OP_DOUBLEDEP
) {
(void)Lst_Concat (nodes
, gn
->cohorts
, LST_CONCNEW
);
} else if (flags
== TARG_NOCREATE
) {
Error ("\"%s\" -- target unknown.", name
);
*-----------------------------------------------------------------------
* Return true if should ignore errors when creating gn
* TRUE if should ignore errors
*-----------------------------------------------------------------------
GNode
*gn
; /* node to check for */
if (ignoreErrors
|| gn
->type
& OP_IGNORE
) {
*-----------------------------------------------------------------------
* Return true if be silent when creating gn
* TRUE if should be silent
*-----------------------------------------------------------------------
GNode
*gn
; /* node to check for */
if (beSilent
|| gn
->type
& OP_SILENT
) {
*-----------------------------------------------------------------------
* See if the given target is precious
* TRUE if it is precious. FALSE otherwise
*-----------------------------------------------------------------------
GNode
*gn
; /* the node to check */
if (allPrecious
|| (gn
->type
& (OP_PRECIOUS
|OP_DOUBLEDEP
))) {
/******************* DEBUG INFO PRINTING ****************/
static GNode
*mainTarg
; /* the main target, as set by Targ_SetMain */
*-----------------------------------------------------------------------
* Set our idea of the main target we'll be creating. Used for
* "mainTarg" is set to the main target's node.
*-----------------------------------------------------------------------
GNode
*gn
; /* The main target we'll create */
TargPrintName (gn
, ppath
)
printf ("%s ", gn
->name
);
printf ("[%s] ", gn
->path
);
*-----------------------------------------------------------------------
* Format a modification time in some reasonable way and return it.
* The time is placed in a static area, so it is overwritten
*-----------------------------------------------------------------------
static char *months
[] = {
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
parts
= localtime(&time
);
sprintf (buf
, "%d:%02d:%02d %s %d, 19%d",
parts
->tm_hour
, parts
->tm_min
, parts
->tm_sec
,
months
[parts
->tm_mon
], parts
->tm_mday
, parts
->tm_year
);
*-----------------------------------------------------------------------
* Print out a type field giving only those attributes the user can
*-----------------------------------------------------------------------
#define PRINTBIT(attr) case CONCAT(OP_,attr): printf("." #attr " "); break
#define PRINTDBIT(attr) case CONCAT(OP_,attr): if (DEBUG(TARG)) printf("." #attr " "); break
#define PRINTBIT(attr) case CONCAT(OP_,attr): printf(".attr "); break
#define PRINTDBIT(attr) case CONCAT(OP_,attr): if (DEBUG(TARG)) printf(".attr "); break
tbit
= 1 << (ffs(type
) - 1);
/*XXX: MEMBER is defined, so CONCAT(OP_,MEMBER) gives OP_"%" */
case OP_MEMBER
: if (DEBUG(TARG
)) printf(".MEMBER "); break;
*-----------------------------------------------------------------------
* print the contents of a node
*-----------------------------------------------------------------------
printf("# *** MAIN TARGET ***\n");
printf("# %d unmade children\n", gn
->unmade
);
printf("# No unmade children\n");
if (! (gn
->type
& (OP_JOIN
|OP_USE
|OP_EXEC
))) {
printf("# last modified %s: %s\n",
(gn
->made
== UNMADE
? "unmade" :
(gn
->made
== MADE
? "made" :
(gn
->made
== UPTODATE
? "up-to-date" :
} else if (gn
->made
!= UNMADE
) {
printf("# non-existent (maybe): %s\n",
(gn
->made
== MADE
? "made" :
(gn
->made
== UPTODATE
? "up-to-date" :
(gn
->made
== ERROR
? "error when made" :
if (!Lst_IsEmpty (gn
->iParents
)) {
printf("# implicit parents: ");
Lst_ForEach (gn
->iParents
, TargPrintName
, (ClientData
)0);
if (!Lst_IsEmpty (gn
->parents
)) {
Lst_ForEach (gn
->parents
, TargPrintName
, (ClientData
)0);
printf("%-16s", gn
->name
);
switch (gn
->type
& OP_OPMASK
) {
Targ_PrintType (gn
->type
);
Lst_ForEach (gn
->children
, TargPrintName
, (ClientData
)0);
Lst_ForEach (gn
->commands
, Targ_PrintCmd
, (ClientData
)0);
if (gn
->type
& OP_DOUBLEDEP
) {
Lst_ForEach (gn
->cohorts
, TargPrintNode
, (ClientData
)pass
);
*-----------------------------------------------------------------------
* Print only those targets that are just a source.
* The name of each file is printed preceeded by #\t
*-----------------------------------------------------------------------
printf("#\t%s [%s]\n", gn
->name
,
gn
->path
? gn
->path
: gn
->name
);
*-----------------------------------------------------------------------
* print the entire graph. heh heh
*-----------------------------------------------------------------------
int pass
; /* Which pass this is. 1 => no processing
* 2 => processing done */
printf("#*** Input graph:\n");
Lst_ForEach (allTargets
, TargPrintNode
, (ClientData
)pass
);
printf("#\n# Files that are only sources:\n");
Lst_ForEach (allTargets
, TargPrintOnlySrc
);
printf("#*** Global Variables:\n");
printf("#*** Command-line Variables:\n");