xref: /netbsd-src/usr.bin/make/targ.c (revision 481fca6e59249d8ffcf24fef7cfbe7b131bfb080)
1 /*	$NetBSD: targ.c,v 1.20 1999/09/16 00:54:15 mycroft 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.20 1999/09/16 00:54:15 mycroft 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.20 1999/09/16 00:54:15 mycroft 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 #include	  "make.h"
100 #include	  "hash.h"
101 #include	  "dir.h"
102 
103 static Lst        allTargets;	/* the list of all targets found so far */
104 #ifdef CLEANUP
105 static Lst	  allGNs;	/* List of all the GNodes */
106 #endif
107 static Hash_Table targets;	/* a hash table of same */
108 
109 #define HTSIZE	191		/* initial size of hash table */
110 
111 static int TargPrintOnlySrc __P((ClientData, ClientData));
112 static int TargPrintName __P((ClientData, ClientData));
113 static int TargPrintNode __P((ClientData, ClientData));
114 #ifdef CLEANUP
115 static void TargFreeGN __P((ClientData));
116 #endif
117 static int TargPropagateCohort __P((ClientData, ClientData));
118 static int TargPropagateNode __P((ClientData, ClientData));
119 
120 /*-
121  *-----------------------------------------------------------------------
122  * Targ_Init --
123  *	Initialize this module
124  *
125  * Results:
126  *	None
127  *
128  * Side Effects:
129  *	The allTargets list and the targets hash table are initialized
130  *-----------------------------------------------------------------------
131  */
132 void
133 Targ_Init ()
134 {
135     allTargets = Lst_Init (FALSE);
136     Hash_InitTable (&targets, HTSIZE);
137 }
138 
139 /*-
140  *-----------------------------------------------------------------------
141  * Targ_End --
142  *	Finalize this module
143  *
144  * Results:
145  *	None
146  *
147  * Side Effects:
148  *	All lists and gnodes are cleared
149  *-----------------------------------------------------------------------
150  */
151 void
152 Targ_End ()
153 {
154 #ifdef CLEANUP
155     Lst_Destroy(allTargets, NOFREE);
156     if (allGNs)
157 	Lst_Destroy(allGNs, TargFreeGN);
158     Hash_DeleteTable(&targets);
159 #endif
160 }
161 
162 /*-
163  *-----------------------------------------------------------------------
164  * Targ_List --
165  *	Return the list of all targets
166  *
167  * Results:
168  *	The list of all targets.
169  *
170  * Side Effects:
171  *	None
172  *-----------------------------------------------------------------------
173  */
174 Lst
175 Targ_List ()
176 {
177     return allTargets;
178 }
179 
180 /*-
181  *-----------------------------------------------------------------------
182  * Targ_NewGN  --
183  *	Create and initialize a new graph node
184  *
185  * Results:
186  *	An initialized graph node with the name field filled with a copy
187  *	of the passed name
188  *
189  * Side Effects:
190  *	The gnode is added to the list of all gnodes.
191  *-----------------------------------------------------------------------
192  */
193 GNode *
194 Targ_NewGN (name)
195     char           *name;	/* the name to stick in the new node */
196 {
197     register GNode *gn;
198 
199     gn = (GNode *) emalloc (sizeof (GNode));
200     gn->name = estrdup (name);
201     gn->uname = NULL;
202     gn->path = (char *) 0;
203     if (name[0] == '-' && name[1] == 'l') {
204 	gn->type = OP_LIB;
205     } else {
206 	gn->type = 0;
207     }
208     gn->unmade =    	0;
209     gn->made = 	    	UNMADE;
210     gn->flags = 	0;
211     gn->order =		0;
212     gn->mtime = gn->cmtime = 0;
213     gn->iParents =  	Lst_Init (FALSE);
214     gn->cohorts =   	Lst_Init (FALSE);
215     gn->parents =   	Lst_Init (FALSE);
216     gn->children =  	Lst_Init (FALSE);
217     gn->successors = 	Lst_Init (FALSE);
218     gn->preds =     	Lst_Init (FALSE);
219     Hash_InitTable(&gn->context, 0);
220     gn->commands =  	Lst_Init (FALSE);
221     gn->suffix =	NULL;
222 
223 #ifdef CLEANUP
224     if (allGNs == NULL)
225 	allGNs = Lst_Init(FALSE);
226     Lst_AtEnd(allGNs, (ClientData) gn);
227 #endif
228 
229     return (gn);
230 }
231 
232 #ifdef CLEANUP
233 /*-
234  *-----------------------------------------------------------------------
235  * TargFreeGN  --
236  *	Destroy a GNode
237  *
238  * Results:
239  *	None.
240  *
241  * Side Effects:
242  *	None.
243  *-----------------------------------------------------------------------
244  */
245 static void
246 TargFreeGN (gnp)
247     ClientData gnp;
248 {
249     GNode *gn = (GNode *) gnp;
250 
251 
252     free(gn->name);
253     if (gn->uname)
254 	free(gn->uname);
255     if (gn->path)
256 	free(gn->path);
257 
258     Lst_Destroy(gn->iParents, NOFREE);
259     Lst_Destroy(gn->cohorts, NOFREE);
260     Lst_Destroy(gn->parents, NOFREE);
261     Lst_Destroy(gn->children, NOFREE);
262     Lst_Destroy(gn->successors, NOFREE);
263     Lst_Destroy(gn->preds, NOFREE);
264     Hash_DeleteTable(&gn->context);
265     Lst_Destroy(gn->commands, NOFREE);
266     free((Address)gn);
267 }
268 #endif
269 
270 
271 /*-
272  *-----------------------------------------------------------------------
273  * Targ_FindNode  --
274  *	Find a node in the list using the given name for matching
275  *
276  * Results:
277  *	The node in the list if it was. If it wasn't, return NILGNODE of
278  *	flags was TARG_NOCREATE or the newly created and initialized node
279  *	if it was TARG_CREATE
280  *
281  * Side Effects:
282  *	Sometimes a node is created and added to the list
283  *-----------------------------------------------------------------------
284  */
285 GNode *
286 Targ_FindNode (name, flags)
287     char           *name;	/* the name to find */
288     int             flags;	/* flags governing events when target not
289 				 * found */
290 {
291     GNode         *gn;	      /* node in that element */
292     Hash_Entry	  *he;	      /* New or used hash entry for node */
293     Boolean	  isNew;      /* Set TRUE if Hash_CreateEntry had to create */
294 			      /* an entry for the node */
295 
296 
297     if (flags & TARG_CREATE) {
298 	he = Hash_CreateEntry (&targets, name, &isNew);
299 	if (isNew) {
300 	    gn = Targ_NewGN (name);
301 	    Hash_SetValue (he, gn);
302 	    (void) Lst_AtEnd (allTargets, (ClientData)gn);
303 	}
304     } else {
305 	he = Hash_FindEntry (&targets, name);
306     }
307 
308     if (he == (Hash_Entry *) NULL) {
309 	return (NILGNODE);
310     } else {
311 	return ((GNode *) Hash_GetValue (he));
312     }
313 }
314 
315 /*-
316  *-----------------------------------------------------------------------
317  * Targ_FindList --
318  *	Make a complete list of GNodes from the given list of names
319  *
320  * Results:
321  *	A complete list of graph nodes corresponding to all instances of all
322  *	the names in names.
323  *
324  * Side Effects:
325  *	If flags is TARG_CREATE, nodes will be created for all names in
326  *	names which do not yet have graph nodes. If flags is TARG_NOCREATE,
327  *	an error message will be printed for each name which can't be found.
328  * -----------------------------------------------------------------------
329  */
330 Lst
331 Targ_FindList (names, flags)
332     Lst        	   names;	/* list of names to find */
333     int            flags;	/* flags used if no node is found for a given
334 				 * name */
335 {
336     Lst            nodes;	/* result list */
337     register LstNode  ln;		/* name list element */
338     register GNode *gn;		/* node in tLn */
339     char    	  *name;
340 
341     nodes = Lst_Init (FALSE);
342 
343     if (Lst_Open (names) == FAILURE) {
344 	return (nodes);
345     }
346     while ((ln = Lst_Next (names)) != NILLNODE) {
347 	name = (char *)Lst_Datum(ln);
348 	gn = Targ_FindNode (name, flags);
349 	if (gn != NILGNODE) {
350 	    /*
351 	     * Note: Lst_AtEnd must come before the Lst_Concat so the nodes
352 	     * are added to the list in the order in which they were
353 	     * encountered in the makefile.
354 	     */
355 	    (void) Lst_AtEnd (nodes, (ClientData)gn);
356 	} else if (flags == TARG_NOCREATE) {
357 	    Error ("\"%s\" -- target unknown.", name);
358 	}
359     }
360     Lst_Close (names);
361     return (nodes);
362 }
363 
364 /*-
365  *-----------------------------------------------------------------------
366  * Targ_Ignore  --
367  *	Return true if should ignore errors when creating gn
368  *
369  * Results:
370  *	TRUE if should ignore errors
371  *
372  * Side Effects:
373  *	None
374  *-----------------------------------------------------------------------
375  */
376 Boolean
377 Targ_Ignore (gn)
378     GNode          *gn;		/* node to check for */
379 {
380     if (ignoreErrors || gn->type & OP_IGNORE) {
381 	return (TRUE);
382     } else {
383 	return (FALSE);
384     }
385 }
386 
387 /*-
388  *-----------------------------------------------------------------------
389  * Targ_Silent  --
390  *	Return true if be silent when creating gn
391  *
392  * Results:
393  *	TRUE if should be silent
394  *
395  * Side Effects:
396  *	None
397  *-----------------------------------------------------------------------
398  */
399 Boolean
400 Targ_Silent (gn)
401     GNode          *gn;		/* node to check for */
402 {
403     if (beSilent || gn->type & OP_SILENT) {
404 	return (TRUE);
405     } else {
406 	return (FALSE);
407     }
408 }
409 
410 /*-
411  *-----------------------------------------------------------------------
412  * Targ_Precious --
413  *	See if the given target is precious
414  *
415  * Results:
416  *	TRUE if it is precious. FALSE otherwise
417  *
418  * Side Effects:
419  *	None
420  *-----------------------------------------------------------------------
421  */
422 Boolean
423 Targ_Precious (gn)
424     GNode          *gn;		/* the node to check */
425 {
426     if (allPrecious || (gn->type & (OP_PRECIOUS|OP_DOUBLEDEP))) {
427 	return (TRUE);
428     } else {
429 	return (FALSE);
430     }
431 }
432 
433 /******************* DEBUG INFO PRINTING ****************/
434 
435 static GNode	  *mainTarg;	/* the main target, as set by Targ_SetMain */
436 /*-
437  *-----------------------------------------------------------------------
438  * Targ_SetMain --
439  *	Set our idea of the main target we'll be creating. Used for
440  *	debugging output.
441  *
442  * Results:
443  *	None.
444  *
445  * Side Effects:
446  *	"mainTarg" is set to the main target's node.
447  *-----------------------------------------------------------------------
448  */
449 void
450 Targ_SetMain (gn)
451     GNode   *gn;  	/* The main target we'll create */
452 {
453     mainTarg = gn;
454 }
455 
456 static int
457 TargPrintName (gnp, ppath)
458     ClientData     gnp;
459     ClientData	    ppath;
460 {
461     GNode *gn = (GNode *) gnp;
462     printf ("%s ", gn->name);
463 #ifdef notdef
464     if (ppath) {
465 	if (gn->path) {
466 	    printf ("[%s]  ", gn->path);
467 	}
468 	if (gn == mainTarg) {
469 	    printf ("(MAIN NAME)  ");
470 	}
471     }
472 #endif /* notdef */
473     return (ppath ? 0 : 0);
474 }
475 
476 
477 int
478 Targ_PrintCmd (cmd, dummy)
479     ClientData cmd;
480     ClientData dummy;
481 {
482     printf ("\t%s\n", (char *) cmd);
483     return (dummy ? 0 : 0);
484 }
485 
486 /*-
487  *-----------------------------------------------------------------------
488  * Targ_FmtTime --
489  *	Format a modification time in some reasonable way and return it.
490  *
491  * Results:
492  *	The time reformatted.
493  *
494  * Side Effects:
495  *	The time is placed in a static area, so it is overwritten
496  *	with each call.
497  *
498  *-----------------------------------------------------------------------
499  */
500 char *
501 Targ_FmtTime (time)
502     time_t    time;
503 {
504     struct tm	  	*parts;
505     static char	  	buf[128];
506 
507     parts = localtime(&time);
508     (void)strftime(buf, sizeof buf, "%k:%M:%S %b %d, %Y", parts);
509     return(buf);
510 }
511 
512 /*-
513  *-----------------------------------------------------------------------
514  * Targ_PrintType --
515  *	Print out a type field giving only those attributes the user can
516  *	set.
517  *
518  * Results:
519  *
520  * Side Effects:
521  *
522  *-----------------------------------------------------------------------
523  */
524 void
525 Targ_PrintType (type)
526     register int    type;
527 {
528     register int    tbit;
529 
530 #ifdef __STDC__
531 #define PRINTBIT(attr)	case CONCAT(OP_,attr): printf("." #attr " "); break
532 #define PRINTDBIT(attr) case CONCAT(OP_,attr): if (DEBUG(TARG)) printf("." #attr " "); break
533 #else
534 #define PRINTBIT(attr) 	case CONCAT(OP_,attr): printf(".attr "); break
535 #define PRINTDBIT(attr)	case CONCAT(OP_,attr): if (DEBUG(TARG)) printf(".attr "); break
536 #endif /* __STDC__ */
537 
538     type &= ~OP_OPMASK;
539 
540     while (type) {
541 	tbit = 1 << (ffs(type) - 1);
542 	type &= ~tbit;
543 
544 	switch(tbit) {
545 	    PRINTBIT(OPTIONAL);
546 	    PRINTBIT(USE);
547 	    PRINTBIT(EXEC);
548 	    PRINTBIT(IGNORE);
549 	    PRINTBIT(PRECIOUS);
550 	    PRINTBIT(SILENT);
551 	    PRINTBIT(MAKE);
552 	    PRINTBIT(JOIN);
553 	    PRINTBIT(INVISIBLE);
554 	    PRINTBIT(NOTMAIN);
555 	    PRINTDBIT(LIB);
556 	    /*XXX: MEMBER is defined, so CONCAT(OP_,MEMBER) gives OP_"%" */
557 	    case OP_MEMBER: if (DEBUG(TARG)) printf(".MEMBER "); break;
558 	    PRINTDBIT(ARCHV);
559 	}
560     }
561 }
562 
563 /*-
564  *-----------------------------------------------------------------------
565  * TargPrintNode --
566  *	print the contents of a node
567  *-----------------------------------------------------------------------
568  */
569 static int
570 TargPrintNode (gnp, passp)
571     ClientData   gnp;
572     ClientData	 passp;
573 {
574     GNode         *gn = (GNode *) gnp;
575     int	    	  pass = *(int *) passp;
576     if (!OP_NOP(gn->type)) {
577 	printf("#\n");
578 	if (gn == mainTarg) {
579 	    printf("# *** MAIN TARGET ***\n");
580 	}
581 	if (pass == 2) {
582 	    if (gn->unmade) {
583 		printf("# %d unmade children\n", gn->unmade);
584 	    } else {
585 		printf("# No unmade children\n");
586 	    }
587 	    if (! (gn->type & (OP_JOIN|OP_USE|OP_EXEC))) {
588 		if (gn->mtime != 0) {
589 		    printf("# last modified %s: %s\n",
590 			      Targ_FmtTime(gn->mtime),
591 			      (gn->made == UNMADE ? "unmade" :
592 			       (gn->made == MADE ? "made" :
593 				(gn->made == UPTODATE ? "up-to-date" :
594 				 "error when made"))));
595 		} else if (gn->made != UNMADE) {
596 		    printf("# non-existent (maybe): %s\n",
597 			      (gn->made == MADE ? "made" :
598 			       (gn->made == UPTODATE ? "up-to-date" :
599 				(gn->made == ERROR ? "error when made" :
600 				 "aborted"))));
601 		} else {
602 		    printf("# unmade\n");
603 		}
604 	    }
605 	    if (!Lst_IsEmpty (gn->iParents)) {
606 		printf("# implicit parents: ");
607 		Lst_ForEach (gn->iParents, TargPrintName, (ClientData)0);
608 		fputc ('\n', stdout);
609 	    }
610 	}
611 	if (!Lst_IsEmpty (gn->parents)) {
612 	    printf("# parents: ");
613 	    Lst_ForEach (gn->parents, TargPrintName, (ClientData)0);
614 	    fputc ('\n', stdout);
615 	}
616 
617 	printf("%-16s", gn->name);
618 	switch (gn->type & OP_OPMASK) {
619 	    case OP_DEPENDS:
620 		printf(": "); break;
621 	    case OP_FORCE:
622 		printf("! "); break;
623 	    case OP_DOUBLEDEP:
624 		printf(":: "); break;
625 	}
626 	Targ_PrintType (gn->type);
627 	Lst_ForEach (gn->children, TargPrintName, (ClientData)0);
628 	fputc ('\n', stdout);
629 	Lst_ForEach (gn->commands, Targ_PrintCmd, (ClientData)0);
630 	printf("\n\n");
631 	if (gn->type & OP_DOUBLEDEP) {
632 	    Lst_ForEach (gn->cohorts, TargPrintNode, (ClientData)&pass);
633 	}
634     }
635     return (0);
636 }
637 
638 /*-
639  *-----------------------------------------------------------------------
640  * TargPrintOnlySrc --
641  *	Print only those targets that are just a source.
642  *
643  * Results:
644  *	0.
645  *
646  * Side Effects:
647  *	The name of each file is printed preceeded by #\t
648  *
649  *-----------------------------------------------------------------------
650  */
651 static int
652 TargPrintOnlySrc(gnp, dummy)
653     ClientData 	  gnp;
654     ClientData 	  dummy;
655 {
656     GNode   	  *gn = (GNode *) gnp;
657     if (OP_NOP(gn->type))
658 	printf("#\t%s [%s]\n", gn->name, gn->path ? gn->path : gn->name);
659 
660     return (dummy ? 0 : 0);
661 }
662 
663 /*-
664  *-----------------------------------------------------------------------
665  * Targ_PrintGraph --
666  *	print the entire graph. heh heh
667  *
668  * Results:
669  *	none
670  *
671  * Side Effects:
672  *	lots o' output
673  *-----------------------------------------------------------------------
674  */
675 void
676 Targ_PrintGraph (pass)
677     int	    pass; 	/* Which pass this is. 1 => no processing
678 			 * 2 => processing done */
679 {
680     printf("#*** Input graph:\n");
681     Lst_ForEach (allTargets, TargPrintNode, (ClientData)&pass);
682     printf("\n\n");
683     printf("#\n#   Files that are only sources:\n");
684     Lst_ForEach (allTargets, TargPrintOnlySrc, (ClientData) 0);
685     printf("#*** Global Variables:\n");
686     Var_Dump (VAR_GLOBAL);
687     printf("#*** Command-line Variables:\n");
688     Var_Dump (VAR_CMD);
689     printf("\n");
690     Dir_PrintDirectories();
691     printf("\n");
692     Suff_PrintAll();
693 }
694 
695 static int
696 TargPropagateCohort (cgnp, pgnp)
697     ClientData   cgnp;
698     ClientData   pgnp;
699 {
700     GNode	  *cgn = (GNode *) cgnp;
701     GNode	  *pgn = (GNode *) pgnp;
702 
703     cgn->type |= pgn->type & ~OP_OPMASK;
704     return (0);
705 }
706 
707 static int
708 TargPropagateNode (gnp, junk)
709     ClientData   gnp;
710     ClientData   junk;
711 {
712     GNode	  *gn = (GNode *) gnp;
713     if (gn->type & OP_DOUBLEDEP)
714 	Lst_ForEach (gn->cohorts, TargPropagateCohort, gnp);
715     return (0);
716 }
717 
718 void
719 Targ_Propagate ()
720 {
721     Lst_ForEach (allTargets, TargPropagateNode, (ClientData)0);
722 }
723