Get rid of the sequential access feature of the lists. This was
[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.17 2004/12/17 07:56:08 okumoto 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(void *, void *);
97 static int TargPrintName(void *, void *);
98 static int TargPrintNode(void *, void *);
99 static void TargFreeGN(void *);
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(void)
115 {
116
117     allTargets = Lst_Init();
118     Hash_InitTable(&targets, HTSIZE);
119 }
120
121 /*-
122  *-----------------------------------------------------------------------
123  * Targ_End --
124  *      Finalize this module
125  *
126  * Results:
127  *      None
128  *
129  * Side Effects:
130  *      All lists and gnodes are cleared
131  *-----------------------------------------------------------------------
132  */
133 void
134 Targ_End(void)
135 {
136
137     Lst_Destroy(allTargets, NOFREE);
138     if (allGNs)
139         Lst_Destroy(allGNs, TargFreeGN);
140     Hash_DeleteTable(&targets);
141 }
142
143 /*-
144  *-----------------------------------------------------------------------
145  * Targ_NewGN  --
146  *      Create and initialize a new graph node
147  *
148  * Results:
149  *      An initialized graph node with the name field filled with a copy
150  *      of the passed name
151  *
152  * Side Effects:
153  *      The gnode is added to the list of all gnodes.
154  *-----------------------------------------------------------------------
155  */
156 GNode *
157 Targ_NewGN(char *name)
158 {
159     GNode *gn;
160
161     gn = emalloc(sizeof(GNode));
162     gn->name = estrdup(name);
163     gn->path = NULL;
164     if (name[0] == '-' && name[1] == 'l') {
165         gn->type = OP_LIB;
166     } else {
167         gn->type = 0;
168     }
169     gn->unmade = 0;
170     gn->make = FALSE;
171     gn->made = UNMADE;
172     gn->childMade = FALSE;
173     gn->order = 0;
174     gn->mtime = gn->cmtime = 0;
175     gn->iParents = Lst_Init();
176     gn->cohorts = Lst_Init();
177     gn->parents = Lst_Init();
178     gn->children = Lst_Init();
179     gn->successors = Lst_Init();
180     gn->preds = Lst_Init();
181     gn->context = Lst_Init();
182     gn->commands = Lst_Init();
183     gn->suffix = NULL;
184
185     if (allGNs == NULL)
186         allGNs = Lst_Init();
187     Lst_AtEnd(allGNs, (void *)gn);
188
189     return (gn);
190 }
191
192 /*-
193  *-----------------------------------------------------------------------
194  * TargFreeGN  --
195  *      Destroy a GNode
196  *
197  * Results:
198  *      None.
199  *
200  * Side Effects:
201  *      None.
202  *-----------------------------------------------------------------------
203  */
204 static void
205 TargFreeGN(void *gnp)
206 {
207     GNode *gn = gnp;
208
209     free(gn->name);
210     free(gn->path);
211
212     Lst_Destroy(gn->iParents, NOFREE);
213     Lst_Destroy(gn->cohorts, NOFREE);
214     Lst_Destroy(gn->parents, NOFREE);
215     Lst_Destroy(gn->children, NOFREE);
216     Lst_Destroy(gn->successors, NOFREE);
217     Lst_Destroy(gn->preds, NOFREE);
218     Lst_Destroy(gn->context, NOFREE);
219     Lst_Destroy(gn->commands, NOFREE);
220     free(gn);
221 }
222
223 /*-
224  *-----------------------------------------------------------------------
225  * Targ_FindNode  --
226  *      Find a node in the list using the given name for matching
227  *
228  * Results:
229  *      The node in the list if it was. If it wasn't, return NULL of
230  *      flags was TARG_NOCREATE or the newly created and initialized node
231  *      if it was TARG_CREATE
232  *
233  * Side Effects:
234  *      Sometimes a node is created and added to the list
235  *-----------------------------------------------------------------------
236  */
237 GNode *
238 Targ_FindNode(char *name, int flags)
239 {
240     GNode         *gn;        /* node in that element */
241     Hash_Entry    *he;        /* New or used hash entry for node */
242     Boolean       isNew;      /* Set TRUE if Hash_CreateEntry had to create */
243                               /* an entry for the node */
244
245     if (flags & TARG_CREATE) {
246         he = Hash_CreateEntry(&targets, name, &isNew);
247         if (isNew) {
248             gn = Targ_NewGN(name);
249             Hash_SetValue (he, gn);
250             Lst_AtEnd(allTargets, gn);
251         }
252     } else {
253         he = Hash_FindEntry(&targets, name);
254     }
255
256     if (he == NULL) {
257         return (NULL);
258     } else {
259         return (Hash_GetValue(he));
260     }
261 }
262
263 /*-
264  *-----------------------------------------------------------------------
265  * Targ_FindList --
266  *      Make a complete list of GNodes from the given list of names
267  *
268  * Results:
269  *      A complete list of graph nodes corresponding to all instances of all
270  *      the names in names.
271  *
272  * Side Effects:
273  *      If flags is TARG_CREATE, nodes will be created for all names in
274  *      names which do not yet have graph nodes. If flags is TARG_NOCREATE,
275  *      an error message will be printed for each name which can't be found.
276  * -----------------------------------------------------------------------
277  */
278 Lst *
279 Targ_FindList(Lst *names, int flags)
280 {
281     Lst            *nodes;      /* result list */
282     LstNode        *ln;         /* name list element */
283     GNode          *gn;         /* node in tLn */
284     char           *name;
285
286     nodes = Lst_Init();
287
288     for (ln = Lst_First(names); ln != NULL; ln = Lst_Succ(ln)) {
289         name = Lst_Datum(ln);
290         gn = Targ_FindNode(name, flags);
291         if (gn != NULL) {
292             /*
293              * Note: Lst_AtEnd must come before the Lst_Concat so the nodes
294              * are added to the list in the order in which they were
295              * encountered in the makefile.
296              */
297             Lst_AtEnd(nodes, gn);
298             if (gn->type & OP_DOUBLEDEP) {
299                 Lst_Concat(nodes, gn->cohorts, LST_CONCNEW);
300             }
301         } else if (flags == TARG_NOCREATE) {
302             Error("\"%s\" -- target unknown.", name);
303         }
304     }
305     return (nodes);
306 }
307
308 /*-
309  *-----------------------------------------------------------------------
310  * Targ_Ignore  --
311  *      Return true if should ignore errors when creating gn
312  *
313  * Results:
314  *      TRUE if should ignore errors
315  *
316  * Side Effects:
317  *      None
318  *-----------------------------------------------------------------------
319  */
320 Boolean
321 Targ_Ignore(GNode *gn)
322 {
323
324     if (ignoreErrors || (gn->type & OP_IGNORE)) {
325         return (TRUE);
326     } else {
327         return (FALSE);
328     }
329 }
330
331 /*-
332  *-----------------------------------------------------------------------
333  * Targ_Silent  --
334  *      Return true if be silent when creating gn
335  *
336  * Results:
337  *      TRUE if should be silent
338  *
339  * Side Effects:
340  *      None
341  *-----------------------------------------------------------------------
342  */
343 Boolean
344 Targ_Silent(GNode *gn)
345 {
346
347     if (beSilent || (gn->type & OP_SILENT)) {
348         return (TRUE);
349     } else {
350         return (FALSE);
351     }
352 }
353
354 /*-
355  *-----------------------------------------------------------------------
356  * Targ_Precious --
357  *      See if the given target is precious
358  *
359  * Results:
360  *      TRUE if it is precious. FALSE otherwise
361  *
362  * Side Effects:
363  *      None
364  *-----------------------------------------------------------------------
365  */
366 Boolean
367 Targ_Precious(GNode *gn)
368 {
369
370     if (allPrecious || (gn->type & (OP_PRECIOUS | OP_DOUBLEDEP))) {
371         return (TRUE);
372     } else {
373         return (FALSE);
374     }
375 }
376
377 /******************* DEBUG INFO PRINTING ****************/
378
379 static GNode      *mainTarg;    /* the main target, as set by Targ_SetMain */
380
381 /*-
382  *-----------------------------------------------------------------------
383  * Targ_SetMain --
384  *      Set our idea of the main target we'll be creating. Used for
385  *      debugging output.
386  *
387  * Results:
388  *      None.
389  *
390  * Side Effects:
391  *      "mainTarg" is set to the main target's node.
392  *-----------------------------------------------------------------------
393  */
394 void
395 Targ_SetMain(GNode *gn)
396 {
397
398     mainTarg = gn;
399 }
400
401 static int
402 TargPrintName(void *gnp, void *ppath)
403 {
404     GNode *gn = (GNode *) gnp;
405
406     printf("%s ", gn->name);
407 #ifdef notdef
408     if (ppath) {
409         if (gn->path) {
410             printf("[%s]  ", gn->path);
411         }
412         if (gn == mainTarg) {
413             printf("(MAIN NAME)  ");
414         }
415     }
416 #endif /* notdef */
417     return (ppath ? 0 : 0);
418 }
419
420
421 int
422 Targ_PrintCmd(void *cmd, void *dummy __unused)
423 {
424
425     printf("\t%s\n", (char *)cmd);
426     return (0);
427 }
428
429 /*-
430  *-----------------------------------------------------------------------
431  * Targ_FmtTime --
432  *      Format a modification time in some reasonable way and return it.
433  *
434  * Results:
435  *      The time reformatted.
436  *
437  * Side Effects:
438  *      The time is placed in a static area, so it is overwritten
439  *      with each call.
440  *
441  *-----------------------------------------------------------------------
442  */
443 char *
444 Targ_FmtTime(time_t modtime)
445 {
446     struct tm           *parts;
447     static char         buf[128];
448
449     parts = localtime(&modtime);
450
451     strftime(buf, sizeof buf, "%H:%M:%S %b %d, %Y", parts);
452     buf[sizeof(buf) - 1] = '\0';
453     return (buf);
454 }
455
456 /*-
457  *-----------------------------------------------------------------------
458  * Targ_PrintType --
459  *      Print out a type field giving only those attributes the user can
460  *      set.
461  *
462  * Results:
463  *
464  * Side Effects:
465  *
466  *-----------------------------------------------------------------------
467  */
468 void
469 Targ_PrintType(int type)
470 {
471     int    tbit;
472
473 #define PRINTBIT(attr)  case CONCAT(OP_,attr): printf("." #attr " "); break
474 #define PRINTDBIT(attr) case CONCAT(OP_,attr): DEBUGF(TARG, ("." #attr " ")); break
475
476     type &= ~OP_OPMASK;
477
478     while (type) {
479         tbit = 1 << (ffs(type) - 1);
480         type &= ~tbit;
481
482         switch(tbit) {
483             PRINTBIT(OPTIONAL);
484             PRINTBIT(USE);
485             PRINTBIT(EXEC);
486             PRINTBIT(IGNORE);
487             PRINTBIT(PRECIOUS);
488             PRINTBIT(SILENT);
489             PRINTBIT(MAKE);
490             PRINTBIT(JOIN);
491             PRINTBIT(INVISIBLE);
492             PRINTBIT(NOTMAIN);
493             PRINTDBIT(LIB);
494             /*XXX: MEMBER is defined, so CONCAT(OP_,MEMBER) gives OP_"%" */
495             case OP_MEMBER: DEBUGF(TARG, (".MEMBER ")); break;
496             PRINTDBIT(ARCHV);
497         }
498     }
499 }
500
501 /*-
502  *-----------------------------------------------------------------------
503  * TargPrintNode --
504  *      print the contents of a node
505  *-----------------------------------------------------------------------
506  */
507 static int
508 TargPrintNode(void *gnp, void *passp)
509 {
510     GNode         *gn = gnp;
511     int           pass = *(int *)passp;
512
513     if (!OP_NOP(gn->type)) {
514         printf("#\n");
515         if (gn == mainTarg) {
516             printf("# *** MAIN TARGET ***\n");
517         }
518         if (pass == 2) {
519             if (gn->unmade) {
520                 printf("# %d unmade children\n", gn->unmade);
521             } else {
522                 printf("# No unmade children\n");
523             }
524             if (!(gn->type & (OP_JOIN | OP_USE | OP_EXEC))) {
525                 if (gn->mtime != 0) {
526                     printf("# last modified %s: %s\n",
527                               Targ_FmtTime(gn->mtime),
528                               (gn->made == UNMADE ? "unmade" :
529                                (gn->made == MADE ? "made" :
530                                 (gn->made == UPTODATE ? "up-to-date" :
531                                  "error when made"))));
532                 } else if (gn->made != UNMADE) {
533                     printf("# non-existent (maybe): %s\n",
534                               (gn->made == MADE ? "made" :
535                                (gn->made == UPTODATE ? "up-to-date" :
536                                 (gn->made == ERROR ? "error when made" :
537                                  "aborted"))));
538                 } else {
539                     printf("# unmade\n");
540                 }
541             }
542             if (!Lst_IsEmpty (gn->iParents)) {
543                 printf("# implicit parents: ");
544                 Lst_ForEach(gn->iParents, TargPrintName, (void *)NULL);
545                 fputc('\n', stdout);
546             }
547         }
548         if (!Lst_IsEmpty (gn->parents)) {
549             printf("# parents: ");
550             Lst_ForEach(gn->parents, TargPrintName, (void *)NULL);
551             fputc('\n', stdout);
552         }
553
554         printf("%-16s", gn->name);
555         switch (gn->type & OP_OPMASK) {
556             case OP_DEPENDS:
557                 printf(": "); break;
558             case OP_FORCE:
559                 printf("! "); break;
560             case OP_DOUBLEDEP:
561                 printf(":: "); break;
562             default:
563                 break;
564         }
565         Targ_PrintType(gn->type);
566         Lst_ForEach(gn->children, TargPrintName, (void *)NULL);
567         fputc('\n', stdout);
568         Lst_ForEach(gn->commands, Targ_PrintCmd, (void *)NULL);
569         printf("\n\n");
570         if (gn->type & OP_DOUBLEDEP) {
571             Lst_ForEach(gn->cohorts, TargPrintNode, &pass);
572         }
573     }
574     return (0);
575 }
576
577 /*-
578  *-----------------------------------------------------------------------
579  * TargPrintOnlySrc --
580  *      Print only those targets that are just a source.
581  *
582  * Results:
583  *      0.
584  *
585  * Side Effects:
586  *      The name of each file is printed preceded by #\t
587  *
588  *-----------------------------------------------------------------------
589  */
590 static int
591 TargPrintOnlySrc(void *gnp, void *dummy __unused)
592 {
593     GNode         *gn = gnp;
594
595     if (OP_NOP(gn->type))
596         printf("#\t%s [%s]\n", gn->name, gn->path ? gn->path : gn->name);
597
598     return (0);
599 }
600
601 /*-
602  *-----------------------------------------------------------------------
603  * Targ_PrintGraph --
604  *      Print the entire graph.
605  *
606  * Results:
607  *      none
608  *
609  * Side Effects:
610  *      lots o' output
611  *-----------------------------------------------------------------------
612  */
613 void
614 Targ_PrintGraph(int pass)
615 {
616
617     printf("#*** Input graph:\n");
618     Lst_ForEach(allTargets, TargPrintNode, &pass);
619     printf("\n\n");
620     printf("#\n#   Files that are only sources:\n");
621     Lst_ForEach(allTargets, TargPrintOnlySrc, (void *)NULL);
622     printf("#*** Global Variables:\n");
623     Var_Dump(VAR_GLOBAL);
624     printf("#*** Command-line Variables:\n");
625     Var_Dump(VAR_CMD);
626     printf("\n");
627     Dir_PrintDirectories();
628     printf("\n");
629     Suff_PrintAll();
630 }