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