/*!The Make-like Build Utility based on Lua
*
* XMake is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* XMake is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with XMake;
* If not, see http://www.gnu.org/licenses/
*
* Copyright (C) 2015 - 2016, ruki All rights reserved.
*
* @author ruki
* @file machine.c
*
*/
/* //////////////////////////////////////////////////////////////////////////////////////
* trace
*/
#define TB_TRACE_MODULE_NAME "machine"
#define TB_TRACE_MODULE_DEBUG (1)
/* //////////////////////////////////////////////////////////////////////////////////////
* includes
*/
#include "xmake.h"
#include "luajit/luajit.h"
#ifdef TB_CONFIG_OS_WINDOWS
# include
#endif
/* //////////////////////////////////////////////////////////////////////////////////////
* types
*/
// the machine impl type
typedef struct __xm_machine_impl_t
{
// the lua
lua_State* lua;
}xm_machine_impl_t;
/* //////////////////////////////////////////////////////////////////////////////////////
* declaration
*/
// the os functions
tb_int_t xm_os_find(lua_State* lua);
tb_int_t xm_os_uuid(lua_State* lua);
tb_int_t xm_os_isdir(lua_State* lua);
tb_int_t xm_os_rmdir(lua_State* lua);
tb_int_t xm_os_mkdir(lua_State* lua);
tb_int_t xm_os_cpdir(lua_State* lua);
tb_int_t xm_os_chdir(lua_State* lua);
tb_int_t xm_os_mtime(lua_State* lua);
tb_int_t xm_os_mclock(lua_State* lua);
tb_int_t xm_os_curdir(lua_State* lua);
tb_int_t xm_os_tmpdir(lua_State* lua);
tb_int_t xm_os_isfile(lua_State* lua);
tb_int_t xm_os_rmfile(lua_State* lua);
tb_int_t xm_os_cpfile(lua_State* lua);
tb_int_t xm_os_rename(lua_State* lua);
tb_int_t xm_os_exists(lua_State* lua);
tb_int_t xm_os_setenv(lua_State* lua);
tb_int_t xm_os_getenv(lua_State* lua);
tb_int_t xm_os_emptydir(lua_State* lua);
tb_int_t xm_os_strerror(lua_State* lua);
// the path functions
tb_int_t xm_path_relative(lua_State* lua);
tb_int_t xm_path_absolute(lua_State* lua);
tb_int_t xm_path_translate(lua_State* lua);
tb_int_t xm_path_is_absolute(lua_State* lua);
// the string functions
tb_int_t xm_string_strcmp(lua_State* lua);
tb_int_t xm_string_endswith(lua_State* lua);
tb_int_t xm_string_startswith(lua_State* lua);
// the process functions
tb_int_t xm_process_open(lua_State* lua);
tb_int_t xm_process_openv(lua_State* lua);
tb_int_t xm_process_wait(lua_State* lua);
tb_int_t xm_process_waitlist(lua_State* lua);
tb_int_t xm_process_close(lua_State* lua);
/* //////////////////////////////////////////////////////////////////////////////////////
* globals
*/
// the os functions
static luaL_Reg const g_os_functions[] =
{
{ "find", xm_os_find }
, { "uuid", xm_os_uuid }
, { "isdir", xm_os_isdir }
, { "rmdir", xm_os_rmdir }
, { "mkdir", xm_os_mkdir }
, { "cpdir", xm_os_cpdir }
, { "chdir", xm_os_chdir }
, { "mtime", xm_os_mtime }
, { "mclock", xm_os_mclock }
, { "curdir", xm_os_curdir }
, { "tmpdir", xm_os_tmpdir }
, { "isfile", xm_os_isfile }
, { "rmfile", xm_os_rmfile }
, { "cpfile", xm_os_cpfile }
, { "rename", xm_os_rename }
, { "exists", xm_os_exists }
, { "setenv", xm_os_setenv }
, { "getenv", xm_os_getenv }
, { "emptydir", xm_os_emptydir }
, { "strerror", xm_os_strerror }
, { tb_null, tb_null }
};
// the path functions
static luaL_Reg const g_path_functions[] =
{
{ "relative", xm_path_relative }
, { "absolute", xm_path_absolute }
, { "translate", xm_path_translate }
, { "is_absolute", xm_path_is_absolute }
, { tb_null, tb_null }
};
// the string functions
static luaL_Reg const g_string_functions[] =
{
{ "strcmp", xm_string_strcmp }
, { "endswith", xm_string_endswith }
, { "startswith", xm_string_startswith }
, { tb_null, tb_null }
};
// the process functions
static luaL_Reg const g_process_functions[] =
{
{ "open", xm_process_open }
, { "openv", xm_process_openv }
, { "wait", xm_process_wait }
, { "waitlist", xm_process_waitlist }
, { "close", xm_process_close }
, { tb_null, tb_null }
};
/* //////////////////////////////////////////////////////////////////////////////////////
* private implementation
*/
static tb_bool_t xm_machine_main_save_arguments(xm_machine_impl_t* impl, tb_int_t argc, tb_char_t** argv)
{
// check
tb_assert_and_check_return_val(impl && impl->lua && argc >= 1 && argv, tb_false);
// put a new table into the stack
lua_newtable(impl->lua);
// save all arguments to the new table
tb_int_t i = 0;
for (i = 1; i < argc; i++)
{
// table_new[table.getn(table_new) + 1] = argv[i]
lua_pushstring(impl->lua, argv[i]);
lua_rawseti(impl->lua, -2, luaL_getn(impl->lua, -2) + 1);
}
// _ARGV = table_new
lua_setglobal(impl->lua, "_ARGV");
// ok
return tb_true;
}
static tb_bool_t xm_machine_main_get_program_directory(xm_machine_impl_t* impl, tb_char_t* path, tb_size_t maxn)
{
// check
tb_assert_and_check_return_val(impl && path && maxn, tb_false);
// done
tb_bool_t ok = tb_false;
do
{
// get it from the environment variable first
tb_char_t data[TB_PATH_MAXN] = {0};
if (tb_environment_first("XMAKE_PROGRAM_DIR", data, sizeof(data)) && tb_path_absolute(data, path, maxn))
{
// ok
ok = tb_true;
break;
}
#ifdef TB_CONFIG_OS_WINDOWS
// get the program directory
tb_size_t size = GetModuleFileName(tb_null, path, maxn);
tb_assert_and_check_break(size < maxn);
// end
path[size] = '\0';
// get the directory
while (size-- > 0)
{
if (path[size] == '\\')
{
path[size] = '\0';
break;
}
}
// ok
ok = tb_true;
#endif
} while (0);
// ok?
if (ok)
{
// trace
tb_trace_d("program: %s", path);
// save the directory to the global variable: _PROGRAM_DIR
lua_pushstring(impl->lua, path);
lua_setglobal(impl->lua, "_PROGRAM_DIR");
}
// ok?
return ok;
}
static tb_bool_t xm_machine_main_get_project_directory(xm_machine_impl_t* impl, tb_char_t* path, tb_size_t maxn)
{
// check
tb_assert_and_check_return_val(impl && path && maxn, tb_false);
// done
tb_bool_t ok = tb_false;
do
{
// attempt to get it from the environment variable first
tb_char_t data[TB_PATH_MAXN] = {0};
if ( !tb_environment_first("XMAKE_PROJECT_DIR", data, sizeof(data))
|| !tb_path_absolute(data, path, maxn))
{
// get it from the current directory
if (!tb_directory_current(path, maxn)) break;
}
// trace
tb_trace_d("project: %s", path);
// save the directory to the global variable: _PROJECT_DIR
lua_pushstring(impl->lua, path);
lua_setglobal(impl->lua, "_PROJECT_DIR");
// ok
ok = tb_true;
} while (0);
// failed?
if (!ok) tb_printf("error: not found the project directory!\n");
// ok?
return ok;
}
/* //////////////////////////////////////////////////////////////////////////////////////
* implementation
*/
xm_machine_ref_t xm_machine_init()
{
// done
tb_bool_t ok = tb_false;
xm_machine_impl_t* impl = tb_null;
do
{
// init machine
impl = tb_malloc0_type(xm_machine_impl_t);
tb_assert_and_check_break(impl);
// init lua
impl->lua = lua_open();
tb_assert_and_check_break(impl->lua);
// open lua libraries
luaL_openlibs(impl->lua);
// bind os functions
luaL_register(impl->lua, "os", g_os_functions);
// bind path functions
luaL_register(impl->lua, "path", g_path_functions);
// bind string functions
luaL_register(impl->lua, "string", g_string_functions);
// bind process functions
luaL_register(impl->lua, "process", g_process_functions);
// init host
#if defined(TB_CONFIG_OS_WINDOWS)
lua_pushstring(impl->lua, "windows");
#elif defined(TB_CONFIG_OS_MACOSX)
lua_pushstring(impl->lua, "macosx");
#elif defined(TB_CONFIG_OS_LINUX)
lua_pushstring(impl->lua, "linux");
#elif defined(TB_CONFIG_OS_IOS)
lua_pushstring(impl->lua, "ios");
#elif defined(TB_CONFIG_OS_ANDROID)
lua_pushstring(impl->lua, "android");
#elif defined(TB_CONFIG_OS_LIKE_UNIX)
lua_pushstring(impl->lua, "unix");
#else
lua_pushstring(impl->lua, "unknown");
#endif
lua_setglobal(impl->lua, "_HOST");
// init architecture
#if defined(TB_ARCH_x86) || defined(TB_CONFIG_OS_WINDOWS)
lua_pushstring(impl->lua, "i386");
#elif defined(TB_ARCH_x64)
lua_pushstring(impl->lua, "x86_64");
#else
lua_pushstring(impl->lua, TB_ARCH_STRING);
#endif
lua_setglobal(impl->lua, "_ARCH");
// init redirect to null
#if defined(TB_CONFIG_OS_WINDOWS)
lua_pushstring(impl->lua, "nul");
#else
lua_pushstring(impl->lua, "/dev/null");
#endif
lua_setglobal(impl->lua, "_NULDEV");
// get version
tb_version_t const* version = xm_version();
tb_assert_and_check_break(version);
// init version string
tb_char_t version_cstr[256] = {0};
tb_snprintf(version_cstr, sizeof(version_cstr), "XMake v%u.%u.%u.%llu", version->major, version->minor, version->alter, version->build);
lua_pushstring(impl->lua, version_cstr);
lua_setglobal(impl->lua, "_VERSION");
// init short version string
tb_snprintf(version_cstr, sizeof(version_cstr), "XMake v%u.%u.%u", version->major, version->minor, version->alter);
lua_pushstring(impl->lua, version_cstr);
lua_setglobal(impl->lua, "_VERSION_SHORT");
// init namespace: xmake
lua_newtable(impl->lua);
lua_setglobal(impl->lua, "xmake");
// ok
ok = tb_true;
} while (0);
// failed?
if (!ok)
{
// exit it
if (impl) xm_machine_exit((xm_machine_ref_t)impl);
impl = tb_null;
}
return (xm_machine_ref_t)impl;
}
tb_void_t xm_machine_exit(xm_machine_ref_t machine)
{
// check
xm_machine_impl_t* impl = (xm_machine_impl_t*)machine;
tb_assert_and_check_return(impl);
// exit lua
if (impl->lua) lua_close(impl->lua);
impl->lua = tb_null;
// exit it
tb_free(impl);
}
tb_int_t xm_machine_main(xm_machine_ref_t machine, tb_int_t argc, tb_char_t** argv)
{
// check
xm_machine_impl_t* impl = (xm_machine_impl_t*)machine;
tb_assert_and_check_return_val(impl && impl->lua, -1);
// save main arguments to the global variable: _ARGV
if (!xm_machine_main_save_arguments(impl, argc, argv)) return -1;
// get the project directory
tb_char_t path[TB_PATH_MAXN] = {0};
if (!xm_machine_main_get_project_directory(impl, path, sizeof(path))) return -1;
// get the program directory
if (!xm_machine_main_get_program_directory(impl, path, sizeof(path))) return -1;
// append the main script path
tb_strcat(path, "/core/_xmake_main.lua");
// exists this script?
if (!tb_file_info(path, tb_null))
{
// error
tb_printf("not found main script: %s\n", path);
// failed
return -1;
}
// trace
tb_trace_d("main: %s", path);
// load and execute the main script
if (luaL_dofile(impl->lua, path))
{
// error
tb_printf("error: %s\n", lua_tostring(impl->lua, -1));
// failed
return -1;
}
// set the error function
lua_getglobal(impl->lua, "debug");
lua_getfield(impl->lua, -1, "traceback");
// call the main function
lua_getglobal(impl->lua, "_xmake_main");
if (lua_pcall(impl->lua, 0, 1, -2))
{
// error
tb_printf("error: %s\n", lua_tostring(impl->lua, -1));
// failed
return -1;
}
// get the error code
return (tb_int_t)lua_tonumber(impl->lua, -1);
}