1package Solution;
2
3#
4# Package that encapsulates a Visual C++ solution file generation
5#
6# src/tools/msvc/Solution.pm
7#
8use Carp;
9use strict;
10use warnings;
11use VSObjectFactory;
12
13sub _new
14{
15	my $classname = shift;
16	my $options   = shift;
17	my $self      = {
18		projects                   => {},
19		options                    => $options,
20		numver                     => '',
21		strver                     => '',
22		VisualStudioVersion        => undef,
23		MinimumVisualStudioVersion => undef,
24		vcver                      => undef,
25		platform                   => undef, };
26	bless($self, $classname);
27
28	$self->DeterminePlatform();
29	my $bits = $self->{platform} eq 'Win32' ? 32 : 64;
30
31	$options->{float4byval} = 1
32	  unless exists $options->{float4byval};
33	$options->{float8byval} = ($bits == 64)
34	  unless exists $options->{float8byval};
35	die "float8byval not permitted on 32 bit platforms"
36	  if $options->{float8byval} && $bits == 32;
37	if ($options->{xslt} && !$options->{xml})
38	{
39		die "XSLT requires XML\n";
40	}
41	$options->{blocksize} = 8
42	  unless $options->{blocksize};    # undef or 0 means default
43	die "Bad blocksize $options->{blocksize}"
44	  unless grep { $_ == $options->{blocksize} } (1, 2, 4, 8, 16, 32);
45	$options->{segsize} = 1
46	  unless $options->{segsize};      # undef or 0 means default
47	 # only allow segsize 1 for now, as we can't do large files yet in windows
48	die "Bad segsize $options->{segsize}"
49	  unless $options->{segsize} == 1;
50	$options->{wal_blocksize} = 8
51	  unless $options->{wal_blocksize};    # undef or 0 means default
52	die "Bad wal_blocksize $options->{wal_blocksize}"
53	  unless grep { $_ == $options->{wal_blocksize} }
54		  (1, 2, 4, 8, 16, 32, 64);
55	$options->{wal_segsize} = 16
56	  unless $options->{wal_segsize};      # undef or 0 means default
57	die "Bad wal_segsize $options->{wal_segsize}"
58	  unless grep { $_ == $options->{wal_segsize} } (1, 2, 4, 8, 16, 32, 64);
59
60	return $self;
61}
62
63sub GetAdditionalHeaders
64{
65	return '';
66}
67
68sub DeterminePlatform
69{
70	my $self = shift;
71
72	# Examine CL help output to determine if we are in 32 or 64-bit mode.
73	my $output = `cl /? 2>&1`;
74	$? >> 8 == 0 or die "cl command not found";
75	$self->{platform} = ($output =~ /^\/favor:<.+AMD64/m) ? 'x64' : 'Win32';
76	print "Detected hardware platform: $self->{platform}\n";
77}
78
79# Return 1 if $oldfile is newer than $newfile, or if $newfile doesn't exist.
80# Special case - if config.pl has changed, always return 1
81sub IsNewer
82{
83	my ($newfile, $oldfile) = @_;
84	-e $oldfile or warn "source file \"$oldfile\" does not exist";
85	if (   $oldfile ne 'src/tools/msvc/config.pl'
86		&& $oldfile ne 'src/tools/msvc/config_default.pl')
87	{
88		return 1
89		  if (-f 'src/tools/msvc/config.pl')
90		  && IsNewer($newfile, 'src/tools/msvc/config.pl');
91		return 1
92		  if (-f 'src/tools/msvc/config_default.pl')
93		  && IsNewer($newfile, 'src/tools/msvc/config_default.pl');
94	}
95	return 1 if (!(-e $newfile));
96	my @nstat = stat($newfile);
97	my @ostat = stat($oldfile);
98	return 1 if ($nstat[9] < $ostat[9]);
99	return 0;
100}
101
102# Copy a file, *not* preserving date. Only works for text files.
103sub copyFile
104{
105	my ($src, $dest) = @_;
106	open(my $i, '<', $src)  || croak "Could not open $src";
107	open(my $o, '>', $dest) || croak "Could not open $dest";
108	while (<$i>)
109	{
110		print $o $_;
111	}
112	close($i);
113	close($o);
114}
115
116# Fetch version of OpenSSL based on a parsing of the command shipped with
117# the installer this build is linking to.  This returns as result an array
118# made of the three first digits of the OpenSSL version, which is enough
119# to decide which options to apply depending on the version of OpenSSL
120# linking with.
121sub GetOpenSSLVersion
122{
123	my $self = shift;
124
125	# Attempt to get OpenSSL version and location.  This assumes that
126	# openssl.exe is in the specified directory.
127	# Quote the .exe name in case it has spaces
128	my $opensslcmd =
129	  qq("$self->{options}->{openssl}\\bin\\openssl.exe" version 2>&1);
130	my $sslout = `$opensslcmd`;
131
132	$? >> 8 == 0
133	  or croak
134	  "Unable to determine OpenSSL version: The openssl.exe command wasn't found.";
135
136	if ($sslout =~ /(\d+)\.(\d+)\.(\d+)(\D)/m)
137	{
138		return ($1, $2, $3);
139	}
140
141	croak
142	  "Unable to determine OpenSSL version: The openssl.exe version could not be determined.";
143}
144
145sub GenerateFiles
146{
147	my $self = shift;
148	my $bits = $self->{platform} eq 'Win32' ? 32 : 64;
149
150	# Parse configure.in to get version numbers
151	open(my $c, '<', "configure.in")
152	  || confess("Could not open configure.in for reading\n");
153	while (<$c>)
154	{
155		if (/^AC_INIT\(\[PostgreSQL\], \[([^\]]+)\]/)
156		{
157			$self->{strver} = $1;
158			if ($self->{strver} !~ /^(\d+)(?:\.(\d+))?/)
159			{
160				confess "Bad format of version: $self->{strver}\n";
161			}
162			$self->{numver} = sprintf("%d%04d", $1, $2 ? $2 : 0);
163			$self->{majorver} = sprintf("%d", $1);
164		}
165	}
166	close($c);
167	confess "Unable to parse configure.in for all variables!"
168	  if ($self->{strver} eq '' || $self->{numver} eq '');
169
170	if (IsNewer("src/include/pg_config_os.h", "src/include/port/win32.h"))
171	{
172		print "Copying pg_config_os.h...\n";
173		copyFile("src/include/port/win32.h", "src/include/pg_config_os.h");
174	}
175
176	if (IsNewer("src/include/pg_config.h", "src/include/pg_config.h.win32"))
177	{
178		print "Generating pg_config.h...\n";
179		open(my $i, '<', "src/include/pg_config.h.win32")
180		  || confess "Could not open pg_config.h.win32\n";
181		open(my $o, '>', "src/include/pg_config.h")
182		  || confess "Could not write to pg_config.h\n";
183		my $extraver = $self->{options}->{extraver};
184		$extraver = '' unless defined $extraver;
185		while (<$i>)
186		{
187			s{PG_VERSION "[^"]+"}{PG_VERSION "$self->{strver}$extraver"};
188			s{PG_VERSION_NUM \d+}{PG_VERSION_NUM $self->{numver}};
189s{PG_VERSION_STR "[^"]+"}{PG_VERSION_STR "PostgreSQL $self->{strver}$extraver, compiled by Visual C++ build " CppAsString2(_MSC_VER) ", $bits-bit"};
190			print $o $_;
191		}
192		print $o "#define PG_MAJORVERSION \"$self->{majorver}\"\n";
193		print $o "#define LOCALEDIR \"/share/locale\"\n"
194		  if ($self->{options}->{nls});
195		print $o "/* defines added by config steps */\n";
196		print $o "#ifndef IGNORE_CONFIGURED_SETTINGS\n";
197		print $o "#define USE_ASSERT_CHECKING 1\n"
198		  if ($self->{options}->{asserts});
199		print $o "#define USE_LDAP 1\n"   if ($self->{options}->{ldap});
200		print $o "#define HAVE_LIBZ 1\n"  if ($self->{options}->{zlib});
201		print $o "#define ENABLE_NLS 1\n" if ($self->{options}->{nls});
202
203		print $o "#define BLCKSZ ", 1024 * $self->{options}->{blocksize},
204		  "\n";
205		print $o "#define RELSEG_SIZE ",
206		  (1024 / $self->{options}->{blocksize}) *
207		  $self->{options}->{segsize} *
208		  1024, "\n";
209		print $o "#define XLOG_BLCKSZ ",
210		  1024 * $self->{options}->{wal_blocksize}, "\n";
211		print $o "#define XLOG_SEG_SIZE (", $self->{options}->{wal_segsize},
212		  " * 1024 * 1024)\n";
213
214		if ($self->{options}->{float4byval})
215		{
216			print $o "#define USE_FLOAT4_BYVAL 1\n";
217			print $o "#define FLOAT4PASSBYVAL true\n";
218		}
219		else
220		{
221			print $o "#define FLOAT4PASSBYVAL false\n";
222		}
223		if ($self->{options}->{float8byval})
224		{
225			print $o "#define USE_FLOAT8_BYVAL 1\n";
226			print $o "#define FLOAT8PASSBYVAL true\n";
227		}
228		else
229		{
230			print $o "#define FLOAT8PASSBYVAL false\n";
231		}
232
233		if ($self->{options}->{uuid})
234		{
235			print $o "#define HAVE_UUID_OSSP\n";
236			print $o "#define HAVE_UUID_H\n";
237		}
238		if ($self->{options}->{xml})
239		{
240			print $o "#define HAVE_LIBXML2\n";
241			print $o "#define USE_LIBXML\n";
242		}
243		if ($self->{options}->{xslt})
244		{
245			print $o "#define HAVE_LIBXSLT\n";
246			print $o "#define USE_LIBXSLT\n";
247		}
248		if ($self->{options}->{gss})
249		{
250			print $o "#define ENABLE_GSS 1\n";
251		}
252		if ($self->{options}->{openssl})
253		{
254			print $o "#define USE_OPENSSL 1\n";
255
256			my ($digit1, $digit2, $digit3) = $self->GetOpenSSLVersion();
257
258			# More symbols are needed with OpenSSL 1.1.0 and above.
259			if (   ($digit1 >= '3' && $digit2 >= '0' && $digit3 >= '0')
260				|| ($digit1 >= '1' && $digit2 >= '1' && $digit3 >= '0'))
261			{
262				print $o "#define HAVE_ASN1_STRING_GET0_DATA 1\n";
263				print $o "#define HAVE_BIO_GET_DATA 1\n";
264				print $o "#define HAVE_BIO_METH_NEW 1\n";
265				print $o "#define HAVE_OPENSSL_INIT_SSL 1\n";
266			}
267		}
268		if ($self->{options}->{icu})
269		{
270			print $o "#define USE_ICU 1\n";
271		}
272		if (my $port = $self->{options}->{"--with-pgport"})
273		{
274			print $o "#undef DEF_PGPORT\n";
275			print $o "#undef DEF_PGPORT_STR\n";
276			print $o "#define DEF_PGPORT $port\n";
277			print $o "#define DEF_PGPORT_STR \"$port\"\n";
278		}
279		print $o "#define VAL_CONFIGURE \""
280		  . $self->GetFakeConfigure() . "\"\n";
281		print $o "#endif /* IGNORE_CONFIGURED_SETTINGS */\n";
282		close($o);
283		close($i);
284	}
285
286	if (IsNewer(
287			"src/include/pg_config_ext.h",
288			"src/include/pg_config_ext.h.win32"))
289	{
290		print "Copying pg_config_ext.h...\n";
291		copyFile(
292			"src/include/pg_config_ext.h.win32",
293			"src/include/pg_config_ext.h");
294	}
295
296	$self->GenerateDefFile(
297		"src/interfaces/libpq/libpqdll.def",
298		"src/interfaces/libpq/exports.txt",
299		"LIBPQ");
300	$self->GenerateDefFile(
301		"src/interfaces/ecpg/ecpglib/ecpglib.def",
302		"src/interfaces/ecpg/ecpglib/exports.txt",
303		"LIBECPG");
304	$self->GenerateDefFile(
305		"src/interfaces/ecpg/compatlib/compatlib.def",
306		"src/interfaces/ecpg/compatlib/exports.txt",
307		"LIBECPG_COMPAT");
308	$self->GenerateDefFile(
309		"src/interfaces/ecpg/pgtypeslib/pgtypeslib.def",
310		"src/interfaces/ecpg/pgtypeslib/exports.txt",
311		"LIBPGTYPES");
312
313	if (IsNewer(
314			'src/backend/utils/fmgrtab.c', 'src/include/catalog/pg_proc.h'))
315	{
316		print "Generating fmgrtab.c, fmgroids.h, fmgrprotos.h...\n";
317		chdir('src/backend/utils');
318		system(
319"perl -I ../catalog Gen_fmgrtab.pl ../../../src/include/catalog/pg_proc.h");
320		chdir('../../..');
321	}
322	if (IsNewer(
323			'src/include/utils/fmgroids.h',
324			'src/backend/utils/fmgroids.h'))
325	{
326		copyFile('src/backend/utils/fmgroids.h',
327			'src/include/utils/fmgroids.h');
328	}
329
330	if (IsNewer(
331			'src/include/utils/fmgrprotos.h',
332			'src/backend/utils/fmgrprotos.h'))
333	{
334		copyFile(
335			'src/backend/utils/fmgrprotos.h',
336			'src/include/utils/fmgrprotos.h');
337	}
338
339	if (IsNewer(
340			'src/include/storage/lwlocknames.h',
341			'src/backend/storage/lmgr/lwlocknames.txt'))
342	{
343		print "Generating lwlocknames.c and lwlocknames.h...\n";
344		chdir('src/backend/storage/lmgr');
345		system('perl generate-lwlocknames.pl lwlocknames.txt');
346		chdir('../../../..');
347	}
348	if (IsNewer(
349			'src/include/storage/lwlocknames.h',
350			'src/backend/storage/lmgr/lwlocknames.h'))
351	{
352		copyFile(
353			'src/backend/storage/lmgr/lwlocknames.h',
354			'src/include/storage/lwlocknames.h');
355	}
356
357	if (IsNewer(
358			'src/include/dynloader.h', 'src/backend/port/dynloader/win32.h'))
359	{
360		copyFile('src/backend/port/dynloader/win32.h',
361			'src/include/dynloader.h');
362	}
363
364	if (IsNewer('src/include/utils/probes.h', 'src/backend/utils/probes.d'))
365	{
366		print "Generating probes.h...\n";
367		system(
368'perl src/backend/utils/Gen_dummy_probes.pl src/backend/utils/probes.d > src/include/utils/probes.h'
369		);
370	}
371
372	if ($self->{options}->{python}
373		&& IsNewer(
374			'src/pl/plpython/spiexceptions.h',
375			'src/backend/utils/errcodes.txt'))
376	{
377		print "Generating spiexceptions.h...\n";
378		system(
379'perl src/pl/plpython/generate-spiexceptions.pl src/backend/utils/errcodes.txt > src/pl/plpython/spiexceptions.h'
380		);
381	}
382
383	if (IsNewer(
384			'src/include/utils/errcodes.h',
385			'src/backend/utils/errcodes.txt'))
386	{
387		print "Generating errcodes.h...\n";
388		system(
389'perl src/backend/utils/generate-errcodes.pl src/backend/utils/errcodes.txt > src/backend/utils/errcodes.h'
390		);
391		copyFile('src/backend/utils/errcodes.h',
392			'src/include/utils/errcodes.h');
393	}
394
395	if (IsNewer(
396			'src/pl/plpgsql/src/plerrcodes.h',
397			'src/backend/utils/errcodes.txt'))
398	{
399		print "Generating plerrcodes.h...\n";
400		system(
401'perl src/pl/plpgsql/src/generate-plerrcodes.pl src/backend/utils/errcodes.txt > src/pl/plpgsql/src/plerrcodes.h'
402		);
403	}
404
405	if ($self->{options}->{tcl}
406		&& IsNewer(
407			'src/pl/tcl/pltclerrcodes.h', 'src/backend/utils/errcodes.txt'))
408	{
409		print "Generating pltclerrcodes.h...\n";
410		system(
411'perl src/pl/tcl/generate-pltclerrcodes.pl src/backend/utils/errcodes.txt > src/pl/tcl/pltclerrcodes.h'
412		);
413	}
414
415	if (IsNewer(
416			'src/backend/utils/sort/qsort_tuple.c',
417			'src/backend/utils/sort/gen_qsort_tuple.pl'))
418	{
419		print "Generating qsort_tuple.c...\n";
420		system(
421'perl src/backend/utils/sort/gen_qsort_tuple.pl > src/backend/utils/sort/qsort_tuple.c'
422		);
423	}
424
425	if (IsNewer(
426			'src/interfaces/libpq/libpq.rc',
427			'src/interfaces/libpq/libpq.rc.in'))
428	{
429		print "Generating libpq.rc...\n";
430		my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) =
431		  localtime(time);
432		my $d = ($year - 100) . "$yday";
433		open(my $i, '<', 'src/interfaces/libpq/libpq.rc.in')
434		  || confess "Could not open libpq.rc.in";
435		open(my $o, '>', 'src/interfaces/libpq/libpq.rc')
436		  || confess "Could not open libpq.rc";
437		while (<$i>)
438		{
439			s/(VERSION.*),0/$1,$d/;
440			print $o $_;
441		}
442		close($i);
443		close($o);
444	}
445
446	if (IsNewer('src/bin/psql/sql_help.h', 'src/bin/psql/create_help.pl'))
447	{
448		print "Generating sql_help.h...\n";
449		chdir('src/bin/psql');
450		system("perl create_help.pl ../../../doc/src/sgml/ref sql_help");
451		chdir('../../..');
452	}
453
454	if (IsNewer(
455			'src/interfaces/ecpg/preproc/preproc.y',
456			'src/backend/parser/gram.y'))
457	{
458		print "Generating preproc.y...\n";
459		chdir('src/interfaces/ecpg/preproc');
460		system('perl parse.pl < ../../../backend/parser/gram.y > preproc.y');
461		chdir('../../../..');
462	}
463
464	if (IsNewer(
465			'src/interfaces/ecpg/include/ecpg_config.h',
466			'src/interfaces/ecpg/include/ecpg_config.h.in'))
467	{
468		print "Generating ecpg_config.h...\n";
469		open(my $o, '>', 'src/interfaces/ecpg/include/ecpg_config.h')
470		  || confess "Could not open ecpg_config.h";
471		print $o <<EOF;
472#if (_MSC_VER > 1200)
473#define HAVE_LONG_LONG_INT 1
474#define HAVE_LONG_LONG_INT_64 1
475#endif
476#define ENABLE_THREAD_SAFETY 1
477EOF
478		close($o);
479	}
480
481	unless (-f "src/port/pg_config_paths.h")
482	{
483		print "Generating pg_config_paths.h...\n";
484		open(my $o, '>', 'src/port/pg_config_paths.h')
485		  || confess "Could not open pg_config_paths.h";
486		print $o <<EOF;
487#define PGBINDIR "/bin"
488#define PGSHAREDIR "/share"
489#define SYSCONFDIR "/etc"
490#define INCLUDEDIR "/include"
491#define PKGINCLUDEDIR "/include"
492#define INCLUDEDIRSERVER "/include/server"
493#define LIBDIR "/lib"
494#define PKGLIBDIR "/lib"
495#define LOCALEDIR "/share/locale"
496#define DOCDIR "/doc"
497#define HTMLDIR "/doc"
498#define MANDIR "/man"
499EOF
500		close($o);
501	}
502
503	my $mf = Project::read_file('src/backend/catalog/Makefile');
504	$mf =~ s{\\\r?\n}{}g;
505	$mf =~ /^POSTGRES_BKI_SRCS\s*:?=[^,]+,(.*)\)$/gm
506	  || croak "Could not find POSTGRES_BKI_SRCS in Makefile\n";
507	my @allbki = split /\s+/, $1;
508	foreach my $bki (@allbki)
509	{
510		next if $bki eq "";
511		if (IsNewer(
512				'src/backend/catalog/postgres.bki',
513				"src/include/catalog/$bki"))
514		{
515			print "Generating postgres.bki and schemapg.h...\n";
516			chdir('src/backend/catalog');
517			my $bki_srcs = join(' ../../../src/include/catalog/', @allbki);
518			system(
519"perl genbki.pl -I../../../src/include/catalog --set-version=$self->{majorver} $bki_srcs"
520			);
521			chdir('../../..');
522			copyFile(
523				'src/backend/catalog/schemapg.h',
524				'src/include/catalog/schemapg.h');
525			last;
526		}
527	}
528
529	open(my $o, '>', "doc/src/sgml/version.sgml")
530	  || croak "Could not write to version.sgml\n";
531	print $o <<EOF;
532<!ENTITY version "$self->{strver}">
533<!ENTITY majorversion "$self->{majorver}">
534EOF
535	close($o);
536}
537
538sub GenerateDefFile
539{
540	my ($self, $deffile, $txtfile, $libname) = @_;
541
542	if (IsNewer($deffile, $txtfile))
543	{
544		print "Generating $deffile...\n";
545		open(my $if, '<', $txtfile) || confess("Could not open $txtfile\n");
546		open(my $of, '>', $deffile) || confess("Could not open $deffile\n");
547		print $of "LIBRARY $libname\nEXPORTS\n";
548		while (<$if>)
549		{
550			next if (/^#/);
551			next if (/^\s*$/);
552			my ($f, $o) = split;
553			print $of " $f @ $o\n";
554		}
555		close($of);
556		close($if);
557	}
558}
559
560sub AddProject
561{
562	my ($self, $name, $type, $folder, $initialdir) = @_;
563
564	my $proj =
565	  VSObjectFactory::CreateProject($self->{vcver}, $name, $type, $self);
566	push @{ $self->{projects}->{$folder} }, $proj;
567	$proj->AddDir($initialdir) if ($initialdir);
568	if ($self->{options}->{zlib})
569	{
570		$proj->AddIncludeDir($self->{options}->{zlib} . '\include');
571		$proj->AddLibrary($self->{options}->{zlib} . '\lib\zdll.lib');
572	}
573	if ($self->{options}->{openssl})
574	{
575		$proj->AddIncludeDir($self->{options}->{openssl} . '\include');
576		my ($digit1, $digit2, $digit3) = $self->GetOpenSSLVersion();
577
578		# Starting at version 1.1.0 the OpenSSL installers have
579		# changed their library names from:
580		# - libeay to libcrypto
581		# - ssleay to libssl
582		if (   ($digit1 >= '3' && $digit2 >= '0' && $digit3 >= '0')
583			|| ($digit1 >= '1' && $digit2 >= '1' && $digit3 >= '0'))
584		{
585			my $dbgsuffix;
586			my $libsslpath;
587			my $libcryptopath;
588
589			# The format name of the libraries is slightly
590			# different between the Win32 and Win64 platform, so
591			# adapt.
592			if (-e "$self->{options}->{openssl}/lib/VC/sslcrypto32MD.lib")
593			{
594				# Win32 here, with a debugging library set.
595				$dbgsuffix     = 1;
596				$libsslpath    = '\lib\VC\libssl32.lib';
597				$libcryptopath = '\lib\VC\libcrypto32.lib';
598			}
599			elsif (-e "$self->{options}->{openssl}/lib/VC/sslcrypto64MD.lib")
600			{
601				# Win64 here, with a debugging library set.
602				$dbgsuffix     = 1;
603				$libsslpath    = '\lib\VC\libssl64.lib';
604				$libcryptopath = '\lib\VC\libcrypto64.lib';
605			}
606			else
607			{
608				# On both Win32 and Win64 the same library
609				# names are used without a debugging context.
610				$dbgsuffix     = 0;
611				$libsslpath    = '\lib\libssl.lib';
612				$libcryptopath = '\lib\libcrypto.lib';
613			}
614
615			$proj->AddLibrary($self->{options}->{openssl} . $libsslpath,
616				$dbgsuffix);
617			$proj->AddLibrary($self->{options}->{openssl} . $libcryptopath,
618				$dbgsuffix);
619		}
620		else
621		{
622			# Choose which set of libraries to use depending on if
623			# debugging libraries are in place in the installer.
624			if (-e "$self->{options}->{openssl}/lib/VC/ssleay32MD.lib")
625			{
626				$proj->AddLibrary(
627					$self->{options}->{openssl} . '\lib\VC\ssleay32.lib', 1);
628				$proj->AddLibrary(
629					$self->{options}->{openssl} . '\lib\VC\libeay32.lib', 1);
630			}
631			else
632			{
633				# We don't expect the config-specific library
634				# to be here, so don't ask for it in last
635				# parameter.
636				$proj->AddLibrary(
637					$self->{options}->{openssl} . '\lib\ssleay32.lib', 0);
638				$proj->AddLibrary(
639					$self->{options}->{openssl} . '\lib\libeay32.lib', 0);
640			}
641		}
642	}
643	if ($self->{options}->{nls})
644	{
645		$proj->AddIncludeDir($self->{options}->{nls} . '\include');
646		$proj->AddLibrary($self->{options}->{nls} . '\lib\libintl.lib');
647	}
648	if ($self->{options}->{gss})
649	{
650		$proj->AddIncludeDir($self->{options}->{gss} . '\include');
651		$proj->AddIncludeDir($self->{options}->{gss} . '\include\krb5');
652		if ($self->{platform} eq 'Win32')
653		{
654			$proj->AddLibrary(
655				$self->{options}->{gss} . '\lib\i386\krb5_32.lib');
656			$proj->AddLibrary(
657				$self->{options}->{gss} . '\lib\i386\comerr32.lib');
658			$proj->AddLibrary(
659				$self->{options}->{gss} . '\lib\i386\gssapi32.lib');
660		}
661		else
662		{
663			$proj->AddLibrary(
664				$self->{options}->{gss} . '\lib\amd64\krb5_64.lib');
665			$proj->AddLibrary(
666				$self->{options}->{gss} . '\lib\amd64\comerr64.lib');
667			$proj->AddLibrary(
668				$self->{options}->{gss} . '\lib\amd64\gssapi64.lib');
669		}
670	}
671	if ($self->{options}->{iconv})
672	{
673		$proj->AddIncludeDir($self->{options}->{iconv} . '\include');
674		$proj->AddLibrary($self->{options}->{iconv} . '\lib\iconv.lib');
675	}
676	if ($self->{options}->{icu})
677	{
678		$proj->AddIncludeDir($self->{options}->{icu} . '\include');
679		if ($self->{platform} eq 'Win32')
680		{
681			$proj->AddLibrary($self->{options}->{icu} . '\lib\icuin.lib');
682			$proj->AddLibrary($self->{options}->{icu} . '\lib\icuuc.lib');
683			$proj->AddLibrary($self->{options}->{icu} . '\lib\icudt.lib');
684		}
685		else
686		{
687			$proj->AddLibrary($self->{options}->{icu} . '\lib64\icuin.lib');
688			$proj->AddLibrary($self->{options}->{icu} . '\lib64\icuuc.lib');
689			$proj->AddLibrary($self->{options}->{icu} . '\lib64\icudt.lib');
690		}
691	}
692	if ($self->{options}->{xml})
693	{
694		$proj->AddIncludeDir($self->{options}->{xml} . '\include');
695		$proj->AddIncludeDir($self->{options}->{xml} . '\include\libxml2');
696		$proj->AddLibrary($self->{options}->{xml} . '\lib\libxml2.lib');
697	}
698	if ($self->{options}->{xslt})
699	{
700		$proj->AddIncludeDir($self->{options}->{xslt} . '\include');
701		$proj->AddLibrary($self->{options}->{xslt} . '\lib\libxslt.lib');
702	}
703	if ($self->{options}->{uuid})
704	{
705		$proj->AddIncludeDir($self->{options}->{uuid} . '\include');
706		$proj->AddLibrary($self->{options}->{uuid} . '\lib\uuid.lib');
707	}
708	return $proj;
709}
710
711sub Save
712{
713	my ($self) = @_;
714	my %flduid;
715
716	$self->GenerateFiles();
717	foreach my $fld (keys %{ $self->{projects} })
718	{
719		foreach my $proj (@{ $self->{projects}->{$fld} })
720		{
721			$proj->Save();
722		}
723	}
724
725	open(my $sln, '>', "pgsql.sln") || croak "Could not write to pgsql.sln\n";
726	print $sln <<EOF;
727Microsoft Visual Studio Solution File, Format Version $self->{solutionFileVersion}
728# $self->{visualStudioName}
729EOF
730
731	print $sln $self->GetAdditionalHeaders();
732
733	foreach my $fld (keys %{ $self->{projects} })
734	{
735		foreach my $proj (@{ $self->{projects}->{$fld} })
736		{
737			print $sln <<EOF;
738Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "$proj->{name}", "$proj->{name}$proj->{filenameExtension}", "$proj->{guid}"
739EndProject
740EOF
741		}
742		if ($fld ne "")
743		{
744			$flduid{$fld} = Win32::GuidGen();
745			print $sln <<EOF;
746Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "$fld", "$fld", "$flduid{$fld}"
747EndProject
748EOF
749		}
750	}
751
752	print $sln <<EOF;
753Global
754	GlobalSection(SolutionConfigurationPlatforms) = preSolution
755		Debug|$self->{platform}= Debug|$self->{platform}
756		Release|$self->{platform} = Release|$self->{platform}
757	EndGlobalSection
758	GlobalSection(ProjectConfigurationPlatforms) = postSolution
759EOF
760
761	foreach my $fld (keys %{ $self->{projects} })
762	{
763		foreach my $proj (@{ $self->{projects}->{$fld} })
764		{
765			print $sln <<EOF;
766		$proj->{guid}.Debug|$self->{platform}.ActiveCfg = Debug|$self->{platform}
767		$proj->{guid}.Debug|$self->{platform}.Build.0  = Debug|$self->{platform}
768		$proj->{guid}.Release|$self->{platform}.ActiveCfg = Release|$self->{platform}
769		$proj->{guid}.Release|$self->{platform}.Build.0 = Release|$self->{platform}
770EOF
771		}
772	}
773
774	print $sln <<EOF;
775	EndGlobalSection
776	GlobalSection(SolutionProperties) = preSolution
777		HideSolutionNode = FALSE
778	EndGlobalSection
779	GlobalSection(NestedProjects) = preSolution
780EOF
781
782	foreach my $fld (keys %{ $self->{projects} })
783	{
784		next if ($fld eq "");
785		foreach my $proj (@{ $self->{projects}->{$fld} })
786		{
787			print $sln "\t\t$proj->{guid} = $flduid{$fld}\n";
788		}
789	}
790
791	print $sln <<EOF;
792	EndGlobalSection
793EndGlobal
794EOF
795	close($sln);
796}
797
798sub GetFakeConfigure
799{
800	my $self = shift;
801
802	my $cfg = '--enable-thread-safety';
803	$cfg .= ' --enable-cassert'   if ($self->{options}->{asserts});
804	$cfg .= ' --enable-nls'       if ($self->{options}->{nls});
805	$cfg .= ' --enable-tap-tests' if ($self->{options}->{tap_tests});
806	$cfg .= ' --with-ldap'        if ($self->{options}->{ldap});
807	$cfg .= ' --without-zlib' unless ($self->{options}->{zlib});
808	$cfg .= ' --with-extra-version' if ($self->{options}->{extraver});
809	$cfg .= ' --with-openssl'       if ($self->{options}->{openssl});
810	$cfg .= ' --with-ossp-uuid'     if ($self->{options}->{uuid});
811	$cfg .= ' --with-libxml'        if ($self->{options}->{xml});
812	$cfg .= ' --with-libxslt'       if ($self->{options}->{xslt});
813	$cfg .= ' --with-gssapi'        if ($self->{options}->{gss});
814	$cfg .= ' --with-icu'           if ($self->{options}->{icu});
815	$cfg .= ' --with-tcl'           if ($self->{options}->{tcl});
816	$cfg .= ' --with-perl'          if ($self->{options}->{perl});
817	$cfg .= ' --with-python'        if ($self->{options}->{python});
818	my $port = $self->{options}->{'--with-pgport'};
819	$cfg .= " --with-pgport=$port" if defined($port);
820
821	return $cfg;
822}
823
824package VS2005Solution;
825
826#
827# Package that encapsulates a Visual Studio 2005 solution file
828#
829
830use strict;
831use warnings;
832use base qw(Solution);
833
834sub new
835{
836	my $classname = shift;
837	my $self      = $classname->SUPER::_new(@_);
838	bless($self, $classname);
839
840	$self->{solutionFileVersion} = '9.00';
841	$self->{vcver}               = '8.00';
842	$self->{visualStudioName}    = 'Visual Studio 2005';
843
844	return $self;
845}
846
847package VS2008Solution;
848
849#
850# Package that encapsulates a Visual Studio 2008 solution file
851#
852
853use strict;
854use warnings;
855use base qw(Solution);
856
857sub new
858{
859	my $classname = shift;
860	my $self      = $classname->SUPER::_new(@_);
861	bless($self, $classname);
862
863	$self->{solutionFileVersion} = '10.00';
864	$self->{vcver}               = '9.00';
865	$self->{visualStudioName}    = 'Visual Studio 2008';
866
867	return $self;
868}
869
870package VS2010Solution;
871
872#
873# Package that encapsulates a Visual Studio 2010 solution file
874#
875
876use Carp;
877use strict;
878use warnings;
879use base qw(Solution);
880
881sub new
882{
883	my $classname = shift;
884	my $self      = $classname->SUPER::_new(@_);
885	bless($self, $classname);
886
887	$self->{solutionFileVersion} = '11.00';
888	$self->{vcver}               = '10.00';
889	$self->{visualStudioName}    = 'Visual Studio 2010';
890
891	return $self;
892}
893
894package VS2012Solution;
895
896#
897# Package that encapsulates a Visual Studio 2012 solution file
898#
899
900use Carp;
901use strict;
902use warnings;
903use base qw(Solution);
904
905sub new
906{
907	my $classname = shift;
908	my $self      = $classname->SUPER::_new(@_);
909	bless($self, $classname);
910
911	$self->{solutionFileVersion} = '12.00';
912	$self->{vcver}               = '11.00';
913	$self->{visualStudioName}    = 'Visual Studio 2012';
914
915	return $self;
916}
917
918package VS2013Solution;
919
920#
921# Package that encapsulates a Visual Studio 2013 solution file
922#
923
924use Carp;
925use strict;
926use warnings;
927use base qw(Solution);
928
929sub new
930{
931	my $classname = shift;
932	my $self      = $classname->SUPER::_new(@_);
933	bless($self, $classname);
934
935	$self->{solutionFileVersion}        = '12.00';
936	$self->{vcver}                      = '12.00';
937	$self->{visualStudioName}           = 'Visual Studio 2013';
938	$self->{VisualStudioVersion}        = '12.0.21005.1';
939	$self->{MinimumVisualStudioVersion} = '10.0.40219.1';
940
941	return $self;
942}
943
944package VS2015Solution;
945
946#
947# Package that encapsulates a Visual Studio 2015 solution file
948#
949
950use Carp;
951use strict;
952use warnings;
953use base qw(Solution);
954
955sub new
956{
957	my $classname = shift;
958	my $self      = $classname->SUPER::_new(@_);
959	bless($self, $classname);
960
961	$self->{solutionFileVersion}        = '12.00';
962	$self->{vcver}                      = '14.00';
963	$self->{visualStudioName}           = 'Visual Studio 2015';
964	$self->{VisualStudioVersion}        = '14.0.24730.2';
965	$self->{MinimumVisualStudioVersion} = '10.0.40219.1';
966
967	return $self;
968}
969
970package VS2017Solution;
971
972#
973# Package that encapsulates a Visual Studio 2017 solution file
974#
975
976use Carp;
977use strict;
978use warnings;
979use base qw(Solution);
980
981sub new
982{
983	my $classname = shift;
984	my $self      = $classname->SUPER::_new(@_);
985	bless($self, $classname);
986
987	$self->{solutionFileVersion}        = '12.00';
988	$self->{vcver}                      = '15.00';
989	$self->{visualStudioName}           = 'Visual Studio 2017';
990	$self->{VisualStudioVersion}        = '15.0.26730.3';
991	$self->{MinimumVisualStudioVersion} = '10.0.40219.1';
992
993	return $self;
994}
995
996package VS2019Solution;
997
998#
999# Package that encapsulates a Visual Studio 2019 solution file
1000#
1001
1002use Carp;
1003use strict;
1004use warnings;
1005use base qw(Solution);
1006
1007no warnings qw(redefine);    ## no critic
1008
1009sub new
1010{
1011	my $classname = shift;
1012	my $self      = $classname->SUPER::_new(@_);
1013	bless($self, $classname);
1014
1015	$self->{solutionFileVersion}        = '12.00';
1016	$self->{vcver}                      = '16.00';
1017	$self->{visualStudioName}           = 'Visual Studio 2019';
1018	$self->{VisualStudioVersion}        = '16.0.28729.10';
1019	$self->{MinimumVisualStudioVersion} = '10.0.40219.1';
1020
1021	return $self;
1022}
1023
1024sub GetAdditionalHeaders
1025{
1026	my ($self, $f) = @_;
1027
1028	return qq|VisualStudioVersion = $self->{VisualStudioVersion}
1029MinimumVisualStudioVersion = $self->{MinimumVisualStudioVersion}
1030|;
1031}
1032
10331;
1034