Initial implementation of a minimal custom rules API (currently Visual Studio only)
This commit is contained in:
parent
026b75bd3f
commit
efe5f1e292
@ -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",
|
||||
|
@ -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,'<ItemGroup>')
|
||||
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,'</ItemGroup>')
|
||||
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,'<Import Project="$(VCTargetsPath)\\Microsoft.Cpp.props" />')
|
||||
_p(1,'<ImportGroup Label="ExtensionSettings">')
|
||||
_p(1,'</ImportGroup>')
|
||||
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)
|
||||
p.pop('</ImportGroup>')
|
||||
end
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
--
|
||||
|
@ -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.
|
||||
|
@ -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 @@
|
||||
</ItemGroup>
|
||||
]]
|
||||
end
|
||||
|
||||
|
||||
--
|
||||
-- Check handling of files using custom rule definitions.
|
||||
--
|
||||
|
||||
function suite.correctlyCategorized_onCustomRule()
|
||||
files { "hello.dae" }
|
||||
filter "files:**.dae"
|
||||
customRule "Animation"
|
||||
prepare()
|
||||
test.capture [[
|
||||
<ItemGroup>
|
||||
<Animation Include="hello.dae" />
|
||||
</ItemGroup>
|
||||
]]
|
||||
end
|
||||
|
||||
|
||||
function suite.customRule_onLiteralVars()
|
||||
files { "hello.dae" }
|
||||
filter "files:**.dae"
|
||||
customRule "Animation"
|
||||
customVar { "GenerateDebugInfo", "True" }
|
||||
prepare()
|
||||
test.capture [[
|
||||
<ItemGroup>
|
||||
<Animation Include="hello.dae">
|
||||
<GenerateDebugInfo Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">True</GenerateDebugInfo>
|
||||
<GenerateDebugInfo Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">True</GenerateDebugInfo>
|
||||
</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
|
||||
|
||||
|
||||
|
75
tests/actions/vstudio/vc2010/test_import_extensions.lua
Normal file
75
tests/actions/vstudio/vc2010/test_import_extensions.lua
Normal file
@ -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 [[
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
]]
|
||||
end
|
||||
|
||||
|
||||
|
||||
--
|
||||
-- Writes entries for each project scoped custom rules path.
|
||||
--
|
||||
|
||||
function suite.addsImport_onEachRulesFile()
|
||||
customRules "MyRules"
|
||||
customRules "MyOtherRules"
|
||||
prepare()
|
||||
test.capture [[
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
<Import Project="MyRules.props" />
|
||||
<Import Project="MyOtherRules.props" />
|
||||
</ImportGroup>
|
||||
]]
|
||||
end
|
||||
|
||||
|
||||
--
|
||||
-- Rule files use a project relative path.
|
||||
--
|
||||
|
||||
function suite.usesProjectRelativePaths()
|
||||
customRules "path/to/MyRules"
|
||||
location "build"
|
||||
prepare()
|
||||
test.capture [[
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
<Import Project="..\path\to\MyRules.props" />
|
||||
</ImportGroup>
|
||||
]]
|
||||
end
|
@ -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")
|
||||
|
Loading…
Reference in New Issue
Block a user