1 /* $NetBSD: targ.c,v 1.88 2020/09/13 15:15:51 rillig 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 /*- 72 * targ.c -- 73 * Functions for maintaining the Lst allTargets. Target nodes are 74 * kept in two structures: a Lst and a hash table. 75 * 76 * Interface: 77 * Targ_Init Initialization procedure. 78 * 79 * Targ_End Cleanup the module 80 * 81 * Targ_List Return the list of all targets so far. 82 * 83 * Targ_NewGN Create a new GNode for the passed target 84 * (string). The node is *not* placed in the 85 * hash table, though all its fields are 86 * initialized. 87 * 88 * Targ_FindNode Find the node for a given target, creating 89 * and storing it if it doesn't exist and the 90 * flags are right (TARG_CREATE) 91 * 92 * Targ_FindList Given a list of names, find nodes for all 93 * of them. If a name doesn't exist and the 94 * TARG_NOCREATE flag was given, an error message 95 * is printed. Else, if a name doesn't exist, 96 * its node is created. 97 * 98 * Targ_Ignore Return TRUE if errors should be ignored when 99 * creating the given target. 100 * 101 * Targ_Silent Return TRUE if we should be silent when 102 * creating the given target. 103 * 104 * Targ_Precious Return TRUE if the target is precious and 105 * should not be removed if we are interrupted. 106 * 107 * Targ_Propagate Propagate information between related 108 * nodes. Should be called after the 109 * makefiles are parsed but before any 110 * action is taken. 111 * 112 * Debugging: 113 * Targ_PrintGraph Print out the entire graphm all variables 114 * and statistics for the directory cache. Should 115 * print something for suffixes, too, but... 116 */ 117 118 #include <stdio.h> 119 #include <time.h> 120 121 #include "make.h" 122 #include "dir.h" 123 124 /* "@(#)targ.c 8.2 (Berkeley) 3/19/94" */ 125 MAKE_RCSID("$NetBSD: targ.c,v 1.88 2020/09/13 15:15:51 rillig Exp $"); 126 127 static Lst allTargets; /* the list of all targets found so far */ 128 #ifdef CLEANUP 129 static Lst allGNs; /* List of all the GNodes */ 130 #endif 131 static Hash_Table targets; /* a hash table of same */ 132 133 static int TargPrintOnlySrc(void *, void *); 134 #ifdef CLEANUP 135 static void TargFreeGN(void *); 136 #endif 137 138 void 139 Targ_Init(void) 140 { 141 allTargets = Lst_Init(); 142 Hash_InitTable(&targets); 143 } 144 145 void 146 Targ_End(void) 147 { 148 Targ_Stats(); 149 #ifdef CLEANUP 150 Lst_Free(allTargets); 151 if (allGNs != NULL) 152 Lst_Destroy(allGNs, TargFreeGN); 153 Hash_DeleteTable(&targets); 154 #endif 155 } 156 157 void 158 Targ_Stats(void) 159 { 160 Hash_DebugStats(&targets, "targets"); 161 } 162 163 /* Return the list of all targets. */ 164 Lst 165 Targ_List(void) 166 { 167 return allTargets; 168 } 169 170 /* Create and initialize a new graph node. The gnode is added to the list of 171 * all gnodes. 172 * 173 * Input: 174 * name the name of the node, such as "clean", "src.c" 175 */ 176 GNode * 177 Targ_NewGN(const char *name) 178 { 179 GNode *gn; 180 181 gn = bmake_malloc(sizeof(GNode)); 182 gn->name = bmake_strdup(name); 183 gn->uname = NULL; 184 gn->path = NULL; 185 gn->type = name[0] == '-' && name[1] == 'l' ? OP_LIB : 0; 186 gn->unmade = 0; 187 gn->unmade_cohorts = 0; 188 gn->cohort_num[0] = 0; 189 gn->centurion = NULL; 190 gn->made = UNMADE; 191 gn->flags = 0; 192 gn->checked = 0; 193 gn->mtime = 0; 194 gn->cmgn = NULL; 195 gn->implicitParents = Lst_Init(); 196 gn->cohorts = Lst_Init(); 197 gn->parents = Lst_Init(); 198 gn->children = Lst_Init(); 199 gn->order_pred = Lst_Init(); 200 gn->order_succ = Lst_Init(); 201 Hash_InitTable(&gn->context); 202 gn->commands = Lst_Init(); 203 gn->suffix = NULL; 204 gn->fname = NULL; 205 gn->lineno = 0; 206 207 #ifdef CLEANUP 208 if (allGNs == NULL) 209 allGNs = Lst_Init(); 210 Lst_Append(allGNs, gn); 211 #endif 212 213 return gn; 214 } 215 216 #ifdef CLEANUP 217 static void 218 TargFreeGN(void *gnp) 219 { 220 GNode *gn = (GNode *)gnp; 221 222 free(gn->name); 223 free(gn->uname); 224 free(gn->path); 225 226 Lst_Free(gn->implicitParents); 227 Lst_Free(gn->cohorts); 228 Lst_Free(gn->parents); 229 Lst_Free(gn->children); 230 Lst_Free(gn->order_succ); 231 Lst_Free(gn->order_pred); 232 Hash_DeleteTable(&gn->context); 233 Lst_Free(gn->commands); 234 235 /* XXX: does gn->suffix need to be freed? It is reference-counted. */ 236 /* gn->fname points to name allocated when file was opened, don't free */ 237 238 free(gn); 239 } 240 #endif 241 242 243 /* Find a node in the list using the given name for matching. 244 * If the node is created, it is added to the .ALLTARGETS list. 245 * 246 * Input: 247 * name the name to find 248 * flags flags governing events when target not found 249 * 250 * Results: 251 * The node in the list if it was. If it wasn't, return NULL if 252 * flags was TARG_NOCREATE or the newly created and initialized node 253 * if it was TARG_CREATE 254 */ 255 GNode * 256 Targ_FindNode(const char *name, int flags) 257 { 258 GNode *gn; /* node in that element */ 259 Hash_Entry *he = NULL; /* New or used hash entry for node */ 260 Boolean isNew; /* Set TRUE if Hash_CreateEntry had to create */ 261 /* an entry for the node */ 262 263 if (!(flags & (TARG_CREATE | TARG_NOHASH))) { 264 he = Hash_FindEntry(&targets, name); 265 if (he == NULL) 266 return NULL; 267 return (GNode *)Hash_GetValue(he); 268 } 269 270 if (!(flags & TARG_NOHASH)) { 271 he = Hash_CreateEntry(&targets, name, &isNew); 272 if (!isNew) 273 return (GNode *)Hash_GetValue(he); 274 } 275 276 gn = Targ_NewGN(name); 277 if (!(flags & TARG_NOHASH)) 278 Hash_SetValue(he, gn); 279 Var_Append(".ALLTARGETS", name, VAR_GLOBAL); 280 Lst_Append(allTargets, gn); 281 if (doing_depend) 282 gn->flags |= FROM_DEPEND; 283 return gn; 284 } 285 286 /* Make a complete list of GNodes from the given list of names. 287 * If flags is TARG_CREATE, nodes will be created for all names in 288 * names which do not yet have graph nodes. If flags is TARG_NOCREATE, 289 * an error message will be printed for each name which can't be found. 290 * 291 * Input: 292 * name list of names to find 293 * flags flags used if no node is found for a given name 294 * 295 * Results: 296 * A complete list of graph nodes corresponding to all instances of all 297 * the names in names. 298 */ 299 Lst 300 Targ_FindList(Lst names, int flags) 301 { 302 Lst nodes; /* result list */ 303 LstNode ln; /* name list element */ 304 GNode *gn; /* node in tLn */ 305 char *name; 306 307 nodes = Lst_Init(); 308 309 Lst_Open(names); 310 while ((ln = Lst_Next(names)) != NULL) { 311 name = LstNode_Datum(ln); 312 gn = Targ_FindNode(name, flags); 313 if (gn != NULL) { 314 /* 315 * Note: Lst_Append must come before the Lst_Concat so the nodes 316 * are added to the list in the order in which they were 317 * encountered in the makefile. 318 */ 319 Lst_Append(nodes, gn); 320 } else if (flags == TARG_NOCREATE) { 321 Error("\"%s\" -- target unknown.", name); 322 } 323 } 324 Lst_Close(names); 325 return nodes; 326 } 327 328 /* Return true if should ignore errors when creating gn. */ 329 Boolean 330 Targ_Ignore(GNode *gn) 331 { 332 return ignoreErrors || gn->type & OP_IGNORE; 333 } 334 335 /* Return true if be silent when creating gn. */ 336 Boolean 337 Targ_Silent(GNode *gn) 338 { 339 return beSilent || gn->type & OP_SILENT; 340 } 341 342 /* See if the given target is precious. */ 343 Boolean 344 Targ_Precious(GNode *gn) 345 { 346 return allPrecious || gn->type & (OP_PRECIOUS | OP_DOUBLEDEP); 347 } 348 349 /******************* DEBUG INFO PRINTING ****************/ 350 351 static GNode *mainTarg; /* the main target, as set by Targ_SetMain */ 352 353 /* Set our idea of the main target we'll be creating. Used for debugging 354 * output. */ 355 void 356 Targ_SetMain(GNode *gn) 357 { 358 mainTarg = gn; 359 } 360 361 static void 362 PrintNodeNames(Lst gnodes) 363 { 364 LstNode node; 365 366 for (node = Lst_First(gnodes); node != NULL; node = LstNode_Next(node)) { 367 GNode *gn = LstNode_Datum(node); 368 fprintf(debug_file, " %s%s", gn->name, gn->cohort_num); 369 } 370 } 371 372 static void 373 PrintNodeNamesLine(const char *label, Lst gnodes) 374 { 375 if (Lst_IsEmpty(gnodes)) 376 return; 377 fprintf(debug_file, "# %s:", label); 378 PrintNodeNames(gnodes); 379 fprintf(debug_file, "\n"); 380 } 381 382 static int 383 TargPrintCmd(void *cmd, void *dummy MAKE_ATTR_UNUSED) 384 { 385 fprintf(debug_file, "\t%s\n", (char *)cmd); 386 return 0; 387 } 388 389 void 390 Targ_PrintCmds(GNode *gn) 391 { 392 Lst_ForEach(gn->commands, TargPrintCmd, NULL); 393 } 394 395 /* Format a modification time in some reasonable way and return it. 396 * The time is placed in a static area, so it is overwritten with each call. */ 397 char * 398 Targ_FmtTime(time_t tm) 399 { 400 struct tm *parts; 401 static char buf[128]; 402 403 parts = localtime(&tm); 404 (void)strftime(buf, sizeof buf, "%k:%M:%S %b %d, %Y", parts); 405 return buf; 406 } 407 408 /* Print out a type field giving only those attributes the user can set. */ 409 void 410 Targ_PrintType(int type) 411 { 412 int tbit; 413 414 #define PRINTBIT(attr) case CONCAT(OP_,attr): fprintf(debug_file, " ." #attr); break 415 #define PRINTDBIT(attr) case CONCAT(OP_,attr): if (DEBUG(TARG))fprintf(debug_file, " ." #attr); break 416 417 type &= ~OP_OPMASK; 418 419 while (type) { 420 tbit = 1 << (ffs(type) - 1); 421 type &= ~tbit; 422 423 switch(tbit) { 424 PRINTBIT(OPTIONAL); 425 PRINTBIT(USE); 426 PRINTBIT(EXEC); 427 PRINTBIT(IGNORE); 428 PRINTBIT(PRECIOUS); 429 PRINTBIT(SILENT); 430 PRINTBIT(MAKE); 431 PRINTBIT(JOIN); 432 PRINTBIT(INVISIBLE); 433 PRINTBIT(NOTMAIN); 434 PRINTDBIT(LIB); 435 /*XXX: MEMBER is defined, so CONCAT(OP_,MEMBER) gives OP_"%" */ 436 case OP_MEMBER: if (DEBUG(TARG))fprintf(debug_file, " .MEMBER"); break; 437 PRINTDBIT(ARCHV); 438 PRINTDBIT(MADE); 439 PRINTDBIT(PHONY); 440 } 441 } 442 } 443 444 static const char * 445 made_name(GNodeMade made) 446 { 447 switch (made) { 448 case UNMADE: return "unmade"; 449 case DEFERRED: return "deferred"; 450 case REQUESTED: return "requested"; 451 case BEINGMADE: return "being made"; 452 case MADE: return "made"; 453 case UPTODATE: return "up-to-date"; 454 case ERROR: return "error when made"; 455 case ABORTED: return "aborted"; 456 default: return "unknown enum_made value"; 457 } 458 } 459 460 static int 461 PrintNode(void *gnp, void *passp) 462 { 463 GNode *gn = gnp; 464 int pass = *(const int *)passp; 465 466 fprintf(debug_file, "# %s%s", gn->name, gn->cohort_num); 467 GNode_FprintDetails(debug_file, ", ", gn, "\n"); 468 if (gn->flags == 0) 469 return 0; 470 471 if (!OP_NOP(gn->type)) { 472 fprintf(debug_file, "#\n"); 473 if (gn == mainTarg) { 474 fprintf(debug_file, "# *** MAIN TARGET ***\n"); 475 } 476 if (pass >= 2) { 477 if (gn->unmade) { 478 fprintf(debug_file, "# %d unmade children\n", gn->unmade); 479 } else { 480 fprintf(debug_file, "# No unmade children\n"); 481 } 482 if (! (gn->type & (OP_JOIN|OP_USE|OP_USEBEFORE|OP_EXEC))) { 483 if (gn->mtime != 0) { 484 fprintf(debug_file, "# last modified %s: %s\n", 485 Targ_FmtTime(gn->mtime), 486 made_name(gn->made)); 487 } else if (gn->made != UNMADE) { 488 fprintf(debug_file, "# non-existent (maybe): %s\n", 489 made_name(gn->made)); 490 } else { 491 fprintf(debug_file, "# unmade\n"); 492 } 493 } 494 PrintNodeNamesLine("implicit parents", gn->implicitParents); 495 } else { 496 if (gn->unmade) 497 fprintf(debug_file, "# %d unmade children\n", gn->unmade); 498 } 499 PrintNodeNamesLine("parents", gn->parents); 500 PrintNodeNamesLine("order_pred", gn->order_pred); 501 PrintNodeNamesLine("order_succ", gn->order_succ); 502 503 fprintf(debug_file, "%-16s", gn->name); 504 switch (gn->type & OP_OPMASK) { 505 case OP_DEPENDS: 506 fprintf(debug_file, ":"); break; 507 case OP_FORCE: 508 fprintf(debug_file, "!"); break; 509 case OP_DOUBLEDEP: 510 fprintf(debug_file, "::"); break; 511 } 512 Targ_PrintType(gn->type); 513 PrintNodeNames(gn->children); 514 fprintf(debug_file, "\n"); 515 Targ_PrintCmds(gn); 516 fprintf(debug_file, "\n\n"); 517 if (gn->type & OP_DOUBLEDEP) { 518 Lst_ForEach(gn->cohorts, PrintNode, passp); 519 } 520 } 521 return 0; 522 } 523 524 /* Print the contents of a node. */ 525 void 526 Targ_PrintNode(GNode *gn, int pass) 527 { 528 PrintNode(gn, &pass); 529 } 530 531 void 532 Targ_PrintNodes(Lst gnodes, int pass) 533 { 534 Lst_ForEach(gnodes, PrintNode, &pass); 535 } 536 537 /* Print only those targets that are just a source. 538 * The name of each file is printed, preceded by #\t. */ 539 static int 540 TargPrintOnlySrc(void *gnp, void *dummy MAKE_ATTR_UNUSED) 541 { 542 GNode *gn = (GNode *)gnp; 543 if (!OP_NOP(gn->type)) 544 return 0; 545 546 fprintf(debug_file, "#\t%s [%s]", 547 gn->name, gn->path ? gn->path : gn->name); 548 Targ_PrintType(gn->type); 549 fprintf(debug_file, "\n"); 550 551 return 0; 552 } 553 554 /* Input: 555 * pass 1 => before processing 556 * 2 => after processing 557 * 3 => after processing, an error occurred 558 */ 559 void 560 Targ_PrintGraph(int pass) 561 { 562 fprintf(debug_file, "#*** Input graph:\n"); 563 Lst_ForEach(allTargets, PrintNode, &pass); 564 fprintf(debug_file, "\n\n"); 565 fprintf(debug_file, "#\n# Files that are only sources:\n"); 566 Lst_ForEach(allTargets, TargPrintOnlySrc, NULL); 567 fprintf(debug_file, "#*** Global Variables:\n"); 568 Var_Dump(VAR_GLOBAL); 569 fprintf(debug_file, "#*** Command-line Variables:\n"); 570 Var_Dump(VAR_CMD); 571 fprintf(debug_file, "\n"); 572 Dir_PrintDirectories(); 573 fprintf(debug_file, "\n"); 574 Suff_PrintAll(); 575 } 576 577 /* Propagate some type information to cohort nodes (those from the :: 578 * dependency operator). 579 * 580 * Should be called after the makefiles are parsed but before any action is 581 * taken. */ 582 void 583 Targ_Propagate(void) 584 { 585 LstNode pn, cn; 586 587 for (pn = Lst_First(allTargets); pn != NULL; pn = LstNode_Next(pn)) { 588 GNode *pgn = LstNode_Datum(pn); 589 590 if (!(pgn->type & OP_DOUBLEDEP)) 591 continue; 592 593 for (cn = Lst_First(pgn->cohorts); cn != NULL; cn = LstNode_Next(cn)) { 594 GNode *cgn = LstNode_Datum(cn); 595 596 cgn->type |= pgn->type & ~OP_OPMASK; 597 } 598 } 599 } 600