Allow modules loaded via require() to use script-relative paths

This commit is contained in:
Jason Perkins 2014-07-30 15:35:26 -04:00
parent 3a4888b810
commit 9044ece96f
6 changed files with 137 additions and 46 deletions

View File

@ -36,6 +36,7 @@
excludes
{
"src/host/lua-5.1.4/src/lauxlib.c",
"src/host/lua-5.1.4/src/lua.c",
"src/host/lua-5.1.4/src/luac.c",
"src/host/lua-5.1.4/src/print.c",

View File

@ -1,11 +1,11 @@
--
-- globals.lua
-- Replacements and extensions to Lua's global functions.
-- Copyright (c) 2002-2013 Jason Perkins and the Premake project
-- Copyright (c) 2002-2014 Jason Perkins and the Premake project
--
--
---
-- Helper for the dofile() and include() function: locate a script on the
-- standard search paths of: the /scripts argument provided on the command
-- line, then the PREMAKE_PATH environment variable.
@ -15,12 +15,10 @@
-- is the one that will be returned.
-- @return
-- The path to file if found, or nil if not.
--
---
local function locate(...)
for i = 1, select("#",...) do
local fname = select(i,...)
local function find(fname)
-- is this a direct path to a file?
if os.isfile(fname) then
return fname
@ -32,41 +30,33 @@
return path.join(dir, fname)
end
end
for i = 1, select("#",...) do
local fname = select(i, ...)
local result = find(fname)
if not result then
result = find(fname .. ".lua")
end
if result then
return result
end
end
end
--
-- A replacement for Lua's built-in dofile() function, this one sets the
-- current working directory to the script's location, enabling script-relative
-- referencing of other files and resources.
--
---
-- A replacement for Lua's built-in dofile() function that knows how to
-- search for script files. Note that I've also modified luaL_loadfile()
-- in src/host/lua_auxlib.c to set the _SCRIPT variable and adjust the
-- working directory.
---
local builtin_dofile = dofile
function dofile(fname)
-- remember the current working directory and file; I'll restore it shortly
local oldcwd = os.getcwd()
local oldfile = _SCRIPT
-- find it; if I can't just continue with the name and let the
-- built-in dofile() handle reporting the error as it will
fname = locate(fname) or fname
-- use the absolute path to the script file, to avoid any file name
-- ambiguity if an error should arise
_SCRIPT = path.getabsolute(fname)
-- switch the working directory to the new script location
local newcwd = path.getdirectory(_SCRIPT)
os.chdir(newcwd)
-- run the chunk. How can I catch variable return values?
local a, b, c, d, e, f = builtin_dofile(_SCRIPT)
-- restore the previous working directory when done
_SCRIPT = oldfile
os.chdir(oldcwd)
return a, b, c, d, e, f
return builtin_dofile(fname)
end
@ -97,24 +87,23 @@
--
---
-- Load and run an external script file, with a bit of extra logic to make
-- including projects easier. if "path" is a directory, will look for
-- path/premake5.lua. And each file is tracked, and loaded only once.
--
-- @param fname
-- The name of the directory or file to include. If a directory, will
-- automatically include the contained premake5.lua or premake4.lua
-- script at that lcoation.
---
io._includedFiles = {}
function include(fname)
local found = locate(fname)
if not found then
found = locate(path.join(fname, "premake5.lua"))
end
if not found then
found = locate(path.join(fname, "premake4.lua"))
end
local found = locate(fname, path.join(fname, "premake5.lua"), path.join(fname, "premake4.lua"))
-- but only load each file once
-- only include each file once
fname = path.getabsolute(found or fname)
if not io._includedFiles[fname] then
io._includedFiles[fname] = true

View File

@ -574,7 +574,7 @@ LUALIB_API int luaL_loadfile (lua_State *L, const char *filename) {
lf.f = freopen(filename, "rb", lf.f); /* reopen in binary mode */
if (lf.f == NULL) return errfile(L, "reopen", fnameindex);
/* skip eventual `#!...' */
while ((c = getc(lf.f)) != EOF && c != LUA_SIGNATURE[0]) ;
while ((c = getc(lf.f)) != EOF && c != LUA_SIGNATURE[0]) {};
lf.extraline = 0;
}
ungetc(c, lf.f);

91
src/host/lua_auxlib.c Normal file
View File

@ -0,0 +1,91 @@
/**
* \file lua_auxlib.c
* \brief Modifications and extensions to Lua's library functions.
* \author Copyright (c) 2014 Jason Perkins and the Premake project
*/
#include "premake.h"
#define luaL_loadfile original_luaL_loadfile
#include "lua-5.1.4/src/lauxlib.c"
#undef luaL_loadfile
/**
* Execute a chunk of code previous loaded by my customized version of
* luaL_loadfile(), below. Sets the _SCRIPT global variable to the
* absolute path of the loaded chunk, and makes its enclosing directory
* current so that relative path references to other files or scripts
* can be used.
*/
static int chunk_wrapper(lua_State* L)
{
char cwd[PATH_MAX];
char script[PATH_MAX];
const char* filename;
char* ptr;
int i, args;
args = lua_gettop(L);
/* Remember the current _SCRIPT and working directory so I can
* restore them after the script chunk has been run. */
do_getcwd(cwd, PATH_MAX);
lua_getglobal(L, "_SCRIPT");
/* Set the new _SCRIPT variable... */
filename = lua_tostring(L, lua_upvalueindex(2));
do_getabsolute(script, filename, NULL);
lua_pushstring(L, script);
lua_setglobal(L, "_SCRIPT");
/* ...and make it's containing directory current */
ptr = strrchr(script, '/');
if (ptr) *ptr = '\0';
do_chdir(script);
if (ptr) *ptr = '/';
/* Move the function's arguments to the top of the stack and
* execute the function created by luaL_loadfile() */
lua_pushvalue(L, lua_upvalueindex(1));
for (i = 1; i <= args; ++i) {
lua_pushvalue(L, i);
}
lua_call(L, args, LUA_MULTRET);
/* Finally, restore the previous _SCRIPT variable and working directory
* before returning control to the previously executing script. */
do_chdir(cwd);
lua_pushvalue(L, args + 1);
lua_setglobal(L, "_SCRIPT");
return lua_gettop(L) - args - 1;
}
/**
* Extend the default implementation of luaL_loadfile() to call my chunk
* wrapper, above, before executing any scripts loaded from a file.
*/
LUALIB_API int luaL_loadfile (lua_State* L, const char* filename)
{
int z = original_luaL_loadfile(L, filename);
if (z == 0) {
lua_pushstring(L, filename);
lua_pushcclosure(L, chunk_wrapper, 2);
}
return z;
}

View File

@ -1,16 +1,15 @@
/**
* \file os_chdir.c
* \brief Change the current working directory.
* \author Copyright (c) 2002-2008 Jason Perkins and the Premake project
* \author Copyright (c) 2002-2014 Jason Perkins and the Premake project
*/
#include "premake.h"
int os_chdir(lua_State* L)
int do_chdir(const char* path)
{
int z;
const char* path = luaL_checkstring(L, 1);
#if PLATFORM_WINDOWS
z = SetCurrentDirectory(path);
@ -18,6 +17,16 @@ int os_chdir(lua_State* L)
z = !chdir(path);
#endif
return z;
}
int os_chdir(lua_State* L)
{
const char* path = luaL_checkstring(L, 1);
int z = do_chdir(path);
if (!z)
{
lua_pushnil(L);

View File

@ -61,6 +61,7 @@
/* Bootstrapping helper functions */
int do_chdir(const char* path);
unsigned long do_hash(const char* str, int seed);
void do_getabsolute(char* result, const char* value, const char* relative_to);
int do_getcwd(char* buffer, size_t size);