Merge new custom rule file support

This commit is contained in:
Jason Perkins 2014-10-29 17:04:07 -04:00
commit 0a4c3e6853
26 changed files with 622 additions and 382 deletions

View File

@ -41,7 +41,7 @@
"base/project.lua",
"base/config.lua",
"base/fileconfig.lua",
"base/rules.lua",
"base/rule.lua",
-- project script processing
"base/oven.lua",

View File

@ -162,13 +162,6 @@
tokens = true,
}
api.register {
name = "customRules",
scope = "project",
kind = "list:file",
tokens = true,
}
api.register {
name = "debugargs",
scope = "config",
@ -624,6 +617,12 @@
tokens = true,
}
api.register {
name = "rules",
scope = "project",
kind = "list:string",
}
api.register {
name = "startproject",
scope = "solution",

View File

@ -241,7 +241,7 @@
m.elements.itemDefinitionGroup = function(cfg)
if cfg.kind == p.UTILITY then
return {
m.customRuleVars,
m.ruleVars,
m.buildEvents,
}
else
@ -253,7 +253,7 @@
m.buildEvents,
m.imageXex,
m.deploy,
m.customRuleVars,
m.ruleVars,
}
end
end
@ -491,23 +491,19 @@
-- Write out project-level custom rule variables.
---
function m.customRuleVars(cfg)
local vars = p.api.getCustomVars()
table.foreachi(cfg.project._customRules, function(rule)
function m.ruleVars(cfg)
for i = 1, #cfg.rules do
local rule = p.global.getRule(cfg.rules[i])
local contents = p.capture(function ()
p.push()
for _, var in ipairs(vars) do
if cfg[var] then
local key = p.api.getCustomVarKey(var)
local value = cfg[var]
if type(value) == "table" then
local fmt = p.api.getCustomListFormat(var)
value = table.concat(value, fmt[1])
end
if value and #value > 0 then
m.element(key, nil, '%s', value)
for prop in p.rule.eachProperty(rule) do
local fld = p.rule.getPropertyField(rule, prop)
local value = cfg[fld.name]
if value ~= nil then
value = p.rule.getPropertyString(rule, prop, value)
if value ~= nil and #value > 0 then
m.element(prop.name, nil, '%s', value)
end
end
end
@ -515,11 +511,11 @@
end)
if #contents > 0 then
p.push('<%s>', rule)
p.push('<%s>', rule.name)
p.outln(contents)
p.pop('</%s>', rule)
p.pop('</%s>', rule.name)
end
end)
end
end
@ -559,12 +555,12 @@
---
m.elements.fileGroups = {
"ClInclude",
"ClCompile",
"None",
"ResourceCompile",
"CustomBuild",
"CustomRule"
"clInclude",
"clCompile",
"none",
"resourceCompile",
"customBuild",
"customRule"
}
m.elements.files = function(prj, groups)
@ -583,7 +579,7 @@
end
function m.ClCompileFiles(prj, group)
function m.clCompileFiles(prj, group)
local files = group.ClCompile or {}
if #files > 0 then
p.push('<ItemGroup>')
@ -622,7 +618,7 @@
end
function m.ClIncludeFiles(prj, groups)
function m.clIncludeFiles(prj, groups)
local files = groups.ClInclude or {}
if #files > 0 then
p.push('<ItemGroup>')
@ -634,7 +630,7 @@
end
function m.CustomBuildFiles(prj, groups)
function m.customBuildFiles(prj, groups)
local files = groups.CustomBuild or {}
if #files > 0 then
p.push('<ItemGroup>')
@ -672,48 +668,42 @@
end
function m.CustomRuleFiles(prj, groups)
local vars = p.api.getCustomVars()
-- Look for rules that aren't in the built-in rule list
for rule, files in pairs(groups) do
if not table.contains(m.elements.fileGroups, rule) and #files > 0 then
function m.customRuleFiles(prj, groups)
for i = 1, #prj.rules do
local rule = p.global.getRule(prj.rules[i])
local files = groups[rule.name]
if files and #files > 0 then
p.push('<ItemGroup>')
for _, file in ipairs(files) do
-- Capture any rule variables that have been set
for _, file in ipairs(files) do
local contents = p.capture(function()
p.push()
for _, var in ipairs(vars) do
for prop in p.rule.eachProperty(rule) do
local fld = p.rule.getPropertyField(rule, prop)
for cfg in project.eachconfig(prj) do
local condition = m.condition(cfg)
local fcfg = fileconfig.getconfig(file, cfg)
if fcfg and fcfg[var] then
local key = p.api.getCustomVarKey(var)
local value = fcfg[var]
if type(value) == "table" then
local fmt = p.api.getCustomListFormat(var)
value = table.concat(value, fmt[1])
end
if fcfg and fcfg[fld.name] then
local value = p.rule.getPropertyString(rule, prop, fcfg[fld.name])
if value and #value > 0 then
m.element(key, condition, '%s', value)
m.element(prop.name, m.condition(cfg), '%s', value)
end
end
end
end
p.pop()
end)
if #contents > 0 then
p.push('<%s Include=\"%s\">', rule, path.translate(file.relpath))
p.push('<%s Include=\"%s\">', rule.name, path.translate(file.relpath))
p.outln(contents)
p.pop('</%s>', rule)
p.pop('</%s>', rule.name)
else
p.x('<%s Include=\"%s\" />', rule, path.translate(file.relpath))
p.x('<%s Include=\"%s\" />', rule.name, path.translate(file.relpath))
end
end
p.pop('</ItemGroup>')
end
end
@ -721,7 +711,7 @@
function m.NoneFiles(prj, groups)
function m.noneFiles(prj, groups)
local files = groups.None or {}
if #files > 0 then
p.push('<ItemGroup>')
@ -733,7 +723,7 @@
end
function m.ResourceCompileFiles(prj, groups)
function m.resourceCompileFiles(prj, groups)
local files = groups.ResourceCompile or {}
if #files > 0 then
p.push('<ItemGroup>')
@ -764,17 +754,21 @@
function m.categorize(prj, file)
-- If any configuration for this file uses a custom build step or a
-- custom, externally defined rule, that's the category to use
-- If any configuration for this file uses a custom build step,
-- that's the category to use
for cfg in project.eachconfig(prj) do
local fcfg = fileconfig.getconfig(file, cfg)
if fileconfig.hasCustomBuildRule(fcfg) then
return "CustomBuild"
elseif fcfg and fcfg._customRule then
return fcfg._customRule
end
end
-- If there is a custom rule associated with it, use that
local rule = p.global.getRuleForFile(file.name, prj.rules)
if rule then
return rule.name
end
-- Otherwise use the file extension to deduce a category
if path.iscppfile(file.name) then
return "ClCompile"
@ -1155,11 +1149,13 @@
function m.importExtensionTargets(prj)
p.w('<Import Project="$(VCTargetsPath)\\Microsoft.Cpp.targets" />')
p.push('<ImportGroup Label="ExtensionTargets">')
table.foreachi(prj.customRules, function(value)
value = path.translate(project.getrelative(prj, value))
value = path.appendExtension(value, ".targets")
p.x('<Import Project="%s" />', value)
end)
for i = 1, #prj.rules do
local rule = p.global.getRule(prj.rules[i])
local loc = project.getrelative(prj, premake.filename(rule, ".targets"))
p.x('<Import Project="%s" />', path.translate(loc))
end
p.pop('</ImportGroup>')
end
@ -1174,11 +1170,13 @@
function m.importExtensionSettings(prj)
p.w('<Import Project="$(VCTargetsPath)\\Microsoft.Cpp.props" />')
p.push('<ImportGroup Label="ExtensionSettings">')
table.foreachi(prj.customRules, function(value)
value = path.translate(project.getrelative(prj, value))
value = path.appendExtension(value, ".props")
p.x('<Import Project="%s" />', value)
end)
for i = 1, #prj.rules do
local rule = p.global.getRule(prj.rules[i])
local loc = project.getrelative(prj, premake.filename(rule, ".props"))
p.x('<Import Project="%s" />', path.translate(loc))
end
p.pop('</ImportGroup>')
end

View File

@ -49,6 +49,12 @@
return api._setContainer(class, name)
end
_G["external" .. containerName:capitalized()] = function(name)
local c = _G[containerName](name)
c.external = true
return c
end
return class
end
@ -238,7 +244,7 @@
-- create a setter function for it
_G[name] = function(value)
return api.callback(field, value)
return api.storeField(field, value)
end
if premake.field.removes(field) then
@ -460,8 +466,8 @@
-- gets parceled out to the individual set...() functions.
--
function api.callback(field, value)
if not value then
function api.storeField(field, value)
if value == nil then
return
end
@ -486,6 +492,7 @@
end
--
-- The remover: adds values to be removed to the "removes" field on
-- current configuration. Removes are keyed by the associated field,
@ -496,7 +503,7 @@
function api.remove(field, value)
-- right now, ignore calls with no value; later might want to
-- return the current baked value
if not value then return end
if value == nil then return end
local target = api.target(field)
if not target then
@ -639,18 +646,9 @@
---
function api.reset()
-- Clear out all top level objects
-- Clear out all top level objects, but keep the root config
api.scope.global.rules = {}
api.scope.global.solutions = {}
-- Remove all custom variables
local vars = api.getCustomVars()
for i, var in ipairs(vars) do
local f = premake.field.get(var)
api.unregister(f)
end
-- Remove all custom list variable formats
api._customVarFormats = {}
end
@ -687,6 +685,47 @@
---
-- Boolean field kind; converts common yes/no strings into true/false values.
---
premake.field.kind("boolean", {
store = function(field, current, value, processor)
local mapping = {
["false"] = false,
["no"] = false,
["off"] = false,
["on"] = true,
["true"] = true,
["yes"] = true,
}
if type(value) == "string" then
value = mapping[value:lower()]
if value == nil then
error { msg="expected boolean; got " .. value }
end
return value
end
if type(value) == "boolean" then
return value
end
if type(value) == "number" then
return (value ~= 0)
end
return (value ~= nil)
end,
compare = function(field, a, b, processor)
return (a == b)
end
})
--
-- Directory data kind; performs wildcard directory searches, converts
-- results to absolute paths.
@ -1044,26 +1083,6 @@
--
-- Activates a reference to an external, non-Premake generated project.
--
-- @param name
-- The name of the project. If a project with this name already
-- exists, it is made current, otherwise a new project is created
-- with this name. If no name is provided, the most recently defined
-- project is made active.
-- @return
-- The active project object.
--
function external(name)
local prj = project(name)
prj.external = true;
return prj
end
--
-- Define a new action.
--
@ -1086,125 +1105,3 @@
function newoption(opt)
premake.option.add(opt)
end
-----------------------------------------------------------------------------
--
-- The custom*() functions act as wrappers that define new ad-hoc fields
-- on the fly, to support arbitrary custom rule variables. These should be
-- considered experimental and temporary for now as a more complete rule-
-- generating solution will certainly be needed, but I will do my best to
-- deprecate them cleanly when that time comes.
--
-----------------------------------------------------------------------------
api._customVarFormats = {}
function api.getCustomVars()
local vars = {}
for f in premake.field.each() do
if f.name:startswith("_custom_") then
table.insert(vars, f.name)
end
end
return vars
end
function api.getCustomVarKey(var)
return var:sub(9)
end
function api.getCustomListFormat(var)
local key = api.getCustomVarKey(var)
return api._customVarFormats[key] or { " " }
end
function api.setCustomVar(name, kind, value)
local fieldName = "_custom_" .. name
local field = premake.field.get(fieldName)
if not field then
field = premake.field.new {
name = fieldName,
scope = "config",
kind = kind,
tokens = true,
}
end
api.callback(field, value)
end
function customVar(value)
if type(value) ~= "table" or #value ~= 2 then
error("invalid value for customVar()")
end
api.setCustomVar(value[1], "string", value[2])
end
function customList(value)
if type(value) ~= "table" or #value < 2 then
error("invalid value for customList()")
end
local name = value[1]
table.remove(value, 1)
api.setCustomVar(name, "list:string", value)
end
function customListFormat(value)
if type(value) ~= "table" or #value < 2 then
error("invalid value for customListFormat()")
end
local name = value[1]
table.remove(value, 1)
api._customVarFormats[name] = value
end
function customRule(value)
-- Store the rule name in the current configuration, like a
-- normal set-field operation would
local fieldName = "_customRule"
local field = premake.field.get(fieldName)
if not field then
field = premake.field.new {
name = fieldName,
scope = "config",
kind = "string",
}
end
api.callback(field, value)
-- Wild hack: I need a way to get all of the custom rule names that are
-- in use within a project. The rule names are currently only associated
-- with individual files. Rather than iterating over all the files after
-- the fact, keep a master list of rule names in the first configuration
-- block of the project. This way it will come out of the baking system
-- looking like a normal list:string field.
fieldName = "_customRules"
field = premake.field.get(fieldName)
if not field then
field = premake.field.new {
name = fieldName,
scope = "config",
kind = "list:string"
}
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)
cset.current = current
end

View File

@ -89,12 +89,12 @@
-- If the filter contains a file path, make it relative to
-- this block's basedir
if value and abspath and not cset.compiled and block._basedir ~= basedir then
if value ~= nil and abspath and not cset.compiled and block._basedir ~= basedir then
basedir = block._basedir
filter.files = path.getrelative(basedir, abspath)
end
if value and (cset.compiled or criteria.matches(block._criteria, filter)) then
if value ~= nil and (cset.compiled or criteria.matches(block._criteria, filter)) then
-- If value is an object, return a copy of it so that any
-- changes later made to it by the caller won't alter the
-- original value (that was a tough bug to find)

View File

@ -70,8 +70,10 @@
self.class = class
self.name = name
self.filename = name
self.script = _SCRIPT
self.basedir = os.getcwd()
self.external = false
for childClass in container.eachChildClass(class) do
self[childClass.pluralName] = {}

View File

@ -79,6 +79,10 @@
end
function expandvalue(value)
if type(value) ~= "string" then
return
end
local count
repeat
value, count = value:gsub("%%{(.-)}", function(token)

View File

@ -290,7 +290,7 @@
local kinds = string.explode(f._kind, ":", true)
for i, kind in ipairs(kinds) do
local value = field._kinds[kind][tag]
if value then
if value ~= nil then
return value
end
end

View File

@ -47,6 +47,48 @@
---
-- Retrieve a rule by name or index.
--
-- @param key
-- The rule key, either a string name or integer index.
-- @returns
-- The rule with the provided key.
---
function global.getRule(key)
local root = p.api.rootContainer()
return root.rules[key]
end
---
-- Retrieve the rule to applies to the provided file name, if any such
-- rule exists.
--
-- @param fname
-- The name of the file.
-- @param rules
-- A list of rule names to be included in the search. If not specified,
-- all rules will be checked.
-- @returns
-- The rule, is one has been registered, or nil.
---
function global.getRuleForFile(fname, rules)
local ext = path.getextension(fname):lower()
for rule in global.eachRule() do
if not rules or table.contains(rules, rule.name) then
if rule.fileExtension == ext then
return rule
end
end
end
end
---
-- Retrieve a solution by name or index.
--
@ -58,7 +100,5 @@
function global.getSolution(key)
local root = p.api.rootContainer()
if root.solutions then
return root.solutions[key]
end
return root.solutions[key]
end

View File

@ -94,7 +94,6 @@
-- to project configurations.
self.configs = oven.bakeConfigs(self)
end
@ -194,6 +193,14 @@
function p.rule.bake(r)
table.sort(r.propertyDefinition, function (a, b)
return a.name < b.name
end)
end
--
-- Assigns a unique objects directory to every configuration of every project
-- in the solution, taking any objdir settings into account, to ensure builds

View File

@ -4,10 +4,12 @@
-- Copyright (c) 2002-2014 Jason Perkins and the Premake project
--
local solution = premake.solution
local project = premake.project
local config = premake.config
local field = premake.field
local p = premake
local solution = p.solution
local project = p.project
local config = p.config
local field = p.field
@ -180,7 +182,7 @@
---
function premake.filename(obj, ext)
local fname = obj.location
local fname = obj.location or obj.basedir
if ext and not ext:startswith(".") then
fname = path.join(fname, ext)
else
@ -189,7 +191,7 @@ function premake.filename(obj, ext)
fname = fname .. ext
end
end
return fname
return path.getabsolute(fname)
end
@ -367,6 +369,14 @@ end
premake.error("project '%s' does not have a language", prj.name)
end
-- all rules must exist
for i = 1, #prj.rules do
local rule = prj.rules[i]
if not p.global.getRule(rule) then
premake.error("project '%s' uses missing rule '%s'", prj.name, rule)
end
end
-- check for out of scope fields
premake.validateScopes(prj, "project", ctx)
end

View File

@ -12,6 +12,15 @@
---
-- Alias the old external() call to the new externalProject(), to distinguish
-- between it and externalRule().
---
external = externalProject
---
-- Create a new project container instance.
---
@ -19,7 +28,6 @@
function project.new(name)
local prj = p.container.new(project, name)
prj.uuid = os.uuid(name)
prj.filename = name
if p.api.scope.group then
prj.group = p.api.scope.group.name

157
src/base/rule.lua Normal file
View File

@ -0,0 +1,157 @@
---
-- base/rule.lua
-- Defines rule sets for generated custom rule files.
-- Copyright (c) 2014 Jason Perkins and the Premake project
---
local p = premake
p.rule = p.api.container("rule", p.global)
local rule = p.rule
---
-- Create a new rule container instance.
---
function rule.new(name)
local self = p.container.new(rule, name)
-- create a variable setting function
name = name:gsub("^%u+", string.lower)
_G[name .. "Vars"] = function(vars)
rule.setVars(self, vars)
end
return self
end
---
-- Enumerate the property definitions for a rule.
---
function rule.eachProperty(self)
local props = self.propertyDefinition
local i = 0
return function ()
i = i + 1
if i <= #props then
return props[i]
end
end
end
---
-- Find a property definition by its name.
--
-- @param name
-- The property name.
-- @returns
-- The property definition if found, nil otherwise.
---
function rule.getProperty(self, name)
local props = self.propertyDefinition
for i = 1, #props do
local prop = props[i]
if prop.name == name then
return prop
end
end
end
---
-- Find the field definition for one this rule's properties. This field
-- can then be used with the api.* functions to manipulate the property's
-- values in the current configuration scope.
--
-- @param prop
-- The property definition.
-- @return
-- The field definition for the property; this will be created if it
-- does not already exist.
---
function rule.getPropertyField(self, prop)
if prop._field then
return prop._field
end
local kind = prop.kind or "string"
if kind == "list" then
kind = "list:string"
end
local fld = p.field.new {
name = "_rule_" .. self.name .. "_" .. prop.name,
scope = "config",
kind = kind,
tokens = true,
}
prop._field = fld
return fld
end
---
-- Given the value for a particular property, returns a formatted string.
--
-- @param prop
-- The property definition.
-- @param value
-- The value of the property to be formatted.
-- @returns
-- A string value.
---
function rule.getPropertyString(self, prop, value)
-- list?
if type(value) == "table" then
local sep = prop.separator or " "
return table.concat(value, sep)
end
-- enum?
if prop.values then
local i = table.indexof(prop.values, value)
return tostring(i)
end
-- primitive
value = tostring(value)
if #value > 0 then
return value
else
return nil
end
end
---
-- Set one or more rule variables in the current configuration scope.
--
-- @param vars
-- A key-value list of variables to set and their corresponding values.
---
function rule.setVars(self, vars)
for key, value in pairs(vars) do
local prop = rule.getProperty(self, key)
if not prop then
error (string.format("rule '%s' does not have property '%s'", self.name, key))
end
local fld = rule.getPropertyField(self, prop)
p.api.storeField(fld, value)
end
end

View File

@ -1,20 +0,0 @@
---
-- base/rules.lua
-- Defines rule sets for generated custom rule files.
-- Copyright (c) 2014 Jason Perkins and the Premake project
---
local p = premake
p.rule = p.api.container("rule", p.global)
local rule = p.rule
---
-- Create a new rule container instance.
---
function rule.new(name)
return p.container.new(rule, name)
end

View File

@ -18,7 +18,6 @@
function solution.new(name)
local sln = p.container.new(solution, name)
sln.filename = name
return sln
end

View File

@ -5,6 +5,16 @@
--
--
-- Capitalize the first letter of the string.
--
function string.capitalized(self)
return self:gsub("^%l", string.upper)
end
--
-- Returns true if the string has a match for the plain specified pattern
--

View File

@ -168,15 +168,15 @@
--
function table.indexof(tbl, obj)
local count = #tbl
for i = 1, count do
if tbl[i] == obj then
return i
for k, v in pairs(tbl) do
if v == obj then
return k
end
end
end
---
-- Insert a new value into a table in the position after the specified
-- existing value. If the specified value does not exist in the table,

View File

@ -37,6 +37,7 @@ return {
"oven/test_objdirs.lua",
-- API tests
"api/test_boolean_kind.lua",
"api/test_containers.lua",
"api/test_directory_kind.lua",
"api/test_list_kind.lua",
@ -114,6 +115,7 @@ 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_vars.lua",
-- Visual Studio 2012
"actions/vs2012/test_csproj_common_props.lua",

View File

@ -184,7 +184,7 @@ EndProject
--
function suite.translatesEnvironmentVars()
external "MyProject"
externalProject "MyProject"
location "$(SDK_LOCATION)/MyProject"
uuid "30A1B994-C2C6-485F-911B-FB4674366DA8"
kind "SharedLib"

View File

@ -16,6 +16,8 @@
local sln
function suite.setup()
rule "MyRules"
rule "MyOtherRules"
sln = test.createsolution()
end
@ -45,8 +47,7 @@
--
function suite.addsImport_onEachRulesFile()
customRules "MyRules"
customRules "MyOtherRules"
rules { "MyRules", "MyOtherRules" }
prepare()
test.capture [[
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
@ -63,13 +64,13 @@
--
function suite.usesProjectRelativePaths()
customRules "path/to/MyRules"
rules "MyRules"
location "build"
prepare()
test.capture [[
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
<Import Project="..\path\to\MyRules.props" />
<Import Project="..\MyRules.props" />
</ImportGroup>
]]
end

View File

@ -16,6 +16,8 @@
local sln
function suite.setup()
rule "MyRules"
rule "MyOtherRules"
sln = test.createsolution()
end
@ -45,8 +47,7 @@
--
function suite.addsImport_onEachRulesFile()
customRules "MyRules"
customRules "MyOtherRules"
rules { "MyRules", "MyOtherRules" }
prepare()
test.capture [[
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
@ -63,13 +64,13 @@
--
function suite.usesProjectRelativePaths()
customRules "path/to/MyRules"
rules { "MyRules" }
location "build"
prepare()
test.capture [[
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
<Import Project="..\path\to\MyRules.targets" />
<Import Project="..\MyRules.targets" />
</ImportGroup>
]]
end

View File

@ -16,6 +16,15 @@
function suite.setup()
_ACTION = "vs2010"
rule "Animation"
fileExtension ".dae"
propertyDefinition {
name = "AdditionalOptions",
kind = "list",
separator = ";"
}
sln = test.createsolution()
end
@ -522,12 +531,11 @@
-- Check handling of files using custom rule definitions.
--
function suite.correctlyCategorized_onCustomRule()
function suite.isCategorizedByRule()
rules "Animation"
files { "hello.dae" }
filter "files:**.dae"
customRule "Animation"
prepare()
test.capture [[
test.capture [[
<ItemGroup>
<Animation Include="hello.dae" />
</ItemGroup>
@ -535,107 +543,20 @@
end
function suite.customRule_onLiteralVars()
function suite.listsPerConfigRuleVars()
rules "Animation"
files { "hello.dae" }
filter "files:**.dae"
customRule "Animation"
customVar { "GenerateDebugInfo", "True" }
filter { "files:hello.*", "configurations:Debug" }
animationVars { AdditionalOptions = { "File1", "File2" }}
filter { "files:hello.*", "configurations:Release" }
animationVars { AdditionalOptions = { "File3" }}
prepare()
test.capture [[
<ItemGroup>
<Animation Include="hello.dae">
<GenerateDebugInfo Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">True</GenerateDebugInfo>
<GenerateDebugInfo Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">True</GenerateDebugInfo>
<AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">File1;File2</AdditionalOptions>
<AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">File3</AdditionalOptions>
</Animation>
</ItemGroup>
]]
end
function suite.customRule_onLiteralPath()
targetdir "../bin/%{cfg.buildcfg}"
files { "hello.dae" }
filter "files:**.dae"
customRule "Animation"
customVar { "OutputDirectory", "%{cfg.targetdir}/anim" }
prepare()
test.capture [[
<ItemGroup>
<Animation Include="hello.dae">
<OutputDirectory Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">../bin/Debug/anim</OutputDirectory>
<OutputDirectory Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">../bin/Release/anim</OutputDirectory>
</Animation>
</ItemGroup>
]]
end
function suite.customRule_onPerConfigLiteralVars()
files { "hello.dae" }
filter { "files:**.dae" }
customRule "Animation"
filter { "files:**.dae", "configurations:Debug" }
customVar { "GenerateDebugInfo", "True" }
prepare()
test.capture [[
<ItemGroup>
<Animation Include="hello.dae">
<GenerateDebugInfo Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">True</GenerateDebugInfo>
</Animation>
</ItemGroup>
]]
end
function suite.customRule_onListVars()
files { "hello.dae" }
filter "files:**.dae"
customRule "Animation"
customList { "ExtraDependencies", "File1", "File2" }
prepare()
test.capture [[
<ItemGroup>
<Animation Include="hello.dae">
<ExtraDependencies Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">File1 File2</ExtraDependencies>
<ExtraDependencies Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">File1 File2</ExtraDependencies>
</Animation>
</ItemGroup>
]]
end
function suite.customRule_onPerConfigListVars()
files { "hello.dae" }
filter { "files:**.dae" }
customRule "Animation"
customList { "ExtraDependencies", "File1", "File2" }
filter { "files:**.dae", "configurations:Release" }
customList { "ExtraDependencies", "File3" }
prepare()
test.capture [[
<ItemGroup>
<Animation Include="hello.dae">
<ExtraDependencies Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">File1 File2</ExtraDependencies>
<ExtraDependencies Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">File1 File2 File3</ExtraDependencies>
</Animation>
</ItemGroup>
]]
end
function suite.customRule_onListVarsWithCustomFormat()
files { "hello.dae" }
filter "files:**.dae"
customRule "Animation"
customListFormat { "ExtraDependencies", ";" }
customList { "ExtraDependencies", "File1", "File2" }
prepare()
test.capture [[
<ItemGroup>
<Animation Include="hello.dae">
<ExtraDependencies Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">File1;File2</ExtraDependencies>
<ExtraDependencies Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">File1;File2</ExtraDependencies>
</Animation>
</ItemGroup>
]]
end

View File

@ -171,9 +171,12 @@
--
function suite.filter_onCustomRule()
rules "Animation"
files { "hello.dae" }
filter "files:**.dae"
customRule "Animation"
rule "Animation"
fileExtension ".dae"
prepare()
test.capture [[
<ItemGroup>

View File

@ -0,0 +1,108 @@
--
-- tests/actions/vstudio/vc2010/test_rule_vars.lua
-- Validate generation of custom rule variables at the project level.
-- Copyright (c) 2014 Jason Perkins and the Premake project
--
local suite = test.declare("vstudio_vs2010_rule_vars")
local vc2010 = premake.vstudio.vc2010
--
-- Setup
--
local sln, prj
function suite.setup()
rule "MyRule"
sln, prj = test.createsolution()
rules { "MyRule" }
end
local function createVar(def)
rule "MyRule"
propertyDefinition(def)
project "MyProject"
end
local function prepare()
local cfg = test.getconfig(prj, "Debug")
vc2010.ruleVars(cfg)
end
--
-- If the configuration has a rule, but does not set any variables,
-- nothing should be written.
--
function suite.noOutput_onNoVars()
prepare()
test.isemptycapture()
end
--
-- Test setting the various property kinds.
--
function suite.onStringVar()
createVar { name="MyVar", kind="string" }
myRuleVars { MyVar = "hello" }
prepare()
test.capture [[
<MyRule>
<MyVar>hello</MyVar>
</MyRule>
]]
end
function suite.onBooleanVar()
createVar { name="MyVar", kind="boolean" }
myRuleVars { MyVar = false }
prepare()
test.capture [[
<MyRule>
<MyVar>false</MyVar>
</MyRule>
]]
end
function suite.onListVar()
createVar { name="MyVar", kind="list" }
myRuleVars { MyVar = { "a", "b", "c" } }
prepare()
test.capture [[
<MyRule>
<MyVar>a b c</MyVar>
</MyRule>
]]
end
function suite.onEnumVar()
createVar {
name = "MyVar",
values = {
[0] = "Win32",
[1] = "Win64",
},
switch = {
[0] = "-m32",
[1] = "-m64",
},
value = 0,
}
myRuleVars { MyVar = "Win32" }
prepare()
test.capture [[
<MyRule>
<MyVar>0</MyVar>
</MyRule>
]]
end

View File

@ -0,0 +1,79 @@
--
-- tests/api/test_boolean_kind.lua
-- Tests the boolean API value type.
-- Copyright (c) 2014 Jason Perkins and the Premake project
--
local suite = test.declare("api_boolean_kind")
local api = premake.api
--
-- Setup and teardown
--
function suite.setup()
api.register {
name = "testapi",
kind = "boolean",
scope = "project",
}
test.createsolution()
end
function suite.teardown()
testapi = nil
end
--
-- Check setting of true values.
--
function suite.setsTrue_onYes()
testapi "yes"
test.istrue(api.scope.project.testapi)
end
function suite.setsTrue_onBooleanTrue()
testapi (true)
test.istrue(api.scope.project.testapi)
end
function suite.setsTrue_onNonZero()
testapi (1)
test.istrue(api.scope.project.testapi)
end
--
-- Check setting of false values.
--
function suite.setsFalse_onNo()
testapi "no"
test.isfalse(api.scope.project.testapi)
end
function suite.setsFalse_onBooleanFalse()
testapi (false)
test.isfalse(api.scope.project.testapi)
end
function suite.setsFalse_onZero()
testapi (0)
test.isfalse(api.scope.project.testapi)
end
--
-- Raise an error on an invalid string value.
--
function suite.raisesError_onDisallowedValue()
ok, err = pcall(function ()
testapi "maybe"
end)
test.isfalse(ok)
end

View File

@ -130,3 +130,17 @@
premake.validate()
test.stderr("'configurations' on config")
end
--
-- If a rule is specified for inclusion, it must have been defined.
--
function suite.fails_onNoSuchRule()
solution "MySolution"
configurations { "Debug", "Release" }
project "MyProject"
rules { "NoSuchRule" }
test.isfalse(pcall(premake.validate))
end