1-- 2-- vs2010_vcxproj.lua 3-- Generate a Visual Studio 201x C/C++ project. 4-- Copyright (c) Jason Perkins and the Premake project 5-- 6 7 local p = premake 8 p.vstudio.vc2010 = {} 9 10 local vstudio = p.vstudio 11 local project = p.project 12 local config = p.config 13 local fileconfig = p.fileconfig 14 local tree = p.tree 15 16 local m = p.vstudio.vc2010 17 18 19--- 20-- Add namespace for element definition lists for p.callArray() 21--- 22 23 m.elements = {} 24 m.conditionalElements = {} 25 26-- 27-- Generate a Visual Studio 201x C++ project, with support for the new platforms API. 28-- 29 30 m.elements.project = function(prj) 31 return { 32 m.xmlDeclaration, 33 m.project, 34 m.projectConfigurations, 35 m.globals, 36 m.importDefaultProps, 37 m.configurationPropertiesGroup, 38 m.importLanguageSettings, 39 m.importExtensionSettings, 40 m.propertySheetGroup, 41 m.userMacros, 42 m.outputPropertiesGroup, 43 m.itemDefinitionGroups, 44 m.assemblyReferences, 45 m.files, 46 m.projectReferences, 47 m.importLanguageTargets, 48 m.importExtensionTargets, 49 m.ensureNuGetPackageBuildImports, 50 } 51 end 52 53 function m.generate(prj) 54 p.utf8() 55 p.callArray(m.elements.project, prj) 56 p.out('</Project>') 57 end 58 59 60-- 61-- Output the XML declaration and opening <Project> tag. 62-- 63 64 function m.project(prj) 65 local action = p.action.current() 66 if _ACTION >= "vs2019" then 67 p.push('<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">') 68 else 69 p.push('<Project DefaultTargets="Build" ToolsVersion="%s" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">', 70 action.vstudio.toolsVersion) 71 end 72 end 73 74 75-- 76-- Write out the list of project configurations, which pairs build 77-- configurations with architectures. 78-- 79 80 function m.projectConfigurations(prj) 81 82 -- build a list of all architectures used in this project 83 local platforms = {} 84 for cfg in project.eachconfig(prj) do 85 local arch = vstudio.archFromConfig(cfg, true) 86 if not table.contains(platforms, arch) then 87 table.insert(platforms, arch) 88 end 89 end 90 91 local configs = {} 92 p.push('<ItemGroup Label="ProjectConfigurations">') 93 for cfg in project.eachconfig(prj) do 94 for _, arch in ipairs(platforms) do 95 local prjcfg = vstudio.projectConfig(cfg, arch) 96 if not configs[prjcfg] then 97 configs[prjcfg] = prjcfg 98 p.push('<ProjectConfiguration Include="%s">', vstudio.projectConfig(cfg, arch)) 99 p.x('<Configuration>%s</Configuration>', vstudio.projectPlatform(cfg)) 100 p.w('<Platform>%s</Platform>', arch) 101 p.pop('</ProjectConfiguration>') 102 end 103 end 104 end 105 p.pop('</ItemGroup>') 106 end 107 108 109-- 110-- Write out the TargetFrameworkVersion property. 111-- 112 113 function m.targetFramework(prj) 114 local action = p.action.current() 115 local tools = string.format(' ToolsVersion="%s"', action.vstudio.toolsVersion) 116 117 local framework = prj.dotnetframework or action.vstudio.targetFramework or "4.0" 118 p.w('<TargetFrameworkVersion>v%s</TargetFrameworkVersion>', framework) 119 end 120 121 122 123-- 124-- Write out the Globals property group. 125-- 126 127 m.elements.globals = function(prj) 128 return { 129 m.projectGuid, 130 m.ignoreWarnDuplicateFilename, 131 m.keyword, 132 m.projectName, 133 m.preferredToolArchitecture, 134 m.latestTargetPlatformVersion, 135 m.windowsTargetPlatformVersion, 136 } 137 end 138 139 m.elements.globalsCondition = function(prj, cfg) 140 return { 141 m.windowsTargetPlatformVersion, 142 m.xpDeprecationWarning, 143 } 144 end 145 146 function m.globals(prj) 147 148 -- Write out the project-level globals 149 m.propertyGroup(nil, "Globals") 150 p.callArray(m.elements.globals, prj) 151 p.pop('</PropertyGroup>') 152 153 -- Write out the configurable globals 154 for cfg in project.eachconfig(prj) do 155 156 -- Find out whether we're going to actually write a property out 157 local captured = p.capture( function() 158 p.push() 159 p.callArray(m.elements.globalsCondition, prj, cfg) 160 p.pop() 161 end) 162 163 -- If we do have something, create the entry, skip otherwise 164 if captured ~= '' then 165 m.propertyGroup(cfg, "Globals") 166 p.callArray(m.elements.globalsCondition, prj, cfg) 167 p.pop('</PropertyGroup>') 168 end 169 170 end 171 172 end 173 174 175-- 176-- Write out the configuration property group: what kind of binary it 177-- produces, and some global settings. 178-- 179 180 m.elements.configurationProperties = function(cfg) 181 if cfg.kind == p.UTILITY then 182 return { 183 m.configurationType, 184 m.platformToolset, 185 } 186 else 187 return { 188 m.configurationType, 189 m.useDebugLibraries, 190 m.useOfMfc, 191 m.useOfAtl, 192 m.clrSupport, 193 m.characterSet, 194 m.platformToolset, 195 m.wholeProgramOptimization, 196 m.nmakeOutDirs, 197 m.windowsSDKDesktopARMSupport, 198 } 199 end 200 end 201 202 function m.configurationProperties(cfg) 203 m.propertyGroup(cfg, "Configuration") 204 p.callArray(m.elements.configurationProperties, cfg) 205 p.pop('</PropertyGroup>') 206 end 207 208 function m.configurationPropertiesGroup(prj) 209 for cfg in project.eachconfig(prj) do 210 m.configurationProperties(cfg) 211 end 212 end 213 214 215 216-- 217-- Write the output property group, which includes the output and intermediate 218-- directories, manifest, etc. 219-- 220 221 m.elements.outputProperties = function(cfg) 222 if cfg.kind == p.UTILITY then 223 return { 224 m.outDir, 225 m.intDir, 226 m.extensionsToDeleteOnClean, 227 m.executablePath, 228 } 229 else 230 return { 231 m.linkIncremental, 232 m.ignoreImportLibrary, 233 m.outDir, 234 m.intDir, 235 m.targetName, 236 m.targetExt, 237 m.includePath, 238 m.libraryPath, 239 m.generateManifest, 240 m.extensionsToDeleteOnClean, 241 m.executablePath, 242 } 243 end 244 end 245 246 function m.outputProperties(cfg) 247 if not vstudio.isMakefile(cfg) then 248 m.propertyGroup(cfg) 249 p.callArray(m.elements.outputProperties, cfg) 250 p.pop('</PropertyGroup>') 251 end 252 end 253 254 255-- 256-- Write the NMake property group for Makefile projects, which includes the custom 257-- build commands, output file location, etc. 258-- 259 260 m.elements.nmakeProperties = function(cfg) 261 return { 262 m.executablePath, 263 m.includePath, 264 m.libraryPath, 265 m.nmakeOutput, 266 m.nmakeBuildCommands, 267 m.nmakeRebuildCommands, 268 m.nmakeCleanCommands, 269 m.nmakePreprocessorDefinitions, 270 m.nmakeIncludeDirs, 271 m.additionalCompileOptions 272 } 273 end 274 275 function m.nmakeProperties(cfg) 276 if vstudio.isMakefile(cfg) then 277 m.propertyGroup(cfg) 278 p.callArray(m.elements.nmakeProperties, cfg) 279 p.pop('</PropertyGroup>') 280 end 281 end 282 283 284-- 285-- Output properties and NMake properties should appear side-by-side 286-- for each configuration. 287-- 288 289 function m.outputPropertiesGroup(prj) 290 for cfg in project.eachconfig(prj) do 291 m.outputProperties(cfg) 292 m.nmakeProperties(cfg) 293 end 294 end 295 296 297 298-- 299-- Write a configuration's item definition group, which contains all 300-- of the per-configuration compile and link settings. 301-- 302 303 m.elements.itemDefinitionGroup = function(cfg) 304 if cfg.kind == p.UTILITY then 305 return { 306 m.ruleVars, 307 m.buildEvents, 308 m.buildLog, 309 } 310 else 311 return { 312 m.clCompile, 313 m.buildStep, 314 m.fxCompile, 315 m.resourceCompile, 316 m.linker, 317 m.manifest, 318 m.buildEvents, 319 m.ruleVars, 320 m.buildLog, 321 } 322 end 323 end 324 325 function m.itemDefinitionGroup(cfg) 326 if not vstudio.isMakefile(cfg) then 327 p.push('<ItemDefinitionGroup %s>', m.condition(cfg)) 328 p.callArray(m.elements.itemDefinitionGroup, cfg) 329 p.pop('</ItemDefinitionGroup>') 330 331 else 332 if cfg == project.getfirstconfig(cfg.project) then 333 p.w('<ItemDefinitionGroup>') 334 p.w('</ItemDefinitionGroup>') 335 end 336 end 337 end 338 339 function m.itemDefinitionGroups(prj) 340 for cfg in project.eachconfig(prj) do 341 m.itemDefinitionGroup(cfg) 342 end 343 end 344 345 346 347-- 348-- Write the the <ClCompile> compiler settings block. 349-- 350 351 m.elements.clCompile = function(cfg) 352 local calls = { 353 m.precompiledHeader, 354 m.warningLevel, 355 m.treatWarningAsError, 356 m.disableSpecificWarnings, 357 m.treatSpecificWarningsAsErrors, 358 m.basicRuntimeChecks, 359 m.clCompilePreprocessorDefinitions, 360 m.clCompileUndefinePreprocessorDefinitions, 361 m.clCompileAdditionalIncludeDirectories, 362 m.clCompileAdditionalUsingDirectories, 363 m.forceIncludes, 364 m.debugInformationFormat, 365 m.optimization, 366 m.functionLevelLinking, 367 m.intrinsicFunctions, 368 m.justMyCodeDebugging, 369 m.minimalRebuild, 370 m.omitFramePointers, 371 m.stringPooling, 372 m.runtimeLibrary, 373 m.omitDefaultLib, 374 m.exceptionHandling, 375 m.runtimeTypeInfo, 376 m.bufferSecurityCheck, 377 m.treatWChar_tAsBuiltInType, 378 m.floatingPointModel, 379 m.floatingPointExceptions, 380 m.inlineFunctionExpansion, 381 m.enableEnhancedInstructionSet, 382 m.multiProcessorCompilation, 383 m.additionalCompileOptions, 384 m.compileAs, 385 m.callingConvention, 386 m.languageStandard, 387 m.conformanceMode, 388 m.structMemberAlignment, 389 m.useFullPaths, 390 m.removeUnreferencedCodeData 391 } 392 393 if cfg.kind == p.STATICLIB then 394 table.insert(calls, m.programDatabaseFileName) 395 end 396 397 return calls 398 end 399 400 function m.clCompile(cfg) 401 p.push('<ClCompile>') 402 p.callArray(m.elements.clCompile, cfg) 403 p.pop('</ClCompile>') 404 end 405 406-- 407-- Write the the <CustomBuildStep> compiler settings block. 408-- 409 410 m.elements.buildStep = function(cfg) 411 local calls = { 412 m.buildCommands, 413 m.buildMessage, 414 m.buildOutputs, 415 m.buildInputs 416 } 417 418 return calls 419 end 420 421 function m.buildStep(cfg) 422 if #cfg.buildCommands > 0 or #cfg.buildOutputs > 0 or #cfg.buildInputs > 0 or cfg.buildMessage then 423 424 p.push('<CustomBuildStep>') 425 p.callArray(m.elements.buildStep, cfg) 426 p.pop('</CustomBuildStep>') 427 428 end 429 end 430 431 432-- 433-- Write the <FxCompile> settings block. 434-- 435 436 m.elements.fxCompile = function(cfg) 437 return { 438 m.fxCompilePreprocessorDefinition, 439 m.fxCompileAdditionalIncludeDirs, 440 m.fxCompileShaderType, 441 m.fxCompileShaderModel, 442 m.fxCompileShaderEntry, 443 m.fxCompileShaderVariableName, 444 m.fxCompileShaderHeaderOutput, 445 m.fxCompileShaderObjectOutput, 446 m.fxCompileShaderAssembler, 447 m.fxCompileShaderAssemblerOutput, 448 m.fxCompileShaderAdditionalOptions, 449 } 450 end 451 452 function m.fxCompile(cfg) 453 if p.config.hasFile(cfg, path.ishlslfile) then 454 local contents = p.capture(function () 455 p.push() 456 p.callArray(m.elements.fxCompile, cfg) 457 p.pop() 458 end) 459 460 if #contents > 0 then 461 p.push('<FxCompile>') 462 p.outln(contents) 463 p.pop('</FxCompile>') 464 end 465 end 466 end 467 468 469-- 470-- Write out the resource compiler block. 471-- 472 473 m.elements.resourceCompile = function(cfg) 474 return { 475 m.resourcePreprocessorDefinitions, 476 m.resourceAdditionalIncludeDirectories, 477 m.culture, 478 } 479 end 480 481 function m.resourceCompile(cfg) 482 if p.config.hasFile(cfg, path.isresourcefile) then 483 local contents = p.capture(function () 484 p.push() 485 p.callArray(m.elements.resourceCompile, cfg) 486 p.pop() 487 end) 488 489 if #contents > 0 then 490 p.push('<ResourceCompile>') 491 p.outln(contents) 492 p.pop('</ResourceCompile>') 493 end 494 end 495 end 496 497 498-- 499-- Write out the linker tool block. 500-- 501 502 m.elements.linker = function(cfg, explicit) 503 return { 504 m.link, 505 m.lib, 506 m.linkLibraryDependencies, 507 } 508 end 509 510 function m.linker(cfg) 511 local explicit = vstudio.needsExplicitLink(cfg) 512 p.callArray(m.elements.linker, cfg, explicit) 513 end 514 515 516 517 m.elements.link = function(cfg, explicit) 518 if cfg.kind == p.STATICLIB then 519 return { 520 m.subSystem, 521 m.fullProgramDatabaseFile, 522 m.generateDebugInformation, 523 m.optimizeReferences, 524 } 525 else 526 return { 527 m.subSystem, 528 m.fullProgramDatabaseFile, 529 m.generateDebugInformation, 530 m.optimizeReferences, 531 m.additionalDependencies, 532 m.additionalLibraryDirectories, 533 m.importLibrary, 534 m.entryPointSymbol, 535 m.generateMapFile, 536 m.moduleDefinitionFile, 537 m.treatLinkerWarningAsErrors, 538 m.ignoreDefaultLibraries, 539 m.largeAddressAware, 540 m.targetMachine, 541 m.additionalLinkOptions, 542 m.programDatabaseFile, 543 m.assemblyDebug, 544 } 545 end 546 end 547 548 function m.link(cfg, explicit) 549 local contents = p.capture(function () 550 p.push() 551 p.callArray(m.elements.link, cfg, explicit) 552 p.pop() 553 end) 554 if #contents > 0 then 555 p.push('<Link>') 556 p.outln(contents) 557 p.pop('</Link>') 558 end 559 end 560 561 562 563 m.elements.lib = function(cfg, explicit) 564 if cfg.kind == p.STATICLIB then 565 return { 566 m.additionalDependencies, 567 m.additionalLibraryDirectories, 568 m.treatLinkerWarningAsErrors, 569 m.targetMachine, 570 m.additionalLinkOptions, 571 } 572 else 573 return {} 574 end 575 end 576 577 function m.lib(cfg, explicit) 578 local contents = p.capture(function () 579 p.push() 580 p.callArray(m.elements.lib, cfg, explicit) 581 p.pop() 582 end) 583 if #contents > 0 then 584 p.push('<Lib>') 585 p.outln(contents) 586 p.pop('</Lib>') 587 end 588 end 589 590 591 592-- 593-- Write the manifest section. 594-- 595 596 m.elements.manifest = function(cfg) 597 return { 598 m.enableDpiAwareness, 599 m.additionalManifestFiles, 600 } 601 end 602 603 function m.manifest(cfg) 604 if cfg.kind ~= p.STATICLIB then 605 local contents = p.capture(function () 606 p.push() 607 p.callArray(m.elements.manifest, cfg) 608 p.pop() 609 end) 610 if #contents > 0 then 611 p.push('<Manifest>') 612 p.outln(contents) 613 p.pop('</Manifest>') 614 end 615 end 616 end 617 618 619 620--- 621-- Write out the pre- and post-build event settings. 622--- 623 624 function m.buildEvents(cfg) 625 local write = function (event) 626 local name = event .. "Event" 627 local field = event:lower() 628 local steps = cfg[field .. "commands"] 629 local msg = cfg[field .. "message"] 630 631 if #steps > 0 then 632 steps = os.translateCommandsAndPaths(steps, cfg.project.basedir, cfg.project.location) 633 p.push('<%s>', name) 634 p.x('<Command>%s</Command>', table.implode(steps, "", "", "\r\n")) 635 if msg then 636 p.x('<Message>%s</Message>', msg) 637 end 638 p.pop('</%s>', name) 639 end 640 end 641 642 write("PreBuild") 643 write("PreLink") 644 write("PostBuild") 645 end 646 647 648 649--- 650-- Transform property to string 651--- 652 653 function m.getRulePropertyString(rule, prop, value, kind) 654 -- list of paths 655 if kind == "list:path" then 656 return table.concat(vstudio.path(cfg, value), ';') 657 end 658 659 -- path 660 if kind == "path" then 661 return vstudio.path(cfg, value) 662 end 663 664 -- list 665 if type(value) == "table" then 666 return table.concat(value, ";") 667 end 668 669 -- enum 670 if prop.values then 671 value = table.findKeyByValue(prop.values, value) 672 end 673 674 -- primitive 675 return tostring(value) 676 end 677 678 679 680--- 681-- Write out project-level custom rule variables. 682--- 683 684 function m.ruleVars(cfg) 685 for i = 1, #cfg.rules do 686 local rule = p.global.getRule(cfg.rules[i]) 687 688 local contents = p.capture(function () 689 p.push() 690 for prop in p.rule.eachProperty(rule) do 691 local fld = p.rule.getPropertyField(rule, prop) 692 local value = cfg[fld.name] 693 if value ~= nil then 694 value = m.getRulePropertyString(rule, prop, value, fld.kind) 695 696 if value ~= nil and #value > 0 then 697 m.element(prop.name, nil, '%s', value) 698 end 699 end 700 end 701 p.pop() 702 end) 703 704 if #contents > 0 then 705 p.push('<%s>', rule.name) 706 p.outln(contents) 707 p.pop('</%s>', rule.name) 708 end 709 end 710 end 711 712 713-- 714-- Reference any managed assemblies listed in the links() 715-- 716 717 function m.assemblyReferences(prj) 718 -- Visual Studio doesn't support per-config references; use 719 -- whatever is contained in the first configuration 720 local cfg = project.getfirstconfig(prj) 721 722 local refs = config.getlinks(cfg, "system", "fullpath", "managed") 723 if #refs > 0 then 724 p.push('<ItemGroup>') 725 for i = 1, #refs do 726 local value = refs[i] 727 728 -- If the link contains a '/' then it is a relative path to 729 -- a local assembly. Otherwise treat it as a system assembly. 730 if value:find('/', 1, true) then 731 p.push('<Reference Include="%s">', path.getbasename(value)) 732 p.x('<HintPath>%s</HintPath>', path.translate(value)) 733 p.pop('</Reference>') 734 else 735 p.x('<Reference Include="%s" />', path.getbasename(value)) 736 end 737 end 738 p.pop('</ItemGroup>') 739 end 740 end 741 742 743 function m.generatedFile(cfg, file) 744 if file.generated then 745 local path = path.translate(file.dependsOn.relpath) 746 m.element("AutoGen", nil, 'true') 747 m.element("DependentUpon", nil, path) 748 end 749 end 750 751 752--- 753-- Write out the list of source code files, and any associated configuration. 754--- 755 756 function m.files(prj) 757 local groups = m.categorizeSources(prj) 758 for _, group in ipairs(groups) do 759 group.category.emitFiles(prj, group) 760 end 761 end 762 763 764 m.categories = {} 765 766--- 767-- ClInclude group 768--- 769 m.categories.ClInclude = { 770 name = "ClInclude", 771 extensions = { ".h", ".hh", ".hpp", ".hxx", ".inl" }, 772 priority = 1, 773 774 emitFiles = function(prj, group) 775 m.emitFiles(prj, group, "ClInclude", {m.generatedFile}) 776 end, 777 778 emitFilter = function(prj, group) 779 m.filterGroup(prj, group, "ClInclude") 780 end 781 } 782 783 784--- 785-- ClCompile group 786--- 787 m.categories.ClCompile = { 788 name = "ClCompile", 789 extensions = { ".cc", ".cpp", ".cxx", ".c++", ".c", ".s", ".m", ".mm" }, 790 priority = 2, 791 792 emitFiles = function(prj, group) 793 local fileCfgFunc = function(fcfg, condition) 794 if fcfg then 795 return { 796 m.excludedFromBuild, 797 m.objectFileName, 798 m.clCompilePreprocessorDefinitions, 799 m.clCompileUndefinePreprocessorDefinitions, 800 m.optimization, 801 m.forceIncludes, 802 m.precompiledHeader, 803 m.enableEnhancedInstructionSet, 804 m.additionalCompileOptions, 805 m.disableSpecificWarnings, 806 m.treatSpecificWarningsAsErrors, 807 m.basicRuntimeChecks, 808 m.exceptionHandling, 809 m.compileAsManaged, 810 m.compileAs, 811 m.runtimeTypeInfo, 812 m.warningLevelFile, 813 } 814 else 815 return { 816 m.excludedFromBuild 817 } 818 end 819 end 820 821 m.emitFiles(prj, group, "ClCompile", {m.generatedFile}, fileCfgFunc) 822 end, 823 824 emitFilter = function(prj, group) 825 m.filterGroup(prj, group, "ClCompile") 826 end 827 } 828 829 830--- 831-- FxCompile group 832--- 833 m.categories.FxCompile = { 834 name = "FxCompile", 835 extensions = { ".hlsl" }, 836 priority = 4, 837 838 emitFiles = function(prj, group) 839 local fileCfgFunc = function(fcfg, condition) 840 if fcfg then 841 return { 842 m.excludedFromBuild, 843 m.fxCompilePreprocessorDefinition, 844 m.fxCompileAdditionalIncludeDirs, 845 m.fxCompileShaderType, 846 m.fxCompileShaderModel, 847 m.fxCompileShaderEntry, 848 m.fxCompileShaderVariableName, 849 m.fxCompileShaderHeaderOutput, 850 m.fxCompileShaderObjectOutput, 851 m.fxCompileShaderAssembler, 852 m.fxCompileShaderAssemblerOutput, 853 m.fxCompileShaderAdditionalOptions, 854 } 855 else 856 return { 857 m.excludedFromBuild 858 } 859 end 860 end 861 862 m.emitFiles(prj, group, "FxCompile", nil, fileCfgFunc) 863 end, 864 865 emitFilter = function(prj, group) 866 m.filterGroup(prj, group, "FxCompile") 867 end 868 } 869 870 871--- 872-- None group 873--- 874 m.categories.None = { 875 name = "None", 876 priority = 5, 877 878 emitFiles = function(prj, group) 879 m.emitFiles(prj, group, "None", {m.generatedFile}) 880 end, 881 882 emitFilter = function(prj, group) 883 m.filterGroup(prj, group, "None") 884 end 885 } 886 887 888--- 889-- ResourceCompile group 890--- 891 m.categories.ResourceCompile = { 892 name = "ResourceCompile", 893 extensions = ".rc", 894 priority = 6, 895 896 emitFiles = function(prj, group) 897 local fileCfgFunc = { 898 m.excludedFromBuild 899 } 900 901 m.emitFiles(prj, group, "ResourceCompile", nil, fileCfgFunc) 902 end, 903 904 emitFilter = function(prj, group) 905 m.filterGroup(prj, group, "ResourceCompile") 906 end 907 } 908 909 910--- 911-- CustomBuild group 912--- 913 m.categories.CustomBuild = { 914 name = "CustomBuild", 915 priority = 7, 916 917 emitFiles = function(prj, group) 918 local fileFunc = { 919 m.fileType 920 } 921 922 local fileCfgFunc = { 923 m.excludedFromBuild, 924 m.buildCommands, 925 m.buildOutputs, 926 m.linkObjects, 927 m.buildMessage, 928 m.buildAdditionalInputs 929 } 930 931 m.emitFiles(prj, group, "CustomBuild", fileFunc, fileCfgFunc, function (cfg, fcfg) 932 return fileconfig.hasCustomBuildRule(fcfg) 933 end) 934 end, 935 936 emitFilter = function(prj, group) 937 m.filterGroup(prj, group, "CustomBuild") 938 end 939 } 940 941 942--- 943-- Midl group 944--- 945 m.categories.Midl = { 946 name = "Midl", 947 extensions = ".idl", 948 priority = 8, 949 950 emitFiles = function(prj, group) 951 local fileCfgFunc = { 952 m.excludedFromBuild 953 } 954 955 m.emitFiles(prj, group, "Midl", nil, fileCfgFunc, function(cfg) 956 return cfg.system == p.WINDOWS 957 end) 958 end, 959 960 emitFilter = function(prj, group) 961 m.filterGroup(prj, group, "Midl") 962 end 963 } 964 965 966--- 967-- Masm group 968--- 969 m.categories.Masm = { 970 name = "Masm", 971 extensions = ".asm", 972 priority = 9, 973 974 emitFiles = function(prj, group) 975 local fileCfgFunc = function(fcfg, condition) 976 if fcfg then 977 return { 978 m.MasmPreprocessorDefinitions, 979 m.excludedFromBuild, 980 m.exceptionHandlingSEH, 981 } 982 else 983 return { 984 m.excludedFromBuild 985 } 986 end 987 end 988 m.emitFiles(prj, group, "Masm", nil, fileCfgFunc) 989 end, 990 991 emitFilter = function(prj, group) 992 m.filterGroup(prj, group, "Masm") 993 end, 994 995 emitExtensionSettings = function(prj, group) 996 p.w('<Import Project="$(VCTargetsPath)\\BuildCustomizations\\masm.props" />') 997 end, 998 999 emitExtensionTargets = function(prj, group) 1000 p.w('<Import Project="$(VCTargetsPath)\\BuildCustomizations\\masm.targets" />') 1001 end 1002 } 1003 1004 1005--- 1006-- Image group 1007--- 1008 m.categories.Image = { 1009 name = "Image", 1010 extensions = { ".gif", ".jpg", ".jpe", ".png", ".bmp", ".dib", "*.tif", "*.wmf", "*.ras", "*.eps", "*.pcx", "*.pcd", "*.tga", "*.dds" }, 1011 priority = 10, 1012 1013 emitFiles = function(prj, group) 1014 local fileCfgFunc = function(fcfg, condition) 1015 return { 1016 m.excludedFromBuild 1017 } 1018 end 1019 m.emitFiles(prj, group, "Image", nil, fileCfgFunc) 1020 end, 1021 1022 emitFilter = function(prj, group) 1023 m.filterGroup(prj, group, "Image") 1024 end 1025 } 1026 1027 1028--- 1029-- Natvis group 1030--- 1031 m.categories.Natvis = { 1032 name = "Natvis", 1033 extensions = { ".natvis" }, 1034 priority = 11, 1035 1036 emitFiles = function(prj, group) 1037 m.emitFiles(prj, group, "Natvis", {m.generatedFile}) 1038 end, 1039 1040 emitFilter = function(prj, group) 1041 m.filterGroup(prj, group, "Natvis") 1042 end 1043 } 1044 1045 1046--- 1047-- Categorize files into groups. 1048--- 1049 function m.categorizeSources(prj) 1050 -- if we already did this, return the cached result. 1051 if prj._vc2010_sources then 1052 return prj._vc2010_sources 1053 end 1054 1055 -- build the new group table. 1056 local result = {} 1057 local groups = {} 1058 prj._vc2010_sources = result 1059 1060 local tr = project.getsourcetree(prj) 1061 tree.traverse(tr, { 1062 onleaf = function(node) 1063 local cat = m.categorizeFile(prj, node) 1064 groups[cat.name] = groups[cat.name] or { 1065 category = cat, 1066 files = {} 1067 } 1068 table.insert(groups[cat.name].files, node) 1069 end 1070 }) 1071 1072 -- sort by relative-to path; otherwise VS will reorder the files 1073 for name, group in pairs(groups) do 1074 table.sort(group.files, function (a, b) 1075 return a.relpath < b.relpath 1076 end) 1077 table.insert(result, group) 1078 end 1079 1080 -- sort by category priority then name; so we get stable results. 1081 table.sort(result, function (a, b) 1082 if (a.category.priority == b.category.priority) then 1083 return a.category.name < b.category.name 1084 end 1085 return a.category.priority < b.category.priority 1086 end) 1087 1088 return result 1089 end 1090 1091 1092 function m.categorizeFile(prj, file) 1093 for cfg in project.eachconfig(prj) do 1094 local fcfg = fileconfig.getconfig(file, cfg) 1095 if fcfg then 1096 -- If any configuration for this file uses a custom build step, that's the category to use 1097 if fileconfig.hasCustomBuildRule(fcfg) then 1098 return m.categories.CustomBuild 1099 end 1100 1101 -- also check for buildaction 1102 if fcfg.buildaction then 1103 return m.categories[fcfg.buildaction] or m.categories.None 1104 end 1105 end 1106 end 1107 1108 -- If there is a custom rule associated with it, use that 1109 local rule = p.global.getRuleForFile(file.name, prj.rules) 1110 if rule then 1111 return { 1112 name = rule.name, 1113 priority = 100, 1114 rule = rule, 1115 emitFiles = function(prj, group) 1116 m.emitRuleFiles(prj, group) 1117 end, 1118 emitFilter = function(prj, group) 1119 m.filterGroup(prj, group, group.category.name) 1120 end 1121 } 1122 end 1123 1124 -- Otherwise use the file extension to deduce a category 1125 for _, cat in pairs(m.categories) do 1126 if cat.extensions and path.hasextension(file.name, cat.extensions) then 1127 return cat 1128 end 1129 end 1130 1131 return m.categories.None 1132 end 1133 1134 1135 function m.configPair(cfg) 1136 return vstudio.projectPlatform(cfg) .. "|" .. vstudio.archFromConfig(cfg, true) 1137 end 1138 1139 1140 function m.getTotalCfgCount(prj) 1141 if prj._totalCfgCount then 1142 return prj._totalCfgCount 1143 else 1144 local result = 0 1145 for _ in p.project.eachconfig(prj) do 1146 result = result + 1 1147 end 1148 -- cache result 1149 prj._totalCfgCount = result 1150 return result 1151 end 1152 end 1153 1154 1155 function m.indexConditionalElements() 1156 local nameMap, nameList, settingList 1157 nameMap = {} 1158 nameList = {} -- to preserve ordering 1159 settingList = {} -- to preserve ordering 1160 for _, element in ipairs(m.conditionalElements) do 1161 local settingMap = nameMap[element.name] 1162 if not settingMap then 1163 settingMap = {} 1164 nameMap[element.name] = settingMap 1165 if not table.contains(nameList, element.name) then 1166 table.insert(nameList, element.name) 1167 end 1168 end 1169 --setting will either be value or args 1170 local elementSet = settingMap[element.setting] 1171 if elementSet then 1172 table.insert(elementSet, element) 1173 else 1174 elementSet = {element} 1175 settingMap[element.setting] = elementSet 1176 if not table.contains(settingList, element.setting) then 1177 table.insert(settingList, element.setting) 1178 end 1179 end 1180 end 1181 return nameMap, nameList, settingList 1182 end 1183 1184 1185 function m.emitConditionalElements(prj) 1186 local keyCount = function(tbl) 1187 local count = 0 1188 for _ in pairs(tbl) do count = count + 1 end 1189 return count 1190 end 1191 1192 local nameMap, nameList, settingList 1193 nameMap, nameList, settingList = m.indexConditionalElements() 1194 1195 local totalCfgCount = m.getTotalCfgCount(prj) 1196 for _, name in ipairs(nameList) do 1197 local settingMap = nameMap[name] 1198 local done = false 1199 if keyCount(settingMap)==1 then 1200 for _, setting in ipairs(settingList) do 1201 local elements = settingMap[setting] 1202 if elements~=nil and #elements==totalCfgCount then 1203 local element = elements[1] 1204 local format = string.format('<%s>%s</%s>', name, element.value, name) 1205 p.w(format, table.unpack(element.args)) 1206 done = true 1207 end 1208 end 1209 end 1210 if not done then 1211 for _, setting in ipairs(settingList) do 1212 local elements = settingMap[setting] 1213 if elements then 1214 for _, element in ipairs(elements) do 1215 local format = string.format('<%s %s>%s</%s>', name, m.conditionFromConfigText(element.condition), element.value, name) 1216 p.w(format, table.unpack(element.args)) 1217 end 1218 end 1219 end 1220 end 1221 end 1222 end 1223 1224 function m.emitFiles(prj, group, tag, fileFunc, fileCfgFunc, checkFunc) 1225 local files = group.files 1226 if files and #files > 0 then 1227 p.push('<ItemGroup>') 1228 for _, file in ipairs(files) do 1229 1230 local contents = p.capture(function () 1231 p.push() 1232 p.callArray(fileFunc, cfg, file) 1233 m.conditionalElements = {} 1234 for cfg in project.eachconfig(prj) do 1235 local fcfg = fileconfig.getconfig(file, cfg) 1236 if not checkFunc or checkFunc(cfg, fcfg) then 1237 p.callArray(fileCfgFunc, fcfg, m.configPair(cfg)) 1238 end 1239 end 1240 if #m.conditionalElements > 0 then 1241 m.emitConditionalElements(prj) 1242 end 1243 p.pop() 1244 end) 1245 1246 local rel = path.translate(file.relpath) 1247 1248 -- SharedItems projects paths are prefixed with a magical variable 1249 if prj.kind == p.SHAREDITEMS then 1250 rel = "$(MSBuildThisFileDirectory)" .. rel 1251 end 1252 1253 if #contents > 0 then 1254 p.push('<%s Include="%s">', tag, rel) 1255 p.outln(contents) 1256 p.pop('</%s>', tag) 1257 else 1258 p.x('<%s Include="%s" />', tag, rel) 1259 end 1260 1261 end 1262 p.pop('</ItemGroup>') 1263 end 1264 end 1265 1266 function m.emitRuleFiles(prj, group) 1267 local files = group.files 1268 local rule = group.category.rule 1269 1270 if files and #files > 0 then 1271 p.push('<ItemGroup>') 1272 1273 for _, file in ipairs(files) do 1274 local contents = p.capture(function() 1275 p.push() 1276 for prop in p.rule.eachProperty(rule) do 1277 local fld = p.rule.getPropertyField(rule, prop) 1278 m.conditionalElements = {} 1279 for cfg in project.eachconfig(prj) do 1280 local fcfg = fileconfig.getconfig(file, cfg) 1281 if fcfg and fcfg[fld.name] then 1282 local value = m.getRulePropertyString(rule, prop, fcfg[fld.name]) 1283 if value and #value > 0 then 1284 m.element(prop.name, m.configPair(cfg), '%s', value) 1285 end 1286 end 1287 end 1288 if #m.conditionalElements > 0 then 1289 m.emitConditionalElements(prj) 1290 end 1291 end 1292 p.pop() 1293 end) 1294 1295 if #contents > 0 then 1296 p.push('<%s Include=\"%s\">', rule.name, path.translate(file.relpath)) 1297 p.outln(contents) 1298 p.pop('</%s>', rule.name) 1299 else 1300 p.x('<%s Include=\"%s\" />', rule.name, path.translate(file.relpath)) 1301 end 1302 end 1303 1304 p.pop('</ItemGroup>') 1305 end 1306 end 1307 1308 1309 function m.isClrMixed(prj) 1310 -- check to see if any files are marked with clr 1311 local isMixed = false 1312 if not prj.clr or prj.clr == p.OFF then 1313 if prj._isClrMixed ~= nil then 1314 isMixed = prj._isClrMixed 1315 else 1316 table.foreachi(prj._.files, function(file) 1317 for cfg in p.project.eachconfig(prj) do 1318 local fcfg = p.fileconfig.getconfig(file, cfg) 1319 if fcfg and fcfg.clr and fcfg.clr ~= p.OFF then 1320 isMixed = true 1321 end 1322 end 1323 end) 1324 prj._isClrMixed = isMixed -- cache the results 1325 end 1326 end 1327 return isMixed 1328 end 1329 1330 1331-- 1332-- Generate the list of project dependencies. 1333-- 1334 1335 m.elements.projectReferences = function(prj, ref) 1336 if prj.clr ~= p.OFF or (m.isClrMixed(prj) and ref and ref.kind ~=p.STATICLIB) then 1337 return { 1338 m.referenceProject, 1339 m.referencePrivate, 1340 m.referenceOutputAssembly, 1341 m.referenceCopyLocalSatelliteAssemblies, 1342 m.referenceLinkLibraryDependencies, 1343 m.referenceUseLibraryDependences, 1344 } 1345 else 1346 return { 1347 m.referenceProject, 1348 } 1349 end 1350 end 1351 1352 function m.projectReferences(prj) 1353 local refs = project.getdependencies(prj, 'linkOnly') 1354 -- Handle linked shared items projects 1355 local contents = p.capture(function() 1356 p.push() 1357 for _, ref in ipairs(refs) do 1358 if ref.kind == p.SHAREDITEMS then 1359 local relpath = vstudio.path(prj, vstudio.projectfile(ref)) 1360 p.x('<Import Project="%s" Label="Shared" />', relpath) 1361 end 1362 end 1363 p.pop() 1364 end) 1365 if #contents > 0 then 1366 p.push('<ImportGroup Label="Shared">') 1367 p.outln(contents) 1368 p.pop('</ImportGroup>') 1369 end 1370 1371 -- Handle all other linked projects 1372 local contents = p.capture(function() 1373 p.push() 1374 for _, ref in ipairs(refs) do 1375 if ref.kind ~= p.SHAREDITEMS then 1376 local relpath = vstudio.path(prj, vstudio.projectfile(ref)) 1377 p.push('<ProjectReference Include=\"%s\">', relpath) 1378 p.callArray(m.elements.projectReferences, prj, ref) 1379 p.pop('</ProjectReference>') 1380 end 1381 end 1382 p.pop() 1383 end) 1384 if #contents > 0 then 1385 p.push('<ItemGroup>') 1386 p.outln(contents) 1387 p.pop('</ItemGroup>') 1388 end 1389 end 1390 1391 1392 1393--------------------------------------------------------------------------- 1394-- 1395-- Handlers for individual project elements 1396-- 1397--------------------------------------------------------------------------- 1398 1399 function m.additionalDependencies(cfg, explicit) 1400 local links 1401 1402 -- check to see if this project uses an external toolset. If so, let the 1403 -- toolset define the format of the links 1404 local toolset = config.toolset(cfg) 1405 if cfg.system ~= premake.WINDOWS and toolset then 1406 links = toolset.getlinks(cfg, not explicit) 1407 else 1408 links = vstudio.getLinks(cfg, explicit) 1409 end 1410 1411 if #links > 0 then 1412 links = path.translate(table.concat(links, ";")) 1413 m.element("AdditionalDependencies", nil, "%s;%%(AdditionalDependencies)", links) 1414 end 1415 end 1416 1417 1418 function m.additionalIncludeDirectories(cfg, includedirs) 1419 if #includedirs > 0 then 1420 local dirs = vstudio.path(cfg, includedirs) 1421 if #dirs > 0 then 1422 m.element("AdditionalIncludeDirectories", nil, "%s;%%(AdditionalIncludeDirectories)", table.concat(dirs, ";")) 1423 end 1424 end 1425 end 1426 1427 1428 function m.additionalLibraryDirectories(cfg) 1429 if #cfg.libdirs > 0 then 1430 local dirs = table.concat(vstudio.path(cfg, cfg.libdirs), ";") 1431 m.element("AdditionalLibraryDirectories", nil, "%s;%%(AdditionalLibraryDirectories)", dirs) 1432 end 1433 end 1434 1435 1436 function m.additionalManifestFiles(cfg) 1437 -- get the manifests files 1438 local manifests = {} 1439 for _, fname in ipairs(cfg.files) do 1440 if path.getextension(fname) == ".manifest" then 1441 table.insert(manifests, project.getrelative(cfg.project, fname)) 1442 end 1443 end 1444 1445 if #manifests > 0 then 1446 m.element("AdditionalManifestFiles", nil, "%s;%%(AdditionalManifestFiles)", table.concat(manifests, ";")) 1447 end 1448 end 1449 1450 1451 function m.additionalUsingDirectories(cfg) 1452 if #cfg.usingdirs > 0 then 1453 local dirs = vstudio.path(cfg, cfg.usingdirs) 1454 if #dirs > 0 then 1455 m.element("AdditionalUsingDirectories", nil, "%s;%%(AdditionalUsingDirectories)", table.concat(dirs, ";")) 1456 end 1457 end 1458 end 1459 1460 1461 function m.largeAddressAware(cfg) 1462 if (cfg.largeaddressaware == true) then 1463 m.element("LargeAddressAware", nil, 'true') 1464 end 1465 end 1466 1467 1468 function m.languageStandard(cfg) 1469 if _ACTION >= "vs2017" then 1470 if (cfg.cppdialect == "C++14") then 1471 m.element("LanguageStandard", nil, 'stdcpp14') 1472 elseif (cfg.cppdialect == "C++17") then 1473 m.element("LanguageStandard", nil, 'stdcpp17') 1474 elseif (cfg.cppdialect == "C++20") then 1475 m.element("LanguageStandard", nil, 'stdcpplatest') 1476 elseif (cfg.cppdialect == "C++latest") then 1477 m.element("LanguageStandard", nil, 'stdcpplatest') 1478 end 1479 end 1480 end 1481 1482 function m.conformanceMode(cfg) 1483 if _ACTION >= "vs2017" then 1484 if cfg.conformancemode ~= nil then 1485 if cfg.conformancemode then 1486 m.element("ConformanceMode", nil, "true") 1487 else 1488 m.element("ConformanceMode", nil, "false") 1489 end 1490 end 1491 end 1492 end 1493 1494 function m.structMemberAlignment(cfg) 1495 local map = { 1496 [1] = "1Byte", 1497 [2] = "2Bytes", 1498 [4] = "4Bytes", 1499 [8] = "8Bytes", 1500 [16] = "16Bytes" 1501 } 1502 1503 local value = map[cfg.structmemberalign] 1504 if value then 1505 m.element("StructMemberAlignment", nil, value) 1506 end 1507 end 1508 1509 function m.useFullPaths(cfg) 1510 if cfg.useFullPaths ~= nil then 1511 if cfg.useFullPaths then 1512 m.element("UseFullPaths", nil, "true") 1513 else 1514 m.element("UseFullPaths", nil, "false") 1515 end 1516 end 1517 end 1518 1519 function m.removeUnreferencedCodeData(cfg) 1520 if cfg.removeUnreferencedCodeData ~= nil then 1521 if cfg.removeUnreferencedCodeData then 1522 m.element("RemoveUnreferencedCodeData", nil, "true") 1523 else 1524 m.element("RemoveUnreferencedCodeData", nil, "false") 1525 end 1526 end 1527 end 1528 1529 function m.additionalCompileOptions(cfg, condition) 1530 local opts = cfg.buildoptions 1531 if _ACTION == "vs2015" or vstudio.isMakefile(cfg) then 1532 if (cfg.cppdialect == "C++14") then 1533 table.insert(opts, "/std:c++14") 1534 elseif (cfg.cppdialect == "C++17") then 1535 table.insert(opts, "/std:c++17") 1536 elseif (cfg.cppdialect == "C++20") then 1537 table.insert(opts, "/std:c++latest") 1538 elseif (cfg.cppdialect == "C++latest") then 1539 table.insert(opts, "/std:c++latest") 1540 end 1541 end 1542 1543 if cfg.toolset and cfg.toolset:startswith("msc") then 1544 local value = iif(cfg.unsignedchar, "On", "Off") 1545 table.insert(opts, p.tools.msc.shared.unsignedchar[value]) 1546 elseif _ACTION >= "vs2019" and cfg.toolset and cfg.toolset == "clang" then 1547 local value = iif(cfg.unsignedchar, "On", "Off") 1548 table.insert(opts, p.tools.msc.shared.unsignedchar[value]) 1549 end 1550 1551 if #opts > 0 then 1552 opts = table.concat(opts, " ") 1553 m.element("AdditionalOptions", condition, '%s %%(AdditionalOptions)', opts) 1554 end 1555 end 1556 1557 1558 function m.additionalLinkOptions(cfg) 1559 if #cfg.linkoptions > 0 then 1560 local opts = table.concat(cfg.linkoptions, " ") 1561 m.element("AdditionalOptions", nil, "%s %%(AdditionalOptions)", opts) 1562 end 1563 end 1564 1565 1566 function m.compileAsManaged(fcfg, condition) 1567 if fcfg.clr and fcfg ~= p.OFF then 1568 m.element("CompileAsManaged", condition, "true") 1569 end 1570 end 1571 1572 1573 function m.basicRuntimeChecks(cfg, condition) 1574 local prjcfg, filecfg = p.config.normalize(cfg) 1575 local runtime = config.getruntime(prjcfg) or iif(config.isDebugBuild(cfg), "Debug", "Release") 1576 if filecfg then 1577 if filecfg.flags.NoRuntimeChecks or (config.isOptimizedBuild(filecfg) and runtime:endswith("Debug")) then 1578 m.element("BasicRuntimeChecks", condition, "Default") 1579 end 1580 else 1581 if prjcfg.flags.NoRuntimeChecks or (config.isOptimizedBuild(prjcfg) and runtime:endswith("Debug")) then 1582 m.element("BasicRuntimeChecks", nil, "Default") 1583 end 1584 end 1585 end 1586 1587 function m.buildInputs(cfg, condition) 1588 if cfg.buildinputs and #cfg.buildinputs > 0 then 1589 local inputs = project.getrelative(cfg.project, cfg.buildinputs) 1590 m.element("Inputs", condition, '%s', table.concat(inputs, ";")) 1591 end 1592 end 1593 1594 function m.buildAdditionalInputs(fcfg, condition) 1595 if fcfg.buildinputs and #fcfg.buildinputs > 0 then 1596 local inputs = project.getrelative(fcfg.project, fcfg.buildinputs) 1597 m.element("AdditionalInputs", condition, '%s', table.concat(inputs, ";")) 1598 end 1599 end 1600 1601 1602 function m.buildCommands(fcfg, condition) 1603 if #fcfg.buildcommands > 0 then 1604 local commands = os.translateCommandsAndPaths(fcfg.buildcommands, fcfg.project.basedir, fcfg.project.location) 1605 m.element("Command", condition, '%s', table.concat(commands,'\r\n')) 1606 end 1607 end 1608 1609 1610 function m.buildLog(cfg) 1611 if cfg.buildlog and #cfg.buildlog > 0 then 1612 p.push('<BuildLog>') 1613 m.element("Path", nil, "%s", vstudio.path(cfg, cfg.buildlog)) 1614 p.pop('</BuildLog>') 1615 end 1616 end 1617 1618 1619 function m.buildMessage(fcfg, condition) 1620 if fcfg.buildmessage then 1621 m.element("Message", condition, '%s', fcfg.buildmessage) 1622 end 1623 end 1624 1625 1626 function m.buildOutputs(fcfg, condition) 1627 if #fcfg.buildoutputs > 0 then 1628 local outputs = project.getrelative(fcfg.project, fcfg.buildoutputs) 1629 m.element("Outputs", condition, '%s', table.concat(outputs, ";")) 1630 end 1631 end 1632 1633 1634 function m.linkObjects(fcfg, condition) 1635 if fcfg.linkbuildoutputs ~= nil then 1636 m.element("LinkObjects", condition, tostring(fcfg.linkbuildoutputs)) 1637 end 1638 end 1639 1640 1641 function m.characterSet(cfg) 1642 if not vstudio.isMakefile(cfg) then 1643 local charactersets = { 1644 ASCII = "NotSet", 1645 MBCS = "MultiByte", 1646 Unicode = "Unicode", 1647 Default = "Unicode" 1648 } 1649 m.element("CharacterSet", nil, charactersets[cfg.characterset]) 1650 end 1651 end 1652 1653 1654 function m.wholeProgramOptimization(cfg) 1655 if cfg.flags.LinkTimeOptimization then 1656 m.element("WholeProgramOptimization", nil, "true") 1657 end 1658 end 1659 1660 function m.clCompileAdditionalIncludeDirectories(cfg) 1661 m.additionalIncludeDirectories(cfg, cfg.includedirs) 1662 end 1663 1664 function m.clCompileAdditionalUsingDirectories(cfg) 1665 m.additionalUsingDirectories(cfg, cfg.usingdirs) 1666 end 1667 1668 1669 function m.clCompilePreprocessorDefinitions(cfg, condition) 1670 local defines = cfg.defines 1671 if cfg.exceptionhandling == p.OFF then 1672 defines = table.join(defines, "_HAS_EXCEPTIONS=0") 1673 end 1674 m.preprocessorDefinitions(cfg, defines, false, condition) 1675 end 1676 1677 1678 function m.clCompileUndefinePreprocessorDefinitions(cfg, condition) 1679 m.undefinePreprocessorDefinitions(cfg, cfg.undefines, false, condition) 1680 end 1681 1682 1683 function m.clrSupport(cfg) 1684 local value 1685 if cfg.clr == "On" or cfg.clr == "Unsafe" then 1686 value = "true" 1687 elseif cfg.clr ~= p.OFF then 1688 value = cfg.clr 1689 end 1690 if value then 1691 m.element("CLRSupport", nil, value) 1692 end 1693 end 1694 1695 1696 function m.compileAs(cfg, condition) 1697 if p.languages.isc(cfg.compileas) then 1698 m.element("CompileAs", condition, "CompileAsC") 1699 elseif p.languages.iscpp(cfg.compileas) then 1700 m.element("CompileAs", condition, "CompileAsCpp") 1701 end 1702 end 1703 1704 1705 function m.configurationType(cfg) 1706 local types = { 1707 SharedLib = "DynamicLibrary", 1708 StaticLib = "StaticLibrary", 1709 ConsoleApp = "Application", 1710 WindowedApp = "Application", 1711 Makefile = "Makefile", 1712 None = "Makefile", 1713 Utility = "Utility", 1714 } 1715 m.element("ConfigurationType", nil, types[cfg.kind]) 1716 end 1717 1718 1719 function m.culture(cfg) 1720 local value = vstudio.cultureForLocale(cfg.locale) 1721 if value then 1722 m.element("Culture", nil, "0x%04x", tostring(value)) 1723 end 1724 end 1725 1726 1727 function m.debugInformationFormat(cfg) 1728 local value 1729 local tool, toolVersion = p.config.toolset(cfg) 1730 if (cfg.symbols == p.ON) or (cfg.symbols == "FastLink") or (cfg.symbols == "Full") then 1731 if cfg.debugformat == "c7" then 1732 value = "OldStyle" 1733 elseif (cfg.architecture == "x86_64" and _ACTION < "vs2015") or 1734 cfg.clr ~= p.OFF or 1735 config.isOptimizedBuild(cfg) or 1736 cfg.editandcontinue == p.OFF or 1737 (toolVersion and toolVersion:startswith("LLVM-vs")) 1738 then 1739 value = "ProgramDatabase" 1740 else 1741 value = "EditAndContinue" 1742 end 1743 1744 m.element("DebugInformationFormat", nil, value) 1745 elseif cfg.symbols == p.OFF then 1746 -- leave field blank for vs2013 and older to workaround bug 1747 if _ACTION < "vs2015" then 1748 value = "" 1749 else 1750 value = "None" 1751 end 1752 1753 m.element("DebugInformationFormat", nil, value) 1754 end 1755 end 1756 1757 1758 function m.enableDpiAwareness(cfg) 1759 local awareness = { 1760 None = "false", 1761 High = "true", 1762 HighPerMonitor = "PerMonitorHighDPIAware", 1763 } 1764 local value = awareness[cfg.dpiawareness] 1765 1766 if value then 1767 m.element("EnableDpiAwareness", nil, value) 1768 end 1769 end 1770 1771 1772 function m.enableEnhancedInstructionSet(cfg, condition) 1773 local v 1774 local x = cfg.vectorextensions 1775 if x == "AVX" and _ACTION > "vs2010" then 1776 v = "AdvancedVectorExtensions" 1777 elseif x == "AVX2" and _ACTION > "vs2012" then 1778 v = "AdvancedVectorExtensions2" 1779 elseif cfg.architecture ~= "x86_64" then 1780 if x == "SSE2" or x == "SSE3" or x == "SSSE3" or x == "SSE4.1" then 1781 v = "StreamingSIMDExtensions2" 1782 elseif x == "SSE" then 1783 v = "StreamingSIMDExtensions" 1784 elseif x == "IA32" and _ACTION > "vs2010" then 1785 v = "NoExtensions" 1786 end 1787 end 1788 if v then 1789 m.element('EnableEnhancedInstructionSet', condition, v) 1790 end 1791 end 1792 1793 1794 function m.entryPointSymbol(cfg) 1795 if cfg.entrypoint then 1796 m.element("EntryPointSymbol", nil, cfg.entrypoint) 1797 end 1798 end 1799 1800 1801 function m.exceptionHandling(cfg, condition) 1802 if cfg.exceptionhandling == p.OFF then 1803 m.element("ExceptionHandling", condition, "false") 1804 elseif cfg.exceptionhandling == "SEH" then 1805 m.element("ExceptionHandling", condition, "Async") 1806 elseif cfg.exceptionhandling == "On" then 1807 m.element("ExceptionHandling", condition, "Sync") 1808 elseif cfg.exceptionhandling == "CThrow" then 1809 m.element("ExceptionHandling", condition, "SyncCThrow") 1810 end 1811 end 1812 1813 1814 function m.excludedFromBuild(filecfg, condition) 1815 if not filecfg or filecfg.flags.ExcludeFromBuild then 1816 m.element("ExcludedFromBuild", condition, "true") 1817 end 1818 end 1819 1820 1821 function m.exceptionHandlingSEH(filecfg, condition) 1822 if not filecfg or filecfg.exceptionhandling == "SEH" then 1823 m.element("UseSafeExceptionHandlers", condition, "true") 1824 end 1825 end 1826 1827 1828 function m.extensionsToDeleteOnClean(cfg) 1829 if #cfg.cleanextensions > 0 then 1830 local value = table.implode(cfg.cleanextensions, "*", ";", "") 1831 m.element("ExtensionsToDeleteOnClean", nil, value .. "$(ExtensionsToDeleteOnClean)") 1832 end 1833 end 1834 1835 1836 function m.fileType(cfg, file) 1837 m.element("FileType", nil, "Document") 1838 end 1839 1840 1841 function m.floatingPointModel(cfg) 1842 if cfg.floatingpoint and cfg.floatingpoint ~= "Default" then 1843 m.element("FloatingPointModel", nil, cfg.floatingpoint) 1844 end 1845 end 1846 1847 1848 function m.floatingPointExceptions(cfg) 1849 if cfg.floatingpointexceptions ~= nil then 1850 if cfg.floatingpointexceptions then 1851 m.element("FloatingPointExceptions", nil, "true") 1852 else 1853 m.element("FloatingPointExceptions", nil, "false") 1854 end 1855 end 1856 end 1857 1858 1859 function m.inlineFunctionExpansion(cfg) 1860 if cfg.inlining then 1861 local types = { 1862 Default = "Default", 1863 Disabled = "Disabled", 1864 Explicit = "OnlyExplicitInline", 1865 Auto = "AnySuitable", 1866 } 1867 m.element("InlineFunctionExpansion", nil, types[cfg.inlining]) 1868 end 1869 end 1870 1871 1872 function m.forceIncludes(cfg, condition) 1873 if #cfg.forceincludes > 0 then 1874 local includes = vstudio.path(cfg, cfg.forceincludes) 1875 if #includes > 0 then 1876 m.element("ForcedIncludeFiles", condition, table.concat(includes, ';')) 1877 end 1878 end 1879 if #cfg.forceusings > 0 then 1880 local usings = vstudio.path(cfg, cfg.forceusings) 1881 if #usings > 0 then 1882 m.element("ForcedUsingFiles", condition, table.concat(usings, ';')) 1883 end 1884 end 1885 end 1886 1887 1888 function m.fullProgramDatabaseFile(cfg) 1889 if _ACTION >= "vs2015" and cfg.symbols == "FastLink" then 1890 m.element("FullProgramDatabaseFile", nil, "true") 1891 end 1892 end 1893 1894 function m.assemblyDebug(cfg) 1895 if cfg.assemblydebug then 1896 m.element("AssemblyDebug", nil, "true") 1897 end 1898 end 1899 1900 1901 function m.functionLevelLinking(cfg) 1902 if cfg.functionlevellinking ~= nil then 1903 if cfg.functionlevellinking then 1904 m.element("FunctionLevelLinking", nil, "true") 1905 else 1906 m.element("FunctionLevelLinking", nil, "false") 1907 end 1908 elseif config.isOptimizedBuild(cfg) then 1909 m.element("FunctionLevelLinking", nil, "true") 1910 end 1911 end 1912 1913 1914 function m.generateDebugInformation(cfg) 1915 local lookup = {} 1916 if _ACTION >= "vs2017" then 1917 lookup[p.ON] = "true" 1918 lookup[p.OFF] = "false" 1919 lookup["FastLink"] = "DebugFastLink" 1920 lookup["Full"] = "DebugFull" 1921 elseif _ACTION == "vs2015" then 1922 lookup[p.ON] = "true" 1923 lookup[p.OFF] = "false" 1924 lookup["FastLink"] = "DebugFastLink" 1925 lookup["Full"] = "true" 1926 else 1927 lookup[p.ON] = "true" 1928 lookup[p.OFF] = "false" 1929 lookup["FastLink"] = "true" 1930 lookup["Full"] = "true" 1931 end 1932 1933 local value = lookup[cfg.symbols] 1934 if value then 1935 m.element("GenerateDebugInformation", nil, value) 1936 end 1937 end 1938 1939 1940 function m.generateManifest(cfg) 1941 if cfg.flags.NoManifest then 1942 m.element("GenerateManifest", nil, "false") 1943 end 1944 end 1945 1946 1947 function m.generateMapFile(cfg) 1948 if cfg.flags.Maps then 1949 m.element("GenerateMapFile", nil, "true") 1950 end 1951 end 1952 1953 1954 function m.ignoreDefaultLibraries(cfg) 1955 if #cfg.ignoredefaultlibraries > 0 then 1956 local ignored = cfg.ignoredefaultlibraries 1957 for i = 1, #ignored do 1958 -- Add extension if required 1959 if not p.tools.msc.getLibraryExtensions()[ignored[i]:match("[^.]+$")] then 1960 ignored[i] = path.appendextension(ignored[i], ".lib") 1961 end 1962 end 1963 1964 m.element("IgnoreSpecificDefaultLibraries", condition, table.concat(ignored, ';')) 1965 end 1966 end 1967 1968 1969 function m.ignoreWarnDuplicateFilename(prj) 1970 -- VS 2013 warns on duplicate file names, even those files which are 1971 -- contained in different, mututally exclusive configurations. See: 1972 -- http://connect.microsoft.com/VisualStudio/feedback/details/797460/incorrect-warning-msb8027-reported-for-files-excluded-from-build 1973 -- Premake already adds unique object names to conflicting file names, so 1974 -- just go ahead and disable that warning. 1975 if _ACTION > "vs2012" then 1976 m.element("IgnoreWarnCompileDuplicatedFilename", nil, "true") 1977 end 1978 end 1979 1980 1981 function m.ignoreImportLibrary(cfg) 1982 if cfg.kind == p.SHAREDLIB and cfg.flags.NoImportLib then 1983 m.element("IgnoreImportLibrary", nil, "true") 1984 end 1985 end 1986 1987 1988 function m.importLanguageTargets(prj) 1989 p.w('<Import Project="$(VCTargetsPath)\\Microsoft.Cpp.targets" />') 1990 end 1991 1992 m.elements.importExtensionTargets = function(prj) 1993 return { 1994 m.importGroupTargets, 1995 m.importRuleTargets, 1996 m.importNuGetTargets, 1997 m.importBuildCustomizationsTargets 1998 } 1999 end 2000 2001 function m.importExtensionTargets(prj) 2002 p.push('<ImportGroup Label="ExtensionTargets">') 2003 p.callArray(m.elements.importExtensionTargets, prj) 2004 p.pop('</ImportGroup>') 2005 end 2006 2007 function m.importGroupTargets(prj) 2008 local groups = m.categorizeSources(prj) 2009 for _, group in ipairs(groups) do 2010 if group.category.emitExtensionTargets then 2011 group.category.emitExtensionTargets(prj, group) 2012 end 2013 end 2014 end 2015 2016 function m.importRuleTargets(prj) 2017 for i = 1, #prj.rules do 2018 local rule = p.global.getRule(prj.rules[i]) 2019 local loc = vstudio.path(prj, p.filename(rule, ".targets")) 2020 p.x('<Import Project="%s" />', loc) 2021 end 2022 end 2023 2024 local function nuGetTargetsFile(prj, package) 2025 local packageAPIInfo = vstudio.nuget2010.packageAPIInfo(prj, package) 2026 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)))) 2027 end 2028 2029 function m.importNuGetTargets(prj) 2030 if not vstudio.nuget2010.supportsPackageReferences(prj) then 2031 for i = 1, #prj.nuget do 2032 local targetsFile = nuGetTargetsFile(prj, prj.nuget[i]) 2033 p.x('<Import Project="%s" Condition="Exists(\'%s\')" />', targetsFile, targetsFile) 2034 end 2035 end 2036 end 2037 2038 function m.importBuildCustomizationsTargets(prj) 2039 for i, build in ipairs(prj.buildcustomizations) do 2040 p.w('<Import Project="$(VCTargetsPath)\\%s.targets" />', path.translate(build)) 2041 end 2042 end 2043 2044 2045 2046 function m.ensureNuGetPackageBuildImports(prj) 2047 if #prj.nuget > 0 then 2048 p.push('<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">') 2049 p.push('<PropertyGroup>') 2050 p.x('<ErrorText>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}.</ErrorText>') 2051 p.pop('</PropertyGroup>') 2052 2053 for i = 1, #prj.nuget do 2054 local targetsFile = nuGetTargetsFile(prj, prj.nuget[i]) 2055 p.x('<Error Condition="!Exists(\'%s\')" Text="$([System.String]::Format(\'$(ErrorText)\', \'%s\'))" />', targetsFile, targetsFile) 2056 end 2057 p.pop('</Target>') 2058 end 2059 end 2060 2061 2062 2063 function m.importDefaultProps(prj) 2064 p.w('<Import Project="$(VCTargetsPath)\\Microsoft.Cpp.Default.props" />') 2065 end 2066 2067 2068 2069 function m.importLanguageSettings(prj) 2070 p.w('<Import Project="$(VCTargetsPath)\\Microsoft.Cpp.props" />') 2071 end 2072 2073 m.elements.importExtensionSettings = function(prj) 2074 return { 2075 m.importGroupSettings, 2076 m.importRuleSettings, 2077 m.importBuildCustomizationsProps 2078 } 2079 end 2080 2081 function m.importExtensionSettings(prj) 2082 p.push('<ImportGroup Label="ExtensionSettings">') 2083 p.callArray(m.elements.importExtensionSettings, prj) 2084 p.pop('</ImportGroup>') 2085 end 2086 2087 2088 function m.importGroupSettings(prj) 2089 local groups = m.categorizeSources(prj) 2090 for _, group in ipairs(groups) do 2091 if group.category.emitExtensionSettings then 2092 group.category.emitExtensionSettings(prj, group) 2093 end 2094 end 2095 end 2096 2097 2098 function m.importRuleSettings(prj) 2099 for i = 1, #prj.rules do 2100 local rule = p.global.getRule(prj.rules[i]) 2101 local loc = vstudio.path(prj, p.filename(rule, ".props")) 2102 p.x('<Import Project="%s" />', loc) 2103 end 2104 end 2105 2106 2107 function m.importBuildCustomizationsProps(prj) 2108 for i, build in ipairs(prj.buildcustomizations) do 2109 p.w('<Import Project="$(VCTargetsPath)\\%s.props" />', path.translate(build)) 2110 end 2111 end 2112 2113 2114 2115 function m.importLibrary(cfg) 2116 if cfg.kind == p.SHAREDLIB then 2117 m.element("ImportLibrary", nil, "%s", path.translate(cfg.linktarget.relpath)) 2118 end 2119 end 2120 2121 2122 function m.includePath(cfg) 2123 local dirs = vstudio.path(cfg, cfg.sysincludedirs) 2124 if #dirs > 0 then 2125 m.element("IncludePath", nil, "%s;$(IncludePath)", table.concat(dirs, ";")) 2126 end 2127 end 2128 2129 2130 function m.intDir(cfg) 2131 local objdir = vstudio.path(cfg, cfg.objdir) 2132 m.element("IntDir", nil, "%s\\", objdir) 2133 end 2134 2135 2136 function m.intrinsicFunctions(cfg) 2137 if cfg.intrinsics ~= nil then 2138 if cfg.intrinsics then 2139 m.element("IntrinsicFunctions", nil, "true") 2140 else 2141 m.element("IntrinsicFunctions", nil, "false") 2142 end 2143 elseif config.isOptimizedBuild(cfg) then 2144 m.element("IntrinsicFunctions", nil, "true") 2145 end 2146 end 2147 2148 function m.justMyCodeDebugging(cfg) 2149 local jmc = cfg.justmycode 2150 2151 if _ACTION >= "vs2017" and jmc == "Off" then 2152 m.element("SupportJustMyCode", nil, "false") 2153 end 2154 end 2155 2156 function m.keyword(prj) 2157 -- try to determine what kind of targets we're building here 2158 local isWin, isManaged, isMakefile 2159 for cfg in project.eachconfig(prj) do 2160 if cfg.system == p.WINDOWS then 2161 isWin = true 2162 end 2163 if cfg.clr ~= p.OFF then 2164 isManaged = true 2165 end 2166 if vstudio.isMakefile(cfg) then 2167 isMakefile = true 2168 end 2169 end 2170 2171 if isWin then 2172 if isMakefile then 2173 m.element("Keyword", nil, "MakeFileProj") 2174 else 2175 if isManaged or m.isClrMixed(prj) then 2176 m.targetFramework(prj) 2177 end 2178 if isManaged then 2179 m.element("Keyword", nil, "ManagedCProj") 2180 else 2181 m.element("Keyword", nil, "Win32Proj") 2182 end 2183 m.element("RootNamespace", nil, "%s", prj.name) 2184 end 2185 end 2186 end 2187 2188 2189 function m.libraryPath(cfg) 2190 local dirs = vstudio.path(cfg, cfg.syslibdirs) 2191 if #dirs > 0 then 2192 m.element("LibraryPath", nil, "%s;$(LibraryPath)", table.concat(dirs, ";")) 2193 end 2194 end 2195 2196 2197 2198 function m.linkIncremental(cfg) 2199 if cfg.kind ~= p.STATICLIB then 2200 m.element("LinkIncremental", nil, "%s", tostring(config.canLinkIncremental(cfg))) 2201 end 2202 end 2203 2204 2205 function m.linkLibraryDependencies(cfg, explicit) 2206 -- Left to its own devices, VS will happily link against a project dependency 2207 -- that has been excluded from the build. As a workaround, disable dependency 2208 -- linking and list all siblings explicitly 2209 if explicit then 2210 p.push('<ProjectReference>') 2211 m.element("LinkLibraryDependencies", nil, "false") 2212 p.pop('</ProjectReference>') 2213 end 2214 end 2215 2216 2217 function m.MasmPreprocessorDefinitions(cfg, condition) 2218 if cfg.defines then 2219 m.preprocessorDefinitions(cfg, cfg.defines, false, condition) 2220 end 2221 end 2222 2223 2224 function m.minimalRebuild(cfg) 2225 if config.isOptimizedBuild(cfg) or 2226 cfg.flags.NoMinimalRebuild or 2227 cfg.flags.MultiProcessorCompile or 2228 cfg.debugformat == p.C7 2229 then 2230 m.element("MinimalRebuild", nil, "false") 2231 end 2232 end 2233 2234 2235 function m.moduleDefinitionFile(cfg) 2236 local df = config.findfile(cfg, ".def") 2237 if df then 2238 m.element("ModuleDefinitionFile", nil, "%s", df) 2239 end 2240 end 2241 2242 2243 function m.multiProcessorCompilation(cfg) 2244 if cfg.flags.MultiProcessorCompile then 2245 m.element("MultiProcessorCompilation", nil, "true") 2246 end 2247 end 2248 2249 2250 function m.nmakeBuildCommands(cfg) 2251 m.nmakeCommandLine(cfg, cfg.buildcommands, "Build") 2252 end 2253 2254 2255 function m.nmakeCleanCommands(cfg) 2256 m.nmakeCommandLine(cfg, cfg.cleancommands, "Clean") 2257 end 2258 2259 2260 function m.nmakeCommandLine(cfg, commands, phase) 2261 if #commands > 0 then 2262 commands = os.translateCommandsAndPaths(commands, cfg.project.basedir, cfg.project.location) 2263 commands = table.concat(p.esc(commands), p.eol()) 2264 p.w('<NMake%sCommandLine>%s</NMake%sCommandLine>', phase, commands, phase) 2265 end 2266 end 2267 2268 2269 function m.nmakeIncludeDirs(cfg) 2270 if cfg.kind ~= p.NONE and #cfg.includedirs > 0 then 2271 local dirs = vstudio.path(cfg, cfg.includedirs) 2272 if #dirs > 0 then 2273 m.element("NMakeIncludeSearchPath", nil, "%s", table.concat(dirs, ";")) 2274 end 2275 end 2276 end 2277 2278 2279 function m.nmakeOutDirs(cfg) 2280 if vstudio.isMakefile(cfg) then 2281 m.outDir(cfg) 2282 m.intDir(cfg) 2283 end 2284 end 2285 2286 2287 function m.windowsSDKDesktopARMSupport(cfg) 2288 if cfg.system == p.WINDOWS then 2289 if cfg.architecture == p.ARM then 2290 p.w('<WindowsSDKDesktopARMSupport>true</WindowsSDKDesktopARMSupport>') 2291 end 2292 if cfg.architecture == p.ARM64 then 2293 p.w('<WindowsSDKDesktopARM64Support>true</WindowsSDKDesktopARM64Support>') 2294 end 2295 end 2296 end 2297 2298 2299 function m.nmakeOutput(cfg) 2300 m.element("NMakeOutput", nil, "$(OutDir)%s", cfg.buildtarget.name) 2301 end 2302 2303 2304 function m.nmakePreprocessorDefinitions(cfg) 2305 if cfg.kind ~= p.NONE and #cfg.defines > 0 then 2306 local defines = table.concat(cfg.defines, ";") 2307 defines = defines .. ";$(NMakePreprocessorDefinitions)" 2308 m.element('NMakePreprocessorDefinitions', nil, defines) 2309 end 2310 end 2311 2312 2313 function m.nmakeRebuildCommands(cfg) 2314 m.nmakeCommandLine(cfg, cfg.rebuildcommands, "ReBuild") 2315 end 2316 2317 2318 function m.objectFileName(fcfg) 2319 if fcfg.objname ~= fcfg.basename then 2320 m.element("ObjectFileName", m.configPair(fcfg.config), "$(IntDir)\\%s.obj", fcfg.objname) 2321 end 2322 end 2323 2324 2325 function m.omitDefaultLib(cfg) 2326 if cfg.flags.OmitDefaultLibrary then 2327 m.element("OmitDefaultLibName", nil, "true") 2328 end 2329 end 2330 2331 2332 function m.omitFramePointers(cfg) 2333 local map = { Off = "false", On = "true" } 2334 local value = map[cfg.omitframepointer] 2335 2336 if value then 2337 m.element("OmitFramePointers", nil, value) 2338 end 2339 end 2340 2341 2342 function m.optimizeReferences(cfg) 2343 if config.isOptimizedBuild(cfg) then 2344 m.element("EnableCOMDATFolding", nil, "true") 2345 m.element("OptimizeReferences", nil, "true") 2346 end 2347 end 2348 2349 2350 function m.optimization(cfg, condition) 2351 local map = { Off="Disabled", On="Full", Debug="Disabled", Full="Full", Size="MinSpace", Speed="MaxSpeed" } 2352 local value = map[cfg.optimize] 2353 if value or not condition then 2354 m.element('Optimization', condition, value or "Disabled") 2355 end 2356 end 2357 2358 2359 function m.outDir(cfg) 2360 local outdir = vstudio.path(cfg, cfg.buildtarget.directory) 2361 m.element("OutDir", nil, "%s\\", outdir) 2362 end 2363 2364 2365 function m.executablePath(cfg) 2366 local dirs = vstudio.path(cfg, cfg.bindirs) 2367 if #dirs > 0 then 2368 dirs = table.translate(dirs, function(dir) 2369 if path.isabsolute(dir) then 2370 return dir 2371 end 2372 return "$(ProjectDir)" .. dir 2373 end) 2374 m.element("ExecutablePath", nil, "%s;$(ExecutablePath)", table.concat(dirs, ";")) 2375 end 2376 end 2377 2378 2379 function m.platformToolset(cfg) 2380 local tool, version = p.config.toolset(cfg) 2381 2382 if not version and _ACTION >= "vs2019" and cfg.toolset == "clang" then 2383 version = "ClangCL" 2384 end 2385 2386 if not version then 2387 local value = p.action.current().toolset 2388 tool, version = p.tools.canonical(value) 2389 end 2390 2391 if version then 2392 if cfg.kind == p.NONE or cfg.kind == p.MAKEFILE then 2393 if p.config.hasFile(cfg, path.iscppfile) or _ACTION >= "vs2015" then 2394 m.element("PlatformToolset", nil, version) 2395 end 2396 else 2397 m.element("PlatformToolset", nil, version) 2398 end 2399 end 2400 end 2401 2402 function m.precompiledHeaderFile(fileName, cfg) 2403 m.element("PrecompiledHeaderFile", nil, "%s", fileName) 2404 end 2405 2406 function m.precompiledHeader(cfg, condition) 2407 local prjcfg, filecfg = p.config.normalize(cfg) 2408 if filecfg then 2409 if prjcfg.pchsource == filecfg.abspath and not prjcfg.flags.NoPCH then 2410 m.element('PrecompiledHeader', condition, 'Create') 2411 elseif filecfg.flags.NoPCH then 2412 m.element('PrecompiledHeader', condition, 'NotUsing') 2413 end 2414 else 2415 if not prjcfg.flags.NoPCH and prjcfg.pchheader then 2416 m.element("PrecompiledHeader", nil, "Use") 2417 m.precompiledHeaderFile(prjcfg.pchheader, prjcfg) 2418 else 2419 m.element("PrecompiledHeader", nil, "NotUsing") 2420 end 2421 end 2422 end 2423 2424 2425 function m.preprocessorDefinitions(cfg, defines, escapeQuotes, condition) 2426 if #defines > 0 then 2427 defines = table.concat(defines, ";") 2428 if escapeQuotes then 2429 defines = defines:gsub('"', '\\"') 2430 end 2431 defines = defines .. ";%%(PreprocessorDefinitions)" 2432 m.element('PreprocessorDefinitions', condition, defines) 2433 end 2434 end 2435 2436 2437 function m.undefinePreprocessorDefinitions(cfg, undefines, escapeQuotes, condition) 2438 if #undefines > 0 then 2439 undefines = table.concat(undefines, ";") 2440 if escapeQuotes then 2441 undefines = undefines:gsub('"', '\\"') 2442 end 2443 undefines = undefines .. ";%%(UndefinePreprocessorDefinitions)" 2444 m.element('UndefinePreprocessorDefinitions', condition, undefines) 2445 end 2446 end 2447 2448 local function getSymbolsPathRelative(cfg) 2449 if cfg.symbolspath and cfg.symbols ~= p.OFF and cfg.debugformat ~= "c7" then 2450 return p.project.getrelative(cfg.project, cfg.symbolspath) 2451 else 2452 return nil 2453 end 2454 end 2455 2456 function m.programDatabaseFile(cfg) 2457 local value = getSymbolsPathRelative(cfg) 2458 2459 if value then 2460 m.element("ProgramDatabaseFile", nil, value) 2461 end 2462 end 2463 2464 function m.programDatabaseFileName(cfg) 2465 local value = getSymbolsPathRelative(cfg) 2466 2467 if value then 2468 m.element("ProgramDataBaseFileName", nil, value) 2469 end 2470 end 2471 2472 2473 function m.projectGuid(prj) 2474 m.element("ProjectGuid", nil, "{%s}", prj.uuid) 2475 end 2476 2477 2478 function m.projectName(prj) 2479 if prj.name ~= prj.filename then 2480 m.element("ProjectName", nil, "%s", prj.name) 2481 end 2482 end 2483 2484 2485 function m.propertyGroup(cfg, label) 2486 local cond 2487 if cfg then 2488 cond = string.format(' %s', m.condition(cfg)) 2489 end 2490 2491 if label then 2492 label = string.format(' Label="%s"', label) 2493 end 2494 2495 p.push('<PropertyGroup%s%s>', cond or "", label or "") 2496 end 2497 2498 2499 2500 function m.propertySheets(cfg) 2501 p.push('<ImportGroup Label="PropertySheets" %s>', m.condition(cfg)) 2502 p.w('<Import Project="$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props" Condition="exists(\'$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\')" Label="LocalAppDataPlatform" />') 2503 p.pop('</ImportGroup>') 2504 end 2505 2506 2507 function m.propertySheetGroup(prj) 2508 for cfg in project.eachconfig(prj) do 2509 m.propertySheets(cfg) 2510 end 2511 end 2512 2513 2514 function m.referenceCopyLocalSatelliteAssemblies(prj, ref) 2515 m.element("CopyLocalSatelliteAssemblies", nil, "false") 2516 end 2517 2518 2519 function m.referenceLinkLibraryDependencies(prj, ref) 2520 m.element("LinkLibraryDependencies", nil, "true") 2521 end 2522 2523 2524 function m.referenceOutputAssembly(prj, ref) 2525 m.element("ReferenceOutputAssembly", nil, "true") 2526 end 2527 2528 2529 function m.referencePrivate(prj, ref) 2530 m.element("Private", nil, "true") 2531 end 2532 2533 2534 function m.referenceProject(prj, ref) 2535 m.element("Project", nil, "{%s}", ref.uuid) 2536 end 2537 2538 2539 function m.referenceUseLibraryDependences(prj, ref) 2540 m.element("UseLibraryDependencyInputs", nil, "false") 2541 end 2542 2543 2544 function m.resourceAdditionalIncludeDirectories(cfg) 2545 m.additionalIncludeDirectories(cfg, table.join(cfg.includedirs, cfg.resincludedirs)) 2546 end 2547 2548 2549 function m.resourcePreprocessorDefinitions(cfg) 2550 local defines = table.join(cfg.defines, cfg.resdefines) 2551 if cfg.exceptionhandling == p.OFF then 2552 table.insert(defines, "_HAS_EXCEPTIONS=0") 2553 end 2554 m.preprocessorDefinitions(cfg, defines, true) 2555 end 2556 2557 2558 function m.runtimeLibrary(cfg) 2559 local runtimes = { 2560 StaticDebug = "MultiThreadedDebug", 2561 StaticRelease = "MultiThreaded", 2562 SharedDebug = "MultiThreadedDebugDLL", 2563 SharedRelease = "MultiThreadedDLL" 2564 } 2565 local runtime = config.getruntime(cfg) 2566 if runtime then 2567 m.element("RuntimeLibrary", nil, runtimes[runtime]) 2568 end 2569 end 2570 2571 function m.callingConvention(cfg) 2572 if cfg.callingconvention then 2573 m.element("CallingConvention", nil, cfg.callingconvention) 2574 end 2575 end 2576 2577 function m.runtimeTypeInfo(cfg, condition) 2578 if cfg.rtti == p.OFF and ((not cfg.clr) or cfg.clr == p.OFF) then 2579 m.element("RuntimeTypeInfo", condition, "false") 2580 elseif cfg.rtti == p.ON then 2581 m.element("RuntimeTypeInfo", condition, "true") 2582 end 2583 end 2584 2585 function m.bufferSecurityCheck(cfg) 2586 local tool, toolVersion = p.config.toolset(cfg) 2587 if cfg.flags.NoBufferSecurityCheck or (toolVersion and toolVersion:startswith("LLVM-vs")) then 2588 m.element("BufferSecurityCheck", nil, "false") 2589 end 2590 end 2591 2592 function m.stringPooling(cfg) 2593 if cfg.stringpooling ~= nil then 2594 if cfg.stringpooling then 2595 m.element("StringPooling", nil, "true") 2596 else 2597 m.element("StringPooling", nil, "false") 2598 end 2599 elseif config.isOptimizedBuild(cfg) then 2600 m.element("StringPooling", nil, "true") 2601 end 2602 end 2603 2604 2605 function m.subSystem(cfg) 2606 local subsystem = iif(cfg.kind == p.CONSOLEAPP, "Console", "Windows") 2607 m.element("SubSystem", nil, subsystem) 2608 end 2609 2610 2611 function m.targetExt(cfg) 2612 local ext = cfg.buildtarget.extension 2613 if ext ~= "" then 2614 m.element("TargetExt", nil, "%s", ext) 2615 else 2616 p.w('<TargetExt>') 2617 p.w('</TargetExt>') 2618 end 2619 end 2620 2621 2622 function m.targetMachine(cfg) 2623 -- If a static library project contains a resource file, VS will choke with 2624 -- "LINK : warning LNK4068: /MACHINE not specified; defaulting to X86" 2625 local targetmachine = { 2626 x86 = "MachineX86", 2627 x86_64 = "MachineX64", 2628 } 2629 if cfg.kind == p.STATICLIB and config.hasFile(cfg, path.isresourcefile) then 2630 local value = targetmachine[cfg.architecture] 2631 if value ~= nil then 2632 m.element("TargetMachine", nil, '%s', value) 2633 end 2634 end 2635 end 2636 2637 2638 function m.targetName(cfg) 2639 m.element("TargetName", nil, "%s%s", cfg.buildtarget.prefix, cfg.buildtarget.basename) 2640 end 2641 2642 2643 function m.latestTargetPlatformVersion(prj) 2644 -- See https://developercommunity.visualstudio.com/content/problem/140294/windowstargetplatformversion-makes-it-impossible-t.html 2645 if _ACTION == "vs2017" then 2646 m.element("LatestTargetPlatformVersion", nil, "$([Microsoft.Build.Utilities.ToolLocationHelper]::GetLatestSDKTargetPlatformVersion('Windows', '10.0'))") 2647 end 2648 end 2649 2650 2651 function m.windowsTargetPlatformVersion(prj, cfg) 2652 if _ACTION < "vs2015" then 2653 return 2654 end 2655 2656 local target = cfg or prj 2657 local version = project.systemversion(target) 2658 2659 -- if this is a config, only emit if different from project 2660 if cfg then 2661 local prjVersion = project.systemversion(prj) 2662 if not prjVersion or version == prjVersion then 2663 return 2664 end 2665 end 2666 2667 -- See https://developercommunity.visualstudio.com/content/problem/140294/windowstargetplatformversion-makes-it-impossible-t.html 2668 if version == "latest" then 2669 if _ACTION == "vs2015" then 2670 version = nil -- SDK v10 is not supported by VS2015 2671 elseif _ACTION == "vs2017" then 2672 version = "$(LatestTargetPlatformVersion)" 2673 else 2674 version = "10.0" 2675 end 2676 end 2677 2678 if version then 2679 m.element("WindowsTargetPlatformVersion", nil, version) 2680 end 2681 end 2682 2683 2684 function m.xpDeprecationWarning(prj, cfg) 2685 if cfg.toolset == "msc-v141_xp" then 2686 m.element("XPDeprecationWarning", nil, "false") 2687 end 2688 end 2689 2690 2691 function m.preferredToolArchitecture(prj) 2692 if _ACTION >= "vs2013" then 2693 if prj.preferredtoolarchitecture == p.X86_64 then 2694 m.element("PreferredToolArchitecture", nil, 'x64') 2695 elseif prj.preferredtoolarchitecture == p.X86 then 2696 m.element("PreferredToolArchitecture", nil, 'x86') 2697 end 2698 else 2699 if prj.preferredtoolarchitecture == p.X86_64 then 2700 m.element("UseNativeEnvironment", nil, 'true') 2701 end 2702 end 2703 end 2704 2705 2706 function m.treatLinkerWarningAsErrors(cfg) 2707 if cfg.flags.FatalLinkWarnings then 2708 local el = iif(cfg.kind == p.STATICLIB, "Lib", "Linker") 2709 m.element("Treat" .. el .. "WarningAsErrors", nil, "true") 2710 end 2711 end 2712 2713 2714 function m.treatWChar_tAsBuiltInType(cfg) 2715 local map = { On = "true", Off = "false" } 2716 local value = map[cfg.nativewchar] 2717 if value then 2718 m.element("TreatWChar_tAsBuiltInType", nil, value) 2719 end 2720 end 2721 2722 2723 function m.treatWarningAsError(cfg) 2724 if cfg.flags.FatalCompileWarnings and cfg.warnings ~= p.OFF then 2725 m.element("TreatWarningAsError", nil, "true") 2726 end 2727 end 2728 2729 2730 function m.disableSpecificWarnings(cfg, condition) 2731 if #cfg.disablewarnings > 0 then 2732 local warnings = table.concat(cfg.disablewarnings, ";") 2733 warnings = warnings .. ";%%(DisableSpecificWarnings)" 2734 m.element('DisableSpecificWarnings', condition, warnings) 2735 end 2736 end 2737 2738 2739 function m.treatSpecificWarningsAsErrors(cfg, condition) 2740 if #cfg.fatalwarnings > 0 then 2741 local fatal = table.concat(cfg.fatalwarnings, ";") 2742 fatal = fatal .. ";%%(TreatSpecificWarningsAsErrors)" 2743 m.element('TreatSpecificWarningsAsErrors', condition, fatal) 2744 end 2745 end 2746 2747 2748 function m.useDebugLibraries(cfg) 2749 local runtime = config.getruntime(cfg) or iif(config.isDebugBuild(cfg), "Debug", "Release") 2750 m.element("UseDebugLibraries", nil, tostring(runtime:endswith("Debug"))) 2751 end 2752 2753 2754 function m.useOfMfc(cfg) 2755 if cfg.flags.MFC then 2756 m.element("UseOfMfc", nil, iif(cfg.staticruntime == "On", "Static", "Dynamic")) 2757 end 2758 end 2759 2760 function m.useOfAtl(cfg) 2761 if cfg.atl then 2762 m.element("UseOfATL", nil, cfg.atl) 2763 end 2764 end 2765 2766 2767 2768 function m.userMacros(cfg) 2769 p.w('<PropertyGroup Label="UserMacros" />') 2770 end 2771 2772 2773 function m.warningLevel(cfg) 2774 local map = { Off = "TurnOffAllWarnings", High = "Level4", Extra = "Level4", Everything = "EnableAllWarnings" } 2775 m.element("WarningLevel", nil, map[cfg.warnings] or "Level3") 2776 end 2777 2778 2779 function m.warningLevelFile(cfg, condition) 2780 local map = { Off = "TurnOffAllWarnings", High = "Level4", Extra = "Level4", Everything = "EnableAllWarnings" } 2781 if cfg.warnings then 2782 m.element("WarningLevel", condition, map[cfg.warnings] or "Level3") 2783 end 2784 end 2785 2786 2787 function m.xmlDeclaration() 2788 p.xmlUtf8() 2789 end 2790 2791 -- Fx Functions 2792 -------------------------------------------------------------------------------------------------------------- 2793 -------------------------------------------------------------------------------------------------------------- 2794 2795 function m.fxCompilePreprocessorDefinition(cfg, condition) 2796 if cfg.shaderdefines and #cfg.shaderdefines > 0 then 2797 local shaderdefines = table.concat(cfg.shaderdefines, ";") 2798 2799 shaderdefines = shaderdefines .. ";%%(PreprocessorDefinitions)" 2800 m.element('PreprocessorDefinitions', condition, shaderdefines) 2801 end 2802 end 2803 2804 function m.fxCompileAdditionalIncludeDirs(cfg, condition) 2805 if cfg.shaderincludedirs and #cfg.shaderincludedirs > 0 then 2806 local dirs = vstudio.path(cfg, cfg.shaderincludedirs) 2807 m.element('AdditionalIncludeDirectories', condition, "%s;%%(AdditionalIncludeDirectories)", table.concat(dirs, ";")) 2808 end 2809 end 2810 2811 function m.fxCompileShaderType(cfg, condition) 2812 if cfg.shadertype then 2813 m.element("ShaderType", condition, cfg.shadertype) 2814 end 2815 end 2816 2817 2818 function m.fxCompileShaderModel(cfg, condition) 2819 if cfg.shadermodel then 2820 m.element("ShaderModel", condition, cfg.shadermodel) 2821 end 2822 end 2823 2824 2825 function m.fxCompileShaderEntry(cfg, condition) 2826 if cfg.shaderentry then 2827 m.element("EntryPointName", condition, cfg.shaderentry) 2828 end 2829 end 2830 2831 2832 function m.fxCompileShaderVariableName(cfg, condition) 2833 if cfg.shadervariablename then 2834 m.element("VariableName", condition, cfg.shadervariablename) 2835 end 2836 end 2837 2838 2839 function m.fxCompileShaderHeaderOutput(cfg, condition) 2840 if cfg.shaderheaderfileoutput then 2841 m.element("HeaderFileOutput", condition, cfg.shaderheaderfileoutput) 2842 end 2843 end 2844 2845 2846 function m.fxCompileShaderObjectOutput(cfg, condition) 2847 if cfg.shaderobjectfileoutput then 2848 m.element("ObjectFileOutput", condition, cfg.shaderobjectfileoutput) 2849 end 2850 end 2851 2852 2853 function m.fxCompileShaderAssembler(cfg, condition) 2854 if cfg.shaderassembler then 2855 m.element("AssemblerOutput", condition, cfg.shaderassembler) 2856 end 2857 end 2858 2859 2860 function m.fxCompileShaderAssemblerOutput(cfg, condition) 2861 if cfg.shaderassembleroutput then 2862 m.element("AssemblerOutputFile", condition, cfg.shaderassembleroutput) 2863 end 2864 end 2865 2866 2867 function m.fxCompileShaderAdditionalOptions(cfg, condition) 2868 local opts = cfg.shaderoptions 2869 if #opts > 0 then 2870 opts = table.concat(opts, " ") 2871 m.element("AdditionalOptions", condition, '%s %%(AdditionalOptions)', opts) 2872 end 2873 end 2874 2875 2876--------------------------------------------------------------------------- 2877-- 2878-- Support functions 2879-- 2880--------------------------------------------------------------------------- 2881 2882-- 2883-- Format and return a Visual Studio Condition attribute. 2884-- 2885 2886 function m.conditionFromConfigText(cfgText) 2887 return string.format('Condition="\'$(Configuration)|$(Platform)\'==\'%s\'"', p.esc(cfgText)) 2888 end 2889 2890 function m.condition(cfg) 2891 return m.conditionFromConfigText(vstudio.projectConfig(cfg)) 2892 end 2893 2894 2895-- 2896-- Output an individual project XML element, with an optional configuration 2897-- condition. 2898-- 2899-- @param depth 2900-- How much to indent the element. 2901-- @param name 2902-- The element name. 2903-- @param condition 2904-- An optional configuration condition, formatted with vc2010.condition(). 2905-- @param value 2906-- The element value, which may contain printf formatting tokens. 2907-- @param ... 2908-- Optional additional arguments to satisfy any tokens in the value. 2909-- 2910 2911 function m.element(name, condition, value, ...) 2912 local arg = {...} 2913 if select('#',...) == 0 then 2914 value = p.esc(value) 2915 else 2916 for i = 1, #arg do 2917 arg[i] = p.esc(arg[i]) 2918 end 2919 end 2920 2921 if condition then 2922 --defer output 2923 local element = {} 2924 element.name = name 2925 element.condition = condition 2926 element.value = value 2927 element.args = arg 2928 if ... then 2929 if value == '%s' then 2930 element.setting = table.concat(arg) 2931 else 2932 element.setting = value .. table.concat(arg) 2933 end 2934 else 2935 element.setting = element.value 2936 end 2937 table.insert(m.conditionalElements, element) 2938 else 2939 local format = string.format('<%s>%s</%s>', name, value, name) 2940 p.w(format, table.unpack(arg)) 2941 end 2942 end 2943