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