1 /* some parts written at high altitudes */
2 /* some parts written at the top of the eiffel tower */
3
4 /* system includes */
5 #include <stdio.h>
6 #include <string.h>
7 #include <stdlib.h>
8 #include <time.h>
9
10 /* lua includes */
11 #define LUA_CORE /* make sure that we don't try to import these functions */
12 #include <lua.h>
13 #include <lualib.h> /* luaL_openlibs */
14 #include <lauxlib.h> /* luaL_loadfile */
15
16 /* program includes */
17 #include "mem.h"
18 #include "node.h"
19 #include "path.h"
20 #include "support.h"
21 #include "context.h"
22 #include "cache.h"
23 #include "luafuncs.h"
24 #include "platform.h"
25 #include "session.h"
26 #include "version.h"
27
28 /* internal base.bam file */
29 #include "internal_base.h"
30
31 /* needed for getcwd */
32 #if defined(BAM_FAMILY_UNIX) || defined(BAM_FAMILY_BEOS)
33 #include <unistd.h>
34 #endif
35
36 #ifdef BAM_FAMILY_WINDOWS
37 #include <direct.h>
38 #define getcwd _getcwd /* stupid msvc is calling getcwd non-ISO-C++ conformant */
39 #endif
40
41 #define DEFAULT_REPORT_STYLE "s"
42
43 /* ** */
44 #define L_FUNCTION_PREFIX "bam_"
45
46 struct OPTION
47 {
48 int print;
49 const char **s;
50 int *v;
51 const char *sw;
52 const char *desc;
53 };
54
55 /* options passed via the command line */
56 static int option_clean = 0;
57 static int option_no_cache = 0;
58 static int option_dry = 0;
59 static int option_dependent = 0;
60 static int option_abort_on_error = 0;
61 static int option_debug_nodes = 0;
62 static int option_debug_nodes_detailed = 0;
63 static int option_debug_jobs = 0;
64 static int option_debug_dot = 0;
65 static int option_debug_jobs_dot = 0;
66 static int option_debug_dumpinternal = 0;
67 static int option_debug_nointernal = 0;
68 static int option_debug_trace_vm = 0;
69
70 static int option_print_help = 0;
71
72 static const char *option_script = "bam.lua"; /* -f filename */
73 static const char *option_threads_str = "0";
74 static const char *option_report_str = DEFAULT_REPORT_STYLE;
75 static const char *option_targets[128] = {0};
76 static int option_num_targets = 0;
77 static const char *option_scriptargs[128] = {0};
78 static int option_num_scriptargs = 0;
79
80 /* filename of the cache, will be filled in at start up, ".bam/xxxxxxxx" = 14 top */
81 static char cache_filename[16] = {0};
82
83 /* session object */
84 struct SESSION session = {
85 "bam", /* exe */
86 "bam", /* name */
87 1, /* threads */
88 0 /* rest */
89 };
90
91 static struct OPTION options[] = {
92 /*@OPTION Targets ( name )
93 Specify a target to be built. A target can be any output
94 specified to the [AddJob] function.
95
96 If no targets are specified, the default target will be built
97 If there are no default target and there is only one target
98 specified with the [Target] function, it will be built.
99 Otherwise bam will report an error.
100
101 There is a special pseudo target named ^all^ that represents
102 all targets specified by the [Target] function.
103 @END*/
104
105 /*@OPTION Script Arguments ( name=value )
106 Sets a script argument. These arguments can be fetched form the build script
107 by accessing the ^ScriptArgs^ table.
108 @END*/
109
110 {1, 0, 0 , "\n Execution:", ""},
111
112 /*@OPTION Abort on error ( -a )
113 Setting this will cause bam to abort the build process when an error has occured.
114 Normally it would continue as far as it can.
115 @END*/
116 {1, 0,&option_abort_on_error , "-a", "abort on error"},
117
118 /*@OPTION Clean ( -c )
119 Cleans the specified targets or the default target.
120 @END*/
121 {1, 0, &option_clean , "-c", "clean targets"},
122
123 /*@OPTION Dependent build ( -d )
124 Builds all targets that are dependent on the given targets.
125 If no targets are given this option doesn't do anything.
126 @END*/
127 {1, 0, &option_dependent , "-d", "build targets that is dependent given targets"},
128
129 /*@OPTION Dry Run ( --dry )
130 Does everything that it normally would do but does not execute any
131 commands.
132 @END*/
133 {1, 0, &option_dry , "--dry", "dry run, don't run any jobs"},
134
135 /*@OPTION Threading ( -j N )
136 Sets the number of threads used when building. A good value for N is
137 the same number as logical cores on the machine. Set to 0 to disable.
138 @END*/
139 {1, &option_threads_str,0 , "-j", "sets the number of threads to use (default: 0, disabled)"},
140
141 /*@OPTION Script File ( -s FILENAME )
142 Bam file to use. In normal operation, Bam executes
143 ^bam.lua^. This option allows you to specify another bam
144 file.
145 @END*/
146 {1, &option_script,0 , "-s", "bam file to use (default: bam.lua)"},
147
148 {1, 0, 0 , "\n Lua:", ""},
149
150 /*@OPTION Script Locals ( -l )
151 Prints local and up values in the backtrace when there is a script error
152 @END*/
153 {1, 0, &session.lua_locals , "-l", "print local variables in backtrace"},
154
155 /*@OPTION Script Backtrace ( -t )
156 Prints backtrace when there is a script error
157 @END*/
158 {1, 0, &session.lua_backtrace , "-t", "print backtrace when an error occurs"},
159
160 {1, 0, 0 , "\n Output:", ""},
161
162 /*@OPTION Report Format ( -r [b][s][c] )
163 Sets the format of the progress report when building.
164 <ul>
165 <li>b</li> - Use a progress bar showing the percentage.
166 <li>s</li> - Show each step when building. (default)
167 <li>c</li> - Use ANSI colors.
168 </ul>
169 @END*/
170 {1, &option_report_str,0 , "-r", "build progress report format (default: " DEFAULT_REPORT_STYLE ")\n"
171 " " " b = progress bar\n"
172 " " " c = use ansi colors\n"
173 " " " s = build steps"},
174
175 /*@OPTION Verbose ( -v )
176 Prints all commands that are runned when building.
177 @END*/
178 {1, 0, &session.verbose , "-v", "be verbose"},
179
180 {1, 0, 0 , "\n Other:", ""},
181
182 /*@OPTION No cache ( -n )
183 Do not use cache when building.
184 @END*/
185 {1, 0, &option_no_cache , "-n", "don't use cache"},
186
187 /*@OPTION Help ( -h, --help )
188 Prints out a short reference of the command line options and quits
189 directly after.
190 @END*/
191 {1, 0, &option_print_help , "-h, --help", "prints this help"},
192 {0, 0, &option_print_help , "-h", "prints this help"},
193 {0, 0, &option_print_help , "--help", "prints this help"},
194
195 {1, 0, 0 , "\n Debug:", ""},
196
197 /*@OPTION Debug: Dump Nodes ( --debug-nodes )
198 Dumps all nodes in the dependency graph.
199 @END*/
200 {1, 0, &option_debug_nodes , "--debug-nodes", "prints all the nodes with dependencies"},
201
202 /*@OPTION Debug: Dump Nodes Detailed ( --debug-detail )
203 Dumps all nodes in the dependency graph, their state and their
204 dependent nodes. This is useful if you are writing your own
205 actions to verify that dependencies are correctly added.
206 @END*/
207 {1, 0, &option_debug_nodes_detailed , "--debug-detail", "prints all the nodes with dependencies and details"},
208
209 /*@OPTION Debug: Dump Jobs ( --debug-jobs )
210 @END*/
211 {1, 0, &option_debug_jobs , "--debug-jobs", "prints all the jobs that exist"},
212
213 /*@OPTION Debug: Dump Dot ( --debug-dot )
214 Dumps all nodes in the dependency graph into a dot file that can
215 be rendered with graphviz.
216 @END*/
217 {1, 0, &option_debug_dot , "--debug-dot", "prints all nodes as a graphviz dot file"},
218
219 /*@OPTION Debug: Dump Jobs Dot ( --debug-jobs-dot )
220 Dumps all jobs and their dependent jobs into a dot file that can
221 be rendered with graphviz.
222 @END*/
223 {1, 0, &option_debug_jobs_dot , "--debug-jobs-dot", "prints all jobs as a graphviz dot file"},
224
225 /*@OPTION Debug: Trace VM ( --debug-trace-vm )
226 Prints a the function and source line for every instruction that the vm makes.
227 @END*/
228 {1, 0, &option_debug_trace_vm , "--debug-trace-vm", "prints a line for every instruction the vm makes"},
229
230 /*@OPTION Debug: Dump Internal Scripts ( --debug-dump-int )
231 @END*/
232 {1, 0, &option_debug_dumpinternal , "--debug-dump-int", "prints the internals scripts to stdout"},
233
234 /*@OPTION Debug: No Internal ( --debug-no-int )
235 Disables all the internal scripts that bam loads on startup.
236 @1, END*/
237 {1, 0, &option_debug_nointernal , "--debug-no-int", "don't load internal scripts"},
238
239 /* terminate list */
240 {0, 0, 0, (const char*)0, (const char*)0}
241 };
242
internal_base_reader(lua_State * L,void * data,size_t * size)243 static const char *internal_base_reader(lua_State *L, void *data, size_t *size)
244 {
245 char **p = (char **)data;
246 if(!*p)
247 return 0;
248
249 *size = strlen(*p);
250 data = *p;
251 *p = 0;
252 return data;
253 }
254
255
lua_setglobalstring(lua_State * L,const char * field,const char * s)256 static void lua_setglobalstring(lua_State *L, const char *field, const char *s)
257 {
258 lua_pushstring(L, s);
259 lua_setglobal(L, field);
260 }
261
lua_vm_trace_hook(lua_State * L,lua_Debug * ar)262 static void lua_vm_trace_hook(lua_State *L, lua_Debug *ar)
263 {
264 lua_getinfo(L, "nSl", ar);
265 if(ar->name)
266 printf("%s %s %d\n", ar->name, ar->source, ar->currentline);
267 }
268
lua_alloctor_malloc(void * ud,void * ptr,size_t osize,size_t nsize)269 static void *lua_alloctor_malloc(void *ud, void *ptr, size_t osize, size_t nsize)
270 {
271 if (nsize == 0)
272 {
273 free(ptr);
274 return NULL;
275 }
276
277 return realloc(ptr, nsize);
278 }
279
280
281 /* *** */
register_lua_globals(struct CONTEXT * context)282 int register_lua_globals(struct CONTEXT *context)
283 {
284 int i, error = 0;
285
286 /* add standard libs */
287 luaL_openlibs(context->lua);
288
289 /* add specific functions */
290 lua_register(context->lua, L_FUNCTION_PREFIX"add_job", lf_add_job);
291 lua_register(context->lua, L_FUNCTION_PREFIX"add_output", lf_add_output);
292 lua_register(context->lua, L_FUNCTION_PREFIX"add_pseudo", lf_add_pseudo);
293 lua_register(context->lua, L_FUNCTION_PREFIX"add_dependency", lf_add_dependency);
294 lua_register(context->lua, L_FUNCTION_PREFIX"add_constraint_shared", lf_add_constraint_shared);
295 lua_register(context->lua, L_FUNCTION_PREFIX"add_constraint_exclusive", lf_add_constraint_exclusive);
296 lua_register(context->lua, L_FUNCTION_PREFIX"default_target", lf_default_target);
297 lua_register(context->lua, L_FUNCTION_PREFIX"set_touch", lf_set_touch);
298 lua_register(context->lua, L_FUNCTION_PREFIX"set_filter", lf_set_filter);
299
300 /* advanced dependency checkers */
301 lua_register(context->lua, L_FUNCTION_PREFIX"add_dependency_cpp_set_paths", lf_add_dependency_cpp_set_paths);
302 lua_register(context->lua, L_FUNCTION_PREFIX"add_dependency_cpp", lf_add_dependency_cpp);
303 lua_register(context->lua, L_FUNCTION_PREFIX"add_dependency_search", lf_add_dependency_search);
304
305 /* path manipulation */
306 lua_register(context->lua, L_FUNCTION_PREFIX"path_join", lf_path_join);
307 lua_register(context->lua, L_FUNCTION_PREFIX"path_normalize", lf_path_normalize);
308 lua_register(context->lua, L_FUNCTION_PREFIX"path_isnice", lf_path_isnice);
309
310 lua_register(context->lua, L_FUNCTION_PREFIX"path_ext", lf_path_ext);
311 lua_register(context->lua, L_FUNCTION_PREFIX"path_dir", lf_path_dir);
312 lua_register(context->lua, L_FUNCTION_PREFIX"path_base", lf_path_base);
313 lua_register(context->lua, L_FUNCTION_PREFIX"path_filename", lf_path_filename);
314
315 /* various support functions */
316 lua_register(context->lua, L_FUNCTION_PREFIX"collect", lf_collect);
317 lua_register(context->lua, L_FUNCTION_PREFIX"collectrecursive", lf_collectrecursive);
318 lua_register(context->lua, L_FUNCTION_PREFIX"collectdirs", lf_collectdirs);
319 lua_register(context->lua, L_FUNCTION_PREFIX"collectdirsrecursive", lf_collectdirsrecursive);
320
321 lua_register(context->lua, L_FUNCTION_PREFIX"listdir", lf_listdir);
322 lua_register(context->lua, L_FUNCTION_PREFIX"update_globalstamp", lf_update_globalstamp);
323 lua_register(context->lua, L_FUNCTION_PREFIX"loadfile", lf_loadfile);
324 lua_register(context->lua, L_FUNCTION_PREFIX"load_plugin", lf_loadplugin);
325
326 lua_register(context->lua, L_FUNCTION_PREFIX"mkdir", lf_mkdir);
327 lua_register(context->lua, L_FUNCTION_PREFIX"fileexist", lf_fileexist);
328
329 lua_register(context->lua, L_FUNCTION_PREFIX"isstring", lf_isstring);
330 lua_register(context->lua, L_FUNCTION_PREFIX"istable", lf_istable);
331
332 lua_register(context->lua, L_FUNCTION_PREFIX"table_walk", lf_table_walk);
333 lua_register(context->lua, L_FUNCTION_PREFIX"table_deepcopy", lf_table_deepcopy);
334 lua_register(context->lua, L_FUNCTION_PREFIX"table_tostring", lf_table_tostring);
335 lua_register(context->lua, L_FUNCTION_PREFIX"table_flatten", lf_table_flatten);
336
337 /* error handling */
338 lua_register(context->lua, "errorfunc", lf_errorfunc);
339
340 /* create arguments table */
341 lua_pushstring(context->lua, CONTEXT_LUA_SCRIPTARGS_TABLE);
342 lua_newtable(context->lua);
343 for(i = 0; i < option_num_scriptargs; i++)
344 {
345 const char *separator = option_scriptargs[i];
346 while(*separator != '=')
347 separator++;
348 lua_pushlstring(context->lua, option_scriptargs[i], separator-option_scriptargs[i]);
349 lua_pushstring(context->lua, separator+1);
350 lua_settable(context->lua, -3);
351 }
352 lua_settable(context->lua, LUA_GLOBALSINDEX);
353
354 /* create targets table */
355 lua_pushstring(context->lua, CONTEXT_LUA_TARGETS_TABLE);
356 lua_newtable(context->lua);
357 for(i = 0; i < option_num_targets; i++)
358 {
359 lua_pushstring(context->lua, option_targets[i]);
360 lua_rawseti(context->lua, -2, i);
361 }
362 lua_settable(context->lua, LUA_GLOBALSINDEX);
363
364 /* set paths */
365 {
366 char cwd[MAX_PATH_LENGTH];
367 if(!getcwd(cwd, sizeof(cwd)))
368 {
369 printf("%s: error: couldn't get current working directory\n", session.name);
370 return -1;
371 }
372
373 lua_setglobalstring(context->lua, CONTEXT_LUA_PATH, context->script_directory);
374 lua_setglobalstring(context->lua, CONTEXT_LUA_WORKPATH, cwd);
375 }
376
377 /* set version, family, platform, arch, verbocity */
378 lua_setglobalstring(context->lua, "_bam_version", BAM_VERSION_STRING);
379 lua_setglobalstring(context->lua, "_bam_version_complete", BAM_VERSION_STRING_COMPLETE);
380 lua_setglobalstring(context->lua, "family", BAM_FAMILY_STRING);
381 lua_setglobalstring(context->lua, "platform", BAM_PLATFORM_STRING);
382 lua_setglobalstring(context->lua, "arch", BAM_ARCH_STRING);
383 lua_setglobalstring(context->lua, "_bam_exe", session.exe);
384 lua_pushnumber(context->lua, session.verbose);
385 lua_setglobal(context->lua, "verbose");
386
387 if(option_debug_trace_vm)
388 lua_sethook(context->lua, lua_vm_trace_hook, LUA_MASKCOUNT, 1);
389
390 /* load base script */
391 if(!option_debug_nointernal)
392 {
393 int ret;
394 const char *p;
395 int f;
396
397 for(f = 0; internal_files[f].filename; f++)
398 {
399 p = internal_files[f].content;
400
401 if(session.verbose)
402 printf("%s: reading internal file '%s'\n", session.name, internal_files[f].filename);
403
404 lua_getglobal(context->lua, "errorfunc");
405
406 /* push error function to stack */
407 ret = lua_load(context->lua, internal_base_reader, (void *)&p, internal_files[f].filename);
408 if(ret != 0)
409 {
410 lf_errorfunc(context->lua);
411
412 if(ret == LUA_ERRSYNTAX)
413 {
414 }
415 else if(ret == LUA_ERRMEM)
416 printf("%s: memory allocation error\n", session.name);
417 else
418 printf("%s: unknown error parsing base script\n", session.name);
419
420 error = 1;
421 }
422 else if(lua_pcall(context->lua, 0, LUA_MULTRET, -2) != 0)
423 error = 1;
424 }
425 }
426
427 return error;
428 }
429
run_deferred_functions(struct CONTEXT * context)430 static int run_deferred_functions(struct CONTEXT *context)
431 {
432 struct DEFERRED *cur;
433 for(cur = context->firstdeferred; cur; cur = cur->next)
434 {
435 if(cur->run(context, cur))
436 return -1;
437 }
438
439 return 0;
440 }
441
bam_setup(struct CONTEXT * context,const char * scriptfile,const char ** targets,int num_targets)442 static int bam_setup(struct CONTEXT *context, const char *scriptfile, const char **targets, int num_targets)
443 {
444 /* */
445 if(session.verbose)
446 printf("%s: setup started\n", session.name);
447
448 /* set filename */
449 context->filename = scriptfile;
450 context->filename_short = path_filename(scriptfile);
451
452 /* set global timestamp to the script file */
453 context->globaltimestamp = file_timestamp(scriptfile);
454
455 /* fetch script directory */
456 {
457 char cwd[MAX_PATH_LENGTH];
458 char path[MAX_PATH_LENGTH];
459
460 if(!getcwd(cwd, sizeof(cwd)))
461 {
462 printf("%s: error: couldn't get current working directory\n", session.name);
463 return -1;
464 }
465
466 if(path_directory(context->filename, path, sizeof(path)))
467 {
468 printf("%s: error: path too long '%s'\n", session.name, path);
469 return -1;
470 }
471
472 if(path_join(cwd, -1, path, -1, context->script_directory, sizeof(context->script_directory)))
473 {
474 printf("%s: error: path too long when joining '%s' and '%s'\n", session.name, cwd, path);
475 return -1;
476 }
477 }
478
479 /* register all functions */
480 if(register_lua_globals(context) != 0)
481 {
482 printf("%s: error: registering of lua functions failed\n", session.name);
483 return -1;
484 }
485
486 /* load script */
487 if(session.verbose)
488 printf("%s: reading script from '%s'\n", session.name, scriptfile);
489
490 /* push error function to stack and load the script */
491 lua_getglobal(context->lua, "errorfunc");
492 switch(luaL_loadfile(context->lua, scriptfile))
493 {
494 case 0: break;
495 case LUA_ERRSYNTAX:
496 lf_errorfunc(context->lua);
497 return -1;
498 case LUA_ERRMEM:
499 printf("%s: memory allocation error\n", session.name);
500 return -1;
501 case LUA_ERRFILE:
502 printf("%s: error opening '%s'\n", session.name, scriptfile);
503 return -1;
504 default:
505 printf("%s: unknown error\n", session.name);
506 return -1;
507 }
508
509 /* call the code chunk */
510 if(lua_pcall(context->lua, 0, LUA_MULTRET, -2) != 0)
511 {
512 printf("%s: script error (-t for more detail)\n", session.name);
513 return -1;
514 }
515
516 /* run deferred functions */
517 if(run_deferred_functions(context) != 0)
518 return -1;
519
520 /* */
521 if(session.verbose)
522 printf("%s: making build target\n", session.name);
523
524 /* make build target */
525 {
526 struct NODE *node;
527 int all_target = 0;
528 int i;
529
530 if(node_create(&context->target, context->graph, "_bam_buildtarget", 0, 0))
531 return -1;
532 node_set_pseudo(context->target);
533
534 if(num_targets)
535 {
536 /* search for all target */
537 for(i = 0; i < num_targets; i++)
538 {
539 if(strcmp(targets[i], "all") == 0)
540 {
541 all_target = 1;
542 break;
543 }
544 }
545 }
546
547 /* default too all if we have no targets or default target */
548 if(num_targets == 0 && !context->defaulttarget)
549 all_target = 1;
550
551 if(all_target)
552 {
553 /* build the all target */
554 for(node = context->graph->first; node; node = node->next)
555 {
556 if(node->firstparent == NULL && node != context->target)
557 {
558 if(!node_add_dependency_withnode(context->target, node))
559 return -1;
560 }
561 }
562 }
563 else
564 {
565 if(num_targets)
566 {
567 for(i = 0; i < num_targets; i++)
568 {
569 struct NODE *node = node_find(context->graph, targets[i]);
570 if(!node)
571 {
572 printf("%s: target '%s' not found\n", session.name, targets[i]);
573 return -1;
574 }
575
576 if(option_dependent)
577 {
578 /* TODO: this should perhaps do a reverse walk up in the tree to
579 find all dependent node with commandline */
580 struct NODELINK *parent;
581 for(parent = node->firstparent; parent; parent = parent->next)
582 {
583 if(!node_add_dependency_withnode(context->target, parent->node))
584 return -1;
585 }
586
587 }
588 else
589 {
590 if(!node_add_dependency_withnode(context->target, node))
591 return -1;
592 }
593 }
594 }
595 else
596 {
597 if(!node_add_dependency_withnode(context->target, context->defaulttarget))
598 return -1;
599 }
600
601 }
602 }
603
604 /* */
605 if(session.verbose)
606 printf("%s: setup done\n", session.name);
607
608
609 /* return success */
610 return 0;
611 }
612
613 /* *** */
bam(const char * scriptfile,const char ** targets,int num_targets)614 static int bam(const char *scriptfile, const char **targets, int num_targets)
615 {
616 struct CONTEXT context;
617 int build_error = 0;
618 int setup_error = 0;
619 int report_done = 0;
620
621 /* build time */
622 time_t starttime = time(0x0);
623
624 /* zero out and create memory heap, graph */
625 memset(&context, 0, sizeof(struct CONTEXT));
626 context.graphheap = mem_create();
627 context.deferredheap = mem_create();
628 context.graph = node_create_graph(context.graphheap);
629 context.exit_on_error = option_abort_on_error;
630 context.buildtime = timestamp();
631
632 /* create lua context */
633 /* HACK: Store the context pointer as the userdata pointer to the allocator to make
634 sure that we have fast access to it. This makes the context_get_pointer call very fast */
635 context.lua = lua_newstate(lua_alloctor_malloc, &context);
636
637 /* install panic function */
638 lua_atpanic(context.lua, lf_panicfunc);
639
640 /* load cache (thread?) */
641 if(option_no_cache == 0)
642 {
643 /* create a hash of all the external variables that can cause the
644 script to generate different results */
645 unsigned int cache_hash = 0;
646 int i;
647 for(i = 0; i < option_num_scriptargs; i++)
648 cache_hash = string_hash_add(cache_hash, option_scriptargs[i]);
649 sprintf(cache_filename, ".bam/%08x", cache_hash);
650 context.cache = cache_load(cache_filename);
651 }
652
653 /* do the setup */
654 setup_error = bam_setup(&context, scriptfile, targets, num_targets);
655
656 /* done with the loopup heap */
657 mem_destroy(context.deferredheap);
658
659 /* if we have a separate lua heap, just destroy it */
660 if(context.luaheap)
661 mem_destroy(context.luaheap);
662 else
663 lua_close(context.lua);
664
665 /* do actions if we don't have any errors */
666 if(!setup_error)
667 {
668 build_error = context_build_prepare(&context);
669
670 if(!build_error)
671 {
672 if(option_debug_nodes) /* debug dump all nodes */
673 node_debug_dump(context.graph);
674 else if(option_debug_nodes_detailed) /* debug dump all nodes detailed */
675 node_debug_dump_detailed(context.graph);
676 else if(option_debug_jobs) /* debug dump all jobs */
677 node_debug_dump_jobs(context.graph);
678 else if(option_debug_dot) /* debug dump all nodes as dot */
679 node_debug_dump_dot(context.graph, context.target);
680 else if(option_debug_jobs_dot) /* debug dump all jobs as dot */
681 node_debug_dump_jobs_dot(context.graph, context.target);
682 else if(option_dry)
683 {
684 }
685 else
686 {
687 /* run build or clean */
688 if(option_clean)
689 build_error = context_build_clean(&context);
690 else
691 {
692 build_error = context_build_make(&context);
693 report_done = 1;
694 }
695 }
696 }
697 }
698
699 /* save cache (thread?) */
700 if(option_no_cache == 0 && setup_error == 0)
701 {
702 /* create the cache directory */
703 file_createdir(".bam");
704 cache_save(cache_filename, context.graph);
705 }
706
707 /* clean up */
708 mem_destroy(context.graphheap);
709
710 /* print final report and return */
711 if(setup_error)
712 {
713 /* no error message on setup error, it reports fine itself */
714 return setup_error;
715 }
716 else if(build_error)
717 printf("%s: error: a build step failed\n", session.name);
718 else if(report_done)
719 {
720 if(context.num_commands == 0)
721 printf("%s: targets are up to date already\n", session.name);
722 else
723 {
724 time_t s = time(0x0) - starttime;
725 if(s <= 1)
726 printf("%s: done\n", session.name);
727 else
728 printf("%s: done (%d:%.2d)\n", session.name, (int)(s/60), (int)(s%60));
729 }
730 }
731
732 return build_error;
733 }
734
735
736
737 /* signal handler */
abortsignal(int i)738 static void abortsignal(int i)
739 {
740 (void)i;
741 printf("%s: signal cought, waiting for jobs to finish\n", session.name);
742 session.abort = 1;
743 }
744
install_abort_signal()745 void install_abort_signal()
746 {
747 install_signals(abortsignal);
748 }
749
750 /* */
print_help()751 static void print_help()
752 {
753 int j;
754 printf("Usage: %s [OPTION]... [VARIABLE=VALUE]... [TARGET]...\n", session.name);
755 printf("Builds applications using the bam build system.\n");
756 for(j = 0; options[j].sw; j++)
757 {
758 if(options[j].print)
759 printf(" %-20s %s\n", options[j].sw, options[j].desc);
760 }
761 printf("\n");
762 printf("bam version " BAM_VERSION_STRING_COMPLETE ". built "__DATE__" "__TIME__" using " LUA_VERSION "\n");
763 printf("by Magnus Auvinen (magnus.auvinen@gmail.com)\n");
764 printf("\n");
765
766 }
767
parse_parameters(int num,char ** params)768 static int parse_parameters(int num, char **params)
769 {
770 int i, j;
771
772 /* parse parameters */
773 for(i = 0; i < num; ++i)
774 {
775 for(j = 0; options[j].sw; j++)
776 {
777 if(strcmp(params[i], options[j].sw) == 0)
778 {
779 if(options[j].s)
780 {
781 if(i+1 >= num)
782 {
783 printf("%s: you must supply a argument with %s\n", session.name, options[j].sw);
784 return -1;
785 }
786
787 i++;
788 *options[j].s = params[i];
789 }
790 else
791 *options[j].v = 1;
792
793 j = -1;
794 break;
795 }
796 }
797
798 if(j != -1)
799 {
800 /* check if it's an option, and warn if it could not be found */
801 if(params[i][0] == '-')
802 {
803 printf("%s: unknown switch '%s'\n", session.name, params[i]);
804 return -1;
805 }
806 else
807 {
808 /* check for = because it indicates if it's a target or script argument */
809 if(strchr(params[i], '='))
810 option_scriptargs[option_num_scriptargs++] = params[i];
811 else
812 option_targets[option_num_targets++] = params[i];
813 }
814 }
815 }
816 return 0;
817 }
818
parse_parameters_str(const char * str)819 static int parse_parameters_str(const char *str)
820 {
821 char *ptrs[64];
822 int num_ptrs = 0;
823 char buffer[1024];
824 char *start = buffer;
825 char *current = start;
826 char *end = buffer+sizeof(buffer);
827 int string_parse = 0;
828
829 ptrs[0] = start;
830
831 while(1)
832 {
833 /* fetch next character */
834 char c = *str++;
835
836 if(string_parse)
837 {
838 if(c == '"')
839 string_parse = 0;
840 else if(*str == 0)
841 {
842 printf("%s: error: unterminated \"\n", session.name);
843 return -1;
844 }
845 else
846 *current++ = c;
847 }
848 else
849 {
850 if(c == ' ' || c == '\t' || c == 0)
851 {
852 /* zero term and add this ptr */
853 *current++ = 0;
854 num_ptrs++;
855 ptrs[num_ptrs] = current;
856 }
857 else if(c == '"')
858 string_parse = 1;
859 else
860 *current++ = c;
861 }
862
863 if(current == end)
864 {
865 printf("%s: error: argument too long\n", session.name);
866 return -1;
867 }
868
869 /* break out on zero term */
870 if(!c)
871 break;
872 }
873
874 /* parse all the parameters */
875 return parse_parameters(num_ptrs, ptrs);
876 }
877
878 /* ********* */
main(int argc,char ** argv)879 int main(int argc, char **argv)
880 {
881 int i, error;
882
883 /* init platform */
884 platform_init();
885
886 /* set exe */
887 session.exe = argv[0];
888
889 /* fetch program name, if we can */
890 for(i = 0; argv[0][i]; ++i)
891 {
892 if(argv[0][i] == '/' || argv[0][i] == '\\')
893 session.name = &argv[0][i+1];
894 }
895
896 /* parse environment parameters */
897 if(getenv("BAM_OPTIONS"))
898 {
899 if(parse_parameters_str(getenv("BAM_OPTIONS")))
900 return -1;
901 }
902
903 /* parse commandline parameters */
904 if(parse_parameters(argc-1, argv+1))
905 return -1;
906
907 /* parse the report str */
908 for(i = 0; option_report_str[i]; i++)
909 {
910 if(option_report_str[i] == 'c')
911 session.report_color = 1;
912 else if(option_report_str[i] == 'b')
913 session.report_bar = 1;
914 else if(option_report_str[i] == 's')
915 session.report_steps = 1;
916 }
917
918 /* convert the threads string */
919 session.threads = atoi(option_threads_str);
920 if(session.threads < 0)
921 {
922 printf("%s: invalid number of threads supplied\n", session.name);
923 return 1;
924 }
925
926 /* check for help argument */
927 if(option_print_help)
928 {
929 print_help();
930 return 0;
931 }
932
933 /* */
934 if(option_debug_dumpinternal)
935 {
936 int f;
937 for(f = 0; internal_files[f].filename; f++)
938 {
939 printf("%s:\n", internal_files[f].filename);
940 puts(internal_files[f].content);
941 }
942
943 return 0;
944 }
945
946 /* init the context */
947 error = bam(option_script, option_targets, option_num_targets);
948
949 platform_shutdown();
950
951 /* error could be some high value like 256 seams like this could */
952 /* be clamped down to a unsigned char and not be an error anymore */
953 if(error)
954 return 1;
955 return 0;
956 }
957