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