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