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