1package MSBuildProject;
2
3#
4# Package that encapsulates a MSBuild project file (Visual C++ 2010 or greater)
5#
6# src/tools/msvc/MSBuildProject.pm
7#
8
9use Carp;
10use strict;
11use warnings;
12use base qw(Project);
13
14sub _new
15{
16	my $classname = shift;
17	my $self      = $classname->SUPER::_new(@_);
18	bless($self, $classname);
19
20	$self->{filenameExtension} = '.vcxproj';
21	$self->{ToolsVersion}      = '4.0';
22
23	return $self;
24}
25
26sub WriteHeader
27{
28	my ($self, $f) = @_;
29
30	print $f <<EOF;
31<?xml version="1.0" encoding="Windows-1252"?>
32<Project DefaultTargets="Build" ToolsVersion="$self->{ToolsVersion}" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
33  <ItemGroup Label="ProjectConfigurations">
34EOF
35	$self->WriteConfigurationHeader($f, 'Debug');
36	$self->WriteConfigurationHeader($f, 'Release');
37	print $f <<EOF;
38  </ItemGroup>
39  <PropertyGroup Label="Globals">
40    <ProjectGuid>$self->{guid}</ProjectGuid>
41EOF
42	# Check whether WindowsSDKVersion env variable is present.
43	# Add WindowsTargetPlatformVersion node if so.
44	my $sdkVersion = $ENV{'WindowsSDKVersion'};
45	if (defined($sdkVersion))
46	{
47		# remove trailing backslash if necessary.
48		$sdkVersion =~ s/\\$//;
49		print $f <<EOF
50    <WindowsTargetPlatformVersion>$sdkVersion</WindowsTargetPlatformVersion>
51EOF
52	}
53	print $f <<EOF;
54  </PropertyGroup>
55  <Import Project="\$(VCTargetsPath)\\Microsoft.Cpp.Default.props" />
56EOF
57	$self->WriteConfigurationPropertyGroup($f, 'Release',
58		{ wholeopt => 'false' });
59	$self->WriteConfigurationPropertyGroup($f, 'Debug',
60		{ wholeopt => 'false' });
61	print $f <<EOF;
62  <Import Project="\$(VCTargetsPath)\\Microsoft.Cpp.props" />
63  <ImportGroup Label="ExtensionSettings">
64  </ImportGroup>
65EOF
66	$self->WritePropertySheetsPropertyGroup($f, 'Release');
67	$self->WritePropertySheetsPropertyGroup($f, 'Debug');
68	print $f <<EOF;
69  <PropertyGroup Label="UserMacros" />
70  <PropertyGroup>
71    <_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion>
72EOF
73	$self->WriteAdditionalProperties($f, 'Debug');
74	$self->WriteAdditionalProperties($f, 'Release');
75	print $f <<EOF;
76  </PropertyGroup>
77EOF
78
79	$self->WriteItemDefinitionGroup(
80		$f, 'Debug',
81		{   defs    => "_DEBUG;DEBUG=1",
82			opt     => 'Disabled',
83			strpool => 'false',
84			runtime => 'MultiThreadedDebugDLL' });
85	$self->WriteItemDefinitionGroup(
86		$f,
87		'Release',
88		{   defs    => "",
89			opt     => 'Full',
90			strpool => 'true',
91			runtime => 'MultiThreadedDLL' });
92}
93
94sub AddDefine
95{
96	my ($self, $def) = @_;
97
98	$self->{defines} .= $def . ';';
99}
100
101sub WriteReferences
102{
103	my ($self, $f) = @_;
104
105	my @references = @{ $self->{references} };
106
107	if (scalar(@references))
108	{
109		print $f <<EOF;
110  <ItemGroup>
111EOF
112		foreach my $ref (@references)
113		{
114			print $f <<EOF;
115    <ProjectReference Include="$ref->{name}$ref->{filenameExtension}">
116      <Project>$ref->{guid}</Project>
117    </ProjectReference>
118EOF
119		}
120		print $f <<EOF;
121  </ItemGroup>
122EOF
123	}
124}
125
126sub WriteFiles
127{
128	my ($self, $f) = @_;
129	print $f <<EOF;
130  <ItemGroup>
131EOF
132	my @grammarFiles  = ();
133	my @resourceFiles = ();
134	my %uniquefiles;
135	foreach my $fileNameWithPath (sort keys %{ $self->{files} })
136	{
137		confess "Bad format filename '$fileNameWithPath'\n"
138		  unless ($fileNameWithPath =~ m!^(.*)/([^/]+)\.(c|cpp|y|l|rc)$!);
139		my $dir      = $1;
140		my $fileName = $2;
141		if ($fileNameWithPath =~ /\.y$/ or $fileNameWithPath =~ /\.l$/)
142		{
143			push @grammarFiles, $fileNameWithPath;
144		}
145		elsif ($fileNameWithPath =~ /\.rc$/)
146		{
147			push @resourceFiles, $fileNameWithPath;
148		}
149		elsif (defined($uniquefiles{$fileName}))
150		{
151
152			# File already exists, so fake a new name
153			my $obj = $dir;
154			$obj =~ s!/!_!g;
155
156			print $f <<EOF;
157    <ClCompile Include="$fileNameWithPath">
158      <ObjectFileName Condition="'\$(Configuration)|\$(Platform)'=='Debug|$self->{platform}'">.\\debug\\$self->{name}\\${obj}_$fileName.obj</ObjectFileName>
159      <ObjectFileName Condition="'\$(Configuration)|\$(Platform)'=='Release|$self->{platform}'">.\\release\\$self->{name}\\${obj}_$fileName.obj</ObjectFileName>
160    </ClCompile>
161EOF
162		}
163		else
164		{
165			$uniquefiles{$fileName} = 1;
166			print $f <<EOF;
167    <ClCompile Include="$fileNameWithPath" />
168EOF
169		}
170
171	}
172	print $f <<EOF;
173  </ItemGroup>
174EOF
175	if (scalar(@grammarFiles))
176	{
177		print $f <<EOF;
178  <ItemGroup>
179EOF
180		foreach my $grammarFile (@grammarFiles)
181		{
182			(my $outputFile = $grammarFile) =~ s/\.(y|l)$/.c/;
183			if ($grammarFile =~ /\.y$/)
184			{
185				$outputFile =~
186s{^src\\pl\\plpgsql\\src\\gram.c$}{src\\pl\\plpgsql\\src\\pl_gram.c};
187				print $f <<EOF;
188    <CustomBuild Include="$grammarFile">
189      <Message Condition="'\$(Configuration)|\$(Platform)'=='Debug|$self->{platform}'">Running bison on $grammarFile</Message>
190      <Command Condition="'\$(Configuration)|\$(Platform)'=='Debug|$self->{platform}'">perl "src\\tools\\msvc\\pgbison.pl" "$grammarFile"</Command>
191      <AdditionalInputs Condition="'\$(Configuration)|\$(Platform)'=='Debug|$self->{platform}'">%(AdditionalInputs)</AdditionalInputs>
192      <Outputs Condition="'\$(Configuration)|\$(Platform)'=='Debug|$self->{platform}'">$outputFile;%(Outputs)</Outputs>
193      <Message Condition="'\$(Configuration)|\$(Platform)'=='Release|$self->{platform}'">Running bison on $grammarFile</Message>
194      <Command Condition="'\$(Configuration)|\$(Platform)'=='Release|$self->{platform}'">perl "src\\tools\\msvc\\pgbison.pl" "$grammarFile"</Command>
195      <AdditionalInputs Condition="'\$(Configuration)|\$(Platform)'=='Release|$self->{platform}'">%(AdditionalInputs)</AdditionalInputs>
196      <Outputs Condition="'\$(Configuration)|\$(Platform)'=='Release|$self->{platform}'">$outputFile;%(Outputs)</Outputs>
197    </CustomBuild>
198EOF
199			}
200			else    #if ($grammarFile =~ /\.l$/)
201			{
202				print $f <<EOF;
203    <CustomBuild Include="$grammarFile">
204      <Message Condition="'\$(Configuration)|\$(Platform)'=='Debug|$self->{platform}'">Running flex on $grammarFile</Message>
205      <Command Condition="'\$(Configuration)|\$(Platform)'=='Debug|$self->{platform}'">perl "src\\tools\\msvc\\pgflex.pl" "$grammarFile"</Command>
206      <AdditionalInputs Condition="'\$(Configuration)|\$(Platform)'=='Debug|$self->{platform}'">%(AdditionalInputs)</AdditionalInputs>
207      <Outputs Condition="'\$(Configuration)|\$(Platform)'=='Debug|$self->{platform}'">$outputFile;%(Outputs)</Outputs>
208      <Message Condition="'\$(Configuration)|\$(Platform)'=='Release|$self->{platform}'">Running flex on $grammarFile</Message>
209      <Command Condition="'\$(Configuration)|\$(Platform)'=='Release|$self->{platform}'">perl "src\\tools\\msvc\\pgflex.pl" "$grammarFile"</Command>
210      <AdditionalInputs Condition="'\$(Configuration)|\$(Platform)'=='Release|$self->{platform}'">%(AdditionalInputs)</AdditionalInputs>
211      <Outputs Condition="'\$(Configuration)|\$(Platform)'=='Release|$self->{platform}'">$outputFile;%(Outputs)</Outputs>
212    </CustomBuild>
213EOF
214			}
215		}
216		print $f <<EOF;
217  </ItemGroup>
218EOF
219	}
220	if (scalar(@resourceFiles))
221	{
222		print $f <<EOF;
223  <ItemGroup>
224EOF
225		foreach my $rcFile (@resourceFiles)
226		{
227			print $f <<EOF;
228    <ResourceCompile Include="$rcFile" />
229EOF
230		}
231		print $f <<EOF;
232  </ItemGroup>
233EOF
234	}
235}
236
237sub WriteConfigurationHeader
238{
239	my ($self, $f, $cfgname) = @_;
240	print $f <<EOF;
241    <ProjectConfiguration Include="$cfgname|$self->{platform}">
242      <Configuration>$cfgname</Configuration>
243      <Platform>$self->{platform}</Platform>
244    </ProjectConfiguration>
245EOF
246}
247
248sub WriteConfigurationPropertyGroup
249{
250	my ($self, $f, $cfgname, $p) = @_;
251	my $cfgtype =
252	  ($self->{type} eq "exe")
253	  ? 'Application'
254	  : ($self->{type} eq "dll" ? 'DynamicLibrary' : 'StaticLibrary');
255
256	print $f <<EOF;
257  <PropertyGroup Condition="'\$(Configuration)|\$(Platform)'=='$cfgname|$self->{platform}'" Label="Configuration">
258    <ConfigurationType>$cfgtype</ConfigurationType>
259    <UseOfMfc>false</UseOfMfc>
260    <CharacterSet>MultiByte</CharacterSet>
261    <WholeProgramOptimization>$p->{wholeopt}</WholeProgramOptimization>
262  </PropertyGroup>
263EOF
264}
265
266sub WritePropertySheetsPropertyGroup
267{
268	my ($self, $f, $cfgname) = @_;
269	print $f <<EOF;
270  <ImportGroup Condition="'\$(Configuration)|\$(Platform)'=='$cfgname|$self->{platform}'" Label="PropertySheets">
271    <Import Project="\$(UserRootDir)\\Microsoft.Cpp.\$(Platform).user.props" Condition="exists('\$(UserRootDir)\\Microsoft.Cpp.\$(Platform).user.props')" Label="LocalAppDataPlatform" />
272  </ImportGroup>
273EOF
274}
275
276sub WriteAdditionalProperties
277{
278	my ($self, $f, $cfgname) = @_;
279	print $f <<EOF;
280    <OutDir Condition="'\$(Configuration)|\$(Platform)'=='$cfgname|$self->{platform}'">.\\$cfgname\\$self->{name}\\</OutDir>
281    <IntDir Condition="'\$(Configuration)|\$(Platform)'=='$cfgname|$self->{platform}'">.\\$cfgname\\$self->{name}\\</IntDir>
282    <LinkIncremental Condition="'\$(Configuration)|\$(Platform)'=='$cfgname|$self->{platform}'">false</LinkIncremental>
283EOF
284}
285
286sub WriteItemDefinitionGroup
287{
288	my ($self, $f, $cfgname, $p) = @_;
289	my $cfgtype =
290	  ($self->{type} eq "exe")
291	  ? 'Application'
292	  : ($self->{type} eq "dll" ? 'DynamicLibrary' : 'StaticLibrary');
293	my $libs = $self->GetAdditionalLinkerDependencies($cfgname, ';');
294
295	my $targetmachine =
296	  $self->{platform} eq 'Win32' ? 'MachineX86' : 'MachineX64';
297
298	my $includes = $self->{includes};
299	unless ($includes eq '' or $includes =~ /;$/)
300	{
301		$includes .= ';';
302	}
303	print $f <<EOF;
304  <ItemDefinitionGroup Condition="'\$(Configuration)|\$(Platform)'=='$cfgname|$self->{platform}'">
305    <ClCompile>
306      <Optimization>$p->{opt}</Optimization>
307      <AdditionalIncludeDirectories>$self->{prefixincludes}src/include;src/include/port/win32;src/include/port/win32_msvc;$includes\%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
308      <PreprocessorDefinitions>WIN32;_WINDOWS;__WINDOWS__;__WIN32__;EXEC_BACKEND;WIN32_STACK_RLIMIT=4194304;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE$self->{defines}$p->{defs}\%(PreprocessorDefinitions)</PreprocessorDefinitions>
309      <StringPooling>$p->{strpool}</StringPooling>
310      <RuntimeLibrary>$p->{runtime}</RuntimeLibrary>
311      <DisableSpecificWarnings>$self->{disablewarnings};\%(DisableSpecificWarnings)</DisableSpecificWarnings>
312      <AdditionalOptions>/MP \%(AdditionalOptions)</AdditionalOptions>
313      <AssemblerOutput>
314      </AssemblerOutput>
315      <AssemblerListingLocation>.\\$cfgname\\$self->{name}\\</AssemblerListingLocation>
316      <ObjectFileName>.\\$cfgname\\$self->{name}\\</ObjectFileName>
317      <ProgramDataBaseFileName>.\\$cfgname\\$self->{name}\\</ProgramDataBaseFileName>
318      <BrowseInformation>false</BrowseInformation>
319      <WarningLevel>Level3</WarningLevel>
320      <SuppressStartupBanner>true</SuppressStartupBanner>
321      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
322      <CompileAs>Default</CompileAs>
323    </ClCompile>
324    <Link>
325      <OutputFile>.\\$cfgname\\$self->{name}\\$self->{name}.$self->{type}</OutputFile>
326      <AdditionalDependencies>$libs;\%(AdditionalDependencies)</AdditionalDependencies>
327      <SuppressStartupBanner>true</SuppressStartupBanner>
328      <AdditionalLibraryDirectories>\%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
329      <IgnoreSpecificDefaultLibraries>libc;\%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
330      <StackReserveSize>4194304</StackReserveSize>
331      <GenerateDebugInformation>true</GenerateDebugInformation>
332      <ProgramDatabaseFile>.\\$cfgname\\$self->{name}\\$self->{name}.pdb</ProgramDatabaseFile>
333      <GenerateMapFile>false</GenerateMapFile>
334      <MapFileName>.\\$cfgname\\$self->{name}\\$self->{name}.map</MapFileName>
335      <RandomizedBaseAddress>false</RandomizedBaseAddress>
336      <!-- Permit links to MinGW-built, 32-bit DLLs (default before VS2012). -->
337      <ImageHasSafeExceptionHandlers/>
338      <SubSystem>Console</SubSystem>
339      <TargetMachine>$targetmachine</TargetMachine>
340EOF
341	if ($self->{disablelinkerwarnings})
342	{
343		print $f
344"      <AdditionalOptions>/ignore:$self->{disablelinkerwarnings} \%(AdditionalOptions)</AdditionalOptions>\n";
345	}
346	if ($self->{implib})
347	{
348		my $l = $self->{implib};
349		$l =~ s/__CFGNAME__/$cfgname/g;
350		print $f "      <ImportLibrary>$l</ImportLibrary>\n";
351	}
352	if ($self->{def})
353	{
354		my $d = $self->{def};
355		$d =~ s/__CFGNAME__/$cfgname/g;
356		print $f "      <ModuleDefinitionFile>$d</ModuleDefinitionFile>\n";
357	}
358	print $f <<EOF;
359    </Link>
360    <ResourceCompile>
361      <AdditionalIncludeDirectories>src\\include;\%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
362    </ResourceCompile>
363EOF
364	if ($self->{builddef})
365	{
366		print $f <<EOF;
367    <PreLinkEvent>
368      <Message>Generate DEF file</Message>
369      <Command>perl src\\tools\\msvc\\gendef.pl $cfgname\\$self->{name} $self->{platform}</Command>
370    </PreLinkEvent>
371EOF
372	}
373	print $f <<EOF;
374  </ItemDefinitionGroup>
375EOF
376}
377
378sub Footer
379{
380	my ($self, $f) = @_;
381	$self->WriteReferences($f);
382
383	print $f <<EOF;
384  <Import Project="\$(VCTargetsPath)\\Microsoft.Cpp.targets" />
385  <ImportGroup Label="ExtensionTargets">
386  </ImportGroup>
387</Project>
388EOF
389}
390
391package VC2010Project;
392
393#
394# Package that encapsulates a Visual C++ 2010 project file
395#
396
397use strict;
398use warnings;
399use base qw(MSBuildProject);
400
401sub new
402{
403	my $classname = shift;
404	my $self      = $classname->SUPER::_new(@_);
405	bless($self, $classname);
406
407	$self->{vcver} = '10.00';
408
409	return $self;
410}
411
412package VC2012Project;
413
414#
415# Package that encapsulates a Visual C++ 2012 project file
416#
417
418use strict;
419use warnings;
420use base qw(MSBuildProject);
421
422sub new
423{
424	my $classname = shift;
425	my $self      = $classname->SUPER::_new(@_);
426	bless($self, $classname);
427
428	$self->{vcver}           = '11.00';
429	$self->{PlatformToolset} = 'v110';
430
431	return $self;
432}
433
434# This override adds the <PlatformToolset> element
435# to the PropertyGroup labeled "Configuration"
436sub WriteConfigurationPropertyGroup
437{
438	my ($self, $f, $cfgname, $p) = @_;
439	my $cfgtype =
440	  ($self->{type} eq "exe")
441	  ? 'Application'
442	  : ($self->{type} eq "dll" ? 'DynamicLibrary' : 'StaticLibrary');
443
444	print $f <<EOF;
445  <PropertyGroup Condition="'\$(Configuration)|\$(Platform)'=='$cfgname|$self->{platform}'" Label="Configuration">
446    <ConfigurationType>$cfgtype</ConfigurationType>
447    <UseOfMfc>false</UseOfMfc>
448    <CharacterSet>MultiByte</CharacterSet>
449    <WholeProgramOptimization>$p->{wholeopt}</WholeProgramOptimization>
450    <PlatformToolset>$self->{PlatformToolset}</PlatformToolset>
451  </PropertyGroup>
452EOF
453}
454
455package VC2013Project;
456
457#
458# Package that encapsulates a Visual C++ 2013 project file
459#
460
461use strict;
462use warnings;
463use base qw(VC2012Project);
464
465sub new
466{
467	my $classname = shift;
468	my $self      = $classname->SUPER::_new(@_);
469	bless($self, $classname);
470
471	$self->{vcver}           = '12.00';
472	$self->{PlatformToolset} = 'v120';
473	$self->{ToolsVersion}    = '12.0';
474
475	return $self;
476}
477
478package VC2015Project;
479
480#
481# Package that encapsulates a Visual C++ 2015 project file
482#
483
484use strict;
485use warnings;
486use base qw(VC2012Project);
487
488sub new
489{
490	my $classname = shift;
491	my $self      = $classname->SUPER::_new(@_);
492	bless($self, $classname);
493
494	$self->{vcver}           = '14.00';
495	$self->{PlatformToolset} = 'v140';
496	$self->{ToolsVersion}    = '14.0';
497
498	return $self;
499}
500
501package VC2017Project;
502
503#
504# Package that encapsulates a Visual C++ 2017 project file
505#
506
507use strict;
508use warnings;
509use base qw(VC2012Project);
510
511sub new
512{
513	my $classname = shift;
514	my $self      = $classname->SUPER::_new(@_);
515	bless($self, $classname);
516
517	$self->{vcver}           = '15.00';
518	$self->{PlatformToolset} = 'v141';
519	$self->{ToolsVersion}    = '15.0';
520
521	return $self;
522}
523
524package VC2019Project;
525
526#
527# Package that encapsulates a Visual C++ 2019 project file
528#
529
530use strict;
531use warnings;
532use base qw(VC2012Project);
533
534no warnings qw(redefine);    ## no critic
535
536sub new
537{
538	my $classname = shift;
539	my $self      = $classname->SUPER::_new(@_);
540	bless($self, $classname);
541
542	$self->{vcver}           = '16.00';
543	$self->{PlatformToolset} = 'v142';
544	$self->{ToolsVersion}    = '16.0';
545
546	return $self;
547}
548
5491;
550