-- -- 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