xref: /freebsd/contrib/bmake/targ.c (revision a521f2116473fbd8c09db395518f060a27d02334)
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