diff --git a/src/_premake_init.lua b/src/_premake_init.lua
index 793b2db1..d8e5e6cf 100644
--- a/src/_premake_init.lua
+++ b/src/_premake_init.lua
@@ -136,6 +136,19 @@
tokens = true,
}
+ api.register {
+ name = "customRule",
+ scope = "config",
+ kind = "string",
+ }
+
+ api.register {
+ name = "customRules",
+ scope = "project",
+ kind = "list:file",
+ tokens = true,
+ }
+
api.register {
name = "debugargs",
scope = "config",
diff --git a/src/actions/vstudio/vs2010_vcxproj.lua b/src/actions/vstudio/vs2010_vcxproj.lua
index e3933bb2..b96df9b6 100644
--- a/src/actions/vstudio/vs2010_vcxproj.lua
+++ b/src/actions/vstudio/vs2010_vcxproj.lua
@@ -482,6 +482,43 @@
m.simplefilesgroup(prj, "None")
m.simplefilesgroup(prj, "ResourceCompile")
m.customBuildFilesGroup(prj)
+ m.customRuleFileGroups(prj)
+ end
+
+
+ function m.customRuleFileGroups(prj)
+ local group = m.getfilegroup(prj, "CustomRule")
+ local vars = p.api.getCustomVars()
+
+ for rule, files in pairs(group) do
+ if #files > 0 then
+ _p(1,'')
+ for i, file in ipairs(files) do
+
+ local contents = p.capture(function ()
+ for i, var in ipairs(vars) do
+ 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 = var:sub(9)
+ _x(3,'<%s %s>%s%s>', key, m.condition(fcfg.config), fcfg[var], key)
+ end
+ end
+ end
+ end)
+
+ if #contents > 0 then
+ _p(2,'<%s Include=\"%s\">', rule, path.translate(file.relpath))
+ _p('%s', contents)
+ _p(2,'%s>', rule)
+ else
+ _p(2,'<%s Include=\"%s\" />', rule, path.translate(file.relpath))
+ end
+ end
+ _p(1,'')
+ end
+ end
end
@@ -594,58 +631,117 @@
function m.getfilegroup(prj, group)
- -- check for a cached copy before creating
- local groups = prj.vc2010_file_groups
- if not groups then
- groups = {
- ClCompile = {},
- ClInclude = {},
- None = {},
- ResourceCompile = {},
- CustomBuild = {},
- }
- prj.vc2010_file_groups = groups
+ -- Have I already created the groups?
+ local groups = prj._vc2010_file_groups
+ if groups then
+ return groups[group]
+ end
- local tr = project.getsourcetree(prj)
- tree.traverse(tr, {
- onleaf = function(node)
- -- if any configuration of this file uses a custom build rule,
- -- then they all must be marked as custom build
- local hasbuildrule = false
- for cfg in project.eachconfig(prj) do
- local filecfg = fileconfig.getconfig(node, cfg)
- if fileconfig.hasCustomBuildRule(filecfg) then
- hasbuildrule = true
- break
- end
- end
+ groups = {
+ ClCompile = {},
+ ClInclude = {},
+ None = {},
+ ResourceCompile = {},
+ CustomBuild = {},
+ CustomRule = {},
+ }
- if hasbuildrule then
- table.insert(groups.CustomBuild, node)
- elseif path.iscppfile(node.name) then
- table.insert(groups.ClCompile, node)
- elseif path.iscppheader(node.name) then
- table.insert(groups.ClInclude, node)
- elseif path.isresourcefile(node.name) then
- table.insert(groups.ResourceCompile, node)
- else
- table.insert(groups.None, node)
+ local tr = project.getsourcetree(prj)
+ tree.traverse(tr, {
+ onleaf = function(node)
+ -- if any configuration of this file uses a custom build rule,
+ -- then they all must be marked as custom build
+ local customBuild, customRule
+
+ for cfg in project.eachconfig(prj) do
+ local fcfg = fileconfig.getconfig(node, cfg)
+ if fileconfig.hasCustomBuildRule(fcfg) then
+ customBuild = true
+ break
+ elseif fcfg and fcfg.customRule then
+ customRule = fcfg.customRule
+ break
end
end
- })
- -- sort by relative to path; otherwise VS will reorder the files
- for group, files in pairs(groups) do
- table.sort(files, function (a, b)
- return a.relpath < b.relpath
- end)
+ if customBuild then
+ table.insert(groups.CustomBuild, node)
+ elseif customRule then
+ groups.CustomRule[customRule] = groups.CustomRule[customRule] or {}
+ table.insert(groups.CustomRule[customRule], node)
+ elseif path.iscppfile(node.name) then
+ table.insert(groups.ClCompile, node)
+ elseif path.iscppheader(node.name) then
+ table.insert(groups.ClInclude, node)
+ elseif path.isresourcefile(node.name) then
+ table.insert(groups.ResourceCompile, node)
+ else
+ table.insert(groups.None, node)
+ end
+ end
+ })
+
+ -- sort by relative to path; otherwise VS will reorder the files
+ for group, files in pairs(groups) do
+ table.sort(files, function (a, b)
+ return a.relpath < b.relpath
+ end)
+ end
+
+ prj._vc2010_file_groups = groups
+ return groups[group]
+ end
+
+
+ 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
+ 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
- return groups[group]
+ -- Otherwise use the file extension to deduce a category
+ if path.iscppfile(node.name) then
+ return "ClCompile"
+ elseif path.iscppheader(node.name) then
+ return "ClInclude"
+ elseif path.isresourcefile(node.name) then
+ return "ResourceCompile"
+ else
+ return "None"
+ end
end
+ function m.categorizeSources(prj)
+ local groups = {}
+
+ local tr = project.getsourcetree(prj)
+ tree.traverse(tr, {
+ onleaf = function(node)
+ local cat = m.categorize(prj, node)
+ groups[cat] = groups[cat] or {}
+ table.insert(groups[cat], node)
+ end
+ })
+
+ -- sort by relative to path; otherwise VS will reorder the files
+ for group, files in pairs(groups) do
+ table.sort(files, function (a, b)
+ return a.relpath < b.relpath
+ end)
+ end
+
+ return groups
+ end
+
+
+
--
-- Generate the list of project dependencies.
--
@@ -966,9 +1062,14 @@
function m.importExtensionSettings(prj)
- _p(1,'')
- _p(1,'')
- _p(1,'')
+ p.w('')
+ p.push('')
+ table.foreachi(prj.customRules, function(value)
+ value = path.translate(project.getrelative(prj, value))
+ value = path.appendExtension(value, ".props")
+ p.x('', value)
+ end)
+ p.pop('')
end
diff --git a/src/base/api.lua b/src/base/api.lua
index f7ecc7ec..20689f03 100755
--- a/src/base/api.lua
+++ b/src/base/api.lua
@@ -125,6 +125,8 @@
return api.remove(field, value)
end
end
+
+ return field
end
@@ -925,3 +927,46 @@
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.
+--
+-----------------------------------------------------------------------------
+
+ function customVar(value)
+ if type(value) ~= "table" or #value ~= 2 then
+ error { msg="invalid value for customVar()" }
+ end
+
+ local name = value[1]
+ local value = value[2]
+
+ local fieldName = "_custom_" .. name
+ local field = premake.field.get(fieldName)
+ if not field then
+ field = api.register {
+ name = fieldName,
+ scope = "config",
+ kind = "string"
+ }
+ end
+
+ _G[fieldName](value)
+ end
+
+
+ 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
diff --git a/src/base/field.lua b/src/base/field.lua
index 19b35b13..961d28d2 100644
--- a/src/base/field.lua
+++ b/src/base/field.lua
@@ -102,6 +102,20 @@
+---
+-- Returns an iterator for the list of register fields.
+---
+
+ function field.each()
+ local index
+ return function ()
+ index = next(field._list, index)
+ return field._list[index]
+ end
+ end
+
+
+
---
-- Register a new kind of data for field storage.
--
diff --git a/src/base/path.lua b/src/base/path.lua
index 66b8cc32..3200fbcc 100644
--- a/src/base/path.lua
+++ b/src/base/path.lua
@@ -1,7 +1,7 @@
--
-- path.lua
-- Path manipulation functions.
--- Copyright (c) 2002-2013 Jason Perkins and the Premake project
+-- Copyright (c) 2002-2014 Jason Perkins and the Premake project
--
@@ -10,7 +10,7 @@
-- isn't already present, and adjusts quotes as necessary.
--
- function path.appendextension(p, ext)
+ function path.appendExtension(p, ext)
-- if the extension is nil or empty, do nothing
if not ext or ext == "" then
return p
@@ -36,6 +36,9 @@
return p
end
+ path.appendextension = path.appendExtension
+
+
--
-- Retrieve the filename portion of a path, without any extension.
diff --git a/tests/actions/vstudio/vc2010/test_files.lua b/tests/actions/vstudio/vc2010/test_files.lua
index 20ba076d..b1599786 100755
--- a/tests/actions/vstudio/vc2010/test_files.lua
+++ b/tests/actions/vstudio/vc2010/test_files.lua
@@ -69,6 +69,11 @@
]]
end
+
+--
+-- Check handling of files with custom build rules.
+--
+
function suite.customBuild_onBuildRule()
files { "hello.cg" }
filter "files:**.cg"
@@ -88,7 +93,6 @@
]]
end
-
function suite.customBuild_onBuildRuleWithMessage()
files { "hello.cg" }
filter "files:**.cg"
@@ -490,3 +494,56 @@
]]
end
+
+
+--
+-- Check handling of files using custom rule definitions.
+--
+
+ function suite.correctlyCategorized_onCustomRule()
+ files { "hello.dae" }
+ filter "files:**.dae"
+ customRule "Animation"
+ prepare()
+ test.capture [[
+
+
+
+ ]]
+ end
+
+
+ function suite.customRule_onLiteralVars()
+ files { "hello.dae" }
+ filter "files:**.dae"
+ customRule "Animation"
+ customVar { "GenerateDebugInfo", "True" }
+ prepare()
+ test.capture [[
+
+
+ True
+ True
+
+
+ ]]
+ end
+
+
+ function suite.customRule_onPerConfigLiteralVars()
+ files { "hello.dae" }
+ filter { "files:**.dae" }
+ customRule "Animation"
+ filter { "files:**.dae", "configurations:Debug" }
+ customVar { "GenerateDebugInfo", "True" }
+ prepare()
+ test.capture [[
+
+
+ True
+
+
+ ]]
+ end
+
+
diff --git a/tests/actions/vstudio/vc2010/test_import_extensions.lua b/tests/actions/vstudio/vc2010/test_import_extensions.lua
new file mode 100644
index 00000000..5f8bdf46
--- /dev/null
+++ b/tests/actions/vstudio/vc2010/test_import_extensions.lua
@@ -0,0 +1,75 @@
+--
+-- tests/actions/vstudio/vc2010/test_import_extensions.lua
+-- Check the import extension settings block of a VS 2010 project.
+-- Copyright (c) 2014 Jason Perkins and the Premake project
+--
+
+ local suite = test.declare("vs2010_import_extensions")
+ local vc2010 = premake.vstudio.vc2010
+ local project = premake.project
+
+
+--
+-- Setup
+--
+
+ local sln
+
+ function suite.setup()
+ sln = test.createsolution()
+ end
+
+ local function prepare()
+ local prj = test.getproject(sln)
+ vc2010.importExtensionSettings(prj)
+ end
+
+
+--
+-- Writes an empty element when no custom rules are specified.
+--
+
+ function suite.structureIsCorrect_onDefaultValues()
+ prepare()
+ test.capture [[
+
+
+
+ ]]
+ end
+
+
+
+--
+-- Writes entries for each project scoped custom rules path.
+--
+
+ function suite.addsImport_onEachRulesFile()
+ customRules "MyRules"
+ customRules "MyOtherRules"
+ prepare()
+ test.capture [[
+
+
+
+
+
+ ]]
+ end
+
+
+--
+-- Rule files use a project relative path.
+--
+
+ function suite.usesProjectRelativePaths()
+ customRules "path/to/MyRules"
+ location "build"
+ prepare()
+ test.capture [[
+
+
+
+
+ ]]
+ end
diff --git a/tests/premake5.lua b/tests/premake5.lua
index ad44964b..a496e1eb 100644
--- a/tests/premake5.lua
+++ b/tests/premake5.lua
@@ -48,6 +48,12 @@
end
+ test.getproject = function(sln, i)
+ local sln = premake.oven.bakeSolution(sln)
+ return premake.solution.getproject(sln, i or 1)
+ end
+
+
test.getconfig = function(prj, buildcfg, platform)
local sln = premake.oven.bakeSolution(prj.solution)
prj = premake.solution.getproject(sln, prj.name)
@@ -167,6 +173,7 @@
dofile("actions/vstudio/vc2010/test_files.lua")
dofile("actions/vstudio/vc2010/test_filter_ids.lua")
dofile("actions/vstudio/vc2010/test_filters.lua")
+ dofile("actions/vstudio/vc2010/test_import_extensions.lua")
dofile("actions/vstudio/vc2010/test_item_def_group.lua")
dofile("actions/vstudio/vc2010/test_link.lua")
dofile("actions/vstudio/vc2010/test_manifest.lua")