--
-- vs2005_csproj.lua
-- Generate a Visual Studio 2005-2010 C# project.
-- Copyright (c) 2009-2014 Jason Perkins and the Premake project
--
premake.vstudio.cs2005 = {}
local p = premake
local vstudio = p.vstudio
local cs2005 = p.vstudio.cs2005
local project = p.project
local config = p.config
local fileconfig = p.fileconfig
local dotnet = p.tools.dotnet
cs2005.elements = {}
--
-- Generate a Visual Studio 200x C# project, with support for the new platforms API.
--
cs2005.elements.project = {
"xmlDeclaration",
"projectElement",
"commonProperties",
"projectProperties",
"configurations",
"applicationIcon",
"references",
}
function cs2005.generate(prj)
p.utf8()
premake.callarray(cs2005, cs2005.elements.project, prj)
_p(1,'')
cs2005.files(prj)
_p(1,'')
cs2005.projectReferences(prj)
cs2005.targets(prj)
cs2005.buildEvents(prj)
p.out('')
end
--
-- Write the opening element.
--
function cs2005.projectElement(prj)
local ver = ''
local action = premake.action.current()
if action.vstudio.toolsVersion then
ver = string.format(' ToolsVersion="%s"', action.vstudio.toolsVersion)
end
_p('', ver)
end
--
-- Write the opening PropertyGroup, which contains the project-level settings.
--
cs2005.elements.projectProperties = {
"configurationCondition",
"platformCondition",
"productVersion",
"schemaVersion",
"projectGuid",
"outputType",
"appDesignerFolder",
"rootNamespace",
"assemblyName",
"targetFrameworkVersion",
"targetFrameworkProfile",
"fileAlignment",
"projectTypeGuids",
}
function cs2005.projectProperties(prj)
_p(1,'')
local cfg = project.getfirstconfig(prj)
premake.callarray(cs2005, cs2005.elements.projectProperties, cfg)
_p(1,'')
end
--
-- Write out the settings for the project configurations.
--
cs2005.elements.configuration = {
"propertyGroup",
"debugProps",
"outputProps",
"compilerProps",
}
function cs2005.configurations(prj)
for cfg in project.eachconfig(prj) do
cs2005.configuration(cfg)
end
end
function cs2005.configuration(cfg)
premake.callarray(cs2005, cs2005.elements.configuration, cfg)
_p(1,'')
end
--
-- Write out the source files item group.
--
function cs2005.files(prj)
-- Some settings applied at project level; can't be changed in cfg
local cfg = project.getfirstconfig(prj)
-- Try to write file-level elements in the same order as Visual Studio
local elements = {
"AutoGen",
"CopyToOutputDirectory",
"DesignTime",
"DependentUpon",
"DesignTimeSharedInput",
"Generator",
"LastGenOutput",
"SubType",
}
local tr = project.getsourcetree(prj)
premake.tree.traverse(tr, {
onleaf = function(node, depth)
local filecfg = fileconfig.getconfig(node, cfg)
local fname = path.translate(node.relpath)
-- Files that live outside of the project tree need to be "linked"
-- and provided with a project relative pseudo-path. Check for any
-- leading "../" sequences and, if found, remove them and mark this
-- path as external.
local link, count = node.relpath:gsub("%.%.%/", "")
local external = (count > 0)
-- Try to provide a little bit of flexibility by allowing virtual
-- paths for external files. Would be great to support them for all
-- files but Visual Studio chokes if file is already in project area.
if external and node.vpath ~= node.relpath then
link = node.vpath
end
-- Deduce what, if any, special attributes are required for this file.
-- For example, forms may have related source, designer, and resource
-- files which need to be associated.
local info = dotnet.fileinfo(filecfg)
-- Process any sub-elements required by this file; choose the write
-- element form to use based on the results.
local contents = premake.capture(function ()
for _, el in ipairs(elements) do
local value = info[el]
if value then
_p(3,"<%s>%s%s>", el, value, el)
end
end
end)
if #contents > 0 or external then
_p(2,'<%s Include="%s">', info.action, fname)
if external then
_p(3,'%s', path.translate(link))
end
if #contents > 0 then
_p("%s", contents)
end
if info.action == "EmbeddedResource" and cfg.customtoolnamespace then
_p(3,"%s", cfg.customtoolnamespace)
end
_p(2,'%s>', info.action)
else
_p(2,'<%s Include="%s" />', info.action, fname)
end
end
}, false)
end
--
-- Write out pre- and post-build events, if provided.
--
function cs2005.buildEvents(prj)
local function output(name, steps)
if #steps > 0 then
steps = os.translateCommands(steps, p.WINDOWS)
steps = table.implode(steps, "", "", "\r\n")
_x(2,'<%sBuildEvent>%s%sBuildEvent>', name, steps, name)
end
end
local cfg = project.getfirstconfig(prj)
if #cfg.prebuildcommands > 0 or #cfg.postbuildcommands > 0 then
_p(1,'')
output("Pre", cfg.prebuildcommands)
output("Post", cfg.postbuildcommands)
_p(1,'')
end
end
--
-- Write the compiler flags for a particular configuration.
--
function cs2005.compilerProps(cfg)
_x(2,'%s', table.concat(cfg.defines, ";"))
_p(2,'prompt')
_p(2,'4')
if cfg.clr == "Unsafe" then
_p(2,'true')
end
if cfg.flags.FatalCompileWarnings then
_p(2,'true')
end
cs2005.debugCommandParameters(cfg)
end
--
-- Write out the debug start parameters for MonoDevelop/Xamarin Studio.
--
function cs2005.debugCommandParameters(cfg)
if #cfg.debugargs > 0 then
_x(2,'%s', table.concat(cfg.debugargs, " "))
end
end
--
-- Write out the debugging and optimization flags for a configuration.
--
function cs2005.debugProps(cfg)
if cfg.flags.Symbols then
_p(2,'true')
_p(2,'full')
else
_p(2,'pdbonly')
end
_p(2,'%s', iif(config.isOptimizedBuild(cfg), "true", "false"))
end
--
-- Write out the target and intermediates settings for a configuration.
--
function cs2005.outputProps(cfg)
local outdir = vstudio.path(cfg, cfg.buildtarget.directory)
_x(2,'%s\\', outdir)
-- Want to set BaseIntermediateOutputPath because otherwise VS will create obj/
-- anyway. But VS2008 throws up ominous warning if present.
local objdir = vstudio.path(cfg, cfg.objdir)
if _ACTION > "vs2008" then
_x(2,'%s\\', objdir)
_p(2,'$(BaseIntermediateOutputPath)')
else
_x(2,'%s\\', objdir)
end
end
--
-- Write the list of assembly (system, or non-sibling) references.
--
cs2005.elements.references = function(prj)
return {
cs2005.assemblyReferences,
cs2005.nuGetReferences,
}
end
function cs2005.assemblyReferences(prj)
-- C# doesn't support per-configuration links (does it?) so just use
-- the settings from the first available config instead
local cfg = project.getfirstconfig(prj)
config.getlinks(cfg, "system", function(original, decorated)
local name = path.getname(decorated)
if path.getextension(name) == ".dll" then
name = name.sub(name, 1, -5)
end
if decorated:find("/", nil, true) then
_x(2,'', name)
_x(3,'%s', path.appendextension(path.translate(decorated), ".dll"))
if not config.isCopyLocal(prj, original, true) then
_p(3,"False")
end
_p(2,'')
else
_x(2,'', name)
end
end)
end
function cs2005.nuGetReferences(prj)
if _ACTION >= "vs2010" and prj.nuget then
for i = 1, #prj.nuget do
local package = prj.nuget[i]
_x(2, '', vstudio.nuget2010.packageId(package))
local targetFramework = vstudio.nuget2010.packageFramework(prj.solution, package)
-- Strip off the "net" prefix so we can compare it.
local targetFrameworkVersion = tonumber(targetFramework:sub(4))
-- If the package doesn't support the target framework, we
-- need to check if it exists in the folders for any of the
-- previous framework versions. The last HintPath will
-- override any previous HintPaths (if the condition is met
-- that is).
-- This is kind of shit because we will need to add new
-- versions of the .NET Framework here.
local frameworks = {}
if targetFrameworkVersion >= 11 then table.insert(frameworks, "net10") end
if targetFrameworkVersion >= 20 then table.insert(frameworks, "net11") end
if targetFrameworkVersion >= 30 then table.insert(frameworks, "net20") end
if targetFrameworkVersion >= 35 then table.insert(frameworks, "net30") end
if targetFrameworkVersion >= 40 then table.insert(frameworks, "net35") end
if targetFrameworkVersion >= 45 then table.insert(frameworks, "net40") end
table.insert(frameworks, targetFramework)
for _, framework in pairs(frameworks) do
local assembly = vstudio.path(prj, p.filename(prj.solution, string.format("packages\\%s\\lib\\%s\\%s.dll", vstudio.nuget2010.packageName(package), framework, vstudio.nuget2010.packageId(package))))
_x(3, '%s', assembly, assembly)
end
_p(3, 'True')
_p(2, '')
end
end
end
function cs2005.references(prj)
_p(1,'')
p.callArray(cs2005.elements.references, prj)
_p(1,'')
end
--
-- Write the list of project dependencies.
--
function cs2005.projectReferences(prj)
_p(1,'')
local deps = project.getdependencies(prj, 'linkOnly')
if #deps > 0 then
for _, dep in ipairs(deps) do
local relpath = vstudio.path(prj, vstudio.projectfile(dep))
_x(2,'', relpath)
_p(3,'{%s}', dep.uuid)
_x(3,'%s', dep.name)
if not config.isCopyLocal(prj, dep.name, true) then
_p(3,"False")
end
_p(2,'')
end
end
_p(1,'')
end
--
-- Return the Visual Studio architecture identification string. The logic
-- to select this is getting more complicated in VS2010, but I haven't
-- tackled all the permutations yet.
--
function cs2005.arch(cfg)
local arch = vstudio.archFromConfig(cfg)
if arch == "Any CPU" then
arch = "AnyCPU"
end
return arch
end
--
-- Write the PropertyGroup element for a specific configuration block.
--
function cs2005.propertyGroup(cfg)
local platform = vstudio.projectPlatform(cfg)
local arch = cs2005.arch(cfg)
p.push('', platform, arch)
if arch ~= "AnyCPU" or _ACTION > "vs2008" then
p.x('%s', arch)
end
end
--
-- Generators for individual project elements.
--
function cs2005.applicationIcon(prj)
if prj.icon then
local icon = vstudio.path(prj, prj.icon)
_p(1,'')
_x(2,'%s', icon)
_p(1,'')
end
end
---------------------------------------------------------------------------
--
-- Support functions
--
---------------------------------------------------------------------------
--
-- Format and return a Visual Studio Condition attribute.
--
function cs2005.condition(cfg)
return string.format('Condition="\'$(Configuration)|$(Platform)\'==\'%s\'"', premake.esc(vstudio.projectConfig(cfg)))
end
---------------------------------------------------------------------------
--
-- Handlers for individual project elements
--
---------------------------------------------------------------------------
function cs2005.appDesignerFolder(cfg)
_p(2,'Properties')
end
function cs2005.assemblyName(cfg)
_p(2,'%s', cfg.buildtarget.basename)
end
function cs2005.commonProperties(prj)
if _ACTION > "vs2010" then
_p(1,'')
end
end
function cs2005.configurationCondition(cfg)
_x(2,'%s', cfg.buildcfg)
end
function cs2005.fileAlignment(cfg)
if _ACTION >= "vs2010" then
_p(2,'512')
end
end
function cs2005.outputType(cfg)
_p(2,'%s', dotnet.getkind(cfg))
end
function cs2005.platformCondition(cfg)
_p(2,'%s', cs2005.arch(cfg.project))
end
function cs2005.productVersion(cfg)
local action = premake.action.current()
if action.vstudio.productVersion then
_p(2,'%s', action.vstudio.productVersion)
end
end
function cs2005.projectGuid(cfg)
_p(2,'{%s}', cfg.uuid)
end
function cs2005.projectTypeGuids(cfg)
if cfg.flags.WPF then
_p(2,'{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}')
end
end
function cs2005.rootNamespace(cfg)
_p(2,'%s', cfg.namespace or cfg.buildtarget.basename)
end
function cs2005.schemaVersion(cfg)
local action = premake.action.current()
if action.vstudio.csprojSchemaVersion then
_p(2,'%s', action.vstudio.csprojSchemaVersion)
end
end
function cs2005.targetFrameworkVersion(cfg)
local action = premake.action.current()
local framework = cfg.dotnetframework or action.vstudio.targetFramework
if framework then
_p(2,'v%s', framework)
end
end
function cs2005.targetFrameworkProfile(cfg)
if _ACTION == "vs2010" then
_p(2,'')
_p(2,'')
end
end
function cs2005.targets(prj)
local bin = iif(_ACTION <= "vs2010", "MSBuildBinPath", "MSBuildToolsPath")
_p(1,'', bin)
_p(1,'')
end
function cs2005.xmlDeclaration()
if _ACTION > "vs2008" then
p.xmlUtf8()
end
end