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