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