Merge pull request #554 from Blizzard/add-uncached-subcontext

Add an 'uncached' sub-context to the context.
This commit is contained in:
Tom van Dijck 2016-08-23 17:51:28 -07:00 committed by GitHub
commit 702d5947b3
11 changed files with 348 additions and 25 deletions

View File

@ -53,6 +53,11 @@
end
function m.getRule(name)
p.oven.bake()
return p.global.getRule(name)
end
function m.getProject(wks, i)
wks = m.getWorkspace(wks)

View File

@ -31,6 +31,7 @@
["file.basename"] = { absolute = false, token = "%(Filename)" },
["file.abspath"] = { absolute = true, token = "%(FullPath)" },
["file.relpath"] = { absolute = false, token = "%(Identity)" },
["file.path"] = { absolute = true, token = "%(RelativeDir)" },
}

View File

@ -84,7 +84,6 @@
return {
m.propertyDefaults,
m.commandLineTemplates,
m.outputs,
m.executionDescription,
m.additionalDependencies,
}
@ -144,10 +143,18 @@
function m.commandLineTemplates(r)
if #r.buildcommands then
local cmds = os.translateCommands(r.buildcommands, p.WINDOWS)
-- create shadow context.
local env = p.rule.createEnvironment(r, "[%s]")
local ctx = p.context.extent(r, env)
-- now use the shadow context to detoken.
local buildcommands = ctx.buildcommands
-- write out the result.
if buildcommands and #buildcommands > 0 then
local cmds = os.translateCommands(buildcommands, p.WINDOWS)
cmds = table.concat(cmds, p.eol())
p.x('<CommandLineTemplate>%s</CommandLineTemplate>', cmds)
p.x('<CommandLineTemplate>@echo off\n%s</CommandLineTemplate>', cmds)
end
end
@ -162,16 +169,16 @@
function m.executionDescription(r)
if r.buildmessage then
p.x('<ExecutionDescription>%s</ExecutionDescription>', r.buildmessage)
-- create shadow context.
local env = p.rule.createEnvironment(r, "%%(%s)")
local ctx = p.context.extent(r, env)
-- now use the shadow context to detoken.
local buildmessage = ctx.buildmessage
-- write out the result.
if buildmessage and #buildmessage > 0 then
p.x('<ExecutionDescription>%s</ExecutionDescription>', buildmessage)
end
end
function m.outputs(r)
if #r.buildoutputs then
local outputs = table.concat(r.buildoutputs, ";")
p.x('<Outputs>%s</Outputs>', path.translate(outputs))
end
end

View File

@ -20,6 +20,7 @@
return {
p.vstudio.projectElement,
m.availableItemGroup,
m.computedProperties,
m.computeInputsGroup,
m.usingTask,
m.ruleTarget,
@ -54,6 +55,29 @@
end
---
-- Generate the computed outputs property.
---
function m.computedProperties(r)
-- create shadow context.
local pathVars = p.rule.createPathVars(r, "%%(%s)")
local ctx = p.context.extent(r, { pathVars = pathVars })
-- now use the shadow context to detoken.
local buildoutputs = ctx.buildoutputs
-- write the output.
if buildoutputs and #buildoutputs > 0 then
local outputs = table.concat(buildoutputs, ";")
p.push('<ItemDefinitionGroup>')
p.push('<%s>', r.name)
p.x('<Outputs>%s</Outputs>', path.translate(outputs))
p.pop('</%s>', r.name)
p.pop('</ItemDefinitionGroup>')
end
end
---
-- Generate the computed input targets group.
@ -277,12 +301,19 @@
function m.linkLib(r)
-- create shadow context.
local pathVars = p.rule.createPathVars(r, "%%(%s)")
local ctx = p.context.extent(r, { pathVars = pathVars })
-- now use the shadow context to detoken.
local buildoutputs = ctx.buildoutputs
local linkable, compileable
for i = 1, #r.buildoutputs do
if (path.islinkable(r.buildoutputs[i])) then
for i = 1, #buildoutputs do
if (path.islinkable(buildoutputs[i])) then
linkable = true
end
if (path.iscppfile(r.buildoutputs[i])) then
if (path.iscppfile(buildoutputs[i])) then
compileable = true
end
end
@ -314,7 +345,7 @@
p.w('<Message')
p.w(' Importance="High"')
p.w(' Text="%%(%s.ExecutionDescription)" />', r.name)
end
end
@ -326,13 +357,13 @@
function m.properties(r)
function m.properties(r)
local defs = r.propertydefinition
for i = 1, #defs do
local name = defs[i].name
p.w('%s="%%(%s.%s)"', name, r.name, name)
end
end
end

View File

@ -59,6 +59,31 @@
end
--
-- Create an extended and uncached context based on another context object.
--
-- @param baseContext
-- The base context to extent
-- @param newEnvVars
-- An optional key-value environment table for token expansion; keys and
-- values provided in this table will be available for tokens to use.
-- @return
-- A new context object.
--
function context.extent(baseContext, newEnvVars)
local ctx = {}
ctx._ctx = baseContext
ctx.environ = newEnvVars or baseContext.environ
ctx.terms = {}
ctx._basedir = baseContext._basedir
setmetatable(ctx, context.__mt_uncached)
return ctx
end
---
-- Add a new key-value pair to refine the context filtering.
@ -204,7 +229,7 @@
local value = configset.fetch(ctx._cfgset, field, ctx.terms, ctx, onlylocal and ctx._cfgset)
if value then
-- store the result for later lookups
ctx[key] = value
rawset(ctx, key, value)
end
return value
@ -214,3 +239,14 @@
__index = context.fetchvalue
}
context.__mt_uncached = {
__index = function(ctx, key)
local field = p.field.get(key)
if not field then
return nil
end
local parent = rawget(ctx, '_ctx')
return configset.fetch(parent._cfgset, field, ctx.terms, ctx, nil)
end
}

View File

@ -42,6 +42,9 @@
end
end
-- fetch the pathVars from the enviroment.
local envMap = environ.pathVars or {}
-- enable access to the global environment
setmetatable(environ, {__index = _G})
@ -79,14 +82,14 @@
end
end
-- If this token is in my path variable mapping table, replace the
-- If this token is in my path variable mapping tables, replace the
-- value with the one from the map. This needs to go here because
-- I don't want to make the result relative, but I don't want the
-- absolute path handling below.
if varMap[token] then
local mapped = envMap[token] or varMap[token]
if mapped then
err = nil
result = varMap[token]
result = mapped
if type(result) == "function" then
success, result = pcall(result, e)
if not success then

View File

@ -159,3 +159,51 @@
p.api.storeField(fld, value)
end
end
---
-- prepare an environment with the rule properties as global tokens,
-- according to the format specified.
--
-- @param environ
-- The environment table to fill up
-- @param format
-- The formatting to be used, ie "[%s]".
---
function rule.prepareEnvironment(self, environ, format)
for _, def in ipairs(self.propertydefinition) do
environ[def.name] = string.format(format, def.name)
end
end
function rule.createEnvironment(self, format)
local environ = {}
rule.prepareEnvironment(self, environ, format)
return environ
end
---
-- prepare an table of pathVars with the rule properties as global tokens,
-- according to the format specified.
--
-- @param pathVars
-- The pathVars table to fill up
-- @param format
-- The formatting to be used, ie "%%(%s)".
---
function rule.preparePathVars(self, pathVars, format)
for _, def in ipairs(self.propertydefinition) do
pathVars[def.name] = { absolute = true, token = string.format(format, def.name) }
end
end
function rule.createPathVars(self, format)
local pathVars = {}
rule.preparePathVars(self, pathVars, format)
return pathVars
end

View File

@ -131,6 +131,8 @@ return {
"actions/vstudio/vc2010/test_project_refs.lua",
"actions/vstudio/vc2010/test_prop_sheet.lua",
"actions/vstudio/vc2010/test_resource_compile.lua",
"actions/vstudio/vc2010/test_rule_props.lua",
"actions/vstudio/vc2010/test_rule_targets.lua",
"actions/vstudio/vc2010/test_rule_vars.lua",
"actions/vstudio/vc2010/test_target_machine.lua",
"actions/vstudio/vc2010/test_user_file.lua",

View File

@ -0,0 +1,71 @@
--
-- tests/actions/vstudio/vc2010/vstudio_vs2010_rule_props.lua
-- Validate generation of custom rules
-- Author Tom van Dijck
-- Copyright (c) 2016 Jason Perkins and the Premake project
--
local suite = test.declare("vstudio_vs2010_rule_props")
local vc2010 = premake.vstudio.vc2010
local m = premake.vstudio.vs2010.rules.props
--
-- Setup
--
local wks, prj
function suite.setup()
premake.action.set("vs2010")
rule 'example'
display 'Example compiler'
fileExtension '.example'
propertydefinition {
name = "output_path",
kind = "string",
display = "Output Path",
description = "",
}
buildmessage 'Compiling %{file.basename} with example-compiler...'
buildcommands {
'package-example-compiler.exe %{output_path} "%{file.relpath}"'
}
buildoutputs {
'%{output_path}%{file.basename}.example.cc',
'%{output_path}%{file.basename}.example.h'
}
end
--
-- commandLineTemplates
--
function suite.commandLineTemplates()
local r = test.getRule("example")
m.commandLineTemplates(r)
test.capture [[
<CommandLineTemplate>@echo off
package-example-compiler.exe [output_path] "%(Identity)"</CommandLineTemplate>
]]
end
--
-- executionDescription
--
function suite.executionDescription()
local r = test.getRule("example")
m.executionDescription(r)
test.capture [[
<ExecutionDescription>Compiling %(Filename) with example-compiler...</ExecutionDescription>
]]
end

View File

@ -0,0 +1,97 @@
--
-- tests/actions/vstudio/vc2010/vstudio_vs2010_rule_targets.lua
-- Validate generation of custom rules
-- Author Tom van Dijck
-- Copyright (c) 2016 Jason Perkins and the Premake project
--
local suite = test.declare("vstudio_vs2010_rule_targets")
local vc2010 = premake.vstudio.vc2010
local m = premake.vstudio.vs2010.rules.targets
--
-- Setup
--
local wks, prj
function suite.setup()
premake.action.set("vs2010")
rule 'example'
display 'Example compiler'
fileExtension '.example'
propertydefinition {
name = "output_path",
kind = "string",
display = "Output Path",
description = "",
}
buildmessage 'Compiling %{file.basename} with example-compiler...'
buildcommands {
'package-example-compiler.exe %{output_path} "%{file.relpath}"'
}
buildoutputs {
'%{output_path}%{file.basename}.example.cc',
'%{output_path}%{file.basename}.example.h'
}
end
--
-- availableItemName
--
function suite.availableItemName()
local r = test.getRule("example")
m.availableItemName(r)
test.capture [[
<AvailableItemName Include="example">
<Targets>_example</Targets>
</AvailableItemName>
]]
end
--
-- computedProperties
--
function suite.computedProperties()
local r = test.getRule("example")
m.computedProperties(r)
test.capture [[
<ItemDefinitionGroup>
<example>
<Outputs>%(output_path)%(Filename).example.cc;%(output_path)%(Filename).example.h</Outputs>
</example>
</ItemDefinitionGroup>
]]
end
--
-- usingTask
--
function suite.usingTask()
local r = test.getRule("example")
m.usingTask(r)
test.capture [[
<UsingTask
TaskName="example"
TaskFactory="XamlTaskFactory"
AssemblyName="Microsoft.Build.Tasks.v4.0">
<Task>$(MSBuildThisFileDirectory)$(MSBuildThisFileName).xml</Task>
</UsingTask>
]]
end

View File

@ -51,3 +51,25 @@
configset.store(cset, field.get("targetname"), "MyProject%{1 + 1}")
test.isequal("MyProject2", ctx.targetname)
end
--
-- Token environment in extended context overrides context.
--
function suite.extent()
-- set in toplevel context.
configset.store(cset, field.get("targetname"), "%{value}")
-- detoken in toplevel context should result in empty string.
test.isequal("", ctx.targetname)
-- create an extended context with a local environ.
local environ = {
value = "text"
}
local ext = context.extent(ctx, environ)
-- detoken in extended context should result in value set in that environ.
test.isequal("text", ext.targetname)
end