Compare commits

...

9 Commits

Author SHA1 Message Date
2bcbc62bf8 Why do I need to merge this?! 2025-03-10 20:59:05 +11:00
b4ed4aa4c0 Fixed issue where first process is null 2025-03-10 20:57:05 +11:00
df20c55b4e Fixed issue where first process is null 2025-03-10 20:55:04 +11:00
af6d3223da Added libgit2 dependency 2025-03-10 20:11:21 +11:00
68e2c49c8f Updated lbs to use new uniproc library
Fixed bug in fs.list_alldirs where it used directory_iterator rather that recursive_directory_iterator
Fixed memory/type bugs in lua_platform.cpp
Updated lbs.lua to reflect this
2025-03-06 19:27:19 +11:00
40ea3caf2a Added ability to switch between printing stdout and stderr in error messages 2025-03-06 17:59:13 +11:00
667151b1b6 Replaced popen usage with uniproc_process 2025-03-06 17:54:53 +11:00
fe01a41aac Added fs.is_newer function to filesystem library 2025-03-06 17:54:26 +11:00
dbf91e0ebc Added uniproc library to project 2025-03-06 17:52:50 +11:00
11 changed files with 457 additions and 237 deletions

3
.gitignore vendored
View File

@@ -14,3 +14,6 @@
/.make/thirdparty_luajit /.make/thirdparty_luajit
/CppProperties.json /CppProperties.json
/lbs.exe /lbs.exe
/build/lbs.ilk
/obj/thirdparty/uniproc/src
/releases

6
.gitmodules vendored
View File

@@ -1,3 +1,9 @@
[submodule "thirdparty/luajit"] [submodule "thirdparty/luajit"]
path = thirdparty/luajit path = thirdparty/luajit
url = https://luajit.org/git/luajit.git url = https://luajit.org/git/luajit.git
[submodule "thirdparty/uniproc"]
path = thirdparty/uniproc
url = ssh://gitea@git.rileyk.au:19482/riley/uniproc.git
[submodule "thirdparty/libgit2"]
path = thirdparty/libgit2
url = https://github.com/libgit2/libgit2.git

View File

@@ -20,6 +20,11 @@ int lua_fs_forall_dir_next(lua_State* L);
int lua_fs_foreach_dir_dtor(lua_State* L); int lua_fs_foreach_dir_dtor(lua_State* L);
int lua_fs_forall_dir_dtor(lua_State* L); int lua_fs_forall_dir_dtor(lua_State* L);
// Returns true when a is newer than b. If b does not exist it returns true. If a does not exist it returns false
// boolean | table<boolean> function fs.is_newer(path_a: string | number, path_b: string | table[string])
int lua_fs_is_newer(lua_State* L);
// table<string> | nil function fs.filter_newer(path_a: string | number, path_b: string | table[string])
int lua_fs_filter_newer(lua_State* L);
// number | nil function fs.last_modified(path: string) // number | nil function fs.last_modified(path: string)
int lua_fs_last_modified(lua_State* L); int lua_fs_last_modified(lua_State* L);

View File

@@ -10,11 +10,11 @@
int luaopen_platform(lua_State* L); int luaopen_platform(lua_State* L);
// Returns the exit code of the command as well as a string of its stdout // Returns the exit code of the command as well as a string of its stdout
// int, string function platform.exec(cmd: string, args: array<string>) // int, string, string function platform.exec(cmd: string, args: array<string>)
int lua_platform_exec(lua_State* L); int lua_platform_exec(lua_State* L);
// Executes a command asynchronosly, returning a promise to the functions return values // Executes a command asynchronosly, returning a promise to the functions return values
// promise<int>, promise<string> function platform.exec_async(cmd: string, args: array<string>) // promise<int>, promise<string>, promise<string> function platform.exec_async(cmd: string, args: array<string>)
int lua_platform_exec_async(lua_State* L); int lua_platform_exec_async(lua_State* L);
// Executes a command asynchronosly, calling a callback on its completion with its error code and // Executes a command asynchronosly, calling a callback on its completion with its error code and
@@ -22,13 +22,14 @@ int lua_platform_exec_async(lua_State* L);
// function platform.exec_async_cb(cmd: string, // function platform.exec_async_cb(cmd: string,
// function on_complete(cmd: string, exit_code: int, overall_output: string), // function on_complete(cmd: string, exit_code: int, overall_output: string),
// function on_stdout(cmd: string, overall_output: string, delta_output: string) // function on_stdout(cmd: string, overall_output: string, delta_output: string)
// function on_stderr(cmd: string, overall_output: string, delta_output: string)
// ) // )
int lua_platform_exec_async_cb(lua_State* L); int lua_platform_exec_async_cb(lua_State* L);
// Executes the same command up to N times in parallel, iterating through the args provided until all arguments have been used // Executes the same command up to N times in parallel, iterating through the args provided until all arguments have been used
// array[promise<int>, promise<string>] function platform.exec_parallel_async(cmd: string, N: int, args: array<array<string>>) // array[promise<int>, promise<string>, promise<string>] function platform.exec_parallel_async(cmd: string, N: int, args: array<array<string>>)
int lua_platform_exec_parallel_async(lua_State* L); int lua_platform_exec_parallel_async(lua_State* L);
// array<int>, array<string> function platform.exec_parallel(cmd: string, N: int, args: array<array<string>>) // array<int>, array<string>, array<string> function platform.exec_parallel(cmd: string, N: int, args: array<array<string>>)
int lua_platform_exec_parallel(lua_State* L); int lua_platform_exec_parallel(lua_State* L);

133
lbs.lua
View File

@@ -7,14 +7,14 @@ local config = {
} }
local program_output = "build/lbs" local program_output = "build/lbs"
local source_dirs = {"src"} local source_dirs = {"src", "thirdparty/uniproc/src"}
local include_dirs = {"include", "src", "thirdparty/luajit/src"} local include_dirs = {"include", "src", "thirdparty/luajit/src", "thirdparty/uniproc/include"}
local library_dirs = {"thirdparty/luajit/src"} local library_dirs = {"thirdparty/luajit/src"}
local linker_inputs = {"lua51", "luajit"} local linker_inputs = {"lua51", "luajit"}
local defines = {} local defines = {}
local additional_arguments = {"-std=c++20"} local additional_arguments = {"-std=c++20"}
function load_config(cfg_table, args) local function load_config(cfg_table, args)
for i=1,#args,1 do for i=1,#args,1 do
k = args[i] k = args[i]
if (k:sub(1,2) == "-j") then if (k:sub(1,2) == "-j") then
@@ -34,61 +34,99 @@ function load_config(cfg_table, args)
return cfg_table return cfg_table
end end
local function compile() function table.shallow_copy(tbl)
local new = {}
for k,v in pairs(tbl) do
new[k] = v
end
return new
end
local base_cmd = CXX .. " -c " local function get_required_objects(dirs, src_extension, obj_prefix, obj_postfix)
obj_prefix = obj_prefix or "obj/"
for _,v in pairs(include_dirs) do base_cmd = base_cmd .. "-I\"" .. v .. "\" " end obj_postfix = obj_postfix or ".o"
for k,v in pairs(defines) do base_cmd = base_cmd .. "-D" .. k .. "=" .. v .. " " end local objs = {}
for _,v in pairs(additional_arguments) do base_cmd = base_cmd .. v .. " " end local new_src = {}
for _,dir in pairs(dirs) do
local src_args = {} for fsobj in fs.forall(dir) do
for _,src_dir in pairs(source_dirs) do -- Get all files in the directory
if (fs.is_dir(src_dir)) then local src_files
for fsobj in fs.forall(src_dir) do if fs.is_dir(fsobj) then src_files = fs.list_alldirs(fsobj)
if (fs.is_file(fsobj) and fs.extension(fsobj) == ".cpp") then else src_files = {fsobj} end
src_args[#src_args+1] = fsobj .. " -o obj/" .. fsobj .. ".o" -- For each of the files,
for _, src_file in pairs(src_files) do repeat
-- Skip files that don't end in the provided extension
if (fs.extension(src_file) ~= src_extension) then break end
local obj_file = obj_prefix .. src_file .. obj_postfix
-- Skip old files that do not need to be recompiled
if not fs.is_newer(src_file, obj_file) then break end
-- Add the src and obj file to a table that will be returned
objs[#objs+1] = obj_file
new_src[#new_src+1] = src_file
until true end
end end
end end
else return objs, new_src
print("[WARN ] Source directory " .. src_dir .. " does not exist!") end
local function compile(CXX, src_dirs, include_dirs, defines, additional_arguments)
local base_argv = {}
additional_arguments[#additional_arguments+1] = "-c"
if (config.buildmode == "debug") then
additional_arguments[#additional_arguments+1] = "-O0"
additional_arguments[#additional_arguments+1] = "-g"
end end
for _,v in pairs(additional_arguments) do base_argv[#base_argv+1] = v end
for _,v in pairs(include_dirs) do base_argv[#base_argv+1] = "-I\"" .. v .. "\"" end
for k,v in pairs(defines) do base_argv[#base_argv+1] = "-D" .. k .. "=" .. v end
local obj_files, src_files = get_required_objects(src_dirs, ".cpp")
local argv = {}
for k,obj_file in pairs(obj_files) do
local args = table.shallow_copy(base_argv)
args[#args+1] = src_files[k]
args[#args+1] = "-o\"" .. obj_file .. "\""
argv[#argv+1] = args
end end
ec, stdout = platform.exec_parallel(base_cmd, config.max_parallel, src_args) print("Compiling...")
local ec, stdout, stderr = platform.exec_parallel(CXX, config.max_parallel, argv)
for i,ec in pairs(ec) do for i,ec in pairs(ec) do
if (ec ~= 0) then if (ec ~= 0) then
print("Command \"" .. base_cmd .. src_args[i] .. "\" failed with exit code " .. tostring(ec) .. "and output;") print("Command ".. tostring(i) .." \"" .. CXX .. " " .. table.concat(argv[i], " ") .. "\" failed with exit code " .. tostring(ec) .. " and output;")
print(stderr[i])
print(stdout[i]) print(stdout[i])
print() print()
end end
end end
end end
local function link() local function link(LNK, obj_dirs, library_dirs, linker_inputs, additional_arguments, file_output)
local cmd = CXX .. " " local argv = {}
for _,lib_dir in pairs(library_dirs) do cmd = cmd .. "-L\"" .. lib_dir .. "\" " end if (config.buildmode == "debug") then
for _,lnk in pairs(linker_inputs) do cmd = cmd .. "-l\"" .. lnk .. "\" " end additional_arguments[#additional_arguments+1] = "-O0"
additional_arguments[#additional_arguments+1] = "-g"
end
for _,v in pairs(library_dirs) do argv[#argv+1] = "-L\"" .. v .. "\"" end
for _,v in pairs(linker_inputs) do argv[#argv+1] = "-l\"" .. v .. "\"" end
for _,v in pairs(additional_arguments) do argv[#argv+1] = v end
for _,src_dir in pairs(source_dirs) do for _,obj_dir in pairs(obj_dirs) do
if (fs.is_dir(src_dir)) then local obj_files = fs.list_alldirs("obj")
for fsobj in fs.forall(src_dir) do for _,obj_file in pairs(obj_files) do repeat
if (fs.is_file(fsobj) and fs.extension(fsobj) == ".cpp") then if fs.extension(obj_file) ~= ".o" then break end
cmd = cmd .. "obj/" .. fsobj .. ".o " argv[#argv+1] = obj_file
until true end
end end
end
else
print("[WARN ] Source directory " .. src_dir .. " does not exist!")
end
end
cmd = cmd .. "-o " .. program_output
local platform_seps = {}
for match in config.target:gmatch("[^-]+") do platform_seps[#platform_seps+1] = match end
if (platform_seps[2] == "windows") then cmd = cmd .. ".exe " else cmd = cmd .. " " end
ec, stdout = platform.exec(cmd, {}) argv[#argv+1] = "-o " .. file_output
print("Linking...")
local ec, stdout, stderr = platform.exec(LNK, argv)
if (ec ~= 0) then if (ec ~= 0) then
print("Command \"" .. cmd .. "\" failed with exit code " .. tostring(ec) .. " and output;") print("Command \"" .. LNK .. " " .. table.concat(argv, " ") .. "\" failed with exit code " .. tostring(ec) .. " and output;")
print(stderr)
print(stdout) print(stdout)
print() print()
end end
@@ -96,10 +134,21 @@ end
function build(...) function build(...)
local args = {...} local args = {...}
local program_name = program_output
for section in config.target:gmatch("[^-]+") do
if section == "windows" then program_name = program_name .. ".exe" end
end
config = load_config(config, args) config = load_config(config, args)
print("Building " .. config.buildmode .. " with up to " .. tostring(config.max_parallel) .. " concurrent jobs") print("Building " .. config.buildmode .. " with up to " .. tostring(config.max_parallel) .. " concurrent jobs")
compile()
link() compile(CXX, source_dirs, include_dirs, defines, additional_arguments)
link(CXX, {"obj"}, library_dirs, linker_inputs, {}, program_name)
print("Program generated! See " .. program_name)
end end
function main(...) build(...) end
return 0 return 0

View File

@@ -1,13 +1,15 @@
CXX := clang++ CXX := clang++
CC := clang
INCLUDES := include src thirdparty/luajit/src INCLUDES := include src thirdparty/luajit/src thirdparty/uniproc/include thirdparty/libgit2/include
LIBRARY_DIRECTORIES := thirdparty/luajit/src LIBRARY_DIRECTORIES := thirdparty/luajit/src thirdparty/libgit2
LINKER_INPUTS := lua51 luajit LINKER_INPUTS := lua51 luajit
DEBUG_DEFINES := DEBUG_DEFINES :=
DEFINES := DEFINES :=
CXX_FLAGS := -std=c++20 -g -O0 CXX_FLAGS := -std=c11 -std=c++20 -g -O0
CC_FLAGS := -std=c11 -g -O0
CXX_DEBUG_FLAGS := CXX_DEBUG_FLAGS :=
LNK_FLAGS := -g -O0 LNK_FLAGS := -g -O0
LNK_DEBUG_FLAGS := LNK_DEBUG_FLAGS :=
@@ -41,19 +43,30 @@ endif
clean: clean:
$(call RM,$(call rwildcard, obj,*.o) build/$(OUTPUT_NAME)) $(call RM,$(call rwildcard, obj,*.o) build/$(OUTPUT_NAME))
SOURCE_FILES := $(call rwildcard,src,*.cpp,*.hpp) SOURCE_FILES := $(call rwildcard,src,*.cpp,*.hpp) $(call rwildcard,thirdparty/uniproc/src,*.c)
OBJECT_FILES := $(foreach d,$(addsuffix .o,$(SOURCE_FILES)), obj/$(d)) OBJECT_FILES := $(foreach d,$(addsuffix .o,$(SOURCE_FILES)), obj/$(d))
obj/thirdparty/uniproc/src/%.c.o: thirdparty/uniproc/src/%.c
$(CC) -c $(CC_FLAGS) $(foreach inc,$(INCLUDES),-I$(inc)) $(foreach def,$(DEFINES),-D$(def)) $< -o $@
obj/src/%.cpp.o: src/%.cpp obj/src/%.cpp.o: src/%.cpp
$(CXX) -c $(CXX_FLAGS) $(foreach inc,$(INCLUDES),-I$(inc)) $(foreach def,$(DEFINES),-D$(def)) $< -o $@ $(CXX) -c $(CXX_FLAGS) $(foreach inc,$(INCLUDES),-I$(inc)) $(foreach def,$(DEFINES),-D$(def)) $< -o $@
obj/src/%.hpp.o: src/%.hpp
link_lbs: $(OBJECT_FILES) link_lbs: $(OBJECT_FILES)
$(CXX) $(LNK_FLAGS) $(LNK_FLAGS) $(foreach lib,$(LIBRARY_DIRECTORIES),-L$(lib)) $(foreach lnk,$(LINKER_INPUTS),-l$(lnk)) $^ -o build/$(OUTPUT_NAME) $(CXX) $(LNK_FLAGS) $(LNK_FLAGS) $(foreach lib,$(LIBRARY_DIRECTORIES),-L$(lib)) $(foreach lnk,$(LINKER_INPUTS),-l$(lnk)) $^ -o build/$(OUTPUT_NAME)
build_deps: .make/thirdparty_luajit build_deps: .make/thirdparty_luajit .make/thirdparty_uniproc
.make/thirdparty_luajit: .make/thirdparty_luajit:
git submodule update --init --recursive git submodule update --init --recursive --remote
cd thirdparty/luajit/src $(PLATFORM_SEPERATOR) $(LUAJUT_BUILD_CMD) cd thirdparty/luajit/src $(PLATFORM_SEPERATOR) $(LUAJUT_BUILD_CMD)
$(call touch,".make/thirdparty_luajit") $(call touch,".make/thirdparty_luajit")
.make/thirdparty_uniproc:
git submodule update --init --recursive --remote
$(call touch,".make/thirdparty_uniproc")
.make/thirdparty_libgit2:
git submodule update --init --recursive --remote
cd thirdparty/libgit2 && mkdir build
cd thirdparty/libgit2/build && cmake .. && cmake --build .
$(call touch,".make/thirdparty_libgit2")

View File

@@ -1,5 +1,6 @@
#include "lua_filesystem.h" #include "lua_filesystem.h"
#include <filesystem> #include <filesystem>
#include <chrono>
int luaopen_filesystem(lua_State* L) int luaopen_filesystem(lua_State* L)
{ {
@@ -18,6 +19,12 @@ int luaopen_filesystem(lua_State* L)
// Push functions // Push functions
lua_newtable(L); lua_newtable(L);
lua_pushcfunction(L, lua_fs_is_newer);
lua_setfield(L, -2, "is_newer");
lua_pushcfunction(L, lua_fs_filter_newer);
lua_setfield(L, -2, "filter_newer");
lua_pushcfunction(L, lua_fs_absolute); lua_pushcfunction(L, lua_fs_absolute);
lua_setfield(L, -2, "absolute"); lua_setfield(L, -2, "absolute");
@@ -87,22 +94,26 @@ int lua_fs_list_alldirs(lua_State* L)
const char* fpath = lua_tostring(L, 1); const char* fpath = lua_tostring(L, 1);
lua_newtable(L); lua_newtable(L);
uint32_t i = 1; uint32_t i = 1;
for (const std::filesystem::path& p : std::filesystem::directory_iterator(fpath)) for (const std::filesystem::path& p : std::filesystem::recursive_directory_iterator(fpath))
{ {
const std::string x = p.string(); const std::string x = p.string();
lua_pushlstring(L, x.c_str(), x.size()); lua_pushlstring(L, x.c_str(), x.size());
lua_rawseti(L, -2, i); lua_rawseti(L, -2, i);
i++;
} }
return 1; return 1;
} }
int lua_fs_foreach_dir(lua_State* L) int lua_fs_foreach_dir(lua_State* L)
{ {
try {
luaL_argcheck(L, lua_type(L, 1) == LUA_TSTRING, 1, "Expected first argument to be a string representing a path"); luaL_argcheck(L, lua_type(L, 1) == LUA_TSTRING, 1, "Expected first argument to be a string representing a path");
const char* path = lua_tostring(L, 1); const char* path = lua_tostring(L, 1);
void* ud = lua_newuserdata(L, sizeof(std::filesystem::directory_iterator)); void* ud = lua_newuserdata(L, sizeof(std::filesystem::directory_iterator));
new (ud) std::filesystem::directory_iterator(path); new (ud) std::filesystem::directory_iterator(path);
} catch (const std::exception& e)
{ return luaL_error(L, "%s:%d: An exception occured creating fs.foreach iterator/generator\n\t%s\n", __FUNCTION__, __LINE__, e.what()); }
luaL_getmetatable(L, "fs.stdlibcpp.filesystem.directory_iterator"); luaL_getmetatable(L, "fs.stdlibcpp.filesystem.directory_iterator");
lua_setmetatable(L, -2); lua_setmetatable(L, -2);
@@ -115,8 +126,11 @@ int lua_fs_forall_dir(lua_State* L)
luaL_argcheck(L, lua_type(L, 1) == LUA_TSTRING, 1, "Expected first argument to be a string representing a path"); luaL_argcheck(L, lua_type(L, 1) == LUA_TSTRING, 1, "Expected first argument to be a string representing a path");
const char* path = lua_tostring(L, 1); const char* path = lua_tostring(L, 1);
try {
void* ud = lua_newuserdata(L, sizeof(std::filesystem::recursive_directory_iterator)); void* ud = lua_newuserdata(L, sizeof(std::filesystem::recursive_directory_iterator));
new (ud) std::filesystem::recursive_directory_iterator(path); new (ud) std::filesystem::recursive_directory_iterator(path);
} catch (const std::exception& e)
{ return luaL_error(L, "%s:%d: An exception occured creating fs.forall iterator/generator\n\t%s\n", __FUNCTION__, __LINE__, e.what()); }
luaL_getmetatable(L, "fs.stdlibcpp.filesystem.recursive_directory_iterator"); luaL_getmetatable(L, "fs.stdlibcpp.filesystem.recursive_directory_iterator");
lua_setmetatable(L, -2); lua_setmetatable(L, -2);
@@ -126,6 +140,7 @@ int lua_fs_forall_dir(lua_State* L)
int lua_fs_foreach_dir_next(lua_State* L) int lua_fs_foreach_dir_next(lua_State* L)
{ {
try {
int idx = lua_upvalueindex(1); int idx = lua_upvalueindex(1);
luaL_argcheck(L, lua_type(L, idx) == LUA_TUSERDATA, 1, "Expected first argument to fs.foreach_dir_next to be userdata<std::filesystem::directory_iterator>"); luaL_argcheck(L, lua_type(L, idx) == LUA_TUSERDATA, 1, "Expected first argument to fs.foreach_dir_next to be userdata<std::filesystem::directory_iterator>");
void* ud = luaL_checkudata(L, idx, "fs.stdlibcpp.filesystem.directory_iterator"); void* ud = luaL_checkudata(L, idx, "fs.stdlibcpp.filesystem.directory_iterator");
@@ -136,11 +151,14 @@ int lua_fs_foreach_dir_next(lua_State* L)
std::string p = (*x).path().string(); std::string p = (*x).path().string();
lua_pushlstring(L, p.c_str(), p.size()); lua_pushlstring(L, p.c_str(), p.size());
x++; x++;
} catch (const std::exception& e)
{ return luaL_error(L, "%s:%d: An exception occured iterating fs.foreach\n\t%s\n", __FUNCTION__, __LINE__, e.what()); }
return 1; return 1;
} }
int lua_fs_forall_dir_next(lua_State* L) int lua_fs_forall_dir_next(lua_State* L)
{ {
try {
int idx = lua_upvalueindex(1); int idx = lua_upvalueindex(1);
luaL_argcheck(L, lua_type(L, idx) == LUA_TUSERDATA, 1, "Expected first argument to fs.forall_dir_next to be userdata<std::filesystem::recursive_directory_iterator>"); luaL_argcheck(L, lua_type(L, idx) == LUA_TUSERDATA, 1, "Expected first argument to fs.forall_dir_next to be userdata<std::filesystem::recursive_directory_iterator>");
void* ud = luaL_checkudata(L, idx, "fs.stdlibcpp.filesystem.recursive_directory_iterator"); void* ud = luaL_checkudata(L, idx, "fs.stdlibcpp.filesystem.recursive_directory_iterator");
@@ -151,26 +169,34 @@ int lua_fs_forall_dir_next(lua_State* L)
std::string p = (*x).path().string(); std::string p = (*x).path().string();
lua_pushlstring(L, p.c_str(), p.size()); lua_pushlstring(L, p.c_str(), p.size());
x++; x++;
} catch (const std::exception& e)
{ return luaL_error(L, "%s:%d: An exception occured iterating fs.forall\n\t%s\n", __FUNCTION__, __LINE__, e.what()); }
return 1; return 1;
} }
int lua_fs_foreach_dir_dtor(lua_State* L) int lua_fs_foreach_dir_dtor(lua_State* L)
{ {
try {
luaL_argcheck(L, lua_type(L, 1) == LUA_TUSERDATA, 1, "Expected first argument to fs.foreach_dir_next to be userdata<std::filesystem::directory_iterator>"); luaL_argcheck(L, lua_type(L, 1) == LUA_TUSERDATA, 1, "Expected first argument to fs.foreach_dir_next to be userdata<std::filesystem::directory_iterator>");
void* ud = luaL_checkudata(L, 1, "fs.stdlibcpp.filesystem.directory_iterator"); void* ud = luaL_checkudata(L, 1, "fs.stdlibcpp.filesystem.directory_iterator");
luaL_argcheck(L, ud != nullptr, 1, "Expected first argument's userdata type to be a std::filesystem::directory_iterator"); luaL_argcheck(L, ud != nullptr, 1, "Expected first argument's userdata type to be a std::filesystem::directory_iterator");
std::filesystem::directory_iterator& x = *(std::filesystem::directory_iterator*)ud; std::filesystem::directory_iterator& x = *(std::filesystem::directory_iterator*)ud;
x.~directory_iterator(); x.~directory_iterator();
} catch (const std::exception& e)
{ return luaL_error(L, "%s:%d: An exception occured destroying fs.foreach iterator/generator\n\t%s\n", __FUNCTION__, __LINE__, e.what()); }
return 0; return 0;
} }
int lua_fs_forall_dir_dtor(lua_State* L) int lua_fs_forall_dir_dtor(lua_State* L)
{ {
try {
luaL_argcheck(L, lua_type(L, 1) == LUA_TUSERDATA, 1, "Expected first argument to fs.forall_dir_next to be userdata<std::filesystem::recursive_directory_iterator>"); luaL_argcheck(L, lua_type(L, 1) == LUA_TUSERDATA, 1, "Expected first argument to fs.forall_dir_next to be userdata<std::filesystem::recursive_directory_iterator>");
void* ud = luaL_checkudata(L, 1, "fs.stdlibcpp.filesystem.recursive_directory_iterator"); void* ud = luaL_checkudata(L, 1, "fs.stdlibcpp.filesystem.recursive_directory_iterator");
luaL_argcheck(L, ud != nullptr, 1, "Expected first argument's userdata type to be a std::filesystem::recursive_directory_iterator"); luaL_argcheck(L, ud != nullptr, 1, "Expected first argument's userdata type to be a std::filesystem::recursive_directory_iterator");
std::filesystem::recursive_directory_iterator& x = *(std::filesystem::recursive_directory_iterator*)ud; std::filesystem::recursive_directory_iterator& x = *(std::filesystem::recursive_directory_iterator*)ud;
x.~recursive_directory_iterator(); x.~recursive_directory_iterator();
} catch (const std::exception& e)
{ return luaL_error(L, "%s:%d: An exception occured destroying fs.forall iterator/generator\n\t%s\n", __FUNCTION__, __LINE__, e.what()); }
return 0; return 0;
} }
@@ -251,11 +277,107 @@ int lua_fs_file_directory(lua_State* L)
return 0; return 0;
} }
int lua_fs_is_newer(lua_State* L)
{
luaL_argcheck(L, lua_type(L, 1) == LUA_TSTRING, 1, "Expected first argument to be a string representing a full file path");
const char* test_file = lua_tostring(L, 1);
if (!std::filesystem::exists(test_file))
{
lua_pushboolean(L, false);
return 1;
}
if (lua_type(L, 2) == LUA_TTABLE)
{
size_t tbl_len = lua_objlen(L, 2);
lua_newtable(L);
for (size_t i = 1; i <= tbl_len; ++i)
{
lua_rawgeti(L, 2, i);
if (lua_type(L, -1) != LUA_TSTRING)
return luaL_error(L, "Expected second argument at index %llu to be a string, not %s\n",
i, lua_typename(L, lua_type(L, -1)));
const char* target = lua_tostring(L, 2);
lua_pushboolean(L,
std::filesystem::last_write_time(test_file) > std::filesystem::last_write_time(target));
lua_rawseti(L, -2, i);
lua_pop(L, 1);
}
return 1;
}
else if (lua_type(L, 2) == LUA_TSTRING)
{
const char* target = lua_tostring(L, 2);
if (!std::filesystem::exists(target))
{
lua_pushboolean(L, true);
return 1;
}
lua_pushboolean(L,
std::filesystem::last_write_time(test_file) > std::filesystem::last_write_time(target));
return 1;
}
else return luaL_error(L, "Expected second argument to be a string or table of strings");
}
int lua_fs_filter_newer(lua_State* L)
{
luaL_argcheck(L, lua_type(L, 1) == LUA_TSTRING, 1, "Expected first argument to be a string representing a full file path");
const char* test_file = lua_tostring(L, 1);
if (!std::filesystem::exists(test_file))
{
lua_pushnil(L);
return 1;
}
if (lua_type(L, 2) == LUA_TTABLE)
{
size_t tbl_len = lua_objlen(L, 2);
lua_newtable(L);
for (size_t i = 1; i <= tbl_len; ++i)
{
lua_rawgeti(L, 2, i);
if (lua_type(L, -1) != LUA_TSTRING)
return luaL_error(L, "Expected second argument at index %llu to be a string, not %s\n",
i, lua_typename(L, lua_type(L, -1)));
const char* target = lua_tostring(L, 2);
if (std::filesystem::last_write_time(test_file) > std::filesystem::last_write_time(target))
{
lua_pushvalue(L, -1);
lua_rawseti(L, -2, lua_objlen(L, -2)+1);
}
lua_pop(L, 1);
}
return 1;
}
else if (lua_type(L, 2) == LUA_TSTRING)
{
const char* target = lua_tostring(L, 2);
if (!std::filesystem::exists(target))
{
lua_pushvalue(L, 2);
return 1;
}
if (std::filesystem::last_write_time(test_file) > std::filesystem::last_write_time(target))
lua_pushvalue(L, 2);
else lua_pushnil(L);
return 1;
}
else return luaL_error(L, "Expected second argument to be a string or table of strings");
}
int lua_fs_last_modified(lua_State* L) int lua_fs_last_modified(lua_State* L)
{ {
luaL_argcheck(L, lua_type(L, 1) == LUA_TSTRING, 1, "Expected first argument to be a string representing a full file path"); luaL_argcheck(L, lua_type(L, 1) == LUA_TSTRING, 1, "Expected first argument to be a string representing a full file path");
try { try {
lua_pushinteger(L, std::filesystem::last_write_time(std::filesystem::path(lua_tostring(L, 1))).time_since_epoch().count()); const uint64_t file_last_write = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::clock_cast<std::chrono::system_clock>(
std::filesystem::last_write_time(lua_tostring(L, 1))
).time_since_epoch()
).count();
lua_pushinteger(L, file_last_write);
return 1; return 1;
} catch (const std::exception& e) } catch (const std::exception& e)
{ {

View File

@@ -4,6 +4,7 @@
#include "mutex" #include "mutex"
#include <chrono> #include <chrono>
#include "simple_string.h" #include "simple_string.h"
#include "uniproc.h"
int luaopen_platform(lua_State* L) int luaopen_platform(lua_State* L)
{ {
@@ -23,110 +24,66 @@ int lua_platform_exec(lua_State* L)
{ {
luaL_argcheck(L, lua_isstring(L, 1), 1, "Expected first argument to be a string"); luaL_argcheck(L, lua_isstring(L, 1), 1, "Expected first argument to be a string");
luaL_argcheck(L, lua_istable(L, 2), 2, "Expected second argument to be a table of strings"); luaL_argcheck(L, lua_istable(L, 2), 2, "Expected second argument to be a table of strings");
std::string cmd(lua_tostring(L, 1));
size_t nargs = lua_objlen(L, 2); size_t nargs = lua_objlen(L, 2);
const char** argv = (const char**)malloc(nargs * sizeof(const char*));
for (size_t i = 0; i < nargs; ++i) for (size_t i = 0; i < nargs; ++i)
{ {
lua_rawgeti(L, -1, i + 1); lua_rawgeti(L, 2, i+1);
if (!lua_isstring(L, -1)) if (lua_type(L, -1) != LUA_TSTRING)
return luaL_error(L, "Expected all values in argument table to be a string. Index %d was a %s", i, lua_typename(L, lua_type(L, -1))); return luaL_error(L, "Expected all values in argument table to be a string. Index %d was a %s", i+1, lua_typename(L, lua_type(L, -1)));
cmd.append(" ").append(lua_tostring(L, -1)); argv[i] = lua_tostring(L, -1);
} }
FILE* f = popen(cmd.c_str(), "r"); int retcode = 0;
std::string std_out;
std::string std_err;
char buf[64]; char buf[64];
std::string result;
while (fgets(buf, sizeof(buf), f) != NULL)
result.append(buf);
lua_pushinteger(L, pclose(f)); uniproc_process p = uniproc_create_process(lua_tostring(L, 1), nargs, argv);
lua_pushlstring(L, result.c_str(), result.size()); if (uniproc_is_process_null(&p))
{
lua_pushnil(L);
lua_pushstring(L, "uniproc: failed to create process");
return 2; return 2;
}
struct command_stack
{
std::string base_cmd;
dynarray<dynarray<std::string>> commands;
std::recursive_mutex mux;
size_t num_cmds(void) const noexcept
{ return commands.size(); }
bool is_empty(void) const noexcept
{ return commands.size() == 0; }
std::pair<dynarray<std::string>, size_t> get_command(void) noexcept
{
std::scoped_lock lock(mux);
const size_t sz = commands.size();
return std::make_pair(std::move(commands.pop_back()),sz-1);
} }
}; uniproc_await_processes(&p, &retcode, 1);
struct result_stack
{
struct result
{
std::string program_output;
int program_ret;
};
std::recursive_mutex mux; while (fgets(buf, sizeof(buf), p.out) != NULL)
dynarray<result> results; std_out.append(buf);
while (fgets(buf, sizeof(buf), p.err) != NULL)
std_err.append(buf);
void add_result(const size_t idx, const std::string& out, const int ret) uniproc_close_process(&p);
{ free(argv);
std::scoped_lock lock(mux);
new (results.data() + idx) result(out, ret);
}
};
lua_pushinteger(L, retcode);
void worker_thread(std::stop_token token, const std::string& base_cmd, command_stack& in, result_stack& out) lua_pushlstring(L, std_out.c_str(), std_out.size());
{ lua_pushlstring(L, std_err.c_str(), std_err.size());
char buf[64]; return 3;
while (!token.stop_requested())
{
std::string cmd = base_cmd;
if (in.is_empty()) return;
auto [arg, idx] = in.get_command();
// Get a command
for (const std::string& a : arg)
cmd.append(" ").append(a);
// Run the command
FILE* f = popen(cmd.c_str(), "r");
// Capture the commands stdout
std::string pout = "";
memset(buf, '\0', sizeof(buf));
while (fgets(buf, sizeof(buf), f) != NULL)
{
using namespace std::chrono_literals;
std::this_thread::sleep_for(10us);
pout.append(buf);
}
// Await the program to exit
int rc = pclose(f);
out.add_result(idx, pout, rc);
}
} }
int lua_platform_exec_parallel(lua_State* L) int lua_platform_exec_parallel(lua_State* L)
{ {
try {
luaL_argcheck(L, lua_type(L, 1) == LUA_TSTRING, 1, "Expected first argument to be a string representing a command"); luaL_argcheck(L, lua_type(L, 1) == LUA_TSTRING, 1, "Expected first argument to be a string representing a command");
luaL_argcheck(L, lua_type(L, 2) == LUA_TNUMBER, 2, "Expected second argument to be an integer representing the maximum number of parallel jobs to start"); luaL_argcheck(L, lua_type(L, 2) == LUA_TNUMBER, 2, "Expected second argument to be an integer representing the maximum number of parallel jobs to start");
luaL_argcheck(L, lua_type(L, 3) == LUA_TTABLE, 3, "Expected third argument to be a table of table of strings representing the arguments"); luaL_argcheck(L, lua_type(L, 3) == LUA_TTABLE, 3, "Expected third argument to be a table of table of strings representing the arguments");
std::string base_cmd(lua_tostring(L, 1)); const char* cmd = (lua_tostring(L, 1));
const size_t max_parallel = lua_tointeger(L, 2); const size_t max_parallel = lua_tointeger(L, 2);
const size_t num_cmds = lua_objlen(L, 3); const size_t num_cmds = lua_objlen(L, 3);
command_stack in; uniproc_process* workers = nullptr;
in.base_cmd = base_cmd; int* retcodes = nullptr;
result_stack out; size_t* num_args = nullptr;
for (size_t i = 0; i < num_cmds; ++i) const char*** args = nullptr;
out.results.push_back(result_stack::result{"Not Executed",-1}); std::vector<std::string> std_outs;
std::vector<std::string> std_errs;
try {
num_args = (size_t*)malloc(num_cmds * sizeof(size_t));
memset(num_args, 0, sizeof(size_t) * num_cmds);
args = (const char***)malloc(num_cmds * sizeof(const char**));
// Generate the command stack from lua arguments // Generate the command stack from lua arguments
for (size_t i = 0; i < num_cmds; ++i) for (size_t i = 0; i < num_cmds; ++i)
@@ -134,58 +91,120 @@ int lua_platform_exec_parallel(lua_State* L)
lua_rawgeti(L, 3, i + 1); lua_rawgeti(L, 3, i + 1);
if (lua_istable(L, -1)) if (lua_istable(L, -1))
{ {
const size_t num_args = lua_objlen(L, -1); const size_t tbl_num_args = lua_objlen(L, -1);
in.commands.push_back(dynarray<std::string>()); num_args[i] = tbl_num_args;
dynarray<std::string>& back = in.commands.back(); args[i] = (const char**)malloc(tbl_num_args * sizeof(const char*));
for (size_t j = 0; j < num_args; ++j) for (size_t j = 0; j < tbl_num_args; ++j)
{ {
lua_rawgeti(L, -1, j + 1); lua_rawgeti(L, -1, j + 1);
if (!lua_isstring(L, -1)) if (!lua_isstring(L, -1))
return luaL_error(L, "Expected program argument to be a string. args[%d][%d] was a %s, not a string!\n", i, j, lua_typename(L, lua_type(L, -1))); return luaL_error(L, "Expected program argument to be a string. args[%d][%d] was a %s, not a string!\n", i, j, lua_typename(L, lua_type(L, -1)));
std::string str(lua_tostring(L, -1), lua_objlen(L, -1)); args[i][j] = lua_tostring(L, -1);
back.push_back(std::move(str));
lua_pop(L, 1); lua_pop(L, 1);
} }
} }
else if (lua_type(L, -1) == LUA_TSTRING) else if (lua_type(L, -1) == LUA_TSTRING)
{ {
in.commands.push_back(dynarray<std::string>()); num_args[i] = 1;
in.commands.back().push_back(lua_tostring(L, -1)); args[i] = (const char**)malloc(1 * sizeof(const char*));
args[i][0] = lua_tostring(L, -1);
} }
else else
return luaL_error(L, "Expected a table of program arguments. args[%d] was a %s, not a table!\n", i, lua_typename(L, lua_type(L, -1))); return luaL_error(L, "Expected a table of program arguments. args[%d] was a %s, not a table!\n", i, lua_typename(L, lua_type(L, -1)));
lua_pop(L, 1); lua_pop(L, 1);
} }
// Dispatch worker threads workers = (uniproc_process*)malloc(max_parallel * sizeof(uniproc_process));
std::vector<std::jthread> workers; uniproc_nullify_processes(workers, max_parallel);
for (size_t i = 0; i < max_parallel; ++i) retcodes = (int*)malloc(num_cmds * sizeof(int));
workers.push_back(std::jthread(worker_thread, memset(retcodes, 0, num_cmds);
std::ref(base_cmd),
std::ref(in), std_outs.resize(num_cmds, "");
std::ref(out) std_errs.resize(num_cmds, "");
));
}
catch (const std::exception& e) {
fprintf(stderr, "%s:%d: Exception occured in preparation for fs.parallel_exec(...)\n\t%s\n", __FUNCTION__, __LINE__, e.what());
return luaL_error(L, "%s:%d: Exception occured in preparation for fs.parallel_exec(...)\n\t%s\n", __FUNCTION__, __LINE__, e.what());
}
size_t cmd_idx = 0;
while (cmd_idx < num_cmds || !uniproc_are_processes_finished(workers, max_parallel))
{
try {
for (uniproc_process* p = workers; p != (workers + max_parallel); ++p)
{
// Check and handle a worker if it has completed
if (!uniproc_is_process_null(p) && uniproc_are_processes_finished(p, 1))
{
char buf[64];
// Read the std. streams into the output vectors
while (fgets(buf, sizeof(buf), p->out) != NULL)
std_outs[p->userdata].append(buf);
while (fgets(buf, sizeof(buf), p->err) != NULL)
std_errs[p->userdata].append(buf);
// Add the process return code to retcode array
uniproc_await_processes(p, retcodes + p->userdata, 1);
// Close the process and zero it
uniproc_close_process(p);
}
// If a worker is null and there is more jobs to complete
// Create a new process
// Is a while loop to handle the possibility that process creation fails
while (uniproc_is_process_null(p) && cmd_idx < num_cmds)
{
// Create process
*p = uniproc_create_process(cmd, num_args[cmd_idx], args[cmd_idx]);
p->userdata = cmd_idx;
cmd_idx++;
// If we failed to create the process, set the output for it to an error value
if (uniproc_is_process_null(p))
{
std_outs[p->userdata] = "uniproc: failed to create process";
std_errs[p->userdata] = "uniproc: failed to create process";
retcodes[p->userdata] = -1;
}
}
}
// Await a process completing before trying to iterate again
// Prevents spinning the CPU
uniproc_await_any_processes(workers, max_parallel);
}
catch (const std::exception& e) {
fprintf(stderr, "%s:%d: Exception occured in parallel execution for fs.parallel_exec(...)\n\t%s\n", __FUNCTION__, __LINE__, e.what());
return luaL_error(L, "%s:%d: Exception occured in parallel execution for fs.parallel_exec(...)\n\t%s\n", __FUNCTION__, __LINE__, e.what());
}
}
for (size_t i = 0; i < num_cmds; ++i)
free(args[i]);
free(num_args);
free(args);
// Don't need to close processes as that is handled in the command loop
free(workers);
using namespace std::chrono_literals;
while (!in.is_empty())
std::this_thread::sleep_for(1ms);
for (size_t i = 0; i < workers.size(); ++i)
workers[i].request_stop();
for (size_t i = 0; i < workers.size(); ++i)
workers[i].join();
// Join thread results together and push them to lua stack // Join thread results together and push them to lua stack
lua_newtable(L); // rc lua_newtable(L); // rc
lua_newtable(L); // stdout lua_newtable(L); // stdout
for (size_t i = 0; i < out.results.size(); ++i) lua_newtable(L); // stderr
for (size_t i = 0; i < num_cmds; ++i)
{ {
const result_stack::result& x = out.results[i]; lua_pushinteger(L, retcodes[i]);
lua_pushinteger(L, x.program_ret); lua_rawseti(L, -4, i + 1);
lua_rawseti(L, -3, i+1); lua_pushlstring(L, std_outs[i].c_str(), std_outs[i].size());
lua_pushlstring(L, x.program_output.c_str(), x.program_output.size()); lua_rawseti(L, -3, i + 1);
lua_pushlstring(L, std_errs[i].c_str(), std_errs[i].size());
lua_rawseti(L, -2, i + 1); lua_rawseti(L, -2, i + 1);
} }
return 2; return 3;
}
catch (const std::exception& e)
{
fprintf(stderr, "%s:%d: Exception occured in cleanup and result handling for fs.parallel_exec(...)\n\t%s\n", __FUNCTION__, __LINE__, e.what());
return luaL_error(L, "%s:%d: Exception occured in cleanup and result handling for fs.parallel_exec(...)\n\t%s\n", __FUNCTION__, __LINE__, e.what());
}
} }

1
thirdparty/libgit2 vendored Submodule

Submodule thirdparty/libgit2 added at 21a351b0ed

1
thirdparty/uniproc vendored Submodule

Submodule thirdparty/uniproc added at 8948e75d06