386BSD 0.1 development
[unix-history] / usr / src / usr.bin / make / targ.c
CommitLineData
e72c71e5
WJ
1/*
2 * Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
3 * Copyright (c) 1988, 1989 by Adam de Boor
4 * Copyright (c) 1989 by Berkeley Softworks
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to Berkeley by
8 * Adam de Boor.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the University of
21 * California, Berkeley and its contributors.
22 * 4. Neither the name of the University nor the names of its contributors
23 * may be used to endorse or promote products derived from this software
24 * without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 * SUCH DAMAGE.
37 */
38
39#ifndef lint
40static char sccsid[] = "@(#)targ.c 5.9 (Berkeley) 3/1/91";
41#endif /* not lint */
42
43/*-
44 * targ.c --
45 * Functions for maintaining the Lst allTargets. Target nodes are
46 * kept in two structures: a Lst, maintained by the list library, and a
47 * hash table, maintained by the hash library.
48 *
49 * Interface:
50 * Targ_Init Initialization procedure.
51 *
52 * Targ_NewGN Create a new GNode for the passed target
53 * (string). The node is *not* placed in the
54 * hash table, though all its fields are
55 * initialized.
56 *
57 * Targ_FindNode Find the node for a given target, creating
58 * and storing it if it doesn't exist and the
59 * flags are right (TARG_CREATE)
60 *
61 * Targ_FindList Given a list of names, find nodes for all
62 * of them. If a name doesn't exist and the
63 * TARG_NOCREATE flag was given, an error message
64 * is printed. Else, if a name doesn't exist,
65 * its node is created.
66 *
67 * Targ_Ignore Return TRUE if errors should be ignored when
68 * creating the given target.
69 *
70 * Targ_Silent Return TRUE if we should be silent when
71 * creating the given target.
72 *
73 * Targ_Precious Return TRUE if the target is precious and
74 * should not be removed if we are interrupted.
75 *
76 * Debugging:
77 * Targ_PrintGraph Print out the entire graphm all variables
78 * and statistics for the directory cache. Should
79 * print something for suffixes, too, but...
80 */
81
82#include <stdio.h>
83#include <time.h>
84#include "make.h"
85#include "hash.h"
86
87static Lst allTargets; /* the list of all targets found so far */
88static Hash_Table targets; /* a hash table of same */
89
90#define HTSIZE 191 /* initial size of hash table */
91
92/*-
93 *-----------------------------------------------------------------------
94 * Targ_Init --
95 * Initialize this module
96 *
97 * Results:
98 * None
99 *
100 * Side Effects:
101 * The allTargets list and the targets hash table are initialized
102 *-----------------------------------------------------------------------
103 */
104void
105Targ_Init ()
106{
107 allTargets = Lst_Init (FALSE);
108 Hash_InitTable (&targets, HTSIZE);
109}
110
111/*-
112 *-----------------------------------------------------------------------
113 * Targ_NewGN --
114 * Create and initialize a new graph node
115 *
116 * Results:
117 * An initialized graph node with the name field filled with a copy
118 * of the passed name
119 *
120 * Side Effects:
121 * None.
122 *-----------------------------------------------------------------------
123 */
124GNode *
125Targ_NewGN (name)
126 char *name; /* the name to stick in the new node */
127{
128 register GNode *gn;
129
130 gn = (GNode *) emalloc (sizeof (GNode));
131 gn->name = strdup (name);
132 gn->path = (char *) 0;
133 if (name[0] == '-' && name[1] == 'l') {
134 gn->type = OP_LIB;
135 } else {
136 gn->type = 0;
137 }
138 gn->unmade = 0;
139 gn->make = FALSE;
140 gn->made = UNMADE;
141 gn->childMade = FALSE;
142 gn->mtime = gn->cmtime = 0;
143 gn->iParents = Lst_Init (FALSE);
144 gn->cohorts = Lst_Init (FALSE);
145 gn->parents = Lst_Init (FALSE);
146 gn->children = Lst_Init (FALSE);
147 gn->successors = Lst_Init(FALSE);
148 gn->preds = Lst_Init(FALSE);
149 gn->context = Lst_Init (FALSE);
150 gn->commands = Lst_Init (FALSE);
151
152 return (gn);
153}
154
155/*-
156 *-----------------------------------------------------------------------
157 * Targ_FindNode --
158 * Find a node in the list using the given name for matching
159 *
160 * Results:
161 * The node in the list if it was. If it wasn't, return NILGNODE of
162 * flags was TARG_NOCREATE or the newly created and initialized node
163 * if it was TARG_CREATE
164 *
165 * Side Effects:
166 * Sometimes a node is created and added to the list
167 *-----------------------------------------------------------------------
168 */
169GNode *
170Targ_FindNode (name, flags)
171 char *name; /* the name to find */
172 int flags; /* flags governing events when target not
173 * found */
174{
175 GNode *gn; /* node in that element */
176 Hash_Entry *he; /* New or used hash entry for node */
177 Boolean isNew; /* Set TRUE if Hash_CreateEntry had to create */
178 /* an entry for the node */
179
180
181 if (flags & TARG_CREATE) {
182 he = Hash_CreateEntry (&targets, name, &isNew);
183 if (isNew) {
184 gn = Targ_NewGN (name);
185 Hash_SetValue (he, gn);
186 (void) Lst_AtEnd (allTargets, (ClientData)gn);
187 }
188 } else {
189 he = Hash_FindEntry (&targets, name);
190 }
191
192 if (he == (Hash_Entry *) NULL) {
193 return (NILGNODE);
194 } else {
195 return ((GNode *) Hash_GetValue (he));
196 }
197}
198
199/*-
200 *-----------------------------------------------------------------------
201 * Targ_FindList --
202 * Make a complete list of GNodes from the given list of names
203 *
204 * Results:
205 * A complete list of graph nodes corresponding to all instances of all
206 * the names in names.
207 *
208 * Side Effects:
209 * If flags is TARG_CREATE, nodes will be created for all names in
210 * names which do not yet have graph nodes. If flags is TARG_NOCREATE,
211 * an error message will be printed for each name which can't be found.
212 * -----------------------------------------------------------------------
213 */
214Lst
215Targ_FindList (names, flags)
216 Lst names; /* list of names to find */
217 int flags; /* flags used if no node is found for a given
218 * name */
219{
220 Lst nodes; /* result list */
221 register LstNode ln; /* name list element */
222 register GNode *gn; /* node in tLn */
223 char *name;
224
225 nodes = Lst_Init (FALSE);
226
227 if (Lst_Open (names) == FAILURE) {
228 return (nodes);
229 }
230 while ((ln = Lst_Next (names)) != NILLNODE) {
231 name = (char *)Lst_Datum(ln);
232 gn = Targ_FindNode (name, flags);
233 if (gn != NILGNODE) {
234 /*
235 * Note: Lst_AtEnd must come before the Lst_Concat so the nodes
236 * are added to the list in the order in which they were
237 * encountered in the makefile.
238 */
239 (void) Lst_AtEnd (nodes, (ClientData)gn);
240 if (gn->type & OP_DOUBLEDEP) {
241 (void)Lst_Concat (nodes, gn->cohorts, LST_CONCNEW);
242 }
243 } else if (flags == TARG_NOCREATE) {
244 Error ("\"%s\" -- target unknown.", name);
245 }
246 }
247 Lst_Close (names);
248 return (nodes);
249}
250
251/*-
252 *-----------------------------------------------------------------------
253 * Targ_Ignore --
254 * Return true if should ignore errors when creating gn
255 *
256 * Results:
257 * TRUE if should ignore errors
258 *
259 * Side Effects:
260 * None
261 *-----------------------------------------------------------------------
262 */
263Boolean
264Targ_Ignore (gn)
265 GNode *gn; /* node to check for */
266{
267 if (ignoreErrors || gn->type & OP_IGNORE) {
268 return (TRUE);
269 } else {
270 return (FALSE);
271 }
272}
273
274/*-
275 *-----------------------------------------------------------------------
276 * Targ_Silent --
277 * Return true if be silent when creating gn
278 *
279 * Results:
280 * TRUE if should be silent
281 *
282 * Side Effects:
283 * None
284 *-----------------------------------------------------------------------
285 */
286Boolean
287Targ_Silent (gn)
288 GNode *gn; /* node to check for */
289{
290 if (beSilent || gn->type & OP_SILENT) {
291 return (TRUE);
292 } else {
293 return (FALSE);
294 }
295}
296
297/*-
298 *-----------------------------------------------------------------------
299 * Targ_Precious --
300 * See if the given target is precious
301 *
302 * Results:
303 * TRUE if it is precious. FALSE otherwise
304 *
305 * Side Effects:
306 * None
307 *-----------------------------------------------------------------------
308 */
309Boolean
310Targ_Precious (gn)
311 GNode *gn; /* the node to check */
312{
313 if (allPrecious || (gn->type & (OP_PRECIOUS|OP_DOUBLEDEP))) {
314 return (TRUE);
315 } else {
316 return (FALSE);
317 }
318}
319
320/******************* DEBUG INFO PRINTING ****************/
321
322static GNode *mainTarg; /* the main target, as set by Targ_SetMain */
323/*-
324 *-----------------------------------------------------------------------
325 * Targ_SetMain --
326 * Set our idea of the main target we'll be creating. Used for
327 * debugging output.
328 *
329 * Results:
330 * None.
331 *
332 * Side Effects:
333 * "mainTarg" is set to the main target's node.
334 *-----------------------------------------------------------------------
335 */
336void
337Targ_SetMain (gn)
338 GNode *gn; /* The main target we'll create */
339{
340 mainTarg = gn;
341}
342
343static int
344TargPrintName (gn, ppath)
345 GNode *gn;
346 int ppath;
347{
348 printf ("%s ", gn->name);
349#ifdef notdef
350 if (ppath) {
351 if (gn->path) {
352 printf ("[%s] ", gn->path);
353 }
354 if (gn == mainTarg) {
355 printf ("(MAIN NAME) ");
356 }
357 }
358#endif notdef
359 return (0);
360}
361
362
363int
364Targ_PrintCmd (cmd)
365 char *cmd;
366{
367 printf ("\t%s\n", cmd);
368 return (0);
369}
370
371/*-
372 *-----------------------------------------------------------------------
373 * Targ_FmtTime --
374 * Format a modification time in some reasonable way and return it.
375 *
376 * Results:
377 * The time reformatted.
378 *
379 * Side Effects:
380 * The time is placed in a static area, so it is overwritten
381 * with each call.
382 *
383 *-----------------------------------------------------------------------
384 */
385char *
386Targ_FmtTime (time)
387 time_t time;
388{
389 struct tm *parts;
390 static char buf[40];
391 static char *months[] = {
392 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
393 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
394 };
395
396 parts = localtime(&time);
397
398 sprintf (buf, "%d:%02d:%02d %s %d, 19%d",
399 parts->tm_hour, parts->tm_min, parts->tm_sec,
400 months[parts->tm_mon], parts->tm_mday, parts->tm_year);
401 return(buf);
402}
403
404/*-
405 *-----------------------------------------------------------------------
406 * Targ_PrintType --
407 * Print out a type field giving only those attributes the user can
408 * set.
409 *
410 * Results:
411 *
412 * Side Effects:
413 *
414 *-----------------------------------------------------------------------
415 */
416void
417Targ_PrintType (type)
418 register int type;
419{
420 register int tbit;
421
422#ifdef __STDC__
423#define PRINTBIT(attr) case CONCAT(OP_,attr): printf("." #attr " "); break
424#define PRINTDBIT(attr) case CONCAT(OP_,attr): if (DEBUG(TARG)) printf("." #attr " "); break
425#else
426#define PRINTBIT(attr) case CONCAT(OP_,attr): printf(".attr "); break
427#define PRINTDBIT(attr) case CONCAT(OP_,attr): if (DEBUG(TARG)) printf(".attr "); break
428#endif /* __STDC__ */
429
430 type &= ~OP_OPMASK;
431
432 while (type) {
433 tbit = 1 << (ffs(type) - 1);
434 type &= ~tbit;
435
436 switch(tbit) {
437 PRINTBIT(OPTIONAL);
438 PRINTBIT(USE);
439 PRINTBIT(EXEC);
440 PRINTBIT(IGNORE);
441 PRINTBIT(PRECIOUS);
442 PRINTBIT(SILENT);
443 PRINTBIT(MAKE);
444 PRINTBIT(JOIN);
445 PRINTBIT(INVISIBLE);
446 PRINTBIT(NOTMAIN);
447 PRINTDBIT(LIB);
448 /*XXX: MEMBER is defined, so CONCAT(OP_,MEMBER) gives OP_"%" */
449 case OP_MEMBER: if (DEBUG(TARG)) printf(".MEMBER "); break;
450 PRINTDBIT(ARCHV);
451 }
452 }
453}
454
455/*-
456 *-----------------------------------------------------------------------
457 * TargPrintNode --
458 * print the contents of a node
459 *-----------------------------------------------------------------------
460 */
461static int
462TargPrintNode (gn, pass)
463 GNode *gn;
464 int pass;
465{
466 if (!OP_NOP(gn->type)) {
467 printf("#\n");
468 if (gn == mainTarg) {
469 printf("# *** MAIN TARGET ***\n");
470 }
471 if (pass == 2) {
472 if (gn->unmade) {
473 printf("# %d unmade children\n", gn->unmade);
474 } else {
475 printf("# No unmade children\n");
476 }
477 if (! (gn->type & (OP_JOIN|OP_USE|OP_EXEC))) {
478 if (gn->mtime != 0) {
479 printf("# last modified %s: %s\n",
480 Targ_FmtTime(gn->mtime),
481 (gn->made == UNMADE ? "unmade" :
482 (gn->made == MADE ? "made" :
483 (gn->made == UPTODATE ? "up-to-date" :
484 "error when made"))));
485 } else if (gn->made != UNMADE) {
486 printf("# non-existent (maybe): %s\n",
487 (gn->made == MADE ? "made" :
488 (gn->made == UPTODATE ? "up-to-date" :
489 (gn->made == ERROR ? "error when made" :
490 "aborted"))));
491 } else {
492 printf("# unmade\n");
493 }
494 }
495 if (!Lst_IsEmpty (gn->iParents)) {
496 printf("# implicit parents: ");
497 Lst_ForEach (gn->iParents, TargPrintName, (ClientData)0);
498 putc ('\n', stdout);
499 }
500 }
501 if (!Lst_IsEmpty (gn->parents)) {
502 printf("# parents: ");
503 Lst_ForEach (gn->parents, TargPrintName, (ClientData)0);
504 putc ('\n', stdout);
505 }
506
507 printf("%-16s", gn->name);
508 switch (gn->type & OP_OPMASK) {
509 case OP_DEPENDS:
510 printf(": "); break;
511 case OP_FORCE:
512 printf("! "); break;
513 case OP_DOUBLEDEP:
514 printf(":: "); break;
515 }
516 Targ_PrintType (gn->type);
517 Lst_ForEach (gn->children, TargPrintName, (ClientData)0);
518 putc ('\n', stdout);
519 Lst_ForEach (gn->commands, Targ_PrintCmd, (ClientData)0);
520 printf("\n\n");
521 if (gn->type & OP_DOUBLEDEP) {
522 Lst_ForEach (gn->cohorts, TargPrintNode, (ClientData)pass);
523 }
524 }
525 return (0);
526}
527
528/*-
529 *-----------------------------------------------------------------------
530 * TargPrintOnlySrc --
531 * Print only those targets that are just a source.
532 *
533 * Results:
534 * 0.
535 *
536 * Side Effects:
537 * The name of each file is printed preceeded by #\t
538 *
539 *-----------------------------------------------------------------------
540 */
541static int
542TargPrintOnlySrc(gn)
543 GNode *gn;
544{
545 if (OP_NOP(gn->type)) {
546 printf("#\t%s [%s]\n", gn->name,
547 gn->path ? gn->path : gn->name);
548 }
549 return (0);
550}
551
552/*-
553 *-----------------------------------------------------------------------
554 * Targ_PrintGraph --
555 * print the entire graph. heh heh
556 *
557 * Results:
558 * none
559 *
560 * Side Effects:
561 * lots o' output
562 *-----------------------------------------------------------------------
563 */
564Targ_PrintGraph (pass)
565 int pass; /* Which pass this is. 1 => no processing
566 * 2 => processing done */
567{
568 printf("#*** Input graph:\n");
569 Lst_ForEach (allTargets, TargPrintNode, (ClientData)pass);
570 printf("\n\n");
571 printf("#\n# Files that are only sources:\n");
572 Lst_ForEach (allTargets, TargPrintOnlySrc);
573 printf("#*** Global Variables:\n");
574 Var_Dump (VAR_GLOBAL);
575 printf("#*** Command-line Variables:\n");
576 Var_Dump (VAR_CMD);
577 printf("\n");
578 Dir_PrintDirectories();
579 printf("\n");
580 Suff_PrintAll();
581}