/*!A cross-platform build utility based on Lua * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * Copyright (C) 2015-present, Xmake Open Source Community. * * @author ruki * @file engine.c * */ /* ////////////////////////////////////////////////////////////////////////////////////// * trace */ #define TB_TRACE_MODULE_NAME "engine" #define TB_TRACE_MODULE_DEBUG (1) /* ////////////////////////////////////////////////////////////////////////////////////// * includes */ #include "xmake.h" #include "io/poller.h" #if defined(TB_CONFIG_OS_WINDOWS) #include #include #include #elif defined(TB_CONFIG_OS_MACOSX) || defined(TB_CONFIG_OS_IOS) #include #include #include #elif defined(TB_CONFIG_OS_LINUX) || defined(TB_CONFIG_OS_BSD) || defined(TB_CONFIG_OS_ANDROID) || \ defined(TB_CONFIG_OS_HAIKU) || defined(TB_CONFIG_OS_SOLARIS) #include #include #endif #ifdef TB_CONFIG_OS_BSD #include #include #include #endif #ifdef TB_CONFIG_OS_SOLARIS #include #include #endif #ifdef TB_CONFIG_OS_HAIKU #include #endif #ifdef __COSMOPOLITAN__ #include #endif // for uid #ifndef TB_CONFIG_OS_WINDOWS #include #include #endif // for embed files #ifdef XM_EMBED_ENABLE #include "lz4/prefix.h" #endif /* ////////////////////////////////////////////////////////////////////////////////////// * macros */ // proc/self #if defined(TB_CONFIG_OS_LINUX) #define XM_PROC_SELF_FILE "/proc/self/exe" #elif defined(TB_CONFIG_OS_BSD) && !defined(__OpenBSD__) && !defined(__FreeBSD__) #if defined(__NetBSD__) #define XM_PROC_SELF_FILE "/proc/curproc/exe" #else #define XM_PROC_SELF_FILE "/proc/curproc/file" #endif #elif defined(TB_CONFIG_OS_SOLARIS) #define XM_PROC_SELF_FILE "/proc/self/path/a.out" #endif // hook lua memory allocator #define XM_HOOK_LUA_MEMALLOC (0) /* ////////////////////////////////////////////////////////////////////////////////////// * types */ // the engine type typedef struct __xm_engine_t { // the lua lua_State *lua; // the engine name tb_char_t name[64]; // the io poller tb_poller_ref_t poller; xm_poller_state_t poller_state; #ifdef XM_EMBED_ENABLE // the temporary directory tb_char_t tmpdir[TB_PATH_MAXN]; // the embed files tb_byte_t const *embeddata[32]; tb_size_t embedsize[32]; tb_size_t embedcount; #endif } xm_engine_t; /* ////////////////////////////////////////////////////////////////////////////////////// * declaration */ // the os functions tb_int_t xm_os_argv(lua_State *lua); tb_int_t xm_os_args(lua_State *lua); tb_int_t xm_os_find(lua_State *lua); tb_int_t xm_os_link(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_sleep(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_islink(lua_State *lua); tb_int_t xm_os_isfile(lua_State *lua); tb_int_t xm_os_touch(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_fscase(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_getenvs(lua_State *lua); tb_int_t xm_os_cpuinfo(lua_State *lua); tb_int_t xm_os_meminfo(lua_State *lua); tb_int_t xm_os_readlink(lua_State *lua); tb_int_t xm_os_filesize(lua_State *lua); tb_int_t xm_os_emptydir(lua_State *lua); tb_int_t xm_os_syserror(lua_State *lua); tb_int_t xm_os_strerror(lua_State *lua); tb_int_t xm_os_getwinsize(lua_State *lua); tb_int_t xm_os_getpid(lua_State *lua); tb_int_t xm_os_signal(lua_State *lua); #ifndef TB_CONFIG_OS_WINDOWS tb_int_t xm_os_uid(lua_State *lua); tb_int_t xm_os_gid(lua_State *lua); tb_int_t xm_os_getown(lua_State *lua); #endif // the io/file functions tb_int_t xm_io_stdfile(lua_State *lua); tb_int_t xm_io_file_open(lua_State *lua); tb_int_t xm_io_file_read(lua_State *lua); tb_int_t xm_io_file_readable(lua_State *lua); tb_int_t xm_io_file_seek(lua_State *lua); tb_int_t xm_io_file_size(lua_State *lua); tb_int_t xm_io_file_rawfd(lua_State *lua); tb_int_t xm_io_file_write(lua_State *lua); tb_int_t xm_io_file_flush(lua_State *lua); tb_int_t xm_io_file_close(lua_State *lua); tb_int_t xm_io_file_isatty(lua_State *lua); // the io/filelock functions tb_int_t xm_io_filelock_open(lua_State *lua); tb_int_t xm_io_filelock_lock(lua_State *lua); tb_int_t xm_io_filelock_unlock(lua_State *lua); tb_int_t xm_io_filelock_trylock(lua_State *lua); tb_int_t xm_io_filelock_close(lua_State *lua); // the io/socket functions tb_int_t xm_io_socket_open(lua_State *lua); tb_int_t xm_io_socket_rawfd(lua_State *lua); tb_int_t xm_io_socket_peeraddr(lua_State *lua); tb_int_t xm_io_socket_wait(lua_State *lua); tb_int_t xm_io_socket_bind(lua_State *lua); tb_int_t xm_io_socket_ctrl(lua_State *lua); tb_int_t xm_io_socket_listen(lua_State *lua); tb_int_t xm_io_socket_accept(lua_State *lua); tb_int_t xm_io_socket_connect(lua_State *lua); tb_int_t xm_io_socket_send(lua_State *lua); tb_int_t xm_io_socket_sendto(lua_State *lua); tb_int_t xm_io_socket_sendfile(lua_State *lua); tb_int_t xm_io_socket_recv(lua_State *lua); tb_int_t xm_io_socket_recvfrom(lua_State *lua); tb_int_t xm_io_socket_kill(lua_State *lua); tb_int_t xm_io_socket_close(lua_State *lua); // the io/pipe functions tb_int_t xm_io_pipe_open(lua_State *lua); tb_int_t xm_io_pipe_openpair(lua_State *lua); tb_int_t xm_io_pipe_close(lua_State *lua); tb_int_t xm_io_pipe_read(lua_State *lua); tb_int_t xm_io_pipe_write(lua_State *lua); tb_int_t xm_io_pipe_wait(lua_State *lua); tb_int_t xm_io_pipe_connect(lua_State *lua); // the io/poller functions tb_int_t xm_io_poller_insert(lua_State *lua); tb_int_t xm_io_poller_modify(lua_State *lua); tb_int_t xm_io_poller_remove(lua_State *lua); tb_int_t xm_io_poller_spank(lua_State *lua); tb_int_t xm_io_poller_support(lua_State *lua); tb_int_t xm_io_poller_wait(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_directory(lua_State *lua); tb_int_t xm_path_is_absolute(lua_State *lua); // the hash functions tb_int_t xm_hash_uuid4(lua_State *lua); tb_int_t xm_hash_sha(lua_State *lua); tb_int_t xm_hash_md5(lua_State *lua); tb_int_t xm_hash_xxhash(lua_State *lua); tb_int_t xm_hash_rand32(lua_State *lua); tb_int_t xm_hash_rand64(lua_State *lua); tb_int_t xm_hash_rand128(lua_State *lua); // the base64 functions tb_int_t xm_base64_encode(lua_State *lua); tb_int_t xm_base64_decode(lua_State *lua); // the lz4 functions tb_int_t xm_lz4_compress(lua_State *lua); tb_int_t xm_lz4_decompress(lua_State *lua); tb_int_t xm_lz4_block_compress(lua_State *lua); tb_int_t xm_lz4_block_decompress(lua_State *lua); tb_int_t xm_lz4_compress_file(lua_State *lua); tb_int_t xm_lz4_decompress_file(lua_State *lua); tb_int_t xm_lz4_compress_stream_open(lua_State *lua); tb_int_t xm_lz4_compress_stream_read(lua_State *lua); tb_int_t xm_lz4_compress_stream_write(lua_State *lua); tb_int_t xm_lz4_compress_stream_close(lua_State *lua); tb_int_t xm_lz4_decompress_stream_open(lua_State *lua); tb_int_t xm_lz4_decompress_stream_read(lua_State *lua); tb_int_t xm_lz4_decompress_stream_write(lua_State *lua); tb_int_t xm_lz4_decompress_stream_close(lua_State *lua); // the bloom filter functions tb_int_t xm_bloom_filter_open(lua_State *lua); tb_int_t xm_bloom_filter_close(lua_State *lua); tb_int_t xm_bloom_filter_clear(lua_State *lua); tb_int_t xm_bloom_filter_data(lua_State *lua); tb_int_t xm_bloom_filter_size(lua_State *lua); tb_int_t xm_bloom_filter_get(lua_State *lua); tb_int_t xm_bloom_filter_set(lua_State *lua); tb_int_t xm_bloom_filter_data_set(lua_State *lua); // the windows functions #ifdef TB_CONFIG_OS_WINDOWS tb_int_t xm_winos_cp_info(lua_State *lua); tb_int_t xm_winos_console_cp(lua_State *lua); tb_int_t xm_winos_console_output_cp(lua_State *lua); tb_int_t xm_winos_ansi_cp(lua_State *lua); tb_int_t xm_winos_oem_cp(lua_State *lua); tb_int_t xm_winos_logical_drives(lua_State *lua); tb_int_t xm_winos_registry_query(lua_State *lua); tb_int_t xm_winos_registry_keys(lua_State *lua); tb_int_t xm_winos_registry_values(lua_State *lua); tb_int_t xm_winos_short_path(lua_State *lua); #endif // the string functions tb_int_t xm_string_trim(lua_State *lua); tb_int_t xm_string_split(lua_State *lua); tb_int_t xm_string_lastof(lua_State *lua); tb_int_t xm_string_convert(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_kill(lua_State *lua); tb_int_t xm_process_close(lua_State *lua); // the fwatcher functions tb_int_t xm_fwatcher_open(lua_State *lua); tb_int_t xm_fwatcher_add(lua_State *lua); tb_int_t xm_fwatcher_remove(lua_State *lua); tb_int_t xm_fwatcher_wait(lua_State *lua); tb_int_t xm_fwatcher_close(lua_State *lua); // the sandbox functions tb_int_t xm_sandbox_interactive(lua_State *lua); #ifdef XM_CONFIG_API_HAVE_READLINE // the readline functions tb_int_t xm_readline_readline(lua_State *lua); tb_int_t xm_readline_history_list(lua_State *lua); tb_int_t xm_readline_add_history(lua_State *lua); tb_int_t xm_readline_clear_history(lua_State *lua); #endif // the semver functions tb_int_t xm_semver_parse(lua_State *lua); tb_int_t xm_semver_compare(lua_State *lua); tb_int_t xm_semver_satisfies(lua_State *lua); tb_int_t xm_semver_select(lua_State *lua); // the libc functions tb_int_t xm_libc_malloc(lua_State *lua); tb_int_t xm_libc_free(lua_State *lua); tb_int_t xm_libc_memcpy(lua_State *lua); tb_int_t xm_libc_memmov(lua_State *lua); tb_int_t xm_libc_memset(lua_State *lua); tb_int_t xm_libc_strndup(lua_State *lua); tb_int_t xm_libc_dataptr(lua_State *lua); tb_int_t xm_libc_byteof(lua_State *lua); tb_int_t xm_libc_setbyte(lua_State *lua); // the tty functions tb_int_t xm_tty_term_mode(lua_State *lua); tb_int_t xm_tty_session_id(lua_State *lua); // the package functions tb_int_t xm_package_loadxmi(lua_State *lua); // the binutils functions tb_int_t xm_binutils_bin2c(lua_State *lua); tb_int_t xm_binutils_bin2coff(lua_State *lua); tb_int_t xm_binutils_bin2macho(lua_State *lua); tb_int_t xm_binutils_bin2elf(lua_State *lua); tb_int_t xm_binutils_readsyms(lua_State *lua); tb_int_t xm_binutils_deplibs(lua_State *lua); tb_int_t xm_binutils_rpath_list(lua_State *lua); tb_int_t xm_binutils_rpath_clean(lua_State *lua); tb_int_t xm_binutils_extractlib(lua_State *lua); #ifdef XM_CONFIG_API_HAVE_CURSES // register curses functions tb_int_t xm_lua_curses_register(lua_State *lua, tb_char_t const *module); #endif // the thread functions tb_int_t xm_thread_init(lua_State *lua); tb_int_t xm_thread_exit(lua_State *lua); tb_int_t xm_thread_wait(lua_State *lua); tb_int_t xm_thread_suspend(lua_State *lua); tb_int_t xm_thread_resume(lua_State *lua); // the thread/mutex functions tb_int_t xm_thread_mutex_init(lua_State *lua); tb_int_t xm_thread_mutex_exit(lua_State *lua); tb_int_t xm_thread_mutex_lock(lua_State *lua); tb_int_t xm_thread_mutex_trylock(lua_State *lua); tb_int_t xm_thread_mutex_unlock(lua_State *lua); tb_int_t xm_thread_mutex_incref(lua_State *lua); // the thread/event functions tb_int_t xm_thread_event_init(lua_State *lua); tb_int_t xm_thread_event_exit(lua_State *lua); tb_int_t xm_thread_event_post(lua_State *lua); tb_int_t xm_thread_event_wait(lua_State *lua); tb_int_t xm_thread_event_incref(lua_State *lua); // the thread/semaphore functions tb_int_t xm_thread_semaphore_init(lua_State *lua); tb_int_t xm_thread_semaphore_exit(lua_State *lua); tb_int_t xm_thread_semaphore_post(lua_State *lua); tb_int_t xm_thread_semaphore_wait(lua_State *lua); tb_int_t xm_thread_semaphore_incref(lua_State *lua); // the thread/queue functions tb_int_t xm_thread_queue_init(lua_State *lua); tb_int_t xm_thread_queue_exit(lua_State *lua); tb_int_t xm_thread_queue_size(lua_State *lua); tb_int_t xm_thread_queue_clear(lua_State *lua); tb_int_t xm_thread_queue_incref(lua_State *lua); tb_int_t xm_thread_queue_push(lua_State *lua); tb_int_t xm_thread_queue_pop(lua_State *lua); // the thread/sharedata functions tb_int_t xm_thread_sharedata_init(lua_State *lua); tb_int_t xm_thread_sharedata_exit(lua_State *lua); tb_int_t xm_thread_sharedata_clear(lua_State *lua); tb_int_t xm_thread_sharedata_incref(lua_State *lua); tb_int_t xm_thread_sharedata_set(lua_State *lua); tb_int_t xm_thread_sharedata_get_(lua_State *lua); // open cjson __tb_extern_c_enter__ tb_int_t luaopen_cjson(lua_State *l); __tb_extern_c_leave__ /* ////////////////////////////////////////////////////////////////////////////////////// * globals */ // the os functions static luaL_Reg const g_os_functions[] = { { "argv", xm_os_argv }, { "args", xm_os_args }, { "find", xm_os_find }, { "link", xm_os_link }, { "isdir", xm_os_isdir }, { "rmdir", xm_os_rmdir }, { "mkdir", xm_os_mkdir }, { "cpdir", xm_os_cpdir }, { "chdir", xm_os_chdir }, { "mtime", xm_os_mtime }, { "sleep", xm_os_sleep }, { "mclock", xm_os_mclock }, { "curdir", xm_os_curdir }, { "tmpdir", xm_os_tmpdir }, { "islink", xm_os_islink }, { "isfile", xm_os_isfile }, { "touch", xm_os_touch }, { "rmfile", xm_os_rmfile }, { "cpfile", xm_os_cpfile }, { "fscase", xm_os_fscase }, { "rename", xm_os_rename }, { "exists", xm_os_exists }, { "setenv", xm_os_setenv }, { "getenv", xm_os_getenv }, { "getenvs", xm_os_getenvs }, { "cpuinfo", xm_os_cpuinfo }, { "meminfo", xm_os_meminfo }, { "readlink", xm_os_readlink }, { "emptydir", xm_os_emptydir }, { "strerror", xm_os_strerror }, { "syserror", xm_os_syserror }, { "filesize", xm_os_filesize }, { "getwinsize", xm_os_getwinsize }, { "getpid", xm_os_getpid }, { "signal", xm_os_signal }, #ifndef TB_CONFIG_OS_WINDOWS { "uid", xm_os_uid }, { "gid", xm_os_gid }, { "getown", xm_os_getown }, #endif { tb_null, tb_null }, }; // the windows functions #ifdef TB_CONFIG_OS_WINDOWS static luaL_Reg const g_winos_functions[] = { { "cp_info", xm_winos_cp_info }, { "console_cp", xm_winos_console_cp }, { "console_output_cp", xm_winos_console_output_cp }, { "oem_cp", xm_winos_oem_cp }, { "ansi_cp", xm_winos_ansi_cp }, { "logical_drives", xm_winos_logical_drives }, { "registry_query", xm_winos_registry_query }, { "registry_keys", xm_winos_registry_keys }, { "registry_values", xm_winos_registry_values }, { "short_path", xm_winos_short_path }, { tb_null, tb_null }, }; #endif // the io functions static luaL_Reg const g_io_functions[] = { { "stdfile", xm_io_stdfile }, { "file_open", xm_io_file_open }, { "file_read", xm_io_file_read }, { "file_readable", xm_io_file_readable }, { "file_seek", xm_io_file_seek }, { "file_size", xm_io_file_size }, { "file_write", xm_io_file_write }, { "file_flush", xm_io_file_flush }, { "file_isatty", xm_io_file_isatty }, { "file_close", xm_io_file_close }, { "file_rawfd", xm_io_file_rawfd }, { "filelock_open", xm_io_filelock_open }, { "filelock_lock", xm_io_filelock_lock }, { "filelock_trylock", xm_io_filelock_trylock }, { "filelock_unlock", xm_io_filelock_unlock }, { "filelock_close", xm_io_filelock_close }, { "socket_open", xm_io_socket_open }, { "socket_rawfd", xm_io_socket_rawfd }, { "socket_peeraddr", xm_io_socket_peeraddr }, { "socket_wait", xm_io_socket_wait }, { "socket_bind", xm_io_socket_bind }, { "socket_ctrl", xm_io_socket_ctrl }, { "socket_listen", xm_io_socket_listen }, { "socket_accept", xm_io_socket_accept }, { "socket_connect", xm_io_socket_connect }, { "socket_send", xm_io_socket_send }, { "socket_sendto", xm_io_socket_sendto }, { "socket_sendfile", xm_io_socket_sendfile }, { "socket_recv", xm_io_socket_recv }, { "socket_recvfrom", xm_io_socket_recvfrom }, { "socket_kill", xm_io_socket_kill }, { "socket_close", xm_io_socket_close }, { "pipe_open", xm_io_pipe_open }, { "pipe_openpair", xm_io_pipe_openpair }, { "pipe_close", xm_io_pipe_close }, { "pipe_read", xm_io_pipe_read }, { "pipe_write", xm_io_pipe_write }, { "pipe_wait", xm_io_pipe_wait }, { "pipe_connect", xm_io_pipe_connect }, { "poller_insert", xm_io_poller_insert }, { "poller_modify", xm_io_poller_modify }, { "poller_remove", xm_io_poller_remove }, { "poller_spank", xm_io_poller_spank }, { "poller_support", xm_io_poller_support }, { "poller_wait", xm_io_poller_wait }, { 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 }, { "directory", xm_path_directory }, { "is_absolute", xm_path_is_absolute }, { tb_null, tb_null }, }; // the hash functions static luaL_Reg const g_hash_functions[] = { { "uuid4", xm_hash_uuid4 }, { "sha", xm_hash_sha }, { "md5", xm_hash_md5 }, { "xxhash", xm_hash_xxhash }, { "rand32", xm_hash_rand32 }, { "rand64", xm_hash_rand64 }, { "rand128", xm_hash_rand128 }, { tb_null, tb_null }, }; // the base64 functions static luaL_Reg const g_base64_functions[] = { { "encode", xm_base64_encode }, { "decode", xm_base64_decode }, { tb_null, tb_null }, }; // the lz4 functions static luaL_Reg const g_lz4_functions[] = { { "compress", xm_lz4_compress }, { "decompress", xm_lz4_decompress }, { "block_compress", xm_lz4_block_compress }, { "block_decompress", xm_lz4_block_decompress }, { "compress_file", xm_lz4_compress_file }, { "decompress_file", xm_lz4_decompress_file }, { "compress_stream_open", xm_lz4_compress_stream_open }, { "compress_stream_read", xm_lz4_compress_stream_read }, { "compress_stream_write", xm_lz4_compress_stream_write }, { "compress_stream_close", xm_lz4_compress_stream_close }, { "decompress_stream_open", xm_lz4_decompress_stream_open }, { "decompress_stream_read", xm_lz4_decompress_stream_read }, { "decompress_stream_write", xm_lz4_decompress_stream_write }, { "decompress_stream_close", xm_lz4_decompress_stream_close }, { tb_null, tb_null }, }; // the bloom filter functions static luaL_Reg const g_bloom_filter_functions[] = { { "open", xm_bloom_filter_open }, { "close", xm_bloom_filter_close }, { "clear", xm_bloom_filter_clear }, { "data", xm_bloom_filter_data }, { "size", xm_bloom_filter_size }, { "get", xm_bloom_filter_get }, { "set", xm_bloom_filter_set }, { "data_set", xm_bloom_filter_data_set }, { tb_null, tb_null }, }; // the string functions static luaL_Reg const g_string_functions[] = { { "trim", xm_string_trim }, { "split", xm_string_split }, { "lastof", xm_string_lastof }, { "convert", xm_string_convert }, { "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 }, { "kill", xm_process_kill }, { "close", xm_process_close }, { tb_null, tb_null }, }; // the fwatcher functions static luaL_Reg const g_fwatcher_functions[] = { { "open", xm_fwatcher_open }, { "add", xm_fwatcher_add }, { "remove", xm_fwatcher_remove }, { "wait", xm_fwatcher_wait }, { "close", xm_fwatcher_close }, { tb_null, tb_null }, }; // the sandbox functions static luaL_Reg const g_sandbox_functions[] = { { "interactive", xm_sandbox_interactive }, { tb_null, tb_null }, }; #ifdef XM_CONFIG_API_HAVE_READLINE // the readline functions static luaL_Reg const g_readline_functions[] = { { "readline", xm_readline_readline }, { "history_list", xm_readline_history_list }, { "add_history", xm_readline_add_history }, { "clear_history", xm_readline_clear_history }, { tb_null, tb_null }, }; #endif // the semver functions static luaL_Reg const g_semver_functions[] = { { "parse", xm_semver_parse }, { "compare", xm_semver_compare }, { "satisfies", xm_semver_satisfies }, { "select", xm_semver_select }, { tb_null, tb_null }, }; // the libc functions static luaL_Reg const g_libc_functions[] = { { "malloc", xm_libc_malloc }, { "free", xm_libc_free }, { "memcpy", xm_libc_memcpy }, { "memset", xm_libc_memset }, { "memmov", xm_libc_memmov }, { "strndup", xm_libc_strndup }, { "dataptr", xm_libc_dataptr }, { "byteof", xm_libc_byteof }, { "setbyte", xm_libc_setbyte }, { tb_null, tb_null }, }; // the tty functions static luaL_Reg const g_tty_functions[] = { { "term_mode", xm_tty_term_mode }, { "session_id", xm_tty_session_id }, { tb_null, tb_null }, }; // the package functions static luaL_Reg const g_package_functions[] = { { "loadxmi", xm_package_loadxmi }, { tb_null, tb_null }, }; // the binutils functions static luaL_Reg const g_binutils_functions[] = { { "bin2c", xm_binutils_bin2c }, { "bin2coff", xm_binutils_bin2coff }, { "bin2macho", xm_binutils_bin2macho }, { "bin2elf", xm_binutils_bin2elf }, { "readsyms", xm_binutils_readsyms }, { "deplibs", xm_binutils_deplibs }, { "rpath_list", xm_binutils_rpath_list }, { "rpath_clean", xm_binutils_rpath_clean }, { "extractlib", xm_binutils_extractlib }, { tb_null, tb_null }, }; // the thread functions static luaL_Reg const g_thread_functions[] = { { "thread_init", xm_thread_init }, { "thread_exit", xm_thread_exit }, { "thread_wait", xm_thread_wait }, { "thread_resume", xm_thread_resume }, { "thread_suspend", xm_thread_suspend }, { "mutex_init", xm_thread_mutex_init }, { "mutex_exit", xm_thread_mutex_exit }, { "mutex_lock", xm_thread_mutex_lock }, { "mutex_trylock", xm_thread_mutex_trylock }, { "mutex_unlock", xm_thread_mutex_unlock }, { "mutex_incref", xm_thread_mutex_incref }, { "event_init", xm_thread_event_init }, { "event_exit", xm_thread_event_exit }, { "event_post", xm_thread_event_post }, { "event_wait", xm_thread_event_wait }, { "event_incref", xm_thread_event_incref }, { "semaphore_init", xm_thread_semaphore_init }, { "semaphore_exit", xm_thread_semaphore_exit }, { "semaphore_post", xm_thread_semaphore_post }, { "semaphore_wait", xm_thread_semaphore_wait }, { "semaphore_incref", xm_thread_semaphore_incref }, { "queue_init", xm_thread_queue_init }, { "queue_exit", xm_thread_queue_exit }, { "queue_size", xm_thread_queue_size }, { "queue_clear", xm_thread_queue_clear }, { "queue_incref", xm_thread_queue_incref }, { "queue_push", xm_thread_queue_push }, { "queue_pop", xm_thread_queue_pop }, { "sharedata_init", xm_thread_sharedata_init }, { "sharedata_exit", xm_thread_sharedata_exit }, { "sharedata_clear", xm_thread_sharedata_clear }, { "sharedata_incref", xm_thread_sharedata_incref }, { "sharedata_set", xm_thread_sharedata_set }, { "sharedata_get", xm_thread_sharedata_get_ }, { tb_null, tb_null }, }; // the lua global instance for signal handler static lua_State *g_lua = tb_null; // the xmake script files data #ifdef XM_EMBED_ENABLE extern tb_byte_t _binary_xmake_xmz_start[]; extern tb_byte_t _binary_xmake_xmz_end[]; #endif /* ////////////////////////////////////////////////////////////////////////////////////// * private implementation */ static tb_bool_t xm_engine_save_arguments(xm_engine_t *engine, tb_int_t argc, tb_char_t **argv, tb_char_t **taskargv) { tb_assert_and_check_return_val(engine && engine->lua && argc >= 1 && argv, tb_false); #if defined(TB_CONFIG_OS_WINDOWS) && !defined(TB_COMPILER_LIKE_UNIX) tb_wchar_t **argvw = CommandLineToArgvW(GetCommandLineW(), &argc); #endif // put a new table into the stack lua_newtable(engine->lua); // patch the task arguments list if (taskargv) { tb_char_t **taskarg = taskargv; while (*taskarg) { lua_pushstring(engine->lua, *taskarg); lua_rawseti(engine->lua, -2, (int)lua_objlen(engine->lua, -2) + 1); taskarg++; } } // save all arguments to the new table tb_int_t i = 0; for (i = 1; i < argc; i++) { #if defined(TB_CONFIG_OS_WINDOWS) && !defined(TB_COMPILER_LIKE_UNIX) tb_char_t argvbuf[4096] = { 0 }; tb_wcstombs(argvbuf, argvw[i], tb_arrayn(argvbuf)); // table_new[table.getn(table_new) + 1] = argv[i] lua_pushstring(engine->lua, argvbuf); #else lua_pushstring(engine->lua, argv[i]); #endif lua_rawseti(engine->lua, -2, (int)lua_objlen(engine->lua, -2) + 1); } // _ARGV = table_new lua_setglobal(engine->lua, "_ARGV"); return tb_true; } static tb_bool_t xm_engine_get_program_file(xm_engine_t *engine, tb_char_t **argv, tb_char_t *path, tb_size_t maxn) { tb_assert_and_check_return_val(engine && path && maxn, tb_false); /* we cache it, because the current path will be changed in thread. * * The executable file compiled using cosmocc on macOS might retrieve a relative path to the programfile. * If the root directory has been changed, the retrieved programfile will be a non-existent path. */ static tb_char_t s_program_filepath[TB_PATH_MAXN] = { 0 }; if (s_program_filepath[0]) { tb_strlcpy(path, s_program_filepath, maxn); lua_pushstring(engine->lua, s_program_filepath); lua_setglobal(engine->lua, "_PROGRAM_FILE"); return tb_true; } tb_bool_t ok = tb_false; do { // get it from the environment variable first if (tb_environment_first("XMAKE_PROGRAM_FILE", path, maxn) && tb_file_info(path, tb_null)) { ok = tb_true; break; } #if defined(TB_CONFIG_OS_WINDOWS) // get the executale file path as program directory tb_wchar_t buf[TB_PATH_MAXN] = { 0 }; tb_size_t size = (tb_size_t)GetModuleFileNameW(tb_null, buf, (DWORD)TB_PATH_MAXN); tb_assert_and_check_break(size < TB_PATH_MAXN); buf[size] = L'\0'; size = tb_wcstombs(path, buf, maxn); tb_assert_and_check_break(size < maxn); path[size] = '\0'; ok = tb_true; #elif defined(TB_CONFIG_OS_MACOSX) || defined(TB_CONFIG_OS_IOS) /* * _NSGetExecutablePath() copies the path of the main executable into the buffer. The bufsize parameter * should initially be the size of the buffer. The function returns 0 if the path was successfully copied, * and *bufsize is left unchanged. It returns -1 if the buffer is not large enough, and *bufsize is set * to the size required. * * Note that _NSGetExecutablePath will return "a path" to the executable not a "real path" to the executable. * That is the path may be a symbolic link and not the real file. With deep directories the total bufsize * needed could be more than MAXPATHLEN. */ tb_uint32_t bufsize = (tb_uint32_t)maxn; if (!_NSGetExecutablePath(path, &bufsize)) { ok = tb_true; } #elif defined(XM_PROC_SELF_FILE) /* get the executale file path as program directory * * @see it may be a relative path */ ssize_t size = readlink(XM_PROC_SELF_FILE, path, (size_t)maxn); if (size > 0 && size < maxn) { path[size] = '\0'; ok = tb_true; } #elif defined(TB_CONFIG_OS_BSD) && defined(KERN_PROC_PATHNAME) // only for FreeBSD and OpenBSD, https://github.com/xmake-io/xmake/issues/2948 tb_int_t mib[4]; mib[0] = CTL_KERN; mib[1] = KERN_PROC; mib[2] = KERN_PROC_PATHNAME; mib[3] = -1; size_t size = maxn; if (sysctl(mib, 4, path, &size, tb_null, 0) == 0 && size < maxn) { path[size] = '\0'; ok = tb_true; } #elif defined(TB_CONFIG_OS_HAIKU) int32 cookie = 0; image_info info; while (get_next_image_info(B_CURRENT_TEAM, &cookie, &info) == B_OK) { if (info.type == B_APP_IMAGE) { tb_strlcpy(path, info.name, maxn); ok = tb_true; break; } } #else static tb_char_t const *s_paths[] = { "~/.local/bin/xmake", "/usr/local/bin/xmake", "/usr/bin/xmake", }; for (tb_size_t i = 0; i < tb_arrayn(s_paths); i++) { tb_char_t const *p = s_paths[i]; if (tb_file_info(p, tb_null)) { tb_strlcpy(path, p, maxn); ok = tb_true; break; } } #endif if (!ok && argv) { tb_char_t const *p = argv[0]; if (p && tb_file_info(p, tb_null)) { tb_strlcpy(path, p, maxn); ok = tb_true; } } } while (0); if (ok) { tb_trace_d("programfile: %s", path); // cache it tb_strlcpy(s_program_filepath, path, sizeof(s_program_filepath)); // save the directory to the global variable: _PROGRAM_FILE lua_pushstring(engine->lua, path); lua_setglobal(engine->lua, "_PROGRAM_FILE"); } return ok; } #ifdef XM_EMBED_ENABLE static tb_bool_t xm_engine_get_temporary_directory(tb_char_t *path, tb_size_t maxn, tb_char_t const *name, tb_char_t const *version_cstr) { tb_char_t data[TB_PATH_MAXN] = { 0 }; if (tb_directory_temporary(data, sizeof(data))) { // get euid tb_int_t euid = 0; #ifndef TB_CONFIG_OS_WINDOWS euid = geteuid(); #endif tb_snprintf(path, maxn, "%s/.%s%d/%s", data, name, euid, version_cstr); return tb_true; } return tb_false; } #endif static tb_bool_t xm_engine_get_program_directory(xm_engine_t *engine, tb_char_t *path, tb_size_t maxn, tb_char_t const *programfile) { tb_assert_and_check_return_val(engine && path && maxn, tb_false); static tb_char_t s_program_directory[TB_PATH_MAXN] = { 0 }; if (s_program_directory[0]) { tb_strlcpy(path, s_program_directory, maxn); lua_pushstring(engine->lua, s_program_directory); lua_setglobal(engine->lua, "_PROGRAM_DIR"); return tb_true; } tb_bool_t ok = tb_false; tb_char_t data[TB_PATH_MAXN] = { 0 }; do { #ifdef XM_EMBED_ENABLE tb_size_t embedcount = engine->embedcount; if (embedcount) { tb_uint32_t crc32 = 0; for (tb_size_t i = 0; i < embedcount; i++) { crc32 += tb_crc32_make(engine->embeddata[i], engine->embedsize[i], 0); } tb_snprintf(path, maxn, "%s/%x", engine->tmpdir, crc32); } else { tb_strlcpy(path, engine->tmpdir, maxn); } ok = tb_true; break; #endif // get it from the environment variable first if (tb_environment_first("XMAKE_PROGRAM_DIR", data, sizeof(data)) && tb_path_absolute(data, path, maxn)) { ok = tb_true; break; } // get it from program file path if (programfile) { // get real program file path from the symbol link #if !defined(TB_CONFIG_OS_WINDOWS) && !defined(TB_CONFIG_OS_IOS) tb_char_t programpath[TB_PATH_MAXN]; tb_long_t size = readlink(programfile, programpath, sizeof(programpath)); if (size >= 0 && size < sizeof(programpath)) { programpath[size] = '\0'; // soft link to relative path? fix it if (!tb_path_is_absolute(programpath)) { tb_char_t buff[TB_PATH_MAXN]; tb_char_t const *rootdir = tb_path_directory(programfile, buff, sizeof(buff)); if (rootdir && tb_path_absolute_to( rootdir, programpath, path, maxn)) { // @note path and programfile are same buffer tb_strlcpy(programpath, path, maxn); } } } else tb_strlcpy(programpath, programfile, sizeof(programpath)); #else tb_char_t const *programpath = programfile; #endif // get the root directory tb_char_t data[TB_PATH_MAXN]; tb_char_t const *rootdir = tb_path_directory(programpath, data, sizeof(data)); tb_assert_and_check_break(rootdir); // init share/name sub-directory tb_char_t sharedir[128]; tb_snprintf(sharedir, sizeof(sharedir), "../share/%s", engine->name); // find the program (lua) directory tb_size_t i; tb_file_info_t info; tb_char_t scriptpath[TB_PATH_MAXN]; tb_char_t const *subdirs[] = { ".", sharedir, }; for (i = 0; i < tb_arrayn(subdirs); i++) { // get program directory if (tb_path_absolute_to(rootdir, subdirs[i], path, maxn) && tb_path_absolute_to(path, "core/_xmake_main.lua", scriptpath, sizeof(scriptpath)) && tb_file_info(scriptpath, &info) && info.type == TB_FILE_TYPE_FILE) { ok = tb_true; break; } } } } while (0); if (ok) { tb_trace_d("programdir: %s", path); // cache it tb_strlcpy(s_program_directory, path, sizeof(s_program_directory)); // save the directory to the global variable: _PROGRAM_DIR lua_pushstring(engine->lua, path); lua_setglobal(engine->lua, "_PROGRAM_DIR"); } return ok; } static tb_bool_t xm_engine_get_project_directory(xm_engine_t *engine, tb_char_t *path, tb_size_t maxn) { tb_assert_and_check_return_val(engine && path && maxn, tb_false); 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; } } tb_trace_d("project: %s", path); // save the directory to the global variable: _PROJECT_DIR lua_pushstring(engine->lua, path); lua_setglobal(engine->lua, "_PROJECT_DIR"); ok = tb_true; } while (0); // failed? if (!ok) { tb_printf("error: not found the project directory!\n"); } return ok; } #if defined(TB_CONFIG_OS_WINDOWS) || defined(SIGINT) static tb_void_t xm_engine_dump_traceback(lua_State *lua) { // @note it's not safe, but it doesn't matter, we're just trying to get the stack backtrace for debugging lua_getglobal(lua, "debug"); lua_getfield(lua, -1, "traceback"); lua_replace(lua, -2); lua_pushvalue(lua, 1); lua_call(lua, 1, 1); tb_trace_i("%s", lua_tostring(lua, -1)); } #endif #if defined(TB_CONFIG_OS_WINDOWS) static BOOL WINAPI xm_engine_signal_handler(DWORD signo) { if (signo == CTRL_C_EVENT && g_lua) { xm_engine_dump_traceback(g_lua); tb_abort(); } return TRUE; } #elif defined(SIGINT) static tb_void_t xm_engine_signal_handler(tb_int_t signo) { if (signo == SIGINT && g_lua) { xm_engine_dump_traceback(g_lua); tb_abort(); } } #endif static tb_void_t xm_engine_init_host(xm_engine_t *engine) { tb_assert_and_check_return(engine && engine->lua); // init system host tb_char_t const *syshost = tb_null; #if defined(__COSMOPOLITAN__) struct utsname buffer; if (uname(&buffer) == 0) { if (tb_strstr(buffer.sysname, "Darwin")) { syshost = "macosx"; } else if (tb_strstr(buffer.sysname, "Linux")) { syshost = "linux"; } else if (tb_strstr(buffer.sysname, "Windows")) { syshost = "windows"; } } #elif defined(TB_CONFIG_OS_WINDOWS) syshost = "windows"; #elif defined(TB_CONFIG_OS_MACOSX) syshost = "macosx"; #elif defined(TB_CONFIG_OS_LINUX) syshost = "linux"; #elif defined(TB_CONFIG_OS_BSD) syshost = "bsd"; #elif defined(TB_CONFIG_OS_SOLARIS) syshost = "solaris"; #elif defined(TB_CONFIG_OS_IOS) syshost = "ios"; #elif defined(TB_CONFIG_OS_ANDROID) syshost = "android"; #elif defined(TB_CONFIG_OS_HAIKU) syshost = "haiku"; #endif lua_pushstring(engine->lua, syshost ? syshost : "unknown"); lua_setglobal(engine->lua, "_HOST"); // init subsystem host tb_char_t const *subhost = syshost; #if defined(TB_CONFIG_OS_WINDOWS) #if defined(TB_COMPILER_ON_MSYS) subhost = "msys"; #elif defined(TB_COMPILER_ON_CYGWIN) subhost = "cygwin"; #else { tb_char_t data[64] = { 0 }; if (tb_environment_first("MSYSTEM", data, sizeof(data))) { // on msys? if (!tb_strnicmp(data, "mingw", 5) // mingw32/64 on msys2 || !tb_strnicmp(data, "clang", 5) // clang32/64 on msys2, @see https://github.com/xmake-io/xmake/issues/3060 || !tb_stricmp(data, "ucrt64") // ucrt64 https://www.msys2.org/docs/environments/ || !tb_stricmp(data, "msys")) { // on msys2 subhost = "msys"; } } } #endif #endif lua_pushstring(engine->lua, subhost ? subhost : "unknown"); lua_setglobal(engine->lua, "_SUBHOST"); } static __tb_inline__ tb_char_t const *xm_engine_xmake_arch() { tb_char_t const *arch = tb_null; #if defined(TB_CONFIG_OS_WINDOWS) && !defined(TB_COMPILER_LIKE_UNIX) #if defined(TB_ARCH_x64) arch = "x64"; #elif defined(TB_ARCH_ARM64) arch = "arm64"; #elif defined(TB_ARCH_ARM) arch = "arm"; #else arch = "x86"; #endif #elif defined(TB_ARCH_x64) arch = "x86_64"; #elif defined(TB_ARCH_x86) arch = "i386"; #elif defined(TB_ARCH_ARM64) arch = "arm64"; #else arch = TB_ARCH_STRING; #endif return arch; } static tb_void_t xm_engine_init_arch(xm_engine_t *engine) { tb_assert_and_check_return(engine && engine->lua); // init xmake arch tb_char_t const *xmakearch = xm_engine_xmake_arch(); lua_pushstring(engine->lua, xmakearch); lua_setglobal(engine->lua, "_XMAKE_ARCH"); // init system architecture tb_char_t const *sysarch = tb_null; #if defined(__COSMOPOLITAN__) struct utsname buffer; if (uname(&buffer) == 0) { sysarch = buffer.machine; if (tb_strstr(buffer.sysname, "Windows")) { if (!tb_strcmp(buffer.machine, "x86_64")) { sysarch = "x64"; } else if (!tb_strcmp(buffer.machine, "i686") || !tb_strcmp(buffer.machine, "i386")) { sysarch = "x86"; } } else if (!tb_strcmp(buffer.machine, "aarch64")) { sysarch = "arm64"; } } #elif defined(TB_CONFIG_OS_WINDOWS) && !defined(TB_COMPILER_LIKE_UNIX) // the GetNativeSystemInfo function type typedef void(WINAPI * GetNativeSystemInfo_t)(LPSYSTEM_INFO); // get system info SYSTEM_INFO systeminfo = { 0 }; GetNativeSystemInfo_t pGetNativeSystemInfo = tb_null; tb_dynamic_ref_t kernel32 = tb_dynamic_init("kernel32.dll"); if (kernel32) { pGetNativeSystemInfo = (GetNativeSystemInfo_t)tb_dynamic_func(kernel32, "GetNativeSystemInfo"); } if (pGetNativeSystemInfo) { pGetNativeSystemInfo(&systeminfo); } else { GetSystemInfo(&systeminfo); } // init architecture switch (systeminfo.wProcessorArchitecture) { case PROCESSOR_ARCHITECTURE_AMD64: sysarch = "x64"; break; #if defined(PROCESSOR_ARCHITECTURE_ARM64) case PROCESSOR_ARCHITECTURE_ARM64: sysarch = "arm64"; break; #endif case PROCESSOR_ARCHITECTURE_ARM: sysarch = "arm"; break; case PROCESSOR_ARCHITECTURE_INTEL: sysarch = "x86"; break; default: break; } #endif if (!sysarch) { sysarch = xmakearch; } lua_pushstring(engine->lua, sysarch); lua_setglobal(engine->lua, "_ARCH"); // init subsystem architecture tb_char_t const *subarch = sysarch; #if defined(TB_CONFIG_OS_WINDOWS) && !defined(TB_COMPILER_LIKE_UNIX) // get architecture from msys environment tb_char_t data[64] = { 0 }; if (tb_environment_first("MSYSTEM_CARCH", data, sizeof(data))) { if (!tb_strcmp(data, "i686")) { subarch = "i386"; } else { subarch = data; } } #endif lua_pushstring(engine->lua, subarch); lua_setglobal(engine->lua, "_SUBARCH"); } static tb_void_t xm_engine_init_features(xm_engine_t *engine) { tb_assert_and_check_return(engine && engine->lua); // init features lua_newtable(engine->lua); // get path seperator lua_pushstring(engine->lua, "path_sep"); #if defined(TB_CONFIG_OS_WINDOWS) && !defined(TB_COMPILER_LIKE_UNIX) lua_pushstring(engine->lua, "\\"); #else lua_pushstring(engine->lua, "/"); #endif lua_settable(engine->lua, -3); // get environment path seperator lua_pushstring(engine->lua, "path_envsep"); #if defined(TB_CONFIG_OS_WINDOWS) && !defined(TB_COMPILER_LIKE_UNIX) lua_pushstring(engine->lua, ";"); #else lua_pushstring(engine->lua, ":"); #endif lua_settable(engine->lua, -3); lua_setglobal(engine->lua, "_FEATURES"); } static tb_void_t xm_engine_init_signal(xm_engine_t *engine) { // we enable it to catch the current lua stack in ctrl-c signal handler if XMAKE_PROFILE=stuck tb_char_t data[64] = { 0 }; if (!tb_environment_first("XMAKE_PROFILE", data, sizeof(data)) || tb_strcmp(data, "stuck")) { return; } g_lua = engine->lua; #if defined(TB_CONFIG_OS_WINDOWS) SetConsoleCtrlHandler(xm_engine_signal_handler, TRUE); #elif defined(SIGINT) signal(SIGINT, xm_engine_signal_handler); #endif } #if XM_HOOK_LUA_MEMALLOC // udata is unused, it has been used by engine. see xm_engine_bind_to_lua() static tb_pointer_t xm_engine_lua_realloc(tb_pointer_t udata, tb_pointer_t data, size_t osize, size_t nsize) { tb_pointer_t ptr = tb_null; if (nsize == 0 && data) { tb_free(data); } else if (!data) { ptr = tb_malloc((tb_size_t)nsize); } else if (nsize != osize) { ptr = tb_ralloc(data, (tb_size_t)nsize); } else { ptr = data; } return ptr; } #endif #ifdef XM_EMBED_ENABLE static tb_bool_t xm_engine_extract_programfiles_impl(xm_engine_t *engine, tb_char_t const *programdir, tb_byte_t const *data, tb_size_t size) { // do decompress tb_bool_t ok = tb_false; LZ4F_errorCode_t code; LZ4F_decompressionContext_t ctx = tb_null; tb_buffer_t result; do { tb_buffer_init(&result); code = LZ4F_createDecompressionContext(&ctx, LZ4F_VERSION); if (LZ4F_isError(code)) { break; } tb_byte_t buffer[8192]; tb_bool_t failed = tb_false; while (1) { size_t advance = (size_t)size; size_t buffer_size = sizeof(buffer); code = LZ4F_decompress(ctx, buffer, &buffer_size, data, &advance, tb_null); if (LZ4F_isError(code)) { failed = tb_true; break; } if (buffer_size == 0) { break; } data += advance; size -= advance; tb_buffer_memncat(&result, buffer, buffer_size); } tb_assert_and_check_break(!failed && tb_buffer_size(&result)); ok = tb_true; } while (0); // extract files to programdir if (ok) { data = tb_buffer_data(&result); size = tb_buffer_size(&result); tb_byte_t const *p = data; tb_byte_t const *e = data + size; tb_size_t n = 0; tb_char_t filepath[TB_PATH_MAXN]; tb_long_t pos = tb_snprintf(filepath, sizeof(filepath), "%s/", programdir); while (p < e) { // get filepath n = (tb_size_t)tb_bits_get_u16_be(p); p += 2; tb_assert_and_check_break(pos + n + 1 < sizeof(filepath)); tb_strncpy(filepath + pos, (tb_char_t const *)p, n); filepath[pos + n] = '\0'; p += n; // get filedata n = (tb_size_t)tb_bits_get_u32_be(p); p += 4; // write file tb_trace_d("extracting %s, %lu bytes ..", filepath, n); tb_stream_ref_t stream = tb_stream_init_from_file(filepath, TB_FILE_MODE_RW | TB_FILE_MODE_CREAT | TB_FILE_MODE_TRUNC); tb_assert_and_check_break(stream); if (tb_stream_open(stream)) { tb_stream_bwrit(stream, p, n); tb_stream_exit(stream); } p += n; } ok = (p == e); if (!ok) { tb_trace_e("extract program files failed"); } } else { tb_trace_e("decompress program files failed, %s", LZ4F_getErrorName(code)); } if (ctx) { LZ4F_freeDecompressionContext(ctx); ctx = tb_null; } tb_buffer_exit(&result); return ok; } static tb_bool_t xm_engine_extract_programfiles(xm_engine_t *engine, tb_char_t const *programdir) { tb_file_info_t info = { 0 }; if (!tb_file_info(programdir, &info)) { tb_byte_t const *data = _binary_xmake_xmz_start; tb_size_t size = _binary_xmake_xmz_end - _binary_xmake_xmz_start; if (!xm_engine_extract_programfiles_impl(engine, programdir, data, size)) { return tb_false; } tb_size_t embedcount = engine->embedcount; for (tb_size_t i = 0; i < embedcount; i++) { data = engine->embeddata[i]; size = engine->embedsize[i]; if (!xm_engine_extract_programfiles_impl(engine, programdir, data, size)) { return tb_false; } } } return tb_true; } #endif static tb_void_t xm_engine_bind_to_lua(lua_State *lua, xm_engine_t *engine) { lua_pushlightuserdata(lua, engine); lua_setglobal(lua, "__global_engine"); } // load and execute the main script static tb_bool_t xm_engine_load_main_script(xm_engine_t *engine, tb_char_t const *mainfile) { #ifdef TB_CONFIG_OS_WINDOWS // use tb_file_init to support unicode file path on windows tb_bool_t ok = tb_false; tb_file_ref_t file = tb_null; tb_byte_t *data = tb_null; do { // open file file = tb_file_init(mainfile, TB_FILE_MODE_RO); if (!file) { tb_printf("error: cannot open file: %s\n", mainfile); break; } // get file size tb_size_t size = (tb_size_t)tb_file_size(file); tb_assert_and_check_break(size); // allocate buffer data = (tb_byte_t *)tb_malloc(size); tb_assert_and_check_break(data); // read file content if (!tb_file_read(file, data, size)) { tb_printf("error: cannot read file: %s\n", mainfile); break; } // load lua buffer if (luaL_loadbuffer(engine->lua, (tb_char_t const *)data, size, mainfile)) { tb_printf("error: %s\n", lua_tostring(engine->lua, -1)); break; } // execute lua script if (lua_pcall(engine->lua, 0, LUA_MULTRET, 0)) { tb_printf("error: %s\n", lua_tostring(engine->lua, -1)); break; } ok = tb_true; } while (0); if (data) { tb_free(data); } if (file) { tb_file_exit(file); } return ok; #else if (luaL_dofile(engine->lua, mainfile)) { tb_printf("error: %s\n", lua_tostring(engine->lua, -1)); return tb_false; } return tb_true; #endif } /* ////////////////////////////////////////////////////////////////////////////////////// * implementation */ xm_engine_ref_t xm_engine_init(tb_char_t const *name, xm_engine_lni_initalizer_cb_t lni_initalizer) { tb_bool_t ok = tb_false; xm_engine_t *engine = tb_null; do { // init self engine = tb_malloc0_type(xm_engine_t); tb_assert_and_check_break(engine); // init name tb_strlcpy(engine->name, name, sizeof(engine->name)); // init lua #if XM_HOOK_LUA_MEMALLOC engine->lua = lua_newstate(xm_engine_lua_realloc, tb_null); #else engine->lua = luaL_newstate(); #endif tb_assert_and_check_break(engine->lua); // open lua libraries luaL_openlibs(engine->lua); // bind os functions xm_lua_register(engine->lua, "os", g_os_functions); // bind io functions xm_lua_register(engine->lua, "io", g_io_functions); // bind path functions xm_lua_register(engine->lua, "path", g_path_functions); // bind hash functions xm_lua_register(engine->lua, "hash", g_hash_functions); // bind lz4 functions xm_lua_register(engine->lua, "lz4", g_lz4_functions); // bind bloom filter functions xm_lua_register(engine->lua, "bloom_filter", g_bloom_filter_functions); // bind base64 functions xm_lua_register(engine->lua, "base64", g_base64_functions); // bind string functions xm_lua_register(engine->lua, "string", g_string_functions); // bind process functions xm_lua_register(engine->lua, "process", g_process_functions); // bind fwatcher functions xm_lua_register(engine->lua, "fwatcher", g_fwatcher_functions); // bind sandbox functions xm_lua_register(engine->lua, "sandbox", g_sandbox_functions); // bind windows functions #ifdef TB_CONFIG_OS_WINDOWS xm_lua_register(engine->lua, "winos", g_winos_functions); #endif #ifdef XM_CONFIG_API_HAVE_READLINE // bind readline functions xm_lua_register(engine->lua, "readline", g_readline_functions); #endif // bind semver functions xm_lua_register(engine->lua, "semver", g_semver_functions); // bind libc functions xm_lua_register(engine->lua, "libc", g_libc_functions); // bind tty functions xm_lua_register(engine->lua, "tty", g_tty_functions); // bind package functions xm_lua_register(engine->lua, "package", g_package_functions); // bind binutils functions xm_lua_register(engine->lua, "binutils", g_binutils_functions); // bind thread functions xm_lua_register(engine->lua, "thread", g_thread_functions); #ifdef XM_CONFIG_API_HAVE_CURSES // bind curses xm_lua_curses_register(engine->lua, "curses"); #endif #ifdef XM_CONFIG_API_HAVE_LUA_CJSON // bind cjson luaopen_cjson(engine->lua); lua_setglobal(engine->lua, "cjson"); #endif // bind engine to lua xm_engine_bind_to_lua(engine->lua, engine); // init host xm_engine_init_host(engine); // init architecture xm_engine_init_arch(engine); // init features xm_engine_init_features(engine); // init signal xm_engine_init_signal(engine); // 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 }; if (tb_strcmp(XM_CONFIG_VERSION_BRANCH, "") && tb_strcmp(XM_CONFIG_VERSION_COMMIT, "")) { tb_snprintf(version_cstr, sizeof(version_cstr), "%u.%u.%u+%s.%s", version->major, version->minor, version->alter, XM_CONFIG_VERSION_BRANCH, XM_CONFIG_VERSION_COMMIT); } else { tb_snprintf(version_cstr, sizeof(version_cstr), "%u.%u.%u+%llu", version->major, version->minor, version->alter, (unsigned long long)version->build); } lua_pushstring(engine->lua, version_cstr); lua_setglobal(engine->lua, "_VERSION"); #ifdef XM_EMBED_ENABLE // init the temporary directory if (!xm_engine_get_temporary_directory(engine->tmpdir, sizeof(engine->tmpdir), name, version_cstr)) { break; } lua_pushboolean(engine->lua, tb_true); lua_setglobal(engine->lua, "_EMBED"); #endif // init short version string tb_snprintf(version_cstr, sizeof(version_cstr), "%u.%u.%u", version->major, version->minor, version->alter); lua_pushstring(engine->lua, version_cstr); lua_setglobal(engine->lua, "_VERSION_SHORT"); // init engine name lua_pushstring(engine->lua, name ? name : "xmake"); lua_setglobal(engine->lua, "_NAME"); // use luajit as runtime? #ifdef USE_LUAJIT lua_pushboolean(engine->lua, tb_true); #else lua_pushboolean(engine->lua, tb_false); #endif lua_setglobal(engine->lua, "_LUAJIT"); // init namespace: xmake lua_newtable(engine->lua); lua_setglobal(engine->lua, "xmake"); /* do lua initializer and init namespace: _lni * * we can get the lni modules for _lni or `import("lib.lni.xxx")` in sandbox */ lua_newtable(engine->lua); if (lni_initalizer) { lni_initalizer((xm_engine_ref_t)engine, engine->lua); } lua_setglobal(engine->lua, "_lni"); #ifdef TB_CONFIG_OS_WINDOWS // enable terminal colors output for windows cmd HANDLE output = GetStdHandle(STD_OUTPUT_HANDLE); if (output != INVALID_HANDLE_VALUE) { DWORD mode; if (GetConsoleMode(output, &mode)) { // attempt to enable 0x4: ENABLE_VIRTUAL_TERMINAL_PROCESSING if (SetConsoleMode(output, mode | 0x4)) { tb_environment_set("COLORTERM", "color256"); } } } #endif ok = tb_true; } while (0); if (!ok) { if (engine) { xm_engine_exit((xm_engine_ref_t)engine); } engine = tb_null; } return (xm_engine_ref_t)engine; } tb_void_t xm_engine_exit(xm_engine_ref_t self) { xm_engine_t *engine = (xm_engine_t *)self; tb_assert_and_check_return(engine); // exit lua if (engine->lua) { lua_close(engine->lua); } engine->lua = tb_null; // exit poller if (engine->poller) { tb_poller_exit(engine->poller); } engine->poller = tb_null; // exit it tb_free(engine); } tb_int_t xm_engine_main(xm_engine_ref_t self, tb_int_t argc, tb_char_t **argv, tb_char_t **taskargv) { xm_engine_t *engine = (xm_engine_t *)self; tb_assert_and_check_return_val(engine && engine->lua, -1); #if defined(TB_CONFIG_OS_WINDOWS) && defined(TB_COMPILER_IS_MSVC) // set "stdin" to have unicode mode if (_isatty(_fileno(stdin))) { _setmode(_fileno(stdin), _O_U16TEXT); } #endif // save main arguments to the global variable: _ARGV if (!xm_engine_save_arguments(engine, argc, argv, taskargv)) { return -1; } // get the project directory tb_char_t path[TB_PATH_MAXN] = { 0 }; if (!xm_engine_get_project_directory(engine, path, sizeof(path))) { return -1; } // get the program file if (!xm_engine_get_program_file(engine, argv, path, sizeof(path))) { return -1; } // get the program directory if (!xm_engine_get_program_directory(engine, path, sizeof(path), path)) { return -1; } #ifdef XM_EMBED_ENABLE if (!xm_engine_extract_programfiles(engine, path)) { return -1; } #endif // append the main script path tb_strcat(path, "/core/_xmake_main.lua"); // exists this script? if (!tb_file_info(path, tb_null)) { tb_printf("not found main script: %s\n", path); return -1; } tb_trace_d("main: %s", path); // load and execute the main script if (!xm_engine_load_main_script(engine, path)) { return -1; } // set the error function lua_getglobal(engine->lua, "debug"); lua_getfield(engine->lua, -1, "traceback"); // call the main function lua_getglobal(engine->lua, "_xmake_main"); if (lua_pcall(engine->lua, 0, 1, -2)) { tb_printf("error: %s\n", lua_tostring(engine->lua, -1)); return -1; } // get the error code return (tb_int_t)lua_tonumber(engine->lua, -1); } tb_void_t xm_engine_register(xm_engine_ref_t self, tb_char_t const *module, luaL_Reg const funcs[]) { xm_engine_t *engine = (xm_engine_t *)self; tb_assert_and_check_return(engine && engine->lua && module && funcs); // do register lua_pushstring(engine->lua, module); lua_newtable(engine->lua); xm_lua_register(engine->lua, tb_null, funcs); lua_rawset(engine->lua, -3); } #ifdef XM_EMBED_ENABLE tb_void_t xm_engine_add_embedfiles(xm_engine_ref_t self, tb_byte_t const *data, tb_size_t size) { xm_engine_t *engine = (xm_engine_t *)self; tb_assert_and_check_return(engine && engine->embedcount < tb_arrayn(engine->embedsize) && data && size); engine->embeddata[engine->embedcount] = data; engine->embedsize[engine->embedcount] = size; engine->embedcount++; } #endif lua_State *xm_engine_lua(xm_engine_ref_t self) { xm_engine_t *engine = (xm_engine_t *)self; tb_assert_and_check_return_val(engine, tb_null); return engine->lua; } tb_poller_ref_t xm_engine_poller(xm_engine_ref_t self) { xm_engine_t *engine = (xm_engine_t *)self; tb_assert_and_check_return_val(engine, tb_null); if (!engine->poller) { // init poller engine->poller_state.lua = engine->lua; tb_poller_ref_t poller = tb_poller_init(&engine->poller_state); tb_assert_and_check_return_val(poller, tb_null); // attach poller to the current thread tb_poller_attach(poller); engine->poller = poller; } return engine->poller; } tb_int_t xm_engine_run(tb_char_t const *name, tb_int_t argc, tb_char_t **argv, tb_char_t **taskargv, xm_engine_lni_initalizer_cb_t lni_initalizer) { tb_int_t ok = -1; if (xm_init()) { xm_engine_ref_t engine = xm_engine_init(name, lni_initalizer); if (engine) { ok = xm_engine_main(engine, argc, argv, taskargv); xm_engine_exit(engine); } xm_exit(); } return ok; } xm_engine_ref_t xm_engine_get(lua_State *lua) { tb_assert_and_check_return_val(lua, tb_null); lua_getglobal(lua, "__global_engine"); return (xm_engine_ref_t)lua_touserdata(lua, -1); }