1 /* $NetBSD: targ.c,v 1.33 2004/07/01 20:38:09 jmc 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.33 2004/07/01 20:38:09 jmc 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.33 2004/07/01 20:38:09 jmc 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 static int TargPrintNode(ClientData, ClientData); 145 #ifdef CLEANUP 146 static void TargFreeGN(ClientData); 147 #endif 148 static int TargPropagateCohort(ClientData, ClientData); 149 static int TargPropagateNode(ClientData, ClientData); 150 151 /*- 152 *----------------------------------------------------------------------- 153 * Targ_Init -- 154 * Initialize this module 155 * 156 * Results: 157 * None 158 * 159 * Side Effects: 160 * The allTargets list and the targets hash table are initialized 161 *----------------------------------------------------------------------- 162 */ 163 void 164 Targ_Init(void) 165 { 166 allTargets = Lst_Init (FALSE); 167 Hash_InitTable (&targets, HTSIZE); 168 } 169 170 /*- 171 *----------------------------------------------------------------------- 172 * Targ_End -- 173 * Finalize this module 174 * 175 * Results: 176 * None 177 * 178 * Side Effects: 179 * All lists and gnodes are cleared 180 *----------------------------------------------------------------------- 181 */ 182 void 183 Targ_End(void) 184 { 185 #ifdef CLEANUP 186 Lst_Destroy(allTargets, NOFREE); 187 if (allGNs) 188 Lst_Destroy(allGNs, TargFreeGN); 189 Hash_DeleteTable(&targets); 190 #endif 191 } 192 193 /*- 194 *----------------------------------------------------------------------- 195 * Targ_List -- 196 * Return the list of all targets 197 * 198 * Results: 199 * The list of all targets. 200 * 201 * Side Effects: 202 * None 203 *----------------------------------------------------------------------- 204 */ 205 Lst 206 Targ_List(void) 207 { 208 return allTargets; 209 } 210 211 /*- 212 *----------------------------------------------------------------------- 213 * Targ_NewGN -- 214 * Create and initialize a new graph node 215 * 216 * Input: 217 * name the name to stick in the new node 218 * 219 * Results: 220 * An initialized graph node with the name field filled with a copy 221 * of the passed name 222 * 223 * Side Effects: 224 * The gnode is added to the list of all gnodes. 225 *----------------------------------------------------------------------- 226 */ 227 GNode * 228 Targ_NewGN(const char *name) 229 { 230 GNode *gn; 231 232 gn = (GNode *) emalloc (sizeof (GNode)); 233 gn->name = estrdup (name); 234 gn->uname = NULL; 235 gn->path = NULL; 236 if (name[0] == '-' && name[1] == 'l') { 237 gn->type = OP_LIB; 238 } else { 239 gn->type = 0; 240 } 241 gn->unmade = 0; 242 gn->unmade_cohorts = 0; 243 gn->centurion = NULL; 244 gn->made = UNMADE; 245 gn->flags = 0; 246 gn->order = 0; 247 gn->mtime = gn->cmtime = 0; 248 gn->iParents = Lst_Init (FALSE); 249 gn->cohorts = Lst_Init (FALSE); 250 gn->parents = Lst_Init (FALSE); 251 gn->children = Lst_Init (FALSE); 252 gn->successors = Lst_Init (FALSE); 253 gn->preds = Lst_Init (FALSE); 254 Hash_InitTable(&gn->context, 0); 255 gn->commands = Lst_Init (FALSE); 256 gn->suffix = NULL; 257 gn->lineno = 0; 258 gn->fname = NULL; 259 260 #ifdef CLEANUP 261 if (allGNs == NULL) 262 allGNs = Lst_Init(FALSE); 263 Lst_AtEnd(allGNs, (ClientData) gn); 264 #endif 265 266 return (gn); 267 } 268 269 #ifdef CLEANUP 270 /*- 271 *----------------------------------------------------------------------- 272 * TargFreeGN -- 273 * Destroy a GNode 274 * 275 * Results: 276 * None. 277 * 278 * Side Effects: 279 * None. 280 *----------------------------------------------------------------------- 281 */ 282 static void 283 TargFreeGN(ClientData gnp) 284 { 285 GNode *gn = (GNode *) gnp; 286 287 288 free(gn->name); 289 if (gn->uname) 290 free(gn->uname); 291 if (gn->path) 292 free(gn->path); 293 if (gn->fname) 294 free(gn->fname); 295 296 Lst_Destroy(gn->iParents, NOFREE); 297 Lst_Destroy(gn->cohorts, NOFREE); 298 Lst_Destroy(gn->parents, NOFREE); 299 Lst_Destroy(gn->children, NOFREE); 300 Lst_Destroy(gn->successors, NOFREE); 301 Lst_Destroy(gn->preds, NOFREE); 302 Hash_DeleteTable(&gn->context); 303 Lst_Destroy(gn->commands, NOFREE); 304 free((Address)gn); 305 } 306 #endif 307 308 309 /*- 310 *----------------------------------------------------------------------- 311 * Targ_FindNode -- 312 * Find a node in the list using the given name for matching 313 * 314 * Input: 315 * name the name to find 316 * flags flags governing events when target not 317 * found 318 * 319 * Results: 320 * The node in the list if it was. If it wasn't, return NILGNODE of 321 * flags was TARG_NOCREATE or the newly created and initialized node 322 * if it was TARG_CREATE 323 * 324 * Side Effects: 325 * Sometimes a node is created and added to the list 326 *----------------------------------------------------------------------- 327 */ 328 GNode * 329 Targ_FindNode(const char *name, int flags) 330 { 331 GNode *gn; /* node in that element */ 332 Hash_Entry *he; /* New or used hash entry for node */ 333 Boolean isNew; /* Set TRUE if Hash_CreateEntry had to create */ 334 /* an entry for the node */ 335 336 337 if (flags & TARG_CREATE) { 338 he = Hash_CreateEntry (&targets, name, &isNew); 339 if (isNew) { 340 gn = Targ_NewGN (name); 341 Hash_SetValue (he, gn); 342 Var_Append(".ALLTARGETS", name, VAR_GLOBAL); 343 (void) Lst_AtEnd (allTargets, (ClientData)gn); 344 } 345 } else { 346 he = Hash_FindEntry (&targets, name); 347 } 348 349 if (he == (Hash_Entry *) NULL) { 350 return (NILGNODE); 351 } else { 352 return ((GNode *) Hash_GetValue (he)); 353 } 354 } 355 356 /*- 357 *----------------------------------------------------------------------- 358 * Targ_FindList -- 359 * Make a complete list of GNodes from the given list of names 360 * 361 * Input: 362 * name list of names to find 363 * flags flags used if no node is found for a given name 364 * 365 * Results: 366 * A complete list of graph nodes corresponding to all instances of all 367 * the names in names. 368 * 369 * Side Effects: 370 * If flags is TARG_CREATE, nodes will be created for all names in 371 * names which do not yet have graph nodes. If flags is TARG_NOCREATE, 372 * an error message will be printed for each name which can't be found. 373 * ----------------------------------------------------------------------- 374 */ 375 Lst 376 Targ_FindList(Lst names, int flags) 377 { 378 Lst nodes; /* result list */ 379 LstNode ln; /* name list element */ 380 GNode *gn; /* node in tLn */ 381 char *name; 382 383 nodes = Lst_Init (FALSE); 384 385 if (Lst_Open (names) == FAILURE) { 386 return (nodes); 387 } 388 while ((ln = Lst_Next (names)) != NILLNODE) { 389 name = (char *)Lst_Datum(ln); 390 gn = Targ_FindNode (name, flags); 391 if (gn != NILGNODE) { 392 /* 393 * Note: Lst_AtEnd must come before the Lst_Concat so the nodes 394 * are added to the list in the order in which they were 395 * encountered in the makefile. 396 */ 397 (void) Lst_AtEnd (nodes, (ClientData)gn); 398 } else if (flags == TARG_NOCREATE) { 399 Error ("\"%s\" -- target unknown.", name); 400 } 401 } 402 Lst_Close (names); 403 return (nodes); 404 } 405 406 /*- 407 *----------------------------------------------------------------------- 408 * Targ_Ignore -- 409 * Return true if should ignore errors when creating gn 410 * 411 * Input: 412 * gn node to check for 413 * 414 * Results: 415 * TRUE if should ignore errors 416 * 417 * Side Effects: 418 * None 419 *----------------------------------------------------------------------- 420 */ 421 Boolean 422 Targ_Ignore(GNode *gn) 423 { 424 if (ignoreErrors || gn->type & OP_IGNORE) { 425 return (TRUE); 426 } else { 427 return (FALSE); 428 } 429 } 430 431 /*- 432 *----------------------------------------------------------------------- 433 * Targ_Silent -- 434 * Return true if be silent when creating gn 435 * 436 * Input: 437 * gn node to check for 438 * 439 * Results: 440 * TRUE if should be silent 441 * 442 * Side Effects: 443 * None 444 *----------------------------------------------------------------------- 445 */ 446 Boolean 447 Targ_Silent(GNode *gn) 448 { 449 if (beSilent || gn->type & OP_SILENT) { 450 return (TRUE); 451 } else { 452 return (FALSE); 453 } 454 } 455 456 /*- 457 *----------------------------------------------------------------------- 458 * Targ_Precious -- 459 * See if the given target is precious 460 * 461 * Input: 462 * gn the node to check 463 * 464 * Results: 465 * TRUE if it is precious. FALSE otherwise 466 * 467 * Side Effects: 468 * None 469 *----------------------------------------------------------------------- 470 */ 471 Boolean 472 Targ_Precious(GNode *gn) 473 { 474 if (allPrecious || (gn->type & (OP_PRECIOUS|OP_DOUBLEDEP))) { 475 return (TRUE); 476 } else { 477 return (FALSE); 478 } 479 } 480 481 /******************* DEBUG INFO PRINTING ****************/ 482 483 static GNode *mainTarg; /* the main target, as set by Targ_SetMain */ 484 /*- 485 *----------------------------------------------------------------------- 486 * Targ_SetMain -- 487 * Set our idea of the main target we'll be creating. Used for 488 * debugging output. 489 * 490 * Input: 491 * gn The main target we'll create 492 * 493 * Results: 494 * None. 495 * 496 * Side Effects: 497 * "mainTarg" is set to the main target's node. 498 *----------------------------------------------------------------------- 499 */ 500 void 501 Targ_SetMain(GNode *gn) 502 { 503 mainTarg = gn; 504 } 505 506 static int 507 TargPrintName(ClientData gnp, ClientData ppath) 508 { 509 GNode *gn = (GNode *) gnp; 510 printf ("%s ", gn->name); 511 #ifdef notdef 512 if (ppath) { 513 if (gn->path) { 514 printf ("[%s] ", gn->path); 515 } 516 if (gn == mainTarg) { 517 printf ("(MAIN NAME) "); 518 } 519 } 520 #endif /* notdef */ 521 return (ppath ? 0 : 0); 522 } 523 524 525 int 526 Targ_PrintCmd(ClientData cmd, ClientData dummy) 527 { 528 printf ("\t%s\n", (char *) cmd); 529 return (dummy ? 0 : 0); 530 } 531 532 /*- 533 *----------------------------------------------------------------------- 534 * Targ_FmtTime -- 535 * Format a modification time in some reasonable way and return it. 536 * 537 * Results: 538 * The time reformatted. 539 * 540 * Side Effects: 541 * The time is placed in a static area, so it is overwritten 542 * with each call. 543 * 544 *----------------------------------------------------------------------- 545 */ 546 char * 547 Targ_FmtTime(time_t tm) 548 { 549 struct tm *parts; 550 static char buf[128]; 551 552 parts = localtime(&tm); 553 (void)strftime(buf, sizeof buf, "%k:%M:%S %b %d, %Y", parts); 554 return(buf); 555 } 556 557 /*- 558 *----------------------------------------------------------------------- 559 * Targ_PrintType -- 560 * Print out a type field giving only those attributes the user can 561 * set. 562 * 563 * Results: 564 * 565 * Side Effects: 566 * 567 *----------------------------------------------------------------------- 568 */ 569 void 570 Targ_PrintType(int type) 571 { 572 int tbit; 573 574 #define PRINTBIT(attr) case CONCAT(OP_,attr): printf("." #attr " "); break 575 #define PRINTDBIT(attr) case CONCAT(OP_,attr): if (DEBUG(TARG)) printf("." #attr " "); break 576 577 type &= ~OP_OPMASK; 578 579 while (type) { 580 tbit = 1 << (ffs(type) - 1); 581 type &= ~tbit; 582 583 switch(tbit) { 584 PRINTBIT(OPTIONAL); 585 PRINTBIT(USE); 586 PRINTBIT(EXEC); 587 PRINTBIT(IGNORE); 588 PRINTBIT(PRECIOUS); 589 PRINTBIT(SILENT); 590 PRINTBIT(MAKE); 591 PRINTBIT(JOIN); 592 PRINTBIT(INVISIBLE); 593 PRINTBIT(NOTMAIN); 594 PRINTDBIT(LIB); 595 /*XXX: MEMBER is defined, so CONCAT(OP_,MEMBER) gives OP_"%" */ 596 case OP_MEMBER: if (DEBUG(TARG)) printf(".MEMBER "); break; 597 PRINTDBIT(ARCHV); 598 PRINTDBIT(MADE); 599 PRINTDBIT(PHONY); 600 } 601 } 602 } 603 604 /*- 605 *----------------------------------------------------------------------- 606 * TargPrintNode -- 607 * print the contents of a node 608 *----------------------------------------------------------------------- 609 */ 610 static int 611 TargPrintNode(ClientData gnp, ClientData passp) 612 { 613 GNode *gn = (GNode *) gnp; 614 int pass = *(int *) passp; 615 if (!OP_NOP(gn->type)) { 616 printf("#\n"); 617 if (gn == mainTarg) { 618 printf("# *** MAIN TARGET ***\n"); 619 } 620 if (pass == 2) { 621 if (gn->unmade) { 622 printf("# %d unmade children\n", gn->unmade); 623 } else { 624 printf("# No unmade children\n"); 625 } 626 if (! (gn->type & (OP_JOIN|OP_USE|OP_USEBEFORE|OP_EXEC))) { 627 if (gn->mtime != 0) { 628 printf("# last modified %s: %s\n", 629 Targ_FmtTime(gn->mtime), 630 (gn->made == UNMADE ? "unmade" : 631 (gn->made == MADE ? "made" : 632 (gn->made == UPTODATE ? "up-to-date" : 633 "error when made")))); 634 } else if (gn->made != UNMADE) { 635 printf("# non-existent (maybe): %s\n", 636 (gn->made == MADE ? "made" : 637 (gn->made == UPTODATE ? "up-to-date" : 638 (gn->made == ERROR ? "error when made" : 639 "aborted")))); 640 } else { 641 printf("# unmade\n"); 642 } 643 } 644 if (!Lst_IsEmpty (gn->iParents)) { 645 printf("# implicit parents: "); 646 Lst_ForEach (gn->iParents, TargPrintName, (ClientData)0); 647 fputc ('\n', stdout); 648 } 649 } 650 if (!Lst_IsEmpty (gn->parents)) { 651 printf("# parents: "); 652 Lst_ForEach (gn->parents, TargPrintName, (ClientData)0); 653 fputc ('\n', stdout); 654 } 655 656 printf("%-16s", gn->name); 657 switch (gn->type & OP_OPMASK) { 658 case OP_DEPENDS: 659 printf(": "); break; 660 case OP_FORCE: 661 printf("! "); break; 662 case OP_DOUBLEDEP: 663 printf(":: "); break; 664 } 665 Targ_PrintType (gn->type); 666 Lst_ForEach (gn->children, TargPrintName, (ClientData)0); 667 fputc ('\n', stdout); 668 Lst_ForEach (gn->commands, Targ_PrintCmd, (ClientData)0); 669 printf("\n\n"); 670 if (gn->type & OP_DOUBLEDEP) { 671 Lst_ForEach (gn->cohorts, TargPrintNode, (ClientData)&pass); 672 } 673 } 674 return (0); 675 } 676 677 /*- 678 *----------------------------------------------------------------------- 679 * TargPrintOnlySrc -- 680 * Print only those targets that are just a source. 681 * 682 * Results: 683 * 0. 684 * 685 * Side Effects: 686 * The name of each file is printed preceded by #\t 687 * 688 *----------------------------------------------------------------------- 689 */ 690 static int 691 TargPrintOnlySrc(ClientData gnp, ClientData dummy) 692 { 693 GNode *gn = (GNode *) gnp; 694 if (OP_NOP(gn->type)) 695 printf("#\t%s [%s]\n", gn->name, gn->path ? gn->path : gn->name); 696 697 return (dummy ? 0 : 0); 698 } 699 700 /*- 701 *----------------------------------------------------------------------- 702 * Targ_PrintGraph -- 703 * print the entire graph. heh heh 704 * 705 * Input: 706 * pass Which pass this is. 1 => no processing 707 * 2 => processing done 708 * 709 * Results: 710 * none 711 * 712 * Side Effects: 713 * lots o' output 714 *----------------------------------------------------------------------- 715 */ 716 void 717 Targ_PrintGraph(int pass) 718 { 719 printf("#*** Input graph:\n"); 720 Lst_ForEach (allTargets, TargPrintNode, (ClientData)&pass); 721 printf("\n\n"); 722 printf("#\n# Files that are only sources:\n"); 723 Lst_ForEach (allTargets, TargPrintOnlySrc, (ClientData) 0); 724 printf("#*** Global Variables:\n"); 725 Var_Dump (VAR_GLOBAL); 726 printf("#*** Command-line Variables:\n"); 727 Var_Dump (VAR_CMD); 728 printf("\n"); 729 Dir_PrintDirectories(); 730 printf("\n"); 731 Suff_PrintAll(); 732 } 733 734 static int 735 TargPropagateCohort(ClientData cgnp, ClientData pgnp) 736 { 737 GNode *cgn = (GNode *) cgnp; 738 GNode *pgn = (GNode *) pgnp; 739 740 cgn->type |= pgn->type & ~OP_OPMASK; 741 return (0); 742 } 743 744 static int 745 TargPropagateNode(ClientData gnp, ClientData junk __unused) 746 { 747 GNode *gn = (GNode *) gnp; 748 if (gn->type & OP_DOUBLEDEP) 749 Lst_ForEach (gn->cohorts, TargPropagateCohort, gnp); 750 return (0); 751 } 752 753 void 754 Targ_Propagate(void) 755 { 756 Lst_ForEach (allTargets, TargPropagateNode, (ClientData)0); 757 } 758