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