Merge work-in-progress rules support, configuration container API

This commit is contained in:
Jason Perkins 2014-10-08 16:45:03 -04:00
commit 352b5ba4ca
28 changed files with 2104 additions and 345 deletions

View File

@ -20,18 +20,13 @@
"base/globals.lua",
-- configuration data
"base/container.lua",
"base/field.lua",
"base/criteria.lua",
"base/detoken.lua",
"base/configset.lua",
"base/context.lua",
-- project objects
"base/project.lua",
"base/solution.lua",
"base/config.lua",
"base/fileconfig.lua",
-- runtime switches
"base/option.lua",
"base/action.lua",
@ -39,6 +34,13 @@
-- project script setup
"base/api.lua",
-- project objects
"base/solution.lua",
"base/project.lua",
"base/config.lua",
"base/fileconfig.lua",
"base/rules.lua",
-- project script processing
"base/oven.lua",
"base/premake.lua",
@ -70,6 +72,9 @@
"actions/vstudio/vs2010_vcxproj.lua",
"actions/vstudio/vs2010_vcxproj_user.lua",
"actions/vstudio/vs2010_vcxproj_filters.lua",
"actions/vstudio/vs2010_rules_props.lua",
"actions/vstudio/vs2010_rules_targets.lua",
"actions/vstudio/vs2010_rules_xml.lua",
"actions/vstudio/vs2012.lua",
"actions/vstudio/vs2013.lua",

View File

@ -62,20 +62,35 @@
},
}
api.register {
name = "buildmessage",
scope = "config",
kind = "string",
tokens = true,
}
api.register {
name = "buildcommands",
scope = "config",
scope = { "config", "rule" },
kind = "list:string",
tokens = true,
}
api.alias("buildcommands", "buildCommands")
api.register {
name = "buildDependencies",
scope = { "rule" },
kind = "list:string",
tokens = true,
}
api.register {
name = "buildmessage",
scope = { "config", "rule" },
kind = "string",
tokens = true
}
api.alias("buildmessage", "buildMessage")
api.register {
name = "buildoptions",
scope = "config",
@ -83,13 +98,17 @@
tokens = true,
}
api.register {
name = "buildoutputs",
scope = "config",
scope = { "config", "rule" },
kind = "list:path",
tokens = true,
}
api.alias("buildoutputs", "buildOutputs")
api.register {
name = "buildinputs",
scope = "config",
@ -214,14 +233,30 @@
tokens = true,
}
api.register {
name = "display",
scope = "rule",
kind = "string",
}
-- For backward compatibility, excludes() is now an alias for removefiles()
function excludes(value)
removefiles(value)
end
api.register {
name = "fileExtension",
scope = "rule",
kind = "string",
}
api.register {
name = "filename",
scope = "project",
scope = { "project", "rule" },
kind = "string",
tokens = true,
}
@ -442,7 +477,7 @@
api.register {
name = "location",
scope = "project",
scope = { "project", "rule" },
kind = "path",
tokens = true,
}
@ -555,6 +590,12 @@
tokens = true,
}
api.register {
name = "propertyDefinition",
scope = "rule",
kind = "list:table",
}
api.register {
name = "rebuildcommands",
scope = "config",

View File

@ -184,7 +184,7 @@
function make.csResponseRules(prj)
local toolset = premake.tools.dotnet
local ext = make.getmakefilename(prj, true)
local makefile = path.getname(premake.project.getfilename(prj, ext))
local makefile = path.getname(premake.filename(prj, ext))
local response = path.translate(make.cs.getresponsefilename(prj))
_p('$(RESPONSE): %s', makefile)

View File

@ -58,7 +58,7 @@
function make.cleanrules(sln)
_p('clean:')
for prj in solution.eachproject(sln) do
local prjpath = project.getfilename(prj, make.getmakefilename(prj, true))
local prjpath = premake.filename(prj, make.getmakefilename(prj, true))
local prjdir = path.getdirectory(path.getrelative(sln.location, prjpath))
local prjname = path.getname(prjpath)
_x(1,'@${MAKE} --no-print-directory -C %s -f %s clean', prjdir, prjname)
@ -174,7 +174,7 @@
_p(1,'@echo "==== Building %s ($(%s_config)) ===="', prj.name, cfgvar)
local prjpath = project.getfilename(prj, make.getmakefilename(prj, true))
local prjpath = premake.filename(prj, make.getmakefilename(prj, true))
local prjdir = path.getdirectory(path.getrelative(sln.location, prjpath))
local prjname = path.getname(prjpath)

View File

@ -6,9 +6,11 @@
premake.vstudio = {}
local vstudio = premake.vstudio
local solution = premake.solution
local project = premake.project
local config = premake.config
local p = premake
local solution = p.solution
local project = p.project
local config = p.config
--
@ -368,6 +370,16 @@
end
---
-- Generates a Visual Studio project element for the current action.
---
function vstudio.projectElement()
p.push('<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">')
end
--
-- Returns the full, absolute path to the Visual Studio project file
-- corresponding to a particular project object.
@ -386,7 +398,7 @@
extension = iif(_ACTION > "vs2008", ".vcxproj", ".vcproj")
end
return project.getfilename(prj, extension)
return premake.filename(prj, extension)
end
@ -563,3 +575,4 @@
end
end

View File

@ -467,7 +467,7 @@
function cs2005.xmlDeclaration()
if _ACTION > "vs2008" then
_p('<?xml version="1.0" encoding="utf-8"?>')
p.xmlUtf8()
end
end

View File

@ -14,7 +14,7 @@
--
function cs2005.generate_user(prj)
_p('<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">')
p.vstudio.projectElement()
_p(1,'<PropertyGroup>')
-- Per-configuration reference paths aren't supported (are they?) so just

View File

@ -39,6 +39,22 @@
---
-- Generate the .props, .targets, and .xml files for custom rules.
---
function vs2010.generateRule(rule)
p.eol("\r\n")
p.indent(" ")
p.escaper(vs2010.esc)
p.generate(rule, ".props", vs2010.rules.props.generate)
p.generate(rule, ".targets", vs2010.rules.targets.generate)
p.generate(rule, ".xml", vs2010.rules.xml.generate)
end
--
-- The VS 2010 standard for XML escaping in generated project files.
--
@ -80,6 +96,7 @@
onsolution = vstudio.vs2005.generateSolution,
onproject = vstudio.vs2010.generateProject,
onrule = vstudio.vs2010.generateRule,
oncleansolution = vstudio.cleanSolution,
oncleanproject = vstudio.cleanProject,

View File

@ -0,0 +1,176 @@
---
-- vs2010_rules_props.lua
-- Generate a Visual Studio 201x custom rules properties file.
-- Copyright (c) 2014 Jason Perkins and the Premake project
--
premake.vstudio.vs2010.rules = {}
premake.vstudio.vs2010.rules.props = {}
local m = premake.vstudio.vs2010.rules.props
m.elements = {}
local p = premake
---
-- Entry point; generate the root <Project> element.
---
m.elements.project = function(r)
return {
p.xmlUtf8,
p.vstudio.projectElement,
m.targetsGroup,
m.dependsOnGroup,
m.ruleGroup,
}
end
function m.generate(r)
p.callArray(m.elements.project, r)
p.pop()
p.out('</Project>')
end
---
-- Generate the targets property group element.
---
m.elements.targetsGroup = function(r)
return {
m.beforeTargets,
m.afterTargets,
}
end
function m.targetsGroup(r)
p.w('<PropertyGroup')
p.push(' Condition="\'$(%sBeforeTargets)\' == \'\' and \'$(%sAfterTargets)\' == \'\' and \'$(ConfigurationType)\' != \'Makefile\'">',
r.name, r.name)
p.callArray(m.elements.targetsGroup, r)
p.pop('</PropertyGroup>')
end
---
-- Generate the dependencies property group element.
---
m.elements.dependsOnGroup = function(r)
return {
m.dependsOn,
}
end
function m.dependsOnGroup(r)
p.push('<PropertyGroup>')
p.callArray(m.elements.dependsOnGroup, r)
p.pop('</PropertyGroup>')
end
---
-- Generate the rule itemm group element.
---
m.elements.ruleGroup = function(r)
return {
m.propertyDefaults,
m.commandLineTemplates,
m.outputs,
m.executionDescription,
m.additionalDependencies,
}
end
function m.ruleGroup(r)
p.push('<ItemDefinitionGroup>')
p.push('<%s>', r.name)
p.callArray(m.elements.ruleGroup, r)
p.pop('</%s>', r.name)
p.pop('</ItemDefinitionGroup>')
end
---
-- Output the default values for all of the property definitions.
---
function m.propertyDefaults(r)
local defs = r.propertyDefinition
for i = 1, #defs do
local def = defs[i]
local value = def.value
if value then
if def.kind == "path" then
value = path.translate(value)
end
p.w('<%s>%s</%s>', def.name, value, def.name)
end
end
end
---
-- Implementations of individual elements.
---
function m.additionalDependencies(r)
local deps = table.concat(r.buildDependencies, ";")
p.x('<AdditionalDependencies>%s</AdditionalDependencies>', deps)
end
function m.afterTargets(r)
p.w('<%sAfterTargets>CustomBuild</%sAfterTargets>', r.name, r.name)
end
function m.beforeTargets(r)
p.w('<%sBeforeTargets>Midl</%sBeforeTargets>', r.name, r.name)
end
function m.commandLineTemplates(r)
if #r.buildcommands then
local cmds = table.concat(r.buildcommands, p.eol())
p.x('<CommandLineTemplate>%s</CommandLineTemplate>', cmds)
end
end
function m.dependsOn(r)
p.w('<%sDependsOn', r.name)
p.w(' Condition="\'$(ConfigurationType)\' != \'Makefile\'">_SelectedFiles;$(%sDependsOn)</%sDependsOn>',
r.name, r.name, r.name)
end
function m.executionDescription(r)
if r.buildmessage then
p.x('<ExecutionDescription>%s</ExecutionDescription>', r.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

@ -0,0 +1,443 @@
---
-- vs2010_rules_targets.lua
-- Generate a Visual Studio 201x custom rules targets file.
-- Copyright (c) 2014 Jason Perkins and the Premake project
---
premake.vstudio.vs2010.rules.targets = {}
local m = premake.vstudio.vs2010.rules.targets
m.elements = {}
local p = premake
---
-- Entry point; generate the root <Project> element.
---
m.elements.project = function(r)
return {
p.xmlUtf8,
p.vstudio.projectElement,
m.availableItemGroup,
m.computeInputsGroup,
m.usingTask,
m.ruleTarget,
m.computeOutputTarget,
}
end
function m.generate(r)
p.callArray(m.elements.project, r)
p.pop()
p.out('</Project>')
end
---
-- Generate the opening item group element.
---
m.elements.availableItemGroup = function(r)
return {
m.propertyPageSchema,
m.availableItemName,
}
end
function m.availableItemGroup(r)
p.push('<ItemGroup>')
p.callArray(m.elements.availableItemGroup, r)
p.pop('</ItemGroup>')
end
---
-- Generate the computed input targets group.
---
m.elements.computeInputsGroup = function(r)
return {
m.computeLinkInputsTargets,
m.computeLibInputsTargets,
}
end
function m.computeInputsGroup(r)
p.push('<PropertyGroup>')
p.callArray(m.elements.computeInputsGroup, r)
p.pop('</PropertyGroup>')
end
---
-- Generate the rule's target element.
---
m.elements.ruleTargetAttributes = function(r)
return {
m.targetName,
m.beforeTargets,
m.afterTargets,
m.targetCondition,
m.targetOutputs,
m.targetInputs,
m.dependsOnTargets,
}
end
m.elements.ruleTarget = function(r)
return {
m.selectedFiles,
m.tlog,
m.message,
m.tlogWrite,
m.tlogRead,
m.rule,
}
end
function m.ruleTarget(r)
local attribs = p.capture(function()
p.push()
p.callArray(m.elements.ruleTargetAttributes, r)
p.pop()
end)
p.push('<Target')
p.outln(attribs .. '>')
p.callArray(m.elements.ruleTarget, r)
p.pop('</Target>')
end
---
-- Write out the tlog entries. I've extended this with an input
-- dependencies fix as described here:
-- http://www.virtualdub.org/blog/pivot/entry.php?id=334
---
m.elements.tlog = function(r)
return {
m.tlogSource,
m.tlogInputs,
m.tlogProperties,
}
end
function m.tlog(r)
p.push('<ItemGroup>')
p.push('<%s_tlog', r.name)
p.w('Include="%%(%s.Outputs)"', r.name)
p.w('Condition="\'%%(%s.Outputs)\' != \'\' and \'%%(%s.ExcludedFromBuild)\' != \'true\'">', r.name, r.name)
p.callArray(m.elements.tlog, r)
p.pop('</%s_tlog>', r.name)
p.pop('</ItemGroup>')
end
---
-- Write out the rule element.
---
m.elements.ruleAttributes = function(r)
return {
m.ruleCondition,
m.commandLineTemplate,
m.properties,
m.additionalOptions,
m.inputs,
m.standardOutputImportance,
}
end
function m.rule(r)
local attribs = p.capture(function()
p.push()
p.callArray(m.elements.ruleAttributes, r)
p.pop()
end)
p.w('<%s', r.name)
p.outln(attribs .. ' />')
end
---
-- Generate the rule's computed output element.
---
m.elements.computeOutputItems = function(r)
return {
m.outputs,
m.linkLib,
}
end
m.elements.computeOutputTarget = function(r)
return {
m.makeDir,
}
end
function m.computeOutputTarget(r)
p.push('<Target')
p.w('Name="Compute%sOutput"', r.name)
p.w('Condition="\'@(%s)\' != \'\'">', r.name)
p.push('<ItemGroup>')
p.callArray(m.elements.computeOutputItems, r)
p.pop('</ItemGroup>')
p.callArray(m.elements.computeOutputTarget, r)
p.pop('</Target>')
end
---
-- Implementations of individual elements.
---
function m.additionalOptions(r)
p.w('AdditionalOptions="%%(%s.AdditionalOptions)"', r.name)
end
function m.commandLineTemplate(r)
p.w('CommandLineTemplate="%%(%s.CommandLineTemplate)"', r.name)
end
function m.afterTargets(r)
p.w('AfterTargets="$(%sAfterTargets)"', r.name)
end
function m.availableItemName(r)
p.push('<AvailableItemName Include="%s">', r.name)
p.w('<Targets>_%s</Targets>', r.name)
p.pop('</AvailableItemName>')
end
function m.beforeTargets(r)
p.w('BeforeTargets="$(%sBeforeTargets)"', r.name)
end
function m.computeLibInputsTargets(r)
p.push('<ComputeLibInputsTargets>')
p.w('$(ComputeLibInputsTargets);')
p.w('Compute%sOutput;', r.name)
p.pop('</ComputeLibInputsTargets>')
end
function m.computeLinkInputsTargets(r)
p.push('<ComputeLinkInputsTargets>')
p.w('$(ComputeLinkInputsTargets);')
p.w('Compute%sOutput;', r.name)
p.pop('</ComputeLinkInputsTargets>')
end
function m.dependsOnTargets(r)
p.w('DependsOnTargets="$(%sDependsOn);Compute%sOutput"', r.name, r.name)
end
function m.inputs(r)
p.w('Inputs="%%(%s.Identity)"', r.name)
end
function m.linkLib(r)
local linkable
for i = 1, #r.buildoutputs do
if (path.islinkable(r.buildoutputs[i])) then
linkable = true
end
end
if linkable then
for i, el in pairs { 'Link', 'Lib', 'ImpLib' } do
p.push('<%s', el)
p.w('Include="%%(%sOutputs.Identity)"', r.name)
p.w('Condition="\'%%(Extension)\'==\'.obj\' or \'%%(Extension)\'==\'.res\' or \'%%(Extension)\'==\'.rsc\' or \'%%(Extension)\'==\'.lib\'" />')
p.pop()
end
end
end
function m.makeDir(r)
p.w('<MakeDir Directories="@(%sOutputs->\'%%(RootDir)%%(Directory)\')" />', r.name)
end
function m.message(r)
p.w('<Message')
p.w(' Importance="High"')
p.w(' Text="%%(%s.ExecutionDescription)" />', r.name)
end
function m.outputs(r)
p.w('<%sOutputs', r.name)
p.w(' Condition="\'@(%s)\' != \'\' and \'%%(%s.ExcludedFromBuild)\' != \'true\'"', r.name, r.name)
p.w(' Include="%%(%s.Outputs)" />', r.name)
end
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
function m.propertyPageSchema(r)
p.w('<PropertyPageSchema')
p.w(' Include="$(MSBuildThisFileDirectory)$(MSBuildThisFileName).xml" />')
end
function m.ruleCondition(r)
p.w('Condition="\'@(%s)\' != \'\' and \'%%(%s.ExcludedFromBuild)\' != \'true\'"', r.name, r.name)
end
function m.selectedFiles(r)
p.push('<ItemGroup Condition="\'@(SelectedFiles)\' != \'\'">')
p.w('<%s Remove="@(%s)" Condition="\'%%(Identity)\' != \'@(SelectedFiles)\'" />', r.name, r.name)
p.pop('</ItemGroup>')
end
function m.standardOutputImportance(r)
p.w('StandardOutputImportance="High"')
p.w('StandardErrorImportance="High"')
end
function m.targetCondition(r)
p.w('Condition="\'@(%s)\' != \'\'"', r.name)
end
function m.targetInputs(r)
local extra = {}
local defs = r.propertyDefinition
for i = 1, #defs do
local def = defs[i]
if def.dependency then
table.insert(extra, string.format("%%(%s.%s);", r.name, def.name))
end
end
extra = table.concat(extra)
p.w('Inputs="%%(%s.Identity);%%(%s.AdditionalDependencies);%s$(MSBuildProjectFile)"', r.name, r.name, extra)
end
function m.targetName(r)
p.w('Name="_%s"', r.name)
end
function m.targetOutputs(r)
p.w('Outputs="%%(%s.Outputs)"', r.name)
end
function m.tlogInputs(r)
p.w("<Inputs>@(%s, ';')</Inputs>", r.name)
end
function m.tlogProperties(r)
local defs = r.propertyDefinition
for i = 1, #defs do
local def = defs[i]
if def.dependency then
p.w('<%s>%%(%s.%s)</%s>', def.name, r.name, def.name, def.name)
end
end
end
function m.tlogRead(r)
local extra = {}
local defs = r.propertyDefinition
for i = 1, #defs do
local def = defs[i]
if def.dependency then
table.insert(extra, string.format("%%(%s_tlog.%s);", r.name, def.name))
end
end
extra = table.concat(extra)
p.w('<WriteLinesToFile')
p.w(' Condition="\'@(%s_tlog)\' != \'\' and \'%%(%s_tlog.ExcludedFromBuild)\' != \'true\'"', r.name, r.name)
p.w(' File="$(IntDir)$(ProjectName).read.1.tlog"')
p.w(' Lines="^%%(%s_tlog.Inputs);%s$(MSBuildProjectFullPath);%%(%s_tlog.Fullpath)" />', r.name, extra, r.name)
end
function m.tlogWrite(r)
p.w('<WriteLinesToFile')
p.w(' Condition="\'@(%s_tlog)\' != \'\' and \'%%(%s_tlog.ExcludedFromBuild)\' != \'true\'"', r.name, r.name)
p.w(' File="$(IntDir)$(ProjectName).write.1.tlog"')
p.w(' Lines="^%%(%s_tlog.Source);%%(%s_tlog.Fullpath)" />', r.name, r.name)
end
function m.tlogSource(r)
p.w("<Source>@(%s, '|')</Source>", r.name)
end
function m.usingTask(r)
p.push('<UsingTask')
p.w('TaskName="%s"', r.name)
p.w('TaskFactory="XamlTaskFactory"')
p.w('AssemblyName="Microsoft.Build.Tasks.v4.0">')
p.w('<Task>$(MSBuildThisFileDirectory)$(MSBuildThisFileName).xml</Task>')
p.pop('</UsingTask>')
end

View File

@ -0,0 +1,392 @@
---
-- vs2010_rules_xml.lua
-- Generate a Visual Studio 201x custom rules XML file.
-- Copyright (c) 2014 Jason Perkins and the Premake project
--
premake.vstudio.vs2010.rules.xml = {}
local m = premake.vstudio.vs2010.rules.xml
m.elements = {}
local p = premake
---
-- Entry point; generate the root <ProjectSchemaDefinitions> element.
---
m.elements.project = function(r)
return {
p.xmlUtf8,
m.projectSchemaDefinitions,
m.rule,
m.ruleItem,
m.fileExtension,
m.contentType,
}
end
function m.generate(r)
p.callArray(m.elements.project, r)
p.out('</ProjectSchemaDefinitions>')
end
---
-- Generate the main <Rule> element.
---
m.elements.rule = function(r)
return {
m.dataSource,
m.categories,
m.inputs,
m.properties,
m.commandLineTemplate,
m.beforeTargets,
m.afterTargets,
m.outputs,
m.executionDescription,
m.additionalDependencies,
m.additionalOptions,
}
end
function m.rule(r)
p.push('<Rule')
p.w('Name="%s"', r.name)
p.w('PageTemplate="tool"')
p.w('DisplayName="%s"', r.display or r.name)
p.w('Order="200">')
p.callArray(m.elements.rule, r)
p.pop('</Rule>')
end
---
-- Generate the list of categories.
---
function m.categories(r)
local categories = {
[1] = { name="General" },
[2] = { name="Command Line", subtype="CommandLine" },
}
p.push('<Rule.Categories>')
for i = 1, #categories do
m.category(categories[i])
end
p.pop('</Rule.Categories>')
end
function m.category(cat)
local attribs = p.capture(function()
p.push()
p.w('Name="%s"', cat.name)
if cat.subtype then
p.w('Subtype="%s"', cat.subtype)
end
p.pop()
end)
p.push('<Category')
p.outln(attribs .. '>')
p.push('<Category.DisplayName>')
p.w('<sys:String>%s</sys:String>', cat.name)
p.pop('</Category.DisplayName>')
p.pop('</Category>')
end
---
-- Generate the list of property definitions.
---
function m.properties(r)
local defs = r.propertyDefinition
for i = 1, #defs do
local def = defs[i]
if def.kind == "boolean" then
m.boolProperty(def)
elseif def.kind == "list" then
m.stringListProperty(def)
elseif type(def.values) == "table" then
m.enumProperty(def)
else
m.stringProperty(def)
end
end
end
function m.baseProperty(def, suffix)
local c = p.capture(function ()
p.w('Name="%s"', def.name)
p.w('HelpContext="0"')
p.w('DisplayName="%s"', def.display or def.name)
if def.description then
p.w('Description="%s"', def.description)
end
end)
if suffix then
c = c .. suffix
end
p.outln(c)
end
function m.boolProperty(def)
p.push('<BoolProperty')
m.baseProperty(def)
p.w('Switch="%s" />', def.switch or "[value]")
p.pop()
end
function m.enumProperty(def)
p.push('<EnumProperty')
m.baseProperty(def, '>')
local values = def.values
local switches = def.switch or {}
local keys = table.keys(def.values)
table.sort(keys)
for _, key in pairs(keys) do
p.push('<EnumValue')
p.w('Name="%d"', key)
if switches[key] then
p.w('DisplayName="%s"', values[key])
p.w('Switch="%s" />', switches[key])
else
p.w('DisplayName="%s" />', values[key])
end
p.pop()
end
p.pop('</EnumProperty>')
end
function m.stringProperty(def)
p.push('<StringProperty')
m.baseProperty(def)
p.w('Switch="%s" />', def.switch or "[value]")
p.pop()
end
function m.stringListProperty(def)
p.push('<StringListProperty')
m.baseProperty(def)
p.w('Separator="%s"', def.separator or " ")
p.w('Switch="%s" />', def.switch or "[value]")
p.pop()
end
---
-- Implementations of individual elements.
---
function m.additionalDependencies(r)
p.push('<StringListProperty')
p.w('Name="AdditionalDependencies"')
p.w('DisplayName="Additional Dependencies"')
p.w('IncludeInCommandLine="False"')
p.w('Visible="false" />')
p.pop()
end
function m.additionalOptions(r)
p.push('<StringProperty')
p.w('Subtype="AdditionalOptions"')
p.w('Name="AdditionalOptions"')
p.w('Category="Command Line">')
p.push('<StringProperty.DisplayName>')
p.w('<sys:String>Additional Options</sys:String>')
p.pop('</StringProperty.DisplayName>')
p.push('<StringProperty.Description>')
p.w('<sys:String>Additional Options</sys:String>')
p.pop('</StringProperty.Description>')
p.pop('</StringProperty>')
end
function m.afterTargets(r)
p.push('<DynamicEnumProperty')
p.w('Name="%sAfterTargets"', r.name)
p.w('Category="General"')
p.w('EnumProvider="Targets"')
p.w('IncludeInCommandLine="False">')
p.push('<DynamicEnumProperty.DisplayName>')
p.w('<sys:String>Execute After</sys:String>')
p.pop('</DynamicEnumProperty.DisplayName>')
p.push('<DynamicEnumProperty.Description>')
p.w('<sys:String>Specifies the targets for the build customization to run after.</sys:String>')
p.pop('</DynamicEnumProperty.Description>')
p.push('<DynamicEnumProperty.ProviderSettings>')
p.push('<NameValuePair')
p.w('Name="Exclude"')
p.w('Value="^%sAfterTargets|^Compute" />', r.name)
p.pop()
p.pop('</DynamicEnumProperty.ProviderSettings>')
p.push('<DynamicEnumProperty.DataSource>')
p.push('<DataSource')
p.w('Persistence="ProjectFile"')
p.w('ItemType=""')
p.w('HasConfigurationCondition="true" />')
p.pop()
p.pop('</DynamicEnumProperty.DataSource>')
p.pop('</DynamicEnumProperty>')
end
function m.beforeTargets(r)
p.push('<DynamicEnumProperty')
p.w('Name="%sBeforeTargets"', r.name)
p.w('Category="General"')
p.w('EnumProvider="Targets"')
p.w('IncludeInCommandLine="False">')
p.push('<DynamicEnumProperty.DisplayName>')
p.w('<sys:String>Execute Before</sys:String>')
p.pop('</DynamicEnumProperty.DisplayName>')
p.push('<DynamicEnumProperty.Description>')
p.w('<sys:String>Specifies the targets for the build customization to run before.</sys:String>')
p.pop('</DynamicEnumProperty.Description>')
p.push('<DynamicEnumProperty.ProviderSettings>')
p.push('<NameValuePair')
p.w('Name="Exclude"')
p.w('Value="^%sBeforeTargets|^Compute" />', r.name)
p.pop()
p.pop('</DynamicEnumProperty.ProviderSettings>')
p.push('<DynamicEnumProperty.DataSource>')
p.push('<DataSource')
p.w('Persistence="ProjectFile"')
p.w('HasConfigurationCondition="true" />')
p.pop()
p.pop('</DynamicEnumProperty.DataSource>')
p.pop('</DynamicEnumProperty>')
end
function m.commandLineTemplate(r)
p.push('<StringProperty')
p.w('Name="CommandLineTemplate"')
p.w('DisplayName="Command Line"')
p.w('Visible="False"')
p.w('IncludeInCommandLine="False" />')
p.pop()
end
function m.contentType(r)
p.push('<ContentType')
p.w('Name="%s"', r.name)
p.w('DisplayName="%s"', r.display or r.name)
p.w('ItemType="%s" />', r.name)
p.pop()
end
function m.dataSource(r)
p.push('<Rule.DataSource>')
p.push('<DataSource')
p.w('Persistence="ProjectFile"')
p.w('ItemType="%s" />', r.name)
p.pop()
p.pop('</Rule.DataSource>')
end
function m.executionDescription(r)
p.push('<StringProperty')
p.w('Name="ExecutionDescription"')
p.w('DisplayName="Execution Description"')
p.w('Visible="False"')
p.w('IncludeInCommandLine="False" />')
p.pop()
end
function m.fileExtension(r)
p.push('<FileExtension')
p.w('Name="*%s"', r.fileExtension)
p.w('ContentType="%s" />', r.name)
p.pop()
end
function m.inputs(r)
p.push('<StringListProperty')
p.w('Name="Inputs"')
p.w('Category="Command Line"')
p.w('IsRequired="true"')
p.w('Switch=" ">')
p.push('<StringListProperty.DataSource>')
p.push('<DataSource')
p.w('Persistence="ProjectFile"')
p.w('ItemType="%s"', r.name)
p.w('SourceType="Item" />')
p.pop()
p.pop('</StringListProperty.DataSource>')
p.pop('</StringListProperty>')
end
function m.outputs(r)
p.push('<StringListProperty')
p.w('Name="Outputs"')
p.w('DisplayName="Outputs"')
p.w('Visible="False"')
p.w('IncludeInCommandLine="False" />')
p.pop()
end
function m.ruleItem(r)
p.push('<ItemType')
p.w('Name="%s"', r.name)
p.w('DisplayName="%s" />', r.display or r.name)
p.pop()
end
function m.projectSchemaDefinitions(r)
p.push('<ProjectSchemaDefinitions xmlns="http://schemas.microsoft.com/build/2009/properties" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:sys="clr-namespace:System;assembly=mscorlib">')
end

View File

@ -47,7 +47,7 @@
function m.generate(prj)
io.utf8()
m.xmlDeclaration()
m.xmlDeclaration()
m.project("Build")
p.callArray(m.elements.project, prj)
p.out('</Project>')
@ -1583,8 +1583,9 @@
end
function m.xmlDeclaration()
_p('<?xml version="1.0" encoding="utf-8"?>')
p.xmlUtf8()
end

View File

@ -1,10 +1,14 @@
--
---
-- action.lua
-- Work with the list of registered actions.
-- Copyright (c) 2002-2009 Jason Perkins and the Premake project
--
-- Copyright (c) 2002-2014 Jason Perkins and the Premake project
---
premake.action = {}
local action = premake.action
local p = premake
--
@ -32,32 +36,32 @@
-- new entries here.
--
premake.action.list = {}
action._list = {}
--
-- Register a new action.
--
-- @param a
-- @param act
-- The new action object.
--
function premake.action.add(a)
function action.add(act)
-- validate the action object, at least a little bit
local missing
for _, field in ipairs({"description", "trigger"}) do
if not a[field] then
if not act[field] then
missing = field
end
end
if missing then
local name = a.trigger or ""
local name = act.trigger or ""
error(string.format('action "%s" needs a %s', name, missing), 3)
end
-- add it to the master list
premake.action.list[a.trigger] = a
action._list[act.trigger] = act
end
@ -70,28 +74,28 @@
-- None.
--
function premake.action.call(name)
local a = premake.action.list[name]
function action.call(name)
local act = action._list[name]
-- TODO: remove this once everyone's had a chance to see
-- the deprecation warning
if _ACTION:endswith("ng") then
_ACTION = _ACTION:sub(1, -3)
end
for sln in premake.solution.each() do
if a.onsolution then
a.onsolution(sln)
for sln in p.solution.each() do
if act.onsolution then
act.onsolution(sln)
end
for prj in premake.solution.eachproject(sln) do
if a.onproject and not prj.external then
a.onproject(prj)
for prj in p.solution.eachproject(sln) do
if act.onproject and not prj.external then
act.onproject(prj)
end
end
end
if a.execute then
a.execute()
for rule in p.rules.each() do
if act.onrule then
act.onrule(rule)
end
end
if act.execute then
act.execute()
end
end
@ -103,8 +107,8 @@
-- The current action, or nil if _ACTION is nil or does not match any action.
--
function premake.action.current()
return premake.action.get(_ACTION)
function action.current()
return action.get(_ACTION)
end
@ -117,13 +121,12 @@
-- The requested action, or nil if the action does not exist.
--
function premake.action.get(name)
function action.get(name)
-- "Next-gen" actions are deprecated
if name and name:endswith("ng") then
name = name:sub(1, -3)
end
return premake.action.list[name]
return action._list[name]
end
@ -131,18 +134,18 @@
-- Iterator for the list of actions.
--
function premake.action.each()
function action.each()
-- sort the list by trigger
local keys = { }
for _, action in pairs(premake.action.list) do
table.insert(keys, action.trigger)
for _, act in pairs(action._list) do
table.insert(keys, act.trigger)
end
table.sort(keys)
local i = 0
return function()
i = i + 1
return premake.action.list[keys[i]]
return act._list[keys[i]]
end
end
@ -154,13 +157,13 @@
-- The name of the action to activate.
--
function premake.action.set(name)
function action.set(name)
_ACTION = name
-- Some actions imply a particular operating system
local action = premake.action.get(name)
if action then
_OS = action.os or _OS
local act = action.get(name)
if act then
_OS = act.os or _OS
end
end
@ -168,7 +171,7 @@
--
-- Determines if an action supports a particular language or target type.
--
-- @param action
-- @param act
-- The action to test.
-- @param feature
-- The feature to check, either a programming language or a target type.
@ -176,17 +179,17 @@
-- True if the feature is supported, false otherwise.
--
function premake.action.supports(action, feature)
if not action then
function action.supports(act, feature)
if not act then
return false
end
if action.valid_languages then
if table.contains(action.valid_languages, feature) then
if act.valid_languages then
if table.contains(act.valid_languages, feature) then
return true
end
end
if action.valid_kinds then
if table.contains(action.valid_kinds, feature) then
if act.valid_kinds then
if table.contains(act.valid_kinds, feature) then
return true
end
end
@ -194,19 +197,20 @@
end
--
-- Determines if an action supports a particular configuration.
-- @return
-- True if the configuration is supported, false otherwise.
--
function premake.action.supportsconfig(action, cfg)
if not action then
function action.supportsconfig(act, cfg)
if not act then
return false
end
if action.supportsconfig then
return action.supportsconfig(cfg)
if act.supportsconfig then
return act.supportsconfig(cfg)
end
return true

View File

@ -6,24 +6,180 @@
premake.api = {}
local api = premake.api
local configset = premake.configset
local p = premake
local configset = p.configset
---
-- Set up a place to store the current active objects in each configuration
-- scope (e.g. solutions, projects, groups, and configurations). Initialize
-- it with a "root" container to contain the other containers, as well as the
-- global configuration settings which should apply to all of them.
--
-- Create a "root" configuration set, to hold the global configuration. Values
-- that are added to this set become available for all add-ons, solution, projects,
-- and on down the line.
-- TODO: this should be hidden, with a perhaps a read-only accessor to fetch
-- individual scopes for testing.
---
api.scope = {}
api.scope.root = p.containerClass.define("root"):new("root")
---
-- Register a new class of configuration container. A configuration container
-- can hold configuration settings, as well as child configuration containers.
-- Solutions, projects, and rules are all examples of containers.
--
-- @param def
-- The container definition; see premake.container.define().
-- @returns
-- The newly defined container class.
---
configset.root = configset.new()
function api.container(def)
-- for now, everything inherits the root configuration
if not def.parent then
def.parent = "root"
end
-- register the new class; validation checks may cause a failure
local cc, err = p.containerClass.define(def)
if not cc then
error(err, 2)
end
-- create a global function to create new instances, e.g project()
_G[cc.name] = function(name)
return api._setScope(cc, name)
end
return cc
end
---
-- Return the currently active configuration container instance.
---
function api.currentContainer()
return api.scope.current or api.scope.root
end
---
-- Return the root container, which contains the global configuration and
-- all of the child containers (solutions, rules).
---
function api.rootContainer()
return api.scope.root
end
---
-- Recursively clear any child scopes for a container class. For example,
-- if a solution is being activated, clear the project and group scopes,
-- as those are children of solutions. If the root scope is made active,
-- all child scopes will be cleared.
---
function api._clearChildScopes(cc)
for ch in cc:eachChildClass() do
api.scope[ch.name] = nil
api._clearChildScopes(ch)
end
end
---
-- Activate a new configuration container, making it the target for all
-- subsequent configuration settings. When you call solution() or project()
-- to active a container, that call comes here (see api.container() for the
-- details on how that happens).
--
-- A place to store the current active objects in each configuration scope
-- (e.g. solutions, projects, groups, and configurations).
--
-- @param cc
-- The container class being activated, e.g. a project or solution.
-- @param name
-- The name of the container instance to be activated. If a container
-- (e.g. project) with this name does not already exist it will be
-- created. If name is not set, the last activated container of this
-- class will be made current again.
-- @return
-- The container instance.
---
function api._setScope(cc, name)
-- for backward compatibility, "*" activates the parent container
if name == "*" then
return api._setScope(cc.parent)
end
-- if name is not set, use whatever was last made current
local container
if not name then
container = api.scope[cc.name]
if not container then
error("no " .. cc.name .. " in scope", 3)
end
end
if not container then
-- all containers should be contained within a parent
local parent = api.scope[cc.parent.name]
if not parent then
error("no active " .. cc.parent.name, 3)
end
-- fetch (creating if necessary) the container
container = parent:fetchChild(cc, name)
if not container then
container = parent:createChild(cc, name, parent)
else
configset.addFilter(container, {}, os.getcwd())
end
end
-- clear out any active child container types
api._clearChildScopes(cc)
-- activate the container, as well as its ancestors
if not cc.placeholder then
api.scope.current = container
end
while container.parent do
api.scope[container.class.name] = container
container = container.parent
end
return api.scope.current
end
---
-- Find the closest active scope for the given container class. If no
-- exact instance of this container class is in scope, searches up the
-- class hierarchy to find the closest parent that is in scope.
--
-- @param cc
-- The container class to target.
-- @return
-- The closest available active container instance.
---
function api._target(cc)
while not api.scope[cc.name] do
cc = cc.parent
end
return api.scope[cc.name]
end
api.scope = { root = configset.root }
---
@ -292,14 +448,47 @@
---
-- Find the currently active configuration scope.
-- Return the target container instance for a field.
--
-- @param field
-- The field being set or fetched.
-- @return
-- The currently active configuration set object.
-- The currently active container instance if one is available, or nil if
-- active container is of the wrong class.
---
function api.gettarget()
return api.scope.project or api.scope.solution or api.scope.root
function api.target(field)
-- if nothing is in scope, use the global root scope
if not api.scope.current then
return api.scope.root
end
-- use the current scope if it falls within the container hierarchy
-- of one of this field's target scopes; it is okay to set a value of
-- the parent of container (e.g. a project-level value can be set on a
-- solution, since it will be inherited).
local currentClass = api.scope.current.class
for i = 1, #field.scopes do
-- temporary: map config scope to the desired container class; will
-- go away once everything is ported to containers
local targetScope = field.scopes[i]
if targetScope == "config" then
targetScope = "project"
end
local targetClass = p.containerClass.get(targetScope)
repeat
if currentClass == targetClass then
return api.scope.current
end
targetClass = targetClass.parent
until not targetClass
end
-- this field's scope isn't available
return nil
end
@ -322,7 +511,12 @@
end
end
local target = api.gettarget()
local target = api.target(field)
if not target then
local err = string.format("unable to set %s in %s scope, should be %s", field.name, api.scope.current.class.name, table.concat(field.scopes, ", "))
error(err, 3)
end
local status, err = configset.store(target, field, value)
if err then
error(err, 3)
@ -342,7 +536,12 @@
-- return the current baked value
if not value then return end
local target = api.gettarget()
local target = api.target(field)
if not target then
local err = string.format("unable to remove %s from %s scope, should be %s", field.name, api.scope.current.class.name, table.concat(field.scopes, ", "))
error(err, 3)
end
local hasDeprecatedValues = (type(field.deprecated) == "table")
-- Build a list of values to be removed. If this field has deprecated
@ -478,6 +677,9 @@
---
function api.reset()
-- Clear out all top level objects
api.scope.root.solutions = {}
-- Remove all custom variables
local vars = api.getCustomVars()
for i, var in ipairs(vars) do
@ -854,7 +1056,7 @@
---
function configuration(terms)
local target = api.gettarget()
local target = api.currentContainer()
if terms then
if terms == "*" then terms = nil end
configset.addblock(target, {terms}, os.getcwd())
@ -870,7 +1072,7 @@
---
function filter(terms)
local target = api.gettarget()
local target = api.currentContainer()
if terms then
if terms == "*" then terms = nil end
local ok, err = configset.addFilter(target, {terms}, os.getcwd())
@ -882,60 +1084,6 @@
---
-- Begin a new solution group, which will contain any subsequent projects.
---
function group(name)
if name == "*" then name = nil end
api.scope.group = name
end
--
-- Set the current configuration scope to a 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 project(name)
if not name then
if api.scope.project then
name = api.scope.project.name
else
return nil
end
end
local sln = api.scope.solution
if not sln then
error("no active solution", 2)
end
local prj
if name ~= "*" then
prj = sln.projects[name]
if not prj then
prj = premake.project.new(sln, name)
prj.group = api.scope.group or ""
premake.solution.addproject(sln, prj)
end
end
api.scope.project = prj
configuration {}
return prj
end
--
-- Activates a reference to an external, non-Premake generated project.
--
@ -955,41 +1103,6 @@
end
--
-- Set the current configuration scope to a solution.
--
-- @param name
-- The name of the solution. If a solution with this name already
-- exists, it is made current, otherwise a new solution is created
-- with this name. If no name is provided, the most recently defined
-- solution is made active.
-- @return
-- The active solution object.
--
function solution(name)
if not name then
if api.scope.solution then
name = api.scope.solution.name
else
return nil
end
end
local sln
if name ~= "*" then
sln = premake.solution.get(name) or premake.solution.new(name)
end
api.scope.solution = sln
api.scope.project = nil
api.scope.group = nil
configuration {}
return sln
end
--
-- Define a new action.
@ -1124,7 +1237,12 @@
}
end
local cset = api.gettarget()
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)

245
src/base/container.lua Normal file
View File

@ -0,0 +1,245 @@
---
-- container.lua
-- Implementation of configuration containers.
-- Copyright (c) 2014 Jason Perkins and the Premake project
---
local p = premake
p.containerClass = {}
p.container = {}
local container = p.container
-- The master list of registered container classes
container._classes = {}
---
-- The metatable allows container functions to be called with the ":" syntax,
-- and also allows API field values to be get and set as if they were direct
-- properties.
--
-- TODO: I think I'd like to get away from treating the fields as direct
-- properties on the containers (fine on the baked contexts later) and require
-- explicit fetch() and store() calls instead.
---
p.containerClass.__index = p.containerClass
p.container.__index = function(c, key)
local f = p.field.get(key)
if f then
return p.configset.fetch(c, f)
else
return p.container[key]
end
end
p.container.__newindex = function(c, key, value)
local f = p.field.get(key)
if f then
local status, err = p.configset.store(c, f, value)
if err then
error(err, 2)
end
else
rawset(c, key, value)
return value
end
end
---
-- A container class holds and operates on the metadata about a particular
-- type of container, including its name, parent container, and any child
-- containers. The container class is responsible for creating new instances
-- of its kind of containers.
--
-- @param def
-- A table containing metadata about the container class being created.
-- Supported keys are:
--
-- name (required)
-- The name of the new container class (e.g. "solution").
-- parent (optional)
-- The name of the parent container class (e.g. "solution").
-- init (optional)
-- An initializer function to call for new instances of this class.
-- Should accept the new instance object as its only argument.
--
-- Other keys are allowed and will be left intact.
--
-- @return
-- A new container class object if successful, else nil and an
-- error message.
---
function p.containerClass.define(def)
-- If the class has no special properties, allow it to be set using
-- just the class name instead of the whole key-value list.
if type(def) == "string" then
def = { name = def }
end
-- Sanity check my inputs
if not def.name then
return nil, "name is required"
end
if container._classes[def.name] then
return nil, "container class name already in use"
end
if def.parent and not container._classes[def.parent] then
return nil, "parent class does not exist"
end
-- Looks good, set myself up and add to master list
def._children = {}
def._listKey = def.name:plural()
setmetatable(def, p.containerClass)
container._classes[def.name] = def
-- Wire myself to my parent class
def.parent = container._classes[def.parent]
if def.parent then
table.insert(def.parent._children, def)
end
return def
end
---
-- Enumerate the child container class of a given class.
---
function p.containerClass:eachChildClass()
local children = self._children
local i = 0
return function ()
i = i + 1
if i <= #children then
return children[i]
end
end
end
---
-- Retrieve a container class by name.
--
-- @param name
-- The class name.
-- @return
-- The corresponding container class if found, nil otherwise.
---
function p.containerClass.get(name)
return container._classes[name]
end
---
-- Create a new instance of a container class.
--
-- @param name
-- The name for the container instance.
-- @return
-- A new instance of the container class.
---
function p.containerClass:new(name, parent)
local c = p.configset.new(parent)
setmetatable(c, p.container)
c.class = self
c.name = name
c.script = _SCRIPT
c.basedir = os.getcwd()
c.filename = name
if type(self.init) == "function" then
self.init(c)
end
return c
end
---
-- Create a new child container of the given class, with the specified name.
--
-- @param cc
-- The class of child container to be fetched.
-- @param key
-- A string key or array index for the container.
-- @param parent
-- The parent container instance.
-- @return
-- The child container instance.
---
function container:createChild(cc, key, parent)
self[cc._listKey] = self[cc._listKey] or {}
local list = self[cc._listKey]
local child = cc:new(key, parent)
child[parent.class.name] = parent -- i.e. child.solution = sln
table.insert(list, child)
list[key] = child
return child
end
---
-- Return an iterator for the child containers of a particular class.
--
-- @param cc
-- The class of child container to be enumerated.
---
function container:eachChild(cc)
local children = self[cc._listKey] or {}
local i = 0
return function ()
i = i + 1
if i <= #children then
return children[i]
end
end
end
---
-- Fetch the child container with the given container class and instance name.
--
-- @param cc
-- The class of child container to be fetched.
-- @param key
-- A string key or array index for the container.
-- @return
-- The child container instance.
---
function container:fetchChild(cc, key)
self[cc._listKey] = self[cc._listKey] or {}
return self[cc._listKey][key]
end

View File

@ -81,6 +81,13 @@
f._kind = kind
-- Make sure scope is always an array; don't overwrite old value
if type(f.scope) == "table" then
f.scopes = f.scope
else
f.scopes = { f.scope }
end
-- All fields must have a valid store() function
if not field.accessor(f, "store") then
return nil, "invalid field kind '" .. f._kind .. "'"
@ -250,6 +257,7 @@
end
function field.merge(f, current, value)
local processor = field.accessor(f, "merge")
if processor then

View File

@ -6,9 +6,10 @@
premake.fileconfig = {}
local fileconfig = premake.fileconfig
local context = premake.context
local project = premake.project
local p = premake
local fileconfig = p.fileconfig
local context = p.context
local project = p.project
--
@ -171,7 +172,7 @@
function fileconfig.hasFileSettings(fcfg)
for key, field in pairs(premake.fields) do
if field.scope == "config" then
if field.scopes[1] == "config" then
local value = fcfg[field.name]
if value then
if type(value) == "table" then

View File

@ -10,29 +10,41 @@
--
premake.oven = {}
local oven = premake.oven
local solution = premake.solution
local project = premake.project
local config = premake.config
local fileconfig = premake.fileconfig
local configset = premake.configset
local context = premake.context
local p = premake
local solution = p.solution
local project = p.project
local config = p.config
local fileconfig = p.fileconfig
local configset = p.configset
local context = p.context
---
-- Traverses the entire container hierarchy built up by the project scripts,
-- filters, merges, and munges the project settings based on the current
-- runtime environment, and returns a new container hierarchy with the end
-- result. This result will then get handed off to the current action to
-- do whatever it is it needs to do, like generate project files.
--
-- Iterates through all of the current solutions, bakes down their contents,
-- and then replaces the original solution object with the baked result.
-- This is the entry point to the whole baking process, which happens after
-- the scripts have run, but before the project files are generated.
--
-- @param root
-- The top level or root container to be baked.
-- @return
-- ???
---
function oven.bake()
function oven.bake(root)
local result = {}
for i, sln in ipairs(solution.list) do
local root = p.api.rootContainer()
root.solutions = root.solutions or {}
for i, sln in ipairs(root.solutions) do
result[i] = oven.bakeSolution(sln)
end
solution.list = result
root.solutions = result
end
@ -94,6 +106,7 @@
-- store that for future reference
local projects = {}
sln.projects = sln.projects or {}
for i, prj in ipairs(sln.projects) do
projects[i] = oven.bakeProject(prj, ctx)
projects[prj.name] = projects[i]

View File

@ -169,6 +169,16 @@
end
---
-- Is this a type of file that can be linked?
---
function path.islinkable(fname)
return path.hasextension(fname, { ".o", ".obj", ".a", ".lib", ".so" })
end
--
-- Returns true if the filename represents an object file.
--

View File

@ -7,6 +7,8 @@
local solution = premake.solution
local project = premake.project
local config = premake.config
local field = premake.field
-- Store captured output text for later testing
@ -135,7 +137,7 @@
-- Used by the actions to generate solution and project files.
--
-- @param obj
-- A solution or project object; will be based to the callback function.
-- A solution or project object; will be passed to the callback function.
-- @param ext
-- An optional extension for the generated file, with the leading dot.
-- @param callback
@ -144,7 +146,7 @@
--
function premake.generate(obj, ext, callback)
local fn = premake.project.getfilename(obj, ext)
local fn = premake.filename(obj, ext)
printf("Generating %s...", path.getrelative(os.getcwd(), fn))
local f, err = io.open(fn, "wb")
@ -160,6 +162,34 @@
---
-- Returns the full path a file generated from any of the project
-- objects (project, solution, rule).
--
-- @param obj
-- The project object being generated.
-- @param ext
-- An optional extension for the generated file, with the leading dot.
-- @param callback
-- The function responsible for writing the file; will receive the
-- project object as its only argument.
---
function premake.filename(obj, ext)
local fname = obj.location
if ext and not ext:startswith(".") then
fname = path.join(fname, ext)
else
fname = path.join(fname, obj.filename)
if ext then
fname = fname .. ext
end
end
return fname
end
---
-- Sets the output indentation parameters.
--
@ -380,23 +410,28 @@
--
function premake.validateScopes(cfg, expected, ctx)
for name, field in pairs(premake.fields) do
local okay = false
-- skip fields that are at or below the expected scope
if field.scope == "config" or field.scope == expected then
okay = true
for f in field.each() do
-- Pull out the project level scope (this will get cleaned up when
-- the project objects move to the new container API)
local scope
if f.scopes[1] ~= "rule" then
scope = f.scopes[1]
end
-- Skip fields that are at or below the expected scope. Config-
-- level fields are the most general (can be applied to projects
-- or solutions) and so can never be out of scope.
local okay = (not scope or scope == "config" or scope == expected)
-- this one needs to checked
if not okay then
okay = premake.field.compare(field, cfg[field.scope][name], cfg[name])
okay = field.compare(f, cfg[scope][f.name], cfg[f.name])
end
-- found a problem?
if not okay then
local key = "validate." .. field.name
premake.warnOnce(key, "'%s' on %s '%s' differs from %s '%s'; may be set out of scope", name, expected, cfg.name, field.scope, cfg[field.scope].name)
local key = "validate." .. f.name
premake.warnOnce(key, "'%s' on %s '%s' differs from %s '%s'; may be set out of scope", f.name, expected, cfg.name, scope, cfg[scope].name)
end
end
@ -435,6 +470,18 @@
---
-- Write a opening XML element for a UTF-8 encoded file. Used by
-- several different files for different actions, so makes sense
-- to have a common call for it.
---
function premake.xmlUtf8()
premake.outln('<?xml version="1.0" encoding="utf-8"?>')
end
--
-- These are the output shortcuts that I used before switching to the
-- indentation-aware calls above. They are still in use all over the

View File

@ -6,35 +6,29 @@
premake.project = {}
local project = premake.project
local configset = premake.configset
local context = premake.context
local tree = premake.tree
local p = premake
local tree = p.tree
--
-- Create a new project object.
--
-- @param sln
-- The solution object to contain the new project.
-- @param name
-- The new project's name.
-- @return
-- A new project object, contained by the specified solution.
--
function project.new(sln, name)
local prj = configset.new(sln)
setmetatable(prj, configset.metatable(prj))
---
-- Register a new container class to represent projects.
---
prj.name = name
prj.solution = sln
prj.script = _SCRIPT
prj.basedir = os.getcwd()
prj.filename = name
prj.uuid = os.uuid(name)
local _prjClass = p.api.container {
name = "project",
parent = "solution",
init = function(prj)
prj.uuid = os.uuid(prj.name)
if p.api.scope.group then
prj.group = p.api.scope.group.name
else
prj.group = ""
end
end
}
return prj
end
--
@ -210,32 +204,17 @@
--
---
-- Returns the file name for this project. Also works with solutions.
--
-- @param prj
-- The project object to query.
-- @param ext
-- An optional file extension to add, with the leading dot. If provided
-- without a leading dot, it will treated as a file name.
-- @return
-- The absolute path to the project's file.
--
-- Deprecated 11 Aug 2014
---
function project.getfilename(prj, ext)
local fn = prj.location
if ext and not ext:startswith(".") then
fn = path.join(fn, ext)
else
fn = path.join(fn, prj.filename)
if ext then
fn = fn .. ext
end
end
return fn
return premake.filename(prj, ext)
end
--
-- Return the first configuration of a project, which is used in some
-- actions to generate project-wide defaults.

50
src/base/rules.lua Normal file
View File

@ -0,0 +1,50 @@
---
-- base/rules.lua
--
-- Defines rule sets for generated custom rule files.
--
-- Copyright (c) 2014 Jason Perkins and the Premake project
---
premake.rules = {}
local rules = premake.rules
local p = premake
---
-- Register a new container class to hold the rules.
---
local _ruleContainerClass = p.api.container {
name = "rule",
}
---
-- Iterate over the collection of rules in a session.
--
-- @returns
-- An iterator function.
---
function rules.each()
return p.api.rootContainer():eachChild(_ruleContainerClass)
end
---
-- Retrieve a rule set by name or numeric index.
--
-- @param key
-- The rule key, either a string name or integer index.
-- @returns
-- The rule set with the provided key.
---
function rules.fetch(key)
return p.api.rootContainer():fetchChild(_ruleContainerClass, key)
end

View File

@ -6,41 +6,26 @@
premake.solution = {}
local solution = premake.solution
local project = premake.project
local configset = premake.configset
local context = premake.context
local tree = premake.tree
local p = premake
local tree = p.tree
-- The list of defined solutions (which contain projects, etc.)
solution.list = {}
---
-- Register a new container class to represent solutions.
---
local _slnClass = p.api.container {
name = "solution",
}
--
-- Create a new solution and add it to the session.
--
-- @param name
-- The new solution's name.
-- @return
-- A new solution object.
--
p.api.container {
name = "group",
parent = "solution",
placeholder = true,
}
function solution.new(name)
sln = configset.new(configset.root)
setmetatable(sln, configset.metatable(sln))
sln.name = name
sln.projects = {}
sln.basedir = os.getcwd()
sln.filename = name
-- Add to master list keyed by both name and index
table.insert(premake.solution.list, sln)
premake.solution.list[name] = sln
return sln
end
--
@ -67,11 +52,13 @@
--
function solution.each()
local root = p.api.rootContainer()
local i = 0
return function ()
i = i + 1
if i <= #premake.solution.list then
return premake.solution.list[i]
if i <= #root.solutions then
return root.solutions[i]
end
end
end
@ -153,24 +140,13 @@
--
function solution.get(key)
return premake.solution.list[key]
local root = p.api.rootContainer()
if root.solutions then
return root.solutions[key]
end
end
--
-- Returns the file name for this solution.
--
-- @param sln
-- The solution object to query.
-- @param ext
-- An optional file extension to add, with the leading dot.
-- @return
-- The absolute path to the solution's file.
--
solution.getfilename = project.getfilename
--
-- Retrieve the tree of project groups.
@ -237,7 +213,7 @@
function solution.hascppproject(sln)
for prj in solution.eachproject(sln) do
if project.iscpp(prj) then
if p.project.iscpp(prj) then
return true
end
end
@ -258,7 +234,7 @@
function solution.hasdotnetproject(sln)
for prj in solution.eachproject(sln) do
if project.isdotnet(prj) then
if p.project.isdotnet(prj) then
return true
end
end

View File

@ -61,3 +61,17 @@
end
return n
end
---
-- Return a plural version of a string.
---
function string:plural()
if self:endswith("y") then
return self:sub(1, #self - 1) .. "ies"
else
return self .. "s"
end
end

View File

@ -1,13 +1,13 @@
--
-- tests/project/test_filename.lua
-- Verify generation of project (and solution) filenames.
-- Copyright (c) 2008-2012 Jason Perkins and the Premake project
-- tests/base/test_filename.lua
-- Verify generation of project/solution/rule filenames.
-- Copyright (c) 2008-2014 Jason Perkins and the Premake project
--
T.project_filename = {}
local suite = T.project_filename
local suite = test.declare("project_filename")
local p = premake
local project = premake.project
--
@ -17,11 +17,11 @@
local sln
function suite.setup()
sln, prj = test.createsolution()
sln = test.createsolution()
end
local function prepare()
prj = premake.solution.getproject(sln, 1)
prj = test.getproject(sln, 1)
end
@ -31,7 +31,7 @@
function suite.isAbsolutePath()
prepare()
test.isequal(os.getcwd(), path.getdirectory(project.getfilename(prj)))
test.isequal(os.getcwd(), path.getdirectory(p.filename(prj)))
end
@ -41,7 +41,7 @@
function suite.isProjectName_onNoFilename()
prepare()
test.isequal("MyProject", path.getname(project.getfilename(prj)))
test.isequal("MyProject", path.getname(p.filename(prj)))
end
@ -52,7 +52,7 @@
function suite.doesUseFilename()
filename "Howdy"
prepare()
test.isequal("Howdy", path.getname(project.getfilename(prj)))
test.isequal("Howdy", path.getname(p.filename(prj)))
end
@ -62,7 +62,7 @@
function suite.doesUseExtension()
prepare()
test.isequal(".xc", path.getextension(project.getfilename(prj, ".xc")))
test.isequal(".xc", path.getextension(p.filename(prj, ".xc")))
end
@ -72,7 +72,7 @@
function suite.worksWithSolution()
prepare()
test.isequal("MySolution", path.getname(project.getfilename(sln)))
test.isequal("MySolution", path.getname(p.filename(sln)))
end
@ -84,7 +84,7 @@
solution ("MySolution")
filename ("Howdy")
prepare()
test.isequal("MyProject", path.getname(project.getfilename(prj)))
test.isequal("MyProject", path.getname(p.filename(prj)))
end
@ -95,6 +95,5 @@
function suite.canOverrideFilename()
prepare()
test.isequal("Makefile", path.getname(project.getfilename(prj, "Makefile")))
test.isequal("Makefile", path.getname(p.filename(prj, "Makefile")))
end

View File

@ -83,38 +83,16 @@
-- Warn if a configuration value is set in the wrong scope.
--
function suite.warns_onSolutionStringField_inProject()
solution "MySolution"
configurations { "Debug", "Release" }
project "MyProject"
kind "ConsoleApp"
language "C++"
startproject "MyProject"
premake.validate()
test.stderr("'startproject' on project")
end
function suite.warns_onSolutionStringField_inConfig()
solution "MySolution"
configurations { "Debug", "Release" }
project "MyProject"
kind "ConsoleApp"
language "C++"
filter "Debug"
startproject "MyProject"
premake.validate()
test.stderr("'startproject' on config")
end
function suite.warns_onSolutionStringField_onlyWarnOnce()
solution "MySolution"
configurations { "Debug", "Release" }
project "MyProject"
kind "ConsoleApp"
language "C++"
startproject "MyProject"
premake.validate()
test.notstderr("'startproject' on config")
test.stderr("'startproject' on config")
end
function suite.warns_onProjectStringField_inConfig()

230
tests/premake5.lua Normal file
View File

@ -0,0 +1,230 @@
--
-- tests/premake5.lua
-- Automated test suite for Premake 5.x
-- Copyright (c) 2008-2013 Jason Perkins and the Premake project
--
dofile("testfx.lua")
newoption {
trigger = "test",
description = "A suite or test to run"
}
newoption {
trigger = "profile",
description = "Profile execution times; saves to profile.txt"
}
if _OPTIONS["profile"] then
dofile("pepperfish_profiler.lua")
end
--
-- The test suites
--
-- Base API tests
dofile("test_dofile.lua")
dofile("test_string.lua")
dofile("base/test_configset.lua")
dofile("base/test_context.lua")
dofile("base/test_criteria.lua")
dofile("base/test_detoken.lua")
dofile("base/test_filename.lua")
dofile("base/test_include.lua")
dofile("base/test_option.lua")
dofile("base/test_os.lua")
dofile("base/test_override.lua")
dofile("base/test_path.lua")
dofile("base/test_premake_command.lua")
dofile("base/test_table.lua")
dofile("base/test_tree.lua")
dofile("base/test_uuid.lua")
-- Solution object tests
dofile("solution/test_eachconfig.lua")
dofile("solution/test_location.lua")
dofile("solution/test_objdirs.lua")
-- Project object tests
dofile("project/test_config_maps.lua")
dofile("project/test_eachconfig.lua")
dofile("project/test_getconfig.lua")
dofile("project/test_location.lua")
dofile("project/test_vpaths.lua")
-- Configuration object tests
dofile("config/test_linkinfo.lua")
dofile("config/test_links.lua")
dofile("config/test_targetinfo.lua")
-- Baking tests
dofile("oven/test_filtering.lua")
dofile("oven/test_objdirs.lua")
-- API tests
dofile("api/test_containers.lua")
dofile("api/test_directory_kind.lua")
dofile("api/test_list_kind.lua")
dofile("api/test_path_kind.lua")
dofile("api/test_register.lua")
dofile("api/test_string_kind.lua")
dofile("api/test_table_kind.lua")
-- Control system tests
dofile("test_premake.lua")
dofile("base/test_validation.lua")
-- Toolset tests
dofile("tools/test_dotnet.lua")
dofile("tools/test_gcc.lua")
dofile("tools/test_msc.lua")
dofile("tools/test_snc.lua")
-- Visual Studio 2005-2010 C# projects
dofile("actions/vstudio/cs2005/test_assembly_refs.lua")
dofile("actions/vstudio/cs2005/test_build_events.lua")
dofile("actions/vstudio/cs2005/test_compiler_props.lua")
dofile("actions/vstudio/cs2005/test_debug_props.lua")
dofile("actions/vstudio/cs2005/test_files.lua")
dofile("actions/vstudio/cs2005/test_icon.lua")
dofile("actions/vstudio/cs2005/test_output_props.lua")
dofile("actions/vstudio/cs2005/projectelement.lua")
dofile("actions/vstudio/cs2005/test_platform_groups.lua")
dofile("actions/vstudio/cs2005/test_project_refs.lua")
dofile("actions/vstudio/cs2005/projectsettings.lua")
-- Visual Studio 2005-2010 solutions
dofile("actions/vstudio/sln2005/test_dependencies.lua")
dofile("actions/vstudio/sln2005/test_header.lua")
dofile("actions/vstudio/sln2005/test_nested_projects.lua")
dofile("actions/vstudio/sln2005/test_projects.lua")
dofile("actions/vstudio/sln2005/test_platforms.lua")
-- Visual Studio 2002-2008 C/C++ projects
dofile("actions/vstudio/vc200x/test_assembly_refs.lua")
dofile("actions/vstudio/vc200x/test_build_steps.lua")
dofile("actions/vstudio/vc200x/test_configuration.lua")
dofile("actions/vstudio/vc200x/test_compiler_block.lua")
dofile("actions/vstudio/vc200x/test_debug_settings.lua")
dofile("actions/vstudio/vc200x/test_excluded_configs.lua")
dofile("actions/vstudio/vc200x/test_external_compiler.lua")
dofile("actions/vstudio/vc200x/test_external_linker.lua")
dofile("actions/vstudio/vc200x/test_files.lua")
dofile("actions/vstudio/vc200x/test_linker_block.lua")
dofile("actions/vstudio/vc200x/test_manifest_block.lua")
dofile("actions/vstudio/vc200x/test_nmake_settings.lua")
dofile("actions/vstudio/vc200x/test_platforms.lua")
dofile("actions/vstudio/vc200x/test_project.lua")
dofile("actions/vstudio/vc200x/test_project_refs.lua")
dofile("actions/vstudio/vc200x/test_resource_compiler.lua")
-- Visual Studio 2010 C/C++ projects
dofile("actions/vstudio/vc2010/test_assembly_refs.lua")
dofile("actions/vstudio/vc2010/test_build_events.lua")
dofile("actions/vstudio/vc2010/test_compile_settings.lua")
dofile("actions/vstudio/vc2010/test_config_props.lua")
dofile("actions/vstudio/vc2010/test_debug_settings.lua")
dofile("actions/vstudio/vc2010/test_excluded_configs.lua")
dofile("actions/vstudio/vc2010/test_extension_settings.lua")
dofile("actions/vstudio/vc2010/test_extension_targets.lua")
dofile("actions/vstudio/vc2010/test_globals.lua")
dofile("actions/vstudio/vc2010/test_header.lua")
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_item_def_group.lua")
dofile("actions/vstudio/vc2010/test_link.lua")
dofile("actions/vstudio/vc2010/test_manifest.lua")
dofile("actions/vstudio/vc2010/test_nmake_props.lua")
dofile("actions/vstudio/vc2010/test_output_props.lua")
dofile("actions/vstudio/vc2010/test_project_configs.lua")
dofile("actions/vstudio/vc2010/test_project_refs.lua")
dofile("actions/vstudio/vc2010/test_prop_sheet.lua")
dofile("actions/vstudio/vc2010/test_resource_compile.lua")
-- Visual Studio 2012
dofile("actions/vs2012/test_csproj_common_props.lua")
dofile("actions/vs2012/test_csproj_project_element.lua")
dofile("actions/vs2012/test_csproj_project_props.lua")
dofile("actions/vs2012/test_csproj_targets.lua")
dofile("actions/vs2012/test_sln_header.lua")
dofile("actions/vs2012/test_vcxproj_clcompile.lua")
dofile("actions/vs2012/test_vcxproj_config_props.lua")
-- Visual Studio 2013
dofile("actions/vs2013/test_csproj_project_element.lua")
dofile("actions/vs2013/test_globals.lua")
dofile("actions/vs2013/test_sln_header.lua")
dofile("actions/vs2013/test_vcxproj_config_props.lua")
-- Makefile tests
dofile("actions/make/test_make_escaping.lua")
dofile("actions/make/test_make_tovar.lua")
-- Makefile solutions
dofile("actions/make/solution/test_config_maps.lua")
dofile("actions/make/solution/test_default_config.lua")
dofile("actions/make/solution/test_help_rule.lua")
dofile("actions/make/solution/test_project_rule.lua")
-- Makefile C/C++ projects
dofile("actions/make/cpp/test_clang.lua")
dofile("actions/make/cpp/test_file_rules.lua")
dofile("actions/make/cpp/test_flags.lua")
dofile("actions/make/cpp/test_make_pch.lua")
dofile("actions/make/cpp/test_make_linking.lua")
dofile("actions/make/cpp/test_objects.lua")
dofile("actions/make/cpp/test_ps3.lua")
dofile("actions/make/cpp/test_target_rules.lua")
dofile("actions/make/cpp/test_tools.lua")
dofile("actions/make/cpp/test_wiidev.lua")
-- Makefile C# projects
dofile("actions/make/cs/test_embed_files.lua")
dofile("actions/make/cs/test_flags.lua")
dofile("actions/make/cs/test_links.lua")
dofile("actions/make/cs/test_response.lua")
dofile("actions/make/cs/test_sources.lua")
newaction {
trigger = "test",
description = "Run the automated test suite",
execute = function ()
local focus = {}
if _OPTIONS["test"] then
focus = string.explode(_OPTIONS["test"] or "", ".", true)
end
local profile = _OPTIONS["profile"]
if profile == "" then profile = "time" end
local profiler
if profile then
profiler = newProfiler()
profiler:start()
end
passed, failed = test.runall(focus[1], focus[2])
if profile then
profiler:stop()
local outfile = io.open("profile.txt", "w+")
profiler:report(outfile)
outfile:close()
end
msg = string.format("%d tests passed, %d failed", passed, failed)
if (failed > 0) then
print(msg)
os.exit(5)
else
print(msg)
end
end
}

View File

@ -339,7 +339,6 @@
stderr_capture = nil
premake.solution.list = { }
premake.clearWarnings()
premake.eol("\n")
premake.escaper(nil)