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