1 #include <string.h>
2 #include <stdlib.h>
3 #include <stdio.h>
4 
5 #include <lua.h>
6 
7 #include "node.h"
8 #include "mem.h"
9 #include "support.h"
10 #include "path.h"
11 #include "context.h"
12 #include "session.h"
13 
14 #include "nodelinktree.inl"
15 
16 /* */
node_create_graph(struct HEAP * heap)17 struct GRAPH *node_create_graph(struct HEAP *heap)
18 {
19 	/* allocate graph structure */
20 	struct GRAPH *graph = (struct GRAPH*)mem_allocate(heap, sizeof(struct GRAPH));
21 	if(!graph)
22 		return (struct GRAPH *)0x0;
23 
24 	memset(graph, 0, sizeof(struct GRAPH));
25 
26 	/* init */
27 	graph->heap = heap;
28 	return graph;
29 }
30 
31 /* creates a node */
node_create(struct NODE ** nodeptr,struct GRAPH * graph,const char * filename,const char * label,const char * cmdline)32 int node_create(struct NODE **nodeptr, struct GRAPH *graph, const char *filename, const char *label, const char *cmdline)
33 {
34 	struct NODE *node;
35 	struct NODETREELINK *link;
36 	int sn;
37 	unsigned hashid = string_hash(filename);
38 
39 	/* check arguments */
40 	if(!path_isnice(filename))
41 	{
42 		printf("%s: error: adding non nice path '%s'. this causes problems with dependency lookups\n", session.name, filename);
43 		return NODECREATE_NOTNICE;
44 	}
45 
46 	if(cmdline && !label)
47 	{
48 		printf("%s: error: adding job '%s' with command but no label\n", session.name, filename);
49 		return NODECREATE_INVALID_ARG;
50 	}
51 	else if(!cmdline && label)
52 	{
53 		printf("%s: error: adding job '%s' with label but no command\n", session.name, filename);
54 		return NODECREATE_INVALID_ARG;
55 	}
56 
57 	/* zero out the return pointer */
58 	*nodeptr = (struct NODE *)0x0;
59 
60 	/* search for the node */
61 	link = nodelinktree_find_closest(graph->nodehash[hashid&0xffff], hashid);
62 	if(link && link->node->hashid == hashid)
63 	{
64 		/* we are allowed to create a new node from a node that doesn't
65 			have a job assigned to it*/
66 		if(link->node->cmdline || cmdline == NULL)
67 			return NODECREATE_EXISTS;
68 		node = link->node;
69 	}
70 	else
71 	{
72 		/* allocate and set pointers */
73 		node = (struct NODE *)mem_allocate(graph->heap, sizeof(struct NODE));
74 
75 		node->graph = graph;
76 		node->id = graph->num_nodes++;
77 		node->timestamp_raw = file_timestamp(filename);
78 		node->timestamp = node->timestamp_raw;
79 
80 		if(node->timestamp_raw == 0)
81 			node->dirty = NODEDIRTY_MISSING;
82 
83 		/* set filename */
84 		node->filename_len = strlen(filename)+1;
85 		node->filename = (char *)mem_allocate(graph->heap, node->filename_len);
86 		memcpy(node->filename, filename, node->filename_len);
87 		node->hashid = string_hash(filename);
88 
89 		/* add to hashed tree */
90 		nodelinktree_insert(&graph->nodehash[node->hashid&0xffff], link, node);
91 
92 		/* add to list */
93 		if(graph->last) graph->last->next = node;
94 		else graph->first = node;
95 		graph->last = node;
96 	}
97 
98 	/* set command and label */
99 	if(cmdline)
100 	{
101 		sn = strlen(cmdline)+1;
102 		node->cmdline = (char *)mem_allocate(graph->heap, sn);
103 		memcpy(node->cmdline, cmdline, sn);
104 		node->cmdhash = string_hash(cmdline);
105 		node->cachehash = node->cmdhash;
106 
107 		/* set label */
108 		sn = strlen(label)+1;
109 		node->label = (char *)mem_allocate(graph->heap, sn);
110 		memcpy(node->label, label, sn);
111 	}
112 
113 	/* return new node */
114 	*nodeptr = node;
115 	return NODECREATE_OK;
116 }
117 
118 /* finds a node based apun the filename */
node_find_byhash(struct GRAPH * graph,unsigned int hashid)119 struct NODE *node_find_byhash(struct GRAPH *graph, unsigned int hashid)
120 {
121 	struct NODETREELINK *link;
122 	link = nodelinktree_find_closest(graph->nodehash[hashid&0xffff], hashid);
123 	if(link && link->node->hashid == hashid)
124 		return link->node;
125 	return NULL;
126 }
127 
node_find(struct GRAPH * graph,const char * filename)128 struct NODE *node_find(struct GRAPH *graph, const char *filename)
129 {
130 	return node_find_byhash(graph, string_hash(filename));
131 }
132 
133 /* this will return the existing node or create a new one */
node_get(struct GRAPH * graph,const char * filename)134 struct NODE *node_get(struct GRAPH *graph, const char *filename)
135 {
136 	struct NODE *node = node_find(graph, filename);
137 
138 	if(!node)
139 	{
140 		if(node_create(&node, graph, filename, 0, 0) == NODECREATE_OK)
141 			return node;
142 	}
143 	return node;
144 }
145 
node_add_dependency_withnode(struct NODE * node,struct NODE * depnode)146 struct NODE *node_add_dependency_withnode(struct NODE *node, struct NODE *depnode)
147 {
148 	struct NODELINK *dep;
149 	struct NODELINK *parent;
150 	struct NODETREELINK *treelink;
151 
152 	/* make sure that the node doesn't try to depend on it self */
153 	if(depnode == node)
154 	{
155 		if(node->cmdline)
156 		{
157 			printf("error: node '%s' is depended on itself and is produced by a job\n", node->filename);
158 			return (struct NODE*)0x0;
159 		}
160 
161 		return node;
162 	}
163 
164 	/* check if we are already dependent on this node */
165 	treelink = nodelinktree_find_closest(node->deproot, depnode->hashid);
166 	if(treelink != NULL && treelink->node->hashid == depnode->hashid)
167 		return depnode;
168 
169 	/* create and add dependency link */
170 	dep = (struct NODELINK *)mem_allocate(node->graph->heap, sizeof(struct NODELINK));
171 	dep->node = depnode;
172 	dep->next = node->firstdep;
173 	node->firstdep = dep;
174 
175 	nodelinktree_insert(&node->deproot, treelink, depnode);
176 
177 	/* create and add parent link */
178 	parent = (struct NODELINK *)mem_allocate(node->graph->heap, sizeof(struct NODELINK));
179 	parent->node = node;
180 	parent->next = depnode->firstparent;
181 	depnode->firstparent = parent;
182 
183 	/* increase dep counter */
184 	node->graph->num_deps++;
185 
186 	/* return the dependency */
187 	return depnode;
188 }
189 
190 
node_add_job_dependency_withnode(struct NODE * node,struct NODE * depnode)191 struct NODE *node_add_job_dependency_withnode(struct NODE *node, struct NODE *depnode)
192 {
193 	struct NODELINK *dep;
194 	struct NODETREELINK *treelink;
195 
196 	/* make sure that the node doesn't try to depend on it self */
197 	if(depnode == node)
198 	{
199 		if(node->cmdline)
200 		{
201 			printf("error: node '%s' is depended on itself and is produced by a job\n", node->filename);
202 			return (struct NODE*)0x0;
203 		}
204 
205 		return node;
206 	}
207 
208 	/* check if we are already dependent on this node */
209 	treelink = nodelinktree_find_closest(node->jobdeproot, depnode->hashid);
210 	if(treelink != NULL && treelink->node->hashid == depnode->hashid)
211 		return depnode;
212 
213 	/* create and add job dependency link */
214 	dep = (struct NODELINK *)mem_allocate(node->graph->heap, sizeof(struct NODELINK));
215 	dep->node = depnode;
216 	dep->next = node->firstjobdep;
217 	node->firstjobdep = dep;
218 
219 	nodelinktree_insert(&node->jobdeproot, treelink, depnode);
220 
221 	return depnode;
222 }
223 
224 
225 /* adds a dependency to a node */
node_add_dependency(struct NODE * node,const char * filename)226 struct NODE *node_add_dependency(struct NODE *node, const char *filename)
227 {
228 	struct NODE *depnode = node_get(node->graph, filename);
229 	if(!depnode)
230 		return NULL;
231 	return node_add_dependency_withnode(node, depnode);
232 }
233 
node_add_constraint(struct NODELINK ** first,struct NODE * node,const char * filename)234 static struct NODE *node_add_constraint(struct NODELINK **first, struct NODE *node, const char *filename)
235 {
236 	struct NODE *contraint = node_get(node->graph, filename);
237 	struct NODELINK *link = (struct NODELINK *)mem_allocate(node->graph->heap, sizeof(struct NODELINK));
238 	link->node = contraint;
239 	link->next = *first;
240 	*first = link;
241 	return contraint;
242 }
243 
node_add_constraint_shared(struct NODE * node,const char * filename)244 struct NODE *node_add_constraint_shared(struct NODE *node, const char *filename)
245 {
246 	return node_add_constraint(&node->constraint_shared, node, filename);
247 }
248 
node_add_constraint_exclusive(struct NODE * node,const char * filename)249 struct NODE *node_add_constraint_exclusive(struct NODE *node, const char *filename)
250 {
251 	return node_add_constraint(&node->constraint_exclusive, node, filename);
252 }
253 
node_cached(struct NODE * node)254 void node_cached(struct NODE *node)
255 {
256 	node->cached = 1;
257 }
258 
node_set_pseudo(struct NODE * node)259 void node_set_pseudo(struct NODE *node)
260 {
261 	node->timestamp = 1;
262 	node->timestamp_raw = 1;
263 }
264 
265 /* functions to handle with bit array access */
bitarray_allocate(int size)266 static unsigned char *bitarray_allocate(int size)
267 { return (unsigned char *)malloc((size+7)/8); }
268 
bitarray_zeroall(unsigned char * a,int size)269 static void bitarray_zeroall(unsigned char *a, int size)
270 { memset(a, 0, (size+7)/8); }
271 
bitarray_free(unsigned char * a)272 static void bitarray_free(unsigned char *a)
273 { free(a); }
274 
bitarray_value(unsigned char * a,int id)275 static int bitarray_value(unsigned char *a, int id)
276 { return a[id>>3]&(1<<(id&0x7)); }
277 
bitarray_set(unsigned char * a,int id)278 static void bitarray_set(unsigned char *a, int id)
279 { a[id>>3] |= (1<<(id&0x7)); }
280 
bitarray_clear(unsigned char * a,int id)281 static void bitarray_clear(unsigned char *a, int id)
282 { a[id>>3] &= ~(1<<(id&0x7)); }
283 
284 /* ************* */
node_walk_r(struct NODEWALK * walk,struct NODE * node)285 static int node_walk_r(
286 	struct NODEWALK *walk,
287 	struct NODE *node)
288 {
289 	/* we should detect changes here before we run */
290 	struct NODELINK *dep;
291 	struct NODEWALKPATH path;
292 	int result = 0;
293 	int flags = walk->flags;
294 
295 	/* check and set mark */
296 	if(bitarray_value(walk->mark, node->id))
297 		return 0;
298 	bitarray_set(walk->mark, node->id);
299 
300 	if(flags&NODEWALK_UNDONE)
301 	{
302 		if(node->workstatus != NODESTATUS_UNDONE)
303 			return 0;
304 	}
305 
306 	if(flags&NODEWALK_TOPDOWN)
307 	{
308 		walk->node = node;
309 		result = walk->callback(walk);
310 	}
311 
312 	/* push parent */
313 	path.node = node;
314 	path.parent = walk->parent;
315 	walk->parent = &path;
316 	walk->depth++;
317 
318 	/* build all dependencies */
319 	dep = node->firstdep;
320 	if(flags&NODEWALK_JOBS)
321 		dep = node->firstjobdep;
322 	for(; dep; dep = dep->next)
323 	{
324 		result = node_walk_r(walk, dep->node);
325 		if(result)
326 			break;
327 	}
328 
329 	/* pop parent */
330 	walk->depth--;
331 	walk->parent = walk->parent->parent;
332 
333 	/* unmark the node so we can walk this tree again if needed */
334 	if(!(flags&NODEWALK_QUICK))
335 		bitarray_clear(walk->mark, node->id);
336 
337 	/* return if we have an error */
338 	if(result)
339 		return result;
340 
341 	/* check if we need to rebuild this node */
342 	if(!(flags&NODEWALK_FORCE) && !node->dirty)
343 		return 0;
344 
345 	/* build */
346 	if(flags&NODEWALK_BOTTOMUP)
347 	{
348 		walk->node = node;
349 		result = walk->callback(walk);
350 	}
351 
352 	return result;
353 }
354 
355 /* walks through all the active nodes that needs a recheck */
node_walk_do_revisits(struct NODEWALK * walk)356 static int node_walk_do_revisits(struct NODEWALK *walk)
357 {
358 	int result;
359 	struct NODE *node;
360 
361 	/* no parent or depth info is available */
362 	walk->parent = NULL;
363 	walk->depth = 0;
364 	walk->revisiting = 1;
365 
366 	while(walk->firstrevisit)
367 	{
368 		/* pop from the list */
369 		node = walk->firstrevisit->node;
370 		walk->firstrevisit->node = NULL;
371 		walk->firstrevisit = walk->firstrevisit->next;
372 
373 		/* issue the call */
374 		walk->node = node;
375 		result = walk->callback(walk);
376 		if(result)
377 			return result;
378 	}
379 
380 	/* return success */
381 	return 0;
382 }
383 
node_walk(struct NODE * node,int flags,int (* callback)(struct NODEWALK *),void * u)384 int node_walk(
385 	struct NODE *node,
386 	int flags,
387 	int (*callback)(struct NODEWALK*),
388 	void *u)
389 {
390 	struct NODEWALK walk;
391 	int result;
392 
393 	/* set walk parameters */
394 	walk.depth = 0;
395 	walk.flags = flags;
396 	walk.callback = callback;
397 	walk.user = u;
398 	walk.parent = 0;
399 	walk.revisiting = 0;
400 	walk.firstrevisit = NULL;
401 	walk.revisits = NULL;
402 
403 	/* allocate and clear mark and sweep array */
404 	walk.mark = bitarray_allocate(node->graph->num_nodes);
405 	bitarray_zeroall(walk.mark, node->graph->num_nodes);
406 
407 	/* allocate memory for activation */
408 	if(flags&NODEWALK_REVISIT)
409 	{
410 		walk.revisits = malloc(sizeof(struct NODEWALKREVISIT)*node->graph->num_nodes);
411 		memset(walk.revisits, 0, sizeof(struct NODEWALKREVISIT)*node->graph->num_nodes);
412 	}
413 
414 	/* do the walk */
415 	result = node_walk_r(&walk, node);
416 
417 	/* do the walk of all active elements, if we don't have an error */
418 	if(flags&NODEWALK_REVISIT && !result)
419 	{
420 		node_walk_do_revisits(&walk);
421 		free(walk.revisits);
422 	}
423 
424 	/* free the array and return */
425 	bitarray_free(walk.mark);
426 	return result;
427 }
428 
node_walk_revisit(struct NODEWALK * walk,struct NODE * node)429 void node_walk_revisit(struct NODEWALK *walk, struct NODE *node)
430 {
431 	struct NODEWALKREVISIT *revisit = &walk->revisits[node->id];
432 
433 	/* check if node already marked for revisit */
434 	if(revisit->node)
435 		return;
436 
437 	/* no need to revisit the node if there is a visit to be done for it */
438 	/* TODO: the necessarily of this check is unknown. should check some larger builds to see
439 			if it helps any substantial amount. */
440 	if(!walk->revisiting && !bitarray_value(walk->mark, node->id))
441 		return;
442 
443 	/* insert the node to the nodes to revisit */
444 	revisit->node = node;
445 	revisit->next = walk->firstrevisit;
446 	walk->firstrevisit = revisit;
447 }
448 
node_debug_dump(struct GRAPH * graph)449 void node_debug_dump(struct GRAPH *graph)
450 {
451 	struct NODE *node = graph->first;
452 	struct NODELINK *link;
453 
454 	for(;node;node = node->next)
455 	{
456 		printf("%s\n", node->filename);
457 		for(link = node->firstdep; link; link = link->next)
458 			printf("   DEPEND %s\n", link->node->filename);
459 	}
460 }
461 
462 /* dumps all nodes to the stdout */
node_debug_dump_detailed(struct GRAPH * graph)463 void node_debug_dump_detailed(struct GRAPH *graph)
464 {
465 	struct NODE *node = graph->first;
466 	struct NODELINK *link;
467 	const char *tool;
468 
469 	for(;node;node = node->next)
470 	{
471 		static const char *dirtyflag[] = {"--", "MI", "CH", "DD", "DN", "GS"};
472 		tool = "***";
473 		if(node->cmdline)
474 			tool = node->cmdline;
475 		printf("%08x %s   %s   %-15s\n", (unsigned)node->timestamp, dirtyflag[node->dirty], node->filename, tool);
476 		for(link = node->firstdep; link; link = link->next)
477 			printf("%08x %s      DEPEND %s\n", (unsigned)link->node->timestamp, dirtyflag[link->node->dirty], link->node->filename);
478 		for(link = node->firstparent; link; link = link->next)
479 			printf("%08x %s      PARENT %s\n", (unsigned)link->node->timestamp, dirtyflag[link->node->dirty], link->node->filename);
480 		for(link = node->constraint_shared; link; link = link->next)
481 			printf("%08x %s      SHARED %s\n", (unsigned)link->node->timestamp, dirtyflag[link->node->dirty], link->node->filename);
482 		for(link = node->constraint_exclusive; link; link = link->next)
483 			printf("%08x %s      EXCLUS %s\n", (unsigned)link->node->timestamp, dirtyflag[link->node->dirty], link->node->filename);
484 		for(link = node->firstjobdep; link; link = link->next)
485 			printf("%08x %s      JOBDEP %s\n", (unsigned)link->node->timestamp, dirtyflag[link->node->dirty], link->node->filename);
486 	}
487 }
488 
489 
node_debug_dump_dot_callback(struct NODEWALK * walkinfo)490 static int node_debug_dump_dot_callback(struct NODEWALK *walkinfo)
491 {
492 	struct NODE *node = walkinfo->node;
493 	struct NODELINK *link;
494 
495 	/* skip top node, always the build target */
496 	if(node == walkinfo->user)
497 		return 0;
498 
499 	printf("node%d [label=\"%s\"];\n", node->id, node->filename);
500 	for(link = node->firstdep; link; link = link->next)
501 		printf("node%d -> node%d;\n", link->node->id, node->id);
502 	return 0;
503 }
504 
505 /* dumps all nodes to the stdout */
node_debug_dump_dot(struct GRAPH * graph,struct NODE * top)506 void node_debug_dump_dot(struct GRAPH *graph, struct NODE *top)
507 {
508 	printf("digraph {\n");
509 	printf("graph [rankdir=\"LR\"];\n");
510 	printf("node [shape=box, height=0.25, color=gray, fontsize=8];\n");
511 	node_walk(top, NODEWALK_FORCE|NODEWALK_TOPDOWN|NODEWALK_QUICK, node_debug_dump_dot_callback, top);
512 	printf("}\n");
513 }
514 
node_debug_dump_jobs_dot_callback(struct NODEWALK * walkinfo)515 static int node_debug_dump_jobs_dot_callback(struct NODEWALK *walkinfo)
516 {
517 	struct NODE *node = walkinfo->node;
518 	struct NODELINK *link;
519 
520 	/* skip top node, always the build target */
521 	if(node == walkinfo->user)
522 		return 0;
523 
524 	printf("node%d [shape=box, label=\"%s\"];\n", node->id, node->filename);
525 	for(link = node->firstjobdep; link; link = link->next)
526 		printf("node%d -> node%d;\n", link->node->id, node->id);
527 	return 0;
528 }
529 
node_debug_dump_jobs_dot(struct GRAPH * graph,struct NODE * top)530 void node_debug_dump_jobs_dot(struct GRAPH *graph, struct NODE *top)
531 {
532 	printf("digraph {\n");
533 	printf("graph [rankdir=\"LR\"];\n");
534 	printf("node [shape=box, height=0.25, color=gray, fontsize=8];\n");
535 	node_walk(top, NODEWALK_FORCE|NODEWALK_TOPDOWN|NODEWALK_JOBS|NODEWALK_QUICK, node_debug_dump_jobs_dot_callback, top);
536 	printf("}\n");
537 }
538 
node_debug_dump_jobs(struct GRAPH * graph)539 void node_debug_dump_jobs(struct GRAPH *graph)
540 {
541 	struct NODELINK *link;
542 	struct NODE *node = graph->first;
543 	static const char *dirtyflag[] = {"--", "MI", "CH", "DD", "DN", "GS"};
544 	printf("MI = Missing CH = Command hash dirty, DD = Dependency dirty\n");
545 	printf("DN = Dependency is newer, GS = Global stamp is newer\n");
546 	printf("Dirty Depth %-30s   Command\n", "Filename");
547 	for(;node;node = node->next)
548 	{
549 		if(node->cmdline)
550 		{
551 			printf(" %s    %3d  %-30s   %s\n", dirtyflag[node->dirty], node->depth, node->filename, node->cmdline);
552 
553 			for(link = node->firstjobdep; link; link = link->next)
554 				printf(" %s         + %-30s\n", dirtyflag[link->node->dirty], link->node->filename);
555 		}
556 	}
557 }
558