Initial import from FreeBSD RELENG_4:
[dragonfly.git] / usr.bin / make / targ.c
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
40 #if 0
41 static char sccsid[] = "@(#)targ.c      8.2 (Berkeley) 3/19/94";
42 #else
43 static const char rcsid[] =
44   "$FreeBSD: src/usr.bin/make/targ.c,v 1.10 1999/09/11 13:08:02 hoek Exp $";
45 #endif
46 #endif /* not lint */
47
48 /*-
49  * targ.c --
50  *      Functions for maintaining the Lst allTargets. Target nodes are
51  * kept in two structures: a Lst, maintained by the list library, and a
52  * hash table, maintained by the hash library.
53  *
54  * Interface:
55  *      Targ_Init               Initialization procedure.
56  *
57  *      Targ_End                Cleanup the module
58  *
59  *      Targ_NewGN              Create a new GNode for the passed target
60  *                              (string). The node is *not* placed in the
61  *                              hash table, though all its fields are
62  *                              initialized.
63  *
64  *      Targ_FindNode           Find the node for a given target, creating
65  *                              and storing it if it doesn't exist and the
66  *                              flags are right (TARG_CREATE)
67  *
68  *      Targ_FindList           Given a list of names, find nodes for all
69  *                              of them. If a name doesn't exist and the
70  *                              TARG_NOCREATE flag was given, an error message
71  *                              is printed. Else, if a name doesn't exist,
72  *                              its node is created.
73  *
74  *      Targ_Ignore             Return TRUE if errors should be ignored when
75  *                              creating the given target.
76  *
77  *      Targ_Silent             Return TRUE if we should be silent when
78  *                              creating the given target.
79  *
80  *      Targ_Precious           Return TRUE if the target is precious and
81  *                              should not be removed if we are interrupted.
82  *
83  * Debugging:
84  *      Targ_PrintGraph         Print out the entire graphm all variables
85  *                              and statistics for the directory cache. Should
86  *                              print something for suffixes, too, but...
87  */
88
89 #include          <stdio.h>
90 #include          <time.h>
91 #include          "make.h"
92 #include          "hash.h"
93 #include          "dir.h"
94
95 static Lst        allTargets;   /* the list of all targets found so far */
96 static Lst        allGNs;       /* List of all the GNodes */
97 static Hash_Table targets;      /* a hash table of same */
98
99 #define HTSIZE  191             /* initial size of hash table */
100
101 static int TargPrintOnlySrc __P((ClientData, ClientData));
102 static int TargPrintName __P((ClientData, ClientData));
103 static int TargPrintNode __P((ClientData, ClientData));
104 static void TargFreeGN __P((ClientData));
105
106 /*-
107  *-----------------------------------------------------------------------
108  * Targ_Init --
109  *      Initialize this module
110  *
111  * Results:
112  *      None
113  *
114  * Side Effects:
115  *      The allTargets list and the targets hash table are initialized
116  *-----------------------------------------------------------------------
117  */
118 void
119 Targ_Init ()
120 {
121     allTargets = Lst_Init (FALSE);
122     Hash_InitTable (&targets, HTSIZE);
123 }
124
125 /*-
126  *-----------------------------------------------------------------------
127  * Targ_End --
128  *      Finalize this module
129  *
130  * Results:
131  *      None
132  *
133  * Side Effects:
134  *      All lists and gnodes are cleared
135  *-----------------------------------------------------------------------
136  */
137 void
138 Targ_End ()
139 {
140     Lst_Destroy(allTargets, NOFREE);
141     if (allGNs)
142         Lst_Destroy(allGNs, TargFreeGN);
143     Hash_DeleteTable(&targets);
144 }
145
146 /*-
147  *-----------------------------------------------------------------------
148  * Targ_NewGN  --
149  *      Create and initialize a new graph node
150  *
151  * Results:
152  *      An initialized graph node with the name field filled with a copy
153  *      of the passed name
154  *
155  * Side Effects:
156  *      The gnode is added to the list of all gnodes.
157  *-----------------------------------------------------------------------
158  */
159 GNode *
160 Targ_NewGN (name)
161     char           *name;       /* the name to stick in the new node */
162 {
163     register GNode *gn;
164
165     gn = (GNode *) emalloc (sizeof (GNode));
166     gn->name = estrdup (name);
167     gn->path = (char *) 0;
168     if (name[0] == '-' && name[1] == 'l') {
169         gn->type = OP_LIB;
170     } else {
171         gn->type = 0;
172     }
173     gn->unmade =        0;
174     gn->make =          FALSE;
175     gn->made =          UNMADE;
176     gn->childMade =     FALSE;
177     gn->order =         0;
178     gn->mtime = gn->cmtime = 0;
179     gn->iParents =      Lst_Init (FALSE);
180     gn->cohorts =       Lst_Init (FALSE);
181     gn->parents =       Lst_Init (FALSE);
182     gn->children =      Lst_Init (FALSE);
183     gn->successors =    Lst_Init (FALSE);
184     gn->preds =         Lst_Init (FALSE);
185     gn->context =       Lst_Init (FALSE);
186     gn->commands =      Lst_Init (FALSE);
187     gn->suffix =        NULL;
188
189     if (allGNs == NULL)
190         allGNs = Lst_Init(FALSE);
191     Lst_AtEnd(allGNs, (ClientData) gn);
192
193     return (gn);
194 }
195
196 /*-
197  *-----------------------------------------------------------------------
198  * TargFreeGN  --
199  *      Destroy a GNode
200  *
201  * Results:
202  *      None.
203  *
204  * Side Effects:
205  *      None.
206  *-----------------------------------------------------------------------
207  */
208 static void
209 TargFreeGN (gnp)
210     ClientData gnp;
211 {
212     GNode *gn = (GNode *) gnp;
213
214
215     free(gn->name);
216     efree(gn->path);
217
218     Lst_Destroy(gn->iParents, NOFREE);
219     Lst_Destroy(gn->cohorts, NOFREE);
220     Lst_Destroy(gn->parents, NOFREE);
221     Lst_Destroy(gn->children, NOFREE);
222     Lst_Destroy(gn->successors, NOFREE);
223     Lst_Destroy(gn->preds, NOFREE);
224     Lst_Destroy(gn->context, NOFREE);
225     Lst_Destroy(gn->commands, NOFREE);
226     free((Address)gn);
227 }
228
229
230 /*-
231  *-----------------------------------------------------------------------
232  * Targ_FindNode  --
233  *      Find a node in the list using the given name for matching
234  *
235  * Results:
236  *      The node in the list if it was. If it wasn't, return NILGNODE of
237  *      flags was TARG_NOCREATE or the newly created and initialized node
238  *      if it was TARG_CREATE
239  *
240  * Side Effects:
241  *      Sometimes a node is created and added to the list
242  *-----------------------------------------------------------------------
243  */
244 GNode *
245 Targ_FindNode (name, flags)
246     char           *name;       /* the name to find */
247     int             flags;      /* flags governing events when target not
248                                  * found */
249 {
250     GNode         *gn;        /* node in that element */
251     Hash_Entry    *he;        /* New or used hash entry for node */
252     Boolean       isNew;      /* Set TRUE if Hash_CreateEntry had to create */
253                               /* an entry for the node */
254
255
256     if (flags & TARG_CREATE) {
257         he = Hash_CreateEntry (&targets, name, &isNew);
258         if (isNew) {
259             gn = Targ_NewGN (name);
260             Hash_SetValue (he, gn);
261             (void) Lst_AtEnd (allTargets, (ClientData)gn);
262         }
263     } else {
264         he = Hash_FindEntry (&targets, name);
265     }
266
267     if (he == (Hash_Entry *) NULL) {
268         return (NILGNODE);
269     } else {
270         return ((GNode *) Hash_GetValue (he));
271     }
272 }
273
274 /*-
275  *-----------------------------------------------------------------------
276  * Targ_FindList --
277  *      Make a complete list of GNodes from the given list of names
278  *
279  * Results:
280  *      A complete list of graph nodes corresponding to all instances of all
281  *      the names in names.
282  *
283  * Side Effects:
284  *      If flags is TARG_CREATE, nodes will be created for all names in
285  *      names which do not yet have graph nodes. If flags is TARG_NOCREATE,
286  *      an error message will be printed for each name which can't be found.
287  * -----------------------------------------------------------------------
288  */
289 Lst
290 Targ_FindList (names, flags)
291     Lst            names;       /* list of names to find */
292     int            flags;       /* flags used if no node is found for a given
293                                  * name */
294 {
295     Lst            nodes;       /* result list */
296     register LstNode  ln;               /* name list element */
297     register GNode *gn;         /* node in tLn */
298     char          *name;
299
300     nodes = Lst_Init (FALSE);
301
302     if (Lst_Open (names) == FAILURE) {
303         return (nodes);
304     }
305     while ((ln = Lst_Next (names)) != NILLNODE) {
306         name = (char *)Lst_Datum(ln);
307         gn = Targ_FindNode (name, flags);
308         if (gn != NILGNODE) {
309             /*
310              * Note: Lst_AtEnd must come before the Lst_Concat so the nodes
311              * are added to the list in the order in which they were
312              * encountered in the makefile.
313              */
314             (void) Lst_AtEnd (nodes, (ClientData)gn);
315             if (gn->type & OP_DOUBLEDEP) {
316                 (void)Lst_Concat (nodes, gn->cohorts, LST_CONCNEW);
317             }
318         } else if (flags == TARG_NOCREATE) {
319             Error ("\"%s\" -- target unknown.", name);
320         }
321     }
322     Lst_Close (names);
323     return (nodes);
324 }
325
326 /*-
327  *-----------------------------------------------------------------------
328  * Targ_Ignore  --
329  *      Return true if should ignore errors when creating gn
330  *
331  * Results:
332  *      TRUE if should ignore errors
333  *
334  * Side Effects:
335  *      None
336  *-----------------------------------------------------------------------
337  */
338 Boolean
339 Targ_Ignore (gn)
340     GNode          *gn;         /* node to check for */
341 {
342     if (ignoreErrors || gn->type & OP_IGNORE) {
343         return (TRUE);
344     } else {
345         return (FALSE);
346     }
347 }
348
349 /*-
350  *-----------------------------------------------------------------------
351  * Targ_Silent  --
352  *      Return true if be silent when creating gn
353  *
354  * Results:
355  *      TRUE if should be silent
356  *
357  * Side Effects:
358  *      None
359  *-----------------------------------------------------------------------
360  */
361 Boolean
362 Targ_Silent (gn)
363     GNode          *gn;         /* node to check for */
364 {
365     if (beSilent || gn->type & OP_SILENT) {
366         return (TRUE);
367     } else {
368         return (FALSE);
369     }
370 }
371
372 /*-
373  *-----------------------------------------------------------------------
374  * Targ_Precious --
375  *      See if the given target is precious
376  *
377  * Results:
378  *      TRUE if it is precious. FALSE otherwise
379  *
380  * Side Effects:
381  *      None
382  *-----------------------------------------------------------------------
383  */
384 Boolean
385 Targ_Precious (gn)
386     GNode          *gn;         /* the node to check */
387 {
388     if (allPrecious || (gn->type & (OP_PRECIOUS|OP_DOUBLEDEP))) {
389         return (TRUE);
390     } else {
391         return (FALSE);
392     }
393 }
394
395 /******************* DEBUG INFO PRINTING ****************/
396
397 static GNode      *mainTarg;    /* the main target, as set by Targ_SetMain */
398 /*-
399  *-----------------------------------------------------------------------
400  * Targ_SetMain --
401  *      Set our idea of the main target we'll be creating. Used for
402  *      debugging output.
403  *
404  * Results:
405  *      None.
406  *
407  * Side Effects:
408  *      "mainTarg" is set to the main target's node.
409  *-----------------------------------------------------------------------
410  */
411 void
412 Targ_SetMain (gn)
413     GNode   *gn;        /* The main target we'll create */
414 {
415     mainTarg = gn;
416 }
417
418 static int
419 TargPrintName (gnp, ppath)
420     ClientData     gnp;
421     ClientData      ppath;
422 {
423     GNode *gn = (GNode *) gnp;
424     printf ("%s ", gn->name);
425 #ifdef notdef
426     if (ppath) {
427         if (gn->path) {
428             printf ("[%s]  ", gn->path);
429         }
430         if (gn == mainTarg) {
431             printf ("(MAIN NAME)  ");
432         }
433     }
434 #endif /* notdef */
435     return (ppath ? 0 : 0);
436 }
437
438
439 int
440 Targ_PrintCmd (cmd, dummy)
441     ClientData cmd;
442     ClientData dummy;
443 {
444     printf ("\t%s\n", (char *) cmd);
445     return (dummy ? 0 : 0);
446 }
447
448 /*-
449  *-----------------------------------------------------------------------
450  * Targ_FmtTime --
451  *      Format a modification time in some reasonable way and return it.
452  *
453  * Results:
454  *      The time reformatted.
455  *
456  * Side Effects:
457  *      The time is placed in a static area, so it is overwritten
458  *      with each call.
459  *
460  *-----------------------------------------------------------------------
461  */
462 char *
463 Targ_FmtTime (time)
464     time_t    time;
465 {
466     struct tm           *parts;
467     static char         buf[128];
468
469     parts = localtime(&time);
470
471     strftime(buf, sizeof buf, "%k:%M:%S %b %d, %Y", parts);
472     buf[sizeof(buf) - 1] = '\0';
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  */
488 void
489 Targ_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  */
533 static int
534 TargPrintNode (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  */
615 static int
616 TargPrintOnlySrc(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  */
639 void
640 Targ_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 }