initial import, built with mingw64-ucrt64-gcc, works on lua5.4 currently

This commit is contained in:
Matt Boney 2023-10-28 14:34:14 -06:00
parent 18acf3a48e
commit a09b951d55
20 changed files with 409 additions and 4 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
build/

19
CMakeLists.txt Normal file
View file

@ -0,0 +1,19 @@
cmake_minimum_required(VERSION 3.25)
project(lesi)
option(USE_STATIC_LUA "Link to static lua library" OFF)
option(USE_LUA_52 "Use Lua 5.1" OFF)
option(USE_LUA_51 "Use Lua 5.2" OFF)
option(USE_LUA_54 "Use Lua 5.3" OFF)
option(USE_LUA_53 "Use Lua 5.3" OFF)
include("${CMAKE_SOURCE_DIR}/config/lua_config.cmake")
message(STATUS "Binary: ${LUA_BINARY}")
message(STATUS "Library: ${LUA_LIBRARY}")
message(STATUS "Including: ${LUA_INCLUDE}")
add_subdirectory(scripts)
add_subdirectory(src)
install(TARGETS lesi RUNTIME DESTINATION ${CMAKE_BINARY_DIR} LIBRARY DESTINATION ${CMAKE_BINARY_DIR})

8
config/lua51.cmake Normal file
View file

@ -0,0 +1,8 @@
set (LUA_BINARY_PATH_SUFFIX)
set (LUA_BINARY_NAME lua51 lua5.1)
set (LUA_LIBRARY_PATH_SUFFIX)
set (LUA_LIBRARY_NAME lua51 lua5.1)
set (LUA_STATIC_NAME liblua5.1.a)
set (LUA_INCLUDE_PATH_SUFFIX lua5.1 5.1)
set (LUA_HEADER_NAME lua.h)
set (LUA_TARGET_VERSION "501")

9
config/lua52.cmake Normal file
View file

@ -0,0 +1,9 @@
set (LUA_BINARY_PATH_SUFFIX)
set (LUA_BINARY_NAME lua52 lua5.2)
set (LUA_LIBRARY_PATH_SUFFIX)
set (LUA_LIBRARY_NAME lua52 lua5.2)
set (LUA_STATIC_NAME liblua5.2.a)
set (LUA_INCLUDE_PATH_SUFFIX lua5.2 5.2)
set (LUA_HEADER_NAME lua.h)
set (LUA_TARGET_VERSION "502")

8
config/lua53.cmake Normal file
View file

@ -0,0 +1,8 @@
set (LUA_BINARY_PATH_SUFFIX)
set (LUA_BINARY_NAME lua51 lua5.3)
set (LUA_LIBRARY_PATH_SUFFIX)
set (LUA_LIBRARY_NAME lua51 lua5.3)
set (LUA_STATIC_NAME liblua5.3.a)
set (LUA_INCLUDE_PATH_SUFFIX lua5.3 5.3)
set (LUA_HEADER_NAME lua.h)
set (LUA_TARGET_VERSION "503")

8
config/lua54.cmake Normal file
View file

@ -0,0 +1,8 @@
set (LUA_BINARY_PATH_SUFFIX)
set (LUA_BINARY_NAME lua)
set (LUA_LIBRARY_PATH_SUFFIX)
set (LUA_LIBRARY_NAME lua)
set (LUA_STATIC_NAME liblua.a)
set (LUA_INCLUDE_PATH_SUFFIX)
set (LUA_HEADER_NAME lua.h)
set (LUA_TARGET_VERSION "504")

27
config/lua_config.cmake Normal file
View file

@ -0,0 +1,27 @@
if(USE_LUA_54)
include("${CMAKE_SOURCE_DIR}/config/lua54.cmake")
elseif(USE_LUA_53)
include("${CMAKE_SOURCE_DIR}/config/lua53.cmake")
elseif(USE_LUA_52)
include("${CMAKE_SOURCE_DIR}/config/lua52.cmake")
elseif(USE_LUA_51)
include("${CMAKE_SOURCE_DIR}/config/lua51.cmake")
else()
include("${CMAKE_SOURCE_DIR}/config/lua_hardcoded.cmake")
endif()
if(USE_STATIC_LUA)
set(LUA_LIBRARY_NAME ${LUA_STATIC_NAME})
endif()
function(check_lua_version find_result_var candidate_path)
file(STRINGS ${candidate_path}/lua.h LUA_VERSION_NUM REGEX "LUA_VERSION_NUM[ \t]+[0-9]+")
string(FIND ${LUA_VERSION_NUM} ${LUA_TARGET_VERSION} ${find_result_var})
if(${find_result_var} LESS 0)
set(${find_result_var} FALSE PARENT_SCOPE)
endif()
endfunction()
find_program(LUA_BINARY NAMES ${LUA_BINARY_NAME} HINTS ${LUA_BINARY_PATH} PATH_SUFFIXES ${LUA_BINARY_PATH_SUFFIX})
find_library(LUA_LIBRARY NAMES ${LUA_LIBRARY_NAME} HINTS ${LUA_LIBRARY_PATH} PATH_SUFFIXES ${LUA_LIBRARY_PATH_SUFFIX})
find_path(LUA_INCLUDE ${LUA_HEADER_NAME} HINTS ${LUA_INCLUDE_PATH} PATH_SUFFIXES ${LUA_INCLUDE_PATH_SUFFIX} VALIDATOR check_lua_version)

View file

@ -0,0 +1,17 @@
### Full path to lua command line interpreter.
set(LUA_BINARY "u:/msys64/ucrt64/bin/lua.exe")
### Full path to lua.h, lua.hpp, lauxlib.h, luaconf.h, lualib.h
set(LUA_INCLUDE "u:/msys64/ucrt64/include")
### Full path to lua library.
#### Can be 1 of 3 options:
#### 1) Full path to the static library (liblua.a)
#### 2) Full path to the shared library (lua.dll/lua.so)
#### 3) Full path to the import library (liblua.dll.a) (Windows Specific)
if(USE_STATIC_LUA)
set(LUA_LIBRARY "u:/msys64/ucrt64/lib/liblua.a")
else()
#set(LUA_LIBRARY "u:/msys64/ucrt64/bin/lua54.dll")
set(LUA_LIBRARY "u:/msys64/ucrt64/lib/liblua.dll.a")
endif()

View file

@ -0,0 +1 @@
set(SCRIPT_SOURCES main.lua helloworld.lua)

28
scripts/CMakeLists.txt Normal file
View file

@ -0,0 +1,28 @@
include("${CMAKE_SOURCE_DIR}/config/scripts_to_embed.cmake")
set(SCRIPT_SOURCES_C)
file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/generated)
foreach(script_source ${SCRIPT_SOURCES})
get_filename_component(GEN_BASE_NAME ${script_source} NAME_WLE)
set(file_c "script_${GEN_BASE_NAME}.c")
add_custom_command(
OUTPUT ${CMAKE_BINARY_DIR}/generated/${file_c}
COMMAND ${LUA_BINARY} ${CMAKE_SOURCE_DIR}/src/lua2c.lua ${script_source} ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_BINARY_DIR}/generated
VERBATIM
COMMENT "Generating: ${CMAKE_BINARY_DIR}/generated/${file_c}"
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${script_source}
)
list(APPEND SCRIPT_BASE_NAMES ${GEN_BASE_NAME})
list(APPEND SCRIPT_SOURCES_C ${CMAKE_BINARY_DIR}/generated/${file_c})
endforeach()
add_custom_command(
OUTPUT ${CMAKE_BINARY_DIR}/generated/generated_scripts.c
COMMAND ${LUA_BINARY} ${CMAKE_SOURCE_DIR}/src/embedded_generate.lua ${CMAKE_BINARY_DIR}/generated ${SCRIPT_BASE_NAMES}
COMMENT "Generating: ${CMAKE_BINARY_DIR}/generated/generated_scripts.c"
DEPENDS ${SCRIPT_SOURCES_C}
)
add_custom_target(generate_scripts DEPENDS ${CMAKE_BINARY_DIR}/generated/generated_scripts.c)

7
scripts/helloworld.lua Normal file
View file

@ -0,0 +1,7 @@
local M = {}
function M.hw()
print(_VERSION, "Hello World!")
end
return M

7
scripts/main.lua Normal file
View file

@ -0,0 +1,7 @@
local hw = assert(require("helloworld"))
print("Start: main.")
hw.hw()
local arg = { select(1, ...) }
for i, v in ipairs(arg) do print(i, v) end
print("main done.")

10
src/CMakeLists.txt Normal file
View file

@ -0,0 +1,10 @@
include_directories($LUA_INCLUDE})
# Required for ${CMAKE_BINARY_DIR}/generated/generated_scripts.c to find generated_scripts.h
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
include_directories(${CMAKE_BINARY_DIR}/generated)
set(SRC embedded_script.c lua54.c ${CMAKE_BINARY_DIR}/generated/generated_scripts.c)
add_executable(lesi ${SRC})
add_dependencies(lesi generate_scripts)
target_link_libraries(lesi ${LUA_LIBRARY})

37
src/embedded_generate.lua Normal file
View file

@ -0,0 +1,37 @@
local embedded_header = [[
#include <generated_scripts.h>
]]
local struct_header = [[
struct generated_script generated_scripts[] =
{
]]
local generated_file = select(1, ...) .. "/generated_scripts.c"
local script_names = { select(2, ...) }
local outf = io.open(generated_file, "w")
if outf then
outf:write(embedded_header)
for i = 1, #script_names do
outf:write(string.format('#include "script_%s.c"\n', script_names[i]))
end
outf:write("\n",struct_header)
for i = 1, #script_names do
if script_names[i] ~= main then
outf:write(string.format('\t{ "%s", script_%s, script_%s_size },\n', script_names[i], script_names[i], script_names[i]))
end
end
outf:write("\t{ NULL, NULL, 0 }\n};\n")
outf:close()
else
print("couldn't open: ", generated_file)
return 1
end
return 0

94
src/embedded_script.c Normal file
View file

@ -0,0 +1,94 @@
#include <string.h>
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
#include "generated_scripts.h"
int embedded_script_searcher(lua_State *L)
{
int status = 0;
const char* module_name = luaL_checkstring(L, 1);
for (size_t i = 0; generated_scripts[i].name != NULL; ++i)
{
struct generated_script *gs = &generated_scripts[i];
if (strcmp(module_name, gs->name) == 0)
{
int rv = luaL_loadbuffer(L, gs->data, gs->data_size, gs->name);
if (rv == LUA_OK)
{
lua_pushvalue(L, 1);
status = 2;
}
}
}
return status;
}
int embedded_script_enable_searcher(lua_State* L)
{
int status = 0;
int top = lua_gettop(L);
// Lua equivalent
// package.searchers[#package.searchers + 1] = embedded_script_searcher
// putting this at the end of the searchers table enables overriding the embedded scripts of the same name.
if (lua_getglobal(L, "package") == LUA_TTABLE)
{
lua_getfield(L, -1, "searchers");
if (lua_type(L, -1) == LUA_TTABLE)
{
lua_pushcfunction(L, embedded_script_searcher);
lua_len(L, -2);
lua_Integer searcher_count = lua_tointeger(L, -1);
lua_pop(L, 1);
lua_rawseti(L, -2, searcher_count + 1);
status = 1;
}
}
// function not called by Lua VM, clean up our own stack.
lua_settop(L, top);
return status;
}
int embedded_script_insert_script(lua_State *L, char ***argv, int *argc, int first_arg)
{
char **oldv = *argv;
char **margv = NULL;
// skip -1, and '-' conditions.
if (first_arg < 0) { return 0; }
else if (first_arg > 0 && strcmp(oldv[first_arg], "-") == 0) { return 0; }
margv = lua_newuserdata(L, (*argc + 1) * sizeof *margv);
lua_setfield(L, LUA_REGISTRYINDEX, "script_main_argv");
// no script, insert script at the end.
if (first_arg == 0)
{
for (int i = 0; i < *argc; ++i) { margv[i] = oldv[i]; }
margv[*argc] = "script_main";
}
// insert script at before arguments lesi.
else
{
int offset = 0;
for (int i = 0; i < *argc; ++i)
{
if (i == first_arg) { margv[i + offset] = "script_main"; offset = 1; }
margv[i + offset] = oldv[i];
}
}
*argv = margv;
++(*argc);
return *argv != oldv;
}
int embedded_script_load_main(lua_State *L)
{
return luaL_loadbuffer(L, script_main, script_main_size, "main");
}

10
src/embedded_script.h Normal file
View file

@ -0,0 +1,10 @@
#ifndef EMBEDDED_SCRIPT_H
#define EMBEDDED_SCRIPT_H
#include <lua.h>
int embedded_script_enable_searcher(lua_State *L);
int embedded_script_load_main(lua_State *L);
int embedded_script_insert_script(lua_State *L, char ***argv, int *argc, int first_arg);
#endif

18
src/generated_scripts.h Normal file
View file

@ -0,0 +1,18 @@
#ifndef GENERATED_SCRIPTS_H
#define GENERATED_SCRIPTS_H
#include <stddef.h>
struct generated_script
{
const char *name;
const char *data;
size_t data_size;
};
extern const unsigned char script_main[];
extern const size_t script_main_size;
extern struct generated_script generated_scripts[];
#endif // GENERATED_SCRIPTS_H

View file

@ -1,7 +1,7 @@
/*
** $Id: lprefix.h $
** Definitions for Lua code that must come before any other header file
** See Copyright Notice in lua.h
** See Copyright Notice at bottom of file.
*/
#ifndef lprefix_h
@ -43,3 +43,25 @@
#endif
/******************************************************************************
* Copyright (C) 1994-2017 Lua.org, PUC-Rio.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
******************************************************************************/

48
src/lua2c.lua Normal file
View file

@ -0,0 +1,48 @@
function lua2c(base_name, destination_file, file_contents)
local result
local fd = io.open(destination_file, "w")
if fd then
fd:write(string.format("const size_t script_%s_size = %u;\n", base_name, #file_contents))
fd:write(string.format("const unsigned char script_%s[%u] = {\n", base_name, #file_contents))
fd:write("\t")
for i = 1, #file_contents do
fd:write(string.format("0x%02x", string.byte(file_contents, i)))
if i ~= #file_contents then fd:write(", ") end
if math.fmod(i, 16) == 0 then fd:write("\n\t") end
end
fd:write("\n};")
fd:close()
result = true
end
return result
end
function get_file_contents(filename)
local file_contents
local fd = io.open(filename, "r")
if fd then
file_contents = fd:read("*a")
fd:close()
end
return file_contents
end
function main(filename, source, destination)
local exit_code = 1
local base_name = filename:gsub("%.lua$", "")
local destination_file = string.format("%s/script_%s.c", destination, base_name)
local file_contents = get_file_contents(string.format("%s/%s", source, filename))
if file_contents and lua2c(base_name, destination_file, file_contents) then
exit_code = 0
end
end
local filename, source, destination = assert(select(1, ...))
os.exit(main(filename, source, destination))

View file

@ -1,7 +1,7 @@
/*
** $Id: lua.c $
** Lua stand-alone interpreter
** See Copyright Notice in lua.h
** See Copyright Notice at end of file.
*/
#define lua_c
@ -20,6 +20,7 @@
#include "lauxlib.h"
#include "lualib.h"
#include "embedded_script.h"
#if !defined(LUA_PROGNAME)
#define LUA_PROGNAME "lua"
@ -249,8 +250,9 @@ static int handle_script (lua_State *L, char **argv) {
int status;
const char *fname = argv[0];
if (strcmp(fname, "-") == 0 && strcmp(argv[-1], "--") != 0)
fname = NULL; /* stdin */
status = luaL_loadfile(L, fname);
status = luaL_loadfile(L, NULL); /* stdin */
else
status = embedded_script_enable_searcher(L) ? embedded_script_load_main(L) : LUA_ERRERR;
if (status == LUA_OK) {
int n = pushargs(L); /* push arguments to script */
status = docall(L, n, LUA_MULTRET);
@ -619,6 +621,8 @@ static int pmain (lua_State *L) {
char **argv = (char **)lua_touserdata(L, 2);
int script;
int args = collectargs(argv, &script);
if (args != has_error) { embedded_script_insert_script(L, &argv, &argc, script); }
args = collectargs(argv, &script);
int optlim = (script > 0) ? script : argc; /* first argv not an option */
luaL_checkversion(L); /* check that interpreter has correct version */
if (args == has_error) { /* bad arg? */
@ -677,3 +681,25 @@ int main (int argc, char **argv) {
return (result && status == LUA_OK) ? EXIT_SUCCESS : EXIT_FAILURE;
}
/******************************************************************************
* Copyright (C) 1994-2017 Lua.org, PUC-Rio.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
******************************************************************************/