Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
LuaRocks all-in-one executable bundler
#!/usr/bin/env lua
--[[
All-in-one packager for LuaRocks
* by Hisham Muhammad <hisham@gobolinux.org>
* licensed under the same terms as Lua (MIT license).
Based on:
* srlua.c - Lua interpreter for self-running programs
* by Luiz Henrique de Figueiredo <lhf@tecgraf.puc-rio.br>
* 03 Nov 2014 15:31:43
* srlua.c is placed in the public domain.
* bin2c.lua - converts a binary to a C string that can be embedded
* by Mark Edgar
* http://lua-users.org/wiki/BinTwoCee
* bin2c.lua is licensed under the same terms as Lua (MIT license).
* lua.c - Lua stand-alone interpreter
* by Luiz Henrique de Figueiredo, Waldemar Celes, Roberto Ierusalimschy
* lua.c is licensed under the same terms as Lua (MIT license).
]]
local fs = require("luarocks.fs")
local function reindent_c(input)
local out = {}
local indent = 0
local previous_is_blank = true
for line in input:gmatch("([^\n]*)") do
line = line:match("^[ \t]*(.-)[ \t]*$")
local is_blank = (#line == 0)
local do_print =
(not is_blank) or
(not previous_is_blank and indent == 0)
if line:match("^[})]") then
indent = indent - 1
if indent < 0 then indent = 0 end
end
if do_print then
table.insert(out, string.rep(" ", indent))
table.insert(out, line)
table.insert(out, "\n")
end
if line:match("[{(]$") then
indent = indent + 1
end
previous_is_blank = is_blank
end
return table.concat(out)
end
local hexdump
do
local numtab = {}
for i = 0, 255 do
numtab[string.char(i)] = ("%-3d,"):format(i)
end
function hexdump(str)
return (str:gsub(".", numtab):gsub(("."):rep(80), "%0\n"))
end
end
local c_preamble = [[
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
/* portable alerts, from srlua */
#ifdef _WIN32
#include <windows.h>
#define alert(message) MessageBox(NULL, message, progname, MB_ICONERROR | MB_OK)
#define getprogname() char name[MAX_PATH]; argv[0]= GetModuleFileName(NULL,name,sizeof(name)) ? name : NULL;
#else
#define alert(message) fprintf(stderr,"%s: %s\n", progname, message)
#define getprogname()
#endif
static int registry_key;
]]
local function bin2c_file(out, filename)
local content = string.dump(assert(loadfile(filename)))
table.insert(out, ("static const unsigned char code[] = {"))
table.insert(out, hexdump(content))
table.insert(out, ("};"))
end
local function declare_modules(out, dir, skip)
table.insert(out, [[
static void declare_modules(lua_State* L) {
lua_settop(L, 0); /* */
lua_newtable(L); /* modules */
lua_pushlightuserdata(L, (void*) &registry_key); /* modules registry_key */
lua_pushvalue(L, 1); /* modules registry_key modules */
lua_rawset(L, LUA_REGISTRYINDEX); /* modules */
]])
for _, name in ipairs(fs.find(dir)) do
local run = true
for _, pat in ipairs(skip) do
if name:match(pat) then
run = false
break
end
end
if run then
local filename = dir .. "/" .. name
if fs.is_file(filename) then
print(name)
local modname = name:gsub("%.lua$", ""):gsub("/", ".")
table.insert(out, ("/* %s */"):format(modname))
table.insert(out, ("{"))
bin2c_file(out, filename)
table.insert(out, ("lua_pushstring(L, %q);"):format(modname))
table.insert(out, ("luaL_loadbuffer(L, code, sizeof(code), %q);"):format(filename))
table.insert(out, ("lua_settable(L, 1);"))
table.insert(out, ("}"))
end
end
end
table.insert(out, [[
lua_settop(L, 0); /* */
}
]])
end
local function load_main(out, main_program, program_name)
table.insert(out, [[static void load_main(lua_State* L) {]])
bin2c_file(out, main_program)
table.insert(out, ("luaL_loadbuffer(L, code, sizeof(code), %q);"):format(program_name))
table.insert(out, [[}]])
table.insert(out, [[]])
end
local c_main = [[
/* custom package loader */
static int pkg_loader(lua_State* L) {
lua_pushlightuserdata(L, (void*) &registry_key); /* modname ? registry_key */
lua_rawget(L, LUA_REGISTRYINDEX); /* modname ? modules */
lua_pushvalue(L, 1); /* modname ? modules modname */
lua_gettable(L, -2); /* modname ? mod */
return 1;
}
static void install_pkg_loader(lua_State* L) {
lua_settop(L, 0); /* */
lua_getglobal(L, "table"); /* table */
lua_getfield(L, -1, "insert"); /* table table.insert */
lua_getglobal(L, "package"); /* table table.insert package */
lua_getfield(L, -1, "searchers"); /* table table.insert package package.searchers */
if (lua_type(L, -1) == LUA_TNIL) {
lua_pop(L, 1);
lua_getfield(L, -1, "loaders"); /* table table.insert package package.loaders */
}
lua_copy(L, 4, 3); /* table table.insert package.searchers */
lua_settop(L, 3); /* table table.insert package.searchers */
lua_pushnumber(L, 1); /* table table.insert package.searchers 1 */
lua_pushcfunction(L, pkg_loader); /* table table.insert package.searchers 1 pkg_loader */
lua_call(L, 3, 0); /* table */
lua_settop(L, 0); /* */
}
/* main script launcher, from srlua */
static int pmain(lua_State *L) {
int argc = lua_tointeger(L, 1);
char** argv = lua_touserdata(L, 2);
int i;
load_main(L);
lua_createtable(L, argc, 0);
for (i = 0; i < argc; i++) {
lua_pushstring(L, argv[i]);
lua_rawseti(L, -2, i);
}
lua_setglobal(L, "arg");
luaL_checkstack(L, argc - 1, "too many arguments to script");
for (i = 1; i < argc; i++) {
lua_pushstring(L, argv[i]);
}
lua_call(L, argc - 1, 0);
return 0;
}
/* fatal error, from srlua */
static void fatal(const char* message) {
alert(message);
exit(EXIT_FAILURE);
}
/* error handler, from luac */
static int msghandler (lua_State *L) {
/* is error object not a string? */
const char *msg = lua_tostring(L, 1);
if (msg == NULL) {
/* does it have a metamethod that produces a string */
if (luaL_callmeta(L, 1, "__tostring") && lua_type(L, -1) == LUA_TSTRING) {
/* then that is the message */
return 1;
} else {
msg = lua_pushfstring(L, "(error object is a %s value)", luaL_typename(L, 1));
}
}
/* append a standard traceback */
luaL_traceback(L, L, msg, 1);
return 1;
}
/* main function, from srlua */
int main(int argc, char** argv) {
lua_State* L;
getprogname();
if (argv[0] == NULL) {
fatal("cannot locate this executable");
}
L = luaL_newstate();
if (L == NULL) {
fatal("not enough memory for state");
}
luaL_openlibs(L);
install_pkg_loader(L);
declare_modules(L);
lua_pushcfunction(L, &pmain);
lua_pushinteger(L, argc);
lua_pushlightuserdata(L, argv);
if (lua_pcall(L, 2, 0, -4) != 0) {
fatal(lua_tostring(L, -1));
}
lua_close(L);
return EXIT_SUCCESS;
}
]]
local function generate(main_program, dir, skip)
local program_name = main_program:gsub(".*/", "")
local out = {}
table.insert(out, c_preamble)
table.insert(out, ([[static const char* progname = %q;]]):format(program_name))
load_main(out, main_program, program_name)
declare_modules(out, dir, skip)
table.insert(out, c_main)
local c_filename = program_name .. ".exe.c"
local fd = io.open(c_filename, "w")
fd:write(reindent_c(table.concat(out, "\n")))
fd:close()
os.execute("gcc -o " .. program_name .. ".exe -g " .. c_filename .. " -llua -ldl")
end
generate("src/bin/luarocks", "src", { "core/site_config", "^bin/?" })
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.