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