Ported key-value handling to new configuration system
This commit is contained in:
parent
5e48d05e3d
commit
2cf609c6ac
@ -46,8 +46,7 @@
|
||||
end
|
||||
|
||||
-- make sure there is a handler available for this kind of value
|
||||
local kind = api.getbasekind(field)
|
||||
if not api["set" .. kind] then
|
||||
if not api.getsetter(field) then
|
||||
error("invalid kind '" .. kind .. "'", 2)
|
||||
end
|
||||
|
||||
@ -68,8 +67,10 @@
|
||||
|
||||
-- if the field needs special handling, tell the config
|
||||
-- set system about it
|
||||
local merge = field.kind:endswith("-list")
|
||||
configset.registerfield(field.name, { merge = merge })
|
||||
configset.registerfield(field.name, {
|
||||
keyed = api.iskeyedfield(field),
|
||||
merge = api.islistfield(field),
|
||||
})
|
||||
end
|
||||
|
||||
|
||||
@ -102,19 +103,10 @@
|
||||
end
|
||||
|
||||
local status, result = pcall(function ()
|
||||
-- A keyed value is a table containing key-value pairs, where the
|
||||
-- type of the value is defined by the field.
|
||||
if api.iskeyedfield(field) then
|
||||
target[field.name] = target[field.name] or {}
|
||||
api.setkeyvalue(target[field.name], field, value)
|
||||
|
||||
-- Lists is an array containing values of another type
|
||||
elseif api.islistfield(field) then
|
||||
api.setlist(target, field.name, field, value)
|
||||
|
||||
-- Otherwise, it is a "simple" value defined by the field
|
||||
api.setkeyvalue(target, field, value)
|
||||
else
|
||||
local setter = api["set" .. field.kind]
|
||||
local setter = api.getsetter(field, true)
|
||||
setter(target, field.name, field, value)
|
||||
end
|
||||
end)
|
||||
@ -227,6 +219,27 @@
|
||||
end
|
||||
|
||||
|
||||
--
|
||||
-- Retrieve the "set" function for a field.
|
||||
--
|
||||
-- @param field
|
||||
-- The field to query.
|
||||
-- @param lists
|
||||
-- If true, will return the list setter for list fields (i.e. string-list);
|
||||
-- else returns base type setter (i.e. string).
|
||||
-- @return
|
||||
-- The setter for the field.
|
||||
--
|
||||
|
||||
function api.getsetter(field, lists)
|
||||
if lists and api.islistfield(field) then
|
||||
return api.setlist
|
||||
else
|
||||
return api["set" .. api.getbasekind(field)]
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--
|
||||
-- Set a new array value. Arrays are lists of values stored by "value",
|
||||
-- in that new values overwrite old ones, rather than merging like lists.
|
||||
@ -294,19 +307,14 @@
|
||||
error({ msg="value must be a table of key-value pairs" })
|
||||
end
|
||||
|
||||
-- remove the "key-" prefix from the field kind
|
||||
local kind = api.getbasekind(field)
|
||||
local newval = {}
|
||||
|
||||
if api.islistfield(field) then
|
||||
local setter = api.getsetter(field, true)
|
||||
for key, value in pairs(values) do
|
||||
api.setlist(target, key, field, value)
|
||||
end
|
||||
else
|
||||
local setter = api["set" .. kind]
|
||||
for key, value in pairs(values) do
|
||||
setter(target, key, field, value)
|
||||
end
|
||||
setter(newval, key, field, value)
|
||||
end
|
||||
|
||||
configset.addvalue(target.configset, field.name, newval)
|
||||
end
|
||||
|
||||
|
||||
@ -316,9 +324,7 @@
|
||||
--
|
||||
|
||||
function api.setlist(target, name, field, value)
|
||||
-- find the contained data type
|
||||
local kind = api.getbasekind(field)
|
||||
local setter = api["set" .. kind]
|
||||
local setter = api.getsetter(field)
|
||||
|
||||
-- am I setting a configurable object, or some kind of subfield?
|
||||
local result
|
||||
@ -496,15 +502,6 @@
|
||||
tokens = true,
|
||||
}
|
||||
|
||||
--[[
|
||||
api.register {
|
||||
name = "excludes",
|
||||
scope = "config",
|
||||
kind = "file-list",
|
||||
tokens = true,
|
||||
}
|
||||
--]]
|
||||
|
||||
-- For backward compatibility, excludes() is now an alias for removefiles()
|
||||
function excludes(value)
|
||||
removefiles(value)
|
||||
@ -971,15 +968,6 @@
|
||||
table.insert(cfg.keywords, path.wildcards(word):lower())
|
||||
end
|
||||
|
||||
--[[
|
||||
-- initialize list-type fields to empty tables
|
||||
for name, field in pairs(premake.fields) do
|
||||
if field.kind:endswith("-list") then
|
||||
cfg[name] = { }
|
||||
end
|
||||
end
|
||||
--]]
|
||||
|
||||
-- this is the new place for storing scoped objects
|
||||
api.scope.configuration = cfg
|
||||
|
||||
|
@ -114,7 +114,7 @@
|
||||
function configset.addvalue(cset, fieldname, value)
|
||||
local current = cset._current
|
||||
local field = configset._fields[fieldname]
|
||||
if field and field.merge then
|
||||
if field and (field.keyed or field.merge) then
|
||||
current[fieldname] = current[fieldname] or {}
|
||||
table.insert(current[fieldname], value)
|
||||
else
|
||||
@ -167,12 +167,39 @@
|
||||
end
|
||||
|
||||
|
||||
--
|
||||
-- Merges two lists of values together. The merged list is both indexed
|
||||
-- and keyed for faster lookups. If duplicate values are encountered,
|
||||
-- the earlier value is removed.
|
||||
--
|
||||
|
||||
local function merge(a, b)
|
||||
-- if b is itself a list, flatten it out
|
||||
if type(b) == "table" then
|
||||
for _, v in ipairs(b) do
|
||||
merge(a, v)
|
||||
end
|
||||
|
||||
-- if b is a simple value, insert it
|
||||
else
|
||||
-- find and remove earlier values
|
||||
if a[b] then
|
||||
table.remove(a, table.indexof(a, b))
|
||||
end
|
||||
|
||||
table.insert(a, b)
|
||||
a[b] = b
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--
|
||||
-- Retrieve a directly assigned value from the configuration set. No merging
|
||||
-- takes place; the last value set is the one returned.
|
||||
--
|
||||
|
||||
local function fetchassign(cset, fieldname, context, filename)
|
||||
-- walk the loop backwards and return on first value encountered
|
||||
local n = #cset._blocks
|
||||
for i = n, 1, -1 do
|
||||
local block = cset._blocks[i]
|
||||
@ -187,6 +214,45 @@
|
||||
end
|
||||
|
||||
|
||||
--
|
||||
-- Retrieve a keyed from the configuration set; keys are assembled into
|
||||
-- a single result; values may optionally be merged too.
|
||||
--
|
||||
|
||||
local function fetchkeyed(cset, fieldname, context, filename, mergevalues)
|
||||
local result = {}
|
||||
|
||||
-- grab values from the parent set first
|
||||
if cset._parent ~= cset then
|
||||
result = fetchkeyed(cset._parent, fieldname, context, filename, merge)
|
||||
end
|
||||
|
||||
function process(values)
|
||||
for k, v in pairs(values) do
|
||||
if type(k) == "number" then
|
||||
process(v)
|
||||
elseif mergevalues then
|
||||
result[k] = result[k] or {}
|
||||
merge(result[k], v)
|
||||
else
|
||||
result[k] = v
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for _, block in ipairs(cset._blocks) do
|
||||
if testblock(block, context, filename) then
|
||||
local value = block[fieldname]
|
||||
if value then
|
||||
process(value)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
|
||||
--
|
||||
-- Retrieve a merged value from the configuration set; all values are
|
||||
-- assembled together into a single result.
|
||||
@ -200,25 +266,6 @@
|
||||
result = fetchmerge(cset._parent, fieldname, context, filename)
|
||||
end
|
||||
|
||||
function add(value)
|
||||
-- recurse into tables to flatten out the list as I go
|
||||
if type(value) == "table" then
|
||||
for _, v in ipairs(value) do
|
||||
add(v)
|
||||
end
|
||||
else
|
||||
-- if the value is already in the result, remove it; enables later
|
||||
-- blocks to adjust the order of earlier values
|
||||
if result[value] then
|
||||
table.remove(result, table.indexof(result, value))
|
||||
end
|
||||
|
||||
-- add the value with both an index and a key (for fast existence checks)
|
||||
table.insert(result, value)
|
||||
result[value] = value
|
||||
end
|
||||
end
|
||||
|
||||
function remove(patterns)
|
||||
for _, pattern in ipairs(patterns) do
|
||||
local i = 1
|
||||
@ -242,7 +289,7 @@
|
||||
|
||||
local value = block[fieldname]
|
||||
if value then
|
||||
add(value)
|
||||
merge(result, value)
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -276,9 +323,12 @@
|
||||
|
||||
-- should this field be merged or assigned?
|
||||
local field = configset._fields[fieldname]
|
||||
local keyed = field and field.keyed
|
||||
local merge = field and field.merge
|
||||
|
||||
if merge then
|
||||
if keyed then
|
||||
value = fetchkeyed(cset, fieldname, context, filename, merge)
|
||||
elseif merge then
|
||||
value = fetchmerge(cset, fieldname, context, filename)
|
||||
else
|
||||
value = fetchassign(cset, fieldname, context, filename)
|
||||
|
@ -662,6 +662,8 @@
|
||||
if stars == 0 then
|
||||
leaf = path.getname(leaf)
|
||||
end
|
||||
else
|
||||
leaf = path.getname(leaf)
|
||||
end
|
||||
|
||||
vpath = path.join(stem, leaf)
|
||||
|
@ -124,19 +124,6 @@
|
||||
end
|
||||
|
||||
|
||||
--
|
||||
-- On key-value APIs, the keyed object value should be the target.
|
||||
--
|
||||
|
||||
function suite.keyObjectTarget_onKeyValue()
|
||||
api.register { name = "testapi", kind = "key-test", scope = "project" }
|
||||
local sln = solution "MySolution"
|
||||
testapi { key = "test" }
|
||||
test.istrue(sln.testapi == test_args.target)
|
||||
end
|
||||
|
||||
|
||||
|
||||
--
|
||||
-- On key-value APIs, the field name should be the key value from the supplied table.
|
||||
--
|
||||
|
@ -128,6 +128,7 @@
|
||||
test.isequal({}, configset.fetchvalue(cset, "buildoptions", {}))
|
||||
end
|
||||
|
||||
|
||||
--
|
||||
-- List fields should merge values fetched from different blocks.
|
||||
--
|
||||
@ -188,3 +189,68 @@
|
||||
configset.removevalues(cset, "defines", { "WIN*" })
|
||||
test.isequal({ "LINUX", "MACOSX" }, configset.fetchvalue(cset, "defines", {}))
|
||||
end
|
||||
|
||||
|
||||
--
|
||||
-- Keyed values should merge keys fetched from different blocks.
|
||||
--
|
||||
|
||||
function suite.keyed_mergesKeys_onFetch()
|
||||
configset.addvalue(cset, "configmap", { Debug="Debug", Release="Release" })
|
||||
configset.addblock(cset, { "windows" })
|
||||
configset.addvalue(cset, "configmap", { Profile="Profile" })
|
||||
local x = configset.fetchvalue(cset, "configmap", {"windows"})
|
||||
test.istrue(x.Debug and x.Release and x.Profile)
|
||||
end
|
||||
|
||||
|
||||
--
|
||||
-- Multiple adds to a keyed value field in the same block should be merged.
|
||||
--
|
||||
|
||||
function suite.keyed_mergesKeys_onAdd()
|
||||
configset.addvalue(cset, "configmap", { Debug="Debug", Release="Release" })
|
||||
configset.addvalue(cset, "configmap", { Profile="Profile" })
|
||||
local x = configset.fetchvalue(cset, "configmap", {"windows"})
|
||||
test.istrue(x.Debug and x.Release and x.Profile)
|
||||
end
|
||||
|
||||
|
||||
--
|
||||
-- Keyed values should overwrite when non-merged fields are fetched.
|
||||
--
|
||||
|
||||
function suite.keyed_overwritesValues_onNonMergeFetch()
|
||||
configset.addvalue(cset, "configmap", { Debug="Debug" })
|
||||
configset.addblock(cset, { "windows" })
|
||||
configset.addvalue(cset, "configmap", { Debug="Development" })
|
||||
local x = configset.fetchvalue(cset, "configmap", {"windows"})
|
||||
test.isequal("Development", x.Debug)
|
||||
end
|
||||
|
||||
function suite.keyed_overwritesValues_onNonMergeAdd()
|
||||
configset.addvalue(cset, "configmap", { Debug="Debug" })
|
||||
configset.addvalue(cset, "configmap", { Debug="Development" })
|
||||
local x = configset.fetchvalue(cset, "configmap", {"windows"})
|
||||
test.isequal("Development", x.Debug)
|
||||
end
|
||||
|
||||
|
||||
--
|
||||
-- Keyed values should merge when merged fields are fetched.
|
||||
--
|
||||
|
||||
function suite.keyed_mergesValues_onMergeFetch()
|
||||
configset.addvalue(cset, "vpaths", { includes="*.h" })
|
||||
configset.addblock(cset, { "windows" })
|
||||
configset.addvalue(cset, "vpaths", { includes="*.hpp" })
|
||||
local x = configset.fetchvalue(cset, "vpaths", {"windows"})
|
||||
test.isequal({ "*.h", "*.hpp" }, x.includes)
|
||||
end
|
||||
|
||||
function suite.keyed_mergesValues_onMergeAdd()
|
||||
configset.addvalue(cset, "vpaths", { includes="*.h" })
|
||||
configset.addvalue(cset, "vpaths", { includes="*.hpp" })
|
||||
local x = configset.fetchvalue(cset, "vpaths", {"windows"})
|
||||
test.isequal({ "*.h", "*.hpp" }, x.includes)
|
||||
end
|
||||
|
@ -44,10 +44,17 @@
|
||||
test.isequal(cfg.files[1], project.getvpath(prj, cfg.files[1]))
|
||||
end
|
||||
|
||||
function suite.CanTrimLeadingPaths()
|
||||
function suite.CanStripPaths()
|
||||
files { "src/myproject/hello.c" }
|
||||
vpaths { [""] = "src" }
|
||||
prepare()
|
||||
test.isequal("hello.c", project.getvpath(prj, cfg.files[1]))
|
||||
end
|
||||
|
||||
function suite.CanTrimLeadingPaths()
|
||||
files { "src/myproject/hello.c" }
|
||||
vpaths { ["*"] = "src" }
|
||||
prepare()
|
||||
test.isequal("myproject/hello.c", project.getvpath(prj, cfg.files[1]))
|
||||
end
|
||||
|
||||
@ -84,6 +91,13 @@
|
||||
test.isequal("Headers/hello.h", project.getvpath(prj, cfg.files[1]))
|
||||
end
|
||||
|
||||
function suite.MatchFilePattern_ToNone_Flat()
|
||||
files { "src/myproject/hello.h" }
|
||||
vpaths { [""] = "**.h" }
|
||||
prepare()
|
||||
test.isequal("hello.h", project.getvpath(prj, cfg.files[1]))
|
||||
end
|
||||
|
||||
function suite.MatchFilePattern_ToNestedGroup_Flat()
|
||||
files { "src/myproject/hello.h" }
|
||||
vpaths { ["Source/Headers"] = "**.h" }
|
||||
@ -107,7 +121,7 @@
|
||||
|
||||
function suite.MatchFilePattern_ToGroup_Nested()
|
||||
files { "src/myproject/hello.h" }
|
||||
vpaths { ["Headers/**"] = "**.h" }
|
||||
vpaths { ["Headers/*"] = "**.h" }
|
||||
prepare()
|
||||
test.isequal("Headers/src/myproject/hello.h", project.getvpath(prj, cfg.files[1]))
|
||||
end
|
||||
@ -121,7 +135,7 @@
|
||||
|
||||
function suite.MatchFilePatternWithPath_ToGroup_Nested()
|
||||
files { "src/myproject/hello.h" }
|
||||
vpaths { ["Headers/**"] = "src/**.h" }
|
||||
vpaths { ["Headers/*"] = "src/**.h" }
|
||||
prepare()
|
||||
test.isequal("Headers/myproject/hello.h", project.getvpath(prj, cfg.files[1]))
|
||||
end
|
||||
|
Loading…
Reference in New Issue
Block a user