diff --git a/src/base/criteria.lua b/src/base/criteria.lua index c5fe8557..fffde97e 100644 --- a/src/base/criteria.lua +++ b/src/base/criteria.lua @@ -8,7 +8,7 @@ -- Copyright (c) 2012-2014 Jason Perkins and the Premake project -- - premake.criteria = {} + premake.criteria = criteria local criteria = premake.criteria @@ -43,15 +43,18 @@ end local parts = path.wildcards(term) + local isWildcard = (parts ~= term) parts = parts:explode(" or ") for i, part in ipairs(parts) do if part:startswith("not ") then table.insert(pattern, "not") - table.insert(pattern, part:sub(5)) - else - table.insert(pattern, part) + part = part:sub(5) end + if isWildcard then + table.insert(pattern, "%%") + end + table.insert(pattern, part) end table.insert(patterns, pattern) @@ -62,101 +65,3 @@ crit.patterns = patterns return crit end - - ---- --- Determine if this criteria is met by the provided list of context terms. --- --- @param crit --- The criteria to be tested. --- @param context --- The list of context terms to test against, provided as a list of --- lowercase strings. --- @return --- True if all criteria are satisfied by the context. ---- - - function criteria.matches(crit, context) - -- If the context specifies a filename, I should only match against - -- blocks targeted at that file specifically. This way, files only - -- pick up the settings that a different from the main project. - local filename = context.files - local filematched = false - - -- Test one value from the context against a part of a pattern - function testValue(value, part) - if type(value) == "table" then - for i = 1, #value do - if testValue(value[i], part) then - return true - end - end - else - if value and value:match(part) == value then - return true; - end - end - return false - end - - -- Test one part of one pattern against the provided context - function testContext(prefix, part, assertion) - if prefix then - local result = testValue(context[prefix], part) - if prefix == "files" and result == assertion then - filematched = true - end - if result then - return assertion - end - else - if filename and assertion and filename:match(part) == filename then - filematched = true - return assertion - end - - for prefix, value in pairs(context) do - if testValue(value, part) then - return assertion - end - end - end - - return not assertion - end - - -- Test an individual pattern in this criteria's list of patterns - function testPattern(pattern) - local n = #pattern - local assertion = true - - for i = 1, n do - local part = pattern[i] - if part == "not" then - assertion = false - else - if testContext(pattern.prefix, part, assertion) then - return true - end - assertion = true - end - end - end - - -- Iterate the list of patterns and test each in turn - local n = #crit.patterns - for i = 1, n do - local pattern = crit.patterns[i] - if not testPattern(pattern) then - return false - end - end - - if filename and not filematched then - return false - end - - return true - end - - diff --git a/src/host/criteria_matches.c b/src/host/criteria_matches.c new file mode 100644 index 00000000..40d010db --- /dev/null +++ b/src/host/criteria_matches.c @@ -0,0 +1,283 @@ +/** + * \file criteria_matches.c + * \brief Determine if this criteria is met by the provided filter terms. + * \author Copyright (c) 2002-2013 Jason Perkins and the Premake project + */ + +#include "premake.h" +#include + + +/* + * return value:match(pattern) == value + */ +static int match(lua_State* L, const char* value, const char* pattern, int wildcard) +{ + if (wildcard) { + const char* result; + int matched = 0; + + int top = lua_gettop(L); + + lua_pushvalue(L, 4); + lua_pushstring(L, value); + lua_pushstring(L, pattern); + lua_call(L, 2, 1); + + if (lua_isstring(L, -1)) { + result = lua_tostring(L, -1); + matched = (strcmp(value, result) == 0); + } + + lua_settop(L, top); + return matched; + } + else { + return (strcmp(value, pattern) == 0); + } +} + + +/* + * Compares the value on the top of the stack to the provided + * part, which is a Lua pattern string. + */ +static int testValue(lua_State* L, const char* part, const int wildcard) +{ + const char* value; + size_t i, n; + int result; + + /* + if type(value) == "table" then + for i = 1, #value do + if testValue(value[i], part) then + return true + end + end + else + if value and value:match(part) == value then + return true; + end + end + */ + + if (lua_istable(L, -1)) { + n = lua_objlen(L, -1); + for (i = 1; i <= n; ++i) { + lua_rawgeti(L, -1, i); + result = testValue(L, part, wildcard); + lua_pop(L, 1); + if (result) { + return 1; + } + } + return 0; + } + + value = lua_tostring(L, -1); + if (value && match(L, value, part, wildcard)) { + return 1; + } + + return 0; +} + + +/* + * The context is a set of key-value pairs, something like: + * { + * action = "vs2010", + * configurations = "Debug", + * system = "windows", + * files = "/absolute/path/to/hello.cpp", + * -- and so on... + * } + */ +static int testContext(lua_State* L, const char* prefix, const char* part, + const int assertion, const int wildcard, + const char* filename, int* fileMatched) +{ + /* + if prefix then + local result = testValue(context[prefix], part, wildcard) + if result == assertion and prefix == "files" then + filematched = true + end + if result then + return assertion + end + else + if filename and assertion and filename:match(part) == filename then + filematched = true + return assertion + end + + for prefix, value in pairs(context) do + if testValue(value, part, wildcard) then + return assertion + end + end + end + */ + + int result; + if (prefix) { + lua_getfield(L, 2, prefix); + result = testValue(L, part, wildcard); + lua_pop(L, 1); + if (result == assertion && strcmp(prefix, "files") == 0) { + (*fileMatched) = 1; + } + if (result) { + return assertion; + } + } + else { + if (filename && assertion && match(L, filename, part, wildcard)) { + (*fileMatched) = 1; + return assertion; + } + + lua_pushnil(L); + while (lua_next(L, 2)) { + if (testValue(L, part, wildcard)) { + lua_pop(L, 2); + return assertion; + } + lua_pop(L, 1); + } + } + + return (!assertion); +} + + + +/* + * Patterns are represented as an array of string values. + * "windows" = { "windows" } + * "not windows" = { "not", "windows" } + * "windows or linux" = { "windows", "linux" } + * + * If the patterns is targeted at a specific prefix, that is stored + * as a keyed value. + * + * "files:**.c" = { prefix="files", "**.c" } + */ +static int testPattern(lua_State* L, const char* filename, int* fileMatched) +{ + const char* prefix; + const char* part; + size_t i, n; + int assertion = 1; + int wildcard = 0; + int result = 0; + + /* prefix = pattern.prefix */ + lua_getfield(L, -1, "prefix"); + prefix = lua_tostring(L, -1); + + /* + for i = 1, #pattern do + part = pattern[i] + if part == "not" then + assertion = false + else + if testContext(pattern.prefix, part, assertion) then + result = true + break + end + assertion = true + end + end + */ + + n = lua_objlen(L, -2); + for (i = 1; i <= n; ++i) { + lua_rawgeti(L, -2, i); + part = lua_tostring(L, -1); + + if (strcmp(part, "not") == 0) { + assertion = 0; + } + else if (part[0] == '%' && part[1] == '%') { + wildcard = 1; + } + else { + if (testContext(L, prefix, part, assertion, wildcard, filename, fileMatched)) { + lua_pop(L, 1); + result = 1; + break; + } + assertion = 1; + wildcard = 0; + } + + lua_pop(L, 1); + } + + lua_pop(L, 1); + return result; +} + + + +int criteria_matches(lua_State* L) +{ + /* stack [1] = criteria */ + /* stack [2] = context */ + + const char* filename; + int top = lua_gettop(L); + int matched = 1; + int fileMatched = 0; + + /* + Cache string.match for a quicker lookup in match() above + stack[3] = string + stack[4] = string.match + */ + + lua_getglobal(L, "string"); + lua_getfield(L, -1, "match"); + + /* filename = context.files */ + + lua_getfield(L, 2, "files"); + filename = lua_tostring(L, -1); + + /* + for i, pattern in pairs(criteria.patterns) do + if not testPattern(pattern) then + matched = false + break + end + end + */ + + lua_getfield(L, 1, "patterns"); + lua_pushnil(L); + while (lua_next(L, -2)) { + if (!testPattern(L, filename, &fileMatched)) { + matched = 0; + break; + } + lua_pop(L, 1); + } + + /* + if matched and filename and not filematched then + matched = false + end + return matched + */ + + if (matched && filename && !fileMatched) { + matched = 0; + } + + lua_settop(L, top); + lua_pushboolean(L, matched); + return 1; +} diff --git a/src/host/premake.c b/src/host/premake.c index 52e0ad93..f70af2e0 100644 --- a/src/host/premake.c +++ b/src/host/premake.c @@ -35,6 +35,11 @@ extern const char* builtin_scripts[]; /* Built-in functions */ +static const luaL_Reg criteria_functions[] = { + { "matches", criteria_matches }, + { NULL, NULL } +}; + static const luaL_Reg path_functions[] = { { "getabsolute", path_getabsolute }, { "getrelative", path_getrelative }, @@ -79,9 +84,10 @@ static const luaL_Reg string_functions[] = { */ int premake_init(lua_State* L) { - luaL_register(L, "path", path_functions); - luaL_register(L, "os", os_functions); - luaL_register(L, "string", string_functions); + luaL_register(L, "criteria", criteria_functions); + 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); diff --git a/src/host/premake.h b/src/host/premake.h index 9e971de1..004583a0 100644 --- a/src/host/premake.h +++ b/src/host/premake.h @@ -62,6 +62,7 @@ void do_translate(char* value, const char sep); /* Built-in functions */ +int criteria_matches(lua_State* L); int path_getabsolute(lua_State* L); int path_getrelative(lua_State* L); int path_isabsolute(lua_State* L);