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