xref: /trueos/usr.bin/make/targ.c (revision 834fb25a9ed2240101506d137b5be7d71c75f306)
1 /*-
2  * Copyright (c) 1988, 1989, 1990, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  * Copyright (c) 1989 by Berkeley Softworks
5  * 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. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *	This product includes software developed by the University of
21  *	California, Berkeley and its contributors.
22  * 4. Neither the name of the University nor the names of its contributors
23  *    may be used to endorse or promote products derived from this software
24  *    without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36  * SUCH DAMAGE.
37  *
38  * @(#)targ.c	8.2 (Berkeley) 3/19/94
39  */
40 
41 #include <sys/cdefs.h>
42 __FBSDID("$FreeBSD$");
43 
44 /*
45  * Functions for maintaining the Lst allTargets. Target nodes are
46  * kept in two structures: a Lst, maintained by the list library, and a
47  * hash table, maintained by the hash library.
48  *
49  * Interface:
50  *	Targ_Init	Initialization procedure.
51  *
52  *	Targ_NewGN	Create a new GNode for the passed target (string).
53  *			The node is *not* placed in the hash table, though all
54  *			its fields are initialized.
55  *
56  *	Targ_FindNode	Find the node for a given target, creating and storing
57  *			it if it doesn't exist and the flags are right
58  *			(TARG_CREATE)
59  *
60  *	Targ_FindList	Given a list of names, find nodes for all of them. If a
61  *			name doesn't exist and the TARG_NOCREATE flag was given,
62  *			an error message is printed. Else, if a name doesn't
63  *			exist, its node is created.
64  *
65  *	Targ_Ignore	Return TRUE if errors should be ignored when creating
66  *			the given target.
67  *
68  *	Targ_Silent	Return TRUE if we should be silent when creating the
69  *			given target.
70  *
71  *	Targ_Precious	Return TRUE if the target is precious and should not
72  *			be removed if we are interrupted.
73  *
74  * Debugging:
75  *	Targ_PrintGraph	Print out the entire graphm all variables and statistics
76  *			for the directory cache. Should print something for
77  *			suffixes, too, but...
78  */
79 
80 #include <stdio.h>
81 
82 #include "dir.h"
83 #include "globals.h"
84 #include "GNode.h"
85 #include "hash.h"
86 #include "suff.h"
87 #include "targ.h"
88 #include "util.h"
89 #include "var.h"
90 
91 /* the list of all targets found so far */
92 static Lst allTargets = Lst_Initializer(allTargets);
93 
94 static Hash_Table targets;	/* a hash table of same */
95 
96 #define	HTSIZE	191		/* initial size of hash table */
97 
98 /**
99  * Targ_Init
100  *	Initialize this module
101  *
102  * Side Effects:
103  *	The allTargets list and the targets hash table are initialized
104  */
105 void
Targ_Init(void)106 Targ_Init(void)
107 {
108 
109 	Hash_InitTable(&targets, HTSIZE);
110 }
111 
112 /**
113  * Targ_NewGN
114  *	Create and initialize a new graph node
115  *
116  * Results:
117  *	An initialized graph node with the name field filled with a copy
118  *	of the passed name
119  *
120  * Side Effects:
121  *	The gnode is added to the list of all gnodes.
122  */
123 GNode *
Targ_NewGN(const char * name)124 Targ_NewGN(const char *name)
125 {
126 	GNode *gn;
127 
128 	gn = emalloc(sizeof(GNode));
129 	gn->name = estrdup(name);
130 	gn->path = NULL;
131 	if (name[0] == '-' && name[1] == 'l') {
132 		gn->type = OP_LIB;
133 	} else {
134 		gn->type = 0;
135 	}
136 	gn->unmade = 0;
137 	gn->make = FALSE;
138 	gn->made = UNMADE;
139 	gn->childMade = FALSE;
140 	gn->order = 0;
141 	gn->mtime = gn->cmtime = 0;
142 	gn->cmtime_gn = NULL;
143 	Lst_Init(&gn->iParents);
144 	Lst_Init(&gn->cohorts);
145 	Lst_Init(&gn->parents);
146 	Lst_Init(&gn->children);
147 	Lst_Init(&gn->successors);
148 	Lst_Init(&gn->preds);
149 	Lst_Init(&gn->context);
150 	Lst_Init(&gn->commands);
151 	gn->suffix = NULL;
152 
153 	return (gn);
154 }
155 
156 /**
157  * Targ_FindNode
158  *	Find a node in the list using the given name for matching
159  *
160  * Results:
161  *	The node in the list if it was. If it wasn't, return NULL of
162  *	flags was TARG_NOCREATE or the newly created and initialized node
163  *	if it was TARG_CREATE
164  *
165  * Side Effects:
166  *	Sometimes a node is created and added to the list
167  */
168 GNode *
Targ_FindNode(const char * name,int flags)169 Targ_FindNode(const char *name, int flags)
170 {
171 	GNode		*gn;	/* node in that element */
172 	Hash_Entry	*he;	/* New or used hash entry for node */
173 	Boolean		isNew;	/* Set TRUE if Hash_CreateEntry had to create */
174 		      		/* an entry for the node */
175 
176 	if (flags & TARG_CREATE) {
177 		he = Hash_CreateEntry(&targets, name, &isNew);
178 		if (isNew) {
179 			gn = Targ_NewGN(name);
180 			Hash_SetValue(he, gn);
181 			Lst_AtEnd(&allTargets, gn);
182 		}
183 	} else {
184 		he = Hash_FindEntry(&targets, name);
185 	}
186 
187 	if (he == NULL) {
188 		return (NULL);
189 	} else {
190 		return (Hash_GetValue(he));
191 	}
192 }
193 
194 /**
195  * Targ_FindList
196  *	Make a complete list of GNodes from the given list of names
197  *
198  * Results:
199  *	A complete list of graph nodes corresponding to all instances of all
200  *	the names in names.
201  *
202  * Side Effects:
203  *	If flags is TARG_CREATE, nodes will be created for all names in
204  *	names which do not yet have graph nodes. If flags is TARG_NOCREATE,
205  *	an error message will be printed for each name which can't be found.
206  */
207 void
Targ_FindList(Lst * nodes,Lst * names,int flags)208 Targ_FindList(Lst *nodes, Lst *names, int flags)
209 {
210 	LstNode	*ln;	/* name list element */
211 	GNode	*gn;	/* node in tLn */
212 	char	*name;
213 
214 	for (ln = Lst_First(names); ln != NULL; ln = Lst_Succ(ln)) {
215 		name = Lst_Datum(ln);
216 		gn = Targ_FindNode(name, flags);
217 		if (gn != NULL) {
218 			/*
219 			 * Note: Lst_AtEnd must come before the Lst_Concat so
220 			 * the nodes are added to the list in the order in which
221 			 * they were encountered in the makefile.
222 			 */
223 			Lst_AtEnd(nodes, gn);
224 			if (gn->type & OP_DOUBLEDEP) {
225 				Lst_Concat(nodes, &gn->cohorts, LST_CONCNEW);
226 			}
227 
228 		} else if (flags == TARG_NOCREATE) {
229 			Error("\"%s\" -- target unknown.", name);
230 		}
231 	}
232 }
233 
234 /**
235  * Targ_Ignore
236  *	Return true if should ignore errors when creating gn
237  *
238  * Results:
239  *	TRUE if should ignore errors
240  */
241 Boolean
Targ_Ignore(GNode * gn)242 Targ_Ignore(GNode *gn)
243 {
244 
245 	if (ignoreErrors || (gn->type & OP_IGNORE)) {
246 		return (TRUE);
247 	} else {
248 		return (FALSE);
249 	}
250 }
251 
252 /**
253  * Targ_Silent
254  *	Return true if be silent when creating gn
255  *
256  * Results:
257  *	TRUE if should be silent
258  */
259 Boolean
Targ_Silent(GNode * gn)260 Targ_Silent(GNode *gn)
261 {
262 
263 	if (beSilent || (gn->type & OP_SILENT)) {
264 		return (TRUE);
265 	} else {
266 		return (FALSE);
267 	}
268 }
269 
270 /**
271  * Targ_Precious
272  *	See if the given target is precious
273  *
274  * Results:
275  *	TRUE if it is precious. FALSE otherwise
276  */
277 Boolean
Targ_Precious(GNode * gn)278 Targ_Precious(GNode *gn)
279 {
280 
281 	if (allPrecious || (gn->type & (OP_PRECIOUS | OP_DOUBLEDEP))) {
282 		return (TRUE);
283 	} else {
284 		return (FALSE);
285 	}
286 }
287 
288 static GNode	*mainTarg;	/* the main target, as set by Targ_SetMain */
289 
290 /**
291  * Targ_SetMain
292  *	Set our idea of the main target we'll be creating. Used for
293  *	debugging output.
294  *
295  * Side Effects:
296  *	"mainTarg" is set to the main target's node.
297  */
298 void
Targ_SetMain(GNode * gn)299 Targ_SetMain(GNode *gn)
300 {
301 
302 	mainTarg = gn;
303 }
304 
305 /**
306  * Targ_FmtTime
307  *	Format a modification time in some reasonable way and return it.
308  *
309  * Results:
310  *	The time reformatted.
311  *
312  * Side Effects:
313  *	The time is placed in a static area, so it is overwritten
314  *	with each call.
315  */
316 char *
Targ_FmtTime(time_t modtime)317 Targ_FmtTime(time_t modtime)
318 {
319 	struct tm	*parts;
320 	static char	buf[128];
321 
322 	parts = localtime(&modtime);
323 
324 	strftime(buf, sizeof(buf), "%H:%M:%S %b %d, %Y", parts);
325 	buf[sizeof(buf) - 1] = '\0';
326 	return (buf);
327 }
328 
329 /**
330  * Targ_PrintType
331  *	Print out a type field giving only those attributes the user can
332  *	set.
333  */
334 void
Targ_PrintType(int type)335 Targ_PrintType(int type)
336 {
337 	static const struct flag2str type2str[] = {
338 		{ OP_OPTIONAL,	".OPTIONAL" },
339 		{ OP_USE,	".USE" },
340 		{ OP_EXEC,	".EXEC" },
341 		{ OP_IGNORE,	".IGNORE" },
342 		{ OP_PRECIOUS,	".PRECIOUS" },
343 		{ OP_SILENT,	".SILENT" },
344 		{ OP_MAKE,	".MAKE" },
345 		{ OP_JOIN,	".JOIN" },
346 		{ OP_INVISIBLE,	".INVISIBLE" },
347 		{ OP_NOTMAIN,	".NOTMAIN" },
348 		{ OP_PHONY,	".PHONY" },
349 		{ OP_LIB,	".LIB" },
350 		{ OP_MEMBER,	".MEMBER" },
351 		{ OP_ARCHV,	".ARCHV" },
352 		{ 0,		NULL }
353 	};
354 
355 	type &= ~OP_OPMASK;
356 	if (!DEBUG(TARG))
357 		type &= ~(OP_ARCHV | OP_LIB | OP_MEMBER);
358 	print_flags(stdout, type2str, type, 0);
359 }
360 
361 /**
362  * TargPrintNode
363  *	print the contents of a node
364  */
365 static int
TargPrintNode(const GNode * gn,int pass)366 TargPrintNode(const GNode *gn, int pass)
367 {
368 	const LstNode	*tln;
369 
370 	if (!OP_NOP(gn->type)) {
371 		printf("#\n");
372 		if (gn == mainTarg) {
373 			printf("# *** MAIN TARGET ***\n");
374 		}
375 		if (pass == 2) {
376 			if (gn->unmade) {
377 				printf("# %d unmade children\n", gn->unmade);
378 			} else {
379 				printf("# No unmade children\n");
380 			}
381 			if (!(gn->type & (OP_JOIN | OP_USE | OP_EXEC))) {
382 				if (gn->mtime != 0) {
383 					printf("# last modified %s: %s\n",
384 					    Targ_FmtTime(gn->mtime),
385 					    gn->made == UNMADE ? "unmade" :
386 					    gn->made == MADE ? "made" :
387 					    gn->made == UPTODATE ? "up-to-date":
388 					    "error when made");
389 				} else if (gn->made != UNMADE) {
390 					printf("# non-existent (maybe): %s\n",
391 					    gn->made == MADE ? "made" :
392 					    gn->made == UPTODATE ? "up-to-date":
393 					    gn->made == ERROR?"error when made":
394 				 	    "aborted");
395 				} else {
396 					printf("# unmade\n");
397 				}
398 			}
399 			if (!Lst_IsEmpty(&gn->iParents)) {
400 				printf("# implicit parents: ");
401 				LST_FOREACH(tln, &gn->iParents)
402 					printf("%s ", ((const GNode *)
403 					    Lst_Datum(tln))->name);
404 				printf("\n");
405 			}
406 		}
407 		if (!Lst_IsEmpty(&gn->parents)) {
408 			printf("# parents: ");
409 			LST_FOREACH(tln, &gn->parents)
410 				printf("%s ", ((const GNode *)
411 				    Lst_Datum(tln))->name);
412 			printf("\n");
413 		}
414 
415 		printf("%-16s", gn->name);
416 		switch (gn->type & OP_OPMASK) {
417 		  case OP_DEPENDS:
418 			printf(": ");
419 			break;
420 		  case OP_FORCE:
421 			printf("! ");
422 			break;
423 		  case OP_DOUBLEDEP:
424 			printf(":: ");
425 			break;
426 		  default:
427 			break;
428 		}
429 		Targ_PrintType(gn->type);
430 		LST_FOREACH(tln, &gn->children)
431 			printf("%s ", ((const GNode *)Lst_Datum(tln))->name);
432 		printf("\n");
433 		LST_FOREACH(tln, &gn->commands)
434 			printf("\t%s\n", (const char *)Lst_Datum(tln));
435 		printf("\n\n");
436 		if (gn->type & OP_DOUBLEDEP) {
437 			LST_FOREACH(tln, &gn->cohorts)
438 				TargPrintNode((const GNode *)Lst_Datum(tln),
439 				    pass);
440 		}
441 	}
442 	return (0);
443 }
444 
445 /**
446  * Targ_PrintGraph
447  *	Print the entire graph.
448  */
449 void
Targ_PrintGraph(int pass)450 Targ_PrintGraph(int pass)
451 {
452 	const GNode	*gn;
453 	const LstNode	*tln;
454 
455 	printf("#*** Input graph:\n");
456 	LST_FOREACH(tln, &allTargets)
457 		TargPrintNode((const GNode *)Lst_Datum(tln), pass);
458 	printf("\n\n");
459 
460 	printf("#\n#   Files that are only sources:\n");
461 	LST_FOREACH(tln, &allTargets) {
462 		gn = Lst_Datum(tln);
463 		if (OP_NOP(gn->type))
464 			printf("#\t%s [%s]\n", gn->name,
465 			    gn->path ? gn->path : gn->name);
466 	}
467 	Var_Dump();
468 	printf("\n");
469 	Dir_PrintDirectories();
470 	printf("\n");
471 	Suff_PrintAll();
472 }
473