Home | History | Annotate | Line # | Download | only in make
targ.c revision 1.41
      1 /*	$NetBSD: targ.c,v 1.41 2006/02/11 20:19:36 dsl Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 1988, 1989, 1990, 1993
      5  *	The Regents of the University of California.  All rights reserved.
      6  *
      7  * This code is derived from software contributed to Berkeley by
      8  * Adam de Boor.
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted provided that the following conditions
     12  * are met:
     13  * 1. Redistributions of source code must retain the above copyright
     14  *    notice, this list of conditions and the following disclaimer.
     15  * 2. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in the
     17  *    documentation and/or other materials provided with the distribution.
     18  * 3. Neither the name of the University nor the names of its contributors
     19  *    may be used to endorse or promote products derived from this software
     20  *    without specific prior written permission.
     21  *
     22  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     25  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     32  * SUCH DAMAGE.
     33  */
     34 
     35 /*
     36  * Copyright (c) 1989 by Berkeley Softworks
     37  * All rights reserved.
     38  *
     39  * This code is derived from software contributed to Berkeley by
     40  * Adam de Boor.
     41  *
     42  * Redistribution and use in source and binary forms, with or without
     43  * modification, are permitted provided that the following conditions
     44  * are met:
     45  * 1. Redistributions of source code must retain the above copyright
     46  *    notice, this list of conditions and the following disclaimer.
     47  * 2. Redistributions in binary form must reproduce the above copyright
     48  *    notice, this list of conditions and the following disclaimer in the
     49  *    documentation and/or other materials provided with the distribution.
     50  * 3. All advertising materials mentioning features or use of this software
     51  *    must display the following acknowledgement:
     52  *	This product includes software developed by the University of
     53  *	California, Berkeley and its contributors.
     54  * 4. Neither the name of the University nor the names of its contributors
     55  *    may be used to endorse or promote products derived from this software
     56  *    without specific prior written permission.
     57  *
     58  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     59  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     60  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     61  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     62  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     63  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     64  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     65  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     66  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     67  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     68  * SUCH DAMAGE.
     69  */
     70 
     71 #ifndef MAKE_NATIVE
     72 static char rcsid[] = "$NetBSD: targ.c,v 1.41 2006/02/11 20:19:36 dsl Exp $";
     73 #else
     74 #include <sys/cdefs.h>
     75 #ifndef lint
     76 #if 0
     77 static char sccsid[] = "@(#)targ.c	8.2 (Berkeley) 3/19/94";
     78 #else
     79 __RCSID("$NetBSD: targ.c,v 1.41 2006/02/11 20:19:36 dsl Exp $");
     80 #endif
     81 #endif /* not lint */
     82 #endif
     83 
     84 /*-
     85  * targ.c --
     86  *	Functions for maintaining the Lst allTargets. Target nodes are
     87  * kept in two structures: a Lst, maintained by the list library, and a
     88  * hash table, maintained by the hash library.
     89  *
     90  * Interface:
     91  *	Targ_Init 	    	Initialization procedure.
     92  *
     93  *	Targ_End 	    	Cleanup the module
     94  *
     95  *	Targ_List 	    	Return the list of all targets so far.
     96  *
     97  *	Targ_NewGN	    	Create a new GNode for the passed target
     98  *	    	  	    	(string). The node is *not* placed in the
     99  *	    	  	    	hash table, though all its fields are
    100  *	    	  	    	initialized.
    101  *
    102  *	Targ_FindNode	    	Find the node for a given target, creating
    103  *	    	  	    	and storing it if it doesn't exist and the
    104  *	    	  	    	flags are right (TARG_CREATE)
    105  *
    106  *	Targ_FindList	    	Given a list of names, find nodes for all
    107  *	    	  	    	of them. If a name doesn't exist and the
    108  *	    	  	    	TARG_NOCREATE flag was given, an error message
    109  *	    	  	    	is printed. Else, if a name doesn't exist,
    110  *	    	  	    	its node is created.
    111  *
    112  *	Targ_Ignore	    	Return TRUE if errors should be ignored when
    113  *	    	  	    	creating the given target.
    114  *
    115  *	Targ_Silent	    	Return TRUE if we should be silent when
    116  *	    	  	    	creating the given target.
    117  *
    118  *	Targ_Precious	    	Return TRUE if the target is precious and
    119  *	    	  	    	should not be removed if we are interrupted.
    120  *
    121  * Debugging:
    122  *	Targ_PrintGraph	    	Print out the entire graphm all variables
    123  *	    	  	    	and statistics for the directory cache. Should
    124  *	    	  	    	print something for suffixes, too, but...
    125  */
    126 
    127 #include	  <stdio.h>
    128 #include	  <time.h>
    129 
    130 #include	  "make.h"
    131 #include	  "hash.h"
    132 #include	  "dir.h"
    133 
    134 static Lst        allTargets;	/* the list of all targets found so far */
    135 #ifdef CLEANUP
    136 static Lst	  allGNs;	/* List of all the GNodes */
    137 #endif
    138 static Hash_Table targets;	/* a hash table of same */
    139 
    140 #define HTSIZE	191		/* initial size of hash table */
    141 
    142 static int TargPrintOnlySrc(ClientData, ClientData);
    143 static int TargPrintName(ClientData, ClientData);
    144 #ifdef CLEANUP
    145 static void TargFreeGN(ClientData);
    146 #endif
    147 static int TargPropagateCohort(ClientData, ClientData);
    148 static int TargPropagateNode(ClientData, ClientData);
    149 
    150 /*-
    151  *-----------------------------------------------------------------------
    152  * Targ_Init --
    153  *	Initialize this module
    154  *
    155  * Results:
    156  *	None
    157  *
    158  * Side Effects:
    159  *	The allTargets list and the targets hash table are initialized
    160  *-----------------------------------------------------------------------
    161  */
    162 void
    163 Targ_Init(void)
    164 {
    165     allTargets = Lst_Init(FALSE);
    166     Hash_InitTable(&targets, HTSIZE);
    167 }
    168 
    169 /*-
    170  *-----------------------------------------------------------------------
    171  * Targ_End --
    172  *	Finalize this module
    173  *
    174  * Results:
    175  *	None
    176  *
    177  * Side Effects:
    178  *	All lists and gnodes are cleared
    179  *-----------------------------------------------------------------------
    180  */
    181 void
    182 Targ_End(void)
    183 {
    184 #ifdef CLEANUP
    185     Lst_Destroy(allTargets, NOFREE);
    186     if (allGNs)
    187 	Lst_Destroy(allGNs, TargFreeGN);
    188     Hash_DeleteTable(&targets);
    189 #endif
    190 }
    191 
    192 /*-
    193  *-----------------------------------------------------------------------
    194  * Targ_List --
    195  *	Return the list of all targets
    196  *
    197  * Results:
    198  *	The list of all targets.
    199  *
    200  * Side Effects:
    201  *	None
    202  *-----------------------------------------------------------------------
    203  */
    204 Lst
    205 Targ_List(void)
    206 {
    207     return allTargets;
    208 }
    209 
    210 /*-
    211  *-----------------------------------------------------------------------
    212  * Targ_NewGN  --
    213  *	Create and initialize a new graph node
    214  *
    215  * Input:
    216  *	name		the name to stick in the new node
    217  *
    218  * Results:
    219  *	An initialized graph node with the name field filled with a copy
    220  *	of the passed name
    221  *
    222  * Side Effects:
    223  *	The gnode is added to the list of all gnodes.
    224  *-----------------------------------------------------------------------
    225  */
    226 GNode *
    227 Targ_NewGN(const char *name)
    228 {
    229     GNode *gn;
    230 
    231     gn = emalloc(sizeof(GNode));
    232     gn->name = estrdup(name);
    233     gn->uname = NULL;
    234     gn->path = NULL;
    235     if (name[0] == '-' && name[1] == 'l') {
    236 	gn->type = OP_LIB;
    237     } else {
    238 	gn->type = 0;
    239     }
    240     gn->unmade =    	0;
    241     gn->unmade_cohorts = 0;
    242     gn->centurion =    	NULL;
    243     gn->made = 	    	UNMADE;
    244     gn->flags = 	0;
    245     gn->order =		0;
    246     gn->mtime = gn->cmtime = 0;
    247     gn->iParents =  	Lst_Init(FALSE);
    248     gn->cohorts =   	Lst_Init(FALSE);
    249     gn->parents =   	Lst_Init(FALSE);
    250     gn->children =  	Lst_Init(FALSE);
    251     gn->successors = 	Lst_Init(FALSE);
    252     gn->preds =     	Lst_Init(FALSE);
    253     Hash_InitTable(&gn->context, 0);
    254     gn->commands =  	Lst_Init(FALSE);
    255     gn->suffix =	NULL;
    256     gn->lineno =	0;
    257     gn->fname = 	NULL;
    258 
    259 #ifdef CLEANUP
    260     if (allGNs == NULL)
    261 	allGNs = Lst_Init(FALSE);
    262     Lst_AtEnd(allGNs, (ClientData) gn);
    263 #endif
    264 
    265     return (gn);
    266 }
    267 
    268 #ifdef CLEANUP
    269 /*-
    270  *-----------------------------------------------------------------------
    271  * TargFreeGN  --
    272  *	Destroy a GNode
    273  *
    274  * Results:
    275  *	None.
    276  *
    277  * Side Effects:
    278  *	None.
    279  *-----------------------------------------------------------------------
    280  */
    281 static void
    282 TargFreeGN(ClientData gnp)
    283 {
    284     GNode *gn = (GNode *)gnp;
    285 
    286 
    287     free(gn->name);
    288     if (gn->uname)
    289 	free(gn->uname);
    290     if (gn->path)
    291 	free(gn->path);
    292     if (gn->fname)
    293 	free(gn->fname);
    294 
    295     Lst_Destroy(gn->iParents, NOFREE);
    296     Lst_Destroy(gn->cohorts, NOFREE);
    297     Lst_Destroy(gn->parents, NOFREE);
    298     Lst_Destroy(gn->children, NOFREE);
    299     Lst_Destroy(gn->successors, NOFREE);
    300     Lst_Destroy(gn->preds, NOFREE);
    301     Hash_DeleteTable(&gn->context);
    302     Lst_Destroy(gn->commands, NOFREE);
    303     free(gn);
    304 }
    305 #endif
    306 
    307 
    308 /*-
    309  *-----------------------------------------------------------------------
    310  * Targ_FindNode  --
    311  *	Find a node in the list using the given name for matching
    312  *
    313  * Input:
    314  *	name		the name to find
    315  *	flags		flags governing events when target not
    316  *			found
    317  *
    318  * Results:
    319  *	The node in the list if it was. If it wasn't, return NILGNODE of
    320  *	flags was TARG_NOCREATE or the newly created and initialized node
    321  *	if it was TARG_CREATE
    322  *
    323  * Side Effects:
    324  *	Sometimes a node is created and added to the list
    325  *-----------------------------------------------------------------------
    326  */
    327 GNode *
    328 Targ_FindNode(const char *name, int flags)
    329 {
    330     GNode         *gn;	      /* node in that element */
    331     Hash_Entry	  *he;	      /* New or used hash entry for node */
    332     Boolean	  isNew;      /* Set TRUE if Hash_CreateEntry had to create */
    333 			      /* an entry for the node */
    334 
    335 
    336     if (flags & TARG_CREATE) {
    337 	he = Hash_CreateEntry(&targets, name, &isNew);
    338 	if (isNew) {
    339 	    gn = Targ_NewGN(name);
    340 	    Hash_SetValue(he, gn);
    341 	    Var_Append(".ALLTARGETS", name, VAR_GLOBAL);
    342 	    (void)Lst_AtEnd(allTargets, (ClientData)gn);
    343 	}
    344     } else {
    345 	he = Hash_FindEntry(&targets, name);
    346     }
    347 
    348     if (he == NULL) {
    349 	return (NILGNODE);
    350     } else {
    351 	return ((GNode *)Hash_GetValue(he));
    352     }
    353 }
    354 
    355 /*-
    356  *-----------------------------------------------------------------------
    357  * Targ_FindList --
    358  *	Make a complete list of GNodes from the given list of names
    359  *
    360  * Input:
    361  *	name		list of names to find
    362  *	flags		flags used if no node is found for a given name
    363  *
    364  * Results:
    365  *	A complete list of graph nodes corresponding to all instances of all
    366  *	the names in names.
    367  *
    368  * Side Effects:
    369  *	If flags is TARG_CREATE, nodes will be created for all names in
    370  *	names which do not yet have graph nodes. If flags is TARG_NOCREATE,
    371  *	an error message will be printed for each name which can't be found.
    372  * -----------------------------------------------------------------------
    373  */
    374 Lst
    375 Targ_FindList(Lst names, int flags)
    376 {
    377     Lst            nodes;	/* result list */
    378     LstNode	   ln;		/* name list element */
    379     GNode	   *gn;		/* node in tLn */
    380     char    	   *name;
    381 
    382     nodes = Lst_Init(FALSE);
    383 
    384     if (Lst_Open(names) == FAILURE) {
    385 	return (nodes);
    386     }
    387     while ((ln = Lst_Next(names)) != NILLNODE) {
    388 	name = (char *)Lst_Datum(ln);
    389 	gn = Targ_FindNode(name, flags);
    390 	if (gn != NILGNODE) {
    391 	    /*
    392 	     * Note: Lst_AtEnd must come before the Lst_Concat so the nodes
    393 	     * are added to the list in the order in which they were
    394 	     * encountered in the makefile.
    395 	     */
    396 	    (void)Lst_AtEnd(nodes, (ClientData)gn);
    397 	} else if (flags == TARG_NOCREATE) {
    398 	    Error("\"%s\" -- target unknown.", name);
    399 	}
    400     }
    401     Lst_Close(names);
    402     return (nodes);
    403 }
    404 
    405 /*-
    406  *-----------------------------------------------------------------------
    407  * Targ_Ignore  --
    408  *	Return true if should ignore errors when creating gn
    409  *
    410  * Input:
    411  *	gn		node to check for
    412  *
    413  * Results:
    414  *	TRUE if should ignore errors
    415  *
    416  * Side Effects:
    417  *	None
    418  *-----------------------------------------------------------------------
    419  */
    420 Boolean
    421 Targ_Ignore(GNode *gn)
    422 {
    423     if (ignoreErrors || gn->type & OP_IGNORE) {
    424 	return (TRUE);
    425     } else {
    426 	return (FALSE);
    427     }
    428 }
    429 
    430 /*-
    431  *-----------------------------------------------------------------------
    432  * Targ_Silent  --
    433  *	Return true if be silent when creating gn
    434  *
    435  * Input:
    436  *	gn		node to check for
    437  *
    438  * Results:
    439  *	TRUE if should be silent
    440  *
    441  * Side Effects:
    442  *	None
    443  *-----------------------------------------------------------------------
    444  */
    445 Boolean
    446 Targ_Silent(GNode *gn)
    447 {
    448     if (beSilent || gn->type & OP_SILENT) {
    449 	return (TRUE);
    450     } else {
    451 	return (FALSE);
    452     }
    453 }
    454 
    455 /*-
    456  *-----------------------------------------------------------------------
    457  * Targ_Precious --
    458  *	See if the given target is precious
    459  *
    460  * Input:
    461  *	gn		the node to check
    462  *
    463  * Results:
    464  *	TRUE if it is precious. FALSE otherwise
    465  *
    466  * Side Effects:
    467  *	None
    468  *-----------------------------------------------------------------------
    469  */
    470 Boolean
    471 Targ_Precious(GNode *gn)
    472 {
    473     if (allPrecious || (gn->type & (OP_PRECIOUS|OP_DOUBLEDEP))) {
    474 	return (TRUE);
    475     } else {
    476 	return (FALSE);
    477     }
    478 }
    479 
    480 /******************* DEBUG INFO PRINTING ****************/
    481 
    482 static GNode	  *mainTarg;	/* the main target, as set by Targ_SetMain */
    483 /*-
    484  *-----------------------------------------------------------------------
    485  * Targ_SetMain --
    486  *	Set our idea of the main target we'll be creating. Used for
    487  *	debugging output.
    488  *
    489  * Input:
    490  *	gn		The main target we'll create
    491  *
    492  * Results:
    493  *	None.
    494  *
    495  * Side Effects:
    496  *	"mainTarg" is set to the main target's node.
    497  *-----------------------------------------------------------------------
    498  */
    499 void
    500 Targ_SetMain(GNode *gn)
    501 {
    502     mainTarg = gn;
    503 }
    504 
    505 #define PrintWait (ClientData)1
    506 #define PrintPath (ClientData)2
    507 
    508 static int
    509 TargPrintName(ClientData gnp, ClientData pflags)
    510 {
    511     static int last_order;
    512     GNode *gn = (GNode *)gnp;
    513 
    514     if (pflags == PrintWait && gn->order > last_order)
    515 	printf(".WAIT ");
    516     last_order = gn->order;
    517 
    518     printf("%s ", gn->name);
    519 
    520 #ifdef notdef
    521     if (pflags == PrintPath) {
    522 	if (gn->path) {
    523 	    printf("[%s]  ", gn->path);
    524 	}
    525 	if (gn == mainTarg) {
    526 	    printf("(MAIN NAME)  ");
    527 	}
    528     }
    529 #endif /* notdef */
    530 
    531     return 0;
    532 }
    533 
    534 
    535 int
    536 Targ_PrintCmd(ClientData cmd, ClientData dummy)
    537 {
    538     printf("\t%s\n", (char *)cmd);
    539     return (dummy ? 0 : 0);
    540 }
    541 
    542 /*-
    543  *-----------------------------------------------------------------------
    544  * Targ_FmtTime --
    545  *	Format a modification time in some reasonable way and return it.
    546  *
    547  * Results:
    548  *	The time reformatted.
    549  *
    550  * Side Effects:
    551  *	The time is placed in a static area, so it is overwritten
    552  *	with each call.
    553  *
    554  *-----------------------------------------------------------------------
    555  */
    556 char *
    557 Targ_FmtTime(time_t tm)
    558 {
    559     struct tm	  	*parts;
    560     static char	  	buf[128];
    561 
    562     parts = localtime(&tm);
    563     (void)strftime(buf, sizeof buf, "%k:%M:%S %b %d, %Y", parts);
    564     return(buf);
    565 }
    566 
    567 /*-
    568  *-----------------------------------------------------------------------
    569  * Targ_PrintType --
    570  *	Print out a type field giving only those attributes the user can
    571  *	set.
    572  *
    573  * Results:
    574  *
    575  * Side Effects:
    576  *
    577  *-----------------------------------------------------------------------
    578  */
    579 void
    580 Targ_PrintType(int type)
    581 {
    582     int    tbit;
    583 
    584 #define PRINTBIT(attr)	case CONCAT(OP_,attr): printf("." #attr " "); break
    585 #define PRINTDBIT(attr) case CONCAT(OP_,attr): if (DEBUG(TARG))printf("." #attr " "); break
    586 
    587     type &= ~OP_OPMASK;
    588 
    589     while (type) {
    590 	tbit = 1 << (ffs(type) - 1);
    591 	type &= ~tbit;
    592 
    593 	switch(tbit) {
    594 	    PRINTBIT(OPTIONAL);
    595 	    PRINTBIT(USE);
    596 	    PRINTBIT(EXEC);
    597 	    PRINTBIT(IGNORE);
    598 	    PRINTBIT(PRECIOUS);
    599 	    PRINTBIT(SILENT);
    600 	    PRINTBIT(MAKE);
    601 	    PRINTBIT(JOIN);
    602 	    PRINTBIT(INVISIBLE);
    603 	    PRINTBIT(NOTMAIN);
    604 	    PRINTDBIT(LIB);
    605 	    /*XXX: MEMBER is defined, so CONCAT(OP_,MEMBER) gives OP_"%" */
    606 	    case OP_MEMBER: if (DEBUG(TARG))printf(".MEMBER "); break;
    607 	    PRINTDBIT(ARCHV);
    608 	    PRINTDBIT(MADE);
    609 	    PRINTDBIT(PHONY);
    610 	}
    611     }
    612 }
    613 
    614 /*-
    615  *-----------------------------------------------------------------------
    616  * TargPrintNode --
    617  *	print the contents of a node
    618  *-----------------------------------------------------------------------
    619  */
    620 int
    621 Targ_PrintNode(ClientData gnp, ClientData passp)
    622 {
    623     GNode         *gn = (GNode *)gnp;
    624     int	    	  pass = passp ? *(int *)passp : 0;
    625     if (!OP_NOP(gn->type)) {
    626 	printf("#\n");
    627 	if (gn == mainTarg) {
    628 	    printf("# *** MAIN TARGET ***\n");
    629 	}
    630 	if (pass == 2) {
    631 	    if (gn->unmade) {
    632 		printf("# %d unmade children\n", gn->unmade);
    633 	    } else {
    634 		printf("# No unmade children\n");
    635 	    }
    636 	    if (! (gn->type & (OP_JOIN|OP_USE|OP_USEBEFORE|OP_EXEC))) {
    637 		if (gn->mtime != 0) {
    638 		    printf("# last modified %s: %s\n",
    639 			      Targ_FmtTime(gn->mtime),
    640 			      (gn->made == UNMADE ? "unmade" :
    641 			       (gn->made == MADE ? "made" :
    642 				(gn->made == UPTODATE ? "up-to-date" :
    643 				 "error when made"))));
    644 		} else if (gn->made != UNMADE) {
    645 		    printf("# non-existent (maybe): %s\n",
    646 			      (gn->made == MADE ? "made" :
    647 			       (gn->made == UPTODATE ? "up-to-date" :
    648 				(gn->made == ERROR ? "error when made" :
    649 				 "aborted"))));
    650 		} else {
    651 		    printf("# unmade\n");
    652 		}
    653 	    }
    654 	    if (!Lst_IsEmpty (gn->iParents)) {
    655 		printf("# implicit parents: ");
    656 		Lst_ForEach(gn->iParents, TargPrintName, (ClientData)0);
    657 		fputc('\n', stdout);
    658 	    }
    659 	} else {
    660 	    if (gn->unmade)
    661 		printf("# %d unmade children\n", gn->unmade);
    662 	}
    663 	if (!Lst_IsEmpty (gn->parents)) {
    664 	    printf("# parents: ");
    665 	    Lst_ForEach(gn->parents, TargPrintName, (ClientData)0);
    666 	    fputc('\n', stdout);
    667 	}
    668 	if (!Lst_IsEmpty (gn->preds)) {
    669 	    printf("# preds: ");
    670 	    Lst_ForEach(gn->preds, TargPrintName, (ClientData)0);
    671 	    fputc('\n', stdout);
    672 	}
    673 	if (!Lst_IsEmpty (gn->successors)) {
    674 	    printf("# successors: ");
    675 	    Lst_ForEach(gn->successors, TargPrintName, (ClientData)0);
    676 	    fputc('\n', stdout);
    677 	}
    678 
    679 	printf("%-16s", gn->name);
    680 	switch (gn->type & OP_OPMASK) {
    681 	    case OP_DEPENDS:
    682 		printf(": "); break;
    683 	    case OP_FORCE:
    684 		printf("! "); break;
    685 	    case OP_DOUBLEDEP:
    686 		printf(":: "); break;
    687 	}
    688 	Targ_PrintType(gn->type);
    689 	Lst_ForEach(gn->children, TargPrintName, PrintWait);
    690 	fputc('\n', stdout);
    691 	Lst_ForEach(gn->commands, Targ_PrintCmd, (ClientData)0);
    692 	printf("\n\n");
    693 	if (gn->type & OP_DOUBLEDEP) {
    694 	    Lst_ForEach(gn->cohorts, Targ_PrintNode, (ClientData)&pass);
    695 	}
    696     }
    697     return (0);
    698 }
    699 
    700 /*-
    701  *-----------------------------------------------------------------------
    702  * TargPrintOnlySrc --
    703  *	Print only those targets that are just a source.
    704  *
    705  * Results:
    706  *	0.
    707  *
    708  * Side Effects:
    709  *	The name of each file is printed preceded by #\t
    710  *
    711  *-----------------------------------------------------------------------
    712  */
    713 static int
    714 TargPrintOnlySrc(ClientData gnp, ClientData dummy)
    715 {
    716     GNode   	  *gn = (GNode *)gnp;
    717     if (OP_NOP(gn->type))
    718 	printf("#\t%s [%s]\n", gn->name, gn->path ? gn->path : gn->name);
    719 
    720     return (dummy ? 0 : 0);
    721 }
    722 
    723 /*-
    724  *-----------------------------------------------------------------------
    725  * Targ_PrintGraph --
    726  *	print the entire graph. heh heh
    727  *
    728  * Input:
    729  *	pass		Which pass this is. 1 => no processing
    730  *			2 => processing done
    731  *
    732  * Results:
    733  *	none
    734  *
    735  * Side Effects:
    736  *	lots o' output
    737  *-----------------------------------------------------------------------
    738  */
    739 void
    740 Targ_PrintGraph(int pass)
    741 {
    742     printf("#*** Input graph:\n");
    743     Lst_ForEach(allTargets, Targ_PrintNode, (ClientData)&pass);
    744     printf("\n\n");
    745     printf("#\n#   Files that are only sources:\n");
    746     Lst_ForEach(allTargets, TargPrintOnlySrc, (ClientData) 0);
    747     printf("#*** Global Variables:\n");
    748     Var_Dump(VAR_GLOBAL);
    749     printf("#*** Command-line Variables:\n");
    750     Var_Dump(VAR_CMD);
    751     printf("\n");
    752     Dir_PrintDirectories();
    753     printf("\n");
    754     Suff_PrintAll();
    755 }
    756 
    757 static int
    758 TargPropagateCohort(ClientData cgnp, ClientData pgnp)
    759 {
    760     GNode	  *cgn = (GNode *)cgnp;
    761     GNode	  *pgn = (GNode *)pgnp;
    762 
    763     cgn->type |= pgn->type & ~OP_OPMASK;
    764     return (0);
    765 }
    766 
    767 static int
    768 TargPropagateNode(ClientData gnp, ClientData junk __unused)
    769 {
    770     GNode	  *gn = (GNode *)gnp;
    771     if (gn->type & OP_DOUBLEDEP)
    772 	Lst_ForEach(gn->cohorts, TargPropagateCohort, gnp);
    773     return (0);
    774 }
    775 
    776 void
    777 Targ_Propagate(void)
    778 {
    779     Lst_ForEach(allTargets, TargPropagateNode, (ClientData)0);
    780 }
    781