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