9942f2ea00857c6acb9ac3fbe88477433d0d3aeb
[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  * @(#)targ.c   8.2 (Berkeley) 3/19/94
39  * $FreeBSD: src/usr.bin/make/targ.c,v 1.10 1999/09/11 13:08:02 hoek Exp $
40  * $DragonFly: src/usr.bin/make/targ.c,v 1.2 2003/06/17 04:29:29 dillon Exp $
41  */
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
90 static Lst        allTargets;   /* the list of all targets found so far */
91 static Lst        allGNs;       /* List of all the GNodes */
92 static Hash_Table targets;      /* a hash table of same */
93
94 #define HTSIZE  191             /* initial size of hash table */
95
96 static int TargPrintOnlySrc __P((ClientData, ClientData));
97 static int TargPrintName __P((ClientData, ClientData));
98 static int TargPrintNode __P((ClientData, ClientData));
99 static 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  */
113 void
114 Targ_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  */
132 void
133 Targ_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  */
154 GNode *
155 Targ_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 = estrdup (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->order =         0;
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     efree(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  */
239 GNode *
240 Targ_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  */
284 Lst
285 Targ_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  */
333 Boolean
334 Targ_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  */
356 Boolean
357 Targ_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  */
379 Boolean
380 Targ_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
392 static 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  */
406 void
407 Targ_SetMain (gn)
408     GNode   *gn;        /* The main target we'll create */
409 {
410     mainTarg = gn;
411 }
412
413 static int
414 TargPrintName (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
434 int
435 Targ_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  */
457 char *
458 Targ_FmtTime (time)
459     time_t    time;
460 {
461     struct tm           *parts;
462     static char         buf[128];
463
464     parts = localtime(&time);
465
466     strftime(buf, sizeof buf, "%k:%M:%S %b %d, %Y", parts);
467     buf[sizeof(buf) - 1] = '\0';
468     return(buf);
469 }
470
471 /*-
472  *-----------------------------------------------------------------------
473  * Targ_PrintType --
474  *      Print out a type field giving only those attributes the user can
475  *      set.
476  *
477  * Results:
478  *
479  * Side Effects:
480  *
481  *-----------------------------------------------------------------------
482  */
483 void
484 Targ_PrintType (type)
485     register int    type;
486 {
487     register int    tbit;
488
489 #ifdef __STDC__
490 #define PRINTBIT(attr)  case CONCAT(OP_,attr): printf("." #attr " "); break
491 #define PRINTDBIT(attr) case CONCAT(OP_,attr): if (DEBUG(TARG)) printf("." #attr " "); break
492 #else
493 #define PRINTBIT(attr)  case CONCAT(OP_,attr): printf(".attr "); break
494 #define PRINTDBIT(attr) case CONCAT(OP_,attr): if (DEBUG(TARG)) printf(".attr "); break
495 #endif /* __STDC__ */
496
497     type &= ~OP_OPMASK;
498
499     while (type) {
500         tbit = 1 << (ffs(type) - 1);
501         type &= ~tbit;
502
503         switch(tbit) {
504             PRINTBIT(OPTIONAL);
505             PRINTBIT(USE);
506             PRINTBIT(EXEC);
507             PRINTBIT(IGNORE);
508             PRINTBIT(PRECIOUS);
509             PRINTBIT(SILENT);
510             PRINTBIT(MAKE);
511             PRINTBIT(JOIN);
512             PRINTBIT(INVISIBLE);
513             PRINTBIT(NOTMAIN);
514             PRINTDBIT(LIB);
515             /*XXX: MEMBER is defined, so CONCAT(OP_,MEMBER) gives OP_"%" */
516             case OP_MEMBER: if (DEBUG(TARG)) printf(".MEMBER "); break;
517             PRINTDBIT(ARCHV);
518         }
519     }
520 }
521
522 /*-
523  *-----------------------------------------------------------------------
524  * TargPrintNode --
525  *      print the contents of a node
526  *-----------------------------------------------------------------------
527  */
528 static int
529 TargPrintNode (gnp, passp)
530     ClientData   gnp;
531     ClientData   passp;
532 {
533     GNode         *gn = (GNode *) gnp;
534     int           pass = *(int *) passp;
535     if (!OP_NOP(gn->type)) {
536         printf("#\n");
537         if (gn == mainTarg) {
538             printf("# *** MAIN TARGET ***\n");
539         }
540         if (pass == 2) {
541             if (gn->unmade) {
542                 printf("# %d unmade children\n", gn->unmade);
543             } else {
544                 printf("# No unmade children\n");
545             }
546             if (! (gn->type & (OP_JOIN|OP_USE|OP_EXEC))) {
547                 if (gn->mtime != 0) {
548                     printf("# last modified %s: %s\n",
549                               Targ_FmtTime(gn->mtime),
550                               (gn->made == UNMADE ? "unmade" :
551                                (gn->made == MADE ? "made" :
552                                 (gn->made == UPTODATE ? "up-to-date" :
553                                  "error when made"))));
554                 } else if (gn->made != UNMADE) {
555                     printf("# non-existent (maybe): %s\n",
556                               (gn->made == MADE ? "made" :
557                                (gn->made == UPTODATE ? "up-to-date" :
558                                 (gn->made == ERROR ? "error when made" :
559                                  "aborted"))));
560                 } else {
561                     printf("# unmade\n");
562                 }
563             }
564             if (!Lst_IsEmpty (gn->iParents)) {
565                 printf("# implicit parents: ");
566                 Lst_ForEach (gn->iParents, TargPrintName, (ClientData)0);
567                 fputc ('\n', stdout);
568             }
569         }
570         if (!Lst_IsEmpty (gn->parents)) {
571             printf("# parents: ");
572             Lst_ForEach (gn->parents, TargPrintName, (ClientData)0);
573             fputc ('\n', stdout);
574         }
575
576         printf("%-16s", gn->name);
577         switch (gn->type & OP_OPMASK) {
578             case OP_DEPENDS:
579                 printf(": "); break;
580             case OP_FORCE:
581                 printf("! "); break;
582             case OP_DOUBLEDEP:
583                 printf(":: "); break;
584         }
585         Targ_PrintType (gn->type);
586         Lst_ForEach (gn->children, TargPrintName, (ClientData)0);
587         fputc ('\n', stdout);
588         Lst_ForEach (gn->commands, Targ_PrintCmd, (ClientData)0);
589         printf("\n\n");
590         if (gn->type & OP_DOUBLEDEP) {
591             Lst_ForEach (gn->cohorts, TargPrintNode, (ClientData)&pass);
592         }
593     }
594     return (0);
595 }
596
597 /*-
598  *-----------------------------------------------------------------------
599  * TargPrintOnlySrc --
600  *      Print only those targets that are just a source.
601  *
602  * Results:
603  *      0.
604  *
605  * Side Effects:
606  *      The name of each file is printed preceeded by #\t
607  *
608  *-----------------------------------------------------------------------
609  */
610 static int
611 TargPrintOnlySrc(gnp, dummy)
612     ClientData    gnp;
613     ClientData    dummy;
614 {
615     GNode         *gn = (GNode *) gnp;
616     if (OP_NOP(gn->type))
617         printf("#\t%s [%s]\n", gn->name, gn->path ? gn->path : gn->name);
618
619     return (dummy ? 0 : 0);
620 }
621
622 /*-
623  *-----------------------------------------------------------------------
624  * Targ_PrintGraph --
625  *      print the entire graph. heh heh
626  *
627  * Results:
628  *      none
629  *
630  * Side Effects:
631  *      lots o' output
632  *-----------------------------------------------------------------------
633  */
634 void
635 Targ_PrintGraph (pass)
636     int     pass;       /* Which pass this is. 1 => no processing
637                          * 2 => processing done */
638 {
639     printf("#*** Input graph:\n");
640     Lst_ForEach (allTargets, TargPrintNode, (ClientData)&pass);
641     printf("\n\n");
642     printf("#\n#   Files that are only sources:\n");
643     Lst_ForEach (allTargets, TargPrintOnlySrc, (ClientData) 0);
644     printf("#*** Global Variables:\n");
645     Var_Dump (VAR_GLOBAL);
646     printf("#*** Command-line Variables:\n");
647     Var_Dump (VAR_CMD);
648     printf("\n");
649     Dir_PrintDirectories();
650     printf("\n");
651     Suff_PrintAll();
652 }