Files
llamaplayer/Src/external_dependencies/openmpt-trunk/include/premake/src/host/premake.c
2024-09-24 14:54:57 +02:00

706 lines
18 KiB
C
Vendored

/**
* \file premake.c
* \brief Program entry point.
* \author Copyright (c) 2002-2017 Jason Perkins and the Premake project
*/
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "premake.h"
#include "lua_shimtable.h"
#if PLATFORM_MACOSX
#include <CoreFoundation/CFBundle.h>
#endif
#if PLATFORM_BSD
#include <sys/types.h>
#include <sys/sysctl.h>
#endif
#define ERROR_MESSAGE "Error: %s\n"
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 */
const char* scripts_path = NULL;
/* Built-in functions */
static const luaL_Reg criteria_functions[] = {
{ "_compile", criteria_compile },
{ "_delete", criteria_delete },
{ "matches", criteria_matches },
{ NULL, NULL }
};
static const luaL_Reg debug_functions[] = {
{ "prompt", debug_prompt },
{ NULL, NULL }
};
static const luaL_Reg path_functions[] = {
{ "getabsolute", path_getabsolute },
{ "getrelative", path_getrelative },
{ "isabsolute", path_isabsolute },
{ "join", path_join },
{ "deferredjoin", path_deferred_join },
{ "hasdeferredjoin", path_has_deferred_join },
{ "resolvedeferredjoin", path_resolve_deferred_join },
{ "normalize", path_normalize },
{ "translate", path_translate },
{ "wildcards", path_wildcards },
{ NULL, NULL }
};
static const luaL_Reg os_functions[] = {
{ "chdir", os_chdir },
{ "chmod", os_chmod },
{ "comparefiles", os_comparefiles },
{ "copyfile", os_copyfile },
{ "_is64bit", os_is64bit },
{ "isdir", os_isdir },
{ "getcwd", os_getcwd },
{ "getpass", os_getpass },
{ "getWindowsRegistry", os_getWindowsRegistry },
{ "listWindowsRegistry", os_listWindowsRegistry },
{ "getversion", os_getversion },
{ "host", os_host },
{ "isfile", os_isfile },
{ "islink", os_islink },
{ "locate", os_locate },
{ "matchdone", os_matchdone },
{ "matchisfile", os_matchisfile },
{ "matchname", os_matchname },
{ "matchnext", os_matchnext },
{ "matchstart", os_matchstart },
{ "mkdir", os_mkdir },
#if PLATFORM_WINDOWS
// utf8 functions for Windows (assuming posix already handle utf8)
{"remove", os_remove },
{"rename", os_rename },
#endif
{ "pathsearch", os_pathsearch },
{ "realpath", os_realpath },
{ "rmdir", os_rmdir },
{ "stat", os_stat },
{ "uuid", os_uuid },
{ "writefile_ifnotequal", os_writefile_ifnotequal },
{ "touchfile", os_touchfile },
{ "compile", os_compile },
{ NULL, NULL }
};
static const luaL_Reg premake_functions[] = {
{ "getEmbeddedResource", premake_getEmbeddedResource },
{ NULL, NULL }
};
static const luaL_Reg string_functions[] = {
{ "endswith", string_endswith },
{ "hash", string_hash },
{ "sha1", string_sha1 },
{ "startswith", string_startswith },
{ NULL, NULL }
};
static const luaL_Reg buffered_functions[] = {
{ "new", buffered_new },
{ "write", buffered_write },
{ "writeln", buffered_writeln },
{ "tostring", buffered_tostring },
{ "close", buffered_close },
{ NULL, NULL }
};
static const luaL_Reg term_functions[] = {
{ "getTextColor", term_getTextColor },
{ "setTextColor", term_setTextColor },
{ NULL, NULL }
};
#ifdef PREMAKE_CURL
static const luaL_Reg http_functions[] = {
{ "get", http_get },
{ "post", http_post },
{ "download", http_download },
{ NULL, NULL }
};
#endif
#ifdef PREMAKE_COMPRESSION
static const luaL_Reg zip_functions[] = {
{ "extract", zip_extract },
{ NULL, NULL }
};
#endif
static void lua_getorcreate_table(lua_State *L, const char *modname)
{
luaL_getsubtable(L, LUA_REGISTRYINDEX, "_LOADED"); // get _LOADED table stack = {_LOADED}
lua_getfield(L, -1, modname); // get _LOADED[modname] stack = { _LOADED, result }
if (!lua_istable(L, -1)) // not found? stack = { _LOADED, result }
{
lua_pop(L, 1); // remove previous result stack = { _LOADED }
lua_pushglobaltable(L); // push _G onto stack stack = { _LOADED, _G }
lua_createtable(L, 0, 0); // new table for field stack = { _LOADED, _G, result }
lua_pushlstring(L, modname, strlen(modname)); // stack = { _LOADED, _G, result, modname }
lua_pushvalue(L, -2); // stack = { _LOADED, _G, result, modname, result }
lua_settable(L, -4); // _G[modname] = result stack = { _LOADED, _G, result }
lua_remove(L, -2); // remove _G from stack stack = { _LOADED, result }
lua_pushvalue(L, -1); // duplicate result stack = { _LOADED, result, result }
lua_setfield(L, -3, modname); // _LOADED[modname] = result stack = { _LOADED, result }
}
lua_remove(L, -2); // remove _LOADED from stack stack = { result }
}
void luaL_register(lua_State *L, const char *libname, const luaL_Reg *l)
{
lua_getorcreate_table(L, libname);
luaL_setfuncs(L, l, 0);
lua_pop(L, 1);
}
/**
* Initialize the Premake Lua environment.
*/
int premake_init(lua_State* L)
{
const char* value;
luaL_register(L, "premake", premake_functions);
luaL_register(L, "criteria", criteria_functions);
luaL_register(L, "debug", debug_functions);
luaL_register(L, "path", path_functions);
luaL_register(L, "os", os_functions);
luaL_register(L, "string", string_functions);
luaL_register(L, "buffered", buffered_functions);
luaL_register(L, "term", term_functions);
#ifdef PREMAKE_CURL
luaL_register(L, "http", http_functions);
#endif
#ifdef PREMAKE_COMPRESSION
luaL_register(L, "zip", zip_functions);
#endif
lua_pushlightuserdata(L, &s_shimTable);
lua_rawseti(L, LUA_REGISTRYINDEX, 0x5348494D); // equal to 'SHIM'
/* push the application metadata */
lua_pushstring(L, LUA_COPYRIGHT);
lua_setglobal(L, "_COPYRIGHT");
lua_pushstring(L, PREMAKE_VERSION);
lua_setglobal(L, "_PREMAKE_VERSION");
lua_pushstring(L, PREMAKE_COPYRIGHT);
lua_setglobal(L, "_PREMAKE_COPYRIGHT");
lua_pushstring(L, PREMAKE_PROJECT_URL);
lua_setglobal(L, "_PREMAKE_URL");
/* set the OS platform variable */
lua_pushstring(L, PLATFORM_STRING);
lua_setglobal(L, "_TARGET_OS");
/* find the user's home directory */
value = getenv("HOME");
if (!value) value = getenv("USERPROFILE");
if (!value) value = "~";
lua_pushstring(L, value);
lua_setglobal(L, "_USER_HOME_DIR");
/* publish the initial working directory */
os_getcwd(L);
lua_setglobal(L, "_WORKING_DIR");
#if !defined(PREMAKE_NO_BUILTIN_SCRIPTS)
/* let native modules initialize themselves */
registerModules(L);
#endif
return OKAY;
}
static void setErrorColor(lua_State* L)
{
int errorColor = 12;
lua_getglobal(L, "term");
lua_pushstring(L, "errorColor");
lua_gettable(L, -2);
if (!lua_isnil(L, -1))
errorColor = (int)luaL_checkinteger(L, -1);
term_doSetTextColor(errorColor);
lua_pop(L, 2);
}
void printLastError(lua_State* L)
{
const char* message = lua_tostring(L, -1);
int oldColor = term_doGetTextColor();
setErrorColor(L);
printf(ERROR_MESSAGE, message);
term_doSetTextColor(oldColor);
}
static int lua_error_handler(lua_State* L)
{
// in debug mode, show full traceback on all errors
#if !defined(NDEBUG)
lua_getglobal(L, "debug");
lua_getfield(L, -1, "traceback");
lua_remove(L, -2); // remove debug table
lua_insert(L, -2); // insert traceback function before error message
lua_pushinteger(L, 3); // push level
lua_call(L, 2, 1); // call traceback
#else
(void) L;
#endif
return 1;
}
int premake_pcall(lua_State* L, int nargs, int nresults)
{
lua_pushcfunction(L, lua_error_handler);
int error_handler_index = lua_gettop(L) - nargs - 1;
lua_insert(L, error_handler_index); // insert lua_error_handler before call parameters
int result = lua_pcall(L, nargs, nresults, error_handler_index);
lua_remove(L, error_handler_index); // remove lua_error_handler from stack
return result;
}
int premake_execute(lua_State* L, int argc, const char** argv, const char* script)
{
/* push the absolute path to the Premake executable */
lua_pushcfunction(L, path_getabsolute);
premake_locate_executable(L, argv[0]);
lua_call(L, 1, 1);
lua_setglobal(L, "_PREMAKE_COMMAND");
/* Parse the command line arguments */
if (process_arguments(L, argc, argv) != OKAY) {
return !OKAY;
}
/* Use --scripts and PREMAKE_PATH to populate premake.path */
build_premake_path(L);
/* Find and run the main Premake bootstrapping script */
if (run_premake_main(L, script) != OKAY) {
printLastError(L);
return !OKAY;
}
/* and call the main entry point */
lua_getglobal(L, "_premake_main");
if (premake_pcall(L, 0, 1) != OKAY) {
printLastError(L);
return !OKAY;
}
else {
int exitCode = (int)lua_tonumber(L, -1);
return exitCode;
}
}
/**
* Locate the Premake executable, and push its full path to the Lua stack.
* Based on:
* http://sourceforge.net/tracker/index.php?func=detail&aid=3351583&group_id=71616&atid=531880
* 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_executable(lua_State* L, const char* argv0)
{
char buffer[PATH_MAX];
const char* path = NULL;
#if PLATFORM_WINDOWS
wchar_t widebuffer[PATH_MAX];
DWORD len = GetModuleFileNameW(NULL, widebuffer, PATH_MAX);
if (len > 0)
{
WideCharToMultiByte(CP_UTF8, 0, widebuffer, len, buffer, PATH_MAX, NULL, NULL);
buffer[len] = 0;
path = buffer;
}
#endif
#if PLATFORM_MACOSX
CFURLRef bundleURL = CFBundleCopyExecutableURL(CFBundleGetMainBundle());
CFStringRef pathRef = CFURLCopyFileSystemPath(bundleURL, kCFURLPOSIXPathStyle);
if (CFStringGetCString(pathRef, buffer, PATH_MAX - 1, kCFStringEncodingUTF8))
path = buffer;
#endif
#if PLATFORM_LINUX
int len = readlink("/proc/self/exe", buffer, PATH_MAX - 1);
if (len > 0)
{
buffer[len] = 0;
path = buffer;
}
#endif
#if PLATFORM_BSD && !defined(__OpenBSD__)
int len = readlink("/proc/curproc/file", buffer, PATH_MAX - 1);
if (len < 0)
len = readlink("/proc/curproc/exe", buffer, PATH_MAX - 1);
if (len < 0)
{
int mib[4];
mib[0] = CTL_KERN;
mib[1] = KERN_PROC;
mib[2] = KERN_PROC_PATHNAME;
mib[3] = -1;
size_t cb = sizeof(buffer);
sysctl(mib, 4, buffer, &cb, NULL, 0);
len = (int)cb;
}
if (len > 0)
{
buffer[len] = 0;
path = buffer;
}
#endif
#if PLATFORM_SOLARIS
int len = readlink("/proc/self/path/a.out", buffer, PATH_MAX - 1);
if (len > 0)
{
buffer[len] = 0;
path = buffer;
}
#endif
/* As a fallback, search the PATH with argv[0] */
if (!path)
{
lua_pushcfunction(L, os_pathsearch);
lua_pushstring(L, argv0);
lua_pushstring(L, getenv("PATH"));
if (lua_pcall(L, 2, 1, 0) == OKAY && !lua_isnil(L, -1))
{
lua_pushstring(L, "/");
lua_pushstring(L, argv0);
lua_concat(L, 3);
path = lua_tostring(L, -1);
}
lua_pop(L, 1);
}
/* If all else fails, use argv[0] as-is and hope for the best */
if (!path)
{
/* make it absolute, if needed */
os_getcwd(L);
lua_pushstring(L, "/");
lua_pushstring(L, argv0);
if (!do_isabsolute(argv0)) {
lua_concat(L, 3);
}
else {
lua_pop(L, 3);
lua_pushstring(L, argv0);
}
path = lua_tostring(L, -1);
lua_pop(L, 1);
}
lua_pushstring(L, path);
return 1;
}
/**
* 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)
{
if (searchMask & TEST_LOCAL) {
if (do_isfile(L, 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 !defined(PREMAKE_NO_BUILTIN_SCRIPTS)
if ((searchMask & TEST_EMBEDDED) != 0) {
/* Try to locate a record matching the filename */
if (premake_find_embedded_script(filename) != NULL) {
lua_pushstring(L, "$/");
lua_pushstring(L, filename);
lua_concat(L, 2);
return OKAY;
}
}
#endif
return !OKAY;
}
static const char* set_scripts_path(const char* relativePath)
{
char* path = (char*)malloc(PATH_MAX);
do_getabsolute(path, relativePath, NULL);
scripts_path = path;
return scripts_path;
}
/**
* Set the premake.path variable, pulling from the --scripts argument
* and PREMAKE_PATH environment variable if present.
*/
static void build_premake_path(lua_State* L)
{
int top;
const char* value;
lua_getglobal(L, "premake");
top = lua_gettop(L);
/* Start by searching the current working directory */
lua_pushstring(L, ".");
/* The --scripts argument goes next, if present */
if (scripts_path) {
lua_pushstring(L, ";");
lua_pushstring(L, scripts_path);
}
/* Then the PREMAKE_PATH environment variable */
value = getenv("PREMAKE_PATH");
if (value) {
lua_pushstring(L, ";");
lua_pushstring(L, value);
}
/* Then in ~/.premake */
lua_pushstring(L, ";");
lua_getglobal(L, "_USER_HOME_DIR");
lua_pushstring(L, "/.premake");
/* In the user's Application Support folder */
#if defined(PLATFORM_MACOSX)
lua_pushstring(L, ";");
lua_getglobal(L, "_USER_HOME_DIR");
lua_pushstring(L, "/Library/Application Support/Premake");
#endif
/* In the /usr tree */
lua_pushstring(L, ";/usr/local/share/premake;/usr/share/premake");
/* Put it all together */
lua_concat(L, lua_gettop(L) - top);
/* Match Lua's package.path; use semicolon separators */
#if !defined(PLATFORM_WINDOWS)
lua_getglobal(L, "string");
lua_getfield(L, -1, "gsub");
lua_pushvalue(L, -3);
lua_pushstring(L, ":");
lua_pushstring(L, ";");
lua_call(L, 3, 1);
/* remove the string global table */
lua_remove(L, -2);
/* remove the previously concatonated result */
lua_remove(L, -2);
#endif
/* Store it in premake.path */
lua_setfield(L, -2, "path");
/* Remove the premake namespace table */
lua_pop(L, 1);
}
/**
* Copy all command line arguments into the script-side _ARGV global, and
* check for the presence of a /scripts=<path> argument to help locate
* the manifest if needed.
* \returns OKAY if successful.
*/
static int process_arguments(lua_State* L, int argc, const char** argv)
{
int i;
/* Copy all arguments in the _ARGV global */
lua_newtable(L);
for (i = 1; i < argc; ++i)
{
lua_pushstring(L, argv[i]);
lua_rawseti(L, -2, luaL_len(L, -2) + 1);
/* The /scripts option gets picked up here; used later to find the
* manifest and scripts later if necessary */
if (strncmp(argv[i], "/scripts=", 9) == 0)
{
argv[i] = set_scripts_path(argv[i] + 9);
}
else if (strncmp(argv[i], "--scripts=", 10) == 0)
{
argv[i] = set_scripts_path(argv[i] + 10);
}
}
lua_setglobal(L, "_ARGV");
return OKAY;
}
/**
* 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) {
const char* filename = lua_tostring(L, -1);
z = luaL_dofile(L, filename);
}
return z;
}
/**
* Locate a file in the embedded script index. If found, returns the
* contents of the file's script.
*/
const buildin_mapping* premake_find_embedded_script(const char* filename)
{
#if !defined(PREMAKE_NO_BUILTIN_SCRIPTS)
int i;
for (i = 0; builtin_scripts[i].name != NULL; ++i) {
if (strcmp(builtin_scripts[i].name, filename) == 0) {
return builtin_scripts + i;
}
}
#endif
return NULL;
}
/**
* Load a script that was previously embedded into the executable. If
* successful, a function containing the new script chunk is pushed to
* the stack, just like luaL_loadfile would do had the chunk been loaded
* from a file.
*/
int premake_load_embedded_script(lua_State* L, const char* filename)
{
#if !defined(NDEBUG)
static int warned = 0;
#endif
const buildin_mapping* chunk = premake_find_embedded_script(filename);
if (chunk == NULL) {
return LUA_ERRFILE;
}
/* Debug builds probably want to be loading scripts from the disk */
#if !defined(NDEBUG)
if (!warned) {
warned = 1;
printf("** warning: using embedded script '%s'; use /scripts argument to load from files\n", filename);
}
#endif
/* "Fully qualify" the filename by turning it into the form $/filename */
lua_pushstring(L, "$/");
lua_pushstring(L, filename);
lua_concat(L, 2);
/* Load the chunk */
return luaL_loadbuffer(L, (const char*)chunk->bytecode, chunk->length, filename);
}
/**
* Give the lua runtime raw access to embedded files.
*/
int premake_getEmbeddedResource(lua_State* L)
{
const char* filename = luaL_checkstring(L, 1);
const buildin_mapping* chunk = premake_find_embedded_script(filename);
if (chunk == NULL)
{
return 0;
}
lua_pushlstring(L, (const char*)chunk->bytecode, chunk->length);
return 1;
}