-- -- vs2010_vcxproj.lua -- Generate a Visual Studio 201x C/C++ project. -- Copyright (c) Jason Perkins and the Premake project -- local p = premake p.vstudio.vc2010 = {} 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 p.callArray() --- m.elements = {} m.conditionalElements = {} -- -- 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, m.ensureNuGetPackageBuildImports, } 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() if _ACTION >= "vs2019" then p.push('') else p.push('', action.vstudio.toolsVersion) end 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, m.preferredToolArchitecture, m.latestTargetPlatformVersion, m.windowsTargetPlatformVersion, } end m.elements.globalsCondition = function(prj, cfg) return { m.windowsTargetPlatformVersion, } end function m.globals(prj) -- Write out the project-level globals m.propertyGroup(nil, "Globals") p.callArray(m.elements.globals, prj) p.pop('') -- Write out the configurable globals for cfg in project.eachconfig(prj) do -- Find out whether we're going to actually write a property out local captured = p.capture( function() p.push() p.callArray(m.elements.globalsCondition, prj, cfg) p.pop() end) -- If we do have something, create the entry, skip otherwise if captured ~= '' then m.propertyGroup(cfg, "Globals") p.callArray(m.elements.globalsCondition, prj, cfg) p.pop('') end end 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, m.windowsSDKDesktopARMSupport, } 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, m.executablePath, } else return { m.linkIncremental, m.ignoreImportLibrary, m.outDir, m.intDir, m.targetName, m.targetExt, m.includePath, m.libraryPath, 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 -- -- Write the NMake property group for Makefile projects, which includes the custom -- build commands, output file location, etc. -- m.elements.nmakeProperties = function(cfg) return { m.executablePath, m.includePath, m.libraryPath, m.nmakeOutput, m.nmakeBuildCommands, m.nmakeRebuildCommands, m.nmakeCleanCommands, m.nmakePreprocessorDefinitions, m.nmakeIncludeDirs } end function m.nmakeProperties(cfg) if vstudio.isMakefile(cfg) then m.propertyGroup(cfg) p.callArray(m.elements.nmakeProperties, cfg) p.pop('') end end -- -- Output properties and NMake properties should appear side-by-side -- for each configuration. -- function m.outputPropertiesGroup(prj) for cfg in project.eachconfig(prj) do m.outputProperties(cfg) m.nmakeProperties(cfg) 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, m.buildLog, } else return { m.clCompile, m.buildStep, m.fxCompile, m.resourceCompile, m.linker, m.manifest, m.buildEvents, 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) local calls = { 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.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.floatingPointExceptions, m.inlineFunctionExpansion, m.enableEnhancedInstructionSet, m.multiProcessorCompilation, m.additionalCompileOptions, m.compileAs, m.callingConvention, m.languageStandard, m.structMemberAlignment, } if cfg.kind == p.STATICLIB then table.insert(calls, m.programDatabaseFileName) end return calls end function m.clCompile(cfg) p.push('') p.callArray(m.elements.clCompile, cfg) p.pop('') end -- -- Write the the compiler settings block. -- m.elements.buildStep = function(cfg) local calls = { m.buildCommands, m.buildMessage, m.buildOutputs, m.buildInputs } return calls end function m.buildStep(cfg) if #cfg.buildCommands > 0 or #cfg.buildOutputs > 0 or #cfg.buildInputs > 0 or cfg.buildMessage then p.push('') p.callArray(m.elements.buildStep, cfg) p.pop('') end end -- -- Write the settings block. -- m.elements.fxCompile = function(cfg) return { m.fxCompilePreprocessorDefinition, m.fxCompileAdditionalIncludeDirs, m.fxCompileShaderType, m.fxCompileShaderModel, m.fxCompileShaderEntry, m.fxCompileShaderVariableName, m.fxCompileShaderHeaderOutput, m.fxCompileShaderObjectOutput, m.fxCompileShaderAssembler, m.fxCompileShaderAssemblerOutput, m.fxCompileShaderAdditionalOptions, } end function m.fxCompile(cfg) if p.config.hasFile(cfg, path.ishlslfile) then local contents = p.capture(function () p.push() p.callArray(m.elements.fxCompile, cfg) p.pop() end) if #contents > 0 then p.push('') p.outln(contents) p.pop('') end end 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 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.fullProgramDatabaseFile, m.generateDebugInformation, m.optimizeReferences, } else return { m.subSystem, m.fullProgramDatabaseFile, m.generateDebugInformation, m.optimizeReferences, m.additionalDependencies, m.additionalLibraryDirectories, m.importLibrary, m.entryPointSymbol, m.generateMapFile, m.moduleDefinitionFile, m.treatLinkerWarningAsErrors, m.ignoreDefaultLibraries, m.largeAddressAware, m.targetMachine, m.additionalLinkOptions, m.programDatabaseFile, m.assemblyDebug, } 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.additionalDependencies, m.additionalLibraryDirectories, m.treatLinkerWarningAsErrors, m.targetMachine, 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. -- m.elements.manifest = function(cfg) return { m.enableDpiAwareness, m.additionalManifestFiles, } end function m.manifest(cfg) if cfg.kind ~= p.STATICLIB then local contents = p.capture(function () p.push() p.callArray(m.elements.manifest, cfg) p.pop() end) if #contents > 0 then p.push('') p.outln(contents) 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.translateCommandsAndPaths(steps, cfg.project.basedir, cfg.project.location) 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 --- -- Transform property to string --- function m.getRulePropertyString(rule, prop, value, kind) -- list of paths if kind == "list:path" then return table.concat(vstudio.path(cfg, value), ';') end -- path if kind == "path" then return vstudio.path(cfg, value) end -- list if type(value) == "table" then return table.concat(value, ";") end -- enum if prop.values then value = table.findKeyByValue(prop.values, value) end -- primitive return tostring(value) 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 value = m.getRulePropertyString(rule, prop, value, fld.kind) 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 function m.generatedFile(cfg, file) if file.generated then local path = path.translate(file.dependsOn.relpath) m.element("AutoGen", nil, 'true') m.element("DependentUpon", nil, path) end end --- -- Write out the list of source code files, and any associated configuration. --- function m.files(prj) local groups = m.categorizeSources(prj) for _, group in ipairs(groups) do group.category.emitFiles(prj, group) end end m.categories = {} --- -- ClInclude group --- m.categories.ClInclude = { name = "ClInclude", extensions = { ".h", ".hh", ".hpp", ".hxx", ".inl" }, priority = 1, emitFiles = function(prj, group) m.emitFiles(prj, group, "ClInclude", {m.generatedFile}) end, emitFilter = function(prj, group) m.filterGroup(prj, group, "ClInclude") end } --- -- ClCompile group --- m.categories.ClCompile = { name = "ClCompile", extensions = { ".cc", ".cpp", ".cxx", ".c++", ".c", ".s", ".m", ".mm" }, priority = 2, emitFiles = function(prj, group) local fileCfgFunc = 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, m.basicRuntimeChecks, m.exceptionHandling, m.compileAsManaged, m.compileAs, m.runtimeTypeInfo, m.warningLevelFile, } else return { m.excludedFromBuild } end end m.emitFiles(prj, group, "ClCompile", {m.generatedFile}, fileCfgFunc) end, emitFilter = function(prj, group) m.filterGroup(prj, group, "ClCompile") end } --- -- FxCompile group --- m.categories.FxCompile = { name = "FxCompile", extensions = { ".hlsl" }, priority = 4, emitFiles = function(prj, group) local fileCfgFunc = function(fcfg, condition) if fcfg then return { m.excludedFromBuild, m.fxCompilePreprocessorDefinition, m.fxCompileAdditionalIncludeDirs, m.fxCompileShaderType, m.fxCompileShaderModel, m.fxCompileShaderEntry, m.fxCompileShaderVariableName, m.fxCompileShaderHeaderOutput, m.fxCompileShaderObjectOutput, m.fxCompileShaderAssembler, m.fxCompileShaderAssemblerOutput, m.fxCompileShaderAdditionalOptions, } else return { m.excludedFromBuild } end end m.emitFiles(prj, group, "FxCompile", nil, fileCfgFunc) end, emitFilter = function(prj, group) m.filterGroup(prj, group, "FxCompile") end } --- -- None group --- m.categories.None = { name = "None", priority = 5, emitFiles = function(prj, group) m.emitFiles(prj, group, "None", {m.generatedFile}) end, emitFilter = function(prj, group) m.filterGroup(prj, group, "None") end } --- -- ResourceCompile group --- m.categories.ResourceCompile = { name = "ResourceCompile", extensions = ".rc", priority = 6, emitFiles = function(prj, group) local fileCfgFunc = { m.excludedFromBuild } m.emitFiles(prj, group, "ResourceCompile", nil, fileCfgFunc) end, emitFilter = function(prj, group) m.filterGroup(prj, group, "ResourceCompile") end } --- -- CustomBuild group --- m.categories.CustomBuild = { name = "CustomBuild", priority = 7, emitFiles = function(prj, group) local fileFunc = { m.fileType } local fileCfgFunc = { m.excludedFromBuild, m.buildCommands, m.buildOutputs, m.linkObjects, m.buildMessage, m.buildAdditionalInputs } m.emitFiles(prj, group, "CustomBuild", fileFunc, fileCfgFunc, function (cfg, fcfg) return fileconfig.hasCustomBuildRule(fcfg) end) end, emitFilter = function(prj, group) m.filterGroup(prj, group, "CustomBuild") end } --- -- Midl group --- m.categories.Midl = { name = "Midl", extensions = ".idl", priority = 8, emitFiles = function(prj, group) local fileCfgFunc = { m.excludedFromBuild } m.emitFiles(prj, group, "Midl", nil, fileCfgFunc, function(cfg) return cfg.system == p.WINDOWS end) end, emitFilter = function(prj, group) m.filterGroup(prj, group, "Midl") end } --- -- Masm group --- m.categories.Masm = { name = "Masm", extensions = ".asm", priority = 9, emitFiles = function(prj, group) local fileCfgFunc = function(fcfg, condition) if fcfg then return { m.MasmPreprocessorDefinitions, m.excludedFromBuild, m.exceptionHandlingSEH, } else return { m.excludedFromBuild } end end m.emitFiles(prj, group, "Masm", nil, fileCfgFunc) end, emitFilter = function(prj, group) m.filterGroup(prj, group, "Masm") end, emitExtensionSettings = function(prj, group) p.w('') end, emitExtensionTargets = function(prj, group) p.w('') end } --- -- Image group --- m.categories.Image = { name = "Image", extensions = { ".gif", ".jpg", ".jpe", ".png", ".bmp", ".dib", "*.tif", "*.wmf", "*.ras", "*.eps", "*.pcx", "*.pcd", "*.tga", "*.dds" }, priority = 10, emitFiles = function(prj, group) local fileCfgFunc = function(fcfg, condition) return { m.excludedFromBuild } end m.emitFiles(prj, group, "Image", nil, fileCfgFunc) end, emitFilter = function(prj, group) m.filterGroup(prj, group, "Image") end } --- -- Natvis group --- m.categories.Natvis = { name = "Natvis", extensions = { ".natvis" }, priority = 11, emitFiles = function(prj, group) m.emitFiles(prj, group, "Natvis", {m.generatedFile}) end, emitFilter = function(prj, group) m.filterGroup(prj, group, "Natvis") end } --- -- Categorize files into groups. --- function m.categorizeSources(prj) -- if we already did this, return the cached result. if prj._vc2010_sources then return prj._vc2010_sources end -- build the new group table. local result = {} local groups = {} prj._vc2010_sources = result local tr = project.getsourcetree(prj) tree.traverse(tr, { onleaf = function(node) local cat = m.categorizeFile(prj, node) groups[cat.name] = groups[cat.name] or { category = cat, files = {} } table.insert(groups[cat.name].files, node) end }) -- sort by relative-to path; otherwise VS will reorder the files for name, group in pairs(groups) do table.sort(group.files, function (a, b) return a.relpath < b.relpath end) table.insert(result, group) end -- sort by category priority then name; so we get stable results. table.sort(result, function (a, b) if (a.category.priority == b.category.priority) then return a.category.name < b.category.name end return a.category.priority < b.category.priority end) return result end function m.categorizeFile(prj, file) for cfg in project.eachconfig(prj) do local fcfg = fileconfig.getconfig(file, cfg) if fcfg then -- If any configuration for this file uses a custom build step, that's the category to use if fileconfig.hasCustomBuildRule(fcfg) then return m.categories.CustomBuild end -- also check for buildaction if fcfg.buildaction then return m.categories[fcfg.buildaction] or m.categories.None end 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 { name = rule.name, priority = 100, rule = rule, emitFiles = function(prj, group) m.emitRuleFiles(prj, group) end, emitFilter = function(prj, group) m.filterGroup(prj, group, group.category.name) end } end -- Otherwise use the file extension to deduce a category for _, cat in pairs(m.categories) do if cat.extensions and path.hasextension(file.name, cat.extensions) then return cat end end return m.categories.None end function m.configPair(cfg) return vstudio.projectPlatform(cfg) .. "|" .. vstudio.archFromConfig(cfg, true) end function m.getTotalCfgCount(prj) if prj._totalCfgCount then return prj._totalCfgCount else local result = 0 for _ in p.project.eachconfig(prj) do result = result + 1 end -- cache result prj._totalCfgCount = result return result end end function m.indexConditionalElements() local nameMap, nameList, settingList nameMap = {} nameList = {} -- to preserve ordering settingList = {} -- to preserve ordering for _, element in ipairs(m.conditionalElements) do local settingMap = nameMap[element.name] if not settingMap then settingMap = {} nameMap[element.name] = settingMap if not table.contains(nameList, element.name) then table.insert(nameList, element.name) end end --setting will either be value or args local elementSet = settingMap[element.setting] if elementSet then table.insert(elementSet, element) else elementSet = {element} settingMap[element.setting] = elementSet if not table.contains(settingList, element.setting) then table.insert(settingList, element.setting) end end end return nameMap, nameList, settingList end function m.emitConditionalElements(prj) local keyCount = function(tbl) local count = 0 for _ in pairs(tbl) do count = count + 1 end return count end local nameMap, nameList, settingList nameMap, nameList, settingList = m.indexConditionalElements() local totalCfgCount = m.getTotalCfgCount(prj) for _, name in ipairs(nameList) do local settingMap = nameMap[name] local done = false if keyCount(settingMap)==1 then for _, setting in ipairs(settingList) do local elements = settingMap[setting] if elements~=nil and #elements==totalCfgCount then local element = elements[1] local format = string.format('<%s>%s', name, element.value, name) p.w(format, table.unpack(element.args)) done = true end end end if not done then for _, setting in ipairs(settingList) do local elements = settingMap[setting] if elements then for _, element in ipairs(elements) do local format = string.format('<%s %s>%s', name, m.conditionFromConfigText(element.condition), element.value, name) p.w(format, table.unpack(element.args)) end end end end end end function m.emitFiles(prj, group, tag, fileFunc, fileCfgFunc, checkFunc) local files = group.files if files and #files > 0 then p.push('') for _, file in ipairs(files) do local contents = p.capture(function () p.push() p.callArray(fileFunc, cfg, file) m.conditionalElements = {} for cfg in project.eachconfig(prj) do local fcfg = fileconfig.getconfig(file, cfg) if not checkFunc or checkFunc(cfg, fcfg) then p.callArray(fileCfgFunc, fcfg, m.configPair(cfg)) end end if #m.conditionalElements > 0 then m.emitConditionalElements(prj) end p.pop() end) local rel = path.translate(file.relpath) -- SharedItems projects paths are prefixed with a magical variable if prj.kind == p.SHAREDITEMS then rel = "$(MSBuildThisFileDirectory)" .. rel end if #contents > 0 then p.push('<%s Include="%s">', tag, rel) p.outln(contents) p.pop('', tag) else p.x('<%s Include="%s" />', tag, rel) end end p.pop('') end end function m.emitRuleFiles(prj, group) local files = group.files local rule = group.category.rule 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) m.conditionalElements = {} for cfg in project.eachconfig(prj) do local fcfg = fileconfig.getconfig(file, cfg) if fcfg and fcfg[fld.name] then local value = m.getRulePropertyString(rule, prop, fcfg[fld.name]) if value and #value > 0 then m.element(prop.name, m.configPair(cfg), '%s', value) end end end if #m.conditionalElements > 0 then m.emitConditionalElements(prj) 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 function m.isClrMixed(prj) -- check to see if any files are marked with clr local isMixed = false if not prj.clr or prj.clr == p.OFF then if prj._isClrMixed ~= nil then isMixed = prj._isClrMixed else table.foreachi(prj._.files, function(file) for cfg in p.project.eachconfig(prj) do local fcfg = p.fileconfig.getconfig(file, cfg) if fcfg and fcfg.clr and fcfg.clr ~= p.OFF then isMixed = true end end end) prj._isClrMixed = isMixed -- cache the results end end return isMixed end -- -- Generate the list of project dependencies. -- m.elements.projectReferences = function(prj, ref) if prj.clr ~= p.OFF or (m.isClrMixed(prj) and ref and ref.kind ~=p.STATICLIB) 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') -- Handle linked shared items projects local contents = p.capture(function() p.push() for _, ref in ipairs(refs) do if ref.kind == p.SHAREDITEMS then local relpath = vstudio.path(prj, vstudio.projectfile(ref)) p.x('', relpath) end end p.pop() end) if #contents > 0 then p.push('') p.outln(contents) p.pop('') end -- Handle all other linked projects local contents = p.capture(function() p.push() for _, ref in ipairs(refs) do if ref.kind ~= p.SHAREDITEMS then local relpath = vstudio.path(prj, vstudio.projectfile(ref)) p.push('', relpath) p.callArray(m.elements.projectReferences, prj, ref) p.pop('') end end p.pop() end) if #contents > 0 then p.push('') p.outln(contents) 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.additionalManifestFiles(cfg) -- 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 m.element("AdditionalManifestFiles", nil, "%s;%%(AdditionalManifestFiles)", table.concat(manifests, ";")) 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.largeAddressAware(cfg) if (cfg.largeaddressaware == true) then m.element("LargeAddressAware", nil, 'true') end end function m.languageStandard(cfg) if _ACTION >= "vs2017" then if (cfg.cppdialect == "C++14") then m.element("LanguageStandard", nil, 'stdcpp14') elseif (cfg.cppdialect == "C++17") then m.element("LanguageStandard", nil, 'stdcpp17') elseif (cfg.cppdialect == "C++latest") then m.element("LanguageStandard", nil, 'stdcpplatest') end end end function m.structMemberAlignment(cfg) local map = { [1] = "1Byte", [2] = "2Bytes", [4] = "4Bytes", [8] = "8Bytes", [16] = "16Bytes" } local value = map[cfg.structmemberalign] if value then m.element("StructMemberAlignment", nil, value) end end function m.additionalCompileOptions(cfg, condition) local opts = cfg.buildoptions if _ACTION == "vs2015" then if (cfg.cppdialect == "C++14") then table.insert(opts, "/std:c++14") elseif (cfg.cppdialect == "C++17") then table.insert(opts, "/std:c++latest") elseif (cfg.cppdialect == "C++latest") then table.insert(opts, "/std:c++latest") end end if cfg.toolset and cfg.toolset:startswith("msc") then local value = iif(cfg.unsignedchar, "On", "Off") table.insert(opts, p.tools.msc.shared.unsignedchar[value]) end if #opts > 0 then opts = table.concat(opts, " ") 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.compileAsManaged(fcfg, condition) if fcfg.clr and fcfg ~= p.OFF then m.element("CompileAsManaged", condition, "true") end end function m.basicRuntimeChecks(cfg, condition) local prjcfg, filecfg = p.config.normalize(cfg) local runtime = config.getruntime(prjcfg) or iif(config.isDebugBuild(cfg), "Debug", "Release") if filecfg then if filecfg.flags.NoRuntimeChecks or (config.isOptimizedBuild(filecfg) and runtime:endswith("Debug")) then m.element("BasicRuntimeChecks", condition, "Default") end else if prjcfg.flags.NoRuntimeChecks or (config.isOptimizedBuild(prjcfg) and runtime:endswith("Debug")) then m.element("BasicRuntimeChecks", nil, "Default") end end end function m.buildInputs(cfg, condition) if cfg.buildinputs and #cfg.buildinputs > 0 then local inputs = project.getrelative(cfg.project, cfg.buildinputs) m.element("Inputs", condition, '%s', table.concat(inputs, ";")) 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) if #fcfg.buildcommands > 0 then local commands = os.translateCommandsAndPaths(fcfg.buildcommands, fcfg.project.basedir, fcfg.project.location) m.element("Command", condition, '%s', table.concat(commands,'\r\n')) end 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) if #fcfg.buildoutputs > 0 then local outputs = project.getrelative(fcfg.project, fcfg.buildoutputs) m.element("Outputs", condition, '%s', table.concat(outputs, ";")) end end function m.linkObjects(fcfg, condition) if fcfg.linkbuildoutputs ~= nil then m.element("LinkObjects", condition, tostring(fcfg.linkbuildoutputs)) end end function m.characterSet(cfg) if not vstudio.isMakefile(cfg) then local charactersets = { ASCII = "NotSet", MBCS = "MultiByte", Unicode = "Unicode", Default = "Unicode" } m.element("CharacterSet", nil, charactersets[cfg.characterset]) 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) local defines = cfg.defines if cfg.exceptionhandling == p.OFF then defines = table.join(defines, "_HAS_EXCEPTIONS=0") end m.preprocessorDefinitions(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, condition) if p.languages.isc(cfg.compileas) then m.element("CompileAs", condition, "CompileAsC") elseif p.languages.iscpp(cfg.compileas) then m.element("CompileAs", condition, "CompileAsCpp") 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.symbols == p.ON) or (cfg.symbols == "FastLink") or (cfg.symbols == "Full") then if cfg.debugformat == "c7" then value = "OldStyle" elseif (cfg.architecture == "x86_64" and _ACTION < "vs2015") 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 m.element("DebugInformationFormat", nil, value) elseif cfg.symbols == p.OFF then -- leave field blank for vs2013 and older to workaround bug if _ACTION < "vs2015" then value = "" else value = "None" end m.element("DebugInformationFormat", nil, value) end end function m.enableDpiAwareness(cfg) local awareness = { None = "false", High = "true", HighPerMonitor = "PerMonitorHighDPIAware", } local value = awareness[cfg.dpiawareness] if value then m.element("EnableDpiAwareness", nil, value) 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" elseif x == "IA32" and _ACTION > "vs2010" then v = "NoExtensions" 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) end end function m.exceptionHandling(cfg, condition) if cfg.exceptionhandling == p.OFF then m.element("ExceptionHandling", condition, "false") elseif cfg.exceptionhandling == "SEH" then m.element("ExceptionHandling", condition, "Async") elseif cfg.exceptionhandling == "On" then m.element("ExceptionHandling", condition, "Sync") elseif cfg.exceptionhandling == "CThrow" then m.element("ExceptionHandling", condition, "SyncCThrow") end end function m.excludedFromBuild(filecfg, condition) if not filecfg or filecfg.flags.ExcludeFromBuild then m.element("ExcludedFromBuild", condition, "true") end end function m.exceptionHandlingSEH(filecfg, condition) if not filecfg or filecfg.exceptionhandling == "SEH" then m.element("UseSafeExceptionHandlers", 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 and cfg.floatingpoint ~= "Default" then m.element("FloatingPointModel", nil, cfg.floatingpoint) end end function m.floatingPointExceptions(cfg) if cfg.floatingpointexceptions ~= nil then if cfg.floatingpointexceptions then m.element("FloatingPointExceptions", nil, "true") else m.element("FloatingPointExceptions", nil, "false") end 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.fullProgramDatabaseFile(cfg) if _ACTION >= "vs2015" and cfg.symbols == "FastLink" then m.element("FullProgramDatabaseFile", nil, "true") end end function m.assemblyDebug(cfg) if cfg.assemblydebug then m.element("AssemblyDebug", nil, "true") end end function m.functionLevelLinking(cfg) if cfg.functionlevellinking ~= nil then if cfg.functionlevellinking then m.element("FunctionLevelLinking", nil, "true") else m.element("FunctionLevelLinking", nil, "false") end elseif config.isOptimizedBuild(cfg) then m.element("FunctionLevelLinking", nil, "true") end end function m.generateDebugInformation(cfg) local lookup = {} if _ACTION >= "vs2017" then lookup[p.ON] = "true" lookup[p.OFF] = "false" lookup["FastLink"] = "DebugFastLink" lookup["Full"] = "DebugFull" elseif _ACTION == "vs2015" then lookup[p.ON] = "true" lookup[p.OFF] = "false" lookup["FastLink"] = "DebugFastLink" lookup["Full"] = "true" else lookup[p.ON] = "true" lookup[p.OFF] = "false" lookup["FastLink"] = "true" lookup["Full"] = "true" end local value = lookup[cfg.symbols] if value then m.element("GenerateDebugInformation", nil, value) end 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.importLanguageTargets(prj) p.w('') end m.elements.importExtensionTargets = function(prj) return { m.importGroupTargets, m.importRuleTargets, m.importNuGetTargets, m.importBuildCustomizationsTargets } end function m.importExtensionTargets(prj) p.push('') p.callArray(m.elements.importExtensionTargets, prj) p.pop('') end function m.importGroupTargets(prj) local groups = m.categorizeSources(prj) for _, group in ipairs(groups) do if group.category.emitExtensionTargets then group.category.emitExtensionTargets(prj, group) end end 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 local function nuGetTargetsFile(prj, package) local packageAPIInfo = vstudio.nuget2010.packageAPIInfo(prj, package) return p.vstudio.path(prj, p.filename(prj.workspace, string.format("packages\\%s.%s\\build\\native\\%s.targets", vstudio.nuget2010.packageId(package), packageAPIInfo.verbatimVersion or packageAPIInfo.version, vstudio.nuget2010.packageId(package)))) end function m.importNuGetTargets(prj) if not vstudio.nuget2010.supportsPackageReferences(prj) then for i = 1, #prj.nuget do local targetsFile = nuGetTargetsFile(prj, prj.nuget[i]) p.x('', targetsFile, targetsFile) end end end function m.importBuildCustomizationsTargets(prj) for i, build in ipairs(prj.buildcustomizations) do p.w('', path.translate(build)) end end function m.ensureNuGetPackageBuildImports(prj) if #prj.nuget > 0 then p.push('') p.push('') p.x('This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.') p.pop('') for i = 1, #prj.nuget do local targetsFile = nuGetTargetsFile(prj, prj.nuget[i]) p.x('', targetsFile, targetsFile) end p.pop('') end end function m.importDefaultProps(prj) p.w('') end function m.importLanguageSettings(prj) p.w('') end m.elements.importExtensionSettings = function(prj) return { m.importGroupSettings, m.importRuleSettings, m.importBuildCustomizationsProps } end function m.importExtensionSettings(prj) p.push('') p.callArray(m.elements.importExtensionSettings, prj) p.pop('') end function m.importGroupSettings(prj) local groups = m.categorizeSources(prj) for _, group in ipairs(groups) do if group.category.emitExtensionSettings then group.category.emitExtensionSettings(prj, group) end end 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.importBuildCustomizationsProps(prj) for i, build in ipairs(prj.buildcustomizations) do p.w('', path.translate(build)) 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 cfg.intrinsics ~= nil then if cfg.intrinsics then m.element("IntrinsicFunctions", nil, "true") else m.element("IntrinsicFunctions", nil, "false") end elseif 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 or m.isClrMixed(prj) then m.targetFramework(prj) end if isManaged then 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.MasmPreprocessorDefinitions(cfg, condition) if cfg.defines then m.preprocessorDefinitions(cfg, cfg.defines, false, condition) 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.nmakeBuildCommands(cfg) m.nmakeCommandLine(cfg, cfg.buildcommands, "Build") end function m.nmakeCleanCommands(cfg) m.nmakeCommandLine(cfg, cfg.cleancommands, "Clean") end function m.nmakeCommandLine(cfg, commands, phase) if #commands > 0 then commands = os.translateCommandsAndPaths(commands, cfg.project.basedir, cfg.project.location) commands = table.concat(p.esc(commands), p.eol()) p.w('%s', phase, commands, phase) end end function m.nmakeIncludeDirs(cfg) if cfg.kind ~= p.NONE and #cfg.includedirs > 0 then local dirs = vstudio.path(cfg, cfg.includedirs) if #dirs > 0 then m.element("NMakeIncludeSearchPath", nil, "%s", table.concat(dirs, ";")) end end end function m.nmakeOutDirs(cfg) if vstudio.isMakefile(cfg) then m.outDir(cfg) m.intDir(cfg) end end function m.windowsSDKDesktopARMSupport(cfg) if cfg.system == p.WINDOWS then if cfg.architecture == p.ARM then p.w('true') end if cfg.architecture == p.ARM64 then p.w('true') end end end function m.nmakeOutput(cfg) m.element("NMakeOutput", nil, "$(OutDir)%s", cfg.buildtarget.name) end function m.nmakePreprocessorDefinitions(cfg) if cfg.kind ~= p.NONE and #cfg.defines > 0 then local defines = table.concat(cfg.defines, ";") defines = defines .. ";$(NMakePreprocessorDefinitions)" m.element('NMakePreprocessorDefinitions', nil, defines) end end function m.nmakeRebuildCommands(cfg) m.nmakeCommandLine(cfg, cfg.rebuildcommands, "ReBuild") end function m.objectFileName(fcfg) if fcfg.objname ~= fcfg.basename then m.element("ObjectFileName", m.configPair(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) local map = { Off = "false", On = "true" } local value = map[cfg.omitframepointer] if value then m.element("OmitFramePointers", nil, value) 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.executablePath(cfg) local dirs = vstudio.path(cfg, cfg.bindirs) if #dirs > 0 then dirs = table.translate(dirs, function(dir) if path.isabsolute(dir) then return dir end return "$(ProjectDir)" .. dir end) 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 value = p.action.current().toolset tool, version = p.tools.canonical(value) end if version then if cfg.kind == p.NONE or cfg.kind == p.MAKEFILE then if p.config.hasFile(cfg, path.iscppfile) or _ACTION >= "vs2015" then m.element("PlatformToolset", nil, version) end else m.element("PlatformToolset", nil, version) end end end function m.precompiledHeaderFile(fileName, cfg) m.element("PrecompiledHeaderFile", nil, "%s", fileName) end function m.precompiledHeader(cfg, condition) local 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.precompiledHeaderFile(prjcfg.pchheader, prjcfg) 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 = 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 = undefines .. ";%%(UndefinePreprocessorDefinitions)" m.element('UndefinePreprocessorDefinitions', condition, undefines) end end local function getSymbolsPathRelative(cfg) if cfg.symbolspath and cfg.symbols ~= p.OFF and cfg.debugformat ~= "c7" then return p.project.getrelative(cfg.project, cfg.symbolspath) else return nil end end function m.programDatabaseFile(cfg) local value = getSymbolsPathRelative(cfg) if value then m.element("ProgramDatabaseFile", nil, value) end end function m.programDatabaseFileName(cfg) local value = getSymbolsPathRelative(cfg) if value then m.element("ProgramDataBaseFileName", nil, value) end 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) local defines = table.join(cfg.defines, cfg.resdefines) if cfg.exceptionhandling == p.OFF then table.insert(defines, "_HAS_EXCEPTIONS=0") end m.preprocessorDefinitions(cfg, defines, true) end function m.runtimeLibrary(cfg) local runtimes = { StaticDebug = "MultiThreadedDebug", StaticRelease = "MultiThreaded", SharedDebug = "MultiThreadedDebugDLL", SharedRelease = "MultiThreadedDLL" } local runtime = config.getruntime(cfg) if runtime then m.element("RuntimeLibrary", nil, runtimes[runtime]) end end function m.callingConvention(cfg) if cfg.callingconvention then m.element("CallingConvention", nil, cfg.callingconvention) end end function m.runtimeTypeInfo(cfg, condition) if cfg.rtti == p.OFF and ((not cfg.clr) or cfg.clr == p.OFF) then m.element("RuntimeTypeInfo", condition, "false") elseif cfg.rtti == p.ON then m.element("RuntimeTypeInfo", condition, "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 cfg.stringpooling ~= nil then if cfg.stringpooling then m.element("StringPooling", nil, "true") else m.element("StringPooling", nil, "false") end elseif config.isOptimizedBuild(cfg) then m.element("StringPooling", nil, "true") end end function m.subSystem(cfg) local subsystem = iif(cfg.kind == p.CONSOLEAPP, "Console", "Windows") m.element("SubSystem", nil, subsystem) 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.targetMachine(cfg) -- If a static library project contains a resource file, VS will choke with -- "LINK : warning LNK4068: /MACHINE not specified; defaulting to X86" local targetmachine = { x86 = "MachineX86", x86_64 = "MachineX64", } if cfg.kind == p.STATICLIB and config.hasFile(cfg, path.isresourcefile) then local value = targetmachine[cfg.architecture] if value ~= nil then m.element("TargetMachine", nil, '%s', value) end end end function m.targetName(cfg) m.element("TargetName", nil, "%s%s", cfg.buildtarget.prefix, cfg.buildtarget.basename) end function m.latestTargetPlatformVersion(prj) -- See https://developercommunity.visualstudio.com/content/problem/140294/windowstargetplatformversion-makes-it-impossible-t.html if _ACTION == "vs2017" then m.element("LatestTargetPlatformVersion", nil, "$([Microsoft.Build.Utilities.ToolLocationHelper]::GetLatestSDKTargetPlatformVersion('Windows', '10.0'))") end end function m.windowsTargetPlatformVersion(prj, cfg) if _ACTION < "vs2015" then return end local target = cfg or prj local version = project.systemversion(target) -- if this is a config, only emit if different from project if cfg then local prjVersion = project.systemversion(prj) if not prjVersion or version == prjVersion then return end end -- See https://developercommunity.visualstudio.com/content/problem/140294/windowstargetplatformversion-makes-it-impossible-t.html if version == "latest" then if _ACTION == "vs2015" then version = nil -- SDK v10 is not supported by VS2015 elseif _ACTION == "vs2017" then version = "$(LatestTargetPlatformVersion)" else version = "10.0" end end if version then m.element("WindowsTargetPlatformVersion", nil, version) end end function m.preferredToolArchitecture(prj) if _ACTION >= "vs2013" then if prj.preferredtoolarchitecture == p.X86_64 then m.element("PreferredToolArchitecture", nil, 'x64') elseif prj.preferredtoolarchitecture == p.X86 then m.element("PreferredToolArchitecture", nil, 'x86') end else if prj.preferredtoolarchitecture == p.X86_64 then m.element("UseNativeEnvironment", nil, 'true') end end 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 = 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 = fatal .. ";%%(TreatSpecificWarningsAsErrors)" m.element('TreatSpecificWarningsAsErrors', condition, fatal) end end function m.useDebugLibraries(cfg) local runtime = config.getruntime(cfg) or iif(config.isDebugBuild(cfg), "Debug", "Release") m.element("UseDebugLibraries", nil, tostring(runtime:endswith("Debug"))) end function m.useOfMfc(cfg) if cfg.flags.MFC then m.element("UseOfMfc", nil, iif(cfg.staticruntime == "On", "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, map[cfg.warnings] or "Level3") end function m.warningLevelFile(cfg, condition) local map = { Off = "TurnOffAllWarnings", Extra = "Level4" } if cfg.warnings then m.element("WarningLevel", condition, map[cfg.warnings] or "Level3") end end function m.xmlDeclaration() p.xmlUtf8() end -- Fx Functions -------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------- function m.fxCompilePreprocessorDefinition(cfg, condition) if cfg.shaderdefines and #cfg.shaderdefines > 0 then local shaderdefines = table.concat(cfg.shaderdefines, ";") shaderdefines = shaderdefines .. ";%%(PreprocessorDefinitions)" m.element('PreprocessorDefinitions', condition, shaderdefines) end end function m.fxCompileAdditionalIncludeDirs(cfg, condition) if cfg.shaderincludedirs and #cfg.shaderincludedirs > 0 then local dirs = vstudio.path(cfg, cfg.shaderincludedirs) m.element('AdditionalIncludeDirectories', condition, "%s;%%(AdditionalIncludeDirectories)", table.concat(dirs, ";")) end end function m.fxCompileShaderType(cfg, condition) if cfg.shadertype then m.element("ShaderType", condition, cfg.shadertype) end end function m.fxCompileShaderModel(cfg, condition) if cfg.shadermodel then m.element("ShaderModel", condition, cfg.shadermodel) end end function m.fxCompileShaderEntry(cfg, condition) if cfg.shaderentry then m.element("EntryPointName", condition, cfg.shaderentry) end end function m.fxCompileShaderVariableName(cfg, condition) if cfg.shadervariablename then m.element("VariableName", condition, cfg.shadervariablename) end end function m.fxCompileShaderHeaderOutput(cfg, condition) if cfg.shaderheaderfileoutput then m.element("HeaderFileOutput", condition, cfg.shaderheaderfileoutput) end end function m.fxCompileShaderObjectOutput(cfg, condition) if cfg.shaderobjectfileoutput then m.element("ObjectFileOutput", condition, cfg.shaderobjectfileoutput) end end function m.fxCompileShaderAssembler(cfg, condition) if cfg.shaderassembler then m.element("AssemblerOutput", condition, cfg.shaderassembler) end end function m.fxCompileShaderAssemblerOutput(cfg, condition) if cfg.shaderassembleroutput then m.element("AssemblerOutputFile", condition, cfg.shaderassembleroutput) end end function m.fxCompileShaderAdditionalOptions(cfg, condition) local opts = cfg.shaderoptions if #opts > 0 then opts = table.concat(opts, " ") m.element("AdditionalOptions", condition, '%s %%(AdditionalOptions)', opts) end end --------------------------------------------------------------------------- -- -- Support functions -- --------------------------------------------------------------------------- -- -- Format and return a Visual Studio Condition attribute. -- function m.conditionFromConfigText(cfgText) return string.format('Condition="\'$(Configuration)|$(Platform)\'==\'%s\'"', p.esc(cfgText)) end function m.condition(cfg) return m.conditionFromConfigText(vstudio.projectConfig(cfg)) end -- -- Get the latest installed SDK 10 version from the registry. -- function m.latestSDK10Version() local arch = iif(os.is64bit(), "\\WOW6432Node\\", "\\") local version = os.getWindowsRegistry("HKLM:SOFTWARE" .. arch .."Microsoft\\Microsoft SDKs\\Windows\\v10.0\\ProductVersion") if version ~= nil then return version .. ".0" else return nil end 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, ...) local arg = {...} if select('#',...) == 0 then value = p.esc(value) else for i = 1, #arg do arg[i] = p.esc(arg[i]) end end if condition then --defer output local element = {} element.name = name element.condition = condition element.value = value element.args = arg if ... then if value == '%s' then element.setting = table.concat(arg) else element.setting = value .. table.concat(arg) end else element.setting = element.value end table.insert(m.conditionalElements, element) else local format = string.format('<%s>%s', name, value, name) p.w(format, table.unpack(arg)) end end