# Copyright (c) 2021-2023, PostgreSQL Global Development Group package Solution; # # Package that encapsulates a Visual C++ solution file generation # # src/tools/msvc/Solution.pm # use Carp; use strict; use warnings; use VSObjectFactory; no warnings qw(redefine); ## no critic sub _new { my $classname = shift; my $options = shift; my $self = { projects => {}, options => $options, VisualStudioVersion => undef, MinimumVisualStudioVersion => undef, vcver => undef, platform => undef, }; bless($self, $classname); $self->DeterminePlatform(); if ($options->{xslt} && !$options->{xml}) { die "XSLT requires XML\n"; } $options->{blocksize} = 8 unless $options->{blocksize}; # undef or 0 means default die "Bad blocksize $options->{blocksize}" unless grep { $_ == $options->{blocksize} } (1, 2, 4, 8, 16, 32); $options->{segsize} = 1 unless $options->{segsize}; # undef or 0 means default # only allow segsize 1 for now, as we can't do large files yet in windows die "Bad segsize $options->{segsize}" unless $options->{segsize} == 1; $options->{wal_blocksize} = 8 unless $options->{wal_blocksize}; # undef or 0 means default die "Bad wal_blocksize $options->{wal_blocksize}" unless grep { $_ == $options->{wal_blocksize} } (1, 2, 4, 8, 16, 32, 64); return $self; } sub GetAdditionalHeaders { return ''; } sub DeterminePlatform { my $self = shift; if ($^O eq "MSWin32") { # Examine CL help output to determine if we are in 32 or 64-bit mode. my $output = `cl /help 2>&1`; $? >> 8 == 0 or die "cl command not found"; $self->{platform} = ($output =~ /^\/favor:<.+AMD64/m) ? 'x64' : 'Win32'; } else { $self->{platform} = 'FAKE'; } print "Detected hardware platform: $self->{platform}\n"; return; } # Return 1 if $oldfile is newer than $newfile, or if $newfile doesn't exist. # Special case - if config.pl has changed, always return 1 sub IsNewer { my ($newfile, $oldfile) = @_; -e $oldfile or warn "source file \"$oldfile\" does not exist"; if ( $oldfile ne 'src/tools/msvc/config.pl' && $oldfile ne 'src/tools/msvc/config_default.pl') { return 1 if (-f 'src/tools/msvc/config.pl') && IsNewer($newfile, 'src/tools/msvc/config.pl'); return 1 if (-f 'src/tools/msvc/config_default.pl') && IsNewer($newfile, 'src/tools/msvc/config_default.pl'); } return 1 if (!(-e $newfile)); my @nstat = stat($newfile); my @ostat = stat($oldfile); return 1 if ($nstat[9] < $ostat[9]); return 0; } # Copy a file, *not* preserving date. Only works for text files. sub copyFile { my ($src, $dest) = @_; open(my $i, '<', $src) || croak "Could not open $src"; open(my $o, '>', $dest) || croak "Could not open $dest"; while (<$i>) { print $o $_; } close($i); close($o); return; } # Fetch version of OpenSSL based on a parsing of the command shipped with # the installer this build is linking to. This returns as result an array # made of the three first digits of the OpenSSL version, which is enough # to decide which options to apply depending on the version of OpenSSL # linking with. sub GetOpenSSLVersion { my $self = shift; # Attempt to get OpenSSL version and location. This assumes that # openssl.exe is in the specified directory. # Quote the .exe name in case it has spaces my $opensslcmd = qq("$self->{options}->{openssl}\\bin\\openssl.exe" version 2>&1); my $sslout = `$opensslcmd`; $? >> 8 == 0 or croak "Unable to determine OpenSSL version: The openssl.exe command wasn't found."; if ($sslout =~ /(\d+)\.(\d+)\.(\d+)(\D)/m) { return ($1, $2, $3); } croak "Unable to determine OpenSSL version: The openssl.exe version could not be determined."; } sub GenerateFiles { my $self = shift; my $bits = $self->{platform} eq 'Win32' ? 32 : 64; my $ac_init_found = 0; my $package_name; my $package_version; my $package_bugreport; my $package_url; my ($majorver, $minorver); my $ac_define_openssl_api_compat_found = 0; my $openssl_api_compat; # Parse configure.ac to get version numbers open(my $c, '<', "configure.ac") || confess("Could not open configure.ac for reading\n"); while (<$c>) { if (/^AC_INIT\(\[([^\]]+)\], \[([^\]]+)\], \[([^\]]+)\], \[([^\]]*)\], \[([^\]]+)\]/ ) { $ac_init_found = 1; $package_name = $1; $package_version = $2; $package_bugreport = $3; #$package_tarname = $4; $package_url = $5; if ($package_version !~ /^(\d+)(?:\.(\d+))?/) { confess "Bad format of version: $package_version\n"; } $majorver = sprintf("%d", $1); $minorver = sprintf("%d", $2 ? $2 : 0); } elsif (/\bAC_DEFINE\(OPENSSL_API_COMPAT, \[([0-9xL]+)\]/) { $ac_define_openssl_api_compat_found = 1; $openssl_api_compat = $1; } } close($c); confess "Unable to parse configure.ac for all variables!" unless $ac_init_found && $ac_define_openssl_api_compat_found; if (IsNewer("src/include/pg_config_os.h", "src/include/port/win32.h")) { print "Copying pg_config_os.h...\n"; copyFile("src/include/port/win32.h", "src/include/pg_config_os.h"); } print "Generating configuration headers...\n"; my $extraver = $self->{options}->{extraver}; $extraver = '' unless defined $extraver; my $port = $self->{options}->{"--with-pgport"} || 5432; # Every symbol in pg_config.h.in must be accounted for here. Set # to undef if the symbol should not be defined. my %define = ( ALIGNOF_DOUBLE => 8, ALIGNOF_INT => 4, ALIGNOF_LONG => 4, ALIGNOF_LONG_LONG_INT => 8, ALIGNOF_PG_INT128_TYPE => undef, ALIGNOF_SHORT => 2, AC_APPLE_UNIVERSAL_BUILD => undef, BLCKSZ => 1024 * $self->{options}->{blocksize}, CONFIGURE_ARGS => '"' . $self->GetFakeConfigure() . '"', DEF_PGPORT => $port, DEF_PGPORT_STR => qq{"$port"}, DLSUFFIX => '".dll"', ENABLE_GSS => $self->{options}->{gss} ? 1 : undef, ENABLE_NLS => $self->{options}->{nls} ? 1 : undef, HAVE_APPEND_HISTORY => undef, HAVE_ASN1_STRING_GET0_DATA => undef, HAVE_ATOMICS => 1, HAVE_ATOMIC_H => undef, HAVE_BACKTRACE_SYMBOLS => undef, HAVE_BIO_METH_NEW => undef, HAVE_COMPUTED_GOTO => undef, HAVE_COPYFILE => undef, HAVE_COPYFILE_H => undef, HAVE_CRTDEFS_H => undef, HAVE_CRYPTO_LOCK => undef, HAVE_DECL_FDATASYNC => 0, HAVE_DECL_F_FULLFSYNC => 0, HAVE_DECL_LLVMCREATEGDBREGISTRATIONLISTENER => 0, HAVE_DECL_LLVMCREATEPERFJITEVENTLISTENER => 0, HAVE_DECL_LLVMGETHOSTCPUNAME => 0, HAVE_DECL_LLVMGETHOSTCPUFEATURES => 0, HAVE_DECL_LLVMORCGETSYMBOLADDRESSIN => 0, HAVE_DECL_POSIX_FADVISE => 0, HAVE_DECL_PREADV => 0, HAVE_DECL_PWRITEV => 0, HAVE_DECL_STRLCAT => 0, HAVE_DECL_STRLCPY => 0, HAVE_DECL_STRNLEN => 1, HAVE_EDITLINE_HISTORY_H => undef, HAVE_EDITLINE_READLINE_H => undef, HAVE_EXECINFO_H => undef, HAVE_EXPLICIT_BZERO => undef, HAVE_FSEEKO => 1, HAVE_GCC__ATOMIC_INT32_CAS => undef, HAVE_GCC__ATOMIC_INT64_CAS => undef, HAVE_GCC__SYNC_CHAR_TAS => undef, HAVE_GCC__SYNC_INT32_CAS => undef, HAVE_GCC__SYNC_INT32_TAS => undef, HAVE_GCC__SYNC_INT64_CAS => undef, HAVE_GETIFADDRS => undef, HAVE_GETOPT => undef, HAVE_GETOPT_H => undef, HAVE_GETOPT_LONG => undef, HAVE_GETPEEREID => undef, HAVE_GETPEERUCRED => undef, HAVE_GSSAPI_EXT_H => undef, HAVE_GSSAPI_GSSAPI_EXT_H => undef, HAVE_GSSAPI_GSSAPI_H => undef, HAVE_GSSAPI_H => undef, HAVE_HMAC_CTX_FREE => undef, HAVE_HMAC_CTX_NEW => undef, HAVE_HISTORY_H => undef, HAVE_HISTORY_TRUNCATE_FILE => undef, HAVE_IFADDRS_H => undef, HAVE_INET_ATON => undef, HAVE_INET_PTON => 1, HAVE_INT_TIMEZONE => 1, HAVE_INT64 => undef, HAVE_INT8 => undef, HAVE_INTTYPES_H => undef, HAVE_INT_OPTERR => undef, HAVE_INT_OPTRESET => undef, HAVE_I_CONSTRAINT__BUILTIN_CONSTANT_P => undef, HAVE_KQUEUE => undef, HAVE_LANGINFO_H => undef, HAVE_LDAP_INITIALIZE => undef, HAVE_LIBCRYPTO => undef, HAVE_LIBLDAP => undef, HAVE_LIBLZ4 => undef, HAVE_LIBM => undef, HAVE_LIBPAM => undef, HAVE_LIBREADLINE => undef, HAVE_LIBSELINUX => undef, HAVE_LIBSSL => undef, HAVE_LIBWLDAP32 => undef, HAVE_LIBXML2 => undef, HAVE_LIBXSLT => undef, HAVE_LIBZ => $self->{options}->{zlib} ? 1 : undef, HAVE_LIBZSTD => undef, HAVE_LONG_INT_64 => undef, HAVE_LONG_LONG_INT_64 => 1, HAVE_MBARRIER_H => undef, HAVE_MBSTOWCS_L => undef, HAVE_MEMORY_H => 1, HAVE_MEMSET_S => undef, HAVE_MKDTEMP => undef, HAVE_OPENSSL_INIT_SSL => undef, HAVE_OSSP_UUID_H => undef, HAVE_PAM_PAM_APPL_H => undef, HAVE_POSIX_FADVISE => undef, HAVE_POSIX_FALLOCATE => undef, HAVE_PPOLL => undef, HAVE_PTHREAD => undef, HAVE_PTHREAD_BARRIER_WAIT => undef, HAVE_PTHREAD_IS_THREADED_NP => undef, HAVE_PTHREAD_PRIO_INHERIT => undef, HAVE_READLINE_H => undef, HAVE_READLINE_HISTORY_H => undef, HAVE_READLINE_READLINE_H => undef, HAVE_RL_COMPLETION_MATCHES => undef, HAVE_RL_COMPLETION_SUPPRESS_QUOTE => undef, HAVE_RL_FILENAME_COMPLETION_FUNCTION => undef, HAVE_RL_FILENAME_QUOTE_CHARACTERS => undef, HAVE_RL_FILENAME_QUOTING_FUNCTION => undef, HAVE_RL_RESET_SCREEN_SIZE => undef, HAVE_RL_VARIABLE_BIND => undef, HAVE_SECURITY_PAM_APPL_H => undef, HAVE_SETPROCTITLE => undef, HAVE_SETPROCTITLE_FAST => undef, HAVE_SOCKLEN_T => 1, HAVE_SPINLOCKS => 1, HAVE_SSL_CTX_SET_CERT_CB => undef, HAVE_STDBOOL_H => 1, HAVE_STDINT_H => 1, HAVE_STDLIB_H => 1, HAVE_STRCHRNUL => undef, HAVE_STRERROR_R => undef, HAVE_STRINGS_H => undef, HAVE_STRING_H => 1, HAVE_STRLCAT => undef, HAVE_STRLCPY => undef, HAVE_STRNLEN => 1, HAVE_STRSIGNAL => undef, HAVE_STRUCT_OPTION => undef, HAVE_STRUCT_SOCKADDR_SA_LEN => undef, HAVE_STRUCT_TM_TM_ZONE => undef, HAVE_SYNC_FILE_RANGE => undef, HAVE_SYNCFS => undef, HAVE_SYSLOG => undef, HAVE_SYS_EPOLL_H => undef, HAVE_SYS_EVENT_H => undef, HAVE_SYS_PERSONALITY_H => undef, HAVE_SYS_PRCTL_H => undef, HAVE_SYS_PROCCTL_H => undef, HAVE_SYS_SIGNALFD_H => undef, HAVE_SYS_STAT_H => 1, HAVE_SYS_TYPES_H => 1, HAVE_SYS_UCRED_H => undef, HAVE_TERMIOS_H => undef, HAVE_TYPEOF => undef, HAVE_UCRED_H => undef, HAVE_UINT64 => undef, HAVE_UINT8 => undef, HAVE_UNION_SEMUN => undef, HAVE_UNISTD_H => 1, HAVE_USELOCALE => undef, HAVE_UUID_BSD => undef, HAVE_UUID_E2FS => undef, HAVE_UUID_OSSP => undef, HAVE_UUID_H => undef, HAVE_UUID_UUID_H => undef, HAVE_WCSTOMBS_L => undef, HAVE_VISIBILITY_ATTRIBUTE => undef, HAVE_X509_GET_SIGNATURE_INFO => undef, HAVE_X86_64_POPCNTQ => undef, HAVE__BOOL => undef, HAVE__BUILTIN_BSWAP16 => undef, HAVE__BUILTIN_BSWAP32 => undef, HAVE__BUILTIN_BSWAP64 => undef, HAVE__BUILTIN_CLZ => undef, HAVE__BUILTIN_CONSTANT_P => undef, HAVE__BUILTIN_CTZ => undef, HAVE__BUILTIN_FRAME_ADDRESS => undef, HAVE__BUILTIN_OP_OVERFLOW => undef, HAVE__BUILTIN_POPCOUNT => undef, HAVE__BUILTIN_TYPES_COMPATIBLE_P => undef, HAVE__BUILTIN_UNREACHABLE => undef, HAVE__CONFIGTHREADLOCALE => 1, HAVE__CPUID => 1, HAVE__GET_CPUID => undef, HAVE__STATIC_ASSERT => undef, INT64_MODIFIER => qq{"ll"}, LOCALE_T_IN_XLOCALE => undef, MAXIMUM_ALIGNOF => 8, MEMSET_LOOP_LIMIT => 1024, OPENSSL_API_COMPAT => $openssl_api_compat, PACKAGE_BUGREPORT => qq{"$package_bugreport"}, PACKAGE_NAME => qq{"$package_name"}, PACKAGE_STRING => qq{"$package_name $package_version"}, PACKAGE_TARNAME => lc qq{"$package_name"}, PACKAGE_URL => qq{"$package_url"}, PACKAGE_VERSION => qq{"$package_version"}, PG_INT128_TYPE => undef, PG_INT64_TYPE => 'long long int', PG_KRB_SRVNAM => qq{"postgres"}, PG_MAJORVERSION => qq{"$majorver"}, PG_MAJORVERSION_NUM => $majorver, PG_MINORVERSION_NUM => $minorver, PG_PRINTF_ATTRIBUTE => undef, PG_USE_STDBOOL => 1, PG_VERSION => qq{"$package_version$extraver"}, PG_VERSION_NUM => sprintf("%d%04d", $majorver, $minorver), PG_VERSION_STR => qq{"PostgreSQL $package_version$extraver, compiled by Visual C++ build " CppAsString2(_MSC_VER) ", $bits-bit"}, PROFILE_PID_DIR => undef, PTHREAD_CREATE_JOINABLE => undef, RELSEG_SIZE => (1024 / $self->{options}->{blocksize}) * $self->{options}->{segsize} * 1024, SIZEOF_BOOL => 1, SIZEOF_LONG => 4, SIZEOF_OFF_T => undef, SIZEOF_SIZE_T => $bits / 8, SIZEOF_VOID_P => $bits / 8, STDC_HEADERS => 1, STRERROR_R_INT => undef, USE_ARMV8_CRC32C => undef, USE_ARMV8_CRC32C_WITH_RUNTIME_CHECK => undef, USE_ASSERT_CHECKING => $self->{options}->{asserts} ? 1 : undef, USE_BONJOUR => undef, USE_BSD_AUTH => undef, USE_ICU => $self->{options}->{icu} ? 1 : undef, USE_LIBXML => undef, USE_LIBXSLT => undef, USE_LZ4 => undef, USE_LDAP => $self->{options}->{ldap} ? 1 : undef, USE_LLVM => undef, USE_LOONGARCH_CRC32C => undef, USE_NAMED_POSIX_SEMAPHORES => undef, USE_OPENSSL => undef, USE_PAM => undef, USE_SLICING_BY_8_CRC32C => undef, USE_SSE42_CRC32C => undef, USE_SSE42_CRC32C_WITH_RUNTIME_CHECK => 1, USE_SYSTEMD => undef, USE_SYSV_SEMAPHORES => undef, USE_SYSV_SHARED_MEMORY => undef, USE_UNNAMED_POSIX_SEMAPHORES => undef, USE_WIN32_SEMAPHORES => 1, USE_WIN32_SHARED_MEMORY => 1, USE_ZSTD => undef, WCSTOMBS_L_IN_XLOCALE => undef, WORDS_BIGENDIAN => undef, XLOG_BLCKSZ => 1024 * $self->{options}->{wal_blocksize}, _FILE_OFFSET_BITS => undef, _LARGEFILE_SOURCE => undef, _LARGE_FILES => undef, inline => '__inline', pg_restrict => '__restrict', # not defined, because it'd conflict with __declspec(restrict) restrict => undef, typeof => undef,); if ($self->{options}->{uuid}) { $define{HAVE_UUID_OSSP} = 1; $define{HAVE_UUID_H} = 1; } if ($self->{options}->{xml}) { $define{HAVE_LIBXML2} = 1; $define{USE_LIBXML} = 1; } if ($self->{options}->{xslt}) { $define{HAVE_LIBXSLT} = 1; $define{USE_LIBXSLT} = 1; } if ($self->{options}->{lz4}) { $define{HAVE_LIBLZ4} = 1; $define{USE_LZ4} = 1; } if ($self->{options}->{zstd}) { $define{HAVE_LIBZSTD} = 1; $define{USE_ZSTD} = 1; } if ($self->{options}->{openssl}) { $define{USE_OPENSSL} = 1; $define{HAVE_SSL_CTX_SET_CERT_CB} = 1; my ($digit1, $digit2, $digit3) = $self->GetOpenSSLVersion(); # Symbols needed with OpenSSL 1.1.1 and above. if ( ($digit1 >= '3' && $digit2 >= '0' && $digit3 >= '0') || ($digit1 >= '1' && $digit2 >= '1' && $digit3 >= '1')) { $define{HAVE_X509_GET_SIGNATURE_INFO} = 1; } # Symbols needed with OpenSSL 1.1.0 and above. if ( ($digit1 >= '3' && $digit2 >= '0' && $digit3 >= '0') || ($digit1 >= '1' && $digit2 >= '1' && $digit3 >= '0')) { $define{HAVE_ASN1_STRING_GET0_DATA} = 1; $define{HAVE_BIO_METH_NEW} = 1; $define{HAVE_HMAC_CTX_FREE} = 1; $define{HAVE_HMAC_CTX_NEW} = 1; $define{HAVE_OPENSSL_INIT_SSL} = 1; } } $self->GenerateConfigHeader('src/include/pg_config.h', \%define, 1); $self->GenerateConfigHeader('src/include/pg_config_ext.h', \%define, 0); $self->GenerateConfigHeader('src/interfaces/ecpg/include/ecpg_config.h', \%define, 0); $self->GenerateDefFile( "src/interfaces/libpq/libpqdll.def", "src/interfaces/libpq/exports.txt", "LIBPQ"); $self->GenerateDefFile( "src/interfaces/ecpg/ecpglib/ecpglib.def", "src/interfaces/ecpg/ecpglib/exports.txt", "LIBECPG"); $self->GenerateDefFile( "src/interfaces/ecpg/compatlib/compatlib.def", "src/interfaces/ecpg/compatlib/exports.txt", "LIBECPG_COMPAT"); $self->GenerateDefFile( "src/interfaces/ecpg/pgtypeslib/pgtypeslib.def", "src/interfaces/ecpg/pgtypeslib/exports.txt", "LIBPGTYPES"); chdir('src/backend/utils'); my $pg_proc_dat = '../../../src/include/catalog/pg_proc.dat'; if ( IsNewer('fmgr-stamp', 'Gen_fmgrtab.pl') || IsNewer('fmgr-stamp', '../catalog/Catalog.pm') || IsNewer('fmgr-stamp', $pg_proc_dat) || IsNewer('fmgr-stamp', '../../../src/include/access/transam.h')) { system( "perl -I ../catalog Gen_fmgrtab.pl --include-path ../../../src/include/ $pg_proc_dat" ); open(my $f, '>', 'fmgr-stamp') || confess "Could not touch fmgr-stamp"; close($f); } chdir('../../..'); if (IsNewer( 'src/include/utils/fmgroids.h', 'src/backend/utils/fmgroids.h')) { copyFile('src/backend/utils/fmgroids.h', 'src/include/utils/fmgroids.h'); } if (IsNewer( 'src/include/utils/fmgrprotos.h', 'src/backend/utils/fmgrprotos.h')) { copyFile( 'src/backend/utils/fmgrprotos.h', 'src/include/utils/fmgrprotos.h'); } if (IsNewer( 'src/include/storage/lwlocknames.h', 'src/backend/storage/lmgr/lwlocknames.txt')) { print "Generating lwlocknames.c and lwlocknames.h...\n"; my $lmgr = 'src/backend/storage/lmgr'; system( "perl $lmgr/generate-lwlocknames.pl --outdir $lmgr $lmgr/lwlocknames.txt" ); } if (IsNewer( 'src/include/storage/lwlocknames.h', 'src/backend/storage/lmgr/lwlocknames.h')) { copyFile( 'src/backend/storage/lmgr/lwlocknames.h', 'src/include/storage/lwlocknames.h'); } if (IsNewer( 'src/include/utils/wait_event_types.h', 'src/backend/utils/activity/wait_event_names.txt')) { print "Generating pgstat_wait_event.c, wait_event_types.h and wait_event_funcs_data.c...\n"; my $activ = 'src/backend/utils/activity'; system( "perl $activ/generate-wait_event_types.pl --outdir $activ --code $activ/wait_event_names.txt" ); } if (IsNewer( 'src/include/utils/wait_event_types.h', 'src/backend/utils/activity/wait_event_types.h')) { copyFile( 'src/backend/utils/activity/wait_event_types.h', 'src/include/utils/wait_event_types.h'); } if (IsNewer('src/include/utils/probes.h', 'src/backend/utils/probes.d')) { print "Generating probes.h...\n"; system( 'perl -n src/backend/utils/Gen_dummy_probes.pl src/backend/utils/probes.d > src/include/utils/probes.h' ); } if ($self->{options}->{python} && IsNewer( 'src/pl/plpython/spiexceptions.h', 'src/backend/utils/errcodes.txt')) { print "Generating spiexceptions.h...\n"; system( 'perl src/pl/plpython/generate-spiexceptions.pl src/backend/utils/errcodes.txt > src/pl/plpython/spiexceptions.h' ); } if (IsNewer( 'src/include/utils/errcodes.h', 'src/backend/utils/errcodes.txt')) { print "Generating errcodes.h...\n"; system( 'perl src/backend/utils/generate-errcodes.pl --outfile src/backend/utils/errcodes.h src/backend/utils/errcodes.txt' ); copyFile('src/backend/utils/errcodes.h', 'src/include/utils/errcodes.h'); } if (IsNewer( 'src/pl/plpgsql/src/plerrcodes.h', 'src/backend/utils/errcodes.txt')) { print "Generating plerrcodes.h...\n"; system( 'perl src/pl/plpgsql/src/generate-plerrcodes.pl src/backend/utils/errcodes.txt > src/pl/plpgsql/src/plerrcodes.h' ); } if ($self->{options}->{tcl} && IsNewer( 'src/pl/tcl/pltclerrcodes.h', 'src/backend/utils/errcodes.txt')) { print "Generating pltclerrcodes.h...\n"; system( 'perl src/pl/tcl/generate-pltclerrcodes.pl src/backend/utils/errcodes.txt > src/pl/tcl/pltclerrcodes.h' ); } if (IsNewer( 'contrib/fuzzystrmatch/daitch_mokotoff.h', 'contrib/fuzzystrmatch/daitch_mokotoff_header.pl')) { print "Generating daitch_mokotoff.h...\n"; system( 'perl contrib/fuzzystrmatch/daitch_mokotoff_header.pl ' . 'contrib/fuzzystrmatch/daitch_mokotoff.h'); } if (IsNewer('src/bin/psql/sql_help.h', 'src/bin/psql/create_help.pl')) { print "Generating sql_help.h...\n"; my $psql = 'src/bin/psql'; system( "perl $psql/create_help.pl --docdir doc/src/sgml/ref --outdir $psql --basename sql_help" ); } if (IsNewer('src/common/kwlist_d.h', 'src/include/parser/kwlist.h')) { print "Generating kwlist_d.h...\n"; system( 'perl -I src/tools src/tools/gen_keywordlist.pl --extern -o src/common src/include/parser/kwlist.h' ); } if (IsNewer( 'src/pl/plpgsql/src/pl_reserved_kwlist_d.h', 'src/pl/plpgsql/src/pl_reserved_kwlist.h') || IsNewer( 'src/pl/plpgsql/src/pl_unreserved_kwlist_d.h', 'src/pl/plpgsql/src/pl_unreserved_kwlist.h')) { print "Generating pl_reserved_kwlist_d.h and pl_unreserved_kwlist_d.h...\n"; chdir('src/pl/plpgsql/src'); system( 'perl -I ../../../tools ../../../tools/gen_keywordlist.pl --varname ReservedPLKeywords pl_reserved_kwlist.h' ); system( 'perl -I ../../../tools ../../../tools/gen_keywordlist.pl --varname UnreservedPLKeywords pl_unreserved_kwlist.h' ); chdir('../../../..'); } if (IsNewer( 'src/interfaces/ecpg/preproc/c_kwlist_d.h', 'src/interfaces/ecpg/preproc/c_kwlist.h') || IsNewer( 'src/interfaces/ecpg/preproc/ecpg_kwlist_d.h', 'src/interfaces/ecpg/preproc/ecpg_kwlist.h')) { print "Generating c_kwlist_d.h and ecpg_kwlist_d.h...\n"; chdir('src/interfaces/ecpg/preproc'); system( 'perl -I ../../../tools ../../../tools/gen_keywordlist.pl --varname ScanCKeywords --no-case-fold c_kwlist.h' ); system( 'perl -I ../../../tools ../../../tools/gen_keywordlist.pl --varname ScanECPGKeywords ecpg_kwlist.h' ); chdir('../../../..'); } if (IsNewer( 'src/interfaces/ecpg/preproc/preproc.y', 'src/backend/parser/gram.y')) { print "Generating preproc.y...\n"; my $ecpg = 'src/interfaces/ecpg'; system( "perl $ecpg/preproc/parse.pl --srcdir $ecpg/preproc --parser src/backend/parser/gram.y --output $ecpg/preproc/preproc.y" ); } unless (-f "src/port/pg_config_paths.h") { print "Generating pg_config_paths.h...\n"; open(my $o, '>', 'src/port/pg_config_paths.h') || confess "Could not open pg_config_paths.h"; print $o <', 'bki-stamp') || confess "Could not touch bki-stamp"; close($f); chdir('../../..'); } if (IsNewer( 'src/include/catalog/header-stamp', 'src/backend/catalog/bki-stamp')) { # Copy generated headers to include directory. opendir(my $dh, 'src/backend/catalog/') || die "Can't opendir src/backend/catalog/ $!"; my @def_headers = grep { /pg_\w+_d\.h$/ } readdir($dh); closedir $dh; foreach my $def_header (@def_headers) { copyFile( "src/backend/catalog/$def_header", "src/include/catalog/$def_header"); } copyFile( 'src/backend/catalog/schemapg.h', 'src/include/catalog/schemapg.h'); copyFile( 'src/backend/catalog/system_fk_info.h', 'src/include/catalog/system_fk_info.h'); open(my $chs, '>', 'src/include/catalog/header-stamp') || confess "Could not touch header-stamp"; close($chs); } my $nmf = Project::read_file('src/backend/nodes/Makefile'); $nmf =~ s{\\\r?\n}{}g; $nmf =~ /^node_headers\s*:?=(.*)$/gm || croak "Could not find node_headers in Makefile\n"; my @node_headers = split /\s+/, $1; @node_headers = grep { $_ ne '' } @node_headers; my @node_files = map { "src/include/$_" } @node_headers; my $need_node_support = 0; foreach my $nodefile (@node_files) { if (IsNewer('src/backend/nodes/node-support-stamp', $nodefile)) { $need_node_support = 1; last; } } $need_node_support = 1 if IsNewer( 'src/backend/nodes/node-support-stamp', 'src/backend/nodes/gen_node_support.pl'); if ($need_node_support) { system( "perl src/backend/nodes/gen_node_support.pl --outdir src/backend/nodes @node_files" ); open(my $f, '>', 'src/backend/nodes/node-support-stamp') || confess "Could not touch node-support-stamp"; close($f); } if (IsNewer( 'src/include/nodes/nodetags.h', 'src/backend/nodes/nodetags.h')) { copyFile('src/backend/nodes/nodetags.h', 'src/include/nodes/nodetags.h'); } open(my $o, '>', "doc/src/sgml/version.sgml") || croak "Could not write to version.sgml\n"; print $o < EOF close($o); return; } # Read lines from input file and substitute symbols using the same # logic that config.status uses. There should be one call of this for # each AC_CONFIG_HEADERS call in configure.ac. # # If the "required" argument is true, we also keep track which of our # defines have been found and error out if any are left unused at the # end. That way we avoid accumulating defines in this file that are # no longer used by configure. sub GenerateConfigHeader { my ($self, $config_header, $defines, $required) = @_; my $config_header_in = $config_header . '.in'; if ( IsNewer($config_header, $config_header_in) || IsNewer($config_header, __FILE__)) { my %defines_copy = %$defines; open(my $i, '<', $config_header_in) || confess "Could not open $config_header_in\n"; open(my $o, '>', $config_header) || confess "Could not write to $config_header\n"; print $o "/* $config_header. Generated from $config_header_in by src/tools/msvc/Solution.pm. */\n"; while (<$i>) { if (m/^#(\s*)undef\s+(\w+)/) { my $ws = $1; my $macro = $2; if (exists $defines->{$macro}) { if (defined $defines->{$macro}) { print $o "#${ws}define $macro ", $defines->{$macro}, "\n"; } else { print $o "/* #${ws}undef $macro */\n"; } delete $defines_copy{$macro}; } else { croak "undefined symbol: $macro at $config_header line $."; } } else { print $o $_; } } close($o); close($i); if ($required && scalar(keys %defines_copy) > 0) { croak "unused defines: " . join(' ', keys %defines_copy); } } } sub GenerateDefFile { my ($self, $deffile, $txtfile, $libname) = @_; if (IsNewer($deffile, $txtfile)) { print "Generating $deffile...\n"; open(my $if, '<', $txtfile) || confess("Could not open $txtfile\n"); open(my $of, '>', $deffile) || confess("Could not open $deffile\n"); print $of "LIBRARY $libname\nEXPORTS\n"; while (<$if>) { next if (/^#/); next if (/^\s*$/); my ($f, $o) = split; print $of " $f @ $o\n"; } close($of); close($if); } return; } sub AddProject { my ($self, $name, $type, $folder, $initialdir) = @_; my $proj = VSObjectFactory::CreateProject($self->{vcver}, $name, $type, $self); push @{ $self->{projects}->{$folder} }, $proj; $proj->AddDir($initialdir) if ($initialdir); if ($self->{options}->{zlib}) { $proj->AddIncludeDir($self->{options}->{zlib} . '\include'); $proj->AddLibrary($self->{options}->{zlib} . '\lib\zdll.lib'); } if ($self->{options}->{openssl}) { $proj->AddIncludeDir($self->{options}->{openssl} . '\include'); my ($digit1, $digit2, $digit3) = $self->GetOpenSSLVersion(); # Starting at version 1.1.0 the OpenSSL installers have # changed their library names from: # - libeay to libcrypto # - ssleay to libssl if ( ($digit1 >= '3' && $digit2 >= '0' && $digit3 >= '0') || ($digit1 >= '1' && $digit2 >= '1' && $digit3 >= '0')) { my $dbgsuffix; my $libsslpath; my $libcryptopath; # The format name of the libraries is slightly # different between the Win32 and Win64 platform, so # adapt. if (-e "$self->{options}->{openssl}/lib/VC/sslcrypto32MD.lib") { # Win32 here, with a debugging library set. $dbgsuffix = 1; $libsslpath = '\lib\VC\libssl32.lib'; $libcryptopath = '\lib\VC\libcrypto32.lib'; } elsif (-e "$self->{options}->{openssl}/lib/VC/sslcrypto64MD.lib") { # Win64 here, with a debugging library set. $dbgsuffix = 1; $libsslpath = '\lib\VC\libssl64.lib'; $libcryptopath = '\lib\VC\libcrypto64.lib'; } else { # On both Win32 and Win64 the same library # names are used without a debugging context. $dbgsuffix = 0; $libsslpath = '\lib\libssl.lib'; $libcryptopath = '\lib\libcrypto.lib'; } $proj->AddLibrary($self->{options}->{openssl} . $libsslpath, $dbgsuffix); $proj->AddLibrary($self->{options}->{openssl} . $libcryptopath, $dbgsuffix); } else { # Choose which set of libraries to use depending on if # debugging libraries are in place in the installer. if (-e "$self->{options}->{openssl}/lib/VC/ssleay32MD.lib") { $proj->AddLibrary( $self->{options}->{openssl} . '\lib\VC\ssleay32.lib', 1); $proj->AddLibrary( $self->{options}->{openssl} . '\lib\VC\libeay32.lib', 1); } else { # We don't expect the config-specific library # to be here, so don't ask for it in last # parameter. $proj->AddLibrary( $self->{options}->{openssl} . '\lib\ssleay32.lib', 0); $proj->AddLibrary( $self->{options}->{openssl} . '\lib\libeay32.lib', 0); } } } if ($self->{options}->{nls}) { $proj->AddIncludeDir($self->{options}->{nls} . '\include'); $proj->AddLibrary($self->{options}->{nls} . '\lib\libintl.lib'); } if ($self->{options}->{gss}) { $proj->AddIncludeDir($self->{options}->{gss} . '\include'); $proj->AddIncludeDir($self->{options}->{gss} . '\include\krb5'); if ($self->{platform} eq 'Win32') { $proj->AddLibrary( $self->{options}->{gss} . '\lib\i386\krb5_32.lib'); $proj->AddLibrary( $self->{options}->{gss} . '\lib\i386\comerr32.lib'); $proj->AddLibrary( $self->{options}->{gss} . '\lib\i386\gssapi32.lib'); } else { $proj->AddLibrary( $self->{options}->{gss} . '\lib\amd64\krb5_64.lib'); $proj->AddLibrary( $self->{options}->{gss} . '\lib\amd64\comerr64.lib'); $proj->AddLibrary( $self->{options}->{gss} . '\lib\amd64\gssapi64.lib'); } } if ($self->{options}->{iconv}) { $proj->AddIncludeDir($self->{options}->{iconv} . '\include'); $proj->AddLibrary($self->{options}->{iconv} . '\lib\iconv.lib'); } if ($self->{options}->{icu}) { $proj->AddIncludeDir($self->{options}->{icu} . '\include'); if ($self->{platform} eq 'Win32') { $proj->AddLibrary($self->{options}->{icu} . '\lib\icuin.lib'); $proj->AddLibrary($self->{options}->{icu} . '\lib\icuuc.lib'); $proj->AddLibrary($self->{options}->{icu} . '\lib\icudt.lib'); } else { $proj->AddLibrary($self->{options}->{icu} . '\lib64\icuin.lib'); $proj->AddLibrary($self->{options}->{icu} . '\lib64\icuuc.lib'); $proj->AddLibrary($self->{options}->{icu} . '\lib64\icudt.lib'); } } if ($self->{options}->{xml}) { $proj->AddIncludeDir($self->{options}->{xml} . '\include'); $proj->AddIncludeDir($self->{options}->{xml} . '\include\libxml2'); $proj->AddLibrary($self->{options}->{xml} . '\lib\libxml2.lib'); } if ($self->{options}->{xslt}) { $proj->AddIncludeDir($self->{options}->{xslt} . '\include'); $proj->AddLibrary($self->{options}->{xslt} . '\lib\libxslt.lib'); } if ($self->{options}->{lz4}) { $proj->AddIncludeDir($self->{options}->{lz4} . '\include'); $proj->AddLibrary($self->{options}->{lz4} . '\lib\liblz4.lib'); } if ($self->{options}->{zstd}) { $proj->AddIncludeDir($self->{options}->{zstd} . '\include'); $proj->AddLibrary($self->{options}->{zstd} . '\lib\libzstd.lib'); } if ($self->{options}->{uuid}) { $proj->AddIncludeDir($self->{options}->{uuid} . '\include'); $proj->AddLibrary($self->{options}->{uuid} . '\lib\uuid.lib'); } return $proj; } sub Save { my ($self) = @_; my %flduid; $self->GenerateFiles(); foreach my $fld (keys %{ $self->{projects} }) { foreach my $proj (@{ $self->{projects}->{$fld} }) { $proj->Save(); } } open(my $sln, '>', "pgsql.sln") || croak "Could not write to pgsql.sln\n"; print $sln <{solutionFileVersion} # $self->{visualStudioName} EOF print $sln $self->GetAdditionalHeaders(); foreach my $fld (keys %{ $self->{projects} }) { foreach my $proj (@{ $self->{projects}->{$fld} }) { print $sln <{name}$proj->{filenameExtension}", "$proj->{guid}" EndProject EOF } if ($fld ne "") { $flduid{$fld} = $^O eq "MSWin32" ? Win32::GuidGen() : 'FAKE'; print $sln <{platform}= Debug|$self->{platform} Release|$self->{platform} = Release|$self->{platform} EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution EOF foreach my $fld (keys %{ $self->{projects} }) { foreach my $proj (@{ $self->{projects}->{$fld} }) { print $sln <{guid}.Debug|$self->{platform}.ActiveCfg = Debug|$self->{platform} $proj->{guid}.Debug|$self->{platform}.Build.0 = Debug|$self->{platform} $proj->{guid}.Release|$self->{platform}.ActiveCfg = Release|$self->{platform} $proj->{guid}.Release|$self->{platform}.Build.0 = Release|$self->{platform} EOF } } print $sln <{projects} }) { next if ($fld eq ""); foreach my $proj (@{ $self->{projects}->{$fld} }) { print $sln "\t\t$proj->{guid} = $flduid{$fld}\n"; } } print $sln <{options}->{asserts}); $cfg .= ' --enable-nls' if ($self->{options}->{nls}); $cfg .= ' --enable-tap-tests' if ($self->{options}->{tap_tests}); $cfg .= ' --with-ldap' if ($self->{options}->{ldap}); $cfg .= ' --without-zlib' unless ($self->{options}->{zlib}); $cfg .= ' --with-extra-version' if ($self->{options}->{extraver}); $cfg .= ' --with-ssl=openssl' if ($self->{options}->{openssl}); $cfg .= ' --with-uuid' if ($self->{options}->{uuid}); $cfg .= ' --with-libxml' if ($self->{options}->{xml}); $cfg .= ' --with-libxslt' if ($self->{options}->{xslt}); $cfg .= ' --with-lz4' if ($self->{options}->{lz4}); $cfg .= ' --with-zstd' if ($self->{options}->{zstd}); $cfg .= ' --with-gssapi' if ($self->{options}->{gss}); $cfg .= ' --with-icu' if ($self->{options}->{icu}); $cfg .= ' --with-tcl' if ($self->{options}->{tcl}); $cfg .= ' --with-perl' if ($self->{options}->{perl}); $cfg .= ' --with-python' if ($self->{options}->{python}); my $port = $self->{options}->{'--with-pgport'}; $cfg .= " --with-pgport=$port" if defined($port); return $cfg; } package VS2015Solution; # # Package that encapsulates a Visual Studio 2015 solution file # use Carp; use strict; use warnings; use base qw(Solution); no warnings qw(redefine); ## no critic sub new { my $classname = shift; my $self = $classname->SUPER::_new(@_); bless($self, $classname); $self->{solutionFileVersion} = '12.00'; $self->{vcver} = '14.00'; $self->{visualStudioName} = 'Visual Studio 2015'; $self->{VisualStudioVersion} = '14.0.24730.2'; $self->{MinimumVisualStudioVersion} = '10.0.40219.1'; return $self; } package VS2017Solution; # # Package that encapsulates a Visual Studio 2017 solution file # use Carp; use strict; use warnings; use base qw(Solution); no warnings qw(redefine); ## no critic sub new { my $classname = shift; my $self = $classname->SUPER::_new(@_); bless($self, $classname); $self->{solutionFileVersion} = '12.00'; $self->{vcver} = '15.00'; $self->{visualStudioName} = 'Visual Studio 2017'; $self->{VisualStudioVersion} = '15.0.26730.3'; $self->{MinimumVisualStudioVersion} = '10.0.40219.1'; return $self; } package VS2019Solution; # # Package that encapsulates a Visual Studio 2019 solution file # use Carp; use strict; use warnings; use base qw(Solution); no warnings qw(redefine); ## no critic sub new { my $classname = shift; my $self = $classname->SUPER::_new(@_); bless($self, $classname); $self->{solutionFileVersion} = '12.00'; $self->{vcver} = '16.00'; $self->{visualStudioName} = 'Visual Studio 2019'; $self->{VisualStudioVersion} = '16.0.28729.10'; $self->{MinimumVisualStudioVersion} = '10.0.40219.1'; return $self; } package VS2022Solution; # # Package that encapsulates a Visual Studio 2022 solution file # use Carp; use strict; use warnings; use base qw(Solution); no warnings qw(redefine); ## no critic sub new { my $classname = shift; my $self = $classname->SUPER::_new(@_); bless($self, $classname); $self->{solutionFileVersion} = '12.00'; $self->{vcver} = '17.00'; $self->{visualStudioName} = 'Visual Studio 2022'; $self->{VisualStudioVersion} = '17.0.31903.59'; $self->{MinimumVisualStudioVersion} = '10.0.40219.1'; return $self; } sub GetAdditionalHeaders { my ($self, $f) = @_; return qq|VisualStudioVersion = $self->{VisualStudioVersion} MinimumVisualStudioVersion = $self->{MinimumVisualStudioVersion} |; } 1;