Fixes for key-array and the new api.register() path

This commit is contained in:
Jason Perkins 2012-04-10 17:21:37 -04:00
parent 86984a87de
commit 8e326ff6f1
8 changed files with 291 additions and 141 deletions

View File

@ -8,126 +8,6 @@
local api = premake.api
--
-- A place to store the current active objects in each project scope.
--
api.scope = {}
--
-- Register a new API function. See the built-in API definitions below
-- for usage examples.
--
function api.register(field)
-- verify the name
local name = field.name
if not name then
error("missing name", 2)
end
if _G[name] then
error("name in use", 2)
end
-- make sure there is a handler available for this kind of value
local kind = field.kind
if kind:startswith("key-") then
kind = kind:sub(5)
end
if not api["set" .. kind] then
error("invalid kind '" .. kind .. "'", 2)
end
_G[name] = function(value)
return api.callback(field, value)
end
end
--
-- Callback for all API functions; everything comes here first, and then
-- parceled out to the individual set...() functions.
--
function api.callback(field, value)
-- find the right target object for this field
local target
if field.scope == "project" then
target = api.scope.project or api.scope.solution
else
target = api.scope.configuration
end
if not target then
error("no " .. field.scope .. " in scope", 3)
end
-- A keyed value is a table containing key-value pairs, where the
-- type of the value is defined by the field.
if field.kind:startswith("key-") then
target[field.name] = target[field.name] or {}
api.setkeyvalue(target[field.name], field, value)
-- Otherwise, it is a "simple" value defined by the field
else
local setter = api["set" .. field.kind]
setter(target, field.name, field, value)
end
end
--
-- Update a keyed value. Iterate over the keys in the new value, and use
-- the corresponding values to update the target object.
--
function api.setkeyvalue(target, field, values)
if type(values) ~= "table" then
error("value must be a table of key-value pairs", 4)
end
local kind = field.kind:sub(5)
local setter = api["set" .. kind]
for key, value in pairs(values) do
setter(target, key, field, value)
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.
--
function api.setarray(target, name, field, value)
-- put simple values in an array
if type(value) ~= "table" then
value = { value }
end
-- store it, overwriting any existing value
target[field.name] = value
end
--
-- Set a new string value on an API field.
--
function api.setstring(target, name, field, value)
error("setstring is not yet implemented")
end
-----------------------------------------------------------------------------
-- Everything below this point is a candidate for deprecation
-----------------------------------------------------------------------------
--
-- Here I define all of the getter/setter functions as metadata. The actual
-- functions are built programmatically below.
@ -554,13 +434,150 @@
vpaths =
{
kind = "keypath",
kind = "key-pathlist",
scope = "container",
},
}
--
-- A place to store the current active objects in each project scope.
--
api.scope = {}
--
-- Register a new API function. See the built-in API definitions below
-- for usage examples.
--
function api.register(field)
-- verify the name
local name = field.name
if not name then
error("missing name", 2)
end
if _G[name] then
error("name in use", 2)
end
-- make sure there is a handler available for this kind of value
local kind = field.kind
if kind:startswith("key-") then
kind = kind:sub(5)
end
if not api["set" .. kind] then
error("invalid kind '" .. kind .. "'", 2)
end
-- add this new field to my master list
premake.fields[field.name] = field
-- add create a setter function for it
_G[name] = function(value)
return api.callback(field, value)
end
end
--
-- Callback for all API functions; everything comes here first, and then
-- parceled out to the individual set...() functions.
--
function api.callback(field, value)
-- find the right target object for this field
local target
if field.scope == "project" then
target = api.scope.project or api.scope.solution
else
target = api.scope.configuration
end
if not target then
error("no " .. field.scope .. " in scope", 3)
end
-- A keyed value is a table containing key-value pairs, where the
-- type of the value is defined by the field.
if field.kind:startswith("key-") then
target[field.name] = target[field.name] or {}
api.setkeyvalue(target[field.name], field, value)
-- Otherwise, it is a "simple" value defined by the field
else
local setter = api["set" .. field.kind]
setter(target, field.name, field, value)
end
end
--
-- Update a keyed value. Iterate over the keys in the new value, and use
-- the corresponding values to update the target object.
--
function api.setkeyvalue(target, field, values)
if type(values) ~= "table" then
error("value must be a table of key-value pairs", 4)
end
local kind = field.kind:sub(5)
local setter = api["set" .. kind]
for key, value in pairs(values) do
setter(target, key, field, value)
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.
--
function api.setarray(target, name, field, value)
-- put simple values in an array
if type(value) ~= "table" then
value = { value }
end
-- store it, overwriting any existing value
target[name] = value
end
--
-- Set a new string value on an API field.
--
function api.setstring(target, name, field, value)
error("setstring is not yet implemented")
end
--
-- Register the core API functions.
--
api.register {
name = "configmaps",
scope = "project",
kind = "key-array"
}
-----------------------------------------------------------------------------
-- Everything below this point is a candidate for deprecation
-----------------------------------------------------------------------------
--
-- Check to see if a value exists in a list of values, using a
-- case-insensitive match. If the value does exist, the canonical
@ -835,7 +852,7 @@
return premake.setdirarray(container, name, value)
elseif kind == "filelist" then
return premake.setfilearray(container, name, value)
elseif kind == "keyvalue" or kind == "keypath" then
elseif kind == "key-value" or kind == "key-pathlist" then
return premake.setkeyvalue(scope, name, value)
elseif kind == "object" then
return premake.setobject(container, name, value)
@ -871,17 +888,20 @@
--
for name, info in pairs(premake.fields) do
_G[name] = function(value)
return accessor(name, value)
end
-- list value types get a remove() call too
if info.kind == "list" or
info.kind == "dirlist" or
info.kind == "filelist"
then
_G["remove"..name] = function(value)
premake.remove(name, value)
-- skip my new register() fields
if not info.name then
_G[name] = function(value)
return accessor(name, value)
end
-- list value types get a remove() call too
if info.kind == "list" or
info.kind == "dirlist" or
info.kind == "filelist"
then
_G["remove"..name] = function(value)
premake.remove(name, value)
end
end
end
end
@ -931,7 +951,6 @@
cfg[name] = { }
end
end
-- this is the new place for storing scoped objects
api.scope.configuration = cfg

View File

@ -6,7 +6,7 @@
-- down into one object per configuration. These objects are cached with the
-- project, and can be retrieved by calling the getconfig() or eachconfig().
--
-- Copyright (c) 2008-2011 Jason Perkins and the Premake project
-- Copyright (c) 2008-2012 Jason Perkins and the Premake project
--
premake.bake = { }
@ -159,7 +159,7 @@
local function mergefield(kind, dest, src)
local tbl = dest or { }
if kind == "keyvalue" or kind == "keypath" then
if kind == "key-value" or kind == "key-pathlist" then
for key, value in pairs(src) do
tbl[key] = mergefield("list", tbl[key], value)
end

View File

@ -257,18 +257,27 @@
--
function oven.mergefield(cfg, name, value)
-- is this field part of the Premake API? If no, just copy and done
local field = premake.fields[name]
-- if this isn't a Premake field, then just copy
if not field then
cfg[name] = value
return
end
if field.kind == "keyvalue" or field.kind == "keypath" then
if field.kind:startswith("key-") then
oven.mergekeyvalue(cfg, name, field.kind, value)
else
oven.mergevalue(cfg, name, field.kind, value)
end
--[[
if field.kind:startswith("key-") then
cfg[name] = cfg[name] or {}
for key, keyvalue in pairs(value) do
cfg[name][key] = oven.mergetables(cfg[name][key] or {}, keyvalue)
end
elseif field.kind == "object" then
cfg[name] = value
elseif type(value) == "table" then
@ -276,13 +285,61 @@
else
cfg[name] = value
end
--]]
end
--
-- Merge the values of one table into another.
-- Merge a collection of key-values into an baked configuration.
-- @param target
-- The object receiving the values.
-- @param fieldname
-- The name of the field being set.
-- @param fieldkind
-- The data type of the field.
-- @param values
-- The object containing the key-value pairs.
--
function oven.mergekeyvalue(target, fieldname, fieldkind, values)
-- make sure I've got an object to hold the key-value pairs
target[fieldname] = target[fieldname] or {}
-- get the data type of the values (remove the "key-" part)
fieldkind = fieldkind:sub(5)
-- merge in the values
for key, value in pairs(values) do
oven.mergevalue(target[fieldname], key, fieldkind, value)
end
end
--
-- @param original
-- Copy, and optionally merge, a value into a baked configuration.
-- @param target
-- The object receiving the values.
-- @param fieldname
-- The name of the field being set.
-- @param fieldkind
-- The data type of the field.
-- @param values
-- The object containing the key-value pairs.
--
function oven.mergevalue(target, fieldname, fieldkind, value)
-- list values get merged, others overwrite
if fieldkind:endswith("list") then
target[fieldname] = oven.mergetables(target[fieldname] or {}, value)
else
target[fieldname] = value
end
end
--
-- Merge a collection of key-values into an baked configuration.
-- @param target
-- The original value of the table being merged into.
-- @param additions
-- The new values to add to the table.

View File

@ -106,7 +106,7 @@
--
function project.getconfig(prj, buildcfg, platform, field, filename)
-- check for a cached version, build by bakeconfigs()
-- check for a cached version, built by bakeconfigs()
if not filename and prj.configs then
local key = (buildcfg or "*") .. (platform or "")
return prj.configs[key]

View File

@ -99,3 +99,21 @@
EndGlobalSection
]]
end
--
-- Verify that solution-project configuration maps are correctly applied.
--
function suite.configIsMapped_onProjectConfigMapping()
configmaps { ["Debug"] = "Development" }
prepare()
test.capture [[
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{C9135098-6047-8142-B10E-D27E7F73FCB3}.Debug|Win32.ActiveCfg = Development|Win32
{C9135098-6047-8142-B10E-D27E7F73FCB3}.Debug|Win32.Build.0 = Development|Win32
{C9135098-6047-8142-B10E-D27E7F73FCB3}.Release|Win32.ActiveCfg = Release|Win32
{C9135098-6047-8142-B10E-D27E7F73FCB3}.Release|Win32.Build.0 = Release|Win32
EndGlobalSection
]]
end

View File

@ -42,3 +42,13 @@
test.isequal({ "myvalue" }, api.scope.project.testapi)
end
--
-- New values should overwrite old.
--
function suite.overwrites_onNewValue()
testapi "first"
testapi "second"
test.isequal({ "second" }, api.scope.project.testapi)
end

View File

@ -0,0 +1,45 @@
--
-- tests/oven/test_keyvalues.lua
-- Test the handling of key-value data types in the oven.
-- Copyright (c) 2011-2012 Jason Perkins and the Premake project
--
T.oven_keyvalues = { }
local suite = T.oven_keyvalues
local oven = premake5.oven
--
-- Setup and teardown
--
local sln, prj
function suite.setup()
sln = solution("MySolution")
end
--
-- Make sure that key-value types show up in the baked result.
--
function suite.valuePresentInResult()
configmaps { ["key"] = "value" }
local cfg = oven.merge({}, sln)
test.isequal("value", cfg.configmaps["key"][1])
end
--
-- When multiple key-value blocks are present, the resulting keys
-- should be merged into a single result.
--
function suite.keysMerged_onMultipleValues()
configmaps { ["sln"] = "slnvalue" }
prj = project("MyProject")
configmaps { ["prj"] = "prjvalue" }
local cfg = oven.merge(oven.merge({}, sln), prj)
test.istrue(cfg.configmaps.sln ~= nil and cfg.configmaps.prj ~= nil)
end

View File

@ -82,6 +82,7 @@
-- Baking tests
dofile("base/test_baking.lua")
dofile("oven/test_keyvalues.lua")
-- Toolset tests
dofile("tools/test_gcc.lua")