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