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