diff options
Diffstat (limited to 'src/tools/msvc/Mkvcbuild.pm')
-rw-r--r-- | src/tools/msvc/Mkvcbuild.pm | 1133 |
1 files changed, 1133 insertions, 0 deletions
diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm new file mode 100644 index 0000000..05ff67e --- /dev/null +++ b/src/tools/msvc/Mkvcbuild.pm @@ -0,0 +1,1133 @@ + +# Copyright (c) 2021, PostgreSQL Global Development Group + +package Mkvcbuild; + +# +# Package that generates build files for msvc build +# +# src/tools/msvc/Mkvcbuild.pm +# +use strict; +use warnings; + +use Carp; +use if ($^O eq "MSWin32"), 'Win32'; +use Project; +use Solution; +use Cwd; +use File::Copy; +use Config; +use VSObjectFactory; +use List::Util qw(first); + +use Exporter; +our (@ISA, @EXPORT_OK); +@ISA = qw(Exporter); +@EXPORT_OK = qw(Mkvcbuild); + +my $solution; +my $libpgport; +my $libpgcommon; +my $libpgfeutils; +my $postgres; +my $libpq; +my @unlink_on_exit; + +# Set of variables for modules in contrib/ and src/test/modules/ +my $contrib_defines = { 'refint' => 'REFINT_VERBOSE' }; +my @contrib_uselibpq = + ('dblink', 'oid2name', 'postgres_fdw', 'vacuumlo', 'libpq_pipeline'); +my @contrib_uselibpgport = ('libpq_pipeline', 'oid2name', 'vacuumlo'); +my @contrib_uselibpgcommon = ('libpq_pipeline', 'oid2name', 'vacuumlo'); +my $contrib_extralibs = { 'libpq_pipeline' => ['ws2_32.lib'] }; +my $contrib_extraincludes = { 'dblink' => ['src/backend'] }; +my $contrib_extrasource = { + 'cube' => [ 'contrib/cube/cubescan.l', 'contrib/cube/cubeparse.y' ], + 'seg' => [ 'contrib/seg/segscan.l', 'contrib/seg/segparse.y' ], +}; +my @contrib_excludes = ( + 'bool_plperl', 'commit_ts', + 'hstore_plperl', 'hstore_plpython', + 'intagg', 'jsonb_plperl', + 'jsonb_plpython', 'ltree_plpython', + 'pgcrypto', 'sepgsql', + 'brin', 'test_extensions', + 'test_misc', 'test_pg_dump', + 'snapshot_too_old', 'unsafe_tests'); + +# Set of variables for frontend modules +my $frontend_defines = { 'initdb' => 'FRONTEND' }; +my @frontend_uselibpq = + ('pg_amcheck', 'pg_ctl', 'pg_upgrade', 'pgbench', 'psql', 'initdb'); +my @frontend_uselibpgport = ( + 'pg_amcheck', 'pg_archivecleanup', + 'pg_test_fsync', 'pg_test_timing', + 'pg_upgrade', 'pg_waldump', + 'pgbench'); +my @frontend_uselibpgcommon = ( + 'pg_amcheck', 'pg_archivecleanup', + 'pg_test_fsync', 'pg_test_timing', + 'pg_upgrade', 'pg_waldump', + 'pgbench'); +my $frontend_extralibs = { + 'initdb' => ['ws2_32.lib'], + 'pg_amcheck' => ['ws2_32.lib'], + 'pg_restore' => ['ws2_32.lib'], + 'pgbench' => ['ws2_32.lib'], + 'psql' => ['ws2_32.lib'] +}; +my $frontend_extraincludes = { + 'initdb' => ['src/timezone'], + 'psql' => ['src/backend'] +}; +my $frontend_extrasource = { + 'psql' => ['src/bin/psql/psqlscanslash.l'], + 'pgbench' => + [ 'src/bin/pgbench/exprscan.l', 'src/bin/pgbench/exprparse.y' ] +}; +my @frontend_excludes = ( + 'pgevent', 'pg_basebackup', 'pg_rewind', 'pg_dump', + 'pg_waldump', 'scripts'); + +sub mkvcbuild +{ + our $config = shift; + + chdir('../../..') if (-d '../msvc' && -d '../../../src'); + die 'Must run from root or msvc directory' + unless (-d 'src/tools/msvc' && -d 'src'); + + my $vsVersion = DetermineVisualStudioVersion(); + + $solution = CreateSolution($vsVersion, $config); + + our @pgportfiles = qw( + chklocale.c explicit_bzero.c fls.c getpeereid.c getrusage.c inet_aton.c random.c + srandom.c getaddrinfo.c gettimeofday.c inet_net_ntop.c kill.c open.c + erand48.c snprintf.c strlcat.c strlcpy.c dirmod.c noblock.c path.c + dirent.c dlopen.c getopt.c getopt_long.c link.c + pread.c preadv.c pwrite.c pwritev.c pg_bitutils.c + pg_strong_random.c pgcheckdir.c pgmkdirp.c pgsleep.c pgstrcasecmp.c + pqsignal.c mkdtemp.c qsort.c qsort_arg.c bsearch_arg.c quotes.c system.c + strerror.c tar.c thread.c + win32env.c win32error.c win32security.c win32setlocale.c win32stat.c); + + push(@pgportfiles, 'strtof.c') if ($vsVersion < '14.00'); + + if ($vsVersion >= '9.00') + { + push(@pgportfiles, 'pg_crc32c_sse42_choose.c'); + push(@pgportfiles, 'pg_crc32c_sse42.c'); + push(@pgportfiles, 'pg_crc32c_sb8.c'); + } + else + { + push(@pgportfiles, 'pg_crc32c_sb8.c'); + } + + our @pgcommonallfiles = qw( + archive.c base64.c checksum_helper.c + config_info.c controldata_utils.c d2s.c encnames.c exec.c + f2s.c file_perm.c file_utils.c hashfn.c ip.c jsonapi.c + keywords.c kwlookup.c link-canary.c md5_common.c + pg_get_line.c pg_lzcompress.c pgfnames.c psprintf.c relpath.c rmtree.c + saslprep.c scram-common.c string.c stringinfo.c unicode_norm.c username.c + wait_error.c wchar.c); + + if ($solution->{options}->{openssl}) + { + push(@pgcommonallfiles, 'cryptohash_openssl.c'); + push(@pgcommonallfiles, 'hmac_openssl.c'); + push(@pgcommonallfiles, 'protocol_openssl.c'); + } + else + { + push(@pgcommonallfiles, 'cryptohash.c'); + push(@pgcommonallfiles, 'hmac.c'); + push(@pgcommonallfiles, 'md5.c'); + push(@pgcommonallfiles, 'sha1.c'); + push(@pgcommonallfiles, 'sha2.c'); + } + + our @pgcommonfrontendfiles = ( + @pgcommonallfiles, qw(fe_memutils.c + logging.c restricted_token.c sprompt.c)); + + our @pgcommonbkndfiles = @pgcommonallfiles; + + our @pgfeutilsfiles = qw( + archive.c cancel.c conditional.c connect_utils.c mbprint.c option_utils.c + parallel_slot.c print.c psqlscan.l psqlscan.c query_utils.c simple_list.c + string_utils.c recovery_gen.c); + + $libpgport = $solution->AddProject('libpgport', 'lib', 'misc'); + $libpgport->AddDefine('FRONTEND'); + $libpgport->AddFiles('src/port', @pgportfiles); + + $libpgcommon = $solution->AddProject('libpgcommon', 'lib', 'misc'); + $libpgcommon->AddDefine('FRONTEND'); + $libpgcommon->AddFiles('src/common', @pgcommonfrontendfiles); + + $libpgfeutils = $solution->AddProject('libpgfeutils', 'lib', 'misc'); + $libpgfeutils->AddDefine('FRONTEND'); + $libpgfeutils->AddIncludeDir('src/interfaces/libpq'); + $libpgfeutils->AddFiles('src/fe_utils', @pgfeutilsfiles); + + $postgres = $solution->AddProject('postgres', 'exe', '', 'src/backend'); + $postgres->AddIncludeDir('src/backend'); + $postgres->AddDir('src/backend/port/win32'); + $postgres->AddFile('src/backend/utils/fmgrtab.c'); + $postgres->ReplaceFile('src/backend/port/pg_sema.c', + 'src/backend/port/win32_sema.c'); + $postgres->ReplaceFile('src/backend/port/pg_shmem.c', + 'src/backend/port/win32_shmem.c'); + $postgres->AddFiles('src/port', @pgportfiles); + $postgres->AddFiles('src/common', @pgcommonbkndfiles); + $postgres->AddDir('src/timezone'); + + # We need source files from src/timezone, but that directory's resource + # file pertains to "zic", not to the backend. + $postgres->RemoveFile('src/timezone/win32ver.rc'); + $postgres->AddFiles('src/backend/parser', 'scan.l', 'gram.y'); + $postgres->AddFiles('src/backend/bootstrap', 'bootscanner.l', + 'bootparse.y'); + $postgres->AddFiles('src/backend/utils/misc', 'guc-file.l'); + $postgres->AddFiles( + 'src/backend/replication', 'repl_scanner.l', + 'repl_gram.y', 'syncrep_scanner.l', + 'syncrep_gram.y'); + $postgres->AddFiles('src/backend/utils/adt', 'jsonpath_scan.l', + 'jsonpath_gram.y'); + $postgres->AddDefine('BUILDING_DLL'); + $postgres->AddLibrary('secur32.lib'); + $postgres->AddLibrary('ws2_32.lib'); + $postgres->AddLibrary('wldap32.lib') if ($solution->{options}->{ldap}); + $postgres->FullExportDLL('postgres.lib'); + + # The OBJS scraper doesn't know about ifdefs, so remove appropriate files + # if building without OpenSSL. + if (!$solution->{options}->{openssl}) + { + $postgres->RemoveFile('src/backend/libpq/be-secure-common.c'); + $postgres->RemoveFile('src/backend/libpq/be-secure-openssl.c'); + } + if (!$solution->{options}->{gss}) + { + $postgres->RemoveFile('src/backend/libpq/be-gssapi-common.c'); + $postgres->RemoveFile('src/backend/libpq/be-secure-gssapi.c'); + } + + my $snowball = $solution->AddProject('dict_snowball', 'dll', '', + 'src/backend/snowball'); + + # This Makefile uses VPATH to find most source files in a subdirectory. + $snowball->RelocateFiles( + 'src/backend/snowball/libstemmer', + sub { + return shift !~ /(dict_snowball.c|win32ver.rc)$/; + }); + $snowball->AddIncludeDir('src/include/snowball'); + $snowball->AddReference($postgres); + + my $plpgsql = + $solution->AddProject('plpgsql', 'dll', 'PLs', 'src/pl/plpgsql/src'); + $plpgsql->AddFiles('src/pl/plpgsql/src', 'pl_gram.y'); + $plpgsql->AddReference($postgres); + + if ($solution->{options}->{tcl}) + { + my $found = 0; + my $pltcl = + $solution->AddProject('pltcl', 'dll', 'PLs', 'src/pl/tcl'); + $pltcl->AddIncludeDir($solution->{options}->{tcl} . '/include'); + $pltcl->AddReference($postgres); + + for my $tclver (qw(86t 86 85 84)) + { + my $tcllib = $solution->{options}->{tcl} . "/lib/tcl$tclver.lib"; + if (-e $tcllib) + { + $pltcl->AddLibrary($tcllib); + $found = 1; + last; + } + } + die "Unable to find $solution->{options}->{tcl}/lib/tcl<version>.lib" + unless $found; + } + + $libpq = $solution->AddProject('libpq', 'dll', 'interfaces', + 'src/interfaces/libpq'); + $libpq->AddDefine('FRONTEND'); + $libpq->AddDefine('UNSAFE_STAT_OK'); + $libpq->AddIncludeDir('src/port'); + $libpq->AddLibrary('secur32.lib'); + $libpq->AddLibrary('ws2_32.lib'); + $libpq->AddLibrary('wldap32.lib') if ($solution->{options}->{ldap}); + $libpq->UseDef('src/interfaces/libpq/libpqdll.def'); + $libpq->AddReference($libpgcommon, $libpgport); + + # The OBJS scraper doesn't know about ifdefs, so remove appropriate files + # if building without OpenSSL. + if (!$solution->{options}->{openssl}) + { + $libpq->RemoveFile('src/interfaces/libpq/fe-secure-common.c'); + $libpq->RemoveFile('src/interfaces/libpq/fe-secure-openssl.c'); + } + if (!$solution->{options}->{gss}) + { + $libpq->RemoveFile('src/interfaces/libpq/fe-gssapi-common.c'); + $libpq->RemoveFile('src/interfaces/libpq/fe-secure-gssapi.c'); + } + + my $libpqwalreceiver = + $solution->AddProject('libpqwalreceiver', 'dll', '', + 'src/backend/replication/libpqwalreceiver'); + $libpqwalreceiver->AddIncludeDir('src/interfaces/libpq'); + $libpqwalreceiver->AddReference($postgres, $libpq); + + my $pgoutput = $solution->AddProject('pgoutput', 'dll', '', + 'src/backend/replication/pgoutput'); + $pgoutput->AddReference($postgres); + + my $pgtypes = $solution->AddProject( + 'libpgtypes', 'dll', + 'interfaces', 'src/interfaces/ecpg/pgtypeslib'); + $pgtypes->AddDefine('FRONTEND'); + $pgtypes->AddReference($libpgcommon, $libpgport); + $pgtypes->UseDef('src/interfaces/ecpg/pgtypeslib/pgtypeslib.def'); + $pgtypes->AddIncludeDir('src/interfaces/ecpg/include'); + + my $libecpg = $solution->AddProject('libecpg', 'dll', 'interfaces', + 'src/interfaces/ecpg/ecpglib'); + $libecpg->AddDefine('FRONTEND'); + $libecpg->AddIncludeDir('src/interfaces/ecpg/include'); + $libecpg->AddIncludeDir('src/interfaces/libpq'); + $libecpg->AddIncludeDir('src/port'); + $libecpg->UseDef('src/interfaces/ecpg/ecpglib/ecpglib.def'); + $libecpg->AddLibrary('ws2_32.lib'); + $libecpg->AddReference($libpq, $pgtypes, $libpgport); + + my $libecpgcompat = $solution->AddProject( + 'libecpg_compat', 'dll', + 'interfaces', 'src/interfaces/ecpg/compatlib'); + $libecpgcompat->AddDefine('FRONTEND'); + $libecpgcompat->AddIncludeDir('src/interfaces/ecpg/include'); + $libecpgcompat->AddIncludeDir('src/interfaces/libpq'); + $libecpgcompat->UseDef('src/interfaces/ecpg/compatlib/compatlib.def'); + $libecpgcompat->AddReference($pgtypes, $libecpg, $libpgport, + $libpgcommon); + + my $ecpg = $solution->AddProject('ecpg', 'exe', 'interfaces', + 'src/interfaces/ecpg/preproc'); + $ecpg->AddIncludeDir('src/interfaces/ecpg/include'); + $ecpg->AddIncludeDir('src/interfaces/ecpg/ecpglib'); + $ecpg->AddIncludeDir('src/interfaces/libpq'); + $ecpg->AddPrefixInclude('src/interfaces/ecpg/preproc'); + $ecpg->AddFiles('src/interfaces/ecpg/preproc', 'pgc.l', 'preproc.y'); + $ecpg->AddReference($libpgcommon, $libpgport); + + my $pgregress_ecpg = + $solution->AddProject('pg_regress_ecpg', 'exe', 'misc'); + $pgregress_ecpg->AddFile('src/interfaces/ecpg/test/pg_regress_ecpg.c'); + $pgregress_ecpg->AddFile('src/test/regress/pg_regress.c'); + $pgregress_ecpg->AddIncludeDir('src/port'); + $pgregress_ecpg->AddIncludeDir('src/test/regress'); + $pgregress_ecpg->AddDefine('HOST_TUPLE="i686-pc-win32vc"'); + $pgregress_ecpg->AddLibrary('ws2_32.lib'); + $pgregress_ecpg->AddDirResourceFile('src/interfaces/ecpg/test'); + $pgregress_ecpg->AddReference($libpgcommon, $libpgport); + + my $isolation_tester = + $solution->AddProject('isolationtester', 'exe', 'misc'); + $isolation_tester->AddFile('src/test/isolation/isolationtester.c'); + $isolation_tester->AddFile('src/test/isolation/specparse.y'); + $isolation_tester->AddFile('src/test/isolation/specscanner.l'); + $isolation_tester->AddFile('src/test/isolation/specparse.c'); + $isolation_tester->AddIncludeDir('src/test/isolation'); + $isolation_tester->AddIncludeDir('src/port'); + $isolation_tester->AddIncludeDir('src/test/regress'); + $isolation_tester->AddIncludeDir('src/interfaces/libpq'); + $isolation_tester->AddDefine('HOST_TUPLE="i686-pc-win32vc"'); + $isolation_tester->AddLibrary('ws2_32.lib'); + $isolation_tester->AddDirResourceFile('src/test/isolation'); + $isolation_tester->AddReference($libpq, $libpgcommon, $libpgport); + + my $pgregress_isolation = + $solution->AddProject('pg_isolation_regress', 'exe', 'misc'); + $pgregress_isolation->AddFile('src/test/isolation/isolation_main.c'); + $pgregress_isolation->AddFile('src/test/regress/pg_regress.c'); + $pgregress_isolation->AddIncludeDir('src/port'); + $pgregress_isolation->AddIncludeDir('src/test/regress'); + $pgregress_isolation->AddDefine('HOST_TUPLE="i686-pc-win32vc"'); + $pgregress_isolation->AddLibrary('ws2_32.lib'); + $pgregress_isolation->AddDirResourceFile('src/test/isolation'); + $pgregress_isolation->AddReference($libpgcommon, $libpgport); + + # src/bin + my $D; + opendir($D, 'src/bin') || croak "Could not opendir on src/bin!\n"; + while (my $d = readdir($D)) + { + next if ($d =~ /^\./); + next unless (-f "src/bin/$d/Makefile"); + next if (grep { /^$d$/ } @frontend_excludes); + AddSimpleFrontend($d); + } + + my $pgbasebackup = AddSimpleFrontend('pg_basebackup', 1); + $pgbasebackup->AddFile('src/bin/pg_basebackup/pg_basebackup.c'); + $pgbasebackup->AddLibrary('ws2_32.lib'); + + my $pgreceivewal = AddSimpleFrontend('pg_basebackup', 1); + $pgreceivewal->{name} = 'pg_receivewal'; + $pgreceivewal->AddFile('src/bin/pg_basebackup/pg_receivewal.c'); + $pgreceivewal->AddLibrary('ws2_32.lib'); + + my $pgrecvlogical = AddSimpleFrontend('pg_basebackup', 1); + $pgrecvlogical->{name} = 'pg_recvlogical'; + $pgrecvlogical->AddFile('src/bin/pg_basebackup/pg_recvlogical.c'); + $pgrecvlogical->AddLibrary('ws2_32.lib'); + + my $pgrewind = AddSimpleFrontend('pg_rewind', 1); + $pgrewind->{name} = 'pg_rewind'; + $pgrewind->AddFile('src/backend/access/transam/xlogreader.c'); + $pgrewind->AddLibrary('ws2_32.lib'); + $pgrewind->AddDefine('FRONTEND'); + + my $pgevent = $solution->AddProject('pgevent', 'dll', 'bin'); + $pgevent->AddFiles('src/bin/pgevent', 'pgevent.c', 'pgmsgevent.rc'); + $pgevent->AddResourceFile('src/bin/pgevent', 'Eventlog message formatter', + 'win32'); + $pgevent->RemoveFile('src/bin/pgevent/win32ver.rc'); + $pgevent->UseDef('src/bin/pgevent/pgevent.def'); + $pgevent->DisableLinkerWarnings('4104'); + + my $pgdump = AddSimpleFrontend('pg_dump', 1); + $pgdump->AddIncludeDir('src/backend'); + $pgdump->AddFile('src/bin/pg_dump/pg_dump.c'); + $pgdump->AddFile('src/bin/pg_dump/common.c'); + $pgdump->AddFile('src/bin/pg_dump/pg_dump_sort.c'); + $pgdump->AddLibrary('ws2_32.lib'); + + my $pgdumpall = AddSimpleFrontend('pg_dump', 1); + + # pg_dumpall doesn't use the files in the Makefile's $(OBJS), unlike + # pg_dump and pg_restore. + # So remove their sources from the object, keeping the other setup that + # AddSimpleFrontend() has done. + my @nodumpall = grep { m!src/bin/pg_dump/.*\.c$! } + keys %{ $pgdumpall->{files} }; + delete @{ $pgdumpall->{files} }{@nodumpall}; + $pgdumpall->{name} = 'pg_dumpall'; + $pgdumpall->AddIncludeDir('src/backend'); + $pgdumpall->AddFile('src/bin/pg_dump/pg_dumpall.c'); + $pgdumpall->AddFile('src/bin/pg_dump/dumputils.c'); + $pgdumpall->AddLibrary('ws2_32.lib'); + + my $pgrestore = AddSimpleFrontend('pg_dump', 1); + $pgrestore->{name} = 'pg_restore'; + $pgrestore->AddIncludeDir('src/backend'); + $pgrestore->AddFile('src/bin/pg_dump/pg_restore.c'); + $pgrestore->AddLibrary('ws2_32.lib'); + + my $zic = $solution->AddProject('zic', 'exe', 'utils'); + $zic->AddFiles('src/timezone', 'zic.c'); + $zic->AddDirResourceFile('src/timezone'); + $zic->AddReference($libpgcommon, $libpgport); + + if (!$solution->{options}->{xml}) + { + push @contrib_excludes, 'xml2'; + } + + if (!$solution->{options}->{openssl}) + { + push @contrib_excludes, 'sslinfo', 'ssl_passphrase_callback'; + } + + if (!$solution->{options}->{uuid}) + { + push @contrib_excludes, 'uuid-ossp'; + } + + # AddProject() does not recognize the constructs used to populate OBJS in + # the pgcrypto Makefile, so it will discover no files. + my $pgcrypto = + $solution->AddProject('pgcrypto', 'dll', 'crypto', 'contrib/pgcrypto'); + $pgcrypto->AddFiles( + 'contrib/pgcrypto', 'pgcrypto.c', + 'px.c', 'px-hmac.c', + 'px-crypt.c', 'crypt-gensalt.c', + 'crypt-blowfish.c', 'crypt-des.c', + 'crypt-md5.c', 'mbuf.c', + 'pgp.c', 'pgp-armor.c', + 'pgp-cfb.c', 'pgp-compress.c', + 'pgp-decrypt.c', 'pgp-encrypt.c', + 'pgp-info.c', 'pgp-mpi.c', + 'pgp-pubdec.c', 'pgp-pubenc.c', + 'pgp-pubkey.c', 'pgp-s2k.c', + 'pgp-pgsql.c'); + if ($solution->{options}->{openssl}) + { + $pgcrypto->AddFiles('contrib/pgcrypto', 'openssl.c', + 'pgp-mpi-openssl.c'); + } + else + { + $pgcrypto->AddFiles( + 'contrib/pgcrypto', 'internal.c', + 'internal-sha2.c', 'blf.c', + 'rijndael.c', 'pgp-mpi-internal.c', + 'imath.c'); + } + $pgcrypto->AddReference($postgres); + $pgcrypto->AddLibrary('ws2_32.lib'); + my $mf = Project::read_file('contrib/pgcrypto/Makefile'); + GenerateContribSqlFiles('pgcrypto', $mf); + + foreach my $subdir ('contrib', 'src/test/modules') + { + opendir($D, $subdir) || croak "Could not opendir on $subdir!\n"; + while (my $d = readdir($D)) + { + next if ($d =~ /^\./); + next unless (-f "$subdir/$d/Makefile"); + next if (grep { /^$d$/ } @contrib_excludes); + AddContrib($subdir, $d); + } + closedir($D); + } + + # Build Perl and Python modules after contrib/ modules to satisfy some + # dependencies with transform contrib modules, like hstore_plpython + # ltree_plpython and hstore_plperl. + if ($solution->{options}->{python}) + { + + # Attempt to get python version and location. + # Assume python.exe in specified dir. + my $pythonprog = "import sys;print(sys.prefix);" + . "print(str(sys.version_info[0])+str(sys.version_info[1]))"; + my $prefixcmd = + qq("$solution->{options}->{python}\\python" -c "$pythonprog"); + my $pyout = `$prefixcmd`; + die "Could not query for python version!\n" if $?; + my ($pyprefix, $pyver) = split(/\r?\n/, $pyout); + + # Sometimes (always?) if python is not present, the execution + # appears to work, but gives no data... + die "Failed to query python for version information\n" + if (!(defined($pyprefix) && defined($pyver))); + + my $pymajorver = substr($pyver, 0, 1); + my $plpython = $solution->AddProject('plpython' . $pymajorver, + 'dll', 'PLs', 'src/pl/plpython'); + $plpython->AddIncludeDir($pyprefix . '/include'); + $plpython->AddLibrary($pyprefix . "/Libs/python$pyver.lib"); + $plpython->AddReference($postgres); + + # Add transform modules dependent on plpython + my $hstore_plpython = AddTransformModule( + 'hstore_plpython' . $pymajorver, 'contrib/hstore_plpython', + 'plpython' . $pymajorver, 'src/pl/plpython', + 'hstore', 'contrib'); + $hstore_plpython->AddDefine( + 'PLPYTHON_LIBNAME="plpython' . $pymajorver . '"'); + my $jsonb_plpython = AddTransformModule( + 'jsonb_plpython' . $pymajorver, 'contrib/jsonb_plpython', + 'plpython' . $pymajorver, 'src/pl/plpython'); + $jsonb_plpython->AddDefine( + 'PLPYTHON_LIBNAME="plpython' . $pymajorver . '"'); + my $ltree_plpython = AddTransformModule( + 'ltree_plpython' . $pymajorver, 'contrib/ltree_plpython', + 'plpython' . $pymajorver, 'src/pl/plpython', + 'ltree', 'contrib'); + $ltree_plpython->AddDefine( + 'PLPYTHON_LIBNAME="plpython' . $pymajorver . '"'); + } + + if ($solution->{options}->{perl}) + { + my $plperlsrc = "src/pl/plperl/"; + my $plperl = + $solution->AddProject('plperl', 'dll', 'PLs', 'src/pl/plperl'); + $plperl->AddIncludeDir($solution->{options}->{perl} . '/lib/CORE'); + $plperl->AddReference($postgres); + + my $perl_path = $solution->{options}->{perl} . '\lib\CORE\*perl*'; + + # ActivePerl 5.16 provided perl516.lib; 5.18 provided libperl518.a + # Starting with ActivePerl 5.24, both perlnn.lib and libperlnn.a are provided. + # In this case, prefer .lib. + my @perl_libs = + reverse sort grep { /perl\d+\.lib$|libperl\d+\.a$/ } + glob($perl_path); + if (@perl_libs > 0) + { + $plperl->AddLibrary($perl_libs[0]); + } + else + { + die + "could not identify perl library version matching pattern $perl_path\n"; + } + + # Add defines from Perl's ccflags; see PGAC_CHECK_PERL_EMBED_CCFLAGS + my @perl_embed_ccflags; + foreach my $f (split(" ", $Config{ccflags})) + { + if ($f =~ /^-D[^_]/) + { + $f =~ s/\-D//; + push(@perl_embed_ccflags, $f); + } + } + + # hack to prevent duplicate definitions of uid_t/gid_t + push(@perl_embed_ccflags, 'PLPERL_HAVE_UID_GID'); + + # Windows offers several 32-bit ABIs. Perl is sensitive to + # sizeof(time_t), one of the ABI dimensions. To get 32-bit time_t, + # use "cl -D_USE_32BIT_TIME_T" or plain "gcc". For 64-bit time_t, use + # "gcc -D__MINGW_USE_VC2005_COMPAT" or plain "cl". Before MSVC 2005, + # plain "cl" chose 32-bit time_t. PostgreSQL doesn't support building + # with pre-MSVC-2005 compilers, but it does support linking to Perl + # built with such a compiler. MSVC-built Perl 5.13.4 and later report + # -D_USE_32BIT_TIME_T in $Config{ccflags} if applicable, but + # MinGW-built Perl never reports -D_USE_32BIT_TIME_T despite typically + # needing it. Ignore the $Config{ccflags} opinion about + # -D_USE_32BIT_TIME_T, and use a runtime test to deduce the ABI Perl + # expects. Specifically, test use of PL_modglobal, which maps to a + # PerlInterpreter field whose position depends on sizeof(time_t). + if ($solution->{platform} eq 'Win32') + { + my $source_file = 'conftest.c'; + my $obj = 'conftest.obj'; + my $exe = 'conftest.exe'; + my @conftest = ($source_file, $obj, $exe); + push @unlink_on_exit, @conftest; + unlink $source_file; + open my $o, '>', $source_file + || croak "Could not write to $source_file"; + print $o ' + /* compare to plperl.h */ + #define __inline__ __inline + #define PERL_NO_GET_CONTEXT + #include <EXTERN.h> + #include <perl.h> + + int + main(int argc, char **argv) + { + int dummy_argc = 1; + char *dummy_argv[1] = {""}; + char *dummy_env[1] = {NULL}; + static PerlInterpreter *interp; + + PERL_SYS_INIT3(&dummy_argc, (char ***) &dummy_argv, + (char ***) &dummy_env); + interp = perl_alloc(); + perl_construct(interp); + { + dTHX; + const char key[] = "dummy"; + + PL_exit_flags |= PERL_EXIT_DESTRUCT_END; + hv_store(PL_modglobal, key, sizeof(key) - 1, newSViv(1), 0); + return hv_fetch(PL_modglobal, key, sizeof(key) - 1, 0) == NULL; + } + } +'; + close $o; + + # Build $source_file with a given #define, and return a true value + # if a run of the resulting binary exits successfully. + my $try_define = sub { + my $define = shift; + + unlink $obj, $exe; + my @cmd = ( + 'cl', + '-I' . $solution->{options}->{perl} . '/lib/CORE', + (map { "-D$_" } @perl_embed_ccflags, $define || ()), + $source_file, + '/link', + $perl_libs[0]); + my $compile_output = `@cmd 2>&1`; + -f $exe || die "Failed to build Perl test:\n$compile_output"; + + { + + # Some builds exhibit runtime failure through Perl warning + # 'Can't spawn "conftest.exe"'; suppress that. + no warnings; + + no strict 'subs'; ## no critic (ProhibitNoStrict) + + # Disable error dialog boxes like we do in the postmaster. + # Here, we run code that triggers relevant errors. + use + if ($^O eq "MSWin32"), 'Win32API::File', + qw(SetErrorMode :SEM_); + my $oldmode = SetErrorMode( + SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX); + system(".\\$exe"); + SetErrorMode($oldmode); + } + + return !($? >> 8); + }; + + my $define_32bit_time = '_USE_32BIT_TIME_T'; + my $ok_now = $try_define->(undef); + my $ok_32bit = $try_define->($define_32bit_time); + unlink @conftest; + if (!$ok_now && !$ok_32bit) + { + + # Unsupported configuration. Since we used %Config from the + # Perl running the build scripts, this is expected if + # attempting to link with some other Perl. + die "Perl test fails with or without -D$define_32bit_time"; + } + elsif ($ok_now && $ok_32bit) + { + + # Resulting build may work, but it's especially important to + # verify with "vcregress plcheck". A refined test may avoid + # this outcome. + warn "Perl test passes with or without -D$define_32bit_time"; + } + elsif ($ok_32bit) + { + push(@perl_embed_ccflags, $define_32bit_time); + } # else $ok_now, hence no flag required + } + + print "CFLAGS recommended by Perl: $Config{ccflags}\n"; + print "CFLAGS to compile embedded Perl: ", + (join ' ', map { "-D$_" } @perl_embed_ccflags), "\n"; + foreach my $f (@perl_embed_ccflags) + { + $plperl->AddDefine($f); + } + + foreach my $xs ('SPI.xs', 'Util.xs') + { + (my $xsc = $xs) =~ s/\.xs/.c/; + if (Solution::IsNewer("$plperlsrc$xsc", "$plperlsrc$xs")) + { + my $xsubppdir = first { -e "$_/ExtUtils/xsubpp" } (@INC); + print "Building $plperlsrc$xsc...\n"; + system( $solution->{options}->{perl} + . '/bin/perl ' + . "$xsubppdir/ExtUtils/xsubpp -typemap " + . $solution->{options}->{perl} + . '/lib/ExtUtils/typemap ' + . "$plperlsrc$xs " + . ">$plperlsrc$xsc"); + if ((!(-f "$plperlsrc$xsc")) || -z "$plperlsrc$xsc") + { + unlink("$plperlsrc$xsc"); # if zero size + die "Failed to create $xsc.\n"; + } + } + } + if (Solution::IsNewer( + 'src/pl/plperl/perlchunks.h', + 'src/pl/plperl/plc_perlboot.pl') + || Solution::IsNewer( + 'src/pl/plperl/perlchunks.h', + 'src/pl/plperl/plc_trusted.pl')) + { + print 'Building src/pl/plperl/perlchunks.h ...' . "\n"; + my $basedir = getcwd; + chdir 'src/pl/plperl'; + system( $solution->{options}->{perl} + . '/bin/perl ' + . 'text2macro.pl ' + . '--strip="^(\#.*|\s*)$$" ' + . 'plc_perlboot.pl plc_trusted.pl ' + . '>perlchunks.h'); + chdir $basedir; + if ((!(-f 'src/pl/plperl/perlchunks.h')) + || -z 'src/pl/plperl/perlchunks.h') + { + unlink('src/pl/plperl/perlchunks.h'); # if zero size + die 'Failed to create perlchunks.h' . "\n"; + } + } + if (Solution::IsNewer( + 'src/pl/plperl/plperl_opmask.h', + 'src/pl/plperl/plperl_opmask.pl')) + { + print 'Building src/pl/plperl/plperl_opmask.h ...' . "\n"; + my $basedir = getcwd; + chdir 'src/pl/plperl'; + system( $solution->{options}->{perl} + . '/bin/perl ' + . 'plperl_opmask.pl ' + . 'plperl_opmask.h'); + chdir $basedir; + if ((!(-f 'src/pl/plperl/plperl_opmask.h')) + || -z 'src/pl/plperl/plperl_opmask.h') + { + unlink('src/pl/plperl/plperl_opmask.h'); # if zero size + die 'Failed to create plperl_opmask.h' . "\n"; + } + } + + # Add transform modules dependent on plperl + my $bool_plperl = AddTransformModule( + 'bool_plperl', 'contrib/bool_plperl', + 'plperl', 'src/pl/plperl'); + my $hstore_plperl = AddTransformModule( + 'hstore_plperl', 'contrib/hstore_plperl', + 'plperl', 'src/pl/plperl', + 'hstore', 'contrib'); + my $jsonb_plperl = AddTransformModule( + 'jsonb_plperl', 'contrib/jsonb_plperl', + 'plperl', 'src/pl/plperl'); + + foreach my $f (@perl_embed_ccflags) + { + $bool_plperl->AddDefine($f); + $hstore_plperl->AddDefine($f); + $jsonb_plperl->AddDefine($f); + } + } + + $mf = + Project::read_file('src/backend/utils/mb/conversion_procs/Makefile'); + $mf =~ s{\\\r?\n}{}g; + $mf =~ m{SUBDIRS\s*=\s*(.*)$}m + || die 'Could not match in conversion makefile' . "\n"; + foreach my $sub (split /\s+/, $1) + { + my $dir = 'src/backend/utils/mb/conversion_procs/' . $sub; + my $p = $solution->AddProject($sub, 'dll', 'conversion procs', $dir); + $p->AddFile("$dir/$sub.c"); # implicit source file + $p->AddReference($postgres); + } + + $mf = Project::read_file('src/bin/scripts/Makefile'); + $mf =~ s{\\\r?\n}{}g; + $mf =~ m{PROGRAMS\s*=\s*(.*)$}m + || die 'Could not match in bin/scripts/Makefile' . "\n"; + foreach my $prg (split /\s+/, $1) + { + my $proj = $solution->AddProject($prg, 'exe', 'bin'); + $mf =~ m{$prg\s*:\s*(.*)$}m + || die 'Could not find script define for $prg' . "\n"; + my @files = split /\s+/, $1; + foreach my $f (@files) + { + $f =~ s/\.o$/\.c/; + if ($f =~ /\.c$/) + { + $proj->AddFile('src/bin/scripts/' . $f); + } + } + $proj->AddIncludeDir('src/interfaces/libpq'); + $proj->AddReference($libpq, $libpgfeutils, $libpgcommon, $libpgport); + $proj->AddDirResourceFile('src/bin/scripts'); + $proj->AddLibrary('ws2_32.lib'); + } + + # Regression DLL and EXE + my $regress = $solution->AddProject('regress', 'dll', 'misc'); + $regress->AddFile('src/test/regress/regress.c'); + $regress->AddDirResourceFile('src/test/regress'); + $regress->AddReference($postgres); + + my $pgregress = $solution->AddProject('pg_regress', 'exe', 'misc'); + $pgregress->AddFile('src/test/regress/pg_regress.c'); + $pgregress->AddFile('src/test/regress/pg_regress_main.c'); + $pgregress->AddIncludeDir('src/port'); + $pgregress->AddDefine('HOST_TUPLE="i686-pc-win32vc"'); + $pgregress->AddLibrary('ws2_32.lib'); + $pgregress->AddDirResourceFile('src/test/regress'); + $pgregress->AddReference($libpgcommon, $libpgport); + + # fix up pg_waldump once it's been set up + # files symlinked on Unix are copied on windows + my $pg_waldump = AddSimpleFrontend('pg_waldump'); + $pg_waldump->AddDefine('FRONTEND'); + foreach my $xf (glob('src/backend/access/rmgrdesc/*desc.c')) + { + $pg_waldump->AddFile($xf); + } + $pg_waldump->AddFile('src/backend/access/transam/xlogreader.c'); + + $solution->Save(); + return $solution->{vcver}; +} + +##################### +# Utility functions # +##################### + +# Add a simple frontend project (exe) +sub AddSimpleFrontend +{ + my $n = shift; + my $uselibpq = shift; + + my $p = $solution->AddProject($n, 'exe', 'bin'); + $p->AddDir('src/bin/' . $n); + $p->AddReference($libpgfeutils, $libpgcommon, $libpgport); + if ($uselibpq) + { + $p->AddIncludeDir('src/interfaces/libpq'); + $p->AddReference($libpq); + } + + # Adjust module definition using frontend variables + AdjustFrontendProj($p); + + return $p; +} + +# Add a simple transform module +sub AddTransformModule +{ + my $n = shift; + my $n_src = shift; + my $pl_proj_name = shift; + my $pl_src = shift; + my $type_name = shift; + my $type_src = shift; + + my $type_proj = undef; + if ($type_name) + { + foreach my $proj (@{ $solution->{projects}->{'contrib'} }) + { + if ($proj->{name} eq $type_name) + { + $type_proj = $proj; + last; + } + } + die "could not find base module $type_name for transform module $n" + if (!defined($type_proj)); + } + + my $pl_proj = undef; + foreach my $proj (@{ $solution->{projects}->{'PLs'} }) + { + if ($proj->{name} eq $pl_proj_name) + { + $pl_proj = $proj; + last; + } + } + die "could not find PL $pl_proj_name for transform module $n" + if (!defined($pl_proj)); + + my $p = $solution->AddProject($n, 'dll', 'contrib', $n_src); + for my $file (glob("$n_src/*.c")) + { + $p->AddFile($file); + } + $p->AddReference($postgres); + + # Add PL dependencies + $p->AddIncludeDir($pl_src); + $p->AddReference($pl_proj); + $p->AddIncludeDir($pl_proj->{includes}); + foreach my $pl_lib (@{ $pl_proj->{libraries} }) + { + $p->AddLibrary($pl_lib); + } + + # Add base module dependencies + if ($type_proj) + { + $p->AddIncludeDir($type_src); + $p->AddIncludeDir($type_proj->{includes}); + foreach my $type_lib (@{ $type_proj->{libraries} }) + { + $p->AddLibrary($type_lib); + } + $p->AddReference($type_proj); + } + + return $p; +} + +# Add a simple contrib project +sub AddContrib +{ + my $subdir = shift; + my $n = shift; + my $mf = Project::read_file("$subdir/$n/Makefile"); + + if ($mf =~ /^MODULE_big\s*=\s*(.*)$/mg) + { + my $dn = $1; + my $proj = $solution->AddProject($dn, 'dll', 'contrib', "$subdir/$n"); + $proj->AddReference($postgres); + AdjustContribProj($proj); + } + elsif ($mf =~ /^MODULES\s*=\s*(.*)$/mg) + { + foreach my $mod (split /\s+/, $1) + { + my $proj = + $solution->AddProject($mod, 'dll', 'contrib', "$subdir/$n"); + my $filename = $mod . '.c'; + $proj->AddFile("$subdir/$n/$filename"); + $proj->AddReference($postgres); + AdjustContribProj($proj); + } + } + elsif ($mf =~ /^PROGRAM\s*=\s*(.*)$/mg) + { + my $proj = $solution->AddProject($1, 'exe', 'contrib', "$subdir/$n"); + AdjustContribProj($proj); + } + else + { + croak "Could not determine contrib module type for $n\n"; + } + + # Are there any output data files to build? + GenerateContribSqlFiles($n, $mf); + return; +} + +sub GenerateContribSqlFiles +{ + my $n = shift; + my $mf = shift; + $mf =~ s{\\\r?\n}{}g; + if ($mf =~ /^DATA_built\s*=\s*(.*)$/mg) + { + my $l = $1; + + # Strip out $(addsuffix) rules + if (index($l, '$(addsuffix ') >= 0) + { + my $pcount = 0; + my $i; + for ($i = index($l, '$(addsuffix ') + 12; $i < length($l); $i++) + { + $pcount++ if (substr($l, $i, 1) eq '('); + $pcount-- if (substr($l, $i, 1) eq ')'); + last if ($pcount < 0); + } + $l = + substr($l, 0, index($l, '$(addsuffix ')) . substr($l, $i + 1); + } + + foreach my $d (split /\s+/, $l) + { + my $in = "$d.in"; + my $out = "$d"; + + if (Solution::IsNewer("contrib/$n/$out", "contrib/$n/$in")) + { + print "Building $out from $in (contrib/$n)...\n"; + my $cont = Project::read_file("contrib/$n/$in"); + my $dn = $out; + $dn =~ s/\.sql$//; + $cont =~ s/MODULE_PATHNAME/\$libdir\/$dn/g; + my $o; + open($o, '>', "contrib/$n/$out") + || croak "Could not write to contrib/$n/$d"; + print $o $cont; + close($o); + } + } + } + return; +} + +sub AdjustContribProj +{ + my $proj = shift; + AdjustModule( + $proj, $contrib_defines, + \@contrib_uselibpq, \@contrib_uselibpgport, + \@contrib_uselibpgcommon, $contrib_extralibs, + $contrib_extrasource, $contrib_extraincludes); + return; +} + +sub AdjustFrontendProj +{ + my $proj = shift; + AdjustModule( + $proj, $frontend_defines, + \@frontend_uselibpq, \@frontend_uselibpgport, + \@frontend_uselibpgcommon, $frontend_extralibs, + $frontend_extrasource, $frontend_extraincludes); + return; +} + +sub AdjustModule +{ + my $proj = shift; + my $module_defines = shift; + my $module_uselibpq = shift; + my $module_uselibpgport = shift; + my $module_uselibpgcommon = shift; + my $module_extralibs = shift; + my $module_extrasource = shift; + my $module_extraincludes = shift; + my $n = $proj->{name}; + + if ($module_defines->{$n}) + { + foreach my $d ($module_defines->{$n}) + { + $proj->AddDefine($d); + } + } + if (grep { /^$n$/ } @{$module_uselibpq}) + { + $proj->AddIncludeDir('src\interfaces\libpq'); + $proj->AddReference($libpq); + } + if (grep { /^$n$/ } @{$module_uselibpgport}) + { + $proj->AddReference($libpgport); + } + if (grep { /^$n$/ } @{$module_uselibpgcommon}) + { + $proj->AddReference($libpgcommon); + } + if ($module_extralibs->{$n}) + { + foreach my $l (@{ $module_extralibs->{$n} }) + { + $proj->AddLibrary($l); + } + } + if ($module_extraincludes->{$n}) + { + foreach my $i (@{ $module_extraincludes->{$n} }) + { + $proj->AddIncludeDir($i); + } + } + if ($module_extrasource->{$n}) + { + foreach my $i (@{ $module_extrasource->{$n} }) + { + print "Files $i\n"; + $proj->AddFile($i); + } + } + return; +} + +END +{ + unlink @unlink_on_exit; +} + +1; |