Merge pull request #464 from Blizzard/file-categories-refactor

File categories refactor
This commit is contained in:
Jason Perkins 2016-04-11 13:29:50 -04:00
commit 4f9bfa17c1
5 changed files with 350 additions and 262 deletions

View File

@ -577,219 +577,244 @@
-- Write out the list of source code files, and any associated configuration.
---
m.elements.files = function(prj, groups)
return {
m.clIncludeFiles,
m.clCompileFiles,
m.noneFiles,
m.resourceCompileFiles,
m.customBuildFiles,
m.customRuleFiles,
m.midlFiles
}
end
function m.files(prj)
local groups = m.categorizeSources(prj)
p.callArray(m.elements.files, prj, groups)
for _, group in ipairs(groups) do
group.category.emitFiles(prj, group)
end
end
m.elements.ClCompileFile = function(cfg, file)
return {}
end
m.categories = {}
---
-- ClInclude group
---
m.categories.ClInclude = {
name = "ClInclude",
extensions = { ".h", ".hh", ".hpp", ".hxx" },
priority = 1,
emitFiles = function(prj, group)
m.emitFiles(prj, group, "ClInclude")
end,
emitFilter = function(prj, group)
m.filterGroup(prj, group, "ClInclude")
end
}
m.elements.ClCompileFileCfg = function(fcfg, condition)
if fcfg then
return {
m.excludedFromBuild,
m.objectFileName,
m.clCompilePreprocessorDefinitions,
m.clCompileUndefinePreprocessorDefinitions,
m.optimization,
m.forceIncludes,
m.precompiledHeader,
m.enableEnhancedInstructionSet,
m.additionalCompileOptions,
m.disableSpecificWarnings,
m.treatSpecificWarningsAsErrors
}
else
return {
---
-- ClCompile group
---
m.categories.ClCompile = {
name = "ClCompile",
extensions = { ".cc", ".cpp", ".cxx", ".c", ".s", ".m", ".mm" },
priority = 2,
emitFiles = function(prj, group)
local fileCfgFunc = function(fcfg, condition)
if fcfg then
return {
m.excludedFromBuild,
m.objectFileName,
m.clCompilePreprocessorDefinitions,
m.clCompileUndefinePreprocessorDefinitions,
m.optimization,
m.forceIncludes,
m.precompiledHeader,
m.enableEnhancedInstructionSet,
m.additionalCompileOptions,
m.disableSpecificWarnings,
m.treatSpecificWarningsAsErrors
}
else
return {
m.excludedFromBuild
}
end
end
m.emitFiles(prj, group, "ClCompile", nil, fileCfgFunc)
end,
emitFilter = function(prj, group)
m.filterGroup(prj, group, "ClCompile")
end
}
---
-- None group
---
m.categories.None = {
name = "None",
priority = 3,
emitFiles = function(prj, group)
m.emitFiles(prj, group, "None")
end,
emitFilter = function(prj, group)
m.filterGroup(prj, group, "None")
end
}
---
-- ResourceCompile group
---
m.categories.ResourceCompile = {
name = "ResourceCompile",
extensions = ".rc",
priority = 4,
emitFiles = function(prj, group)
local fileCfgFunc = {
m.excludedFromBuild
}
m.emitFiles(prj, group, "ResourceCompile", nil, fileCfgFunc, function(cfg)
return cfg.system == p.WINDOWS
end)
end,
emitFilter = function(prj, group)
m.filterGroup(prj, group, "ResourceCompile")
end
end
}
function m.clCompileFiles(prj, groups)
m.emitFiles(prj, groups, "ClCompile")
end
---
-- CustomBuild group
---
m.categories.CustomBuild = {
name = "CustomBuild",
priority = 5,
emitFiles = function(prj, group)
local fileFunc = {
m.fileType
}
m.elements.ClIncludeFile = function(cfg, file)
return {}
end
local fileCfgFunc = {
m.excludedFromBuild,
m.buildCommands,
m.buildOutputs,
m.buildMessage,
m.buildAdditionalInputs
}
m.emitFiles(prj, group, "CustomBuild", fileFunc, fileCfgFunc, function (cfg, fcfg)
return fileconfig.hasCustomBuildRule(fcfg)
end)
end,
m.elements.ClIncludeFileCfg = function(fcfg, condition)
return {}
end
function m.clIncludeFiles(prj, groups)
m.emitFiles(prj, groups, "ClInclude")
end
m.elements.CustomBuildFile = function(cfg, file)
return {
m.fileType
}
end
m.elements.CustomBuildFileCfg = function(fcfg, condition)
return {
m.excludedFromBuild,
m.buildCommands,
m.buildOutputs,
m.buildMessage,
m.buildAdditionalInputs
}
end
function m.customBuildFiles(prj, groups)
m.emitFiles(prj, groups, "CustomBuild", function (cfg, fcfg)
return fileconfig.hasCustomBuildRule(fcfg)
end)
end
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
local contents = p.capture(function()
p.push()
for prop in p.rule.eachProperty(rule) do
local fld = p.rule.getPropertyField(rule, prop)
for cfg in project.eachconfig(prj) do
local fcfg = fileconfig.getconfig(file, cfg)
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(prop.name, m.condition(cfg), '%s', value)
end
end
end
end
p.pop()
end)
if #contents > 0 then
p.push('<%s Include=\"%s\">', rule.name, path.translate(file.relpath))
p.outln(contents)
p.pop('</%s>', rule.name)
else
p.x('<%s Include=\"%s\" />', rule.name, path.translate(file.relpath))
end
end
p.pop('</ItemGroup>')
end
emitFilter = function(prj, group)
m.filterGroup(prj, group, "CustomBuild")
end
end
}
---
-- Midl group
---
m.categories.Midl = {
name = "Midl",
extensions = ".idl",
priority = 6,
m.elements.NoneFile = function(cfg, file)
return {}
end
m.elements.NoneFileCfg = function(fcfg, condition)
return {}
end
function m.noneFiles(prj, groups)
m.emitFiles(prj, groups, "None")
end
m.elements.ResourceCompileFile = function(cfg, file)
return {}
end
m.elements.ResourceCompileFileCfg = function(fcfg, condition)
return {
m.excludedFromBuild
}
end
function m.resourceCompileFiles(prj, groups)
m.emitFiles(prj, groups, "ResourceCompile", function(cfg)
return cfg.system == p.WINDOWS
end)
end
m.elements.MidlFile = function(cfg, file)
return {}
end
m.elements.MidlFileCfg = function(fcfg, condition)
return {
m.excludedFromBuild
}
end
function m.midlFiles(prj, groups)
m.emitFiles(prj, groups, "Midl", function(cfg)
return cfg.system == p.WINDOWS
end)
end
emitFiles = function(prj, group)
local fileCfgFunc = {
m.excludedFromBuild
}
m.emitFiles(prj, group, "Midl", nil, fileCfgFunc, function(cfg)
return cfg.system == p.WINDOWS
end)
end,
emitFilter = function(prj, group)
m.filterGroup(prj, group, "Midl")
end
}
---
-- Masm group
---
m.categories.Masm = {
name = "Masm",
extensions = ".asm",
priority = 7,
emitFiles = function(prj, group)
local fileCfgFunc = {
m.excludedFromBuild
}
m.emitFiles(prj, group, "Masm", nil, fileCfgFunc, function(cfg)
return cfg.system == p.WINDOWS
end)
end,
emitFilter = function(prj, group)
m.filterGroup(prj, group, "Masm")
end,
emitExtensionSettings = function(prj, group)
p.w('<Import Project="$(VCTargetsPath)\\BuildCustomizations\\masm.props" />')
end,
emitExtensionTargets = function(prj, group)
p.w('<Import Project="$(VCTargetsPath)\\BuildCustomizations\\masm.targets" />')
end
}
---
-- Categorize files into groups.
---
function m.categorizeSources(prj)
local groups = prj._vc2010_sources
if groups then
return groups
-- if we already did this, return the cached result.
if prj._vc2010_sources then
return prj._vc2010_sources
end
groups = {}
prj._vc2010_sources = groups
-- build the new group table.
local result = {}
local groups = {}
prj._vc2010_sources = result
local tr = project.getsourcetree(prj)
tree.traverse(tr, {
onleaf = function(node)
local cat = m.categorizeFile(prj, node)
groups[cat] = groups[cat] or {}
table.insert(groups[cat], node)
groups[cat.name] = groups[cat.name] or {
category = cat,
files = {}
}
table.insert(groups[cat.name].files, 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)
for name, group in pairs(groups) do
table.sort(group.files, function (a, b)
return a.relpath < b.relpath
end)
table.insert(result, group)
end
return groups
-- sort by category priority then name; so we get stable results.
table.sort(result, function (a, b)
if (a.category.priority == b.category.priority) then
return a.category.name < b.category.name
end
return a.category.priority < b.category.priority
end)
return result
end
@ -799,44 +824,50 @@
for cfg in project.eachconfig(prj) do
local fcfg = fileconfig.getconfig(file, cfg)
if fileconfig.hasCustomBuildRule(fcfg) then
return "CustomBuild"
return m.categories.CustomBuild
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
return {
name = rule.name,
priority = 100,
rule = rule,
emitFiles = function(prj, group)
m.emitRuleFiles(prj, group)
end,
emitFilter = function(prj, group)
m.filterGroup(prj, group, group.category.name)
end
}
end
-- Otherwise use the file extension to deduce a category
if path.iscppfile(file.name) then
return "ClCompile"
elseif path.iscppheader(file.name) then
return "ClInclude"
elseif path.isresourcefile(file.name) then
return "ResourceCompile"
elseif path.isidlfile(file.name) then
return "Midl"
else
return "None"
for _, cat in pairs(m.categories) do
if cat.extensions and path.hasextension(file.name, cat.extensions) then
return cat
end
end
return m.categories.None
end
function m.emitFiles(prj, groups, group, check)
local files = groups[group]
function m.emitFiles(prj, group, tag, fileFunc, fileCfgFunc, checkFunc)
local files = group.files
if files and #files > 0 then
p.push('<ItemGroup>')
for _, file in ipairs(files) do
local contents = p.capture(function ()
p.push()
p.callArray(m.elements[group .. "File"], cfg, file)
p.callArray(fileFunc, cfg, file)
for cfg in project.eachconfig(prj) do
local fcfg = fileconfig.getconfig(file, cfg)
if not check or check(cfg, fcfg) then
p.callArray(m.elements[group .. "FileCfg"], fcfg, m.condition(cfg))
if not checkFunc or checkFunc(cfg, fcfg) then
p.callArray(fileCfgFunc, fcfg, m.condition(cfg))
end
end
p.pop()
@ -844,11 +875,11 @@
local rel = path.translate(file.relpath)
if #contents > 0 then
p.push('<%s Include="%s">', group, rel)
p.push('<%s Include="%s">', tag, rel)
p.outln(contents)
p.pop('</%s>', group)
p.pop('</%s>', tag)
else
p.x('<%s Include="%s" />', group, rel)
p.x('<%s Include="%s" />', tag, rel)
end
end
@ -856,6 +887,46 @@
end
end
function m.emitRuleFiles(prj, group)
local files = group.files
local rule = group.category.rule
if files and #files > 0 then
p.push('<ItemGroup>')
for _, file in ipairs(files) do
local contents = p.capture(function()
p.push()
for prop in p.rule.eachProperty(rule) do
local fld = p.rule.getPropertyField(rule, prop)
for cfg in project.eachconfig(prj) do
local fcfg = fileconfig.getconfig(file, cfg)
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(prop.name, m.condition(cfg), '%s', value)
end
end
end
end
p.pop()
end)
if #contents > 0 then
p.push('<%s Include=\"%s\">', rule.name, path.translate(file.relpath))
p.outln(contents)
p.pop('</%s>', rule.name)
else
p.x('<%s Include=\"%s\" />', rule.name, path.translate(file.relpath))
end
end
p.pop('</ItemGroup>')
end
end
--
@ -1312,6 +1383,7 @@
m.elements.importExtensionTargets = function(prj)
return {
m.importGroupTargets,
m.importRuleTargets,
m.importBuildCustomizationsTargets
}
@ -1323,6 +1395,15 @@
p.pop('</ImportGroup>')
end
function m.importGroupTargets(prj)
local groups = m.categorizeSources(prj)
for _, group in ipairs(groups) do
if group.category.emitExtensionTargets then
group.category.emitExtensionTargets(prj, group)
end
end
end
function m.importRuleTargets(prj)
for i = 1, #prj.rules do
local rule = p.global.getRule(prj.rules[i])
@ -1333,8 +1414,8 @@
function m.importBuildCustomizationsTargets(prj)
for i, build in ipairs(prj.buildcustomizations) do
premake.w('<Import Project="$(VCTargetsPath)\\%s.targets" />', path.translate(build))
end
p.w('<Import Project="$(VCTargetsPath)\\%s.targets" />', path.translate(build))
end
end
@ -1351,6 +1432,7 @@
m.elements.importExtensionSettings = function(prj)
return {
m.importGroupSettings,
m.importRuleSettings,
m.importBuildCustomizationsProps
}
@ -1362,6 +1444,17 @@
p.pop('</ImportGroup>')
end
function m.importGroupSettings(prj)
local groups = m.categorizeSources(prj)
for _, group in ipairs(groups) do
if group.category.emitExtensionSettings then
group.category.emitExtensionSettings(prj, group)
end
end
end
function m.importRuleSettings(prj)
for i = 1, #prj.rules do
local rule = p.global.getRule(prj.rules[i])
@ -1370,10 +1463,11 @@
end
end
function m.importBuildCustomizationsProps(prj)
for i, build in ipairs(prj.buildcustomizations) do
premake.w('<Import Project="$(VCTargetsPath)\\%s.props" />', path.translate(build))
end
p.w('<Import Project="$(VCTargetsPath)\\%s.props" />', path.translate(build))
end
end

View File

@ -36,28 +36,11 @@
m.elements.filterGroups = {
"None",
"ClInclude",
"ClCompile",
"ResourceCompile",
"CustomBuild",
"CustomRule"
}
m.elements.filters = function(prj, groups)
local calls = {}
for i, group in ipairs(m.elements.filterGroups) do
calls[i] = m[group .. "Filters"]
end
return calls
end
function m.filterGroups(prj)
-- Categorize the source files in groups by build rule; each will
-- be written to a separate item group by one of the handlers
local groups = m.categorizeSources(prj)
p.callArray(m.elements.filters, prj, groups)
for _, group in ipairs(groups) do
group.category.emitFilter(prj, group)
end
end
@ -90,51 +73,17 @@
end
--
-- The second portion of the filters file assigns filters to each source
-- code file, as needed. Group is one of "ClCompile", "ClInclude",
-- "ResourceCompile", or "None".
--
function m.ClCompileFilters(prj, groups)
m.filterGroup(prj, groups, "ClCompile")
end
function m.ClIncludeFilters(prj, groups)
m.filterGroup(prj, groups, "ClInclude")
end
function m.CustomBuildFilters(prj, groups)
m.filterGroup(prj, groups, "CustomBuild")
end
function m.CustomRuleFilters(prj, groups)
for group, files in pairs(groups) do
if not table.contains(m.elements.filterGroups, group) then
m.filterGroup(prj, groups, group)
end
end
end
function m.NoneFilters(prj, groups)
m.filterGroup(prj, groups, "None")
end
function m.ResourceCompileFilters(prj, groups)
m.filterGroup(prj, groups, "ResourceCompile")
end
function m.filterGroup(prj, groups, group)
local files = groups[group] or {}
if #files > 0 then
function m.filterGroup(prj, group, tag)
local files = group.files
if files and #files > 0 then
p.push('<ItemGroup>')
for _, file in ipairs(files) do
if file.parent.path then
p.push('<%s Include=\"%s\">', group, path.translate(file.relpath))
p.push('<%s Include=\"%s\">', tag, path.translate(file.relpath))
p.w('<Filter>%s</Filter>', path.translate(file.parent.path))
p.pop('</%s>', group)
p.pop('</%s>', tag)
else
p.w('<%s Include=\"%s\" />', group, path.translate(file.relpath))
p.w('<%s Include=\"%s\" />', tag, path.translate(file.relpath))
end
end
p.pop('</ItemGroup>')

View File

@ -72,3 +72,18 @@
</ImportGroup>
]]
end
--
-- the asm 'file category' should add the right settings.
--
function suite.hasAssemblyFiles()
files { "test.asm" }
location "build"
prepare()
test.capture [[
<ImportGroup Label="ExtensionSettings">
<Import Project="$(VCTargetsPath)\BuildCustomizations\masm.props" />
</ImportGroup>
]]
end

View File

@ -72,3 +72,19 @@
</ImportGroup>
]]
end
--
-- the asm 'file category' should add the right target.
--
function suite.hasAssemblyFiles()
files { "test.asm" }
location "build"
prepare()
test.capture [[
<ImportGroup Label="ExtensionTargets">
<Import Project="$(VCTargetsPath)\BuildCustomizations\masm.targets" />
</ImportGroup>
]]
end

View File

@ -63,15 +63,15 @@
files { "hello.c", "hello.h", "hello.rc", "hello.txt" }
prepare()
test.capture [[
<ItemGroup>
<None Include="hello.txt" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="hello.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="hello.c" />
</ItemGroup>
<ItemGroup>
<None Include="hello.txt" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="hello.rc" />
</ItemGroup>
@ -184,3 +184,17 @@
</ItemGroup>
]]
end
--
-- Check handling of .asm files
--
function suite.itemGroup_onNoneSection()
files { "hello.asm" }
prepare()
test.capture [[
<ItemGroup>
<Masm Include="hello.asm" />
</ItemGroup>
]]
end