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