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