1 /* $NetBSD: targ.c,v 1.126 2020/10/30 07:19:30 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 Clean up 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, or return NULL. 89 * 90 * Targ_GetNode Find the node, or create it. 91 * 92 * Targ_NewInternalNode 93 * Create an internal node. 94 * 95 * Targ_FindList Given a list of names, find nodes for all 96 * of them, creating them as necessary. 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 nodes. 108 * Should be called after the makefiles are parsed 109 * but before any action is taken. 110 * 111 * Debugging: 112 * Targ_PrintGraph 113 * Print out the entire graphm all variables and 114 * statistics for the directory cache. Should print 115 * something for suffixes, too, but... 116 */ 117 118 #include <time.h> 119 120 #include "make.h" 121 #include "dir.h" 122 123 /* "@(#)targ.c 8.2 (Berkeley) 3/19/94" */ 124 MAKE_RCSID("$NetBSD: targ.c,v 1.126 2020/10/30 07:19:30 rillig Exp $"); 125 126 static GNodeList *allTargets; /* the list of all targets found so far */ 127 #ifdef CLEANUP 128 static GNodeList *allGNs; /* List of all the GNodes */ 129 #endif 130 static HashTable targets; /* a hash table of same */ 131 132 #ifdef CLEANUP 133 static void TargFreeGN(void *); 134 #endif 135 136 void 137 Targ_Init(void) 138 { 139 allTargets = Lst_New(); 140 HashTable_Init(&targets); 141 } 142 143 void 144 Targ_End(void) 145 { 146 Targ_Stats(); 147 #ifdef CLEANUP 148 Lst_Free(allTargets); 149 if (allGNs != NULL) 150 Lst_Destroy(allGNs, TargFreeGN); 151 HashTable_Done(&targets); 152 #endif 153 } 154 155 void 156 Targ_Stats(void) 157 { 158 HashTable_DebugStats(&targets, "targets"); 159 } 160 161 /* Return the list of all targets. */ 162 GNodeList * 163 Targ_List(void) 164 { 165 return allTargets; 166 } 167 168 /* Create and initialize a new graph node. The gnode is added to the list of 169 * all gnodes. 170 * 171 * Input: 172 * name the name of the node, such as "clean", "src.c", ".END" 173 */ 174 GNode * 175 Targ_NewGN(const char *name) 176 { 177 GNode *gn; 178 179 gn = bmake_malloc(sizeof(GNode)); 180 gn->name = bmake_strdup(name); 181 gn->uname = NULL; 182 gn->path = NULL; 183 gn->type = name[0] == '-' && name[1] == 'l' ? OP_LIB : 0; 184 gn->unmade = 0; 185 gn->unmade_cohorts = 0; 186 gn->cohort_num[0] = '\0'; 187 gn->centurion = NULL; 188 gn->made = UNMADE; 189 gn->flags = 0; 190 gn->checked_seqno = 0; 191 gn->mtime = 0; 192 gn->youngestChild = NULL; 193 gn->implicitParents = Lst_New(); 194 gn->cohorts = Lst_New(); 195 gn->parents = Lst_New(); 196 gn->children = Lst_New(); 197 gn->order_pred = Lst_New(); 198 gn->order_succ = Lst_New(); 199 HashTable_Init(&gn->context); 200 gn->commands = Lst_New(); 201 gn->suffix = NULL; 202 gn->fname = NULL; 203 gn->lineno = 0; 204 205 #ifdef CLEANUP 206 if (allGNs == NULL) 207 allGNs = Lst_New(); 208 Lst_Append(allGNs, gn); 209 #endif 210 211 return gn; 212 } 213 214 #ifdef CLEANUP 215 static void 216 TargFreeGN(void *gnp) 217 { 218 GNode *gn = gnp; 219 220 free(gn->name); 221 free(gn->uname); 222 free(gn->path); 223 224 Lst_Free(gn->implicitParents); 225 Lst_Free(gn->cohorts); 226 Lst_Free(gn->parents); 227 Lst_Free(gn->children); 228 Lst_Free(gn->order_succ); 229 Lst_Free(gn->order_pred); 230 HashTable_Done(&gn->context); 231 Lst_Free(gn->commands); 232 233 /* XXX: does gn->suffix need to be freed? It is reference-counted. */ 234 235 free(gn); 236 } 237 #endif 238 239 /* Get the existing global node, or return NULL. */ 240 GNode * 241 Targ_FindNode(const char *name) 242 { 243 return HashTable_FindValue(&targets, name); 244 } 245 246 /* Get the existing global node, or create it. */ 247 GNode * 248 Targ_GetNode(const char *name) 249 { 250 Boolean isNew; 251 HashEntry *he = HashTable_CreateEntry(&targets, name, &isNew); 252 if (!isNew) 253 return HashEntry_Get(he); 254 255 { 256 GNode *gn = Targ_NewInternalNode(name); 257 HashEntry_Set(he, gn); 258 return gn; 259 } 260 } 261 262 /* Create a node, register it in .ALLTARGETS but don't store it in the 263 * table of global nodes. This means it cannot be found by name. 264 * 265 * This is used for internal nodes, such as cohorts or .WAIT nodes. */ 266 GNode * 267 Targ_NewInternalNode(const char *name) 268 { 269 GNode *gn = Targ_NewGN(name); 270 Var_Append(".ALLTARGETS", name, VAR_GLOBAL); 271 Lst_Append(allTargets, gn); 272 if (doing_depend) 273 gn->flags |= FROM_DEPEND; 274 return gn; 275 } 276 277 /* Return the .END node, which contains the commands to be executed when 278 * everything else is done. */ 279 GNode *Targ_GetEndNode(void) 280 { 281 /* Save the node locally to avoid having to search for it all the time. */ 282 static GNode *endNode = NULL; 283 if (endNode == NULL) { 284 endNode = Targ_GetNode(".END"); 285 endNode->type = OP_SPECIAL; 286 } 287 return endNode; 288 } 289 290 /* Return the named nodes, creating them as necessary. */ 291 GNodeList * 292 Targ_FindList(StringList *names) 293 { 294 StringListNode *ln; 295 GNodeList *nodes = Lst_New(); 296 for (ln = names->first; ln != NULL; ln = ln->next) { 297 const char *name = ln->datum; 298 GNode *gn = Targ_GetNode(name); 299 Lst_Append(nodes, gn); 300 } 301 return nodes; 302 } 303 304 /* Return true if should ignore errors when creating gn. */ 305 Boolean 306 Targ_Ignore(GNode *gn) 307 { 308 return opts.ignoreErrors || gn->type & OP_IGNORE; 309 } 310 311 /* Return true if be silent when creating gn. */ 312 Boolean 313 Targ_Silent(GNode *gn) 314 { 315 return opts.beSilent || gn->type & OP_SILENT; 316 } 317 318 /* See if the given target is precious. */ 319 Boolean 320 Targ_Precious(GNode *gn) 321 { 322 return allPrecious || gn->type & (OP_PRECIOUS | OP_DOUBLEDEP); 323 } 324 325 /******************* DEBUG INFO PRINTING ****************/ 326 327 static GNode *mainTarg; /* the main target, as set by Targ_SetMain */ 328 329 /* Set our idea of the main target we'll be creating. Used for debugging 330 * output. */ 331 void 332 Targ_SetMain(GNode *gn) 333 { 334 mainTarg = gn; 335 } 336 337 static void 338 PrintNodeNames(GNodeList *gnodes) 339 { 340 GNodeListNode *node; 341 342 for (node = gnodes->first; node != NULL; node = node->next) { 343 GNode *gn = node->datum; 344 debug_printf(" %s%s", gn->name, gn->cohort_num); 345 } 346 } 347 348 static void 349 PrintNodeNamesLine(const char *label, GNodeList *gnodes) 350 { 351 if (Lst_IsEmpty(gnodes)) 352 return; 353 debug_printf("# %s:", label); 354 PrintNodeNames(gnodes); 355 debug_printf("\n"); 356 } 357 358 void 359 Targ_PrintCmds(GNode *gn) 360 { 361 StringListNode *ln; 362 for (ln = gn->commands->first; ln != NULL; ln = ln->next) { 363 const char *cmd = ln->datum; 364 debug_printf("\t%s\n", cmd); 365 } 366 } 367 368 /* Format a modification time in some reasonable way and return it. 369 * The time is placed in a static area, so it is overwritten with each call. */ 370 char * 371 Targ_FmtTime(time_t tm) 372 { 373 struct tm *parts; 374 static char buf[128]; 375 376 parts = localtime(&tm); 377 (void)strftime(buf, sizeof buf, "%k:%M:%S %b %d, %Y", parts); 378 return buf; 379 } 380 381 /* Print out a type field giving only those attributes the user can set. */ 382 void 383 Targ_PrintType(int type) 384 { 385 int tbit; 386 387 #define PRINTBIT(attr) case CONCAT(OP_,attr): debug_printf(" ." #attr); break 388 #define PRINTDBIT(attr) case CONCAT(OP_,attr): if (DEBUG(TARG))debug_printf(" ." #attr); break 389 390 type &= ~OP_OPMASK; 391 392 while (type) { 393 tbit = 1 << (ffs(type) - 1); 394 type &= ~tbit; 395 396 switch(tbit) { 397 PRINTBIT(OPTIONAL); 398 PRINTBIT(USE); 399 PRINTBIT(EXEC); 400 PRINTBIT(IGNORE); 401 PRINTBIT(PRECIOUS); 402 PRINTBIT(SILENT); 403 PRINTBIT(MAKE); 404 PRINTBIT(JOIN); 405 PRINTBIT(INVISIBLE); 406 PRINTBIT(NOTMAIN); 407 PRINTDBIT(LIB); 408 /*XXX: MEMBER is defined, so CONCAT(OP_,MEMBER) gives OP_"%" */ 409 case OP_MEMBER: if (DEBUG(TARG))debug_printf(" .MEMBER"); break; 410 PRINTDBIT(ARCHV); 411 PRINTDBIT(MADE); 412 PRINTDBIT(PHONY); 413 } 414 } 415 } 416 417 static const char * 418 made_name(GNodeMade made) 419 { 420 switch (made) { 421 case UNMADE: return "unmade"; 422 case DEFERRED: return "deferred"; 423 case REQUESTED: return "requested"; 424 case BEINGMADE: return "being made"; 425 case MADE: return "made"; 426 case UPTODATE: return "up-to-date"; 427 case ERROR: return "error when made"; 428 case ABORTED: return "aborted"; 429 default: return "unknown enum_made value"; 430 } 431 } 432 433 static const char * 434 GNode_OpName(const GNode *gn) 435 { 436 switch (gn->type & OP_OPMASK) { 437 case OP_DEPENDS: 438 return ":"; 439 case OP_FORCE: 440 return "!"; 441 case OP_DOUBLEDEP: 442 return "::"; 443 } 444 return ""; 445 } 446 447 /* Print the contents of a node. */ 448 void 449 Targ_PrintNode(GNode *gn, int pass) 450 { 451 debug_printf("# %s%s", gn->name, gn->cohort_num); 452 GNode_FprintDetails(opts.debug_file, ", ", gn, "\n"); 453 if (gn->flags == 0) 454 return; 455 456 if (GNode_IsTarget(gn)) { 457 debug_printf("#\n"); 458 if (gn == mainTarg) { 459 debug_printf("# *** MAIN TARGET ***\n"); 460 } 461 if (pass >= 2) { 462 if (gn->unmade) { 463 debug_printf("# %d unmade children\n", gn->unmade); 464 } else { 465 debug_printf("# No unmade children\n"); 466 } 467 if (! (gn->type & (OP_JOIN|OP_USE|OP_USEBEFORE|OP_EXEC))) { 468 if (gn->mtime != 0) { 469 debug_printf("# last modified %s: %s\n", 470 Targ_FmtTime(gn->mtime), 471 made_name(gn->made)); 472 } else if (gn->made != UNMADE) { 473 debug_printf("# non-existent (maybe): %s\n", 474 made_name(gn->made)); 475 } else { 476 debug_printf("# unmade\n"); 477 } 478 } 479 PrintNodeNamesLine("implicit parents", gn->implicitParents); 480 } else { 481 if (gn->unmade) 482 debug_printf("# %d unmade children\n", gn->unmade); 483 } 484 PrintNodeNamesLine("parents", gn->parents); 485 PrintNodeNamesLine("order_pred", gn->order_pred); 486 PrintNodeNamesLine("order_succ", gn->order_succ); 487 488 debug_printf("%-16s%s", gn->name, GNode_OpName(gn)); 489 Targ_PrintType(gn->type); 490 PrintNodeNames(gn->children); 491 debug_printf("\n"); 492 Targ_PrintCmds(gn); 493 debug_printf("\n\n"); 494 if (gn->type & OP_DOUBLEDEP) { 495 Targ_PrintNodes(gn->cohorts, pass); 496 } 497 } 498 } 499 500 void 501 Targ_PrintNodes(GNodeList *gnodes, int pass) 502 { 503 GNodeListNode *ln; 504 for (ln = gnodes->first; ln != NULL; ln = ln->next) 505 Targ_PrintNode(ln->datum, pass); 506 } 507 508 /* Print only those targets that are just a source. */ 509 static void 510 PrintOnlySources(void) 511 { 512 GNodeListNode *ln; 513 514 for (ln = allTargets->first; ln != NULL; ln = ln->next) { 515 GNode *gn = ln->datum; 516 if (GNode_IsTarget(gn)) 517 continue; 518 519 debug_printf("#\t%s [%s]", gn->name, GNode_Path(gn)); 520 Targ_PrintType(gn->type); 521 debug_printf("\n"); 522 } 523 } 524 525 /* Input: 526 * pass 1 => before processing 527 * 2 => after processing 528 * 3 => after processing, an error occurred 529 */ 530 void 531 Targ_PrintGraph(int pass) 532 { 533 debug_printf("#*** Input graph:\n"); 534 Targ_PrintNodes(allTargets, pass); 535 debug_printf("\n\n"); 536 debug_printf("#\n# Files that are only sources:\n"); 537 PrintOnlySources(); 538 debug_printf("#*** Global Variables:\n"); 539 Var_Dump(VAR_GLOBAL); 540 debug_printf("#*** Command-line Variables:\n"); 541 Var_Dump(VAR_CMDLINE); 542 debug_printf("\n"); 543 Dir_PrintDirectories(); 544 debug_printf("\n"); 545 Suff_PrintAll(); 546 } 547 548 /* Propagate some type information to cohort nodes (those from the :: 549 * dependency operator). 550 * 551 * Should be called after the makefiles are parsed but before any action is 552 * taken. */ 553 void 554 Targ_Propagate(void) 555 { 556 GNodeListNode *ln, *cln; 557 558 for (ln = allTargets->first; ln != NULL; ln = ln->next) { 559 GNode *gn = ln->datum; 560 GNodeType type = gn->type; 561 562 if (!(type & OP_DOUBLEDEP)) 563 continue; 564 565 for (cln = gn->cohorts->first; cln != NULL; cln = cln->next) { 566 GNode *cohort = cln->datum; 567 568 cohort->type |= type & ~OP_OPMASK; 569 } 570 } 571 } 572