xref: /netbsd/usr.bin/make/targ.c (revision c4a72b64)
1 /*	$NetBSD: targ.c,v 1.28 2002/06/15 18:24:58 wiz Exp $	*/
2 
3 /*
4  * Copyright (c) 1988, 1989, 1990, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  * Copyright (c) 1989 by Berkeley Softworks
7  * All rights reserved.
8  *
9  * This code is derived from software contributed to Berkeley by
10  * Adam de Boor.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  * 3. All advertising materials mentioning features or use of this software
21  *    must display the following acknowledgement:
22  *	This product includes software developed by the University of
23  *	California, Berkeley and its contributors.
24  * 4. Neither the name of the University nor the names of its contributors
25  *    may be used to endorse or promote products derived from this software
26  *    without specific prior written permission.
27  *
28  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
29  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
32  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
36  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
37  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38  * SUCH DAMAGE.
39  */
40 
41 #ifdef MAKE_BOOTSTRAP
42 static char rcsid[] = "$NetBSD: targ.c,v 1.28 2002/06/15 18:24:58 wiz Exp $";
43 #else
44 #include <sys/cdefs.h>
45 #ifndef lint
46 #if 0
47 static char sccsid[] = "@(#)targ.c	8.2 (Berkeley) 3/19/94";
48 #else
49 __RCSID("$NetBSD: targ.c,v 1.28 2002/06/15 18:24:58 wiz Exp $");
50 #endif
51 #endif /* not lint */
52 #endif
53 
54 /*-
55  * targ.c --
56  *	Functions for maintaining the Lst allTargets. Target nodes are
57  * kept in two structures: a Lst, maintained by the list library, and a
58  * hash table, maintained by the hash library.
59  *
60  * Interface:
61  *	Targ_Init 	    	Initialization procedure.
62  *
63  *	Targ_End 	    	Cleanup the module
64  *
65  *	Targ_List 	    	Return the list of all targets so far.
66  *
67  *	Targ_NewGN	    	Create a new GNode for the passed target
68  *	    	  	    	(string). The node is *not* placed in the
69  *	    	  	    	hash table, though all its fields are
70  *	    	  	    	initialized.
71  *
72  *	Targ_FindNode	    	Find the node for a given target, creating
73  *	    	  	    	and storing it if it doesn't exist and the
74  *	    	  	    	flags are right (TARG_CREATE)
75  *
76  *	Targ_FindList	    	Given a list of names, find nodes for all
77  *	    	  	    	of them. If a name doesn't exist and the
78  *	    	  	    	TARG_NOCREATE flag was given, an error message
79  *	    	  	    	is printed. Else, if a name doesn't exist,
80  *	    	  	    	its node is created.
81  *
82  *	Targ_Ignore	    	Return TRUE if errors should be ignored when
83  *	    	  	    	creating the given target.
84  *
85  *	Targ_Silent	    	Return TRUE if we should be silent when
86  *	    	  	    	creating the given target.
87  *
88  *	Targ_Precious	    	Return TRUE if the target is precious and
89  *	    	  	    	should not be removed if we are interrupted.
90  *
91  * Debugging:
92  *	Targ_PrintGraph	    	Print out the entire graphm all variables
93  *	    	  	    	and statistics for the directory cache. Should
94  *	    	  	    	print something for suffixes, too, but...
95  */
96 
97 #include	  <stdio.h>
98 #include	  <time.h>
99 
100 #include	  "make.h"
101 #include	  "hash.h"
102 #include	  "dir.h"
103 
104 static Lst        allTargets;	/* the list of all targets found so far */
105 #ifdef CLEANUP
106 static Lst	  allGNs;	/* List of all the GNodes */
107 #endif
108 static Hash_Table targets;	/* a hash table of same */
109 
110 #define HTSIZE	191		/* initial size of hash table */
111 
112 static int TargPrintOnlySrc(ClientData, ClientData);
113 static int TargPrintName(ClientData, ClientData);
114 static int TargPrintNode(ClientData, ClientData);
115 #ifdef CLEANUP
116 static void TargFreeGN(ClientData);
117 #endif
118 static int TargPropagateCohort(ClientData, ClientData);
119 static int TargPropagateNode(ClientData, ClientData);
120 
121 /*-
122  *-----------------------------------------------------------------------
123  * Targ_Init --
124  *	Initialize this module
125  *
126  * Results:
127  *	None
128  *
129  * Side Effects:
130  *	The allTargets list and the targets hash table are initialized
131  *-----------------------------------------------------------------------
132  */
133 void
134 Targ_Init(void)
135 {
136     allTargets = Lst_Init (FALSE);
137     Hash_InitTable (&targets, HTSIZE);
138 }
139 
140 /*-
141  *-----------------------------------------------------------------------
142  * Targ_End --
143  *	Finalize this module
144  *
145  * Results:
146  *	None
147  *
148  * Side Effects:
149  *	All lists and gnodes are cleared
150  *-----------------------------------------------------------------------
151  */
152 void
153 Targ_End(void)
154 {
155 #ifdef CLEANUP
156     Lst_Destroy(allTargets, NOFREE);
157     if (allGNs)
158 	Lst_Destroy(allGNs, TargFreeGN);
159     Hash_DeleteTable(&targets);
160 #endif
161 }
162 
163 /*-
164  *-----------------------------------------------------------------------
165  * Targ_List --
166  *	Return the list of all targets
167  *
168  * Results:
169  *	The list of all targets.
170  *
171  * Side Effects:
172  *	None
173  *-----------------------------------------------------------------------
174  */
175 Lst
176 Targ_List(void)
177 {
178     return allTargets;
179 }
180 
181 /*-
182  *-----------------------------------------------------------------------
183  * Targ_NewGN  --
184  *	Create and initialize a new graph node
185  *
186  * Input:
187  *	name		the name to stick in the new node
188  *
189  * Results:
190  *	An initialized graph node with the name field filled with a copy
191  *	of the passed name
192  *
193  * Side Effects:
194  *	The gnode is added to the list of all gnodes.
195  *-----------------------------------------------------------------------
196  */
197 GNode *
198 Targ_NewGN(char *name)
199 {
200     GNode *gn;
201 
202     gn = (GNode *) emalloc (sizeof (GNode));
203     gn->name = estrdup (name);
204     gn->uname = NULL;
205     gn->path = (char *) 0;
206     if (name[0] == '-' && name[1] == 'l') {
207 	gn->type = OP_LIB;
208     } else {
209 	gn->type = 0;
210     }
211     gn->unmade =    	0;
212     gn->unmade_cohorts = 0;
213     gn->centurion =    	NULL;
214     gn->made = 	    	UNMADE;
215     gn->flags = 	0;
216     gn->order =		0;
217     gn->mtime = gn->cmtime = 0;
218     gn->iParents =  	Lst_Init (FALSE);
219     gn->cohorts =   	Lst_Init (FALSE);
220     gn->parents =   	Lst_Init (FALSE);
221     gn->children =  	Lst_Init (FALSE);
222     gn->successors = 	Lst_Init (FALSE);
223     gn->preds =     	Lst_Init (FALSE);
224     Hash_InitTable(&gn->context, 0);
225     gn->commands =  	Lst_Init (FALSE);
226     gn->suffix =	NULL;
227     gn->lineno =	0;
228     gn->fname = 	NULL;
229 
230 #ifdef CLEANUP
231     if (allGNs == NULL)
232 	allGNs = Lst_Init(FALSE);
233     Lst_AtEnd(allGNs, (ClientData) gn);
234 #endif
235 
236     return (gn);
237 }
238 
239 #ifdef CLEANUP
240 /*-
241  *-----------------------------------------------------------------------
242  * TargFreeGN  --
243  *	Destroy a GNode
244  *
245  * Results:
246  *	None.
247  *
248  * Side Effects:
249  *	None.
250  *-----------------------------------------------------------------------
251  */
252 static void
253 TargFreeGN(ClientData gnp)
254 {
255     GNode *gn = (GNode *) gnp;
256 
257 
258     free(gn->name);
259     if (gn->uname)
260 	free(gn->uname);
261     if (gn->path)
262 	free(gn->path);
263     if (gn->fname)
264 	free(gn->fname);
265 
266     Lst_Destroy(gn->iParents, NOFREE);
267     Lst_Destroy(gn->cohorts, NOFREE);
268     Lst_Destroy(gn->parents, NOFREE);
269     Lst_Destroy(gn->children, NOFREE);
270     Lst_Destroy(gn->successors, NOFREE);
271     Lst_Destroy(gn->preds, NOFREE);
272     Hash_DeleteTable(&gn->context);
273     Lst_Destroy(gn->commands, NOFREE);
274     free((Address)gn);
275 }
276 #endif
277 
278 
279 /*-
280  *-----------------------------------------------------------------------
281  * Targ_FindNode  --
282  *	Find a node in the list using the given name for matching
283  *
284  * Input:
285  *	name		the name to find
286  *	flags		flags governing events when target not
287  *			found
288  *
289  * Results:
290  *	The node in the list if it was. If it wasn't, return NILGNODE of
291  *	flags was TARG_NOCREATE or the newly created and initialized node
292  *	if it was TARG_CREATE
293  *
294  * Side Effects:
295  *	Sometimes a node is created and added to the list
296  *-----------------------------------------------------------------------
297  */
298 GNode *
299 Targ_FindNode(char *name, int flags)
300 {
301     GNode         *gn;	      /* node in that element */
302     Hash_Entry	  *he;	      /* New or used hash entry for node */
303     Boolean	  isNew;      /* Set TRUE if Hash_CreateEntry had to create */
304 			      /* an entry for the node */
305 
306 
307     if (flags & TARG_CREATE) {
308 	he = Hash_CreateEntry (&targets, name, &isNew);
309 	if (isNew) {
310 	    gn = Targ_NewGN (name);
311 	    Hash_SetValue (he, gn);
312 	    Var_Append(".ALLTARGETS", name, VAR_GLOBAL);
313 	    (void) Lst_AtEnd (allTargets, (ClientData)gn);
314 	}
315     } else {
316 	he = Hash_FindEntry (&targets, name);
317     }
318 
319     if (he == (Hash_Entry *) NULL) {
320 	return (NILGNODE);
321     } else {
322 	return ((GNode *) Hash_GetValue (he));
323     }
324 }
325 
326 /*-
327  *-----------------------------------------------------------------------
328  * Targ_FindList --
329  *	Make a complete list of GNodes from the given list of names
330  *
331  * Input:
332  *	name		list of names to find
333  *	flags		flags used if no node is found for a given name
334  *
335  * Results:
336  *	A complete list of graph nodes corresponding to all instances of all
337  *	the names in names.
338  *
339  * Side Effects:
340  *	If flags is TARG_CREATE, nodes will be created for all names in
341  *	names which do not yet have graph nodes. If flags is TARG_NOCREATE,
342  *	an error message will be printed for each name which can't be found.
343  * -----------------------------------------------------------------------
344  */
345 Lst
346 Targ_FindList(Lst names, int flags)
347 {
348     Lst            nodes;	/* result list */
349     LstNode	   ln;		/* name list element */
350     GNode	   *gn;		/* node in tLn */
351     char    	   *name;
352 
353     nodes = Lst_Init (FALSE);
354 
355     if (Lst_Open (names) == FAILURE) {
356 	return (nodes);
357     }
358     while ((ln = Lst_Next (names)) != NILLNODE) {
359 	name = (char *)Lst_Datum(ln);
360 	gn = Targ_FindNode (name, flags);
361 	if (gn != NILGNODE) {
362 	    /*
363 	     * Note: Lst_AtEnd must come before the Lst_Concat so the nodes
364 	     * are added to the list in the order in which they were
365 	     * encountered in the makefile.
366 	     */
367 	    (void) Lst_AtEnd (nodes, (ClientData)gn);
368 	} else if (flags == TARG_NOCREATE) {
369 	    Error ("\"%s\" -- target unknown.", name);
370 	}
371     }
372     Lst_Close (names);
373     return (nodes);
374 }
375 
376 /*-
377  *-----------------------------------------------------------------------
378  * Targ_Ignore  --
379  *	Return true if should ignore errors when creating gn
380  *
381  * Input:
382  *	gn		node to check for
383  *
384  * Results:
385  *	TRUE if should ignore errors
386  *
387  * Side Effects:
388  *	None
389  *-----------------------------------------------------------------------
390  */
391 Boolean
392 Targ_Ignore(GNode *gn)
393 {
394     if (ignoreErrors || gn->type & OP_IGNORE) {
395 	return (TRUE);
396     } else {
397 	return (FALSE);
398     }
399 }
400 
401 /*-
402  *-----------------------------------------------------------------------
403  * Targ_Silent  --
404  *	Return true if be silent when creating gn
405  *
406  * Input:
407  *	gn		node to check for
408  *
409  * Results:
410  *	TRUE if should be silent
411  *
412  * Side Effects:
413  *	None
414  *-----------------------------------------------------------------------
415  */
416 Boolean
417 Targ_Silent(GNode *gn)
418 {
419     if (beSilent || gn->type & OP_SILENT) {
420 	return (TRUE);
421     } else {
422 	return (FALSE);
423     }
424 }
425 
426 /*-
427  *-----------------------------------------------------------------------
428  * Targ_Precious --
429  *	See if the given target is precious
430  *
431  * Input:
432  *	gn		the node to check
433  *
434  * Results:
435  *	TRUE if it is precious. FALSE otherwise
436  *
437  * Side Effects:
438  *	None
439  *-----------------------------------------------------------------------
440  */
441 Boolean
442 Targ_Precious(GNode *gn)
443 {
444     if (allPrecious || (gn->type & (OP_PRECIOUS|OP_DOUBLEDEP))) {
445 	return (TRUE);
446     } else {
447 	return (FALSE);
448     }
449 }
450 
451 /******************* DEBUG INFO PRINTING ****************/
452 
453 static GNode	  *mainTarg;	/* the main target, as set by Targ_SetMain */
454 /*-
455  *-----------------------------------------------------------------------
456  * Targ_SetMain --
457  *	Set our idea of the main target we'll be creating. Used for
458  *	debugging output.
459  *
460  * Input:
461  *	gn		The main target we'll create
462  *
463  * Results:
464  *	None.
465  *
466  * Side Effects:
467  *	"mainTarg" is set to the main target's node.
468  *-----------------------------------------------------------------------
469  */
470 void
471 Targ_SetMain(GNode *gn)
472 {
473     mainTarg = gn;
474 }
475 
476 static int
477 TargPrintName(ClientData gnp, ClientData ppath)
478 {
479     GNode *gn = (GNode *) gnp;
480     printf ("%s ", gn->name);
481 #ifdef notdef
482     if (ppath) {
483 	if (gn->path) {
484 	    printf ("[%s]  ", gn->path);
485 	}
486 	if (gn == mainTarg) {
487 	    printf ("(MAIN NAME)  ");
488 	}
489     }
490 #endif /* notdef */
491     return (ppath ? 0 : 0);
492 }
493 
494 
495 int
496 Targ_PrintCmd(ClientData cmd, ClientData dummy)
497 {
498     printf ("\t%s\n", (char *) cmd);
499     return (dummy ? 0 : 0);
500 }
501 
502 /*-
503  *-----------------------------------------------------------------------
504  * Targ_FmtTime --
505  *	Format a modification time in some reasonable way and return it.
506  *
507  * Results:
508  *	The time reformatted.
509  *
510  * Side Effects:
511  *	The time is placed in a static area, so it is overwritten
512  *	with each call.
513  *
514  *-----------------------------------------------------------------------
515  */
516 char *
517 Targ_FmtTime(time_t tm)
518 {
519     struct tm	  	*parts;
520     static char	  	buf[128];
521 
522     parts = localtime(&tm);
523     (void)strftime(buf, sizeof buf, "%k:%M:%S %b %d, %Y", parts);
524     return(buf);
525 }
526 
527 /*-
528  *-----------------------------------------------------------------------
529  * Targ_PrintType --
530  *	Print out a type field giving only those attributes the user can
531  *	set.
532  *
533  * Results:
534  *
535  * Side Effects:
536  *
537  *-----------------------------------------------------------------------
538  */
539 void
540 Targ_PrintType(int type)
541 {
542     int    tbit;
543 
544 #define PRINTBIT(attr)	case CONCAT(OP_,attr): printf("." #attr " "); break
545 #define PRINTDBIT(attr) case CONCAT(OP_,attr): if (DEBUG(TARG)) printf("." #attr " "); break
546 
547     type &= ~OP_OPMASK;
548 
549     while (type) {
550 	tbit = 1 << (ffs(type) - 1);
551 	type &= ~tbit;
552 
553 	switch(tbit) {
554 	    PRINTBIT(OPTIONAL);
555 	    PRINTBIT(USE);
556 	    PRINTBIT(EXEC);
557 	    PRINTBIT(IGNORE);
558 	    PRINTBIT(PRECIOUS);
559 	    PRINTBIT(SILENT);
560 	    PRINTBIT(MAKE);
561 	    PRINTBIT(JOIN);
562 	    PRINTBIT(INVISIBLE);
563 	    PRINTBIT(NOTMAIN);
564 	    PRINTDBIT(LIB);
565 	    /*XXX: MEMBER is defined, so CONCAT(OP_,MEMBER) gives OP_"%" */
566 	    case OP_MEMBER: if (DEBUG(TARG)) printf(".MEMBER "); break;
567 	    PRINTDBIT(ARCHV);
568 	    PRINTDBIT(MADE);
569 	    PRINTDBIT(PHONY);
570 	}
571     }
572 }
573 
574 /*-
575  *-----------------------------------------------------------------------
576  * TargPrintNode --
577  *	print the contents of a node
578  *-----------------------------------------------------------------------
579  */
580 static int
581 TargPrintNode(ClientData gnp, ClientData passp)
582 {
583     GNode         *gn = (GNode *) gnp;
584     int	    	  pass = *(int *) passp;
585     if (!OP_NOP(gn->type)) {
586 	printf("#\n");
587 	if (gn == mainTarg) {
588 	    printf("# *** MAIN TARGET ***\n");
589 	}
590 	if (pass == 2) {
591 	    if (gn->unmade) {
592 		printf("# %d unmade children\n", gn->unmade);
593 	    } else {
594 		printf("# No unmade children\n");
595 	    }
596 	    if (! (gn->type & (OP_JOIN|OP_USE|OP_USEBEFORE|OP_EXEC))) {
597 		if (gn->mtime != 0) {
598 		    printf("# last modified %s: %s\n",
599 			      Targ_FmtTime(gn->mtime),
600 			      (gn->made == UNMADE ? "unmade" :
601 			       (gn->made == MADE ? "made" :
602 				(gn->made == UPTODATE ? "up-to-date" :
603 				 "error when made"))));
604 		} else if (gn->made != UNMADE) {
605 		    printf("# non-existent (maybe): %s\n",
606 			      (gn->made == MADE ? "made" :
607 			       (gn->made == UPTODATE ? "up-to-date" :
608 				(gn->made == ERROR ? "error when made" :
609 				 "aborted"))));
610 		} else {
611 		    printf("# unmade\n");
612 		}
613 	    }
614 	    if (!Lst_IsEmpty (gn->iParents)) {
615 		printf("# implicit parents: ");
616 		Lst_ForEach (gn->iParents, TargPrintName, (ClientData)0);
617 		fputc ('\n', stdout);
618 	    }
619 	}
620 	if (!Lst_IsEmpty (gn->parents)) {
621 	    printf("# parents: ");
622 	    Lst_ForEach (gn->parents, TargPrintName, (ClientData)0);
623 	    fputc ('\n', stdout);
624 	}
625 
626 	printf("%-16s", gn->name);
627 	switch (gn->type & OP_OPMASK) {
628 	    case OP_DEPENDS:
629 		printf(": "); break;
630 	    case OP_FORCE:
631 		printf("! "); break;
632 	    case OP_DOUBLEDEP:
633 		printf(":: "); break;
634 	}
635 	Targ_PrintType (gn->type);
636 	Lst_ForEach (gn->children, TargPrintName, (ClientData)0);
637 	fputc ('\n', stdout);
638 	Lst_ForEach (gn->commands, Targ_PrintCmd, (ClientData)0);
639 	printf("\n\n");
640 	if (gn->type & OP_DOUBLEDEP) {
641 	    Lst_ForEach (gn->cohorts, TargPrintNode, (ClientData)&pass);
642 	}
643     }
644     return (0);
645 }
646 
647 /*-
648  *-----------------------------------------------------------------------
649  * TargPrintOnlySrc --
650  *	Print only those targets that are just a source.
651  *
652  * Results:
653  *	0.
654  *
655  * Side Effects:
656  *	The name of each file is printed preceded by #\t
657  *
658  *-----------------------------------------------------------------------
659  */
660 static int
661 TargPrintOnlySrc(ClientData gnp, ClientData dummy)
662 {
663     GNode   	  *gn = (GNode *) gnp;
664     if (OP_NOP(gn->type))
665 	printf("#\t%s [%s]\n", gn->name, gn->path ? gn->path : gn->name);
666 
667     return (dummy ? 0 : 0);
668 }
669 
670 /*-
671  *-----------------------------------------------------------------------
672  * Targ_PrintGraph --
673  *	print the entire graph. heh heh
674  *
675  * Input:
676  *	pass		Which pass this is. 1 => no processing
677  *			2 => processing done
678  *
679  * Results:
680  *	none
681  *
682  * Side Effects:
683  *	lots o' output
684  *-----------------------------------------------------------------------
685  */
686 void
687 Targ_PrintGraph(int pass)
688 {
689     printf("#*** Input graph:\n");
690     Lst_ForEach (allTargets, TargPrintNode, (ClientData)&pass);
691     printf("\n\n");
692     printf("#\n#   Files that are only sources:\n");
693     Lst_ForEach (allTargets, TargPrintOnlySrc, (ClientData) 0);
694     printf("#*** Global Variables:\n");
695     Var_Dump (VAR_GLOBAL);
696     printf("#*** Command-line Variables:\n");
697     Var_Dump (VAR_CMD);
698     printf("\n");
699     Dir_PrintDirectories();
700     printf("\n");
701     Suff_PrintAll();
702 }
703 
704 static int
705 TargPropagateCohort(ClientData cgnp, ClientData pgnp)
706 {
707     GNode	  *cgn = (GNode *) cgnp;
708     GNode	  *pgn = (GNode *) pgnp;
709 
710     cgn->type |= pgn->type & ~OP_OPMASK;
711     return (0);
712 }
713 
714 static int
715 TargPropagateNode(ClientData gnp, ClientData junk)
716 {
717     GNode	  *gn = (GNode *) gnp;
718     if (gn->type & OP_DOUBLEDEP)
719 	Lst_ForEach (gn->cohorts, TargPropagateCohort, gnp);
720     return (0);
721 }
722 
723 void
724 Targ_Propagate(void)
725 {
726     Lst_ForEach (allTargets, TargPropagateNode, (ClientData)0);
727 }
728