xref: /openbsd-src/usr.bin/make/targ.c (revision d13be5d47e4149db2549a9828e244d59dbc43f15)
1 /*	$OpenBSD: targ.c,v 1.62 2010/07/19 19:46:44 espie Exp $ */
2 /*	$NetBSD: targ.c,v 1.11 1997/02/20 16:51:50 christos Exp $	*/
3 
4 /*
5  * Copyright (c) 1999 Marc Espie.
6  *
7  * Extensive code changes for the OpenBSD project.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE OPENBSD PROJECT AND CONTRIBUTORS
19  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21  * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OPENBSD
22  * PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30 /*
31  * Copyright (c) 1988, 1989, 1990, 1993
32  *	The Regents of the University of California.  All rights reserved.
33  * Copyright (c) 1989 by Berkeley Softworks
34  * All rights reserved.
35  *
36  * This code is derived from software contributed to Berkeley by
37  * Adam de Boor.
38  *
39  * Redistribution and use in source and binary forms, with or without
40  * modification, are permitted provided that the following conditions
41  * are met:
42  * 1. Redistributions of source code must retain the above copyright
43  *    notice, this list of conditions and the following disclaimer.
44  * 2. Redistributions in binary form must reproduce the above copyright
45  *    notice, this list of conditions and the following disclaimer in the
46  *    documentation and/or other materials provided with the distribution.
47  * 3. Neither the name of the University nor the names of its contributors
48  *    may be used to endorse or promote products derived from this software
49  *    without specific prior written permission.
50  *
51  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
52  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
53  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
54  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
55  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
56  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
57  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
58  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
59  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
60  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
61  * SUCH DAMAGE.
62  */
63 
64 /*-
65  * targ.c --
66  *		Target nodes are kept into a hash table.
67  *
68  * Interface:
69  *	Targ_Init		Initialization procedure.
70  *
71  *	Targ_End		Cleanup the module
72  *
73  *	Targ_NewGN		Create a new GNode for the passed target
74  *				(string). The node is *not* placed in the
75  *				hash table, though all its fields are
76  *				initialized.
77  *
78  *	Targ_FindNode		Find the node for a given target, creating
79  *				and storing it if it doesn't exist and the
80  *				flags are right (TARG_CREATE)
81  *
82  *	Targ_FindList		Given a list of names, find nodes for all
83  *				of them, creating nodes if needed.
84  *
85  *	Targ_Ignore		Return true if errors should be ignored when
86  *				creating the given target.
87  *
88  *	Targ_Silent		Return true if we should be silent when
89  *				creating the given target.
90  *
91  *	Targ_Precious		Return true if the target is precious and
92  *				should not be removed if we are interrupted.
93  *
94  * Debugging:
95  *	Targ_PrintGraph 	Print out the entire graphm all variables
96  *				and statistics for the directory cache. Should
97  *				print something for suffixes, too, but...
98  */
99 
100 #include <limits.h>
101 #include <stddef.h>
102 #include <stdio.h>
103 #include <stdint.h>
104 #include <string.h>
105 #include "config.h"
106 #include "defines.h"
107 #include "ohash.h"
108 #include "stats.h"
109 #include "suff.h"
110 #include "var.h"
111 #include "targ.h"
112 #include "memory.h"
113 #include "gnode.h"
114 #include "extern.h"
115 #include "timestamp.h"
116 #include "lst.h"
117 #include "node_int.h"
118 #include "nodehashconsts.h"
119 #ifdef CLEANUP
120 #include <stdlib.h>
121 #endif
122 
123 static struct ohash targets;	/* hash table of targets */
124 struct ohash_info gnode_info = {
125 	offsetof(GNode, name), NULL, hash_alloc, hash_free, element_alloc
126 };
127 
128 static void TargPrintOnlySrc(GNode *);
129 static void TargPrintName(void *);
130 static void TargPrintNode(GNode *, int);
131 #ifdef CLEANUP
132 static LIST allTargets;
133 static void TargFreeGN(void *);
134 #endif
135 #define Targ_FindConstantNode(n, f) Targ_FindNodeh(n, sizeof(n), K_##n, f)
136 
137 
138 GNode *begin_node, *end_node, *interrupt_node, *DEFAULT;
139 
140 void
141 Targ_Init(void)
142 {
143 	/* A small make file already creates 200 targets.  */
144 	ohash_init(&targets, 10, &gnode_info);
145 #ifdef CLEANUP
146 	Lst_Init(&allTargets);
147 #endif
148 	begin_node = Targ_FindConstantNode(NODE_BEGIN, TARG_CREATE);
149 	begin_node->type |= OP_DUMMY | OP_NOTMAIN | OP_NODEFAULT;
150 	end_node = Targ_FindConstantNode(NODE_END, TARG_CREATE);
151 	end_node->type |= OP_DUMMY | OP_NOTMAIN | OP_NODEFAULT;
152 	interrupt_node = Targ_FindConstantNode(NODE_INTERRUPT, TARG_CREATE);
153 	interrupt_node->type |= OP_DUMMY | OP_NOTMAIN | OP_NODEFAULT;
154 	DEFAULT = Targ_FindConstantNode(NODE_DEFAULT, TARG_CREATE);
155 	DEFAULT->type |= OP_DUMMY | OP_NOTMAIN| OP_TRANSFORM | OP_NODEFAULT;
156 
157 }
158 
159 #ifdef CLEANUP
160 void
161 Targ_End(void)
162 {
163 	Lst_Every(&allTargets, TargFreeGN);
164 	ohash_delete(&targets);
165 }
166 #endif
167 
168 GNode *
169 Targ_NewGNi(const char *name, const char *ename)
170 {
171 	GNode *gn;
172 
173 	gn = ohash_create_entry(&gnode_info, name, &ename);
174 	gn->path = NULL;
175 	if (name[0] == '-' && name[1] == 'l')
176 		gn->type = OP_LIB;
177 	else
178 		gn->type = 0;
179 
180 	gn->special = SPECIAL_NONE;
181 	gn->unmade = 0;
182 	gn->must_make = false;
183 	gn->built_status = UNKNOWN;
184 	gn->childMade =	false;
185 	gn->order = 0;
186 	ts_set_out_of_date(gn->mtime);
187 	ts_set_out_of_date(gn->cmtime);
188 	Lst_Init(&gn->cohorts);
189 	Lst_Init(&gn->parents);
190 	Lst_Init(&gn->children);
191 	Lst_Init(&gn->successors);
192 	Lst_Init(&gn->preds);
193 	SymTable_Init(&gn->context);
194 	gn->lineno = 0;
195 	gn->fname = NULL;
196 	gn->impliedsrc = NULL;
197 	Lst_Init(&gn->commands);
198 	Lst_Init(&gn->expanded);
199 	gn->suffix = NULL;
200 	gn->next = NULL;
201 	gn->basename = NULL;
202 	gn->sibling = gn;
203 	gn->build_lock = false;
204 
205 #ifdef STATS_GN_CREATION
206 	STAT_GN_COUNT++;
207 #endif
208 
209 #ifdef CLEANUP
210 	Lst_AtEnd(&allTargets, gn);
211 #endif
212 	return gn;
213 }
214 
215 #ifdef CLEANUP
216 static void
217 TargFreeGN(void *gnp)
218 {
219 	GNode *gn = (GNode *)gnp;
220 
221 	efree(gn->path);
222 	Lst_Destroy(&gn->cohorts, NOFREE);
223 	Lst_Destroy(&gn->parents, NOFREE);
224 	Lst_Destroy(&gn->children, NOFREE);
225 	Lst_Destroy(&gn->successors, NOFREE);
226 	Lst_Destroy(&gn->preds, NOFREE);
227 	Lst_Destroy(&gn->commands, NOFREE);
228 	SymTable_Destroy(&gn->context);
229 	free(gn);
230 }
231 #endif
232 
233 GNode *
234 Targ_FindNodei(const char *name, const char *ename, int flags)
235 {
236 	uint32_t hv;
237 
238 	hv = ohash_interval(name, &ename);
239 	return Targ_FindNodeih(name, ename, hv, flags);
240 }
241 
242 GNode *
243 Targ_FindNodeih(const char *name, const char *ename, uint32_t hv, int flags)
244 {
245 	GNode *gn;
246 	unsigned int slot;
247 
248 	slot = ohash_lookup_interval(&targets, name, ename, hv);
249 
250 	gn = ohash_find(&targets, slot);
251 
252 	if (gn == NULL && (flags & TARG_CREATE)) {
253 		gn = Targ_NewGNi(name, ename);
254 		ohash_insert(&targets, slot, gn);
255 	}
256 
257 	return gn;
258 }
259 
260 void
261 Targ_FindList(Lst nodes, Lst names)
262 {
263 	LstNode ln;
264 	GNode *gn;
265 	char *name;
266 
267 	for (ln = Lst_First(names); ln != NULL; ln = Lst_Adv(ln)) {
268 		name = (char *)Lst_Datum(ln);
269 		gn = Targ_FindNode(name, TARG_CREATE);
270 		/* Note: Lst_AtEnd must come before the Lst_Concat so the nodes
271 		 * are added to the list in the order in which they were
272 		 * encountered in the makefile.  */
273 		Lst_AtEnd(nodes, gn);
274 		if (gn->type & OP_DOUBLEDEP)
275 			Lst_Concat(nodes, &gn->cohorts);
276 	}
277 }
278 
279 bool
280 Targ_Ignore(GNode *gn)
281 {
282 	if (ignoreErrors || gn->type & OP_IGNORE)
283 		return true;
284 	else
285 		return false;
286 }
287 
288 bool
289 Targ_Silent(GNode *gn)
290 {
291 	if (beSilent || gn->type & OP_SILENT)
292 		return true;
293 	else
294 		return false;
295 }
296 
297 bool
298 Targ_Precious(GNode *gn)
299 {
300 	if (allPrecious || (gn->type & (OP_PRECIOUS|OP_DOUBLEDEP|OP_PHONY)))
301 		return true;
302 	else
303 		return false;
304 }
305 
306 static void
307 TargPrintName(void *gnp)
308 {
309 	GNode *gn = (GNode *)gnp;
310 	printf("%s ", gn->name);
311 }
312 
313 
314 void
315 Targ_PrintCmd(void *cmd)
316 {
317 	printf("\t%s\n", (char *)cmd);
318 }
319 
320 void
321 Targ_PrintType(int type)
322 {
323 	int    tbit;
324 
325 #define PRINTBIT(attr)	case CONCAT(OP_,attr): printf("." #attr " "); break
326 #define PRINTDBIT(attr) case CONCAT(OP_,attr): if (DEBUG(TARG)) printf("." #attr " "); break
327 
328 	type &= ~OP_OPMASK;
329 
330 	while (type) {
331 		tbit = 1 << (ffs(type) - 1);
332 		type &= ~tbit;
333 
334 		switch (tbit) {
335 		PRINTBIT(OPTIONAL);
336 		PRINTBIT(USE);
337 		PRINTBIT(EXEC);
338 		PRINTBIT(IGNORE);
339 		PRINTBIT(PRECIOUS);
340 		PRINTBIT(SILENT);
341 		PRINTBIT(MAKE);
342 		PRINTBIT(JOIN);
343 		PRINTBIT(INVISIBLE);
344 		PRINTBIT(NOTMAIN);
345 		PRINTDBIT(LIB);
346 		/*XXX: MEMBER is defined, so CONCAT(OP_,MEMBER) gives OP_"%" */
347 		case OP_MEMBER:
348 			if (DEBUG(TARG))
349 				printf(".MEMBER ");
350 			break;
351 		PRINTDBIT(ARCHV);
352 		}
353     }
354 }
355 const char *
356 status_to_string(GNode *gn)
357 {
358 	switch (gn->built_status) {
359 	case UNKNOWN:
360 		return "unknown";
361 	case MADE:
362 		return "made";
363 	case UPTODATE:
364 		return "up-to-date";
365 	case ERROR:
366 		return "error when made";
367 	case ABORTED:
368 		return "aborted";
369 	default:
370 		return "other status";
371 	}
372 }
373 
374 static void
375 TargPrintNode(GNode *gn, int pass)
376 {
377 	if (OP_NOP(gn->type))
378 		return;
379 	printf("#\n");
380 	if (pass == 2) {
381 		printf("# %d unmade children\n", gn->unmade);
382 		if (! (gn->type & (OP_JOIN|OP_USE|OP_EXEC))) {
383 			if (!is_out_of_date(gn->mtime)) {
384 				printf("# last modified %s: %s\n",
385 				      time_to_string(gn->mtime),
386 				      status_to_string(gn));
387 			} else if (gn->built_status != UNKNOWN) {
388 				printf("# non-existent (maybe): %s\n",
389 				    status_to_string(gn));
390 			} else {
391 				printf("# unmade\n");
392 			}
393 		}
394 	}
395 	if (!Lst_IsEmpty(&gn->parents)) {
396 		printf("# parents: ");
397 		Lst_Every(&gn->parents, TargPrintName);
398 		fputc('\n', stdout);
399 	}
400 	if (gn->impliedsrc)
401 		printf("# implied source: %s\n", gn->impliedsrc->name);
402 
403 	printf("%-16s", gn->name);
404 	switch (gn->type & OP_OPMASK) {
405 	case OP_DEPENDS:
406 		printf(": "); break;
407 	case OP_FORCE:
408 		printf("! "); break;
409 	case OP_DOUBLEDEP:
410 		printf(":: "); break;
411 	}
412 	Targ_PrintType(gn->type);
413 	Lst_Every(&gn->children, TargPrintName);
414 	fputc('\n', stdout);
415 	Lst_Every(&gn->commands, Targ_PrintCmd);
416 	printf("\n\n");
417 	if (gn->type & OP_DOUBLEDEP) {
418 		LstNode ln;
419 
420 		for (ln = Lst_First(&gn->cohorts); ln != NULL; ln = Lst_Adv(ln))
421 			TargPrintNode((GNode *)Lst_Datum(ln), pass);
422 	}
423 }
424 
425 static void
426 TargPrintOnlySrc(GNode *gn)
427 {
428 	if (OP_NOP(gn->type) && gn->special == SPECIAL_NONE &&
429 	    !(gn->type & OP_DUMMY))
430 		printf("#\t%s [%s]\n", gn->name,
431 		    gn->path != NULL ? gn->path : gn->name);
432 }
433 
434 void
435 Targ_PrintGraph(int pass)	/* Which pass this is. 1 => no processing
436 				 * 2 => processing done */
437 {
438 	GNode		*gn;
439 	unsigned int	i;
440 
441 	printf("#*** Input graph:\n");
442 	for (gn = ohash_first(&targets, &i); gn != NULL;
443 	    gn = ohash_next(&targets, &i))
444 		TargPrintNode(gn, pass);
445 	printf("\n\n");
446 	printf("#\n#   Files that are only sources:\n");
447 	for (gn = ohash_first(&targets, &i); gn != NULL;
448 	    gn = ohash_next(&targets, &i))
449 		TargPrintOnlySrc(gn);
450 	Var_Dump();
451 	printf("\n");
452 #ifdef DEBUG_DIRECTORY_CACHE
453 	Dir_PrintDirectories();
454 	printf("\n");
455 #endif
456 	Suff_PrintAll();
457 }
458 
459 struct ohash *
460 targets_hash()
461 {
462 	return &targets;
463 }
464 
465 GNode *
466 Targ_FindNodeh(const char *name, size_t n, uint32_t hv, int flags)
467 {
468 	return Targ_FindNodeih(name, name + n - 1, hv, flags);
469 }
470