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