/** * \file premake.c * \brief Program entry point. * \author Copyright (c) 2002-2011 Jason Perkins and the Premake project */ #include #include #include "premake.h" #define VERSION "HEAD" #define COPYRIGHT "Copyright (C) 2002-2011 Jason Perkins and the Premake Project" #define ERROR_MESSAGE "%s\n" static int process_arguments(lua_State* L, int argc, const char** argv); static int process_option(lua_State* L, const char* arg); static int load_builtin_scripts(lua_State* L); /* A search path for script files */ static const char* scripts_path = NULL; /* precompiled bytecode buffer; in bytecode.c */ extern const char* builtin_scripts[]; /* Built-in functions */ static const luaL_Reg path_functions[] = { { "isabsolute", path_isabsolute }, { NULL, NULL } }; static const luaL_Reg os_functions[] = { { "chdir", os_chdir }, { "copyfile", os_copyfile }, { "isdir", os_isdir }, { "getcwd", os_getcwd }, { "getversion", os_getversion }, { "isfile", os_isfile }, { "matchdone", os_matchdone }, { "matchisfile", os_matchisfile }, { "matchname", os_matchname }, { "matchnext", os_matchnext }, { "matchstart", os_matchstart }, { "mkdir", os_mkdir }, { "pathsearch", os_pathsearch }, { "rmdir", os_rmdir }, { "uuid", os_uuid }, { NULL, NULL } }; static const luaL_Reg string_functions[] = { { "endswith", string_endswith }, { NULL, NULL } }; /** * Program entry point. */ int main(int argc, const char** argv) { lua_State* L; int z = OKAY; /* prepare Lua for use */ L = lua_open(); luaL_openlibs(L); luaL_register(L, "path", path_functions); luaL_register(L, "os", os_functions); luaL_register(L, "string", string_functions); /* push the application metadata */ lua_pushstring(L, LUA_COPYRIGHT); lua_setglobal(L, "_COPYRIGHT"); lua_pushstring(L, VERSION); lua_setglobal(L, "_PREMAKE_VERSION"); lua_pushstring(L, COPYRIGHT); lua_setglobal(L, "_PREMAKE_COPYRIGHT"); /* set the OS platform variable */ lua_pushstring(L, PLATFORM_STRING); lua_setglobal(L, "_OS"); /* Parse the command line arguments */ if (z == OKAY) z = process_arguments(L, argc, argv); /* Run the built-in Premake scripts */ if (z == OKAY) z = load_builtin_scripts(L); /* Clean up and turn off the lights */ lua_close(L); return z; } /** * Process the command line arguments, splitting them into options, the * target action, and any arguments to that action. The results are pushed * into the session for later use. I could have done this in the scripts, * but I need the value of the /scripts option to find them. * \returns OKAY if successful. */ int process_arguments(lua_State* L, int argc, const char** argv) { int i; /* Create empty lists for Options and Args */ lua_newtable(L); lua_newtable(L); for (i = 1; i < argc; ++i) { /* Options start with '/' or '--'. The first argument that isn't an option * is the action. Anything after that is an argument to the action */ if (argv[i][0] == '/') { process_option(L, argv[i] + 1); } else if (argv[i][0] == '-' && argv[i][1] == '-') { process_option(L, argv[i] + 2); } else { /* not an option, is the action */ lua_pushstring(L, argv[i++]); lua_setglobal(L, "_ACTION"); /* everything else is an argument */ while (i < argc) { lua_pushstring(L, argv[i++]); lua_rawseti(L, -2, luaL_getn(L, -2) + 1); } } } /* push the Options and Args lists */ lua_setglobal(L, "_ARGS"); lua_setglobal(L, "_OPTIONS"); return OKAY; } /** * Parse an individual command-line option. * \returns OKAY if successful. */ int process_option(lua_State* L, const char* arg) { char key[512]; const char* value; /* If a value is specified, split the option into a key/value pair */ char* ptr = strchr(arg, '='); if (ptr) { int len = ptr - arg; if (len > 511) len = 511; strncpy(key, arg, len); key[len] = '\0'; value = ptr + 1; } else { strcpy(key, arg); value = ""; } /* Store it in the Options table, which is already on the stack */ lua_pushstring(L, value); lua_setfield(L, -3, key); /* The /scripts option gets picked up here to find the built-in scripts */ if (strcmp(key, "scripts") == 0 && strlen(value) > 0) { scripts_path = value; } return OKAY; } #if defined(_DEBUG) /** * When running in debug mode, the scripts are loaded from the disk. The path to * the scripts must be provided via either the /scripts command line option or * the PREMAKE_PATH environment variable. */ int load_builtin_scripts(lua_State* L) { const char* filename; /* call os.pathsearch() to locate _premake_main.lua */ lua_pushcfunction(L, os_pathsearch); lua_pushstring(L, "_premake_main.lua"); lua_pushstring(L, scripts_path); lua_pushstring(L, getenv("PREMAKE_PATH")); lua_call(L, 3, 1); if (lua_isnil(L, -1)) { printf(ERROR_MESSAGE, "Unable to find _premake_main.lua; use /scripts option when in debug mode!\n" "Please refer to the documentation (or build in release mode instead)." ); return !OKAY; } /* run the bootstrapping script */ scripts_path = lua_tostring(L, -1); filename = lua_pushfstring(L, "%s/_premake_main.lua", scripts_path); if (luaL_dofile(L, filename)) { printf(ERROR_MESSAGE, lua_tostring(L, -1)); return !OKAY; } /* hand off control to the scripts */ lua_getglobal(L, "_premake_main"); lua_pushstring(L, scripts_path); if (lua_pcall(L, 1, 1, 0) != OKAY) { printf(ERROR_MESSAGE, lua_tostring(L, -1)); return !OKAY; } else { return (int)lua_tonumber(L, -1); } } #endif #if defined(NDEBUG) /** * When running in release mode, the scripts are loaded from a static data * buffer, where they were stored by a preprocess. To update these embedded * scripts, run `premake4 embed` then rebuild. */ int load_builtin_scripts(lua_State* L) { int i; for (i = 0; builtin_scripts[i]; ++i) { if (luaL_dostring(L, builtin_scripts[i]) != OKAY) { printf(ERROR_MESSAGE, lua_tostring(L, -1)); return !OKAY; } } /* hand off control to the scripts */ lua_getglobal(L, "_premake_main"); if (lua_pcall(L, 0, 1, 0) != OKAY) { printf(ERROR_MESSAGE, lua_tostring(L, -1)); return !OKAY; } else { return (int)lua_tonumber(L, -1); } } #endif