-- -- vs2010_vcxproj.lua -- Generate a Visual Studio 201x C/C++ project. -- Copyright (c) 2009-2015 Jason Perkins and the Premake project -- premake.vstudio.vc2010 = {} local p = premake local vstudio = p.vstudio local project = p.project local config = p.config local fileconfig = p.fileconfig local tree = p.tree local m = p.vstudio.vc2010 --- -- Add namespace for element definition lists for premake.callArray() --- m.elements = {} -- -- Generate a Visual Studio 201x C++ project, with support for the new platforms API. -- m.elements.project = function(prj) return { m.xmlDeclaration, m.project, m.projectConfigurations, m.globals, m.importDefaultProps, m.configurationPropertiesGroup, m.importLanguageSettings, m.importExtensionSettings, m.propertySheetGroup, m.userMacros, m.outputPropertiesGroup, m.itemDefinitionGroups, m.assemblyReferences, m.files, m.projectReferences, m.importLanguageTargets, m.importExtensionTargets, } end function m.generate(prj) p.utf8() p.callArray(m.elements.project, prj) p.out('') end -- -- Output the XML declaration and opening tag. -- function m.project(prj) local action = p.action.current() p.push('', action.vstudio.toolsVersion) end -- -- Write out the list of project configurations, which pairs build -- configurations with architectures. -- function m.projectConfigurations(prj) -- build a list of all architectures used in this project local platforms = {} for cfg in project.eachconfig(prj) do local arch = vstudio.archFromConfig(cfg, true) if not table.contains(platforms, arch) then table.insert(platforms, arch) end end local configs = {} p.push('') for cfg in project.eachconfig(prj) do for _, arch in ipairs(platforms) do local prjcfg = vstudio.projectConfig(cfg, arch) if not configs[prjcfg] then configs[prjcfg] = prjcfg p.push('', vstudio.projectConfig(cfg, arch)) p.x('%s', vstudio.projectPlatform(cfg)) p.w('%s', arch) p.pop('') end end end p.pop('') end -- -- Write out the TargetFrameworkVersion property. -- function m.targetFramework(prj) local action = p.action.current() local tools = string.format(' ToolsVersion="%s"', action.vstudio.toolsVersion) local framework = prj.dotnetframework or action.vstudio.targetFramework or "4.0" p.w('v%s', framework) end -- -- Write out the Globals property group. -- m.elements.globals = function(prj) return { m.projectGuid, m.ignoreWarnDuplicateFilename, m.keyword, m.projectName, } end function m.globals(prj) m.propertyGroup(nil, "Globals") p.callArray(m.elements.globals, prj) p.pop('') end -- -- Write out the configuration property group: what kind of binary it -- produces, and some global settings. -- m.elements.configurationProperties = function(cfg) if cfg.kind == p.UTILITY then return { m.configurationType, m.platformToolset, } else return { m.configurationType, m.useDebugLibraries, m.useOfMfc, m.useOfAtl, m.clrSupport, m.characterSet, m.platformToolset, m.wholeProgramOptimization, m.nmakeOutDirs, } end end function m.configurationProperties(cfg) m.propertyGroup(cfg, "Configuration") p.callArray(m.elements.configurationProperties, cfg) p.pop('') end function m.configurationPropertiesGroup(prj) for cfg in project.eachconfig(prj) do m.configurationProperties(cfg) end end -- -- Write the output property group, which includes the output and intermediate -- directories, manifest, etc. -- m.elements.outputProperties = function(cfg) if cfg.kind == p.UTILITY then return { m.outDir, m.intDir, m.extensionsToDeleteOnClean, } else return { m.linkIncremental, m.ignoreImportLibrary, m.outDir, m.outputFile, m.intDir, m.targetName, m.targetExt, m.includePath, m.libraryPath, m.imageXexOutput, m.generateManifest, m.extensionsToDeleteOnClean, m.executablePath, } end end function m.outputProperties(cfg) if not vstudio.isMakefile(cfg) then m.propertyGroup(cfg) p.callArray(m.elements.outputProperties, cfg) p.pop('') end end function m.outputPropertiesGroup(prj) for cfg in project.eachconfig(prj) do m.outputProperties(cfg) m.nmakeProperties(cfg) end end -- -- Write the NMake property group for Makefile projects, which includes the custom -- build commands, output file location, etc. -- function m.nmakeProperties(cfg) if vstudio.isMakefile(cfg) then m.propertyGroup(cfg) m.nmakeOutput(cfg) m.nmakeCommandLine(cfg, cfg.buildcommands, "Build") m.nmakeCommandLine(cfg, cfg.rebuildcommands, "ReBuild") m.nmakeCommandLine(cfg, cfg.cleancommands, "Clean") p.pop('') end end -- -- Write a configuration's item definition group, which contains all -- of the per-configuration compile and link settings. -- m.elements.itemDefinitionGroup = function(cfg) if cfg.kind == p.UTILITY then return { m.ruleVars, m.buildEvents, } else return { m.clCompile, m.resourceCompile, m.linker, m.manifest, m.buildEvents, m.imageXex, m.deploy, m.ruleVars, m.buildLog, } end end function m.itemDefinitionGroup(cfg) if not vstudio.isMakefile(cfg) then p.push('', m.condition(cfg)) p.callArray(m.elements.itemDefinitionGroup, cfg) p.pop('') else if cfg == project.getfirstconfig(cfg.project) then p.w('') p.w('') end end end function m.itemDefinitionGroups(prj) for cfg in project.eachconfig(prj) do m.itemDefinitionGroup(cfg) end end -- -- Write the the compiler settings block. -- m.elements.clCompile = function(cfg) return { m.precompiledHeader, m.warningLevel, m.treatWarningAsError, m.disableSpecificWarnings, m.treatSpecificWarningsAsErrors, m.basicRuntimeChecks, m.clCompilePreprocessorDefinitions, m.clCompileUndefinePreprocessorDefinitions, m.clCompileAdditionalIncludeDirectories, m.clCompileAdditionalUsingDirectories, m.forceIncludes, m.debugInformationFormat, m.programDataBaseFileName, m.optimization, m.functionLevelLinking, m.intrinsicFunctions, m.minimalRebuild, m.omitFramePointers, m.stringPooling, m.runtimeLibrary, m.omitDefaultLib, m.exceptionHandling, m.runtimeTypeInfo, m.bufferSecurityCheck, m.treatWChar_tAsBuiltInType, m.floatingPointModel, m.inlineFunctionExpansion, m.enableEnhancedInstructionSet, m.multiProcessorCompilation, m.additionalCompileOptions, m.compileAs, m.callingConvention, } end function m.clCompile(cfg) p.push('') p.callArray(m.elements.clCompile, cfg) p.pop('') end -- -- Write out the resource compiler block. -- m.elements.resourceCompile = function(cfg) return { m.resourcePreprocessorDefinitions, m.resourceAdditionalIncludeDirectories, m.culture, } end function m.resourceCompile(cfg) if cfg.system ~= p.XBOX360 and p.config.hasFile(cfg, path.isresourcefile) then local contents = p.capture(function () p.push() p.callArray(m.elements.resourceCompile, cfg) p.pop() end) if #contents > 0 then p.push('') p.outln(contents) p.pop('') end end end -- -- Write out the linker tool block. -- m.elements.linker = function(cfg, explicit) return { m.link, m.lib, m.linkLibraryDependencies, } end function m.linker(cfg) local explicit = vstudio.needsExplicitLink(cfg) p.callArray(m.elements.linker, cfg, explicit) end m.elements.link = function(cfg, explicit) if cfg.kind == p.STATICLIB then return { m.subSystem, m.generateDebugInformation, m.optimizeReferences, } else return { m.subSystem, m.generateDebugInformation, m.optimizeReferences, m.additionalDependencies, m.additionalLibraryDirectories, m.importLibrary, m.entryPointSymbol, m.generateMapFile, m.moduleDefinitionFile, m.treatLinkerWarningAsErrors, m.ignoreDefaultLibraries, m.additionalLinkOptions, } end end function m.link(cfg, explicit) local contents = p.capture(function () p.push() p.callArray(m.elements.link, cfg, explicit) p.pop() end) if #contents > 0 then p.push('') p.outln(contents) p.pop('') end end m.elements.lib = function(cfg, explicit) if cfg.kind == p.STATICLIB then return { m.treatLinkerWarningAsErrors, m.additionalLinkOptions, } else return {} end end function m.lib(cfg, explicit) local contents = p.capture(function () p.push() p.callArray(m.elements.lib, cfg, explicit) p.pop() end) if #contents > 0 then p.push('') p.outln(contents) p.pop('') end end -- -- Write the manifest section. -- function m.manifest(cfg) if cfg.kind ~= p.STATICLIB then -- get the manifests files local manifests = {} for _, fname in ipairs(cfg.files) do if path.getextension(fname) == ".manifest" then table.insert(manifests, project.getrelative(cfg.project, fname)) end end if #manifests > 0 then p.push('') m.element("AdditionalManifestFiles", nil, "%s %%(AdditionalManifestFiles)", table.concat(manifests, " ")) p.pop('') end end end --- -- Write out the pre- and post-build event settings. --- function m.buildEvents(cfg) local write = function (event) local name = event .. "Event" local field = event:lower() local steps = cfg[field .. "commands"] local msg = cfg[field .. "message"] if #steps > 0 then steps = os.translateCommands(steps, p.WINDOWS) p.push('<%s>', name) p.x('%s', table.implode(steps, "", "", "\r\n")) if msg then p.x('%s', msg) end p.pop('', name) end end write("PreBuild") write("PreLink") write("PostBuild") end --- -- Write out project-level custom rule variables. --- function m.ruleVars(cfg) for i = 1, #cfg.rules do local rule = p.global.getRule(cfg.rules[i]) local contents = p.capture(function () p.push() for prop in p.rule.eachProperty(rule) do local fld = p.rule.getPropertyField(rule, prop) local value = cfg[fld.name] if value ~= nil then if fld.kind == "path" then value = vstudio.path(cfg, value) else value = p.rule.getPropertyString(rule, prop, value) end if value ~= nil and #value > 0 then m.element(prop.name, nil, '%s', value) end end end p.pop() end) if #contents > 0 then p.push('<%s>', rule.name) p.outln(contents) p.pop('', rule.name) end end end -- -- Reference any managed assemblies listed in the links() -- function m.assemblyReferences(prj) -- Visual Studio doesn't support per-config references; use -- whatever is contained in the first configuration local cfg = project.getfirstconfig(prj) local refs = config.getlinks(cfg, "system", "fullpath", "managed") if #refs > 0 then p.push('') for i = 1, #refs do local value = refs[i] -- If the link contains a '/' then it is a relative path to -- a local assembly. Otherwise treat it as a system assembly. if value:find('/', 1, true) then p.push('', path.getbasename(value)) p.x('%s', path.translate(value)) p.pop('') else p.x('', path.getbasename(value)) end end p.pop('') end end --- -- Write out the list of source code files, and any associated configuration. --- m.elements.files = function(prj, groups) return { m.clIncludeFiles, m.clCompileFiles, m.noneFiles, m.resourceCompileFiles, m.customBuildFiles, m.customRuleFiles, m.midlFiles } end function m.files(prj) local groups = m.categorizeSources(prj) p.callArray(m.elements.files, prj, groups) end m.elements.ClCompileFile = function(cfg, file) return {} end m.elements.ClCompileFileCfg = function(fcfg, condition) if fcfg then return { m.excludedFromBuild, m.objectFileName, m.clCompilePreprocessorDefinitions, m.clCompileUndefinePreprocessorDefinitions, m.optimization, m.forceIncludes, m.precompiledHeader, m.enableEnhancedInstructionSet, m.additionalCompileOptions, m.disableSpecificWarnings, m.treatSpecificWarningsAsErrors } else return { m.excludedFromBuild } end end function m.clCompileFiles(prj, groups) m.emitFiles(prj, groups, "ClCompile") end m.elements.ClIncludeFile = function(cfg, file) return {} end m.elements.ClIncludeFileCfg = function(fcfg, condition) return {} end function m.clIncludeFiles(prj, groups) m.emitFiles(prj, groups, "ClInclude") end m.elements.CustomBuildFile = function(cfg, file) return { m.fileType } end m.elements.CustomBuildFileCfg = function(fcfg, condition) return { m.excludedFromBuild, m.buildCommands, m.buildOutputs, m.buildMessage, m.buildAdditionalInputs } end function m.customBuildFiles(prj, groups) m.emitFiles(prj, groups, "CustomBuild", function (cfg, fcfg) return fileconfig.hasCustomBuildRule(fcfg) end) end function m.customRuleFiles(prj, groups) for i = 1, #prj.rules do local rule = p.global.getRule(prj.rules[i]) local files = groups[rule.name] if files and #files > 0 then p.push('') for _, file in ipairs(files) do local contents = p.capture(function() p.push() for prop in p.rule.eachProperty(rule) do local fld = p.rule.getPropertyField(rule, prop) for cfg in project.eachconfig(prj) do local fcfg = fileconfig.getconfig(file, cfg) if fcfg and fcfg[fld.name] then local value = p.rule.getPropertyString(rule, prop, fcfg[fld.name]) if value and #value > 0 then m.element(prop.name, m.condition(cfg), '%s', value) end end end end p.pop() end) if #contents > 0 then p.push('<%s Include=\"%s\">', rule.name, path.translate(file.relpath)) p.outln(contents) p.pop('', rule.name) else p.x('<%s Include=\"%s\" />', rule.name, path.translate(file.relpath)) end end p.pop('') end end end m.elements.NoneFile = function(cfg, file) return {} end m.elements.NoneFileCfg = function(fcfg, condition) return {} end function m.noneFiles(prj, groups) m.emitFiles(prj, groups, "None") end m.elements.ResourceCompileFile = function(cfg, file) return {} end m.elements.ResourceCompileFileCfg = function(fcfg, condition) return { m.excludedFromBuild } end function m.resourceCompileFiles(prj, groups) m.emitFiles(prj, groups, "ResourceCompile", function(cfg) return cfg.system == p.WINDOWS end) end m.elements.MidlFile = function(cfg, file) return {} end m.elements.MidlFileCfg = function(fcfg, condition) return { m.excludedFromBuild } end function m.midlFiles(prj, groups) m.emitFiles(prj, groups, "Midl", function(cfg) return cfg.system == p.WINDOWS end) end function m.categorizeSources(prj) local groups = prj._vc2010_sources if groups then return groups end groups = {} prj._vc2010_sources = groups local tr = project.getsourcetree(prj) tree.traverse(tr, { onleaf = function(node) local cat = m.categorizeFile(prj, node) groups[cat] = groups[cat] or {} table.insert(groups[cat], node) end }) -- sort by relative-to path; otherwise VS will reorder the files for group, files in pairs(groups) do table.sort(files, function (a, b) return a.relpath < b.relpath end) end return groups end function m.categorizeFile(prj, file) -- If any configuration for this file uses a custom build step, -- that's the category to use for cfg in project.eachconfig(prj) do local fcfg = fileconfig.getconfig(file, cfg) if fileconfig.hasCustomBuildRule(fcfg) then return "CustomBuild" end end -- If there is a custom rule associated with it, use that local rule = p.global.getRuleForFile(file.name, prj.rules) if rule then return rule.name end -- Otherwise use the file extension to deduce a category if path.iscppfile(file.name) then return "ClCompile" elseif path.iscppheader(file.name) then return "ClInclude" elseif path.isresourcefile(file.name) then return "ResourceCompile" elseif path.isidlfile(file.name) then return "Midl" else return "None" end end function m.emitFiles(prj, groups, group, check) local files = groups[group] if files and #files > 0 then p.push('') for _, file in ipairs(files) do local contents = p.capture(function () p.push() p.callArray(m.elements[group .. "File"], cfg, file) for cfg in project.eachconfig(prj) do local fcfg = fileconfig.getconfig(file, cfg) if not check or check(cfg, fcfg) then p.callArray(m.elements[group .. "FileCfg"], fcfg, m.condition(cfg)) end end p.pop() end) local rel = path.translate(file.relpath) if #contents > 0 then p.push('<%s Include="%s">', group, rel) p.outln(contents) p.pop('', group) else p.x('<%s Include="%s" />', group, rel) end end p.pop('') end end -- -- Generate the list of project dependencies. -- m.elements.projectReferences = function(prj, ref) if prj.clr ~= p.OFF then return { m.referenceProject, m.referencePrivate, m.referenceOutputAssembly, m.referenceCopyLocalSatelliteAssemblies, m.referenceLinkLibraryDependencies, m.referenceUseLibraryDependences, } else return { m.referenceProject, } end end function m.projectReferences(prj) local refs = project.getdependencies(prj, 'linkOnly') if #refs > 0 then p.push('') for _, ref in ipairs(refs) do local relpath = vstudio.path(prj, vstudio.projectfile(ref)) p.push('', relpath) p.callArray(m.elements.projectReferences, prj, ref) p.pop('') end p.pop('') end end --------------------------------------------------------------------------- -- -- Handlers for individual project elements -- --------------------------------------------------------------------------- function m.additionalDependencies(cfg, explicit) local links -- check to see if this project uses an external toolset. If so, let the -- toolset define the format of the links local toolset = config.toolset(cfg) if toolset then links = toolset.getlinks(cfg, not explicit) else links = vstudio.getLinks(cfg, explicit) end if #links > 0 then links = path.translate(table.concat(links, ";")) m.element("AdditionalDependencies", nil, "%s;%%(AdditionalDependencies)", links) end end function m.additionalIncludeDirectories(cfg, includedirs) if #includedirs > 0 then local dirs = vstudio.path(cfg, includedirs) if #dirs > 0 then m.element("AdditionalIncludeDirectories", nil, "%s;%%(AdditionalIncludeDirectories)", table.concat(dirs, ";")) end end end function m.additionalLibraryDirectories(cfg) if #cfg.libdirs > 0 then local dirs = table.concat(vstudio.path(cfg, cfg.libdirs), ";") m.element("AdditionalLibraryDirectories", nil, "%s;%%(AdditionalLibraryDirectories)", dirs) end end function m.additionalUsingDirectories(cfg) if #cfg.usingdirs > 0 then local dirs = vstudio.path(cfg, cfg.usingdirs) if #dirs > 0 then m.element("AdditionalUsingDirectories", nil, "%s;%%(AdditionalUsingDirectories)", table.concat(dirs, ";")) end end end function m.additionalCompileOptions(cfg, condition) if #cfg.buildoptions > 0 then local opts = table.concat(cfg.buildoptions, " ") m.element("AdditionalOptions", condition, '%s %%(AdditionalOptions)', opts) end end function m.additionalLinkOptions(cfg) if #cfg.linkoptions > 0 then local opts = table.concat(cfg.linkoptions, " ") m.element("AdditionalOptions", nil, "%s %%(AdditionalOptions)", opts) end end function m.basicRuntimeChecks(cfg) local runtime = config.getruntime(cfg) if cfg.flags.NoRuntimeChecks or (config.isOptimizedBuild(cfg) and runtime:endswith("Debug")) then m.element("BasicRuntimeChecks", nil, "Default") end end function m.buildAdditionalInputs(fcfg, condition) if fcfg.buildinputs and #fcfg.buildinputs > 0 then local inputs = project.getrelative(fcfg.project, fcfg.buildinputs) m.element("AdditionalInputs", condition, '%s', table.concat(inputs, ";")) end end function m.buildCommands(fcfg, condition) local commands = os.translateCommands(fcfg.buildcommands, p.WINDOWS) commands = table.concat(commands,'\r\n') m.element("Command", condition, '%s', commands) end function m.buildLog(cfg) if cfg.buildlog and #cfg.buildlog > 0 then p.push('') m.element("Path", nil, "%s", vstudio.path(cfg, cfg.buildlog)) p.pop('') end end function m.buildMessage(fcfg, condition) if fcfg.buildmessage then m.element("Message", condition, '%s', fcfg.buildmessage) end end function m.buildOutputs(fcfg, condition) local outputs = project.getrelative(fcfg.project, fcfg.buildoutputs) m.element("Outputs", condition, '%s', table.concat(outputs, ";")) end function m.characterSet(cfg) if not vstudio.isMakefile(cfg) then m.element("CharacterSet", nil, iif(cfg.flags.Unicode, "Unicode", "MultiByte")) end end function m.wholeProgramOptimization(cfg) if cfg.flags.LinkTimeOptimization then m.element("WholeProgramOptimization", nil, "true") end end function m.clCompileAdditionalIncludeDirectories(cfg) m.additionalIncludeDirectories(cfg, cfg.includedirs) end function m.clCompileAdditionalUsingDirectories(cfg) m.additionalUsingDirectories(cfg, cfg.usingdirs) end function m.clCompilePreprocessorDefinitions(cfg, condition) m.preprocessorDefinitions(cfg, cfg.defines, false, condition) end function m.clCompileUndefinePreprocessorDefinitions(cfg, condition) m.undefinePreprocessorDefinitions(cfg, cfg.undefines, false, condition) end function m.clrSupport(cfg) local value if cfg.clr == "On" or cfg.clr == "Unsafe" then value = "true" elseif cfg.clr ~= p.OFF then value = cfg.clr end if value then m.element("CLRSupport", nil, value) end end function m.compileAs(cfg) if cfg.project.language == "C" then m.element("CompileAs", nil, "CompileAsC") end end function m.configurationType(cfg) local types = { SharedLib = "DynamicLibrary", StaticLib = "StaticLibrary", ConsoleApp = "Application", WindowedApp = "Application", Makefile = "Makefile", None = "Makefile", Utility = "Utility", } m.element("ConfigurationType", nil, types[cfg.kind]) end function m.culture(cfg) local value = vstudio.cultureForLocale(cfg.locale) if value then m.element("Culture", nil, "0x%04x", tostring(value)) end end function m.debugInformationFormat(cfg) local value local tool, toolVersion = p.config.toolset(cfg) if cfg.flags.Symbols then if cfg.debugformat == "c7" then value = "OldStyle" elseif cfg.architecture == "x86_64" or cfg.clr ~= p.OFF or config.isOptimizedBuild(cfg) or cfg.editandcontinue == p.OFF or (toolVersion and toolVersion:startswith("LLVM-vs")) then value = "ProgramDatabase" else value = "EditAndContinue" end end if value then m.element("DebugInformationFormat", nil, value) end end function m.deploy(cfg) if cfg.system == p.XBOX360 then p.push('') m.element("DeploymentType", nil, "CopyToHardDrive") m.element("DvdEmulationType", nil, "ZeroSeekTimes") m.element("DeploymentFiles", nil, "$(RemoteRoot)=$(ImagePath);") p.pop('') end end function m.enableEnhancedInstructionSet(cfg, condition) local v local x = cfg.vectorextensions if x == "AVX" and _ACTION > "vs2010" then v = "AdvancedVectorExtensions" elseif x == "AVX2" and _ACTION > "vs2012" then v = "AdvancedVectorExtensions2" elseif cfg.architecture ~= "x86_64" then if x == "SSE2" or x == "SSE3" or x == "SSSE3" or x == "SSE4.1" then v = "StreamingSIMDExtensions2" elseif x == "SSE" then v = "StreamingSIMDExtensions" end end if v then m.element('EnableEnhancedInstructionSet', condition, v) end end function m.entryPointSymbol(cfg) if cfg.entrypoint then m.element("EntryPointSymbol", nil, cfg.entrypoint) else if (cfg.kind == premake.CONSOLEAPP or cfg.kind == premake.WINDOWEDAPP) and not cfg.flags.WinMain and cfg.clr == p.OFF and cfg.system ~= p.XBOX360 then m.element("EntryPointSymbol", nil, "mainCRTStartup") end end end function m.exceptionHandling(cfg) local value if cfg.exceptionhandling == p.OFF then m.element("ExceptionHandling", nil, "false") elseif cfg.exceptionhandling == "SEH" then m.element("ExceptionHandling", nil, "Async") end end function m.excludedFromBuild(filecfg, condition) if not filecfg or filecfg.flags.ExcludeFromBuild then m.element("ExcludedFromBuild", condition, "true") end end function m.extensionsToDeleteOnClean(cfg) if #cfg.cleanextensions > 0 then local value = table.implode(cfg.cleanextensions, "*", ";", "") m.element("ExtensionsToDeleteOnClean", nil, value .. "$(ExtensionsToDeleteOnClean)") end end function m.fileType(cfg, file) m.element("FileType", nil, "Document") end function m.floatingPointModel(cfg) if cfg.floatingpoint then m.element("FloatingPointModel", nil, cfg.floatingpoint) end end function m.inlineFunctionExpansion(cfg) if cfg.inlining then local types = { Default = "Default", Disabled = "Disabled", Explicit = "OnlyExplicitInline", Auto = "AnySuitable", } m.element("InlineFunctionExpansion", nil, types[cfg.inlining]) end end function m.forceIncludes(cfg, condition) if #cfg.forceincludes > 0 then local includes = vstudio.path(cfg, cfg.forceincludes) if #includes > 0 then m.element("ForcedIncludeFiles", condition, table.concat(includes, ';')) end end if #cfg.forceusings > 0 then local usings = vstudio.path(cfg, cfg.forceusings) if #usings > 0 then m.element("ForcedUsingFiles", condition, table.concat(usings, ';')) end end end function m.functionLevelLinking(cfg) if config.isOptimizedBuild(cfg) then m.element("FunctionLevelLinking", nil, "true") end end function m.generateDebugInformation(cfg) m.element("GenerateDebugInformation", nil, tostring(cfg.flags.Symbols ~= nil)) end function m.generateManifest(cfg) if cfg.flags.NoManifest then m.element("GenerateManifest", nil, "false") end end function m.generateMapFile(cfg) if cfg.flags.Maps then m.element("GenerateMapFile", nil, "true") end end function m.ignoreDefaultLibraries(cfg) if #cfg.ignoredefaultlibraries > 0 then local ignored = cfg.ignoredefaultlibraries for i = 1, #ignored do -- Add extension if required if not p.tools.msc.getLibraryExtensions()[ignored[i]:match("[^.]+$")] then ignored[i] = path.appendextension(ignored[i], ".lib") end end m.element("IgnoreSpecificDefaultLibraries", condition, table.concat(ignored, ';')) end end function m.ignoreWarnDuplicateFilename(prj) -- VS 2013 warns on duplicate file names, even those files which are -- contained in different, mututally exclusive configurations. See: -- http://connect.microsoft.com/VisualStudio/feedback/details/797460/incorrect-warning-msb8027-reported-for-files-excluded-from-build -- Premake already adds unique object names to conflicting file names, so -- just go ahead and disable that warning. if _ACTION > "vs2012" then m.element("IgnoreWarnCompileDuplicatedFilename", nil, "true") end end function m.ignoreImportLibrary(cfg) if cfg.kind == p.SHAREDLIB and cfg.flags.NoImportLib then m.element("IgnoreImportLibrary", nil, "true") end end function m.imageXex(cfg) if cfg.system == p.XBOX360 then p.push('') if cfg.configfile then m.element("ConfigurationFile", nil, "%s", cfg.configfile) else p.w('') p.w('') end p.w('') p.w('') p.pop('') end end function m.imageXexOutput(cfg) if cfg.system == p.XBOX360 then m.element("ImageXexOutput", nil, "%s", "$(OutDir)$(TargetName).xex") end end function m.importLanguageTargets(prj) p.w('') end m.elements.importExtensionTargets = function(prj) return { m.importRuleTargets, } end function m.importExtensionTargets(prj) p.push('') p.callArray(m.elements.importExtensionTargets, prj) p.pop('') end function m.importRuleTargets(prj) for i = 1, #prj.rules do local rule = p.global.getRule(prj.rules[i]) local loc = vstudio.path(prj, p.filename(rule, ".targets")) p.x('', loc) end end function m.importDefaultProps(prj) p.w('') end function m.importLanguageSettings(prj) p.w('') end m.elements.importExtensionSettings = function(prj) return { m.importRuleSettings, } end function m.importExtensionSettings(prj) p.push('') p.callArray(m.elements.importExtensionSettings, prj) p.pop('') end function m.importRuleSettings(prj) for i = 1, #prj.rules do local rule = p.global.getRule(prj.rules[i]) local loc = vstudio.path(prj, p.filename(rule, ".props")) p.x('', loc) end end function m.importLibrary(cfg) if cfg.kind == p.SHAREDLIB then m.element("ImportLibrary", nil, "%s", path.translate(cfg.linktarget.relpath)) end end function m.includePath(cfg) local dirs = vstudio.path(cfg, cfg.sysincludedirs) if #dirs > 0 then m.element("IncludePath", nil, "%s;$(IncludePath)", table.concat(dirs, ";")) end end function m.intDir(cfg) local objdir = vstudio.path(cfg, cfg.objdir) m.element("IntDir", nil, "%s\\", objdir) end function m.intrinsicFunctions(cfg) if config.isOptimizedBuild(cfg) then m.element("IntrinsicFunctions", nil, "true") end end function m.keyword(prj) -- try to determine what kind of targets we're building here local isWin, isManaged, isMakefile for cfg in project.eachconfig(prj) do if cfg.system == p.WINDOWS then isWin = true end if cfg.clr ~= p.OFF then isManaged = true end if vstudio.isMakefile(cfg) then isMakefile = true end end if isWin then if isMakefile then m.element("Keyword", nil, "MakeFileProj") else if isManaged then m.targetFramework(prj) m.element("Keyword", nil, "ManagedCProj") else m.element("Keyword", nil, "Win32Proj") end m.element("RootNamespace", nil, "%s", prj.name) end end end function m.libraryPath(cfg) local dirs = vstudio.path(cfg, cfg.syslibdirs) if #dirs > 0 then m.element("LibraryPath", nil, "%s;$(LibraryPath)", table.concat(dirs, ";")) end end function m.linkIncremental(cfg) if cfg.kind ~= p.STATICLIB then m.element("LinkIncremental", nil, "%s", tostring(config.canLinkIncremental(cfg))) end end function m.linkLibraryDependencies(cfg, explicit) -- Left to its own devices, VS will happily link against a project dependency -- that has been excluded from the build. As a workaround, disable dependency -- linking and list all siblings explicitly if explicit then p.push('') m.element("LinkLibraryDependencies", nil, "false") p.pop('') end end function m.minimalRebuild(cfg) if config.isOptimizedBuild(cfg) or cfg.flags.NoMinimalRebuild or cfg.flags.MultiProcessorCompile or cfg.debugformat == p.C7 then m.element("MinimalRebuild", nil, "false") end end function m.moduleDefinitionFile(cfg) local df = config.findfile(cfg, ".def") if df then m.element("ModuleDefinitionFile", nil, "%s", df) end end function m.multiProcessorCompilation(cfg) if cfg.flags.MultiProcessorCompile then m.element("MultiProcessorCompilation", nil, "true") end end function m.nmakeCommandLine(cfg, commands, phase) if #commands > 0 then commands = os.translateCommands(commands, p.WINDOWS) commands = table.concat(p.esc(commands), p.eol()) p.w('%s', phase, commands, phase) end end function m.nmakeOutDirs(cfg) if vstudio.isMakefile(cfg) then m.outDir(cfg) m.intDir(cfg) end end function m.nmakeOutput(cfg) m.element("NMakeOutput", nil, "$(OutDir)%s", cfg.buildtarget.name) end function m.objectFileName(fcfg) if fcfg.objname ~= fcfg.basename then m.element("ObjectFileName", m.condition(fcfg.config), "$(IntDir)\\%s.obj", fcfg.objname) end end function m.omitDefaultLib(cfg) if cfg.flags.OmitDefaultLibrary then m.element("OmitDefaultLibName", nil, "true") end end function m.omitFramePointers(cfg) if cfg.flags.NoFramePointer then m.element("OmitFramePointers", nil, "true") end end function m.optimizeReferences(cfg) if config.isOptimizedBuild(cfg) then m.element("EnableCOMDATFolding", nil, "true") m.element("OptimizeReferences", nil, "true") end end function m.optimization(cfg, condition) local map = { Off="Disabled", On="Full", Debug="Disabled", Full="Full", Size="MinSpace", Speed="MaxSpeed" } local value = map[cfg.optimize] if value or not condition then m.element('Optimization', condition, value or "Disabled") end end function m.outDir(cfg) local outdir = vstudio.path(cfg, cfg.buildtarget.directory) m.element("OutDir", nil, "%s\\", outdir) end function m.outputFile(cfg) if cfg.system == p.XBOX360 then m.element("OutputFile", nil, "$(OutDir)%s", cfg.buildtarget.name) end end function m.executablePath(cfg) local dirs = vstudio.path(cfg, cfg.bindirs) if #dirs > 0 then m.element("ExecutablePath", nil, "%s;$(ExecutablePath)", table.concat(dirs, ";")) end end function m.platformToolset(cfg) local tool, version = p.config.toolset(cfg) if not version then local action = p.action.current() version = action.vstudio.platformToolset end if version then if cfg.kind == p.NONE or cfg.kind == p.MAKEFILE then if p.config.hasFile(cfg, path.iscppfile) then m.element("PlatformToolset", nil, version) end else m.element("PlatformToolset", nil, version) end end end function m.precompiledHeader(cfg, condition) prjcfg, filecfg = p.config.normalize(cfg) if filecfg then if prjcfg.pchsource == filecfg.abspath and not prjcfg.flags.NoPCH then m.element('PrecompiledHeader', condition, 'Create') elseif filecfg.flags.NoPCH then m.element('PrecompiledHeader', condition, 'NotUsing') end else if not prjcfg.flags.NoPCH and prjcfg.pchheader then m.element("PrecompiledHeader", nil, "Use") m.element("PrecompiledHeaderFile", nil, "%s", prjcfg.pchheader) else m.element("PrecompiledHeader", nil, "NotUsing") end end end function m.preprocessorDefinitions(cfg, defines, escapeQuotes, condition) if #defines > 0 then defines = table.concat(defines, ";") if escapeQuotes then defines = defines:gsub('"', '\\"') end defines = p.esc(defines) .. ";%%(PreprocessorDefinitions)" m.element('PreprocessorDefinitions', condition, defines) end end function m.undefinePreprocessorDefinitions(cfg, undefines, escapeQuotes, condition) if #undefines > 0 then undefines = table.concat(undefines, ";") if escapeQuotes then undefines = undefines:gsub('"', '\\"') end undefines = p.esc(undefines) .. ";%%(UndefinePreprocessorDefinitions)" m.element('UndefinePreprocessorDefinitions', condition, undefines) end end function m.programDataBaseFileName(cfg) -- just a placeholder for overriding; will use the default VS name -- for changes, see https://github.com/premake/premake-core/issues/151 end function m.projectGuid(prj) m.element("ProjectGuid", nil, "{%s}", prj.uuid) end function m.projectName(prj) if prj.name ~= prj.filename then m.element("ProjectName", nil, "%s", prj.name) end end function m.propertyGroup(cfg, label) local cond if cfg then cond = string.format(' %s', m.condition(cfg)) end if label then label = string.format(' Label="%s"', label) end p.push('', cond or "", label or "") end function m.propertySheets(cfg) p.push('', m.condition(cfg)) p.w('') p.pop('') end function m.propertySheetGroup(prj) for cfg in project.eachconfig(prj) do m.propertySheets(cfg) end end function m.referenceCopyLocalSatelliteAssemblies(prj, ref) m.element("CopyLocalSatelliteAssemblies", nil, "false") end function m.referenceLinkLibraryDependencies(prj, ref) m.element("LinkLibraryDependencies", nil, "true") end function m.referenceOutputAssembly(prj, ref) m.element("ReferenceOutputAssembly", nil, "true") end function m.referencePrivate(prj, ref) m.element("Private", nil, "true") end function m.referenceProject(prj, ref) m.element("Project", nil, "{%s}", ref.uuid) end function m.referenceUseLibraryDependences(prj, ref) m.element("UseLibraryDependencyInputs", nil, "false") end function m.resourceAdditionalIncludeDirectories(cfg) m.additionalIncludeDirectories(cfg, table.join(cfg.includedirs, cfg.resincludedirs)) end function m.resourcePreprocessorDefinitions(cfg) m.preprocessorDefinitions(cfg, table.join(cfg.defines, cfg.resdefines), true) end function m.runtimeLibrary(cfg) local runtimes = { StaticDebug = "MultiThreadedDebug", StaticRelease = "MultiThreaded", } local runtime = runtimes[config.getruntime(cfg)] if runtime then m.element("RuntimeLibrary", nil, runtime) end end function m.callingConvention(cfg) if cfg.callingconvention then m.element("CallingConvention", nil, cfg.callingconvention) end end function m.runtimeTypeInfo(cfg) if cfg.rtti == p.OFF and cfg.clr == p.OFF then m.element("RuntimeTypeInfo", nil, "false") elseif cfg.rtti == p.ON then m.element("RuntimeTypeInfo", nil, "true") end end function m.bufferSecurityCheck(cfg) local tool, toolVersion = p.config.toolset(cfg) if cfg.flags.NoBufferSecurityCheck or (toolVersion and toolVersion:startswith("LLVM-vs")) then m.element("BufferSecurityCheck", nil, "false") end end function m.stringPooling(cfg) if config.isOptimizedBuild(cfg) then m.element("StringPooling", nil, "true") end end function m.subSystem(cfg) if cfg.system ~= p.XBOX360 then local subsystem = iif(cfg.kind == p.CONSOLEAPP, "Console", "Windows") m.element("SubSystem", nil, subsystem) end end function m.targetExt(cfg) local ext = cfg.buildtarget.extension if ext ~= "" then m.element("TargetExt", nil, "%s", ext) else p.w('') p.w('') end end function m.targetName(cfg) m.element("TargetName", nil, "%s%s", cfg.buildtarget.prefix, cfg.buildtarget.basename) end function m.treatLinkerWarningAsErrors(cfg) if cfg.flags.FatalLinkWarnings then local el = iif(cfg.kind == p.STATICLIB, "Lib", "Linker") m.element("Treat" .. el .. "WarningAsErrors", nil, "true") end end function m.treatWChar_tAsBuiltInType(cfg) local map = { On = "true", Off = "false" } local value = map[cfg.nativewchar] if value then m.element("TreatWChar_tAsBuiltInType", nil, value) end end function m.treatWarningAsError(cfg) if cfg.flags.FatalCompileWarnings and cfg.warnings ~= p.OFF then m.element("TreatWarningAsError", nil, "true") end end function m.disableSpecificWarnings(cfg, condition) if #cfg.disablewarnings > 0 then local warnings = table.concat(cfg.disablewarnings, ";") warnings = p.esc(warnings) .. ";%%(DisableSpecificWarnings)" m.element('DisableSpecificWarnings', condition, warnings) end end function m.treatSpecificWarningsAsErrors(cfg, condition) if #cfg.fatalwarnings > 0 then local fatal = table.concat(cfg.fatalwarnings, ";") fatal = p.esc(fatal) .. ";%%(TreatSpecificWarningsAsErrors)" m.element('TreatSpecificWarningsAsErrors', condition, fatal) end end function m.useDebugLibraries(cfg) local runtime = config.getruntime(cfg) m.element("UseDebugLibraries", nil, tostring(runtime:endswith("Debug"))) end function m.useOfMfc(cfg) if cfg.flags.MFC then m.element("UseOfMfc", nil, iif(cfg.flags.StaticRuntime, "Static", "Dynamic")) end end function m.useOfAtl(cfg) if cfg.atl then m.element("UseOfATL", nil, cfg.atl) end end function m.userMacros(cfg) p.w('') end function m.warningLevel(cfg) local map = { Off = "TurnOffAllWarnings", Extra = "Level4" } m.element("WarningLevel", nil, "%s", map[cfg.warnings] or "Level3") end function m.xmlDeclaration() p.xmlUtf8() end --------------------------------------------------------------------------- -- -- Support functions -- --------------------------------------------------------------------------- -- -- Format and return a Visual Studio Condition attribute. -- function m.condition(cfg) return string.format('Condition="\'$(Configuration)|$(Platform)\'==\'%s\'"', p.esc(vstudio.projectConfig(cfg))) end -- -- Output an individual project XML element, with an optional configuration -- condition. -- -- @param depth -- How much to indent the element. -- @param name -- The element name. -- @param condition -- An optional configuration condition, formatted with vc2010.condition(). -- @param value -- The element value, which may contain printf formatting tokens. -- @param ... -- Optional additional arguments to satisfy any tokens in the value. -- function m.element(name, condition, value, ...) if select('#',...) == 0 then value = p.esc(value) end local format if condition then format = string.format('<%s %s>%s', name, condition, value, name) else format = string.format('<%s>%s', name, value, name) end p.x(format, ...) end