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