Rework filter() and configuration() to support new container hierarchies; can now use on rules

This commit is contained in:
Jason Perkins 2014-08-27 16:38:58 -04:00
parent 6471232ced
commit 98aba2438f
9 changed files with 143 additions and 73 deletions

View File

@ -95,11 +95,13 @@
api.register {
name = "buildcommands",
scope = "config",
scope = { "config", "rule" },
kind = "list:string",
tokens = true,
}
api.alias("buildcommands", "buildCommands")
api.register {
name = "buildoptions",
scope = "config",
@ -231,7 +233,7 @@
api.register {
name = "filename",
scope = "project;rule",
scope = { "project", "rule" },
kind = "string",
tokens = true,
}
@ -452,7 +454,7 @@
api.register {
name = "location",
scope = "project;rule",
scope = { "project", "rule" },
kind = "path",
tokens = true,
}

View File

@ -9,6 +9,9 @@
_WORKING_DIR = os.getcwd()
local p = premake
--
-- Script-side program entry point.
@ -93,7 +96,7 @@
if action then
print("Building configurations...")
premake.oven.bake()
p.oven.bake(p.api.rootScope())
end
-- Run the interactive prompt, if requested

View File

@ -33,6 +33,16 @@
---
-- Return the currently active configuration container instance.
---
function api.activeScope()
return api.scope.current or api.scope.root
end
---
-- Register a new class of configuration container. A configuration container
-- can hold configuration settings, as well as child configuration containers.
@ -105,7 +115,8 @@
-- << start a new settings block >>
-- Activate the container
api.scope[cc.name] = container
api.scope[cc.name] = container -- TODO: do I still need this?
api.scope.current = container
return container
end
@ -397,22 +408,47 @@
---
-- Find the currently active configuration scope.
-- Return the target container instance for a field.
--
-- @param fld
-- @param field
-- The field being set or fetched.
-- @return
-- The currently active configuration set object.
-- The currently active container instance if one is available, or nil if
-- active container is of the wrong class.
---
function api.target(fld)
if api.scope.rule and p.field.hasScope(fld, "rule") then
return api.scope.rule
elseif fld.scope ~= "rule" then
return api.scope.project or api.scope.solution or api.scope.root
else
function api.target(field)
-- if nothing is in scope, use the global root scope
if not api.scope.current then
return api.scope.root
end
-- use the current scope if it falls within the container hierarchy
-- of one of this field's target scopes; it is okay to set a value of
-- the parent of container (e.g. a project-level value can be set on a
-- solution, since it will be inherited).
local currentClass = api.scope.current.class
for i = 1, #field.scopes do
-- temporary: map config scope to the desired container class; will
-- go away once everything is ported to containers
local targetScope = field.scopes[i]
if targetScope == "config" then
targetScope = "project"
end
local targetClass = p.containerClass.get(targetScope)
repeat
if currentClass == targetClass then
return api.scope.current
end
targetClass = targetClass.parent
until not targetClass
end
-- this field's scope isn't available
return nil
end
@ -436,6 +472,11 @@
end
local target = api.target(field)
if not target then
local err = string.format("unable to set %s in %s scope, should be %s", field.name, api.scope.current.class.name, table.concat(field.scopes, ", "))
error(err, 3)
end
local status, err = configset.store(target, field, value)
if err then
error(err, 3)
@ -456,6 +497,11 @@
if not value then return end
local target = api.target(field)
if not target then
local err = string.format("unable to remove %s from %s scope, should be %s", field.name, api.scope.current.class.name, table.concat(field.scopes, ", "))
error(err, 3)
end
local hasDeprecatedValues = (type(field.deprecated) == "table")
-- Build a list of values to be removed. If this field has deprecated
@ -957,7 +1003,7 @@
---
function configuration(terms)
local target = api.scope.project or api.scope.solution or api.scope.root
local target = api.activeScope()
if terms then
if terms == "*" then terms = nil end
configset.addblock(target, {terms}, os.getcwd())
@ -973,7 +1019,7 @@
---
function filter(terms)
local target = api.scope.project or api.scope.solution or api.scope.root
local target = api.activeScope()
if terms then
if terms == "*" then terms = nil end
local ok, err = configset.addFilter(target, {terms}, os.getcwd())
@ -1007,6 +1053,15 @@
-- The active project object.
--
api.container {
name = "solution",
}
api.container {
name = "project",
parent = "solution",
}
function project(name)
if not name then
if api.scope.project then
@ -1026,12 +1081,15 @@
prj = sln.projects[name]
if not prj then
prj = premake.project.new(sln, name)
prj.class = p.containerClass.get("project")
prj.group = api.scope.group or ""
premake.solution.addproject(sln, prj)
end
end
api.scope.solution = sln
api.scope.project = prj
api.scope.current = prj
configuration {}
@ -1083,11 +1141,13 @@
local sln
if name ~= "*" then
sln = premake.solution.get(name) or premake.solution.new(name)
sln.class = p.containerClass.get("solution")
end
api.scope.solution = sln
api.scope.project = nil
api.scope.group = nil
api.scope.current = sln
configuration {}
@ -1229,6 +1289,11 @@
end
local cset = api.target(field)
if not cset then
local err = string.format("unable to set rule in %s scope, should be project", api.scope.current.class.name)
error(err, 2)
end
local current = cset.current
cset.current = cset.blocks[1]
api.callback(field, value)

View File

@ -98,20 +98,40 @@
return nil, "parent class does not exist"
end
-- Fill in some calculated properties
-- Looks good, set myself up and add to master list
def.children = {}
def.listKey = def.name:plural()
setmetatable(def, p.containerClass)
container._classes[def.name] = def
-- Wire myself to my parent class
def.parent = container._classes[def.parent]
def.listKey = def.name:plural()
if def.parent then
table.insert(def.parent.children, def)
end
-- Finish: add to master list, enable calling with ":" syntax
container._classes[def.name] = def
setmetatable(def, p.containerClass)
return def
end
---
-- Retrieve a container class by name.
--
-- @param name
-- The class name.
-- @return
-- The corresponding container class if found, nil otherwise.
---
function p.containerClass.get(name)
return container._classes[name]
end
---
-- Create a new instance of a container class.
--

View File

@ -81,6 +81,13 @@
f._kind = kind
-- Make sure scope is always an array; don't overwrite old value
if type(f.scope) == "table" then
f.scopes = f.scope
else
f.scopes = { f.scope }
end
-- All fields must have a valid store() function
if not field.accessor(f, "store") then
return nil, "invalid field kind '" .. f._kind .. "'"
@ -251,25 +258,6 @@
---
-- Check to see if a field supports a given project scope.
--
-- @param f
-- The field to test.
-- @param scope
-- The scope to look for (e.g. "config", "project", "rule").
-- @return
-- The scope value if found, nil otherwise.
---
function field.hasScope(f, scope)
if f.scope == scope or f.scope:find(scope, 1, true) then
return scope
end
end
function field.merge(f, current, value)
local processor = field.accessor(f, "merge")
if processor then

View File

@ -172,7 +172,7 @@
function fileconfig.hasFileSettings(fcfg)
for key, field in pairs(premake.fields) do
if p.field.hasScope(field, "config") then
if field.scopes[1] == "config" then
local value = fcfg[field.name]
if value then
if type(value) == "table" then

View File

@ -20,19 +20,29 @@
local context = premake.context
---
-- Traverses the entire container hierarchy built up by the project scripts,
-- filters, merges, and munges the project settings based on the current
-- runtime environment, and returns a new container hierarchy with the end
-- result. This result will then get handed off to the current action to
-- do whatever it is it needs to do, like generate project files.
--
-- Iterates through all of the current solutions, bakes down their contents,
-- and then replaces the original solution object with the baked result.
-- This is the entry point to the whole baking process, which happens after
-- the scripts have run, but before the project files are generated.
--
-- @param root
-- The top level or root container to be baked.
-- @return
-- ???
---
function oven.bake()
function oven.bake(root)
-- I haven't yet ported the project objects to containers
local result = {}
for i, sln in ipairs(solution.list) do
result[i] = oven.bakeSolution(sln)
end
solution.list = result
-- Start container implementation
end

View File

@ -410,8 +410,12 @@ end
function premake.validateScopes(cfg, expected, ctx)
for f in field.each() do
-- Pull out the project level scope
local scope = field.hasScope(f, "config") or field.hasScope(f, "project") or field.hasScope(f, "solution")
-- Pull out the project level scope (this will get cleaned up when
-- the project objects move to the new container API)
local scope
if f.scopes[1] ~= "rule" then
scope = f.scopes[1]
end
-- Skip fields that are at or below the expected scope. Config-
-- level fields are the most general (can be applied to projects

View File

@ -83,38 +83,16 @@
-- Warn if a configuration value is set in the wrong scope.
--
function suite.warns_onSolutionStringField_inProject()
solution "MySolution"
configurations { "Debug", "Release" }
project "MyProject"
kind "ConsoleApp"
language "C++"
startproject "MyProject"
premake.validate()
test.stderr("'startproject' on project")
end
function suite.warns_onSolutionStringField_inConfig()
solution "MySolution"
configurations { "Debug", "Release" }
project "MyProject"
kind "ConsoleApp"
language "C++"
filter "Debug"
startproject "MyProject"
premake.validate()
test.stderr("'startproject' on config")
end
function suite.warns_onSolutionStringField_onlyWarnOnce()
solution "MySolution"
configurations { "Debug", "Release" }
project "MyProject"
kind "ConsoleApp"
language "C++"
startproject "MyProject"
premake.validate()
test.notstderr("'startproject' on config")
test.stderr("'startproject' on config")
end
function suite.warns_onProjectStringField_inConfig()