diff --git a/src/_premake_main.lua b/src/_premake_main.lua index 9d3053de..d8a17404 100644 --- a/src/_premake_main.lua +++ b/src/_premake_main.lua @@ -28,6 +28,7 @@ p.main.elements = function() return { + p.main.locateUserScript, p.main.installModuleLoader, } end @@ -48,14 +49,6 @@ _PREMAKE_DIR = path.getdirectory(_PREMAKE_COMMAND) premake.path = premake.path .. ";" .. _PREMAKE_DIR - local file = _OPTIONS["file"] or "premake5.lua" - _MAIN_SCRIPT = os.locate(file, file .. ".lua", "premake4.lua") - if _MAIN_SCRIPT then - _MAIN_SCRIPT_DIR = path.getdirectory(_MAIN_SCRIPT) - else - _MAIN_SCRIPT_DIR = _WORKING_DIR - end - p.callArray(p.main.elements) -- Look for and run the system-wide configuration script; make sure any @@ -79,7 +72,7 @@ -- If there is a project script available, run it to get the -- project information, available options and actions, etc. - if _MAIN_SCRIPT then + if os.isfile(_MAIN_SCRIPT) then dofile(_MAIN_SCRIPT) end @@ -119,8 +112,8 @@ return 1 end - if not _MAIN_SCRIPT then - print("No Premake script (premake5.lua) found!") + if not os.isfile(_MAIN_SCRIPT) then + print(string.format("No Premake script (%s) found!", path.getname(_MAIN_SCRIPT))) return 1 end end @@ -153,6 +146,34 @@ +--- +-- Look for a user project script, and set up the related global +-- variables if I can find one. +--- + + function p.main.locateUserScript() + local defaults = { "premake5.lua", "premake4.lua" } + for i = 1, #defaults do + if os.isfile(defaults[i]) then + _MAIN_SCRIPT = defaults[i] + break + end + end + + if not _MAIN_SCRIPT then + _MAIN_SCRIPT = defaults[1] + end + + if _OPTIONS.file then + _MAIN_SCRIPT = _OPTIONS.file + end + + _MAIN_SCRIPT = path.getabsolute(_MAIN_SCRIPT) + _MAIN_SCRIPT_DIR = path.getdirectory(_MAIN_SCRIPT) + end + + + --- -- Add a new module loader that knows how to use the Premake paths like -- PREMAKE_PATH and the --scripts option, and follows the module/module.lua diff --git a/src/host/lua_auxlib.c b/src/host/lua_auxlib.c index 3815f195..eb5d4920 100644 --- a/src/host/lua_auxlib.c +++ b/src/host/lua_auxlib.c @@ -1,7 +1,7 @@ /** * \file lua_auxlib.c * \brief Modifications and extensions to Lua's library functions. - * \author Copyright (c) 2014 Jason Perkins and the Premake project + * \author Copyright (c) 2014-2015 Jason Perkins and the Premake project */ #include "premake.h" @@ -33,29 +33,38 @@ LUALIB_API int luaL_loadfile (lua_State* L, const char* filename) int bottom = lua_gettop(L); int z = !OKAY; - /* If the currently running script was embedded, try to load this file - * as it if were embedded too. */ - lua_getglobal(L, "_SCRIPT_DIR"); - script_dir = lua_tostring(L, -1); - - if (script_dir && script_dir[0] == '$') { - /* Call `path.getabsolute(filename, _SCRIPT_DIR)` to resolve any - * "../" sequences in the filename */ - lua_pushcfunction(L, path_getabsolute); - lua_pushstring(L, filename); - lua_pushvalue(L, -3); - lua_call(L, 2, 1); - test_name = lua_tostring(L, -1); - - /* if successful, filename and chunk will be on top of stack */ - z = premake_load_embedded_script(L, test_name + 2); /* Skip over leading "$/" */ - - /* remove test_name */ - lua_remove(L, bottom + 1); + /* If filename is starts with "$/" then we want to load the version that + * was embedded into the executable and skip the local file system */ + if (filename[0] == '$') { + z = premake_load_embedded_script(L, filename + 2); /* Skip over leading "$/" */ + if (z != OKAY) return z; } - /* remove _SCRIPT_DIR */ - lua_remove(L, bottom); + /* If the currently running script was embedded, try to load this file + * as it if were embedded too. */ + if (z != OKAY) { + lua_getglobal(L, "_SCRIPT_DIR"); + script_dir = lua_tostring(L, -1); + + if (script_dir && script_dir[0] == '$') { + /* Call `path.getabsolute(filename, _SCRIPT_DIR)` to resolve any + * "../" sequences in the filename */ + lua_pushcfunction(L, path_getabsolute); + lua_pushstring(L, filename); + lua_pushvalue(L, -3); + lua_call(L, 2, 1); + test_name = lua_tostring(L, -1); + + /* if successful, filename and chunk will be on top of stack */ + z = premake_load_embedded_script(L, test_name + 2); /* Skip over leading "$/" */ + + /* remove test_name */ + lua_remove(L, bottom + 1); + } + + /* remove _SCRIPT_DIR */ + lua_remove(L, bottom); + } /* Try to locate the script on the filesystem */ if (z != OKAY) { diff --git a/src/host/os_locate.c b/src/host/os_locate.c index 606e10df..9aa33b38 100644 --- a/src/host/os_locate.c +++ b/src/host/os_locate.c @@ -8,6 +8,20 @@ #include "premake.h" +int do_locate(lua_State* L, const char* filename, const char* path) +{ + if (do_pathsearch(L, filename, path)) { + lua_pushstring(L, "/"); + lua_pushstring(L, filename); + lua_concat(L, 3); + return 1; + } + + return 0; +} + + + int os_locate(lua_State* L) { int i; @@ -26,22 +40,10 @@ int os_locate(lua_State* L) return 1; } - /* Call os.pathsearch(arg[i], premake.path) */ - lua_pushcfunction(L, os_pathsearch); - lua_pushvalue(L, i); - lua_pushvalue(L, -3); - lua_call(L, 2, 1); - - /* os.pathsearch() returns the directory containing the file; - * append the filename to complete the path */ - if (!lua_isnil(L, -1)) { - lua_pushstring(L, "/"); - lua_pushvalue(L, 1); - lua_concat(L, 3); + /* do_locate(arg[i], premake.path) */ + if (do_locate(L, lua_tostring(L, i), lua_tostring(L, -1))) { return 1; } - - lua_pop(L, 1); } return 0; diff --git a/src/host/os_pathsearch.c b/src/host/os_pathsearch.c index ec11e579..ec152258 100644 --- a/src/host/os_pathsearch.c +++ b/src/host/os_pathsearch.c @@ -11,76 +11,82 @@ #include "premake.h" +int do_pathsearch(lua_State* L, const char* filename, const char* path) +{ + do + { + const char* split; + + /* look for the closest path separator ; or : */ + /* can't use : on windows because it breaks on C:\path */ + const char* semi = strchr(path, ';'); +#if !defined(PLATFORM_WINDOWS) + const char* full = strchr(path, ':'); +#else + const char* full = NULL; +#endif + + if (!semi) + { + split = full; + } + else if (!full) + { + split = semi; + } + else + { + split = (semi < full) ? semi : full; + } + + /* push this piece of the full search string onto the stack */ + if (split) + { + lua_pushlstring(L, path, split - path); + } + else + { + lua_pushstring(L, path); + } + + /* keep an extra copy around, so I can return it if I have a match */ + lua_pushvalue(L, -1); + + /* append the filename to make the full test path */ + lua_pushstring(L, "/"); + lua_pushstring(L, filename); + lua_concat(L, 3); + + /* test it - if it exists, return the absolute path */ + if (do_isfile(lua_tostring(L, -1))) + { + lua_pop(L, 1); + lua_pushcfunction(L, path_getabsolute); + lua_pushvalue(L, -2); + lua_call(L, 1, 1); + return 1; + } + + /* no match, set up the next try */ + lua_pop(L, 2); + path = (split) ? split + 1 : NULL; + } + while (path); + + return 0; +} + + + int os_pathsearch(lua_State* L) { int i; + + const char* filename = luaL_checkstring(L, 1); for (i = 2; i <= lua_gettop(L); ++i) { - const char* path; - - if (lua_isnil(L, i)) - continue; - - path = luaL_checkstring(L, i); - do - { - const char* split; - - /* look for the closest path separator ; or : */ - /* can't use : on windows because it breaks on C:\path */ - const char* semi = strchr(path, ';'); -#if !defined(PLATFORM_WINDOWS) - const char* full = strchr(path, ':'); -#else - const char* full = NULL; -#endif - - if (!semi) - { - split = full; - } - else if (!full) - { - split = semi; - } - else - { - split = (semi < full) ? semi : full; - } - - /* push this piece of the full search string onto the stack */ - if (split) - { - lua_pushlstring(L, path, split - path); - } - else - { - lua_pushstring(L, path); - } - - /* keep an extra copy around, so I can return it if I have a match */ - lua_pushvalue(L, -1); - - /* append the filename to make the full test path */ - lua_pushstring(L, "/"); - lua_pushvalue(L, 1); - lua_concat(L, 3); - - /* test it - if it exists, return the absolute path */ - if (do_isfile(lua_tostring(L, -1))) - { - lua_pop(L, 1); - lua_pushcfunction(L, path_getabsolute); - lua_pushvalue(L, -2); - lua_call(L, 1, 1); - return 1; - } - - /* no match, set up the next try */ - lua_pop(L, 2); - path = (split) ? split + 1 : NULL; - } - while (path); + if (lua_isnil(L, i)) continue; + if (do_pathsearch(L, filename, luaL_checkstring(L, i))) return 1; } return 0; diff --git a/src/host/premake.c b/src/host/premake.c index cab7269f..847986ee 100644 --- a/src/host/premake.c +++ b/src/host/premake.c @@ -22,6 +22,7 @@ static void build_premake_path(lua_State* L); static int process_arguments(lua_State* L, int argc, const char** argv); +static int run_premake_main(lua_State* L, const char* script); /* A search path for script files */ @@ -132,13 +133,14 @@ int premake_init(lua_State* L) } + int premake_execute(lua_State* L, int argc, const char** argv, const char* script) { int iErrFunc; /* push the absolute path to the Premake executable */ lua_pushcfunction(L, path_getabsolute); - premake_locate(L, argv[0]); + premake_locate_executable(L, argv[0]); lua_call(L, 1, 1); lua_setglobal(L, "_PREMAKE_COMMAND"); @@ -150,8 +152,8 @@ int premake_execute(lua_State* L, int argc, const char** argv, const char* scrip /* Use --scripts and PREMAKE_PATH to populate premake.path */ build_premake_path(L); - /* load the main script */ - if (luaL_dofile(L, script) != OKAY) { + /* Find and run the main Premake bootstrapping script */ + if (run_premake_main(L, script) != OKAY) { printf(ERROR_MESSAGE, lua_tostring(L, -1)); return !OKAY; } @@ -185,7 +187,7 @@ int premake_execute(lua_State* L, int argc, const char** argv, const char* scrip * http://stackoverflow.com/questions/933850/how-to-find-the-location-of-the-executable-in-c * http://stackoverflow.com/questions/1023306/finding-current-executables-path-without-proc-self-exe */ -int premake_locate(lua_State* L, const char* argv0) +int premake_locate_executable(lua_State* L, const char* argv0) { char buffer[PATH_MAX]; const char* path = NULL; @@ -262,6 +264,50 @@ int premake_locate(lua_State* L, const char* argv0) +/** + * Checks one or more of the standard script search locations to locate the + * specified file. If found, returns the discovered path to the script on + * the top of the Lua stack. + */ +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); + lua_pushstring(L, filename); + lua_call(L, 1, 1); + return OKAY; + } + } + + if (scripts_path && (searchMask & TEST_SCRIPTS)) { + if (do_locate(L, filename, scripts_path)) return OKAY; + } + + if (searchMask & TEST_PATH) { + const char* path = getenv("PREMAKE_PATH"); + if (path && do_locate(L, filename, path)) return OKAY; + } + + 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; + } + } + } + + return !OKAY; +} + + + static const char* set_scripts_path(const char* relativePath) { char* path = (char*)malloc(PATH_MAX); @@ -376,6 +422,41 @@ static int process_arguments(lua_State* L, int argc, const char** argv) +/** + * Find and run the main Premake bootstrapping script. The loading of the + * bootstrap and the other core scripts use a limited set of search paths + * to avoid mismatches between the native host code and the scripts + * themselves. + */ +static int run_premake_main(lua_State* L, const char* script) +{ + /* Release builds want to load the embedded scripts, with --scripts + * argument allowed as an override. Debug builds will look at the + * local file system first, then fall back to embedded. */ +#if defined(NDEBUG) + int z = premake_test_file(L, script, + TEST_SCRIPTS | TEST_EMBEDDED); +#else + int z = premake_test_file(L, script, + TEST_LOCAL | TEST_SCRIPTS | TEST_PATH | TEST_EMBEDDED); +#endif + + /* If no embedded script can be found, release builds will then + * try to fall back to the local file system, just in case */ +#if defined(NDEBUG) + if (z != OKAY) { + z = premake_test_file(L, script, TEST_LOCAL | TEST_PATH); + } +#endif + + if (z == OKAY) { + z = luaL_dofile(L, lua_tostring(L, -1)); + } + return z; +} + + + /** * Load a script that was previously embedded into the executable. If * successful, a function containing the new script chunk is pushed to diff --git a/src/host/premake.h b/src/host/premake.h index a9df7345..1a3c2bd3 100644 --- a/src/host/premake.h +++ b/src/host/premake.h @@ -1,7 +1,7 @@ /** * \file premake.h * \brief Program-wide constants and definitions. - * \author Copyright (c) 2002-2014 Jason Perkins and the Premake project + * \author Copyright (c) 2002-2015 Jason Perkins and the Premake project */ #define lua_c @@ -60,6 +60,13 @@ #define OKAY (0) +/* Bitmasks for the different script file search locations */ +#define TEST_LOCAL (0x01) +#define TEST_SCRIPTS (0x02) +#define TEST_PATH (0x04) +#define TEST_EMBEDDED (0x08) + + /* If a /scripts argument is present, its value */ extern const char* scripts_path; @@ -71,7 +78,9 @@ void do_getabsolute(char* result, const char* value, const char* relative_to); int do_getcwd(char* buffer, size_t size); int do_isabsolute(const char* path); int do_isfile(const char* filename); +int do_locate(lua_State* L, const char* filename, const char* path); void do_normalize(lua_State* L, char* buffer, const char* path); +int do_pathsearch(lua_State* L, const char* filename, const char* path); void do_translate(char* value, const char sep); @@ -113,10 +122,10 @@ int string_startswith(lua_State* L); /* Engine interface */ int premake_init(lua_State* L); -int premake_locate(lua_State* L, const char* argv0); int premake_execute(lua_State* L, int argc, const char** argv, const char* script); -int premake_find_exe(lua_State* L, const char* argv0); int premake_load_embedded_script(lua_State* L, 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[];