Created
May 8, 2018 11:28
-
-
Save hishamhm/6f8723f0fced81b0ff2f88e5217ccc69 to your computer and use it in GitHub Desktop.
LuaRocks all-in-one executable bundler
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
| #!/usr/bin/env lua | |
| --[[ | |
| All-in-one packager for LuaRocks | |
| * by Hisham Muhammad <[email protected]> | |
| * licensed under the same terms as Lua (MIT license). | |
| Based on: | |
| * srlua.c - Lua interpreter for self-running programs | |
| * by Luiz Henrique de Figueiredo <[email protected]> | |
| * 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*) ®istry_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*) ®istry_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