premake/modules/vstudio/vs2005_solution.lua

416 lines
10 KiB
Lua

--
-- vs2005_solution.lua
-- Generate a Visual Studio 2005+ solution.
-- Copyright (c) Jason Perkins and the Premake project
--
local p = premake
p.vstudio.sln2005 = {}
local vstudio = p.vstudio
local sln2005 = p.vstudio.sln2005
local project = p.project
local tree = p.tree
---
-- Add namespace for element definition lists for p.callArray()
---
sln2005.elements = {}
--
-- Return the list of sections contained in the solution.
-- TODO: Get rid of this when the MonoDevelop module no longer needs it
--
function sln2005.solutionSections(wks)
return {
"ConfigurationPlatforms",
"SolutionProperties",
"NestedProjects",
"ExtensibilityGlobals"
}
end
--
-- Generate a Visual Studio 200x solution, with support for the new platforms API.
--
function sln2005.generate(wks)
-- Mark the file as Unicode
p.utf8()
p.outln('')
sln2005.reorderProjects(wks)
sln2005.header()
sln2005.projects(wks)
p.push('Global')
sln2005.sections(wks)
p.pop('EndGlobal')
p.w()
end
--
-- Generate the solution header. Each Visual Studio action definition
-- should include its own version.
--
function sln2005.header()
local action = p.action.current()
p.w('Microsoft Visual Studio Solution File, Format Version %d.00', action.vstudio.solutionVersion)
p.w('# Visual Studio %s', action.vstudio.versionName)
end
--
-- If a startup project is specified, move it (and any enclosing groups)
-- to the front of the project list. This will make Visual Studio treat
-- it like a startup project.
--
-- I force the new ordering into the tree so that it will get applied to
-- all sections of the solution; otherwise the first change to the solution
-- in the IDE will cause the orderings to get rewritten.
--
function sln2005.reorderProjects(wks)
if wks.startproject then
local np
local tr = p.workspace.grouptree(wks)
tree.traverse(tr, {
onleaf = function(n)
if n.project.name == wks.startproject then
np = n
end
end
})
while np and np.parent do
local p = np.parent
local i = table.indexof(p.children, np)
table.remove(p.children, i)
table.insert(p.children, 1, np)
np = p
end
end
end
--
-- Build a relative path from the solution file to the project file
--
function sln2005.buildRelativePath(prj)
local prjpath = vstudio.projectfile(prj)
prjpath = vstudio.path(prj.workspace, prjpath)
-- Unlike projects, solutions must use old-school %...% DOS style
-- for environment variables.
return prjpath:gsub("$%((.-)%)", "%%%1%%")
end
--
-- Write out the list of projects and groups contained by the solution.
--
function sln2005.projects(wks)
local tr = p.workspace.grouptree(wks)
tree.traverse(tr, {
onleaf = function(n)
local prj = n.project
p.x('Project("{%s}") = "%s", "%s", "{%s}"', vstudio.tool(prj), prj.name, sln2005.buildRelativePath(prj), prj.uuid)
p.push()
sln2005.projectdependencies(prj)
p.pop('EndProject')
end,
onbranch = function(n)
p.push('Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "%s", "%s", "{%s}"', n.name, n.name, n.uuid)
p.pop('EndProject')
end,
})
end
--
-- Write out the list of project dependencies for a particular project.
--
function sln2005.projectdependencies(prj)
local deps = project.getdependencies(prj, 'dependOnly')
if #deps > 0 then
p.push('ProjectSection(ProjectDependencies) = postProject')
for _, dep in ipairs(deps) do
p.w('{%s} = {%s}', dep.uuid, dep.uuid)
end
p.pop('EndProjectSection')
end
end
--
-- Write out the list of shared project files and their links
--
function sln2005.sharedProjects(wks)
local contents = p.capture(function ()
local tr = p.workspace.grouptree(wks)
p.tree.traverse(tr, {
onleaf = function(n)
local prj = n.project
-- SharedItems projects reference their own UUID with a "9"
-- SharedItems projects reference the UUID of projects that link them with a "4"
if prj.kind == p.SHAREDITEMS then
p.w('%s*{%s}*SharedItemsImports = %s', sln2005.buildRelativePath(prj), prj.uuid:lower(), "9")
else
local deps = p.project.getdependencies(prj, 'linkOnly')
for _, dep in ipairs(deps) do
if dep.kind == p.SHAREDITEMS then
p.w('%s*{%s}*SharedItemsImports = %s', sln2005.buildRelativePath(dep), prj.uuid:lower(), "4")
end
end
end
end,
})
end)
if #contents > 0 then
p.push('GlobalSection(SharedMSBuildProjectFiles) = preSolution')
p.outln(contents)
p.pop('EndGlobalSection')
end
end
--
-- Write out the list of project configuration platforms.
--
sln2005.elements.projectConfigurationPlatforms = function(cfg, context)
return {
sln2005.activeCfg,
sln2005.build0,
}
end
function sln2005.projectConfigurationPlatforms(wks, sorted, descriptors)
p.w("GlobalSection(ProjectConfigurationPlatforms) = postSolution")
local tr = p.workspace.grouptree(wks)
tree.traverse(tr, {
onleaf = function(n)
local prj = n.project
-- SharedItems projects don't have any configuration platform entries
if prj.kind == p.SHAREDITEMS then
return
end
table.foreachi(sorted, function(cfg)
local context = {}
-- Look up the matching project configuration. If none exist, this
-- configuration has been excluded from the project, and should map
-- to closest available project configuration instead.
context.prj = prj
context.prjCfg = project.getconfig(prj, cfg.buildcfg, cfg.platform)
context.excluded = (context.prjCfg == nil or context.prjCfg.flags.ExcludeFromBuild)
if context.prjCfg == nil then
context.prjCfg = project.findClosestMatch(prj, cfg.buildcfg, cfg.platform)
end
context.descriptor = descriptors[cfg]
context.platform = vstudio.projectPlatform(context.prjCfg)
context.architecture = vstudio.archFromConfig(context.prjCfg, true)
p.push()
p.callArray(sln2005.elements.projectConfigurationPlatforms, cfg, context)
p.pop()
end)
end
})
p.w("EndGlobalSection")
end
function sln2005.activeCfg(cfg, context)
p.w('{%s}.%s.ActiveCfg = %s|%s', context.prj.uuid, context.descriptor, context.platform, context.architecture)
end
function sln2005.build0(cfg, context)
if not context.excluded and context.prjCfg.kind ~= p.NONE then
p.w('{%s}.%s.Build.0 = %s|%s', context.prj.uuid, context.descriptor, context.platform, context.architecture)
end
end
--
-- Write out the tables that map solution configurations to project configurations.
--
function sln2005.configurationPlatforms(wks)
local descriptors = {}
local sorted = {}
for cfg in p.workspace.eachconfig(wks) do
-- Create a Visual Studio solution descriptor (i.e. Debug|Win32) for
-- this solution configuration. I need to use it in a few different places
-- below so it makes sense to precompute it up front.
local platform = vstudio.solutionPlatform(cfg)
descriptors[cfg] = string.format("%s|%s", cfg.buildcfg, platform)
-- Also add the configuration to an indexed table which I can sort below
table.insert(sorted, cfg)
end
-- Sort the solution configurations to match Visual Studio's preferred
-- order, which appears to be a simple alpha sort on the descriptors.
table.sort(sorted, function(cfg0, cfg1)
return descriptors[cfg0]:lower() < descriptors[cfg1]:lower()
end)
-- Now I can output the sorted list of solution configuration descriptors
-- Visual Studio assumes the first configurations as the defaults.
if wks.defaultplatform then
p.push('GlobalSection(SolutionConfigurationPlatforms) = preSolution')
table.foreachi(sorted, function (cfg)
if cfg.platform == wks.defaultplatform then
p.w('%s = %s', descriptors[cfg], descriptors[cfg])
end
end)
p.pop("EndGlobalSection")
end
p.push('GlobalSection(SolutionConfigurationPlatforms) = preSolution')
table.foreachi(sorted, function (cfg)
if not wks.defaultplatform or cfg.platform ~= wks.defaultplatform then
p.w('%s = %s', descriptors[cfg], descriptors[cfg])
end
end)
p.pop("EndGlobalSection")
-- For each project in the solution...
sln2005.projectConfigurationPlatforms(wks, sorted, descriptors)
end
--
-- Write out contents of the SolutionProperties section; currently unused.
--
function sln2005.properties(wks)
p.push('GlobalSection(SolutionProperties) = preSolution')
p.w('HideSolutionNode = FALSE')
p.pop('EndGlobalSection')
end
--
-- Write out the NestedProjects block, which describes the structure of
-- any solution groups.
--
function sln2005.nestedProjects(wks)
local tr = p.workspace.grouptree(wks)
if tree.hasbranches(tr) then
p.push('GlobalSection(NestedProjects) = preSolution')
tree.traverse(tr, {
onnode = function(n)
if n.parent.uuid then
p.w('{%s} = {%s}', (n.project or n).uuid, n.parent.uuid)
end
end
})
p.pop('EndGlobalSection')
end
end
--
-- Write out the ExtensibilityGlobals block, which embeds some data for the
-- Visual Studio PremakeExtension.
--
function sln2005.premakeExtensibilityGlobals(wks)
if wks.editorintegration then
-- we need to filter out the 'file' argument, since we already output
-- the script separately.
local args = {}
for _, arg in ipairs(_ARGV) do
if not (arg:startswith("--file") or arg:startswith("/file")) then
table.insert(args, arg);
end
end
p.w('PremakeBinary = %s', _PREMAKE_COMMAND)
p.w('PremakeScript = %s', p.workspace.getrelative(wks, _MAIN_SCRIPT))
p.w('PremakeArguments = %s', table.concat(args, ' '))
end
end
--
-- Map ExtensibilityGlobals to output functions.
--
sln2005.elements.extensibilityGlobals = function(wks)
return {
sln2005.premakeExtensibilityGlobals,
}
end
--
-- Output the ExtensibilityGlobals section.
--
function sln2005.extensibilityGlobals(wks)
local contents = p.capture(function ()
p.push()
p.callArray(sln2005.elements.extensibilityGlobals, wks)
p.pop()
end)
if #contents > 0 then
p.push('GlobalSection(ExtensibilityGlobals) = postSolution')
p.outln(contents)
p.pop('EndGlobalSection')
end
end
--
-- Map solution sections to output functions. Tools that aren't listed will
-- be ignored.
--
sln2005.elements.sections = function(wks)
return {
sln2005.sharedProjects,
sln2005.configurationPlatforms,
sln2005.properties,
sln2005.nestedProjects,
sln2005.extensibilityGlobals,
}
end
--
-- Write out all of the workspace sections.
--
function sln2005.sections(wks)
p.callArray(sln2005.elements.sections, wks)
end