Browse Source

Add luac.cross capability to nodeMCU

TerryE 8 years ago
parent
commit
901fdf60cd

+ 2 - 0
app/include/user_modules.h

@@ -11,6 +11,7 @@
 // #define LUA_USE_BUILTIN_DEBUG
 #define LUA_USE_BUILTIN_DEBUG_MINIMAL // for debug.getregistry() and debug.traceback()
 
+#ifndef LUA_CROSS_COMPILER
 #define LUA_USE_MODULES
 
 #ifdef LUA_USE_MODULES
@@ -38,5 +39,6 @@
 #define LUA_USE_MODULES_DHT
 
 #endif /* LUA_USE_MODULES */
+#endif
 
 #endif	/* __USER_MODULES_H__ */

+ 4 - 6
app/lua/lapi.c

@@ -4,17 +4,15 @@
 ** See Copyright Notice in lua.h
 */
 
-
-//#include "c_assert.h"
-#include "c_math.h"
-#include "c_stdarg.h"
-#include "c_string.h"
-
 #define lapi_c
 #define LUA_CORE
+#define LUAC_CROSS_FILE
 
 #include "lua.h"
 
+//#include C_HEADER_ASSERT
+#include C_HEADER_MATH
+#include C_HEADER_STRING
 #include "lapi.h"
 #include "ldebug.h"
 #include "ldo.h"

+ 12 - 10
app/lua/lauxlib.c

@@ -4,14 +4,18 @@
 ** See Copyright Notice in lua.h
 */
 
+#define LUAC_CROSS_FILE
 
-#include "c_ctype.h"
-// #include "c_errno.h"
-#include "c_stdarg.h"
-#include "c_stdio.h"
-#include "c_stdlib.h"
-#include "c_string.h"
+#include "lua.h"
+#include C_HEADER_CTYPE
+#include C_HEADER_ERRNO
+#include C_HEADER_STDIO
+#include C_HEADER_STDLIB
+#include C_HEADER_STRING
+#ifndef LUA_CROSS_COMPILER
 #include "flash_fs.h"
+#else
+#endif
 
 /* This file uses only the official API of Lua.
 ** Any function declared here could be written as an application function.
@@ -20,8 +24,6 @@
 #define lauxlib_c
 #define LUA_LIB
 
-#include "lua.h"
-
 #include "lrotable.h"
 
 #include "lauxlib.h"
@@ -573,7 +575,7 @@ LUALIB_API void luaL_unref (lua_State *L, int t, int ref) {
 ** =======================================================
 */
 
-#if 0
+#ifdef LUA_CROSS_COMPILER
 
 typedef struct LoadF {
   int extraline;
@@ -647,7 +649,7 @@ LUALIB_API int luaL_loadfile (lua_State *L, const char *filename) {
 
 #else
 
-#include "c_fcntl.h"
+#include C_HEADER_FCNTL
 
 typedef struct LoadFSF {
   int extraline;

+ 7 - 4
app/lua/lauxlib.h

@@ -9,11 +9,14 @@
 #define lauxlib_h
 
 
-//#include "c_stddef.h"
-#include "c_stdio.h"
-
 #include "lua.h"
 
+#ifdef LUA_CROSS_COMPILER
+#include <stdio.h>
+#else
+#include "c_stdio.h"
+#endif
+
 
 #if defined(LUA_COMPAT_GETN)
 LUALIB_API int (luaL_getn) (lua_State *L, int t);
@@ -79,7 +82,7 @@ LUALIB_API int (luaL_checkoption) (lua_State *L, int narg, const char *def,
 LUALIB_API int (luaL_ref) (lua_State *L, int t);
 LUALIB_API void (luaL_unref) (lua_State *L, int t, int ref);
 
-#if 0
+#ifdef LUA_CROSS_COMPILER
 LUALIB_API int (luaL_loadfile) (lua_State *L, const char *filename);
 #else
 LUALIB_API int (luaL_loadfsfile) (lua_State *L, const char *filename);

+ 6 - 8
app/lua/lbaselib.c

@@ -6,16 +6,14 @@
 
 
 
-#include "c_ctype.h"
-#include "c_stdio.h"
-#include "c_stdlib.h"
-#include "c_string.h"
-
 #define lbaselib_c
 #define LUA_LIB
+#define LUAC_CROSS_FILE
 
 #include "lua.h"
-
+#include C_HEADER_STDIO
+#include C_HEADER_STRING
+#include C_HEADER_STDLIB
 #include "lauxlib.h"
 #include "lualib.h"
 #include "lrotable.h"
@@ -295,7 +293,7 @@ static int luaB_loadstring (lua_State *L) {
 
 static int luaB_loadfile (lua_State *L) {
   const char *fname = luaL_optstring(L, 1, NULL);
-#if 0
+#ifdef LUA_CROSS_COMPILER
   return load_aux(L, luaL_loadfile(L, fname));
 #else
   return load_aux(L, luaL_loadfsfile(L, fname));
@@ -342,7 +340,7 @@ static int luaB_load (lua_State *L) {
 static int luaB_dofile (lua_State *L) {
   const char *fname = luaL_optstring(L, 1, NULL);
   int n = lua_gettop(L);
-#if 0
+#ifdef LUA_CROSS_COMPILER
   if (luaL_loadfile(L, fname) != 0) lua_error(L);
 #else
   if (luaL_loadfsfile(L, fname) != 0) lua_error(L);

+ 2 - 2
app/lua/lcode.c

@@ -5,12 +5,12 @@
 */
 
 
-#include "c_stdlib.h"
-
 #define lcode_c
 #define LUA_CORE
+#define LUAC_CROSS_FILE
 
 #include "lua.h"
+#include C_HEADER_STDLIB
 
 #include "lcode.h"
 #include "ldebug.h"

+ 4 - 4
app/lua/ldblib.c

@@ -5,14 +5,14 @@
 */
 
 
-#include "c_stdio.h"
-#include "c_stdlib.h"
-#include "c_string.h"
-
 #define ldblib_c
 #define LUA_LIB
+#define LUAC_CROSS_FILE
 
 #include "lua.h"
+#include C_HEADER_STDIO
+#include C_HEADER_STDLIB
+#include C_HEADER_STRING
 
 #include "lauxlib.h"
 #include "lualib.h"

+ 2 - 5
app/lua/ldebug.c

@@ -5,15 +5,12 @@
 */
 
 
-#include "c_stdarg.h"
-#include "c_stddef.h"
-#include "c_string.h"
-
-
 #define ldebug_c
 #define LUA_CORE
+#define LUAC_CROSS_FILE
 
 #include "lua.h"
+#include C_HEADER_STRING
 
 #include "lapi.h"
 #include "lcode.h"

+ 2 - 3
app/lua/ldo.c

@@ -4,15 +4,14 @@
 ** See Copyright Notice in lua.h
 */
 
-
 #include <setjmp.h>
-#include "c_stdlib.h"
-#include "c_string.h"
 
 #define ldo_c
 #define LUA_CORE
+#define LUAC_CROSS_FILE
 
 #include "lua.h"
+#include C_HEADER_STRING
 
 #include "ldebug.h"
 #include "ldo.h"

+ 2 - 3
app/lua/ldump.c

@@ -4,13 +4,12 @@
 ** See Copyright Notice in lua.h
 */
 
-#include "c_stddef.h"
-#include "c_string.h"
-
 #define ldump_c
 #define LUA_CORE
+#define LUAC_CROSS_FILE
 
 #include "lua.h"
+#include C_HEADER_STRING
 
 #include "lobject.h"
 #include "lstate.h"

+ 1 - 3
app/lua/lfunc.c

@@ -4,11 +4,9 @@
 ** See Copyright Notice in lua.h
 */
 
-
-#include "c_stddef.h"
-
 #define lfunc_c
 #define LUA_CORE
+#define LUAC_CROSS_FILE
 
 #include "lua.h"
 

+ 2 - 2
app/lua/lgc.c

@@ -4,12 +4,12 @@
 ** See Copyright Notice in lua.h
 */
 
-#include "c_string.h"
-
 #define lgc_c
 #define LUA_CORE
+#define LUAC_CROSS_FILE
 
 #include "lua.h"
+#include C_HEADER_STRING
 
 #include "ldebug.h"
 #include "ldo.h"

+ 4 - 4
app/lua/llex.c

@@ -5,14 +5,14 @@
 */
 
 
-#include "c_ctype.h"
-#include "c_locale.h"
-#include "c_string.h"
-
 #define llex_c
 #define LUA_CORE
+#define LUAC_CROSS_FILE
 
 #include "lua.h"
+#include C_HEADER_CTYPE
+#include C_HEADER_LOCALE
+#include C_HEADER_STRING
 
 #include "ldo.h"
 #include "llex.h"

+ 0 - 3
app/lua/llimits.h

@@ -9,12 +9,9 @@
 
 
 //#include "c_limits.h"
-//#include "c_stddef.h"
-
 
 #include "lua.h"
 
-
 typedef LUAI_UINT32 lu_int32;
 
 typedef LUAI_UMEM lu_mem;

+ 11 - 6
app/lua/lmathlib.c

@@ -5,13 +5,13 @@
 */
 
 
-#include "c_stdlib.h"
-#include "c_math.h"
-
 #define lmathlib_c
 #define LUA_LIB
+#define LUAC_CROSS_FILE
 
 #include "lua.h"
+#include C_HEADER_STDLIB
+#include C_HEADER_MATH
 
 #include "lauxlib.h"
 #include "lualib.h"
@@ -35,7 +35,7 @@ static int math_abs (lua_State *L) {
 }
 
 #ifndef LUA_NUMBER_INTEGRAL
-
+#if 0
 static int math_sin (lua_State *L) {
   lua_pushnumber(L, sin(luaL_checknumber(L, 1)));
   return 1;
@@ -85,6 +85,7 @@ static int math_atan2 (lua_State *L) {
   lua_pushnumber(L, atan2(luaL_checknumber(L, 1), luaL_checknumber(L, 2)));
   return 1;
 }
+#endif
 
 static int math_ceil (lua_State *L) {
   lua_pushnumber(L, ceil(luaL_checknumber(L, 1)));
@@ -95,7 +96,7 @@ static int math_floor (lua_State *L) {
   lua_pushnumber(L, floor(luaL_checknumber(L, 1)));
   return 1;
 }
-
+#if 0
 static int math_fmod (lua_State *L) {
   lua_pushnumber(L, fmod(luaL_checknumber(L, 1), luaL_checknumber(L, 2)));
   return 1;
@@ -108,6 +109,7 @@ static int math_modf (lua_State *L) {
   lua_pushnumber(L, fp);
   return 2;
 }
+#endif
 
 #else  // #ifndef LUA_NUMBER_INTEGRAL
 
@@ -173,7 +175,7 @@ static int math_pow (lua_State *L) {
 
 
 #ifndef LUA_NUMBER_INTEGRAL
-
+#if 0
 static int math_log (lua_State *L) {
   lua_pushnumber(L, log(luaL_checknumber(L, 1)));
   return 1;
@@ -210,6 +212,7 @@ static int math_ldexp (lua_State *L) {
   lua_pushnumber(L, ldexp(luaL_checknumber(L, 1), luaL_checkint(L, 2)));
   return 1;
 }
+#endif
 
 #endif // #ifdef LUA_NUMBER_INTEGRAL
 
@@ -306,6 +309,8 @@ static int math_randomseed (lua_State *L) {
   return 0;
 }
 
+
+
 #define MIN_OPT_LEVEL 1
 #include "lrodefs.h"
 const LUA_REG_TYPE math_map[] = {

+ 1 - 2
app/lua/lmem.c

@@ -5,10 +5,9 @@
 */
 
 
-#include "c_stddef.h"
-
 #define lmem_c
 #define LUA_CORE
+#define LUAC_CROSS_FILE
 
 #include "lua.h"
 

+ 4 - 0
app/lua/lmem.h

@@ -8,7 +8,11 @@
 #define lmem_h
 
 
+//#ifdef LUA_CROSS_COMPILER
+//#include <stddef.h>
+//#else
 //#include "c_stddef.h"
+//#endif
 
 #include "llimits.h"
 #include "lua.h"

+ 10 - 7
app/lua/loadlib.c

@@ -9,15 +9,18 @@
 */
 
 
-#include "c_stdlib.h"
-#include "c_string.h"
-#include "c_fcntl.h"
-#include "flash_fs.h"
-
 #define loadlib_c
 #define LUA_LIB
+#define LUAC_CROSS_FILE
 
 #include "lua.h"
+#include C_HEADER_STDLIB
+#include C_HEADER_STRING
+#include C_HEADER_FCNTL
+
+#ifndef LUA_CROSS_COMPILER
+#include "flash_fs.h"
+#endif
 
 #include "lauxlib.h"
 #include "lualib.h"
@@ -328,7 +331,7 @@ static int ll_loadlib (lua_State *L) {
 ** 'require' function
 ** =======================================================
 */
-#if 0
+#ifdef LUA_CROSS_COMPILER
 static int readable (const char *filename) {
   FILE *f = c_fopen(filename, "r");  /* try to open file */
   if (f == NULL) return 0;  /* open failed */
@@ -389,7 +392,7 @@ static int loader_Lua (lua_State *L) {
   const char *name = luaL_checkstring(L, 1);
   filename = findfile(L, name, "path");
   if (filename == NULL) return 1;  /* library not found in this path */
-#if 0
+#ifdef LUA_CROSS_COMPILER
   if (luaL_loadfile(L, filename) != 0)
 #else
   if (luaL_loadfsfile(L, filename) != 0)

+ 10 - 9
app/lua/lobject.c

@@ -4,16 +4,15 @@
 ** See Copyright Notice in lua.h
 */
 
-#include "c_ctype.h"
-#include "c_stdarg.h"
-#include "c_stdio.h"
-#include "c_stdlib.h"
-#include "c_string.h"
 
 #define lobject_c
 #define LUA_CORE
+#define LUAC_CROSS_FILE
 
 #include "lua.h"
+#include C_HEADER_STDIO
+#include C_HEADER_STRING
+#include C_HEADER_STDLIB
 
 #include "ldo.h"
 #include "lmem.h"
@@ -21,9 +20,9 @@
 #include "lstate.h"
 #include "lstring.h"
 #include "lvm.h"
-
+#ifndef LUA_CROSS_COMPILER
 #include "flash_api.h"
-
+#endif
 const TValue luaO_nilobject_ = {LUA_TVALUE_NIL};
 
 
@@ -64,9 +63,11 @@ int luaO_log2 (unsigned int x) {
   };
   int l = -1;
   while (x >= 256) { l += 8; x >>= 8; }
-  // return l + log_2[x];
+#ifdef LUA_CROSS_COMPILER
+  return l + log_2[x];
+#else
   return l + byte_of_aligned_array(log_2,x);
-
+#endif
 }
 
 

+ 4 - 2
app/lua/lobject.h

@@ -8,9 +8,11 @@
 #ifndef lobject_h
 #define lobject_h
 
-
+#ifdef LUA_CROSS_COMPILER
+#include <stdarg.h>
+#else
 #include "c_stdarg.h"
-
+#endif
 
 #include "llimits.h"
 #include "lua.h"

+ 2 - 2
app/lua/lopcodes.c

@@ -6,11 +6,11 @@
 
 #define lopcodes_c
 #define LUA_CORE
+#define LUAC_CROSS_FILE
 
-
+#include "luac_cross.h"
 #include "lopcodes.h"
 
-
 /* ORDER OP */
 
 const char *const luaP_opnames[NUM_OPCODES+1] = {

+ 2 - 2
app/lua/lparser.c

@@ -5,12 +5,12 @@
 */
 
 
-#include "c_string.h"
-
 #define lparser_c
 #define LUA_CORE
+#define LUAC_CROSS_FILE
 
 #include "lua.h"
+#include C_HEADER_STRING
 
 #include "lcode.h"
 #include "ldebug.h"

+ 3 - 2
app/lua/lrotable.c

@@ -1,8 +1,9 @@
 /* Read-only tables for Lua */
+#define LUAC_CROSS_FILE
 
-#include "c_string.h"
-#include "lrotable.h"
 #include "lua.h"
+#include C_HEADER_STRING
+#include "lrotable.h"
 #include "lauxlib.h"
 #include "lstring.h"
 #include "lobject.h"

+ 1 - 2
app/lua/lstate.c

@@ -5,10 +5,9 @@
 */
 
 
-#include "c_stddef.h"
-
 #define lstate_c
 #define LUA_CORE
+#define LUAC_CROSS_FILE
 
 #include "lua.h"
 

+ 2 - 1
app/lua/lstring.c

@@ -5,12 +5,13 @@
 */
 
 
-#include "c_string.h"
 
 #define lstring_c
 #define LUA_CORE
+#define LUAC_CROSS_FILE
 
 #include "lua.h"
+#include C_HEADER_STRING
 
 #include "lmem.h"
 #include "lobject.h"

+ 3 - 6
app/lua/lstrlib.c

@@ -5,16 +5,13 @@
 */
 
 
-#include "c_ctype.h"
-#include "c_stddef.h"
-#include "c_stdio.h"
-#include "c_stdlib.h"
-#include "c_string.h"
-
 #define lstrlib_c
 #define LUA_LIB
+#define LUAC_CROSS_FILE
 
 #include "lua.h"
+#include C_HEADER_STDIO
+#include C_HEADER_STRING
 
 #include "lauxlib.h"
 #include "lualib.h"

+ 3 - 3
app/lua/ltable.c

@@ -18,13 +18,13 @@
 ** Hence even when the load factor reaches 100%, performance remains good.
 */
 
-#include "c_math.h"
-#include "c_string.h"
-
 #define ltable_c
 #define LUA_CORE
+#define LUAC_CROSS_FILE
 
 #include "lua.h"
+#include C_HEADER_MATH
+#include C_HEADER_STRING
 
 #include "ldebug.h"
 #include "ldo.h"

+ 1 - 2
app/lua/ltablib.c

@@ -5,10 +5,9 @@
 */
 
 
-#include "c_stddef.h"
-
 #define ltablib_c
 #define LUA_LIB
+#define LUAC_CROSS_FILE
 
 #include "lua.h"
 

+ 2 - 2
app/lua/ltm.c

@@ -5,12 +5,12 @@
 */
 
 
-#include "c_string.h"
-
 #define ltm_c
 #define LUA_CORE
+#define LUAC_CROSS_FILE
 
 #include "lua.h"
+#include C_HEADER_STRING
 
 #include "lobject.h"
 #include "lstate.h"

+ 9 - 1
app/lua/lua.h

@@ -8,10 +8,18 @@
 
 #ifndef lua_h
 #define lua_h
-
+#ifdef LUAC_CROSS_FILE
+#include "luac_cross.h"
+#endif
+#ifdef LUA_CROSS_COMPILER
+#include <stdarg.h>
+#include <stddef.h>
+#include <ctype.h>
+#else
 #include "c_stdarg.h"
 #include "c_stddef.h"
 #include "c_types.h"
+#endif
 
 #include "luaconf.h"
 

+ 76 - 0
app/lua/luac_cross.h

@@ -0,0 +1,76 @@
+/*
+** Header to allow luac.cross compile within NodeMCU
+** See Copyright Notice in lua.h
+*/
+#ifndef luac_cross_h
+#define luac_cross_h
+
+#ifdef LUA_CROSS_COMPILER
+
+#define C_HEADER_ASSERT <assert.h>
+#define C_HEADER_CTYPE <ctype.h>
+#define C_HEADER_ERRNO <errno.h>
+#define C_HEADER_FCNTL <fcntl.h>
+#define C_HEADER_LOCALE <locale.h>
+#define C_HEADER_MATH <math.h>
+#define C_HEADER_STDIO <stdio.h>
+#define C_HEADER_STDLIB <stdlib.h>
+#define C_HEADER_STRING <string.h>
+#define C_HEADER_TIME <time.h>
+
+#define ICACHE_RODATA_ATTR
+
+#define c_abs abs
+#define c_exit exit
+#define c_fclose fclose
+#define c_feof feof
+#define c_ferror ferror
+#define c_fopen fopen
+#define c_fread fread
+#define c_free free
+#define c_freopen freopen
+#define c_getc getc
+#define c_getenv getenv
+#define c_memcmp memcmp
+#define c_memcpy memcpy
+#define c_printf printf
+#define c_puts puts
+#define c_reader reader
+#define c_realloc realloc
+#define c_sprintf sprintf
+#define c_stderr stderr
+#define c_stdin stdin
+#define c_stdout stdout
+#define c_strcat strcat
+#define c_strchr strchr
+#define c_strcmp strcmp
+#define c_strcoll strcoll
+#define c_strcpy strcpy
+#define c_strcspn strcspn
+#define c_strerror strerror
+#define c_strlen strlen
+#define c_strncat strncat
+#define c_strncmp strncmp
+#define c_strncpy strncpy
+#define c_strpbrk strpbrk
+#define c_strrchr strrchr
+#define c_strstr strstr
+#define c_strtod strtod
+#define c_strtoul strtoul
+#define c_ungetc ungetc
+
+#else
+
+#define C_HEADER_ASSERT "c_assert.h"
+#define C_HEADER_CTYPE "c_ctype.h"
+#define C_HEADER_ERRNO "c_errno.h"
+#define C_HEADER_FCNTL "c_fcntl.h"
+#define C_HEADER_LOCALE "c_locale.h"
+#define C_HEADER_MATH "c_math.h"
+#define C_HEADER_STDIO "c_stdio.h"
+#define C_HEADER_STDLIB "c_stdlib.h"
+#define C_HEADER_STRING "c_string.h"
+#define C_HEADER_TIME "c_time.h"
+
+#endif /* LUA_CROSS_COMPILER */
+#endif /* luac_cross_h */

+ 8 - 6
app/lua/not_ported/loslib.c → app/lua/luac_cross/loslib.c

@@ -4,12 +4,14 @@
 ** See Copyright Notice in lua.h
 */
 
-
-#include <errno.h>
-#include <locale.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
+#define LUAC_CROSS_FILE
+
+#include "luac_cross.h"
+#include C_HEADER_ERRNO
+#include C_HEADER_LOCALE
+#include C_HEADER_STDLIB
+#include C_HEADER_STRING
+#include C_HEADER_TIME
 
 #define loslib_c
 #define LUA_LIB

+ 7 - 4
app/lua/not_ported/luac.c → app/lua/luac_cross/luac.c

@@ -4,10 +4,13 @@
 ** See Copyright Notice in lua.h
 */
 
-#include "c_errno.h"
-#include "c_stdio.h"
-#include "c_stdlib.h"
-#include "c_string.h"
+#define LUAC_CROSS_FILE
+
+#include "luac_cross.h"
+#include C_HEADER_ERRNO
+#include C_HEADER_STDIO
+#include C_HEADER_STDLIB
+#include C_HEADER_STRING
 
 #define luac_c
 #define LUA_CORE

+ 5 - 2
app/lua/not_ported/print.c → app/lua/luac_cross/print.c

@@ -4,8 +4,11 @@
 ** See Copyright Notice in lua.h
 */
 
-#include "c_ctype.h"
-#include "c_stdio.h"
+#define LUAC_CROSS_FILE
+
+#include "luac_cross.h"
+#include C_HEADER_CTYPE
+#include C_HEADER_STDIO
 
 #define luac_c
 #define LUA_CORE

+ 21 - 4
app/lua/luaconf.h

@@ -8,8 +8,13 @@
 #ifndef lconfig_h
 #define lconfig_h
 
+#ifdef LUA_CROSS_COMPILER
+#include <limits.h>
+#include <stddef.h>
+#else
 #include "c_limits.h"
 #include "c_stddef.h"
+#endif
 #include "user_config.h"
 
 /*
@@ -252,7 +257,12 @@
 #define lua_stdin_is_tty()	isatty(0)
 #elif defined(LUA_WIN)
 #include <io.h>
+#ifdef LUA_CROSS_COMPILER
+#include <stdio.h>
+else
 #include "c_stdio.h"
+#endif
+
 #define lua_stdin_is_tty()	_isatty(_fileno(stdin))
 #else
 #define lua_stdin_is_tty()	1  /* assume stdin is a tty */
@@ -294,8 +304,8 @@
 ** GNU readline and history facilities).
 */
 #if defined(LUA_USE_STDIO)
-#if defined(LUA_USE_READLINE)
-#include "c_stdio.h"
+#if defined(LUA_CROSS_COMPILER) && defined(LUA_USE_READLINE)
+#include <stdio.h>
 #include <readline/readline.h>
 #include <readline/history.h>
 #define lua_readline(L,b,p)	((void)L, ((b)=readline(p)) != NULL)
@@ -303,7 +313,7 @@
 	if (lua_strlen(L,idx) > 0)  /* non-empty line? */ \
 	  add_history(lua_tostring(L, idx));  /* add it to history */
 #define lua_freeline(L,b)	((void)L, c_free(b))
-#else // #if defined(LUA_USE_READLINE)
+#else // #if defined(LUA_CROSS_COMPILER) && defined(LUA_USE_READLINE)
 #define lua_readline(L,b,p)	\
 	((void)L, c_fputs(p, c_stdout), c_fflush(c_stdout),  /* show prompt */ \
 	c_fgets(b, LUA_MAXINPUT, c_stdin) != NULL)  /* get line */
@@ -623,7 +633,11 @@ extern int readline4lua(const char *prompt, char *buffer, int length);
 @@ The luai_num* macros define the primitive operations over numbers.
 */
 #if defined(LUA_CORE)
+#ifdef LUA_CROSS_COMPILER
+#include <math.h>
+#else
 #include "c_math.h"
+#endif
 #define luai_numadd(a,b)	((a)+(b))
 #define luai_numsub(a,b)	((a)-(b))
 #define luai_nummul(a,b)	((a)*(b))
@@ -866,7 +880,10 @@ union luai_Cast { double l_d; long l_l; };
 
 #define LUA_INTFRMLEN		"l"
 #define LUA_INTFRM_T		long
-
+#ifndef LUA_CROSS_COMPILER
+typedef short int16_t;
+typedef long int32_t;
+#endif
 #endif
 
 

+ 2 - 3
app/lua/lundump.c

@@ -4,13 +4,12 @@
 ** See Copyright Notice in lua.h
 */
 
-#include "c_string.h"
-#include "c_types.h"
-
 #define lundump_c
 #define LUA_CORE
+#define LUAC_CROSS_FILE
 
 #include "lua.h"
+#include C_HEADER_STRING
 
 #include "ldebug.h"
 #include "ldo.h"

+ 4 - 0
app/lua/lundump.h

@@ -7,7 +7,11 @@
 #ifndef lundump_h
 #define lundump_h
 
+#ifdef LUA_CROSS_COMPILER
+#include <stdint.h>
+#else
 #include "c_stdint.h"
+#endif
 
 #include "lobject.h"
 #include "lzio.h"

+ 4 - 5
app/lua/lvm.c

@@ -5,15 +5,14 @@
 */
 
 
-#include "c_stdio.h"
-#include "c_stdlib.h"
-#include "c_string.h"
-#include "c_math.h"
-
 #define lvm_c
 #define LUA_CORE
+#define LUAC_CROSS_FILE
 
 #include "lua.h"
+#include C_HEADER_STDIO
+#include C_HEADER_STRING
+#include C_HEADER_MATH
 
 #include "ldebug.h"
 #include "ldo.h"

+ 2 - 2
app/lua/lzio.c

@@ -5,12 +5,12 @@
 */
 
 
-#include "c_string.h"
-
 #define lzio_c
 #define LUA_CORE
+#define LUAC_CROSS_FILE
 
 #include "lua.h"
+#include C_HEADER_STRING
 
 #include "llimits.h"
 #include "lmem.h"

+ 1 - 0
app/modules/linit.c

@@ -7,6 +7,7 @@
 
 #define linit_c
 #define LUA_LIB
+#define LUAC_CROSS_FILE
 
 #include "lua.h"
 

+ 848 - 0
tools/build.lua

@@ -0,0 +1,848 @@
+-- eLua build system
+
+module( ..., package.seeall )
+
+local lfs = require "lfs"
+local sf = string.format
+utils = require "tools.utils"
+
+-------------------------------------------------------------------------------
+-- Various helpers
+
+-- Return the time of the last modification of the file
+local function get_ftime( path )
+  local t = lfs.attributes( path, 'modification' )
+  return t or -1
+end
+
+-- Check if a given target name is phony
+local function is_phony( target )
+  return target:find( "#phony" ) == 1
+end
+
+-- Return a string with $(key) replaced with 'value'
+local function expand_key( s, key, value )
+  if not value then return s end
+  local fmt = sf( "%%$%%(%s%%)", key )
+  return ( s:gsub( fmt, value ) )
+end
+
+-- Return a target name considering phony targets
+local function get_target_name( s )
+  if not is_phony( s ) then return s end
+end
+
+-- 'Liniarize' a file name by replacing its path separators indicators with '_'
+local function linearize_fname( s )
+  return ( s:gsub( "[\\/]", "__" ) )
+end
+
+-- Helper: transform a table into a string if needed
+local function table_to_string( t )
+  if not t then return nil end
+  if type( t ) == "table" then t = table.concat( t, " " ) end
+  return t
+end
+
+-- Helper: return the extended type of an object (takes into account __type)
+local function exttype( o )
+  local t = type( o )
+  if t == "table" and o.__type then t = o:__type() end
+  return t
+end
+
+---------------------------------------
+-- Table utils 
+-- (from http://lua-users.org/wiki/TableUtils)
+
+function table.val_to_str( v )
+  if "string" == type( v ) then
+    v = string.gsub( v, "\n", "\\n" )
+    if string.match( string.gsub(v,"[^'\"]",""), '^"+$' ) then
+      return "'" .. v .. "'"
+    end
+    return '"' .. string.gsub(v,'"', '\\"' ) .. '"'
+  else
+    return "table" == type( v ) and table.tostring( v ) or tostring( v )
+  end
+end
+
+function table.key_to_str ( k )
+  if "string" == type( k ) and string.match( k, "^[_%a][_%a%d]*$" ) then
+    return k
+  else
+    return "[" .. table.val_to_str( k ) .. "]"
+  end
+end
+
+function table.tostring( tbl )
+  local result, done = {}, {}
+  for k, v in ipairs( tbl ) do
+    table.insert( result, table.val_to_str( v ) )
+    done[ k ] = true
+  end
+  for k, v in pairs( tbl ) do
+    if not done[ k ] then
+      table.insert( result,
+        table.key_to_str( k ) .. "=" .. table.val_to_str( v ) )
+    end
+  end
+  return "{" .. table.concat( result, "," ) .. "}"
+end
+
+-------------------------------------------------------------------------------
+-- Dummy 'builder': simply checks the date of a file
+
+local _fbuilder = {}
+
+_fbuilder.new = function( target, dep )
+  local self = {}
+  setmetatable( self, { __index = _fbuilder } )
+  self.target = target
+  self.dep = dep
+  return self
+end
+
+_fbuilder.build = function( self )
+ -- Doesn't build anything but returns 'true' if the dependency is newer than
+ -- the target
+ if is_phony( self.target ) then
+   return true
+ else
+   return get_ftime( self.dep ) > get_ftime( self.target )
+ end
+end
+
+_fbuilder.target_name = function( self )
+  return get_target_name( self.dep )
+end
+
+-- Object type
+_fbuilder.__type = function()
+  return "_fbuilder"
+end
+
+-------------------------------------------------------------------------------
+-- Target object
+
+local _target = {}
+
+_target.new = function( target, dep, command, builder, ttype )
+  local self = {}
+  setmetatable( self, { __index = _target } )
+  self.target = target
+  self.command = command
+  self.builder = builder
+  builder:register_target( target, self )
+  self:set_dependencies( dep )
+  self.dep = self:_build_dependencies( self.origdep )
+  self.dont_clean = false
+  self.can_substitute_cmdline = false
+  self._force_rebuild = #self.dep == 0
+  builder.runlist[ target ] = false
+  self:set_type( ttype )
+  return self
+end
+
+-- Set dependencies as a string; actual dependencies are computed by _build_dependencies
+-- (below) when 'build' is called
+_target.set_dependencies = function( self, dep )
+  self.origdep = dep
+end
+
+-- Set the target type
+-- This is only for displaying actions
+_target.set_type = function( self, ttype )
+  local atable = { comp = { "[COMPILE]", 'blue' } , dep = { "[DEPENDS]", 'magenta' }, link = { "[LINK]", 'yellow' }, asm = { "[ASM]", 'white' } }
+  local tdata = atable[ ttype ]
+  if not tdata then
+    self.dispstr = is_phony( self.target ) and "[PHONY]" or "[TARGET]"
+    self.dispcol = 'green'
+  else
+    self.dispstr = tdata[ 1 ]
+    self.dispcol = tdata[ 2 ]
+  end
+end
+
+-- Set dependencies
+-- This uses a proxy table and returns string deps dynamically according
+-- to the targets currently registered in the builder
+_target._build_dependencies = function( self, dep )
+  -- Step 1: start with an array
+  if type( dep ) == "string" then dep = utils.string_to_table( dep ) end
+  -- Step 2: linearize "dep" array keeping targets
+  local filter = function( e )
+    local t = exttype( e )
+    return t ~= "_ftarget" and t ~= "_target"
+  end
+  dep = utils.linearize_array( dep, filter )
+  -- Step 3: strings are turned into _fbuilder objects if not found as targets;
+  -- otherwise the corresponding target object is used
+  for i = 1, #dep do
+    if type( dep[ i ] ) == 'string' then
+      local t = self.builder:get_registered_target( dep[ i ] )
+      dep[ i ] = t or _fbuilder.new( self.target, dep[ i ] )
+    end
+  end
+  return dep
+end
+
+-- Set pre-build function
+_target.set_pre_build_function = function( self, f )
+  self._pre_build_function = f
+end
+
+-- Set post-build function
+_target.set_post_build_function = function( self, f )
+  self._post_build_function = f
+end
+
+-- Force rebuild
+_target.force_rebuild = function( self, flag )
+  self._force_rebuild = flag
+end
+
+-- Set additional arguments to send to the builder function if it is a callable
+_target.set_target_args = function( self, args )
+  self._target_args = args
+end
+
+-- Function to execute in clean mode
+_target._cleaner = function( target, deps, tobj, disp_mode )
+  -- Clean the main target if it is not a phony target
+  local dprint = function( ... )
+    if disp_mode ~= "minimal" then print( ... ) end
+  end
+  if not is_phony( target ) then 
+    if tobj.dont_clean then
+      dprint( sf( "[builder] Target '%s' will not be deleted", target ) )
+      return 0
+    end
+    if disp_mode ~= "minimal" then io.write( sf( "[builder] Removing %s ... ", target ) ) end
+    if os.remove( target ) then dprint "done." else dprint "failed!" end
+  end
+  return 0
+end
+
+-- Build the given target
+_target.build = function( self )
+  if self.builder.runlist[ self.target ] then return end
+  local docmd = self:target_name() and lfs.attributes( self:target_name(), "mode" ) ~= "file"
+  docmd = docmd or self.builder.global_force_rebuild
+  local initdocmd = docmd
+  self.dep = self:_build_dependencies( self.origdep )
+  local depends, dep, previnit = '', self.dep, self.origdep
+  -- Iterate through all dependencies, execute each one in turn
+  local deprunner = function()
+    for i = 1, #dep do
+      local res = dep[ i ]:build()
+      docmd = docmd or res
+      local t = dep[ i ]:target_name()
+      if exttype( dep[ i ] ) == "_target" and t and not is_phony( self.target ) then
+        docmd = docmd or get_ftime( t ) > get_ftime( self.target )
+      end
+      if t then depends = depends .. t .. " " end
+    end
+  end
+  deprunner()
+  -- Execute the preb-build function if needed
+  if self._pre_build_function then self._pre_build_function( self, docmd ) end
+  -- If the dependencies changed as a result of running the pre-build function
+  -- run through them again
+  if previnit ~= self.origdep then
+    self.dep = self:_build_dependencies( self.origdep )
+    depends, dep, docmd = '', self.dep, initdocmd
+    deprunner()
+  end
+  -- If at least one dependency is new rebuild the target
+  docmd = docmd or self._force_rebuild or self.builder.clean_mode
+  local keep_flag = true
+  if docmd and self.command then
+    if self.builder.disp_mode ~= 'all' and self.builder.disp_mode ~= "minimal" and not self.builder.clean_mode then
+      io.write( utils.col_funcs[ self.dispcol ]( self.dispstr ) .. " " )
+    end
+    local cmd, code = self.command
+    if self.builder.clean_mode then cmd = _target._cleaner end
+    if type( cmd ) == 'string' then
+      cmd = expand_key( cmd, "TARGET", self.target )
+      cmd = expand_key( cmd, "DEPENDS", depends )
+      cmd = expand_key( cmd, "FIRST", dep[ 1 ]:target_name() )
+      if self.builder.disp_mode == 'all' then
+        print( cmd )
+      elseif self.builder.disp_mode ~= "minimal" then
+        print( self.target )
+      end
+      code = self:execute( cmd )
+    else
+      if not self.builder.clean_mode and self.builder.disp_mode ~= "all" and self.builder.disp_mode ~= "minimal" then
+        print( self.target )
+      end
+      code = cmd( self.target, self.dep, self.builder.clean_mode and self or self._target_args, self.builder.disp_mode )
+      if code == 1 then -- this means "mark target as 'not executed'"
+        keep_flag = false
+        code = 0
+      end
+    end
+    if code ~= 0 then 
+      print( utils.col_red( "[builder] Error building target" ) )
+      if self.builder.disp_mode ~= 'all' and type( cmd ) == "string" then
+        print( utils.col_red( "[builder] Last executed command was: " ) )
+        print( cmd )
+      end
+      os.exit( 1 ) 
+    end
+  end
+  -- Execute the post-build function if needed
+  if self._post_build_function then self._post_build_function( self, docmd ) end
+  -- Marked target as "already ran" so it won't run again
+  self.builder.runlist[ self.target ] = true
+  return docmd and keep_flag
+end
+
+-- Return the actual target name (taking into account phony targets)
+_target.target_name = function( self )
+  return get_target_name( self.target )
+end
+
+-- Restrict cleaning this target
+_target.prevent_clean = function( self, flag )
+  self.dont_clean = flag
+end
+
+-- Object type
+_target.__type = function()
+  return "_target"
+end
+
+_target.execute = function( self, cmd )
+  local code
+  if utils.is_windows() and #cmd > 8190 and self.can_substitute_cmdline then
+    -- Avoid cmd's maximum command line length limitation
+    local t = cmd:find( " " )
+    f = io.open( "tmpcmdline", "w" )
+    local rest = cmd:sub( t + 1 )
+    f:write( ( rest:gsub( "\\", "/" ) ) )
+    f:close()
+    cmd = cmd:sub( 1, t - 1 ) .. " @tmpcmdline"
+  end
+  local code = os.execute( cmd )
+  os.remove( "tmpcmdline" )
+  return code
+end
+
+_target.set_substitute_cmdline = function( self, flag )
+  self.can_substitute_cmdline = flag
+end
+
+-------------------------------------------------------------------------------
+-- Builder public interface
+
+builder = { KEEP_DIR = 0, BUILD_DIR_LINEARIZED = 1 }
+
+---------------------------------------
+-- Initialization and option handling
+
+-- Create a new builder object with the output in 'build_dir' and with the 
+-- specified compile, dependencies and link command
+builder.new = function( build_dir )
+  self = {}
+  setmetatable( self, { __index = builder } )
+  self.build_dir = build_dir or ".build"
+  self.exe_extension = utils.is_windows() and "exe" or ""
+  self.clean_mode = false
+  self.opts = utils.options_handler()
+  self.args = {}
+  self.user_args = {}
+  self.build_mode = self.KEEP_DIR
+  self.targets = {}
+  self.targetargs = {}
+  self._tlist = {}
+  self.runlist = {}
+  self.disp_mode = 'all'
+  self.cmdline_macros = {}
+  self.c_targets = {}
+  self.preprocess_mode = false
+  self.asm_mode = false
+  return self
+end
+
+-- Helper: create the build output directory
+builder._create_build_dir = function( self )
+  if self.build_dir_created then return end
+  if self.build_mode ~= self.KEEP_DIR then
+     -- Create builds directory if needed
+    local mode = lfs.attributes( self.build_dir, "mode" )
+    if not mode or mode ~= "directory" then
+      if not utils.full_mkdir( self.build_dir ) then
+        print( "[builder] Unable to create directory " .. self.build_dir )
+        os.exit( 1 )
+      end
+    end
+  end
+  self.build_dir_created = true
+end
+
+-- Add an options to the builder
+builder.add_option = function( self, name, help, default, data )
+  self.opts:add_option( name, help, default, data )
+end
+
+-- Initialize builder from the given command line
+builder.init = function( self, args )
+  -- Add the default options
+  local opts = self.opts
+  opts:add_option( "build_mode", 'choose location of the object files', self.KEEP_DIR,
+                   { keep_dir = self.KEEP_DIR, build_dir_linearized = self.BUILD_DIR_LINEARIZED } )
+  opts:add_option( "build_dir", 'choose build directory', self.build_dir )
+  opts:add_option( "disp_mode", 'set builder display mode', 'summary', { 'all', 'summary', 'minimal' } )
+  -- Apply default values to all options
+  for i = 1, opts:get_num_opts() do
+    local o = opts:get_option( i )
+    self.args[ o.name:upper() ] = o.default
+  end
+  -- Read and interpret command line
+  for i = 1, #args do
+    local a = args[ i ]
+    if a:upper() == "-C" then                   -- clean option (-c)
+      self.clean_mode = true
+    elseif a:upper() == '-H' then               -- help option (-h)
+      self:_show_help()
+      os.exit( 1 )
+    elseif a:upper() == "-E" then               -- preprocess
+      self.preprocess_mode = true
+    elseif a:upper() == "-S" then               -- generate assembler
+      self.asm_mode = true
+    elseif a:find( '-D' ) == 1 and #a > 2 then  -- this is a macro definition that will be auomatically added to the compiler flags
+      table.insert( self.cmdline_macros, a:sub( 3 ) ) 
+    elseif a:find( '=' ) then                   -- builder argument (key=value)
+      local k, v = opts:handle_arg( a )
+      if not k then
+        self:_show_help()
+        os.exit( 1 )
+      end
+      self.args[ k:upper() ] = v
+      self.user_args[ k:upper() ] = true
+    else                                        -- this must be the target name / target arguments
+      if self.targetname == nil then            
+        self.targetname = a
+      else
+        table.insert( self.targetargs, a )
+      end
+    end
+  end
+  -- Read back the default options
+  self.build_mode = self.args.BUILD_MODE
+  self.build_dir = self.args.BUILD_DIR
+  self.disp_mode = self.args.DISP_MODE
+end
+
+-- Return the value of the option with the given name
+builder.get_option = function( self, optname )
+  return self.args[ optname:upper() ]
+end
+
+-- Returns true if the given option was specified by the user on the command line, false otherwise
+builder.is_user_option = function( self, optname )
+  return self.user_args[ optname:upper() ]
+end
+
+-- Show builder help
+builder._show_help = function( self )
+  print( "[builder] Valid options:" )
+  print( "  -h: help (this text)" )
+  print( "  -c: clean target" )
+  print( "  -E: generate preprocessed output for single file targets" )
+  print( "  -S: generate assembler output for single file targets" )
+  self.opts:show_help()
+end
+
+---------------------------------------
+-- Builder configuration
+
+-- Set the compile command
+builder.set_compile_cmd = function( self, cmd )
+  self.comp_cmd = cmd
+end
+
+-- Set the link command
+builder.set_link_cmd = function( self, cmd )
+  self.link_cmd = cmd
+end
+
+-- Set the assembler command
+builder.set_asm_cmd = function( self, cmd )
+  self._asm_cmd = cmd
+end
+
+-- Set (actually force) the object file extension
+builder.set_object_extension = function( self, ext )
+  self.obj_extension = ext
+end
+
+-- Set (actually force) the executable file extension
+builder.set_exe_extension = function( self, ext )
+  self.exe_extension = ext
+end
+
+-- Set the clean mode
+builder.set_clean_mode = function( self, isclean )
+  self.clean_mode = isclean
+end
+
+-- Sets the build mode
+builder.set_build_mode = function( self, mode )
+  self.build_mode = mode
+end
+
+-- Set the build directory
+builder.set_build_dir = function( self, dir )
+  if self.build_dir_created then
+    print "[builder] Error: build directory already created"
+    os.exit( 1 )
+  end
+  self.build_dir = dir
+  self:_create_build_dir()
+end
+
+-- Return the current build directory
+builder.get_build_dir = function( self )
+  return self.build_dir
+end
+
+-- Return the target arguments
+builder.get_target_args = function( self )
+  return self.targetargs
+end
+
+-- Set a specific dependency generation command for the assembler
+-- Pass 'false' to skip dependency generation for assembler files
+builder.set_asm_dep_cmd = function( self, asm_dep_cmd ) 
+  self.asm_dep_cmd = asm_dep_cmd
+end
+
+-- Set a specific dependency generation command for the compiler
+-- Pass 'false' to skip dependency generation for C files
+builder.set_c_dep_cmd = function( self, c_dep_cmd )
+  self.c_dep_cmd = c_dep_cmd
+end
+
+-- Save the builder configuration for a given component to a string
+builder._config_to_string = function( self, what )
+  local ctable = {}
+  local state_fields 
+  if what == 'comp' then
+    state_fields = { 'comp_cmd', '_asm_cmd', 'c_dep_cmd', 'asm_dep_cmd', 'obj_extension' }
+  elseif what == 'link' then
+    state_fields = { 'link_cmd' }
+  else
+    print( sf( "Invalid argument '%s' to _config_to_string", what ) )
+    os.exit( 1 )
+  end
+  utils.foreach( state_fields, function( k, v ) ctable[ v ] = self[ v ] end )
+  return table.tostring( ctable )
+end
+
+-- Check the configuration of the given component against the previous one
+-- Return true if the configuration has changed
+builder._compare_config = function( self, what )
+  local res = false
+  local crtstate = self:_config_to_string( what )
+  if not self.clean_mode then
+    local fconf = io.open( self.build_dir .. utils.dir_sep .. ".builddata." .. what, "rb" )
+    if fconf then
+      local oldstate = fconf:read( "*a" )
+      fconf:close()
+      if oldstate:lower() ~= crtstate:lower() then res = true end
+    end
+  end
+  -- Write state to build dir
+  fconf = io.open( self.build_dir .. utils.dir_sep .. ".builddata." .. what, "wb" )
+  if fconf then
+    fconf:write( self:_config_to_string( what ) )
+    fconf:close()
+  end
+  return res
+end
+
+-- Sets the way commands are displayed
+builder.set_disp_mode = function( self, mode )
+  mode = mode:lower()
+  if mode ~= 'all' and mode ~= 'summary' and mode ~= "minimal" then
+    print( sf( "[builder] Invalid display mode '%s'", mode ) )
+    os.exit( 1 )
+  end
+  self.disp_mode = mode
+end
+
+---------------------------------------
+-- Command line builders
+
+-- Internal helper
+builder._generic_cmd = function( self, args )
+  local compcmd = args.compiler or "gcc"
+  compcmd = compcmd .. " "
+  local flags = type( args.flags ) == 'table' and table_to_string( utils.linearize_array( args.flags ) ) or args.flags
+  local defines = type( args.defines ) == 'table' and table_to_string( utils.linearize_array( args.defines ) ) or args.defines
+  local includes = type( args.includes ) == 'table' and table_to_string( utils.linearize_array( args.includes ) ) or args.includes
+  local comptype = table_to_string( args.comptype ) or "-c"
+  compcmd = compcmd .. utils.prepend_string( defines, "-D" )
+  compcmd = compcmd .. utils.prepend_string( includes, "-I" )
+  return compcmd .. flags .. " " .. comptype .. " -o $(TARGET) $(FIRST)"
+end
+
+-- Return a compile command based on the specified args
+builder.compile_cmd = function( self, args )
+  args.defines = { args.defines, self.cmdline_macros }
+  if self.preprocess_mode then
+    args.comptype = "-E"
+  elseif self.asm_mode then
+    args.comptype = "-S"
+  else
+    args.comptype = "-c"
+  end
+  return self:_generic_cmd( args )
+end
+
+-- Return an assembler command based on the specified args
+builder.asm_cmd = function( self, args )
+  args.defines = { args.defines, self.cmdline_macros }
+  args.compiler = args.assembler
+  args.comptype = self.preprocess_mode and "-E" or "-c"
+  return self:_generic_cmd( args )
+end
+
+-- Return a link command based on the specified args
+builder.link_cmd = function( self, args )
+  local flags = type( args.flags ) == 'table' and table_to_string( utils.linearize_array( args.flags ) ) or args.flags
+  local libraries = type( args.libraries ) == 'table' and table_to_string( utils.linearize_array( args.libraries ) ) or args.libraries
+  local linkcmd = args.linker or "gcc"
+  linkcmd = linkcmd .. " " .. flags .. " -o $(TARGET) $(DEPENDS)"
+  linkcmd = linkcmd .. " " .. utils.prepend_string( libraries, "-l" )
+  return linkcmd
+end
+
+---------------------------------------
+-- Target handling
+
+-- Create a return a new C to object target
+builder.c_target = function( self, target, deps, comp_cmd )
+  return _target.new( target, deps, comp_cmd or self.comp_cmd, self, 'comp' )
+end
+
+-- Create a return a new ASM to object target
+builder.asm_target = function( self, target, deps, asm_cmd )
+  return _target.new( target, deps, asm_cmd or self._asm_cmd, self, 'asm' )
+end
+
+-- Return the name of a dependency file name corresponding to a C source
+builder.get_dep_filename = function( self, srcname )
+  return utils.replace_extension( self.build_dir .. utils.dir_sep .. linearize_fname( srcname ), "d" )
+end
+
+-- Create a return a new C dependency target
+builder.dep_target = function( self, dep, depdeps, dep_cmd )
+  local depname = self:get_dep_filename( dep )
+  return _target.new( depname, depdeps, dep_cmd, self, 'dep' )
+end
+
+-- Create and return a new link target
+builder.link_target = function( self, out, dep, link_cmd )
+  local path, ext = utils.split_ext( out )
+  if not ext and self.exe_extension and #self.exe_extension > 0 then
+    out = out .. self.exe_extension
+  end
+  local t = _target.new( out, dep, link_cmd or self.link_cmd, self, 'link' )
+  if self:_compare_config( 'link' ) then t:force_rebuild( true ) end
+  t:set_substitute_cmdline( true )
+  return t
+end
+
+-- Create and return a new generic target
+builder.target = function( self, dest_target, deps, cmd )
+  return _target.new( dest_target, deps, cmd, self )
+end
+
+-- Register a target (called from _target.new)
+builder.register_target = function( self, name, obj )
+  self._tlist[ name:gsub( "\\", "/" ) ] = obj
+end
+
+-- Returns a registered target (nil if not found)
+builder.get_registered_target = function( self, name )
+  return self._tlist[ name:gsub( "\\", "/" ) ] 
+end
+
+---------------------------------------
+-- Actual building functions
+
+-- Return the object name corresponding to a source file name
+builder.obj_name = function( self, name, ext )
+  local r = ext or self.obj_extension
+  if not r then
+    r = utils.is_windows() and "obj" or "o"
+  end
+  local objname = utils.replace_extension( name, r )
+  -- KEEP_DIR: object file in the same directory as source file
+  -- BUILD_DIR_LINEARIZED: object file in the build directory, linearized filename
+  if self.build_mode == self.KEEP_DIR then 
+    return objname
+  elseif self.build_mode == self.BUILD_DIR_LINEARIZED then
+    return self.build_dir .. utils.dir_sep .. linearize_fname( objname )
+  end
+end
+
+-- Read and interpret dependencies for each file specified in "ftable"
+-- "ftable" is either a space-separated string with all the source files or an array
+builder.read_depends = function( self, ftable )
+  if type( ftable ) == 'string' then ftable = utils.string_to_table( ftable ) end
+  -- Read dependency data
+  local dtable = {}
+  for i = 1, #ftable do
+    local f = io.open( self:get_dep_filename( ftable[ i ] ), "rb" )
+    local lines = ftable[ i ]
+    if f then
+      lines = f:read( "*a" )
+      f:close()
+      lines = lines:gsub( "\n", " " ):gsub( "\\%s+", " " ):gsub( "%s+", " " ):gsub( "^.-: (.*)", "%1" )
+    end
+    dtable[ ftable[ i ] ] = lines
+  end
+  return dtable
+end
+
+-- Create and return compile targets for the given sources
+builder.create_compile_targets = function( self, ftable, res )
+  if type( ftable ) == 'string' then ftable = utils.string_to_table( ftable ) end
+  res = res or {}
+  ccmd, oname = "-c", "o"
+  if self.preprocess_mode then
+    ccmd, oname = '-E', "pre"
+  elseif self.asm_mode then
+    ccmd, oname = '-S', 's'
+  end
+  -- Build dependencies for all targets
+  for i = 1, #ftable do
+    local isasm = ftable[ i ]:find( "%.c$" ) == nil
+    -- Skip assembler targets if 'asm_dep_cmd' is set to 'false'
+    -- Skip C targets if 'c_dep_cmd' is set to 'false'
+    local skip = isasm and self.asm_dep_cmd == false
+    skip = skip or ( not isasm and self.c_dep_cmd == false )
+    local deps = self:get_dep_filename( ftable[ i ] )
+    local target
+    if not isasm then
+      local depcmd = skip and self.comp_cmd or ( self.c_dep_cmd or self.comp_cmd:gsub( ccmd .. " ", sf( ccmd .. " -MD -MF %s ", deps ) ) )
+      target = self:c_target( self:obj_name( ftable[ i ], oname ), { self:get_registered_target( deps ) or ftable[ i ] }, depcmd )
+    else
+      local depcmd = skip and self._asm_cmd or ( self.asm_dep_cmd or self._asm_cmd:gsub( ccmd .. " ", sf( ccmd .. " -MD -MF %s ", deps ) ) )
+      target = self:asm_target( self:obj_name( ftable[ i ], oname ), { self:get_registered_target( deps ) or ftable[ i ] }, depcmd )
+    end
+    -- Pre build step: replace dependencies with the ones from the compiler generated dependency file
+    local dprint = function( ... ) if self.disp_mode ~= "minimal" then print( ... ) end end
+    if not skip then
+      target:set_pre_build_function( function( t, _ )
+        if not self.clean_mode then
+          local fres = self:read_depends( ftable[ i ] )
+          local fdeps = fres[ ftable[ i ] ]
+          if #fdeps:gsub( "%s+", "" ) == 0 then fdeps = ftable[ i ] end
+          t:set_dependencies( fdeps )
+        else
+          if self.disp_mode ~= "minimal" then io.write( sf( "[builder] Removing %s ... ", deps ) ) end
+          if os.remove( deps ) then dprint "done." else dprint "failed!" end
+        end
+      end )
+    end
+    target.srcname = ftable[ i ]
+    -- TODO: check clean mode?
+    if not isasm then self.c_targets[ #self.c_targets + 1 ] = target end
+    table.insert( res, target )
+  end
+  return res
+end
+
+-- Add a target to the list of builder targets
+builder.add_target = function( self, target, help, alias )
+  self.targets[ target.target ] = { target = target, help = help }
+  alias = alias or {}
+  for _, v in ipairs( alias ) do
+    self.targets[ v ] = { target = target, help = help }
+  end
+  return target
+end
+
+-- Make a target the default one
+builder.default = function( self, target )
+  self.deftarget = target.target
+  self.targets.default = { target = target, help = "default target" }
+end
+
+-- Build everything
+builder.build = function( self, target )
+  local t = self.targetname or self.deftarget
+  if not t then
+    print( utils.col_red( "[builder] Error: build target not specified" ) )
+    os.exit( 1 )
+  end
+  local trg 
+  -- Look for single targets (C source files)
+  for _, ct in pairs( self.c_targets ) do
+    if ct.srcname == t then
+      trg = ct
+      break
+    end
+  end
+  if not trg then
+    if not self.targets[ t ] then
+      print( sf( "[builder] Error: target '%s' not found", t ) )
+      print( "Available targets: " )
+      print( "  all source files" )
+      for k, v in pairs( self.targets ) do
+        if not is_phony( k ) then 
+          print( sf( "  %s - %s", k, v.help or "(no help available)" ) )
+        end
+      end
+      if self.deftarget and not is_phony( self.deftarget ) then
+        print( sf( "Default target is '%s'", self.deftarget ) )
+      end
+      os.exit( 1 )
+    else
+      if self.preprocess_mode or self.asm_mode then
+        print( "[builder] Error: preprocess (-E) or asm (-S) works only with single file targets." )
+        os.exit( 1 )
+      end
+      trg = self.targets[ t ].target
+    end
+  end
+  self:_create_build_dir()
+  -- At this point check if we have a change in the state that would require a rebuild
+  if self:_compare_config( 'comp' ) then
+    print( utils.col_yellow( "[builder] Forcing rebuild due to configuration change." ) )
+    self.global_force_rebuild = true
+  else
+    self.global_force_rebuild = false
+  end
+  -- Do the actual build
+  local res = trg:build()
+  if not res then print( utils.col_yellow( sf( '[builder] %s: up to date', t ) ) ) end
+  if self.clean_mode then 
+    os.remove( self.build_dir .. utils.dir_sep .. ".builddata.comp" ) 
+    os.remove( self.build_dir .. utils.dir_sep .. ".builddata.link" ) 
+  end
+  print( utils.col_yellow( "[builder] Done building target." ) )
+  return res
+end
+
+-- Create dependencies, create object files, link final object
+builder.make_exe_target = function( self, target, file_list )
+  local odeps = self:create_compile_targets( file_list )
+  local exetarget = self:link_target( target, odeps )
+  self:default( self:add_target( exetarget ) )
+  return exetarget
+end
+
+-------------------------------------------------------------------------------
+-- Other exported functions
+
+function new_builder( build_dir )
+  return builder.new( build_dir )
+end
+

+ 32 - 0
tools/cross-lua.lua

@@ -0,0 +1,32 @@
+local args = { ... }
+local b = require "tools.build"
+local builder = b.new_builder( ".build/cross-lua" )
+local utils = b.utils
+local sf = string.format
+builder:init( args )
+builder:set_build_mode( builder.BUILD_DIR_LINEARIZED )
+ 
+local output = 'luac.cross'
+local cdefs = '-DLUA_CROSS_COMPILER -O2'
+
+-- Lua source files and include path
+local lua_files = [[
+    lapi.c lauxlib.c lbaselib.c lcode.c ldblib.c ldebug.c ldo.c ldump.c 
+    lfunc.c lgc.c llex.c lmathlib.c lmem.c loadlib.c lobject.c lopcodes.c  
+    lparser.c lrotable.c lstate.c lstring.c lstrlib.c ltable.c ltablib.c 
+    ltm.c  lundump.c lvm.c lzio.c 
+    luac_cross/luac.c luac_cross/loslib.c luac_cross/print.c
+    ../modules/linit.c
+  ]]
+lua_files = lua_files:gsub( "\n" , "" )
+local lua_full_files = utils.prepend_path( lua_files, "app/lua" )
+local local_include = "-Iapp/include -Iinclude -Iapp/lua"
+
+-- Compiler/linker options
+builder:set_compile_cmd( sf( "gcc -O2 %s -Wall %s -c $(FIRST) -o $(TARGET)", local_include, cdefs ) )
+builder:set_link_cmd( "gcc -o $(TARGET) $(DEPENDS) -lm" )
+
+-- Build everything
+builder:make_exe_target( output, lua_full_files )
+builder:build()
+

+ 425 - 0
tools/utils.lua

@@ -0,0 +1,425 @@
+-- Generic utility functions
+
+module( ..., package.seeall )
+
+local lfs = require "lfs"
+local sf = string.format
+-- Taken from Lake
+dir_sep = package.config:sub( 1, 1 )
+is_os_windows = dir_sep == '\\'
+
+-- Converts a string with items separated by 'sep' into a table
+string_to_table = function( s, sep )
+  if type( s ) ~= "string" then return end
+  sep = sep or ' '
+  if s:sub( -1, -1 ) ~= sep then s = s .. sep end
+  s = s:gsub( sf( "^%s*", sep ), "" )
+  local t = {}
+  local fmt = sf( "(.-)%s+", sep )
+  for w in s:gmatch( fmt ) do table.insert( t, w ) end
+  return t
+end
+
+-- Split a file name into 'path part' and 'extension part'
+split_ext = function( s )
+  local pos
+  for i = #s, 1, -1 do
+    if s:sub( i, i ) == "." then
+      pos = i
+      break
+    end
+  end
+  if not pos or s:find( dir_sep, pos + 1 ) then return s end
+  return s:sub( 1, pos - 1 ), s:sub( pos )
+end
+
+-- Replace the extension of a given file name
+replace_extension = function( s, newext )
+  local p, e = split_ext( s )
+  if e then 
+    if newext and #newext > 0 then 
+      s = p .. "." .. newext
+    else
+      s = p
+    end
+  end
+  return s
+end
+
+-- Return 'true' if building from Windows, false otherwise
+is_windows = function()
+  return is_os_windows
+end
+
+-- Prepend each component of a 'pat'-separated string with 'prefix'
+prepend_string = function( s, prefix, pat )  
+  if not s or #s == 0 then return "" end
+  pat = pat or ' '
+  local res = ''
+  local st = string_to_table( s, pat )
+  foreach( st, function( k, v ) res = res .. prefix .. v .. " " end )
+  return res
+end
+
+-- Like above, but consider 'prefix' a path
+prepend_path = function( s, prefix, pat )
+  return prepend_string( s, prefix .. dir_sep, pat )
+end
+
+-- full mkdir: create all the paths needed for a multipath
+full_mkdir = function( path )
+  local ptables = string_to_table( path, dir_sep )
+  local p, res = ''
+  for i = 1, #ptables do
+    p = ( i ~= 1 and p .. dir_sep or p ) .. ptables[ i ]
+    res = lfs.mkdir( p )
+  end
+  return res
+end
+
+-- Concatenate the given paths to form a complete path
+concat_path = function( paths )
+  return table.concat( paths, dir_sep )
+end
+
+-- Return true if the given array contains the given element, false otherwise
+array_element_index = function( arr, element )
+  for i = 1, #arr do
+    if arr[ i ] == element then return i end
+  end
+end
+
+-- Linearize an array with (possibly) embedded arrays into a simple array
+_linearize_array = function( arr, res, filter )
+  if type( arr ) ~= "table" then return end
+  for i = 1, #arr do
+    local e = arr[ i ]
+    if type( e ) == 'table' and filter( e ) then
+      _linearize_array( e, res, filter )
+    else
+      table.insert( res, e )
+    end
+  end 
+end
+
+linearize_array = function( arr, filter )
+  local res = {}
+  filter = filter or function( v ) return true end
+  _linearize_array( arr, res, filter )
+  return res
+end
+
+-- Return an array with the keys of a table
+table_keys = function( t )
+  local keys = {}
+  foreach( t, function( k, v ) table.insert( keys, k ) end )
+  return keys
+end
+
+-- Return an array with the values of a table
+table_values = function( t )
+  local vals = {}
+  foreach( t, function( k, v ) table.insert( vals, v ) end )
+  return vals
+end
+
+-- Returns true if 'path' is a regular file, false otherwise
+is_file = function( path )
+  return lfs.attributes( path, "mode" ) == "file"
+end
+
+-- Returns true if 'path' is a directory, false otherwise
+is_dir = function( path )
+  return lfs.attributes( path, "mode" ) == "directory"
+end
+
+-- Return a list of files in the given directory matching a given mask
+get_files = function( path, mask, norec, level )
+  local t = ''
+  level = level or 0
+  for f in lfs.dir( path ) do
+    local fname = path .. dir_sep .. f
+    if lfs.attributes( fname, "mode" ) == "file" then
+      local include
+      if type( mask ) == "string" then
+        include = fname:find( mask )
+      else
+        include = mask( fname )
+      end
+      if include then t = t .. ' ' .. fname end
+    elseif lfs.attributes( fname, "mode" ) == "directory" and not fname:find( "%.+$" ) and not norec then
+      t = t .. " " .. get_files( fname, mask, norec, level + 1 )
+    end
+  end
+  return level > 0 and t or t:gsub( "^%s+", "" )
+end
+
+-- Check if the given command can be executed properly
+check_command = function( cmd )
+  local res = os.execute( cmd .. " > .build.temp 2>&1" )
+  os.remove( ".build.temp" )
+  return res
+end
+
+-- Execute a command and capture output
+-- From: http://stackoverflow.com/a/326715/105950
+exec_capture = function( cmd, raw )
+  local f = assert(io.popen(cmd, 'r'))
+  local s = assert(f:read('*a'))
+   f:close()
+   if raw then return s end
+   s = string.gsub(s, '^%s+', '')
+   s = string.gsub(s, '%s+$', '')
+   s = string.gsub(s, '[\n\r]+', ' ')
+   return s
+end
+
+-- Execute the given command for each value in a table
+foreach = function ( t, cmd )
+  if type( t ) ~= "table" then return end
+  for k, v in pairs( t ) do cmd( k, v ) end
+end
+
+-- Generate header with the given #defines, return result as string
+gen_header_string = function( name, defines )
+  local s = "// eLua " .. name:lower() .. " definition\n\n"
+  s = s .. "#ifndef __" .. name:upper() .. "_H__\n"
+  s = s .. "#define __" .. name:upper() .. "_H__\n\n"
+
+  for key,value in pairs(defines) do 
+     s = s .. string.format("#define   %-25s%-19s\n",key:upper(),value)
+  end
+
+  s = s .. "\n#endif\n"
+  return s
+end
+
+-- Generate header with the given #defines, save result to file
+gen_header_file = function( name, defines )
+  local hname = concat_path{ "inc", name:lower() .. ".h" }
+  local h = assert( io.open( hname, "w" ) )
+  h:write( gen_header_string( name, defines ) )
+  h:close()
+end
+
+-- Remove the given elements from an array
+remove_array_elements = function( arr, del )
+  del = istable( del ) and del or { del }
+  foreach( del, function( k, v )
+    local pos = array_element_index( arr, v )
+    if pos then table.remove( arr, pos ) end
+  end )
+end
+
+-- Remove a directory recusively
+-- USE WITH CARE!! Doesn't do much checks :)
+rmdir_rec = function ( dirname )
+  if lfs.attributes( dirname, "mode" ) ~= "directory" then return end
+  for f in lfs.dir( dirname ) do
+    local ename = string.format( "%s/%s", dirname, f )
+    local attrs = lfs.attributes( ename )
+    if attrs.mode == 'directory' and f ~= '.' and f ~= '..' then
+      rmdir_rec( ename ) 
+    elseif attrs.mode == 'file' or attrs.mode == 'named pipe' or attrs.mode == 'link' then
+      os.remove( ename )
+    end
+  end
+  lfs.rmdir( dirname )
+end
+
+-- Concatenates the second table into the first one
+concat_tables = function( dst, src )
+  foreach( src, function( k, v ) dst[ k ] = v end )
+end
+
+-------------------------------------------------------------------------------
+-- Color-related funtions
+-- Currently disabled when running in Windows
+-- (they can be enabled by setting WIN_ANSI_TERM)
+
+local dcoltable = { 'black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white' }
+local coltable = {}
+foreach( dcoltable, function( k, v ) coltable[ v ] = k - 1 end )
+
+local _col_builder = function( col )
+  local _col_maker = function( s )
+    if is_os_windows and not os.getenv( "WIN_ANSI_TERM" ) then
+      return s
+    else
+      return( sf( "\027[%d;1m%s\027[m", coltable[ col ] + 30, s ) )
+    end
+  end
+  return _col_maker
+end
+
+col_funcs = {}
+foreach( coltable, function( k, v ) 
+  local fname = "col_" .. k
+  _G[ fname ] = _col_builder( k ) 
+  col_funcs[ k ] = _G[ fname ]
+end )
+
+-------------------------------------------------------------------------------
+-- Option handling
+
+local options = {}
+
+options.new = function()
+  local self = {}
+  self.options = {}
+  setmetatable( self, { __index = options } )
+  return self
+end
+
+-- Argument validator: boolean value
+options._bool_validator = function( v )
+  if v == '0' or v:upper() == 'FALSE' then
+    return false
+  elseif v == '1' or v:upper() == 'TRUE' then
+    return true
+  end
+end
+
+-- Argument validator: choice value
+options._choice_validator = function( v, allowed )
+  for i = 1, #allowed do
+    if v:upper() == allowed[ i ]:upper() then return allowed[ i ] end
+  end
+end
+
+-- Argument validator: choice map (argument value maps to something)
+options._choice_map_validator = function( v, allowed )
+  for k, value in pairs( allowed ) do
+    if v:upper() == k:upper() then return value end
+  end
+end
+
+-- Argument validator: string value (no validation)
+options._string_validator = function( v )
+  return v
+end
+
+-- Argument printer: boolean value
+options._bool_printer = function( o )
+  return "true|false", o.default and "true" or "false"
+end
+
+-- Argument printer: choice value
+options._choice_printer = function( o )
+  local clist, opts  = '', o.data
+  for i = 1, #opts do
+    clist = clist .. ( i ~= 1 and "|" or "" ) .. opts[ i ]
+  end
+  return clist, o.default
+end
+
+-- Argument printer: choice map printer
+options._choice_map_printer = function( o )
+  local clist, opts, def = '', o.data
+  local i = 1
+  for k, v in pairs( opts ) do
+    clist = clist .. ( i ~= 1 and "|" or "" ) .. k
+    if o.default == v then def = k end
+    i = i + 1
+  end
+  return clist, def
+end
+
+-- Argument printer: string printer
+options._string_printer = function( o )
+  return nil, o.default
+end
+
+-- Add an option of the specified type
+options._add_option = function( self, optname, opttype, help, default, data )
+  local validators = 
+  { 
+    string = options._string_validator, choice = options._choice_validator, 
+    boolean = options._bool_validator, choice_map = options._choice_map_validator
+  }
+  local printers = 
+  { 
+    string = options._string_printer, choice = options._choice_printer, 
+    boolean = options._bool_printer, choice_map = options._choice_map_printer
+  }
+  if not validators[ opttype ] then
+    print( sf( "[builder] Invalid option type '%s'", opttype ) )
+    os.exit( 1 )
+  end
+  table.insert( self.options, { name = optname, help = help, validator = validators[ opttype ], printer = printers[ opttype ], data = data, default = default } )
+end
+
+-- Find an option with the given name
+options._find_option = function( self, optname )
+  for i = 1, #self.options do
+    local o = self.options[ i ]
+    if o.name:upper() == optname:upper() then return self.options[ i ] end
+  end
+end
+
+-- 'add option' helper (automatically detects option type)
+options.add_option = function( self, name, help, default, data )
+  local otype
+  if type( default ) == 'boolean' then
+    otype = 'boolean'
+  elseif data and type( data ) == 'table' and #data == 0 then
+    otype = 'choice_map'
+  elseif data and type( data ) == 'table' then
+    otype = 'choice'
+    data = linearize_array( data )
+  elseif type( default ) == 'string' then
+    otype = 'string'
+  else
+    print( sf( "Error: cannot detect option type for '%s'", name ) )
+    os.exit( 1 )
+  end
+  self:_add_option( name, otype, help, default, data )
+end
+
+options.get_num_opts = function( self )
+  return #self.options
+end
+
+options.get_option = function( self, i )
+  return self.options[ i ]
+end
+
+-- Handle an option of type 'key=value'
+-- Returns both the key and the value or nil for error
+options.handle_arg = function( self, a )
+  local si, ei, k, v = a:find( "([^=]+)=(.*)$" )
+  if not k or not v then 
+    print( sf( "Error: invalid syntax in '%s'", a ) )
+    return
+  end
+  local opt = self:_find_option( k )
+  if not opt then
+    print( sf( "Error: invalid option '%s'", k ) )
+    return
+  end
+  local optv = opt.validator( v, opt.data )
+  if optv == nil then
+    print( sf( "Error: invalid value '%s' for option '%s'", v, k ) )
+    return
+  end
+  return k, optv
+end
+
+-- Show help for all the registered options
+options.show_help = function( self )
+  for i = 1, #self.options do
+    local o = self.options[ i ]
+    print( sf( "\n  %s: %s", o.name, o.help ) )
+    local values, default = o.printer( o )
+    if values then
+      print( sf( "    Possible values: %s", values ) )
+    end
+    print( sf( "    Default value: %s", default or "none (changes at runtime)" ) )
+  end
+end
+
+-- Create a new option handler
+function options_handler()
+  return options.new()
+end
+