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