targ.c revision 1.26 1 /* $NetBSD: targ.c,v 1.26 2002/02/10 12:03:08 pk 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.26 2002/02/10 12:03:08 pk 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.26 2002/02/10 12:03:08 pk 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 gn->lineno = 0;
223 gn->fname = NULL;
224
225 #ifdef CLEANUP
226 if (allGNs == NULL)
227 allGNs = Lst_Init(FALSE);
228 Lst_AtEnd(allGNs, (ClientData) gn);
229 #endif
230
231 return (gn);
232 }
233
234 #ifdef CLEANUP
235 /*-
236 *-----------------------------------------------------------------------
237 * TargFreeGN --
238 * Destroy a GNode
239 *
240 * Results:
241 * None.
242 *
243 * Side Effects:
244 * None.
245 *-----------------------------------------------------------------------
246 */
247 static void
248 TargFreeGN (gnp)
249 ClientData gnp;
250 {
251 GNode *gn = (GNode *) gnp;
252
253
254 free(gn->name);
255 if (gn->uname)
256 free(gn->uname);
257 if (gn->path)
258 free(gn->path);
259 if (gn->fname)
260 free(gn->fname);
261
262 Lst_Destroy(gn->iParents, NOFREE);
263 Lst_Destroy(gn->cohorts, NOFREE);
264 Lst_Destroy(gn->parents, NOFREE);
265 Lst_Destroy(gn->children, NOFREE);
266 Lst_Destroy(gn->successors, NOFREE);
267 Lst_Destroy(gn->preds, NOFREE);
268 Hash_DeleteTable(&gn->context);
269 Lst_Destroy(gn->commands, NOFREE);
270 free((Address)gn);
271 }
272 #endif
273
274
275 /*-
276 *-----------------------------------------------------------------------
277 * Targ_FindNode --
278 * Find a node in the list using the given name for matching
279 *
280 * Results:
281 * The node in the list if it was. If it wasn't, return NILGNODE of
282 * flags was TARG_NOCREATE or the newly created and initialized node
283 * if it was TARG_CREATE
284 *
285 * Side Effects:
286 * Sometimes a node is created and added to the list
287 *-----------------------------------------------------------------------
288 */
289 GNode *
290 Targ_FindNode (name, flags)
291 char *name; /* the name to find */
292 int flags; /* flags governing events when target not
293 * found */
294 {
295 GNode *gn; /* node in that element */
296 Hash_Entry *he; /* New or used hash entry for node */
297 Boolean isNew; /* Set TRUE if Hash_CreateEntry had to create */
298 /* an entry for the node */
299
300
301 if (flags & TARG_CREATE) {
302 he = Hash_CreateEntry (&targets, name, &isNew);
303 if (isNew) {
304 gn = Targ_NewGN (name);
305 Hash_SetValue (he, gn);
306 Var_Append(".ALLTARGETS", name, VAR_GLOBAL);
307 (void) Lst_AtEnd (allTargets, (ClientData)gn);
308 }
309 } else {
310 he = Hash_FindEntry (&targets, name);
311 }
312
313 if (he == (Hash_Entry *) NULL) {
314 return (NILGNODE);
315 } else {
316 return ((GNode *) Hash_GetValue (he));
317 }
318 }
319
320 /*-
321 *-----------------------------------------------------------------------
322 * Targ_FindList --
323 * Make a complete list of GNodes from the given list of names
324 *
325 * Results:
326 * A complete list of graph nodes corresponding to all instances of all
327 * the names in names.
328 *
329 * Side Effects:
330 * If flags is TARG_CREATE, nodes will be created for all names in
331 * names which do not yet have graph nodes. If flags is TARG_NOCREATE,
332 * an error message will be printed for each name which can't be found.
333 * -----------------------------------------------------------------------
334 */
335 Lst
336 Targ_FindList (names, flags)
337 Lst names; /* list of names to find */
338 int flags; /* flags used if no node is found for a given
339 * name */
340 {
341 Lst nodes; /* result list */
342 register LstNode ln; /* name list element */
343 register GNode *gn; /* node in tLn */
344 char *name;
345
346 nodes = Lst_Init (FALSE);
347
348 if (Lst_Open (names) == FAILURE) {
349 return (nodes);
350 }
351 while ((ln = Lst_Next (names)) != NILLNODE) {
352 name = (char *)Lst_Datum(ln);
353 gn = Targ_FindNode (name, flags);
354 if (gn != NILGNODE) {
355 /*
356 * Note: Lst_AtEnd must come before the Lst_Concat so the nodes
357 * are added to the list in the order in which they were
358 * encountered in the makefile.
359 */
360 (void) Lst_AtEnd (nodes, (ClientData)gn);
361 } else if (flags == TARG_NOCREATE) {
362 Error ("\"%s\" -- target unknown.", name);
363 }
364 }
365 Lst_Close (names);
366 return (nodes);
367 }
368
369 /*-
370 *-----------------------------------------------------------------------
371 * Targ_Ignore --
372 * Return true if should ignore errors when creating gn
373 *
374 * Results:
375 * TRUE if should ignore errors
376 *
377 * Side Effects:
378 * None
379 *-----------------------------------------------------------------------
380 */
381 Boolean
382 Targ_Ignore (gn)
383 GNode *gn; /* node to check for */
384 {
385 if (ignoreErrors || gn->type & OP_IGNORE) {
386 return (TRUE);
387 } else {
388 return (FALSE);
389 }
390 }
391
392 /*-
393 *-----------------------------------------------------------------------
394 * Targ_Silent --
395 * Return true if be silent when creating gn
396 *
397 * Results:
398 * TRUE if should be silent
399 *
400 * Side Effects:
401 * None
402 *-----------------------------------------------------------------------
403 */
404 Boolean
405 Targ_Silent (gn)
406 GNode *gn; /* node to check for */
407 {
408 if (beSilent || gn->type & OP_SILENT) {
409 return (TRUE);
410 } else {
411 return (FALSE);
412 }
413 }
414
415 /*-
416 *-----------------------------------------------------------------------
417 * Targ_Precious --
418 * See if the given target is precious
419 *
420 * Results:
421 * TRUE if it is precious. FALSE otherwise
422 *
423 * Side Effects:
424 * None
425 *-----------------------------------------------------------------------
426 */
427 Boolean
428 Targ_Precious (gn)
429 GNode *gn; /* the node to check */
430 {
431 if (allPrecious || (gn->type & (OP_PRECIOUS|OP_DOUBLEDEP))) {
432 return (TRUE);
433 } else {
434 return (FALSE);
435 }
436 }
437
438 /******************* DEBUG INFO PRINTING ****************/
439
440 static GNode *mainTarg; /* the main target, as set by Targ_SetMain */
441 /*-
442 *-----------------------------------------------------------------------
443 * Targ_SetMain --
444 * Set our idea of the main target we'll be creating. Used for
445 * debugging output.
446 *
447 * Results:
448 * None.
449 *
450 * Side Effects:
451 * "mainTarg" is set to the main target's node.
452 *-----------------------------------------------------------------------
453 */
454 void
455 Targ_SetMain (gn)
456 GNode *gn; /* The main target we'll create */
457 {
458 mainTarg = gn;
459 }
460
461 static int
462 TargPrintName (gnp, ppath)
463 ClientData gnp;
464 ClientData ppath;
465 {
466 GNode *gn = (GNode *) gnp;
467 printf ("%s ", gn->name);
468 #ifdef notdef
469 if (ppath) {
470 if (gn->path) {
471 printf ("[%s] ", gn->path);
472 }
473 if (gn == mainTarg) {
474 printf ("(MAIN NAME) ");
475 }
476 }
477 #endif /* notdef */
478 return (ppath ? 0 : 0);
479 }
480
481
482 int
483 Targ_PrintCmd (cmd, dummy)
484 ClientData cmd;
485 ClientData dummy;
486 {
487 printf ("\t%s\n", (char *) cmd);
488 return (dummy ? 0 : 0);
489 }
490
491 /*-
492 *-----------------------------------------------------------------------
493 * Targ_FmtTime --
494 * Format a modification time in some reasonable way and return it.
495 *
496 * Results:
497 * The time reformatted.
498 *
499 * Side Effects:
500 * The time is placed in a static area, so it is overwritten
501 * with each call.
502 *
503 *-----------------------------------------------------------------------
504 */
505 char *
506 Targ_FmtTime (tm)
507 time_t tm;
508 {
509 struct tm *parts;
510 static char buf[128];
511
512 parts = localtime(&tm);
513 (void)strftime(buf, sizeof buf, "%k:%M:%S %b %d, %Y", parts);
514 return(buf);
515 }
516
517 /*-
518 *-----------------------------------------------------------------------
519 * Targ_PrintType --
520 * Print out a type field giving only those attributes the user can
521 * set.
522 *
523 * Results:
524 *
525 * Side Effects:
526 *
527 *-----------------------------------------------------------------------
528 */
529 void
530 Targ_PrintType (type)
531 register int type;
532 {
533 register int tbit;
534
535 #ifdef __STDC__
536 #define PRINTBIT(attr) case CONCAT(OP_,attr): printf("." #attr " "); break
537 #define PRINTDBIT(attr) case CONCAT(OP_,attr): if (DEBUG(TARG)) printf("." #attr " "); break
538 #else
539 #define PRINTBIT(attr) case CONCAT(OP_,attr): printf(".attr "); break
540 #define PRINTDBIT(attr) case CONCAT(OP_,attr): if (DEBUG(TARG)) printf(".attr "); break
541 #endif /* __STDC__ */
542
543 type &= ~OP_OPMASK;
544
545 while (type) {
546 tbit = 1 << (ffs(type) - 1);
547 type &= ~tbit;
548
549 switch(tbit) {
550 PRINTBIT(OPTIONAL);
551 PRINTBIT(USE);
552 PRINTBIT(EXEC);
553 PRINTBIT(IGNORE);
554 PRINTBIT(PRECIOUS);
555 PRINTBIT(SILENT);
556 PRINTBIT(MAKE);
557 PRINTBIT(JOIN);
558 PRINTBIT(INVISIBLE);
559 PRINTBIT(NOTMAIN);
560 PRINTDBIT(LIB);
561 /*XXX: MEMBER is defined, so CONCAT(OP_,MEMBER) gives OP_"%" */
562 case OP_MEMBER: if (DEBUG(TARG)) printf(".MEMBER "); break;
563 PRINTDBIT(ARCHV);
564 PRINTDBIT(MADE);
565 PRINTDBIT(PHONY);
566 }
567 }
568 }
569
570 /*-
571 *-----------------------------------------------------------------------
572 * TargPrintNode --
573 * print the contents of a node
574 *-----------------------------------------------------------------------
575 */
576 static int
577 TargPrintNode (gnp, passp)
578 ClientData gnp;
579 ClientData passp;
580 {
581 GNode *gn = (GNode *) gnp;
582 int pass = *(int *) passp;
583 if (!OP_NOP(gn->type)) {
584 printf("#\n");
585 if (gn == mainTarg) {
586 printf("# *** MAIN TARGET ***\n");
587 }
588 if (pass == 2) {
589 if (gn->unmade) {
590 printf("# %d unmade children\n", gn->unmade);
591 } else {
592 printf("# No unmade children\n");
593 }
594 if (! (gn->type & (OP_JOIN|OP_USE|OP_USEBEFORE|OP_EXEC))) {
595 if (gn->mtime != 0) {
596 printf("# last modified %s: %s\n",
597 Targ_FmtTime(gn->mtime),
598 (gn->made == UNMADE ? "unmade" :
599 (gn->made == MADE ? "made" :
600 (gn->made == UPTODATE ? "up-to-date" :
601 "error when made"))));
602 } else if (gn->made != UNMADE) {
603 printf("# non-existent (maybe): %s\n",
604 (gn->made == MADE ? "made" :
605 (gn->made == UPTODATE ? "up-to-date" :
606 (gn->made == ERROR ? "error when made" :
607 "aborted"))));
608 } else {
609 printf("# unmade\n");
610 }
611 }
612 if (!Lst_IsEmpty (gn->iParents)) {
613 printf("# implicit parents: ");
614 Lst_ForEach (gn->iParents, TargPrintName, (ClientData)0);
615 fputc ('\n', stdout);
616 }
617 }
618 if (!Lst_IsEmpty (gn->parents)) {
619 printf("# parents: ");
620 Lst_ForEach (gn->parents, TargPrintName, (ClientData)0);
621 fputc ('\n', stdout);
622 }
623
624 printf("%-16s", gn->name);
625 switch (gn->type & OP_OPMASK) {
626 case OP_DEPENDS:
627 printf(": "); break;
628 case OP_FORCE:
629 printf("! "); break;
630 case OP_DOUBLEDEP:
631 printf(":: "); break;
632 }
633 Targ_PrintType (gn->type);
634 Lst_ForEach (gn->children, TargPrintName, (ClientData)0);
635 fputc ('\n', stdout);
636 Lst_ForEach (gn->commands, Targ_PrintCmd, (ClientData)0);
637 printf("\n\n");
638 if (gn->type & OP_DOUBLEDEP) {
639 Lst_ForEach (gn->cohorts, TargPrintNode, (ClientData)&pass);
640 }
641 }
642 return (0);
643 }
644
645 /*-
646 *-----------------------------------------------------------------------
647 * TargPrintOnlySrc --
648 * Print only those targets that are just a source.
649 *
650 * Results:
651 * 0.
652 *
653 * Side Effects:
654 * The name of each file is printed preceded by #\t
655 *
656 *-----------------------------------------------------------------------
657 */
658 static int
659 TargPrintOnlySrc(gnp, dummy)
660 ClientData gnp;
661 ClientData dummy;
662 {
663 GNode *gn = (GNode *) gnp;
664 if (OP_NOP(gn->type))
665 printf("#\t%s [%s]\n", gn->name, gn->path ? gn->path : gn->name);
666
667 return (dummy ? 0 : 0);
668 }
669
670 /*-
671 *-----------------------------------------------------------------------
672 * Targ_PrintGraph --
673 * print the entire graph. heh heh
674 *
675 * Results:
676 * none
677 *
678 * Side Effects:
679 * lots o' output
680 *-----------------------------------------------------------------------
681 */
682 void
683 Targ_PrintGraph (pass)
684 int pass; /* Which pass this is. 1 => no processing
685 * 2 => processing done */
686 {
687 printf("#*** Input graph:\n");
688 Lst_ForEach (allTargets, TargPrintNode, (ClientData)&pass);
689 printf("\n\n");
690 printf("#\n# Files that are only sources:\n");
691 Lst_ForEach (allTargets, TargPrintOnlySrc, (ClientData) 0);
692 printf("#*** Global Variables:\n");
693 Var_Dump (VAR_GLOBAL);
694 printf("#*** Command-line Variables:\n");
695 Var_Dump (VAR_CMD);
696 printf("\n");
697 Dir_PrintDirectories();
698 printf("\n");
699 Suff_PrintAll();
700 }
701
702 static int
703 TargPropagateCohort (cgnp, pgnp)
704 ClientData cgnp;
705 ClientData pgnp;
706 {
707 GNode *cgn = (GNode *) cgnp;
708 GNode *pgn = (GNode *) pgnp;
709
710 cgn->type |= pgn->type & ~OP_OPMASK;
711 return (0);
712 }
713
714 static int
715 TargPropagateNode (gnp, junk)
716 ClientData gnp;
717 ClientData junk;
718 {
719 GNode *gn = (GNode *) gnp;
720 if (gn->type & OP_DOUBLEDEP)
721 Lst_ForEach (gn->cohorts, TargPropagateCohort, gnp);
722 return (0);
723 }
724
725 void
726 Targ_Propagate ()
727 {
728 Lst_ForEach (allTargets, TargPropagateNode, (ClientData)0);
729 }
730