| 1 | /*- |
| 2 | * targ.c -- |
| 3 | * Functions for maintaining the Lst allTargets. Target nodes are |
| 4 | * kept in two structures: a Lst, maintained by the list library, and a |
| 5 | * hash table, maintained by the hash library. |
| 6 | * |
| 7 | * Copyright (c) 1988, 1989 by the Regents of the University of California |
| 8 | * Copyright (c) 1988, 1989 by Adam de Boor |
| 9 | * Copyright (c) 1989 by Berkeley Softworks |
| 10 | * |
| 11 | * Permission to use, copy, modify, and distribute this |
| 12 | * software and its documentation for any non-commercial purpose |
| 13 | * and without fee is hereby granted, provided that the above copyright |
| 14 | * notice appears in all copies. The University of California, |
| 15 | * Berkeley Softworks and Adam de Boor make no representations about |
| 16 | * the suitability of this software for any purpose. It is provided |
| 17 | * "as is" without express or implied warranty. |
| 18 | * |
| 19 | * Interface: |
| 20 | * Targ_Init Initialization procedure. |
| 21 | * |
| 22 | * Targ_NewGN Create a new GNode for the passed target |
| 23 | * (string). The node is *not* placed in the |
| 24 | * hash table, though all its fields are |
| 25 | * initialized. |
| 26 | * |
| 27 | * Targ_FindNode Find the node for a given target, creating |
| 28 | * and storing it if it doesn't exist and the |
| 29 | * flags are right (TARG_CREATE) |
| 30 | * |
| 31 | * Targ_FindList Given a list of names, find nodes for all |
| 32 | * of them. If a name doesn't exist and the |
| 33 | * TARG_NOCREATE flag was given, an error message |
| 34 | * is printed. Else, if a name doesn't exist, |
| 35 | * its node is created. |
| 36 | * |
| 37 | * Targ_Ignore Return TRUE if errors should be ignored when |
| 38 | * creating the given target. |
| 39 | * |
| 40 | * Targ_Silent Return TRUE if we should be silent when |
| 41 | * creating the given target. |
| 42 | * |
| 43 | * Targ_Precious Return TRUE if the target is precious and |
| 44 | * should not be removed if we are interrupted. |
| 45 | * |
| 46 | * Debugging: |
| 47 | * Targ_PrintGraph Print out the entire graphm all variables |
| 48 | * and statistics for the directory cache. Should |
| 49 | * print something for suffixes, too, but... |
| 50 | */ |
| 51 | #ifndef lint |
| 52 | static char *rcsid = "$Id: targ.c,v 1.38 89/11/14 13:44:15 adam Exp $ SPRITE (Berkeley)"; |
| 53 | #endif lint |
| 54 | |
| 55 | #include <stdio.h> |
| 56 | #include <time.h> |
| 57 | #include "make.h" |
| 58 | #include "hash.h" |
| 59 | |
| 60 | static Lst allTargets; /* the list of all targets found so far */ |
| 61 | static Hash_Table targets; /* a hash table of same */ |
| 62 | |
| 63 | #define HTSIZE 191 /* initial size of hash table */ |
| 64 | |
| 65 | /*- |
| 66 | *----------------------------------------------------------------------- |
| 67 | * Targ_Init -- |
| 68 | * Initialize this module |
| 69 | * |
| 70 | * Results: |
| 71 | * None |
| 72 | * |
| 73 | * Side Effects: |
| 74 | * The allTargets list and the targets hash table are initialized |
| 75 | *----------------------------------------------------------------------- |
| 76 | */ |
| 77 | void |
| 78 | Targ_Init () |
| 79 | { |
| 80 | allTargets = Lst_Init (FALSE); |
| 81 | Hash_InitTable (&targets, HTSIZE, HASH_STRING_KEYS); |
| 82 | } |
| 83 | \f |
| 84 | /*- |
| 85 | *----------------------------------------------------------------------- |
| 86 | * Targ_NewGN -- |
| 87 | * Create and initialize a new graph node |
| 88 | * |
| 89 | * Results: |
| 90 | * An initialized graph node with the name field filled with a copy |
| 91 | * of the passed name |
| 92 | * |
| 93 | * Side Effects: |
| 94 | * None. |
| 95 | *----------------------------------------------------------------------- |
| 96 | */ |
| 97 | GNode * |
| 98 | Targ_NewGN (name) |
| 99 | char *name; /* the name to stick in the new node */ |
| 100 | { |
| 101 | register GNode *gn; |
| 102 | |
| 103 | gn = (GNode *) malloc (sizeof (GNode)); |
| 104 | gn->name = Str_New (name); |
| 105 | gn->path = (char *) 0; |
| 106 | if (name[0] == '-' && name[1] == 'l') { |
| 107 | gn->type = OP_LIB; |
| 108 | } else { |
| 109 | gn->type = 0; |
| 110 | } |
| 111 | gn->unmade = 0; |
| 112 | gn->make = FALSE; |
| 113 | gn->made = UNMADE; |
| 114 | gn->childMade = FALSE; |
| 115 | gn->mtime = gn->cmtime = 0; |
| 116 | gn->iParents = Lst_Init (FALSE); |
| 117 | gn->cohorts = Lst_Init (FALSE); |
| 118 | gn->parents = Lst_Init (FALSE); |
| 119 | gn->children = Lst_Init (FALSE); |
| 120 | gn->successors = Lst_Init(FALSE); |
| 121 | gn->preds = Lst_Init(FALSE); |
| 122 | gn->context = Lst_Init (FALSE); |
| 123 | gn->commands = Lst_Init (FALSE); |
| 124 | |
| 125 | return (gn); |
| 126 | } |
| 127 | \f |
| 128 | /*- |
| 129 | *----------------------------------------------------------------------- |
| 130 | * Targ_FindNode -- |
| 131 | * Find a node in the list using the given name for matching |
| 132 | * |
| 133 | * Results: |
| 134 | * The node in the list if it was. If it wasn't, return NILGNODE of |
| 135 | * flags was TARG_NOCREATE or the newly created and initialized node |
| 136 | * if it was TARG_CREATE |
| 137 | * |
| 138 | * Side Effects: |
| 139 | * Sometimes a node is created and added to the list |
| 140 | *----------------------------------------------------------------------- |
| 141 | */ |
| 142 | GNode * |
| 143 | Targ_FindNode (name, flags) |
| 144 | char *name; /* the name to find */ |
| 145 | int flags; /* flags governing events when target not |
| 146 | * found */ |
| 147 | { |
| 148 | GNode *gn; /* node in that element */ |
| 149 | Hash_Entry *he; /* New or used hash entry for node */ |
| 150 | Boolean isNew; /* Set TRUE if Hash_CreateEntry had to create */ |
| 151 | /* an entry for the node */ |
| 152 | |
| 153 | |
| 154 | if (flags & TARG_CREATE) { |
| 155 | he = Hash_CreateEntry (&targets, name, &isNew); |
| 156 | if (isNew) { |
| 157 | gn = Targ_NewGN (name); |
| 158 | Hash_SetValue (he, gn); |
| 159 | (void) Lst_AtEnd (allTargets, (ClientData)gn); |
| 160 | } |
| 161 | } else { |
| 162 | he = Hash_FindEntry (&targets, name); |
| 163 | } |
| 164 | |
| 165 | if (he == (Hash_Entry *) NULL) { |
| 166 | return (NILGNODE); |
| 167 | } else { |
| 168 | return ((GNode *) Hash_GetValue (he)); |
| 169 | } |
| 170 | } |
| 171 | \f |
| 172 | /*- |
| 173 | *----------------------------------------------------------------------- |
| 174 | * Targ_FindList -- |
| 175 | * Make a complete list of GNodes from the given list of names |
| 176 | * |
| 177 | * Results: |
| 178 | * A complete list of graph nodes corresponding to all instances of all |
| 179 | * the names in names. |
| 180 | * |
| 181 | * Side Effects: |
| 182 | * If flags is TARG_CREATE, nodes will be created for all names in |
| 183 | * names which do not yet have graph nodes. If flags is TARG_NOCREATE, |
| 184 | * an error message will be printed for each name which can't be found. |
| 185 | * ----------------------------------------------------------------------- |
| 186 | */ |
| 187 | Lst |
| 188 | Targ_FindList (names, flags) |
| 189 | Lst names; /* list of names to find */ |
| 190 | int flags; /* flags used if no node is found for a given |
| 191 | * name */ |
| 192 | { |
| 193 | Lst nodes; /* result list */ |
| 194 | register LstNode ln; /* name list element */ |
| 195 | register GNode *gn; /* node in tLn */ |
| 196 | char *name; |
| 197 | |
| 198 | nodes = Lst_Init (FALSE); |
| 199 | |
| 200 | if (Lst_Open (names) == FAILURE) { |
| 201 | return (nodes); |
| 202 | } |
| 203 | while ((ln = Lst_Next (names)) != NILLNODE) { |
| 204 | name = (char *)Lst_Datum(ln); |
| 205 | gn = Targ_FindNode (name, flags); |
| 206 | if (gn != NILGNODE) { |
| 207 | /* |
| 208 | * Note: Lst_AtEnd must come before the Lst_Concat so the nodes |
| 209 | * are added to the list in the order in which they were |
| 210 | * encountered in the makefile. |
| 211 | */ |
| 212 | (void) Lst_AtEnd (nodes, (ClientData)gn); |
| 213 | if (gn->type & OP_DOUBLEDEP) { |
| 214 | (void)Lst_Concat (nodes, gn->cohorts, LST_CONCNEW); |
| 215 | } |
| 216 | } else if (flags == TARG_NOCREATE) { |
| 217 | Error ("\"%s\" -- target unknown.", name); |
| 218 | } |
| 219 | } |
| 220 | Lst_Close (names); |
| 221 | return (nodes); |
| 222 | } |
| 223 | \f |
| 224 | /*- |
| 225 | *----------------------------------------------------------------------- |
| 226 | * Targ_Ignore -- |
| 227 | * Return true if should ignore errors when creating gn |
| 228 | * |
| 229 | * Results: |
| 230 | * TRUE if should ignore errors |
| 231 | * |
| 232 | * Side Effects: |
| 233 | * None |
| 234 | *----------------------------------------------------------------------- |
| 235 | */ |
| 236 | Boolean |
| 237 | Targ_Ignore (gn) |
| 238 | GNode *gn; /* node to check for */ |
| 239 | { |
| 240 | if (ignoreErrors || gn->type & OP_IGNORE) { |
| 241 | return (TRUE); |
| 242 | } else { |
| 243 | return (FALSE); |
| 244 | } |
| 245 | } |
| 246 | \f |
| 247 | /*- |
| 248 | *----------------------------------------------------------------------- |
| 249 | * Targ_Silent -- |
| 250 | * Return true if be silent when creating gn |
| 251 | * |
| 252 | * Results: |
| 253 | * TRUE if should be silent |
| 254 | * |
| 255 | * Side Effects: |
| 256 | * None |
| 257 | *----------------------------------------------------------------------- |
| 258 | */ |
| 259 | Boolean |
| 260 | Targ_Silent (gn) |
| 261 | GNode *gn; /* node to check for */ |
| 262 | { |
| 263 | if (beSilent || gn->type & OP_SILENT) { |
| 264 | return (TRUE); |
| 265 | } else { |
| 266 | return (FALSE); |
| 267 | } |
| 268 | } |
| 269 | \f |
| 270 | /*- |
| 271 | *----------------------------------------------------------------------- |
| 272 | * Targ_Precious -- |
| 273 | * See if the given target is precious |
| 274 | * |
| 275 | * Results: |
| 276 | * TRUE if it is precious. FALSE otherwise |
| 277 | * |
| 278 | * Side Effects: |
| 279 | * None |
| 280 | *----------------------------------------------------------------------- |
| 281 | */ |
| 282 | Boolean |
| 283 | Targ_Precious (gn) |
| 284 | GNode *gn; /* the node to check */ |
| 285 | { |
| 286 | if (allPrecious || (gn->type & (OP_PRECIOUS|OP_DOUBLEDEP))) { |
| 287 | return (TRUE); |
| 288 | } else { |
| 289 | return (FALSE); |
| 290 | } |
| 291 | } |
| 292 | \f |
| 293 | /******************* DEBUG INFO PRINTING ****************/ |
| 294 | |
| 295 | static GNode *mainTarg; /* the main target, as set by Targ_SetMain */ |
| 296 | /*- |
| 297 | *----------------------------------------------------------------------- |
| 298 | * Targ_SetMain -- |
| 299 | * Set our idea of the main target we'll be creating. Used for |
| 300 | * debugging output. |
| 301 | * |
| 302 | * Results: |
| 303 | * None. |
| 304 | * |
| 305 | * Side Effects: |
| 306 | * "mainTarg" is set to the main target's node. |
| 307 | *----------------------------------------------------------------------- |
| 308 | */ |
| 309 | void |
| 310 | Targ_SetMain (gn) |
| 311 | GNode *gn; /* The main target we'll create */ |
| 312 | { |
| 313 | mainTarg = gn; |
| 314 | } |
| 315 | |
| 316 | static int |
| 317 | TargPrintName (gn, ppath) |
| 318 | GNode *gn; |
| 319 | int ppath; |
| 320 | { |
| 321 | printf ("%s ", gn->name); |
| 322 | #ifdef notdef |
| 323 | if (ppath) { |
| 324 | if (gn->path) { |
| 325 | printf ("[%s] ", gn->path); |
| 326 | } |
| 327 | if (gn == mainTarg) { |
| 328 | printf ("(MAIN NAME) "); |
| 329 | } |
| 330 | } |
| 331 | #endif notdef |
| 332 | return (0); |
| 333 | } |
| 334 | |
| 335 | |
| 336 | int |
| 337 | Targ_PrintCmd (cmd) |
| 338 | char *cmd; |
| 339 | { |
| 340 | printf ("\t%s\n", cmd); |
| 341 | return (0); |
| 342 | } |
| 343 | |
| 344 | /*- |
| 345 | *----------------------------------------------------------------------- |
| 346 | * Targ_FmtTime -- |
| 347 | * Format a modification time in some reasonable way and return it. |
| 348 | * |
| 349 | * Results: |
| 350 | * The time reformatted. |
| 351 | * |
| 352 | * Side Effects: |
| 353 | * The time is placed in a static area, so it is overwritten |
| 354 | * with each call. |
| 355 | * |
| 356 | *----------------------------------------------------------------------- |
| 357 | */ |
| 358 | char * |
| 359 | Targ_FmtTime (time) |
| 360 | int time; |
| 361 | { |
| 362 | struct tm *parts; |
| 363 | static char buf[40]; |
| 364 | static char *months[] = { |
| 365 | "Jan", "Feb", "Mar", "Apr", "May", "Jun", |
| 366 | "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" |
| 367 | }; |
| 368 | |
| 369 | parts = localtime(&time); |
| 370 | |
| 371 | sprintf (buf, "%d:%02d:%02d %s %d, 19%d", |
| 372 | parts->tm_hour, parts->tm_min, parts->tm_sec, |
| 373 | months[parts->tm_mon], parts->tm_mday, parts->tm_year); |
| 374 | return(buf); |
| 375 | } |
| 376 | |
| 377 | /*- |
| 378 | *----------------------------------------------------------------------- |
| 379 | * Targ_PrintType -- |
| 380 | * Print out a type field giving only those attributes the user can |
| 381 | * set. |
| 382 | * |
| 383 | * Results: |
| 384 | * |
| 385 | * Side Effects: |
| 386 | * |
| 387 | *----------------------------------------------------------------------- |
| 388 | */ |
| 389 | void |
| 390 | Targ_PrintType (type) |
| 391 | register int type; |
| 392 | { |
| 393 | register int tbit; |
| 394 | |
| 395 | #ifdef __STDC__ |
| 396 | #define PRINTBIT(attr) case CONCAT(OP_,attr): printf("." #attr " "); break |
| 397 | #define PRINTDBIT(attr) case CONCAT(OP_,attr): if (DEBUG(TARG)) printf("." #attr " "); break |
| 398 | #else |
| 399 | #define PRINTBIT(attr) case CONCAT(OP_,attr): printf(".attr "); break |
| 400 | #define PRINTDBIT(attr) case CONCAT(OP_,attr): if (DEBUG(TARG)) printf(".attr "); break |
| 401 | #endif /* __STDC__ */ |
| 402 | |
| 403 | type &= ~OP_OPMASK; |
| 404 | |
| 405 | while (type) { |
| 406 | tbit = 1 << (ffs(type) - 1); |
| 407 | type &= ~tbit; |
| 408 | |
| 409 | switch(tbit) { |
| 410 | PRINTBIT(DONTCARE); |
| 411 | PRINTBIT(USE); |
| 412 | PRINTBIT(EXEC); |
| 413 | PRINTBIT(IGNORE); |
| 414 | PRINTBIT(PRECIOUS); |
| 415 | PRINTBIT(SILENT); |
| 416 | PRINTBIT(MAKE); |
| 417 | PRINTBIT(JOIN); |
| 418 | PRINTBIT(EXPORT); |
| 419 | PRINTBIT(NOEXPORT); |
| 420 | PRINTBIT(EXPORTSAME); |
| 421 | PRINTBIT(INVISIBLE); |
| 422 | PRINTBIT(NOTMAIN); |
| 423 | PRINTDBIT(LIB); |
| 424 | PRINTBIT(M68020); |
| 425 | /*XXX: MEMBER is defined, so CONCAT(OP_,MEMBER) gives OP_"%" */ |
| 426 | case OP_MEMBER: if (DEBUG(TARG)) printf(".MEMBER "); break; |
| 427 | PRINTDBIT(ARCHV); |
| 428 | } |
| 429 | } |
| 430 | } |
| 431 | |
| 432 | /*- |
| 433 | *----------------------------------------------------------------------- |
| 434 | * TargPrintNode -- |
| 435 | * print the contents of a node |
| 436 | *----------------------------------------------------------------------- |
| 437 | */ |
| 438 | static int |
| 439 | TargPrintNode (gn, pass) |
| 440 | GNode *gn; |
| 441 | int pass; |
| 442 | { |
| 443 | if (!OP_NOP(gn->type)) { |
| 444 | printf("#\n"); |
| 445 | if (gn == mainTarg) { |
| 446 | printf("# *** MAIN TARGET ***\n"); |
| 447 | } |
| 448 | if (pass == 2) { |
| 449 | if (gn->unmade) { |
| 450 | printf("# %d unmade children\n", gn->unmade); |
| 451 | } else { |
| 452 | printf("# No unmade children\n"); |
| 453 | } |
| 454 | if (! (gn->type & (OP_JOIN|OP_USE|OP_EXEC))) { |
| 455 | if (gn->mtime != 0) { |
| 456 | printf("# last modified %s: %s\n", |
| 457 | Targ_FmtTime(gn->mtime), |
| 458 | (gn->made == UNMADE ? "unmade" : |
| 459 | (gn->made == MADE ? "made" : |
| 460 | (gn->made == UPTODATE ? "up-to-date" : |
| 461 | "error when made")))); |
| 462 | } else if (gn->made != UNMADE) { |
| 463 | printf("# non-existent (maybe): %s\n", |
| 464 | (gn->made == MADE ? "made" : |
| 465 | (gn->made == UPTODATE ? "up-to-date" : |
| 466 | (gn->made == ERROR ? "error when made" : |
| 467 | "aborted")))); |
| 468 | } else { |
| 469 | printf("# unmade\n"); |
| 470 | } |
| 471 | } |
| 472 | if (!Lst_IsEmpty (gn->iParents)) { |
| 473 | printf("# implicit parents: "); |
| 474 | Lst_ForEach (gn->iParents, TargPrintName, (ClientData)0); |
| 475 | putc ('\n', stdout); |
| 476 | } |
| 477 | } |
| 478 | if (!Lst_IsEmpty (gn->parents)) { |
| 479 | printf("# parents: "); |
| 480 | Lst_ForEach (gn->parents, TargPrintName, (ClientData)0); |
| 481 | putc ('\n', stdout); |
| 482 | } |
| 483 | |
| 484 | printf("%-16s", gn->name); |
| 485 | switch (gn->type & OP_OPMASK) { |
| 486 | case OP_DEPENDS: |
| 487 | printf(": "); break; |
| 488 | case OP_FORCE: |
| 489 | printf("! "); break; |
| 490 | case OP_DOUBLEDEP: |
| 491 | printf(":: "); break; |
| 492 | } |
| 493 | Targ_PrintType (gn->type); |
| 494 | Lst_ForEach (gn->children, TargPrintName, (ClientData)0); |
| 495 | putc ('\n', stdout); |
| 496 | Lst_ForEach (gn->commands, Targ_PrintCmd, (ClientData)0); |
| 497 | printf("\n\n"); |
| 498 | if (gn->type & OP_DOUBLEDEP) { |
| 499 | Lst_ForEach (gn->cohorts, TargPrintNode, (ClientData)pass); |
| 500 | } |
| 501 | } |
| 502 | return (0); |
| 503 | } |
| 504 | \f |
| 505 | /*- |
| 506 | *----------------------------------------------------------------------- |
| 507 | * TargPrintOnlySrc -- |
| 508 | * Print only those targets that are just a source. |
| 509 | * |
| 510 | * Results: |
| 511 | * 0. |
| 512 | * |
| 513 | * Side Effects: |
| 514 | * The name of each file is printed preceeded by #\t |
| 515 | * |
| 516 | *----------------------------------------------------------------------- |
| 517 | */ |
| 518 | static int |
| 519 | TargPrintOnlySrc(gn) |
| 520 | GNode *gn; |
| 521 | { |
| 522 | if (OP_NOP(gn->type)) { |
| 523 | printf("#\t%s [%s]\n", gn->name, |
| 524 | gn->path ? gn->path : gn->name); |
| 525 | } |
| 526 | return (0); |
| 527 | } |
| 528 | \f |
| 529 | /*- |
| 530 | *----------------------------------------------------------------------- |
| 531 | * Targ_PrintGraph -- |
| 532 | * print the entire graph. heh heh |
| 533 | * |
| 534 | * Results: |
| 535 | * none |
| 536 | * |
| 537 | * Side Effects: |
| 538 | * lots o' output |
| 539 | *----------------------------------------------------------------------- |
| 540 | */ |
| 541 | Targ_PrintGraph (pass) |
| 542 | int pass; /* Which pass this is. 1 => no processing |
| 543 | * 2 => processing done */ |
| 544 | { |
| 545 | printf("#*** Input graph:\n"); |
| 546 | Lst_ForEach (allTargets, TargPrintNode, (ClientData)pass); |
| 547 | printf("\n\n"); |
| 548 | printf("#\n# Files that are only sources:\n"); |
| 549 | Lst_ForEach (allTargets, TargPrintOnlySrc); |
| 550 | printf("#*** Global Variables:\n"); |
| 551 | Var_Dump (VAR_GLOBAL); |
| 552 | printf("#*** Command-line Variables:\n"); |
| 553 | Var_Dump (VAR_CMD); |
| 554 | printf("\n"); |
| 555 | Dir_PrintDirectories(); |
| 556 | printf("\n"); |
| 557 | Suff_PrintAll(); |
| 558 | } |