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