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