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