local function unresolve_require(paths, f) for _, pattern in ipairs(paths) do local path = f:match(pattern); if path then return path:gsub("%/", "."); end end error("path " .. f .. " couldn't be resolved to any valid module"); end local function all_loader(paths, ...) local files = { ... }; return coroutine.wrap(function () for _, f in ipairs(files) do local name = unresolve_require(paths, f); local bytecode = string.dump(assert(load(io.lines(f, 1024), "@" .. f, "t", nil))); coroutine.yield(("package.preload[%q] = load(%q, %q, 'b');"):format(name, bytecode, f)); end coroutine.yield("require 'init' (...);"); end); end local function escape(str) return "\"" .. str:gsub(".", function (c) local b = string.byte(c); if c == "\n" then return "\\n"; elseif c == "\\" then return "\\\\"; elseif c == "\"" then return "\\\""; elseif b >= 32 and b <= 126 then return c; else return ("\\%.3o"):format(b); end end) .. "\""; end local function main(root, out, ...) local paths = { root .. "/(.+).lua", root .. "/(.+)/init.lua" }; for el in package.path:gmatch "[^;]+" do paths[#paths + 1] = el:gsub("?", "(.+)"); end local res = string.dump(assert(load(all_loader(paths, ...), "@", "t", nil))); local f = assert(io.open(out, "w")); f:write "#include \n"; f:write "#define BYTECODE entry_bytecode\n"; f:write "#define BYTECODE_SIZE (sizeof entry_bytecode - 1)\n"; f:write "static const uint8_t entry_bytecode[] = "; for i = 1, math.ceil(#res / 64) do f:write(escape(res:sub((i - 1) * 64 + 1, i * 64))); f:write"\n"; end f:write ";"; assert(f:close()); end main(...);