Last active
August 8, 2024 09:50
-
-
Save q962/871967a69d181c61d8adcac3be2b6723 to your computer and use it in GitHub Desktop.
c 集成 lua 模块的方法
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
includes("./luafiles.lua") | |
--[[ | |
对于这个target就不需要调用 <module_name>_loadmodules 函数了 | |
见 test.c 中的例子 | |
]] | |
target("lua_require") | |
set_kind("static") | |
add_files("require.lua") | |
add_rules("luafiles", {root_path = ".", module_name = "require"}) | |
-- 为每一个 lua 提供一个 target 目标 | |
target("Penlight") | |
set_kind("static") | |
add_files("Penlight/lua/pl/**.lua") | |
add_rules("luafiles", {root_path = "Penlight/lua", module_name = "pl"}) | |
--[[ | |
]] | |
target("Program") | |
add_deps("lua_require", Penlight) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--[[ | |
利用 objcopy 将 root_path 下的所有 lua 文件变成符号 | |
需要配合特殊的 require 去识别这些内容。 | |
该规则会生成一个 c 文件 | |
暴露一个符号: <module_name>_loadmodules,需要在 c 中调用。 | |
不需要 include 文件。 直接 extern void <module_name>_loadmodules( lua_State * state ); | |
该函数会向栈顶的 table 注册注册一个名为 <module_name> 的 table | |
该 table 将按照 路径:文件内容 的模式存数存储所有的lua 文件内容。 | |
该路径是 lua 文件相对于 root_path 的路径。 | |
并且将分割替换为 '.', 去除文件后缀。 | |
利用 debug 模块提供的调用源的 source 字段,实现相对路径的识别。 | |
这会临时修改 package.path 印象 lua 的文件查找逻辑 | |
]] | |
rule("luafiles") | |
set_extensions(".lua") | |
on_load(function(target) | |
local root_path = target:extraconf("rules", "luafiles", "root_path") | |
local module_name = target:extraconf("rules", "luafiles", "module_name") or target:name() | |
assert(root_path) | |
local chunks = ""; | |
local changed = false | |
for _, sourcefile in ipairs(target:sourcebatches().luafiles.sourcefiles) do | |
local filepath = sourcefile | |
local objectfile = target:objectfile(sourcefile) | |
local f_mtime = os.mtime(filepath); | |
local o_mtime = os.mtime(objectfile); | |
local projectdir = vformat("$(projectdir)") | |
local r_filepath = path.relative(projectdir .. "/" .. sourcefile, target:scriptdir() .. "/" .. root_path) | |
local binary_name = r_filepath:gsub("[\\/.-]", "_") | |
local chunk_name = r_filepath:gsub("[\\/]", "."):sub(1, -1 - 4) | |
chunks = chunks .. format([[ | |
{ | |
#define XXX(X) \ | |
extern const char ]] .. module_name .. [[_binary_##X##_start; \ | |
extern const char ]] .. module_name .. [[_binary_##X##_end; \ | |
const char* _start = &]] .. module_name .. [[_binary_##X##_start; \ | |
const char* _end = &]] .. module_name .. [[_binary_##X##_end; \ | |
size_t _size = _end - _start; | |
XXX(%s) | |
lua_pushstring(L, "%s" ); | |
luaL_loadbuffer(L, _start, _size, "@M@%s"); | |
lua_settable(L, -3); | |
#undef XXX | |
} | |
]], binary_name, chunk_name, chunk_name) | |
if f_mtime > o_mtime then | |
changed = true | |
end | |
end | |
local module_c_filepath = target:autogendir() .. "/" .. module_name .. ".c" | |
if changed or not os.isfile(module_c_filepath) then | |
io.writefile(module_c_filepath, [[ | |
#include <lualib.h> | |
#include <lauxlib.h> | |
#include <string.h> | |
void ]] .. module_name .. [[_loadmodules(lua_State* L) { | |
lua_pushstring(L, "]] .. module_name .. [["); | |
lua_newtable(L); | |
]] .. chunks .. [[ | |
lua_settable( L, -3 ); | |
} | |
]]); | |
end | |
target:add("files", module_c_filepath) | |
end) | |
before_build(function(target) | |
import("lib.detect.find_program") | |
local root_path = target:extraconf("rules", "luafiles", "root_path") | |
local module_name = target:extraconf("rules", "luafiles", "module_name") or target:name() | |
assert(root_path) | |
local projectdir = vformat("$(projectdir)/") | |
local scriptdir = vformat("$(scriptdir)/") | |
local sourcedir = target:scriptdir() .. "/" .. root_path | |
local changed = false; | |
local lua_files = {}; | |
local luac = find_program("luac") | |
for _, sourcefile in ipairs(target:sourcebatches().luafiles.sourcefiles) do | |
local objectfile = path.relative(target:objectfile(sourcefile), projectdir) | |
sourcefile = projectdir .. sourcefile | |
local r_filepath = path.relative(sourcefile, sourcedir) | |
local a_filepath = projectdir .. objectfile | |
local f_mtime = os.mtime(sourcefile); | |
local a_mtime = os.mtime(a_filepath); | |
table.insert(lua_files, a_filepath) | |
if f_mtime > a_mtime then | |
changed = true; | |
os.cd(sourcedir) | |
os.mkdir(path.directory(a_filepath)) | |
os.execv("objcopy", | |
{"-I", "binary", "-O", "elf64-x86-64", "--prefix-symbols", module_name, r_filepath, a_filepath}) | |
os.execv("objcopy", | |
{"-I", "binary", "-O", "elf64-x86-64", "--prefix-symbols", module_name, r_filepath, a_filepath}) | |
os.cd("-") | |
end | |
end | |
local out_filepath = projectdir .. path.relative(target:autogendir() .. "/" .. module_name .. ".obj", projectdir) | |
-- 合并 obj | |
if changed then | |
os.mkdir(path.directory(out_filepath)) | |
os.runv(target:tool("ld"), table.join2({"-r", "-o", out_filepath}, lua_files)) | |
end | |
table.insert(target:objectfiles(), out_filepath) | |
end) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
local realRequire = require; | |
local __internal_packages = {} | |
local function string_split(path, sep) | |
local stack = {}; | |
local _last_end = 1 | |
local _s = 1 -- 游标 | |
while _s do | |
_s = path:find(sep, _s) | |
if _s then | |
table.insert(stack, path:sub(_last_end, _s - 1)) | |
_s = _s + 1 | |
_last_end = _s | |
else | |
table.insert(stack, path:sub(_last_end)) | |
break | |
end | |
end | |
return stack | |
end | |
local function string_find_last(string, substring, count) | |
count = count or 1 | |
local list = {}; | |
table.insert(list, 0) | |
local _s = 1 | |
while _s do | |
_s = string:find(substring, _s) | |
if _s then | |
table.insert(list, _s) | |
_s = _s + 1 | |
else | |
break | |
end | |
end | |
return list[#list - (count - 1)] | |
end | |
-- 针对文件 | |
local function get_source_dir(up) | |
if up:sub(1, 1) ~= '@' then | |
return realRequire(name); | |
end | |
up = up:sub(2) | |
local sep_index = string_find_last(up, "/"); | |
if sep_index ~= 0 then | |
return up:sub(1, sep_index) | |
end | |
return | |
end | |
local function get_module(name) | |
local module = package.loaded[name] | |
if module then | |
return module | |
end | |
for package_name, modules in pairs(__internal_packages) do | |
local module = modules[name] or modules[name .. ".init"]; | |
if module then | |
module = module() | |
package.loaded[name] = module; | |
return module or true | |
end | |
end | |
end | |
-- root: a.b.c.d.e | |
-- path: ./ | |
-- path: ../././../ | |
-- path 确保是 ./ 开头 | |
local function fix_relative_paths(root, path) | |
path = path:gsub("[\\]", "/"):gsub("\\\\", "") | |
local root_path_stack = string_split(root, "[.]") | |
local path_stack = string_split(path, "/") | |
table.remove(root_path_stack, #root_path_stack) | |
local remove_count = 0 | |
for _, v in ipairs(path_stack) do | |
if v == "." then | |
elseif v == ".." then | |
if #root_path_stack == 0 then | |
break | |
end | |
table.remove(root_path_stack, #root_path_stack) | |
else | |
break | |
end | |
remove_count = remove_count + 1 | |
end | |
if (#path_stack - remove_count) ~= 1 then | |
return nil | |
end | |
for i = 1, remove_count do | |
table.remove(path_stack, 1) | |
end | |
local parent_path = table.concat(root_path_stack, "."); | |
local child_path = table.concat(path_stack, "."); | |
-- 如果找到 . 则去掉 .* | |
if #parent_path > 0 then | |
return parent_path .. "." .. child_path | |
end | |
return child_path | |
end | |
local function relative_require(name) | |
if name:sub(1, 1) == '.' then | |
local _name = name:gsub("\\", "/"); | |
local rela_path = _name:match("^[./]+") | |
local short_name = _name:sub(#rela_path + 1) | |
local up = debug.getinfo(2, "S").source:gsub("\\", "/"); | |
if up:sub(1, 3) == "@M@" then -- 内部模块 | |
local up_module_path = up:sub(4) | |
module_name = fix_relative_paths(up_module_path, name) | |
local module = get_module(module_name) | |
if (module) then | |
return module | |
else | |
error("Couldn't find module: " .. name) | |
end | |
elseif up:sub(1, 1) == "@" then -- 文件路径 | |
local source_dir = get_source_dir(up) | |
local old_ppath = package.path; | |
if source_dir then | |
local pdir = (source_dir .. "/" .. rela_path .. "/"):gsub("//", "/") | |
package.path = pdir .. "?.lua;" .. pdir .. "?/init.lua;" .. package.path | |
end | |
local ret = realRequire(short_name) | |
package.path = old_ppath; | |
return ret; | |
else -- | |
return; | |
end | |
else | |
return get_module(name) or realRequire(name) | |
end | |
end | |
function require(name) | |
if not name or #name == 0 then | |
return realRequire(name) | |
end | |
return relative_require(name) | |
end | |
return __internal_packages |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
int set(){ | |
int require_ret = LUA_OK; | |
do { | |
extern const char require_binary_require_lua_start; | |
extern const char require_binary_require_lua_end; | |
size_t require_binary_require_lua_size = &require_binary_require_lua_end - &require_binary_require_lua_start; | |
require_ret = luaL_loadbuffer( | |
L, &require_binary_require_lua_start, require_binary_require_lua_size, "_binary_require_lua" ); | |
if ( require_ret != LUA_OK ) | |
break; | |
require_ret = lua_pcall( L, 0, 1, 0 ); // return local:__internal_packages | |
if ( require_ret != LUA_OK ) | |
break; | |
extern void pl_loadmodules( lua_State * state ); | |
pl_loadmodules( L ); | |
lua_pop( L, 1 ); // pop table:__internal_packages | |
} while ( 0 ); | |
assert( require_ret == LUA_OK ); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment