1--
2-- vs200x_vcproj.lua
3-- Generate a Visual Studio 2002-2008 C/C++ project.
4-- Copyright (c) 2009-2014 Jason Perkins and the Premake project
5--
6
7	local p = premake
8	p.vstudio.vc200x = {}
9	local m = p.vstudio.vc200x
10
11	local vstudio = p.vstudio
12	local context = p.context
13	local project = p.project
14	local config = p.config
15	local fileconfig = p.fileconfig
16
17	m.elements = {}
18
19
20
21---
22-- Generate a Visual Studio 200x C++ or Makefile project.
23---
24
25	m.elements.project = function(prj)
26		return {
27			m.xmlElement,
28			m.visualStudioProject,
29			m.platforms,
30			m.toolFiles,
31			m.configurations,
32			m.references,
33			m.files,
34			m.globals
35		}
36	end
37
38	function m.generate(prj)
39		p.indent("\t")
40		p.callArray(m.elements.project, prj)
41		p.pop('</VisualStudioProject>')
42		p.w()
43	end
44
45
46
47---
48-- Write the opening <VisualStudioProject> element of the project file.
49-- In this case, the call list is for XML attributes rather than elements.
50---
51
52	m.elements.visualStudioProject = function(prj)
53		return {
54			m.projectType,
55			m.version,
56			m.projectName,
57			m.projectGUID,
58			m.rootNamespace,
59			m.keyword,
60			m.targetFrameworkVersion
61		}
62	end
63
64	function m.visualStudioProject(prj)
65		p.push('<VisualStudioProject')
66		p.callArray(m.elements.visualStudioProject, prj)
67		p.w('>')
68	end
69
70
71
72---
73-- Write out the <Configurations> element group, enumerating each of the
74-- configuration-architecture pairings.
75---
76
77	function m.configurations(prj)
78		p.push('<Configurations>')
79
80		-- Visual Studio requires each configuration to be paired up with each
81		-- architecture, even if the pairing doesn't make any sense (i.e. Win32
82		-- DLL DCRT|PS3). Start by building a map between configurations and
83		-- their Visual Studio names. I will use this to determine which
84		-- pairings are "real", and which need to be synthesized.
85
86		local mapping = {}
87		for cfg in project.eachconfig(prj) do
88			local name = vstudio.projectConfig(cfg)
89			mapping[cfg] = name
90			mapping[name] = cfg
91		end
92
93		-- Now enumerate each configuration and architecture pairing
94
95		for cfg in project.eachconfig(prj) do
96			for i, arch in ipairs(architectures) do
97				local target
98
99				-- Generate a Visual Studio name from this pairing and see if
100				-- it matches. If so, I can go ahead and output the markup for
101				-- this configuration.
102
103				local testName = vstudio.projectConfig(cfg, arch)
104				if testName == mapping[cfg] then
105					target = cfg
106
107				-- Okay, this pairing doesn't match this configuration. Check
108				-- the mapping to see if it matches some *other* configuration.
109				-- If it does, I can ignore it as it will getting written on
110				-- another pass through the loop. If it does not, then this is
111				-- one of those fake configurations that I have to synthesize.
112
113				elseif not mapping[testName] then
114					target = { fake = true }
115				end
116
117				-- If I'm not ignoring this pairing, output the result now
118
119				if target then
120					m.configuration(target, testName)
121					m.tools(target)
122					p.pop('</Configuration>')
123				end
124			end
125		end
126
127		p.pop('</Configurations>')
128	end
129
130
131
132---
133-- Write out the <Configuration> element, describing a specific Premake
134-- build configuration/platform pairing.
135---
136
137	m.elements.configuration = function(cfg)
138		if cfg.fake then
139			return {
140				m.intermediateDirectory,
141				m.configurationType
142			}
143		else
144			return {
145				m.outputDirectory,
146				m.intermediateDirectory,
147				m.configurationType,
148				m.useOfMFC,
149				m.characterSet,
150				m.managedExtensions,
151			}
152		end
153	end
154
155	function m.configuration(cfg, name)
156		p.push('<Configuration')
157		p.w('Name="%s"', name)
158		p.callArray(m.elements.configuration, cfg)
159		p.w('>')
160	end
161
162
163
164---
165-- Return the list of tools required to build a specific configuration.
166-- Each tool gets represented by an XML element in the project file, all
167-- of which are implemented farther down in this file.
168--
169-- @param cfg
170--    The configuration being written.
171---
172
173	m.elements.tools = function(cfg)
174		if vstudio.isMakefile(cfg) and not cfg.fake then
175			return {
176				m.VCNMakeTool
177			}
178		end
179		if cfg.system == p.XBOX360 then
180			return {
181				m.VCPreBuildEventTool,
182				m.VCCustomBuildTool,
183				m.VCXMLDataGeneratorTool,
184				m.VCWebServiceProxyGeneratorTool,
185				m.VCMIDLTool,
186				m.VCCLCompilerTool,
187				m.VCManagedResourceCompilerTool,
188				m.VCResourceCompilerTool,
189				m.VCPreLinkEventTool,
190				m.VCLinkerTool,
191				m.VCALinkTool,
192				m.VCX360ImageTool,
193				m.VCBscMakeTool,
194				m.VCX360DeploymentTool,
195				m.VCPostBuildEventTool,
196				m.DebuggerTool,
197			}
198		else
199			return {
200				m.VCPreBuildEventTool,
201				m.VCCustomBuildTool,
202				m.VCXMLDataGeneratorTool,
203				m.VCWebServiceProxyGeneratorTool,
204				m.VCMIDLTool,
205				m.VCCLCompilerTool,
206				m.VCManagedResourceCompilerTool,
207				m.VCResourceCompilerTool,
208				m.VCPreLinkEventTool,
209				m.VCLinkerTool,
210				m.VCALinkTool,
211				m.VCManifestTool,
212				m.VCXDCMakeTool,
213				m.VCBscMakeTool,
214				m.VCFxCopTool,
215				m.VCAppVerifierTool,
216				m.VCPostBuildEventTool,
217			}
218		end
219	end
220
221	function m.tools(cfg)
222		p.callArray(m.elements.tools, cfg, config.toolset(cfg))
223	end
224
225
226
227---
228-- Write out the <References> element group.
229---
230
231	m.elements.references = function(prj)
232		return {
233			m.assemblyReferences,
234			m.projectReferences,
235		}
236	end
237
238	function m.references(prj)
239		p.push('<References>')
240		p.callArray(m.elements.references, prj)
241		p.pop('</References>')
242	end
243
244
245
246---
247-- Write out the <Files> element group.
248---
249
250	function m.files(prj)
251		local tr = m.filesSorted(prj)
252		p.push('<Files>')
253		p.tree.traverse(tr, {
254			onbranchenter = m.filesFilterStart,
255			onbranchexit = m.filesFilterEnd,
256			onleaf = m.filesFile,
257		}, false)
258		p.pop('</Files>')
259	end
260
261	function m.filesSorted(prj)
262		-- Fetch the source tree, sorted how Visual Studio likes it: alpha
263		-- sorted, with any leading ../ sequences ignored. At the top level
264		-- of the tree, files go after folders, otherwise before.
265		return project.getsourcetree(prj, function(a,b)
266			local istop = (a.parent.parent == nil)
267
268			local aSortName = a.name
269			local bSortName = b.name
270
271			-- Only file nodes have a relpath field; folder nodes do not
272			if a.relpath then
273				if not b.relpath then
274					return not istop
275				end
276				aSortName = a.relpath:gsub("%.%.%/", "")
277			end
278
279			if b.relpath then
280				if not a.relpath then
281					return istop
282				end
283				bSortName = b.relpath:gsub("%.%.%/", "")
284			end
285
286			return aSortName < bSortName
287		end)
288	end
289
290	function m.filesFilterStart(node)
291		p.push('<Filter')
292		p.w('Name="%s"', node.name)
293		p.w('>')
294	end
295
296	function m.filesFilterEnd(node)
297		p.pop('</Filter>')
298
299	end
300
301	function m.filesFile(node)
302		p.push('<File')
303		p.w('RelativePath="%s"', path.translate(node.relpath))
304		p.w('>')
305		local prj = node.project
306		for cfg in project.eachconfig(prj) do
307			m.fileConfiguration(cfg, node)
308		end
309		p.pop('</File>')
310	end
311
312	m.elements.fileConfigurationAttributes = function(filecfg)
313		return {
314			m.excludedFromBuild,
315		}
316	end
317
318	function m.fileConfiguration(cfg, node)
319		local filecfg = fileconfig.getconfig(node, cfg)
320
321		-- Generate the individual sections of the file configuration
322		-- element and capture the results to a buffer. I will only
323		-- write the file configuration if the buffers are not empty.
324
325		local configAttribs = p.capture(function ()
326			p.push()
327			p.callArray(m.elements.fileConfigurationAttributes, filecfg)
328			p.pop()
329		end)
330
331		local compilerAttribs = p.capture(function ()
332			p.push()
333			m.VCCLCompilerTool(filecfg)
334			p.pop()
335		end)
336
337		-- lines() > 3 skips empty <Tool Name="VCCLCompiler" /> elements
338		if #configAttribs > 0 or compilerAttribs:lines() > 3 then
339			p.push('<FileConfiguration')
340			p.w('Name="%s"', vstudio.projectConfig(cfg))
341			if #configAttribs > 0 then
342				p.outln(configAttribs)
343			end
344			p.w('>')
345			p.outln(compilerAttribs)
346			p.pop('</FileConfiguration>')
347		end
348	end
349
350
351
352---
353-- I don't do anything with globals yet, but here it is if you want to
354-- extend it.
355---
356
357	m.elements.globals = function(prj)
358		return {}
359	end
360
361	function m.globals(prj)
362		p.push('<Globals>')
363		p.callArray(m.elements.globals, prj)
364		p.pop('</Globals>')
365	end
366
367
368
369---------------------------------------------------------------------------
370--
371-- Handlers for the individual tool sections of the project.
372--
373-- There is a lot of repetition here; most of these tools are just
374-- placeholders for modules to override as needed.
375--
376---------------------------------------------------------------------------
377
378
379---
380-- The implementation of a "normal" tool. Writes the opening tool element
381-- and name attribute, calls the corresponding function list, and then
382-- closes the element.
383--
384-- @param name
385--    The name of the tool, e.g. "VCCustomBuildTool".
386-- @param ...
387--    Any additional arguments required by the call list.
388---
389
390	function m.VCTool(name, cfg, ...)
391		p.push('<Tool')
392
393		local nameFunc = m[name .. "Name"]
394		local callFunc = m.elements[name]
395
396		if nameFunc then
397			name = nameFunc(cfg, ...)
398		end
399		p.w('Name="%s"', name)
400
401		if cfg and not cfg.fake then
402			p.callArray(callFunc, cfg, ...)
403		end
404
405		p.pop('/>')
406	end
407
408	------------
409
410	m.elements.DebuggerTool = function(cfg)
411		return {}
412	end
413
414	function m.DebuggerTool(cfg)
415		p.push('<DebuggerTool')
416		p.pop('/>')
417	end
418
419	------------
420
421	m.elements.VCALinkTool = function(cfg)
422		return {}
423	end
424
425	function m.VCALinkTool(cfg)
426		m.VCTool("VCALinkTool", cfg)
427	end
428
429	------------
430
431	m.elements.VCAppVerifierTool = function(cfg)
432		return {}
433	end
434
435	function m.VCAppVerifierTool(cfg)
436		if cfg.kind ~= p.STATICLIB then
437			m.VCTool("VCAppVerifierTool", cfg)
438		end
439	end
440
441	------------
442
443	m.elements.VCBscMakeTool = function(cfg)
444		return {}
445	end
446
447	function m.VCBscMakeTool(cfg)
448		m.VCTool("VCBscMakeTool", cfg)
449	end
450
451	------------
452
453	m.elements.VCCLCompilerTool = function(cfg, toolset)
454		if not toolset then
455			-- not a custom tool, use the standard set of attributes
456			return {
457				m.customBuildTool,
458				m.objectFile,
459				m.additionalCompilerOptions,
460				m.optimization,
461				m.additionalIncludeDirectories,
462				m.wholeProgramOptimization,
463				m.preprocessorDefinitions,
464				m.undefinePreprocessorDefinitions,
465				m.minimalRebuild,
466				m.basicRuntimeChecks,
467				m.bufferSecurityCheck,
468				m.stringPooling,
469				m.exceptionHandling,
470				m.runtimeLibrary,
471				m.enableFunctionLevelLinking,
472				m.enableEnhancedInstructionSet,
473				m.floatingPointModel,
474				m.runtimeTypeInfo,
475				m.treatWChar_tAsBuiltInType,
476				m.usePrecompiledHeader,
477				m.programDataBaseFileName,
478				m.warningLevel,
479				m.warnAsError,
480				m.detect64BitPortabilityProblems,
481				m.debugInformationFormat,
482				m.compileAs,
483				m.disableSpecificWarnings,
484				m.forcedIncludeFiles,
485				m.omitDefaultLib,
486			}
487		else
488			-- custom tool, use subset of attributes
489			return {
490				m.additionalExternalCompilerOptions,
491				m.additionalIncludeDirectories,
492				m.preprocessorDefinitions,
493				m.undefinePreprocessorDefinitions,
494				m.usePrecompiledHeader,
495				m.programDataBaseFileName,
496				m.debugInformationFormat,
497				m.compileAs,
498				m.forcedIncludeFiles,
499			}
500		end
501	end
502
503	function m.VCCLCompilerToolName(cfg)
504		local prjcfg, filecfg = config.normalize(cfg)
505		if filecfg and fileconfig.hasCustomBuildRule(filecfg) then
506			return "VCCustomBuildTool"
507		elseif prjcfg and prjcfg.system == p.XBOX360 then
508			return "VCCLX360CompilerTool"
509		else
510			return "VCCLCompilerTool"
511		end
512	end
513
514	function m.VCCLCompilerTool(cfg, toolset)
515		m.VCTool("VCCLCompilerTool", cfg, toolset)
516	end
517
518	------------
519
520	m.elements.VCCustomBuildTool = function(cfg)
521		return {}
522	end
523
524	function m.VCCustomBuildTool(cfg)
525		m.VCTool("VCCustomBuildTool", cfg)
526	end
527
528	------------
529
530	m.elements.VCFxCopTool = function(cfg)
531		return {}
532	end
533
534	function m.VCFxCopTool(cfg)
535		m.VCTool("VCFxCopTool", cfg)
536	end
537
538	------------
539
540	m.elements.VCLinkerTool = function(cfg, toolset)
541		if cfg.kind ~= p.STATICLIB then
542			return {
543				m.linkLibraryDependencies,
544				m.ignoreImportLibrary,
545				m.additionalLinkerOptions,
546				m.additionalDependencies,
547				m.outputFile,
548				m.linkIncremental,
549				m.additionalLibraryDirectories,
550				m.moduleDefinitionFile,
551				m.generateManifest,
552				m.generateDebugInformation,
553				m.programDatabaseFile,
554				m.subSystem,
555				m.largeAddressAware,
556				m.optimizeReferences,
557				m.enableCOMDATFolding,
558				m.entryPointSymbol,
559				m.importLibrary,
560				m.targetMachine,
561			}
562		else
563			return {
564				m.additionalLinkerOptions,
565				m.additionalDependencies,
566				m.outputFile,
567				m.additionalLibraryDirectories,
568			}
569		end
570	end
571
572	function m.VCLinkerToolName(cfg)
573		if cfg.kind == p.STATICLIB then
574			return "VCLibrarianTool"
575		elseif cfg.system == p.XBOX360 then
576			return "VCX360LinkerTool"
577		else
578			return "VCLinkerTool"
579		end
580	end
581
582	function m.VCLinkerTool(cfg, toolset)
583		m.VCTool("VCLinkerTool", cfg, toolset)
584	end
585
586	------------
587
588	m.elements.VCManagedResourceCompilerTool = function(cfg)
589		return {}
590	end
591
592	function m.VCManagedResourceCompilerTool(cfg)
593		m.VCTool("VCManagedResourceCompilerTool", cfg)
594	end
595
596	------------
597
598	m.elements.VCManifestTool = function(cfg)
599		return {
600			m.additionalManifestFiles,
601		}
602	end
603
604	function m.VCManifestTool(cfg)
605		if cfg.kind ~= p.STATICLIB then
606			m.VCTool("VCManifestTool", cfg)
607		end
608	end
609
610	------------
611
612	m.elements.VCMIDLTool = function(cfg)
613		return {
614			m.targetEnvironment
615		}
616	end
617
618	function m.VCMIDLTool(cfg)
619		m.VCTool("VCMIDLTool", cfg)
620	end
621
622	------------
623
624	m.elements.VCNMakeTool = function(cfg)
625		return {
626			m.buildCommandLine,
627			m.reBuildCommandLine,
628			m.cleanCommandLine,
629			m.output,
630			m.preprocessorDefinitions,
631			m.undefinePreprocessorDefinitions,
632			m.includeSearchPath,
633			m.forcedIncludes,
634			m.assemblySearchPath,
635			m.forcedUsingAssemblies,
636			m.compileAsManaged,
637		}
638	end
639
640	function m.VCNMakeTool(cfg)
641		m.VCTool("VCNMakeTool", cfg)
642	end
643
644	------------
645
646	m.elements.VCBuildTool = function(cfg, stage)
647		return {
648			m.commandLine,
649		}
650	end
651
652	function m.VCBuildToolName(cfg, stage)
653		return "VC" .. stage .. "EventTool"
654	end
655
656	function m.VCPreBuildEventTool(cfg)
657		m.VCTool("VCBuildTool", cfg, "PreBuild")
658	end
659
660	function m.VCPreLinkEventTool(cfg)
661		m.VCTool("VCBuildTool", cfg, "PreLink")
662	end
663
664	function m.VCPostBuildEventTool(cfg)
665		m.VCTool("VCBuildTool", cfg, "PostBuild")
666	end
667
668	------------
669
670	m.elements.VCResourceCompilerTool = function(cfg)
671		return {
672			m.additionalResourceOptions,
673			m.resourcePreprocessorDefinitions,
674			m.additionalResourceIncludeDirectories,
675			m.culture,
676		}
677	end
678
679	function m.VCResourceCompilerTool(cfg)
680		m.VCTool("VCResourceCompilerTool", cfg)
681	end
682
683	------------
684
685	m.elements.VCWebServiceProxyGeneratorTool = function(cfg)
686		return {}
687	end
688
689	function m.VCWebServiceProxyGeneratorTool(cfg)
690		m.VCTool("VCWebServiceProxyGeneratorTool", cfg)
691	end
692
693	------------
694
695	m.elements.VCX360DeploymentTool = function(cfg)
696		return {
697			m.deploymentType,
698			m.additionalDeploymentOptions,
699		}
700	end
701
702	function m.VCX360DeploymentTool(cfg)
703		m.VCTool("VCX360DeploymentTool", cfg)
704	end
705
706	------------
707
708	m.elements.VCX360ImageTool = function(cfg)
709		return {
710			m.additionalImageOptions,
711			m.outputFileName,
712		}
713	end
714
715	function m.VCX360ImageTool(cfg)
716		m.VCTool("VCX360ImageTool", cfg)
717	end
718
719	------------
720
721	m.elements.VCXDCMakeTool = function(cfg)
722		return {}
723	end
724
725	function m.VCXDCMakeTool(cfg)
726		m.VCTool("VCXDCMakeTool", cfg)
727	end
728
729	------------
730
731	m.elements.VCXMLDataGeneratorTool = function(cfg)
732		return {}
733	end
734
735	function m.VCXMLDataGeneratorTool(cfg)
736		m.VCTool("VCXMLDataGeneratorTool", cfg)
737	end
738
739
740
741---------------------------------------------------------------------------
742--
743-- Support functions
744--
745---------------------------------------------------------------------------
746
747--
748-- Return the debugging symbol level for a configuration.
749--
750
751	function m.symbols(cfg)
752		if not (cfg.symbols == p.ON) then
753			return 0
754		elseif cfg.debugformat == "c7" then
755			return 1
756		else
757			-- Edit-and-continue doesn't work for some configurations
758			if cfg.editandcontinue == p.OFF or
759			   config.isOptimizedBuild(cfg) or
760			   cfg.clr ~= p.OFF or
761			   cfg.architecture == p.X86_64
762			then
763				return 3
764			else
765				return 4
766			end
767		end
768	end
769
770
771
772---------------------------------------------------------------------------
773--
774-- Handlers for individual project elements
775--
776---------------------------------------------------------------------------
777
778
779	function m.additionalCompilerOptions(cfg)
780		local opts = cfg.buildoptions
781		if cfg.flags.MultiProcessorCompile then
782			table.insert(opts, "/MP")
783		end
784		if #opts > 0 then
785			p.x('AdditionalOptions="%s"', table.concat(opts, " "))
786		end
787	end
788
789
790
791	function m.additionalDependencies(cfg, toolset)
792		if #cfg.links == 0 then return end
793
794		local ex = vstudio.needsExplicitLink(cfg)
795
796		local links
797		if not toolset then
798			links = vstudio.getLinks(cfg, ex)
799			for i, link in ipairs(links) do
800				if link:find(" ", 1, true) then
801					link = '"' .. link .. '"'
802				end
803				links[i] = path.translate(link)
804			end
805		else
806			links = path.translate(toolset.getlinks(cfg, not ex))
807		end
808
809		if #links > 0 then
810			p.x('AdditionalDependencies="%s"', table.concat(links, " "))
811		end
812	end
813
814
815
816	function m.additionalDeploymentOptions(cfg)
817		if #cfg.deploymentoptions > 0 then
818			p.x('AdditionalOptions="%s"', table.concat(cfg.deploymentoptions, " "))
819		end
820	end
821
822
823
824	function m.additionalExternalCompilerOptions(cfg, toolset)
825		local buildoptions = table.join(toolset.getcxxflags(cfg), cfg.buildoptions)
826		if not cfg.flags.NoPCH and cfg.pchheader then
827			table.insert(buildoptions, '--use_pch="$(IntDir)/$(TargetName).pch"')
828		end
829		if #buildoptions > 0 then
830			p.x('AdditionalOptions="%s"', table.concat(buildoptions, " "))
831		end
832	end
833
834
835
836	function m.additionalImageOptions(cfg)
837		if #cfg.imageoptions > 0 then
838			p.x('AdditionalOptions="%s"', table.concat(cfg.imageoptions, " "))
839		end
840	end
841
842
843
844	function m.additionalIncludeDirectories(cfg)
845		if #cfg.includedirs > 0 then
846			local dirs = vstudio.path(cfg, cfg.includedirs)
847			p.x('AdditionalIncludeDirectories="%s"', table.concat(dirs, ";"))
848		end
849	end
850
851
852	function m.additionalLibraryDirectories(cfg)
853		if #cfg.libdirs > 0 then
854			local dirs = vstudio.path(cfg, cfg.libdirs)
855			p.x('AdditionalLibraryDirectories="%s"', table.concat(dirs, ";"))
856		end
857	end
858
859
860
861	function m.additionalLinkerOptions(cfg, toolset)
862		local flags
863		if toolset then
864			flags = table.join(toolset.getldflags(cfg), cfg.linkoptions)
865		else
866			flags = cfg.linkoptions
867		end
868		if #flags > 0 then
869			p.x('AdditionalOptions="%s"', table.concat(flags, " "))
870		end
871	end
872
873
874
875	function m.additionalManifestFiles(cfg)
876		local manifests = {}
877		for i, fname in ipairs(cfg.files) do
878			if path.getextension(fname) == ".manifest" then
879				table.insert(manifests, project.getrelative(cfg.project, fname))
880			end
881		end
882		if #manifests > 0 then
883			p.x('AdditionalManifestFiles="%s"', table.concat(manifests, ";"))
884		end
885	end
886
887
888
889	function m.additionalResourceIncludeDirectories(cfg)
890		local dirs = table.join(cfg.includedirs, cfg.resincludedirs)
891		if #dirs > 0 then
892			dirs = vstudio.path(cfg, dirs)
893			p.x('AdditionalIncludeDirectories="%s"', table.concat(dirs, ";"))
894		end
895	end
896
897
898
899	function m.additionalResourceOptions(cfg)
900		if #cfg.resoptions > 0 then
901			p.x('AdditionalOptions="%s"', table.concat(cfg.resoptions, " "))
902		end
903	end
904
905
906
907	function m.assemblyReferences(prj)
908		-- Visual Studio doesn't support per-config references
909		local cfg = project.getfirstconfig(prj)
910		local refs = config.getlinks(cfg, "system", "fullpath", "managed")
911		table.foreachi(refs, function(value)
912			p.push('<AssemblyReference')
913			p.x('RelativePath="%s"', path.translate(value))
914			p.pop('/>')
915		end)
916	end
917
918
919
920	function m.assemblySearchPath(cfg)
921		p.w('AssemblySearchPath=""')
922	end
923
924
925
926	function m.basicRuntimeChecks(cfg)
927		local cfg, filecfg = config.normalize(cfg)
928		if not filecfg
929			and not config.isOptimizedBuild(cfg)
930			and cfg.clr == p.OFF
931			and not cfg.flags.NoRuntimeChecks
932		then
933			p.w('BasicRuntimeChecks="3"')
934		end
935	end
936
937
938
939	function m.bufferSecurityCheck(cfg)
940		if cfg.flags.NoBufferSecurityCheck then
941			p.w('BufferSecurityCheck="false"')
942		end
943	end
944
945
946
947	function m.buildCommandLine(cfg)
948		local cmds = os.translateCommandsAndPaths(cfg.buildcommands, cfg.project.basedir, cfg.project.location)
949		p.x('BuildCommandLine="%s"', table.concat(cmds, "\r\n"))
950	end
951
952
953
954	function m.characterSet(cfg)
955		if not vstudio.isMakefile(cfg) then
956			p.w('CharacterSet="%s"', iif(cfg.characterset == p.MBCS, 2, 1))
957		end
958	end
959
960
961
962	function m.cleanCommandLine(cfg)
963		local cmds = os.translateCommandsAndPaths(cfg.cleancommands, cfg.project.basedir, cfg.project.location)
964		cmds = table.concat(cmds, "\r\n")
965		p.x('CleanCommandLine="%s"', cmds)
966	end
967
968
969
970	function m.commandLine(cfg, stage)
971		local field = stage:lower()
972		local steps = cfg[field .. "commands"]
973		local msg = cfg[field .. "message"]
974		if #steps > 0 then
975			if msg then
976				p.x('Description="%s"', msg)
977			end
978			steps = os.translateCommandsAndPaths(steps, cfg.project.basedir, cfg.project.location)
979			p.x('CommandLine="%s"', table.implode(steps, "", "", "\r\n"))
980		end
981	end
982
983
984
985	function m.compileAs(cfg, toolset)
986		local cfg, filecfg = config.normalize(cfg)
987		local c = p.languages.isc(cfg.language)
988		if filecfg then
989			if path.iscfile(filecfg.name) ~= c then
990				if path.iscppfile(filecfg.name) then
991					local value = iif(c, 2, 1)
992					p.w('CompileAs="%s"', value)
993				end
994			end
995		else
996			local compileAs
997			if toolset then
998				compileAs = "0"
999			elseif c then
1000				compileAs = "1"
1001			end
1002			if compileAs then
1003				p.w('CompileAs="%s"', compileAs)
1004			end
1005		end
1006	end
1007
1008
1009
1010	function m.disableSpecificWarnings(cfg)
1011		if #cfg.disablewarnings > 0 then
1012			p.x('DisableSpecificWarnings="%s"', table.concat(cfg.disablewarnings, ";"))
1013		end
1014	end
1015
1016
1017
1018	function m.compileAsManaged(cfg)
1019		p.w('CompileAsManaged=""')
1020	end
1021
1022
1023
1024	function m.configurationType(cfg)
1025		local cfgtypes = {
1026			Makefile = 0,
1027			None = 0,
1028			SharedLib = 2,
1029			StaticLib = 4,
1030		}
1031		p.w('ConfigurationType="%s"', cfgtypes[cfg.kind] or 1)
1032	end
1033
1034
1035
1036	function m.culture(cfg)
1037		local value = vstudio.cultureForLocale(cfg.locale)
1038		if value then
1039			p.w('Culture="%d"', value)
1040		end
1041	end
1042
1043
1044
1045	function m.customBuildTool(cfg)
1046		local cfg, filecfg = config.normalize(cfg)
1047		if filecfg and fileconfig.hasCustomBuildRule(filecfg) then
1048			local cmds = os.translateCommandsAndPaths(filecfg.buildcommands, filecfg.project.basedir, filecfg.project.location)
1049			p.x('CommandLine="%s"', table.concat(cmds,'\r\n'))
1050
1051			local outputs = project.getrelative(filecfg.project, filecfg.buildoutputs)
1052			p.x('Outputs="%s"', table.concat(outputs, ';'))
1053
1054			if filecfg.buildinputs and #filecfg.buildinputs > 0 then
1055				local inputs = project.getrelative(filecfg.project, filecfg.buildinputs)
1056				p.x('AdditionalDependencies="%s"', table.concat(inputs, ';'))
1057			end
1058		end
1059	end
1060
1061
1062
1063	function m.debugInformationFormat(cfg, toolset)
1064		local prjcfg, filecfg = config.normalize(cfg)
1065		if not filecfg then
1066			local fmt = iif(toolset, "0", m.symbols(cfg))
1067			p.w('DebugInformationFormat="%s"', fmt)
1068		end
1069	end
1070
1071
1072
1073	function m.deploymentType(cfg)
1074		p.w('DeploymentType="0"')
1075	end
1076
1077
1078
1079	function m.detect64BitPortabilityProblems(cfg)
1080		local prjcfg, filecfg = config.normalize(cfg)
1081		if _ACTION < "vs2008" and cfg.clr == p.OFF and cfg.warnings ~= p.OFF and not filecfg then
1082			p.w('Detect64BitPortabilityProblems="%s"', tostring(not cfg.flags.No64BitChecks))
1083		end
1084	end
1085
1086
1087
1088	function m.enableCOMDATFolding(cfg, toolset)
1089		if config.isOptimizedBuild(cfg) and not toolset then
1090			p.w('EnableCOMDATFolding="2"')
1091		end
1092	end
1093
1094
1095
1096	function m.largeAddressAware(cfg)
1097		if (cfg.largeaddressaware == true) then
1098			p.w('LargeAddressAware="2"')
1099		end
1100	end
1101
1102
1103
1104	function m.enableEnhancedInstructionSet(cfg)
1105		local map = { SSE = "1", SSE2 = "2" }
1106		local value = map[cfg.vectorextensions]
1107		if value and cfg.system ~= "Xbox360" and cfg.architecture ~= "x86_64" then
1108			p.w('EnableEnhancedInstructionSet="%d"', value)
1109		end
1110	end
1111
1112
1113
1114	function m.enableFunctionLevelLinking(cfg)
1115		local cfg, filecfg = config.normalize(cfg)
1116		if not filecfg then
1117			p.w('EnableFunctionLevelLinking="true"')
1118		end
1119	end
1120
1121
1122
1123	function m.entryPointSymbol(cfg, toolset)
1124		if cfg.entrypoint then
1125			p.w('EntryPointSymbol="%s"', cfg.entrypoint)
1126		end
1127	end
1128
1129
1130
1131	function m.exceptionHandling(cfg)
1132		if cfg.exceptionhandling == p.OFF then
1133			p.w('ExceptionHandling="%s"', iif(_ACTION < "vs2005", "FALSE", 0))
1134		elseif cfg.exceptionhandling == "SEH" and _ACTION > "vs2003" then
1135			p.w('ExceptionHandling="2"')
1136		end
1137	end
1138
1139
1140
1141	function m.excludedFromBuild(filecfg)
1142		if not filecfg or filecfg.flags.ExcludeFromBuild then
1143			p.w('ExcludedFromBuild="true"')
1144		end
1145	end
1146
1147
1148
1149	function m.floatingPointModel(cfg)
1150		local map = { Strict = "1", Fast = "2" }
1151		local value = map[cfg.floatingpoint]
1152		if value then
1153			p.w('FloatingPointModel="%d"', value)
1154		end
1155	end
1156
1157
1158
1159	function m.forcedIncludeFiles(cfg)
1160		if #cfg.forceincludes > 0 then
1161			local includes = vstudio.path(cfg, cfg.forceincludes)
1162			p.w('ForcedIncludeFiles="%s"', table.concat(includes, ';'))
1163		end
1164		if #cfg.forceusings > 0 then
1165			local usings = vstudio.path(cfg, cfg.forceusings)
1166			p.w('ForcedUsingFiles="%s"', table.concat(usings, ';'))
1167		end
1168	end
1169
1170
1171
1172	function m.forcedIncludes(cfg)
1173		p.w('ForcedIncludes=""')
1174	end
1175
1176
1177
1178	function m.forcedUsingAssemblies(cfg)
1179		p.w('ForcedUsingAssemblies=""')
1180	end
1181
1182
1183
1184	function m.keyword(prj)
1185		local windows, managed, makefile
1186		for cfg in project.eachconfig(prj) do
1187			if cfg.system == p.WINDOWS then windows = true end
1188			if cfg.clr ~= p.OFF then managed = true end
1189			if vstudio.isMakefile(cfg) then makefile = true end
1190		end
1191
1192		if windows then
1193			local keyword = "Win32Proj"
1194			if managed then
1195				keyword = "ManagedCProj"
1196			end
1197			if makefile then
1198				keyword = "MakeFileProj"
1199			end
1200			p.w('Keyword="%s"', keyword)
1201		end
1202	end
1203
1204
1205
1206	function m.generateDebugInformation(cfg, toolset)
1207		if not toolset then
1208			p.w('GenerateDebugInformation="%s"', tostring(m.symbols(cfg) ~= 0))
1209		end
1210	end
1211
1212
1213
1214	function m.generateManifest(cfg, toolset)
1215		if cfg.flags.NoManifest or toolset then
1216			p.w('GenerateManifest="false"')
1217		end
1218	end
1219
1220
1221
1222	function m.ignoreImportLibrary(cfg, toolset)
1223		if cfg.flags.NoImportLib and not toolset then
1224			p.w('IgnoreImportLibrary="true"')
1225		end
1226	end
1227
1228
1229
1230	function m.importLibrary(cfg, toolset)
1231		if cfg.kind == p.SHAREDLIB and not toolset then
1232			local implibdir = cfg.linktarget.abspath
1233
1234			-- I can't actually stop the import lib, but I can hide it in the objects directory
1235			if cfg.flags.NoImportLib then
1236				implibdir = path.join(cfg.objdir, path.getname(implibdir))
1237			end
1238
1239			implibdir = vstudio.path(cfg, implibdir)
1240			p.x('ImportLibrary="%s"', implibdir)
1241		end
1242	end
1243
1244
1245
1246	function m.includeSearchPath(cfg)
1247		p.w('IncludeSearchPath=""')
1248	end
1249
1250
1251
1252	function m.intermediateDirectory(cfg)
1253		local objdir
1254		if not cfg.fake then
1255			objdir = vstudio.path(cfg, cfg.objdir)
1256		else
1257			objdir = "$(PlatformName)\\$(ConfigurationName)"
1258		end
1259		p.x('IntermediateDirectory="%s"', objdir)
1260	end
1261
1262
1263
1264	function m.linkIncremental(cfg, toolset)
1265		local value
1266		if not toolset then
1267			value = iif(config.canLinkIncremental(cfg) , 2, 1)
1268		else
1269			value = 0
1270		end
1271		p.w('LinkIncremental="%s"', value)
1272	end
1273
1274
1275
1276	function m.linkLibraryDependencies(cfg, toolset)
1277		if vstudio.needsExplicitLink(cfg) and not toolset then
1278			p.w('LinkLibraryDependencies="false"')
1279		end
1280	end
1281
1282
1283
1284	function m.managedExtensions(cfg)
1285		if cfg.clr ~= p.OFF then
1286			p.w('ManagedExtensions="1"')
1287		end
1288	end
1289
1290
1291
1292	function m.minimalRebuild(cfg)
1293		if config.isDebugBuild(cfg) and
1294		   cfg.debugformat ~= "c7" and
1295		   not cfg.flags.NoMinimalRebuild and
1296		   cfg.clr == p.OFF and
1297		   not cfg.flags.MultiProcessorCompile
1298		then
1299			p.w('MinimalRebuild="true"')
1300		end
1301	end
1302
1303
1304
1305	function m.moduleDefinitionFile(cfg, toolset)
1306		if not toolset then
1307			local deffile = config.findfile(cfg, ".def")
1308			if deffile then
1309				p.w('ModuleDefinitionFile="%s"', deffile)
1310			end
1311		end
1312	end
1313
1314
1315
1316	function m.objectFile(cfg)
1317		local cfg, filecfg = config.normalize(cfg)
1318		if filecfg and path.iscppfile(filecfg.name) then
1319			if filecfg.objname ~= path.getbasename(filecfg.abspath) then
1320				p.x('ObjectFile="$(IntDir)\\%s.obj"', filecfg.objname)
1321			end
1322		end
1323	end
1324
1325
1326
1327	function m.omitDefaultLib(cfg)
1328		if cfg.flags.OmitDefaultLibrary then
1329			p.w('OmitDefaultLibName="true"')
1330		end
1331	end
1332
1333
1334
1335	function m.omitFramePointers(cfg)
1336		if cfg.flags.NoFramePointer then
1337			p.w('OmitFramePointers="true"')
1338		end
1339	end
1340
1341
1342
1343	function m.optimization(cfg)
1344		local map = { Off=0, On=3, Debug=0, Full=3, Size=1, Speed=2 }
1345		local value = map[cfg.optimize]
1346		if value or not cfg.abspath then
1347			p.w('Optimization="%s"', value or 0)
1348		end
1349	end
1350
1351
1352
1353	function m.optimizeReferences(cfg, toolset)
1354		if config.isOptimizedBuild(cfg) and not toolset then
1355			p.w('OptimizeReferences="2"')
1356		end
1357	end
1358
1359
1360
1361	function m.output(cfg)
1362		p.w('Output="$(OutDir)%s"', cfg.buildtarget.name)
1363	end
1364
1365
1366
1367	function m.outputDirectory(cfg)
1368		local outdir = project.getrelative(cfg.project, cfg.buildtarget.directory)
1369		p.x('OutputDirectory="%s"', path.translate(outdir))
1370	end
1371
1372
1373
1374	function m.outputFile(cfg)
1375		p.x('OutputFile="$(OutDir)\\%s"', cfg.buildtarget.name)
1376	end
1377
1378
1379
1380	function m.outputFileName(cfg)
1381		if cfg.imagepath ~= nil then
1382			p.x('OutputFileName="%s"', path.translate(cfg.imagepath))
1383		end
1384	end
1385
1386
1387
1388	function m.platforms(prj)
1389		architectures = {}
1390		for cfg in project.eachconfig(prj) do
1391			local arch = vstudio.archFromConfig(cfg, true)
1392			if not table.contains(architectures, arch) then
1393				table.insert(architectures, arch)
1394			end
1395		end
1396
1397		p.push('<Platforms>')
1398		table.foreachi(architectures, function(arch)
1399			p.push('<Platform')
1400			p.w('Name="%s"', arch)
1401			p.pop('/>')
1402		end)
1403		p.pop('</Platforms>')
1404	end
1405
1406
1407
1408	function m.preprocessorDefinitions(cfg)
1409		if #cfg.defines > 0 or vstudio.isMakefile(cfg) then
1410			p.x('PreprocessorDefinitions="%s"', table.concat(cfg.defines, ";"))
1411		end
1412	end
1413
1414
1415	function m.undefinePreprocessorDefinitions(cfg)
1416		if #cfg.undefines > 0 then
1417			p.x('UndefinePreprocessorDefinitions="%s"', table.concat(cfg.undefines, ";"))
1418		end
1419	end
1420
1421
1422	function m.programDatabaseFile(cfg, toolset)
1423		if toolset then
1424			p.w('ProgramDatabaseFile=""')
1425		end
1426	end
1427
1428
1429	function m.programDataBaseFileName(cfg, toolset)
1430		if toolset then
1431			p.w('ProgramDataBaseFileName=""')
1432		end
1433	end
1434
1435
1436
1437	function m.projectGUID(prj)
1438		p.w('ProjectGUID="{%s}"', prj.uuid)
1439	end
1440
1441
1442
1443	function m.projectName(prj)
1444		p.x('Name="%s"', prj.name)
1445	end
1446
1447
1448
1449	function m.projectReferences(prj)
1450		local deps = project.getdependencies(prj)
1451		if #deps > 0 then
1452			-- This is a little odd: Visual Studio wants the "relative path to project"
1453			-- to be relative to the *workspace*, rather than the project doing the
1454			-- referencing. Which, in theory, would break if the project is included
1455			-- in more than one workspace. But that's how they do it.
1456
1457			for i, dep in ipairs(deps) do
1458				local relpath = vstudio.path(prj.workspace, vstudio.projectfile(dep))
1459
1460				-- Visual Studio wants the path to start with ./ or ../
1461				if not relpath:startswith(".") then
1462					relpath = ".\\" .. relpath
1463				end
1464
1465				p.push('<ProjectReference')
1466				p.w('ReferencedProjectIdentifier="{%s}"', dep.uuid)
1467				p.w('RelativePathToProject="%s"', relpath)
1468				p.pop('/>')
1469			end
1470		end
1471	end
1472
1473
1474
1475	function m.projectType(prj)
1476		p.w('ProjectType="Visual C++"')
1477	end
1478
1479
1480
1481	function m.reBuildCommandLine(cfg)
1482		commands = table.concat(cfg.rebuildcommands, "\r\n")
1483		p.x('ReBuildCommandLine="%s"', commands)
1484	end
1485
1486
1487
1488	function m.resourcePreprocessorDefinitions(cfg)
1489		local defs = table.join(cfg.defines, cfg.resdefines)
1490		if #defs > 0 then
1491			p.x('PreprocessorDefinitions="%s"', table.concat(defs, ";"))
1492		end
1493	end
1494
1495
1496
1497	function m.rootNamespace(prj)
1498		local hasWindows = project.hasConfig(prj, function(cfg)
1499			return cfg.system == p.WINDOWS
1500		end)
1501
1502		-- Technically, this should be skipped for pure makefile projects that
1503		-- do not contain any empty configurations. But I need to figure out a
1504		-- a good way to check the empty configuration bit first.
1505
1506		if hasWindows and _ACTION > "vs2003" then
1507			p.x('RootNamespace="%s"', prj.name)
1508		end
1509	end
1510
1511
1512
1513	function m.runtimeLibrary(cfg)
1514		local cfg, filecfg = config.normalize(cfg)
1515		if not filecfg then
1516			local runtimes = {
1517				StaticRelease = 0,
1518				StaticDebug = 1,
1519				SharedRelease = 2,
1520				SharedDebug = 3,
1521			}
1522			p.w('RuntimeLibrary="%s"', runtimes[config.getruntime(cfg)])
1523		end
1524	end
1525
1526
1527
1528	function m.runtimeTypeInfo(cfg)
1529		if cfg.rtti == p.OFF and cfg.clr == p.OFF then
1530			p.w('RuntimeTypeInfo="false"')
1531		elseif cfg.rtti == p.ON then
1532			p.w('RuntimeTypeInfo="true"')
1533		end
1534	end
1535
1536
1537	function m.stringPooling(cfg)
1538		if config.isOptimizedBuild(cfg) then
1539			p.w('StringPooling="true"')
1540		end
1541	end
1542
1543
1544	function m.subSystem(cfg, toolset)
1545		if not toolset then
1546			p.w('SubSystem="%s"', iif(cfg.kind == "ConsoleApp", 1, 2))
1547		end
1548	end
1549
1550
1551	function m.targetEnvironment(cfg)
1552		if cfg.architecture == "x86_64" then
1553			p.w('TargetEnvironment="3"')
1554		end
1555	end
1556
1557
1558	function m.targetFrameworkVersion(prj)
1559		local windows, makefile
1560		for cfg in project.eachconfig(prj) do
1561			if cfg.system == p.WINDOWS then windows = true end
1562			if vstudio.isMakefile(cfg) then makefile = true end
1563		end
1564
1565		local version = 0
1566		if makefile or not windows then
1567			version = 196613
1568		end
1569		p.w('TargetFrameworkVersion="%d"', version)
1570	end
1571
1572
1573	function m.targetMachine(cfg, toolset)
1574		if not toolset then
1575			p.w('TargetMachine="%d"', iif(cfg.architecture == "x86_64", 17, 1))
1576		end
1577	end
1578
1579
1580	function m.toolFiles(prj)
1581		if _ACTION > "vs2003" then
1582			p.w('<ToolFiles>')
1583			p.w('</ToolFiles>')
1584		end
1585	end
1586
1587
1588	function m.treatWChar_tAsBuiltInType(cfg)
1589		local map = { On = "true", Off = "false" }
1590		local value = map[cfg.nativewchar]
1591		if value then
1592			p.w('TreatWChar_tAsBuiltInType="%s"', value)
1593		end
1594	end
1595
1596
1597	function m.useOfMFC(cfg)
1598		if (cfg.flags.MFC) then
1599			p.w('UseOfMFC="%d"', iif(cfg.flags.StaticRuntime, 1, 2))
1600		end
1601	end
1602
1603
1604	function m.usePrecompiledHeader(cfg)
1605		local prj, file = config.normalize(cfg)
1606		if file then
1607			if prj.pchsource == file.abspath and
1608			   not prj.flags.NoPCH and
1609			   prj.system == p.WINDOWS
1610			then
1611				p.w('UsePrecompiledHeader="1"')
1612			elseif file.flags.NoPCH then
1613				p.w('UsePrecompiledHeader="0"')
1614			end
1615		else
1616			if not prj.flags.NoPCH and prj.pchheader then
1617				p.w('UsePrecompiledHeader="%s"', iif(_ACTION < "vs2005", 3, 2))
1618				p.x('PrecompiledHeaderThrough="%s"', prj.pchheader)
1619			else
1620				p.w('UsePrecompiledHeader="%s"', iif(_ACTION > "vs2003" or prj.flags.NoPCH, 0, 2))
1621			end
1622		end
1623	end
1624
1625
1626
1627	function m.version(prj)
1628		local map = {
1629			vs2002 = '7.0',
1630			vs2003 = '7.1',
1631			vs2005 = '8.0',
1632			vs2008 = '9.0'
1633		}
1634		p.w('Version="%s0"', map[_ACTION])
1635	end
1636
1637
1638
1639	function m.warnAsError(cfg)
1640		if cfg.flags.FatalCompileWarnings and cfg.warnings ~= p.OFF then
1641			p.w('WarnAsError="true"')
1642		end
1643	end
1644
1645
1646
1647	function m.warningLevel(cfg)
1648		local prjcfg, filecfg = config.normalize(cfg)
1649
1650		local level
1651		if cfg.warnings == p.OFF then
1652			level = "0"
1653		elseif cfg.warnings == "Extra" then
1654			level = "4"
1655		elseif not filecfg then
1656			level = "3"
1657		end
1658
1659		if level then
1660			p.w('WarningLevel="%s"', level)
1661		end
1662	end
1663
1664
1665
1666	function m.wholeProgramOptimization(cfg)
1667		if cfg.flags.LinkTimeOptimization then
1668			p.x('WholeProgramOptimization="true"')
1669		end
1670	end
1671
1672
1673	function m.xmlElement()
1674		p.w('<?xml version="1.0" encoding="Windows-1252"?>')
1675	end
1676