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