diff --git a/premake5.lua b/premake5.lua index b06fd511..6622a100 100644 --- a/premake5.lua +++ b/premake5.lua @@ -73,7 +73,10 @@ description = "Disable Zlib/Zip 3rd party lib" } - + newoption { + trigger = "no-bytecode", + description = "Don't embed bytecode, but instead use the stripped souce code." + } -- -- Define the project. Put the release configuration first so it will be the diff --git a/scripts/embed.lua b/scripts/embed.lua index 7e79bf3c..78796780 100644 --- a/scripts/embed.lua +++ b/scripts/embed.lua @@ -5,85 +5,102 @@ -- issues in Mac OS X Universal builds. -- + local scriptCount = 0 + local function loadScript(fname) fname = path.getabsolute(fname) - local f = io.open(fname) - local s = assert(f:read("*a")) + local f = io.open(fname, "rb") + local s = assert(f:read("*all")) f:close() + return s + end + + local function stripScript(s) -- strip tabs - s = s:gsub("[\t]", "") + local result = s:gsub("[\t]", "") -- strip any CRs - s = s:gsub("[\r]", "") + result = result:gsub("[\r]", "") -- strip out block comments - s = s:gsub("[^\"']%-%-%[%[.-%]%]", "") - s = s:gsub("[^\"']%-%-%[=%[.-%]=%]", "") - s = s:gsub("[^\"']%-%-%[==%[.-%]==%]", "") + result = result:gsub("[^\"']%-%-%[%[.-%]%]", "") + result = result:gsub("[^\"']%-%-%[=%[.-%]=%]", "") + sresult = result:gsub("[^\"']%-%-%[==%[.-%]==%]", "") -- strip out inline comments - s = s:gsub("\n%-%-[^\n]*", "\n") - - -- escape backslashes - s = s:gsub("\\", "\\\\") + result = result:gsub("\n%-%-[^\n]*", "\n") -- strip duplicate line feeds - s = s:gsub("\n+", "\n") + result = result:gsub("\n+", "\n") -- strip out leading comments - s = s:gsub("^%-%-[^\n]*\n", "") + result = result:gsub("^%-%-[^\n]*\n", "") - -- escape line feeds - s = s:gsub("\n", "\\n") - - -- escape double quote marks - s = s:gsub("\"", "\\\"") - - return s + return result end - local function appendScript(result, contents) - -- break up large strings to fit in Visual Studio's string length limit - local max = 4096 - local start = 1 - local len = contents:len() - if len > 0 then - while start <= len do - local n = len - start - if n > max then n = max end - local finish = start + n + local function outputScript(result, script) + local data = script.data + local length = #data - -- make sure I don't cut an escape sequence - while contents:sub(finish, finish) == "\\" do - finish = finish - 1 + if length > 0 then + script.table = string.format("builtin_script_%d", scriptCount) + scriptCount = scriptCount + 1 + + buffered.writeln(result, "// ".. script.name) + buffered.writeln(result, "static const unsigned char " .. script.table .. "[] = {") + + for i = 1, length do + buffered.write(result, string.format("%3d, ", data:byte(i))) + if (i % 32 == 0) then + buffered.writeln(result) end - - local s = contents:sub(start, finish) - table.insert(result, "\t\"" .. s .. iif(finish < len, '"', '",')) - - start = finish + 1 end - else - table.insert(result, "\t\"\",") + + buffered.writeln(result, "};") + buffered.writeln(result) + end + end + + + local function addScript(result, filename, name, data) + if not data then + if not _OPTIONS["no-bytecode"] then + verbosef("Compiling... " .. filename) + local output = path.replaceextension(filename, ".luac") + local res, err = os.compile(filename, output); + if res ~= nil then + data = loadScript(output) + os.remove(output) + else + print(err) + print("Embedding source instead.") + data = stripScript(loadScript(filename)) + end + else + data = stripScript(loadScript(filename)) + end end - table.insert(result, "") + local script = {} + script.filename = filename + script.name = name + script.data = data + table.insert(result, script) end - -- Prepare the file header - local result = {} - table.insert(result, "/* Premake's Lua scripts, as static data buffers for release mode builds */") - table.insert(result, "/* DO NOT EDIT - this file is autogenerated - see BUILD.txt */") - table.insert(result, "/* To regenerate this file, run: premake5 embed */") - table.insert(result, "") - table.insert(result, '#include "premake.h"') - table.insert(result, "") - + local result = buffered.new() + buffered.writeln(result, "/* Premake's Lua scripts, as static data buffers for release mode builds */") + buffered.writeln(result, "/* DO NOT EDIT - this file is autogenerated - see BUILD.txt */") + buffered.writeln(result, "/* To regenerate this file, run: premake5 embed */") + buffered.writeln(result, "") + buffered.writeln(result, '#include "premake.h"') + buffered.writeln(result, "") -- Find all of the _manifest.lua files within the project @@ -96,84 +113,70 @@ userModuleFiles = table.join(userModuleFiles, os.matchfiles(path.join(_MAIN_SCRIPT_DIR, "**/_user_modules.lua"))) userModuleFiles = table.join(userModuleFiles, os.matchfiles(path.join(_MAIN_SCRIPT_DIR, "_user_modules.lua"))) --- Generate an index of the script file names. Script names are stored --- relative to the directory containing the manifest, i.e. the main --- Xcode script, which is at $/modules/xcode/xcode.lua is stored as --- "xcode/xcode.lua". - table.insert(result, "const char* builtin_scripts_index[] = {") +-- Generate table of embedded content. + local contentTable = {} + print("Compiling... ") for mi = 1, #manifests do local manifestName = manifests[mi] - local manifestDir = path.getdirectory(manifestName) - local baseDir = path.getdirectory(manifestDir) + local manifestDir = path.getdirectory(manifestName) + local baseDir = path.getdirectory(manifestDir) local files = dofile(manifests[mi]) for fi = 1, #files do local filename = path.join(manifestDir, files[fi]) - filename = path.getrelative(baseDir, filename) - table.insert(result, '\t"' .. filename .. '",') + addScript(contentTable, filename, path.getrelative(baseDir, filename)) end end - table.insert(result, '\t"src/_premake_main.lua",') - table.insert(result, '\t"src/_manifest.lua",') - table.insert(result, '\t"src/_modules.lua",') - table.insert(result, "\tNULL") - table.insert(result, "};") - table.insert(result, "") + addScript(contentTable, path.join(_SCRIPT_DIR, "../src/_premake_main.lua"), "src/_premake_main.lua") + addScript(contentTable, path.join(_SCRIPT_DIR, "../src/_manifest.lua"), "src/_manifest.lua") - --- Embed the actual script contents - - table.insert(result, "const char* builtin_scripts[] = {") - - for mi = 1, #manifests do - local manifestName = manifests[mi] - local manifestDir = path.getdirectory(manifestName) - - local files = dofile(manifests[mi]) - for fi = 1, #files do - local filename = path.join(manifestDir, files[fi]) - - local scr = loadScript(filename) - appendScript(result, scr) - end - end - - appendScript(result, loadScript(path.join(_SCRIPT_DIR, "../src/_premake_main.lua"))) - appendScript(result, loadScript(path.join(_SCRIPT_DIR, "../src/_manifest.lua"))) - --- Write the list of modules +-- Add the list of modules local modules = dofile("../src/_modules.lua") for _, userModules in ipairs(userModuleFiles) do modules = table.join(modules, dofile(userModules)) end - appendScript(result, "return {" .. table.implode(modules, "\\\"", "\\\"", ",\\n") .. "}") - table.insert(result, "\tNULL") - table.insert(result, "};") - table.insert(result, "") + addScript(contentTable, "_modules.lua", "src/_modules.lua", "return {" .. table.implode(modules, '"', '"', ', ') .. "}") +-- Embed the actual script contents + + print("Embedding...") + for mi = 1, #contentTable do + outputScript(result, contentTable[mi]) + end + +-- Generate an index of the script file names. Script names are stored +-- relative to the directory containing the manifest, i.e. the main +-- Xcode script, which is at $/modules/xcode/xcode.lua is stored as +-- "xcode/xcode.lua". + buffered.writeln(result, "const buildin_mapping builtin_scripts[] = {") + + for mi = 1, #contentTable do + if contentTable[mi].table then + buffered.writeln(result, string.format('\t{"%s", %s, sizeof(%s)},', contentTable[mi].name, contentTable[mi].table, contentTable[mi].table)) + else + buffered.writeln(result, string.format('\t{"%s", NULL, 0},', contentTable[mi].name)) + end + end + + buffered.writeln(result, "\t{NULL, NULL, 0}") + buffered.writeln(result, "};") + buffered.writeln(result, "") -- Write it all out. Check against the current contents of scripts.c first, -- and only overwrite it if there are actual changes. - result = table.concat(result, "\n") - + print("Writing...") local scriptsFile = path.getabsolute(path.join(_SCRIPT_DIR, "../src/host/scripts.c")) + local output = buffered.tostring(result) - local oldVersion - local file = io.open(scriptsFile, "r") - if file then - oldVersion = file:read("*a") - file:close() - end - - if oldVersion ~= result then - print("Writing scripts.c") - file = io.open(scriptsFile, "w+b") - file:write(result) - file:close() + local f, err = os.writefile_ifnotequal(output, scriptsFile); + if (f < 0) then + error(err, 0) + elseif (f > 0) then + printf("Generated %s...", path.getrelative(os.getcwd(), scriptsFile)) end diff --git a/src/host/buffered_io.c b/src/host/buffered_io.c index e60c359e..08bf3838 100644 --- a/src/host/buffered_io.c +++ b/src/host/buffered_io.c @@ -66,10 +66,11 @@ int buffered_write(lua_State* L) int buffered_writeln(lua_State* L) { size_t l; - const char *s = luaL_checklstring(L, 2, &l); + const char *s = luaL_optlstring(L, 2, NULL, &l); Buffer* b = (Buffer*)lua_touserdata(L, 1); - do_write(b, s, l); + if (s != NULL) + do_write(b, s, l); do_write(b, "\r\n", 2); return 0; } diff --git a/src/host/os_compile.c b/src/host/os_compile.c new file mode 100644 index 00000000..a1958050 --- /dev/null +++ b/src/host/os_compile.c @@ -0,0 +1,57 @@ +/** + * \file os_uuid.c + * \brief Create a new UUID. + * \author Copyright (c) 2002-2012 Jason Perkins and the Premake project + */ + +#include "premake.h" +#include "lua/src/lundump.h" +#include "lua/src/lobject.h" +#include "lua/src/lstate.h" + +extern int original_luaL_loadfile(lua_State* L, const char* filename); + +static int writer(lua_State* L, const void* p, size_t size, void* u) +{ + UNUSED(L); + return (fwrite(p, size, 1, (FILE*)u) != 1) && (size != 0); +} + +int os_compile(lua_State* L) +{ + const char* input = luaL_checkstring(L, 1); + const char* output = luaL_checkstring(L, 2); + lua_State* P = luaL_newstate(); + + if (original_luaL_loadfile(P, input) != OKAY) + { + const char* msg = lua_tostring(P, -1); + if (msg == NULL) + msg = "(error with no message)"; + + lua_pushnil(L); + lua_pushfstring(L, "Unable to compile '%s': %s", input, msg); + + lua_close(P); + return 2; + } + else + { + FILE* outputFile = (output == NULL) ? stdout : fopen(output, "wb"); + if (outputFile == NULL) + { + lua_close(P); + + lua_pushnil(L); + lua_pushfstring(L, "unable to write to '%s'", output); + return 2; + } + + lua_dump(P, writer, outputFile); + fclose(outputFile); + + lua_close(P); + lua_pushboolean(L, 1); + return 1; + } +} diff --git a/src/host/premake.c b/src/host/premake.c index 88c74d23..e5f23498 100644 --- a/src/host/premake.c +++ b/src/host/premake.c @@ -76,6 +76,7 @@ static const luaL_Reg os_functions[] = { { "stat", os_stat }, { "uuid", os_uuid }, { "writefile_ifnotequal", os_writefile_ifnotequal }, + { "compile", os_compile }, { NULL, NULL } }; @@ -306,8 +307,6 @@ int premake_locate_executable(lua_State* L, const char* argv0) */ int premake_test_file(lua_State* L, const char* filename, int searchMask) { - int i; - if (searchMask & TEST_LOCAL) { if (do_isfile(filename)) { lua_pushcfunction(L, path_getabsolute); @@ -329,13 +328,11 @@ int premake_test_file(lua_State* L, const char* filename, int searchMask) #if !defined(PREMAKE_NO_BUILTIN_SCRIPTS) if ((searchMask & TEST_EMBEDDED) != 0) { /* Try to locate a record matching the filename */ - for (i = 0; builtin_scripts_index[i] != NULL; ++i) { - if (strcmp(builtin_scripts_index[i], filename) == 0) { - lua_pushstring(L, "$/"); - lua_pushstring(L, filename); - lua_concat(L, 2); - return OKAY; - } + if (premake_find_embedded_script(filename) != NULL) { + lua_pushstring(L, "$/"); + lua_pushstring(L, filename); + lua_concat(L, 2); + return OKAY; } } #endif @@ -499,13 +496,13 @@ static int run_premake_main(lua_State* L, const char* script) * contents of the file's script. */ - const char* premake_find_embedded_script(const char* filename) + const buildin_mapping* premake_find_embedded_script(const char* filename) { #if !defined(PREMAKE_NO_BUILTIN_SCRIPTS) int i; - for (i = 0; builtin_scripts_index[i] != NULL; ++i) { - if (strcmp(builtin_scripts_index[i], filename) == 0) { - return builtin_scripts[i]; + for (i = 0; builtin_scripts[i].name != NULL; ++i) { + if (strcmp(builtin_scripts[i].name, filename) == 0) { + return builtin_scripts + i; } } #endif @@ -527,7 +524,7 @@ int premake_load_embedded_script(lua_State* L, const char* filename) static int warned = 0; #endif - const char* chunk = premake_find_embedded_script(filename); + const buildin_mapping* chunk = premake_find_embedded_script(filename); if (chunk == NULL) { return !OKAY; } @@ -546,5 +543,5 @@ int premake_load_embedded_script(lua_State* L, const char* filename) lua_concat(L, 2); /* Load the chunk */ - return luaL_loadbuffer(L, chunk, strlen(chunk), filename); + return luaL_loadbuffer(L, (const char*)chunk->bytecode, chunk->length, filename); } diff --git a/src/host/premake.h b/src/host/premake.h index 8b48f021..8f3bee55 100644 --- a/src/host/premake.h +++ b/src/host/premake.h @@ -9,7 +9,6 @@ #include "lauxlib.h" #include "lualib.h" - /* Identify the current platform I'm not sure how to reliably detect * Windows but since it is the most common I use it as the default */ #if defined(__linux__) @@ -118,6 +117,7 @@ int os_rmdir(lua_State* L); int os_stat(lua_State* L); int os_uuid(lua_State* L); int os_writefile_ifnotequal(lua_State* L); +int os_compile(lua_State* L); int string_endswith(lua_State* L); int string_hash(lua_State* L); int string_sha1(lua_State* L); @@ -138,13 +138,21 @@ int zip_extract(lua_State* L); #endif /* Engine interface */ + +typedef struct +{ + const char* name; + const unsigned char* bytecode; + size_t length; +} buildin_mapping; + +extern const buildin_mapping builtin_scripts[]; + + int premake_init(lua_State* L); int premake_execute(lua_State* L, int argc, const char** argv, const char* script); -const char* premake_find_embedded_script(const char* filename); int premake_load_embedded_script(lua_State* L, const char* filename); +const buildin_mapping* premake_find_embedded_script(const char* filename); + int premake_locate_executable(lua_State* L, const char* argv0); int premake_test_file(lua_State* L, const char* filename, int searchMask); - - -extern const char* builtin_scripts_index[]; -extern const char* builtin_scripts[];