1 /* $NetBSD: targ.c,v 1.27 2002/03/20 18:10:31 pk 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.27 2002/03/20 18:10:31 pk 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.27 2002/03/20 18:10:31 pk 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 #include "make.h" 100 #include "hash.h" 101 #include "dir.h" 102 103 static Lst allTargets; /* the list of all targets found so far */ 104 #ifdef CLEANUP 105 static Lst allGNs; /* List of all the GNodes */ 106 #endif 107 static Hash_Table targets; /* a hash table of same */ 108 109 #define HTSIZE 191 /* initial size of hash table */ 110 111 static int TargPrintOnlySrc __P((ClientData, ClientData)); 112 static int TargPrintName __P((ClientData, ClientData)); 113 static int TargPrintNode __P((ClientData, ClientData)); 114 #ifdef CLEANUP 115 static void TargFreeGN __P((ClientData)); 116 #endif 117 static int TargPropagateCohort __P((ClientData, ClientData)); 118 static int TargPropagateNode __P((ClientData, ClientData)); 119 120 /*- 121 *----------------------------------------------------------------------- 122 * Targ_Init -- 123 * Initialize this module 124 * 125 * Results: 126 * None 127 * 128 * Side Effects: 129 * The allTargets list and the targets hash table are initialized 130 *----------------------------------------------------------------------- 131 */ 132 void 133 Targ_Init () 134 { 135 allTargets = Lst_Init (FALSE); 136 Hash_InitTable (&targets, HTSIZE); 137 } 138 139 /*- 140 *----------------------------------------------------------------------- 141 * Targ_End -- 142 * Finalize this module 143 * 144 * Results: 145 * None 146 * 147 * Side Effects: 148 * All lists and gnodes are cleared 149 *----------------------------------------------------------------------- 150 */ 151 void 152 Targ_End () 153 { 154 #ifdef CLEANUP 155 Lst_Destroy(allTargets, NOFREE); 156 if (allGNs) 157 Lst_Destroy(allGNs, TargFreeGN); 158 Hash_DeleteTable(&targets); 159 #endif 160 } 161 162 /*- 163 *----------------------------------------------------------------------- 164 * Targ_List -- 165 * Return the list of all targets 166 * 167 * Results: 168 * The list of all targets. 169 * 170 * Side Effects: 171 * None 172 *----------------------------------------------------------------------- 173 */ 174 Lst 175 Targ_List () 176 { 177 return allTargets; 178 } 179 180 /*- 181 *----------------------------------------------------------------------- 182 * Targ_NewGN -- 183 * Create and initialize a new graph node 184 * 185 * Results: 186 * An initialized graph node with the name field filled with a copy 187 * of the passed name 188 * 189 * Side Effects: 190 * The gnode is added to the list of all gnodes. 191 *----------------------------------------------------------------------- 192 */ 193 GNode * 194 Targ_NewGN (name) 195 char *name; /* the name to stick in the new node */ 196 { 197 register GNode *gn; 198 199 gn = (GNode *) emalloc (sizeof (GNode)); 200 gn->name = estrdup (name); 201 gn->uname = NULL; 202 gn->path = (char *) 0; 203 if (name[0] == '-' && name[1] == 'l') { 204 gn->type = OP_LIB; 205 } else { 206 gn->type = 0; 207 } 208 gn->unmade = 0; 209 gn->unmade_cohorts = 0; 210 gn->centurion = NULL; 211 gn->made = UNMADE; 212 gn->flags = 0; 213 gn->order = 0; 214 gn->mtime = gn->cmtime = 0; 215 gn->iParents = Lst_Init (FALSE); 216 gn->cohorts = Lst_Init (FALSE); 217 gn->parents = Lst_Init (FALSE); 218 gn->children = Lst_Init (FALSE); 219 gn->successors = Lst_Init (FALSE); 220 gn->preds = Lst_Init (FALSE); 221 Hash_InitTable(&gn->context, 0); 222 gn->commands = Lst_Init (FALSE); 223 gn->suffix = NULL; 224 gn->lineno = 0; 225 gn->fname = NULL; 226 227 #ifdef CLEANUP 228 if (allGNs == NULL) 229 allGNs = Lst_Init(FALSE); 230 Lst_AtEnd(allGNs, (ClientData) gn); 231 #endif 232 233 return (gn); 234 } 235 236 #ifdef CLEANUP 237 /*- 238 *----------------------------------------------------------------------- 239 * TargFreeGN -- 240 * Destroy a GNode 241 * 242 * Results: 243 * None. 244 * 245 * Side Effects: 246 * None. 247 *----------------------------------------------------------------------- 248 */ 249 static void 250 TargFreeGN (gnp) 251 ClientData gnp; 252 { 253 GNode *gn = (GNode *) gnp; 254 255 256 free(gn->name); 257 if (gn->uname) 258 free(gn->uname); 259 if (gn->path) 260 free(gn->path); 261 if (gn->fname) 262 free(gn->fname); 263 264 Lst_Destroy(gn->iParents, NOFREE); 265 Lst_Destroy(gn->cohorts, NOFREE); 266 Lst_Destroy(gn->parents, NOFREE); 267 Lst_Destroy(gn->children, NOFREE); 268 Lst_Destroy(gn->successors, NOFREE); 269 Lst_Destroy(gn->preds, NOFREE); 270 Hash_DeleteTable(&gn->context); 271 Lst_Destroy(gn->commands, NOFREE); 272 free((Address)gn); 273 } 274 #endif 275 276 277 /*- 278 *----------------------------------------------------------------------- 279 * Targ_FindNode -- 280 * Find a node in the list using the given name for matching 281 * 282 * Results: 283 * The node in the list if it was. If it wasn't, return NILGNODE of 284 * flags was TARG_NOCREATE or the newly created and initialized node 285 * if it was TARG_CREATE 286 * 287 * Side Effects: 288 * Sometimes a node is created and added to the list 289 *----------------------------------------------------------------------- 290 */ 291 GNode * 292 Targ_FindNode (name, flags) 293 char *name; /* the name to find */ 294 int flags; /* flags governing events when target not 295 * found */ 296 { 297 GNode *gn; /* node in that element */ 298 Hash_Entry *he; /* New or used hash entry for node */ 299 Boolean isNew; /* Set TRUE if Hash_CreateEntry had to create */ 300 /* an entry for the node */ 301 302 303 if (flags & TARG_CREATE) { 304 he = Hash_CreateEntry (&targets, name, &isNew); 305 if (isNew) { 306 gn = Targ_NewGN (name); 307 Hash_SetValue (he, gn); 308 Var_Append(".ALLTARGETS", name, VAR_GLOBAL); 309 (void) Lst_AtEnd (allTargets, (ClientData)gn); 310 } 311 } else { 312 he = Hash_FindEntry (&targets, name); 313 } 314 315 if (he == (Hash_Entry *) NULL) { 316 return (NILGNODE); 317 } else { 318 return ((GNode *) Hash_GetValue (he)); 319 } 320 } 321 322 /*- 323 *----------------------------------------------------------------------- 324 * Targ_FindList -- 325 * Make a complete list of GNodes from the given list of names 326 * 327 * Results: 328 * A complete list of graph nodes corresponding to all instances of all 329 * the names in names. 330 * 331 * Side Effects: 332 * If flags is TARG_CREATE, nodes will be created for all names in 333 * names which do not yet have graph nodes. If flags is TARG_NOCREATE, 334 * an error message will be printed for each name which can't be found. 335 * ----------------------------------------------------------------------- 336 */ 337 Lst 338 Targ_FindList (names, flags) 339 Lst names; /* list of names to find */ 340 int flags; /* flags used if no node is found for a given 341 * name */ 342 { 343 Lst nodes; /* result list */ 344 register LstNode ln; /* name list element */ 345 register GNode *gn; /* node in tLn */ 346 char *name; 347 348 nodes = Lst_Init (FALSE); 349 350 if (Lst_Open (names) == FAILURE) { 351 return (nodes); 352 } 353 while ((ln = Lst_Next (names)) != NILLNODE) { 354 name = (char *)Lst_Datum(ln); 355 gn = Targ_FindNode (name, flags); 356 if (gn != NILGNODE) { 357 /* 358 * Note: Lst_AtEnd must come before the Lst_Concat so the nodes 359 * are added to the list in the order in which they were 360 * encountered in the makefile. 361 */ 362 (void) Lst_AtEnd (nodes, (ClientData)gn); 363 } else if (flags == TARG_NOCREATE) { 364 Error ("\"%s\" -- target unknown.", name); 365 } 366 } 367 Lst_Close (names); 368 return (nodes); 369 } 370 371 /*- 372 *----------------------------------------------------------------------- 373 * Targ_Ignore -- 374 * Return true if should ignore errors when creating gn 375 * 376 * Results: 377 * TRUE if should ignore errors 378 * 379 * Side Effects: 380 * None 381 *----------------------------------------------------------------------- 382 */ 383 Boolean 384 Targ_Ignore (gn) 385 GNode *gn; /* node to check for */ 386 { 387 if (ignoreErrors || gn->type & OP_IGNORE) { 388 return (TRUE); 389 } else { 390 return (FALSE); 391 } 392 } 393 394 /*- 395 *----------------------------------------------------------------------- 396 * Targ_Silent -- 397 * Return true if be silent when creating gn 398 * 399 * Results: 400 * TRUE if should be silent 401 * 402 * Side Effects: 403 * None 404 *----------------------------------------------------------------------- 405 */ 406 Boolean 407 Targ_Silent (gn) 408 GNode *gn; /* node to check for */ 409 { 410 if (beSilent || gn->type & OP_SILENT) { 411 return (TRUE); 412 } else { 413 return (FALSE); 414 } 415 } 416 417 /*- 418 *----------------------------------------------------------------------- 419 * Targ_Precious -- 420 * See if the given target is precious 421 * 422 * Results: 423 * TRUE if it is precious. FALSE otherwise 424 * 425 * Side Effects: 426 * None 427 *----------------------------------------------------------------------- 428 */ 429 Boolean 430 Targ_Precious (gn) 431 GNode *gn; /* the node to check */ 432 { 433 if (allPrecious || (gn->type & (OP_PRECIOUS|OP_DOUBLEDEP))) { 434 return (TRUE); 435 } else { 436 return (FALSE); 437 } 438 } 439 440 /******************* DEBUG INFO PRINTING ****************/ 441 442 static GNode *mainTarg; /* the main target, as set by Targ_SetMain */ 443 /*- 444 *----------------------------------------------------------------------- 445 * Targ_SetMain -- 446 * Set our idea of the main target we'll be creating. Used for 447 * debugging output. 448 * 449 * Results: 450 * None. 451 * 452 * Side Effects: 453 * "mainTarg" is set to the main target's node. 454 *----------------------------------------------------------------------- 455 */ 456 void 457 Targ_SetMain (gn) 458 GNode *gn; /* The main target we'll create */ 459 { 460 mainTarg = gn; 461 } 462 463 static int 464 TargPrintName (gnp, ppath) 465 ClientData gnp; 466 ClientData ppath; 467 { 468 GNode *gn = (GNode *) gnp; 469 printf ("%s ", gn->name); 470 #ifdef notdef 471 if (ppath) { 472 if (gn->path) { 473 printf ("[%s] ", gn->path); 474 } 475 if (gn == mainTarg) { 476 printf ("(MAIN NAME) "); 477 } 478 } 479 #endif /* notdef */ 480 return (ppath ? 0 : 0); 481 } 482 483 484 int 485 Targ_PrintCmd (cmd, dummy) 486 ClientData cmd; 487 ClientData dummy; 488 { 489 printf ("\t%s\n", (char *) cmd); 490 return (dummy ? 0 : 0); 491 } 492 493 /*- 494 *----------------------------------------------------------------------- 495 * Targ_FmtTime -- 496 * Format a modification time in some reasonable way and return it. 497 * 498 * Results: 499 * The time reformatted. 500 * 501 * Side Effects: 502 * The time is placed in a static area, so it is overwritten 503 * with each call. 504 * 505 *----------------------------------------------------------------------- 506 */ 507 char * 508 Targ_FmtTime (tm) 509 time_t tm; 510 { 511 struct tm *parts; 512 static char buf[128]; 513 514 parts = localtime(&tm); 515 (void)strftime(buf, sizeof buf, "%k:%M:%S %b %d, %Y", parts); 516 return(buf); 517 } 518 519 /*- 520 *----------------------------------------------------------------------- 521 * Targ_PrintType -- 522 * Print out a type field giving only those attributes the user can 523 * set. 524 * 525 * Results: 526 * 527 * Side Effects: 528 * 529 *----------------------------------------------------------------------- 530 */ 531 void 532 Targ_PrintType (type) 533 register int type; 534 { 535 register int tbit; 536 537 #ifdef __STDC__ 538 #define PRINTBIT(attr) case CONCAT(OP_,attr): printf("." #attr " "); break 539 #define PRINTDBIT(attr) case CONCAT(OP_,attr): if (DEBUG(TARG)) printf("." #attr " "); break 540 #else 541 #define PRINTBIT(attr) case CONCAT(OP_,attr): printf(".attr "); break 542 #define PRINTDBIT(attr) case CONCAT(OP_,attr): if (DEBUG(TARG)) printf(".attr "); break 543 #endif /* __STDC__ */ 544 545 type &= ~OP_OPMASK; 546 547 while (type) { 548 tbit = 1 << (ffs(type) - 1); 549 type &= ~tbit; 550 551 switch(tbit) { 552 PRINTBIT(OPTIONAL); 553 PRINTBIT(USE); 554 PRINTBIT(EXEC); 555 PRINTBIT(IGNORE); 556 PRINTBIT(PRECIOUS); 557 PRINTBIT(SILENT); 558 PRINTBIT(MAKE); 559 PRINTBIT(JOIN); 560 PRINTBIT(INVISIBLE); 561 PRINTBIT(NOTMAIN); 562 PRINTDBIT(LIB); 563 /*XXX: MEMBER is defined, so CONCAT(OP_,MEMBER) gives OP_"%" */ 564 case OP_MEMBER: if (DEBUG(TARG)) printf(".MEMBER "); break; 565 PRINTDBIT(ARCHV); 566 PRINTDBIT(MADE); 567 PRINTDBIT(PHONY); 568 } 569 } 570 } 571 572 /*- 573 *----------------------------------------------------------------------- 574 * TargPrintNode -- 575 * print the contents of a node 576 *----------------------------------------------------------------------- 577 */ 578 static int 579 TargPrintNode (gnp, passp) 580 ClientData gnp; 581 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(gnp, dummy) 662 ClientData gnp; 663 ClientData dummy; 664 { 665 GNode *gn = (GNode *) gnp; 666 if (OP_NOP(gn->type)) 667 printf("#\t%s [%s]\n", gn->name, gn->path ? gn->path : gn->name); 668 669 return (dummy ? 0 : 0); 670 } 671 672 /*- 673 *----------------------------------------------------------------------- 674 * Targ_PrintGraph -- 675 * print the entire graph. heh heh 676 * 677 * Results: 678 * none 679 * 680 * Side Effects: 681 * lots o' output 682 *----------------------------------------------------------------------- 683 */ 684 void 685 Targ_PrintGraph (pass) 686 int pass; /* Which pass this is. 1 => no processing 687 * 2 => processing done */ 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 (cgnp, pgnp) 706 ClientData cgnp; 707 ClientData pgnp; 708 { 709 GNode *cgn = (GNode *) cgnp; 710 GNode *pgn = (GNode *) pgnp; 711 712 cgn->type |= pgn->type & ~OP_OPMASK; 713 return (0); 714 } 715 716 static int 717 TargPropagateNode (gnp, junk) 718 ClientData gnp; 719 ClientData junk; 720 { 721 GNode *gn = (GNode *) gnp; 722 if (gn->type & OP_DOUBLEDEP) 723 Lst_ForEach (gn->cohorts, TargPropagateCohort, gnp); 724 return (0); 725 } 726 727 void 728 Targ_Propagate () 729 { 730 Lst_ForEach (allTargets, TargPropagateNode, (ClientData)0); 731 } 732