blob: 259b14e3ff5c7b5a8521bf3cc7e9af73268432b8 [file] [log] [blame]
package Mkvcbuild;
#
# Package that generates build files for msvc build
#
# src/tools/msvc/Mkvcbuild.pm
#
use Carp;
use Win32;
use strict;
use warnings;
use Project;
use Solution;
use Cwd;
use File::Copy;
use Exporter;
our (@ISA, @EXPORT_OK);
@ISA = qw(Exporter);
@EXPORT_OK = qw(Mkvcbuild);
my $solution;
my $libpgport;
my $postgres;
my $libpq;
my $contrib_defines = {'refint' => 'REFINT_VERBOSE'};
# for GPDB, I've added xlogdump
my @contrib_uselibpq = ('dblink', 'oid2name', 'pgbench', 'vacuumlo', 'xlogdump');
my @contrib_uselibpgport = ('oid2name', 'pgbench', 'pg_standby', 'vacuumlo', 'xlogdump');
my $contrib_extralibs = {'pgbench' => ['wsock32.lib']};
my $contrib_extraincludes = {'tsearch2' => ['contrib/tsearch2'], 'dblink' => ['src/backend']};
my $contrib_extrasource = {
'cube' => ['cubescan.l','cubeparse.y'],
'seg' => ['segscan.l','segparse.y']
};
my @contrib_excludes = ('pgcrypto','intagg','uuid-ossp','bufferedtest', 'varblocktest', 'hstore', 'gp_filedump', 'tsearch2', 'dblink', 'xlogdump','xlogviewer', 'changetrackingdump','xml2','adminpack');
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');
$solution = new Solution($config);
our @pgportfiles = qw(
chklocale.c crypt.c fseeko.c getrusage.c inet_aton.c random.c srandom.c
getaddrinfo.c gettimeofday.c kill.c open.c rand.c
snprintf.c strlcat.c strlcpy.c copydir.c dirmod.c exec.c noblock.c path.c pipe.c
pgsleep.c pgstrcasecmp.c qsort.c qsort_arg.c sprompt.c thread.c
getopt.c getopt_long.c dirent.c rint.c win32env.c win32error.c glob.c);
$libpgport = $solution->AddProject('libpgport','lib','misc');
$libpgport->AddDefine('FRONTEND');
$libpgport->AddFiles('src\port',@pgportfiles);
$postgres = $solution->AddProject('postgres','exe','','src\backend');
$postgres->AddIncludeDir('src\backend');
$postgres->AddIncludeDir('src\backend\gp_libpq_fe');
$postgres->AddIncludeDir('src\port');
$postgres->AddDir('src\backend\port\win32');
$postgres->AddFile('src\backend\utils\fmgrtab.c');
$postgres->ReplaceFile('src\backend\port\dynloader.c','src\backend\port\dynloader\win32.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->AddDir('src\timezone');
$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->AddDefine('BUILDING_DLL');
$postgres->AddLibrary('wsock32.lib');
$postgres->AddLibrary('ws2_32.lib');
$postgres->AddLibrary('secur32.lib');
$postgres->AddLibrary('wldap32.lib') if ($solution->{options}->{ldap});
# GPDB actually requires pthreads, can't run without.
$postgres->AddLibrary($config->{'pthread'} . '\pthreadVC2.lib');
$postgres->FullExportDLL('postgres.lib');
my $plpgsql = $solution->AddProject('plpgsql','dll','PLs','src\pl\plpgsql\src');
$plpgsql->AddFiles('src\pl\plpgsql\src','scan.l','gram.y');
$plpgsql->AddReference($postgres);
$plpgsql->AddLibrary('wsock32.lib');
$plpgsql->AddLibrary('ws2_32.lib');
if ($solution->{options}->{python})
{
# Attempt to get python version and location. Assume python.exe in specified dir.
open(P,
$solution->{options}->{python}
. "\\python -c \"import sys;print(sys.prefix);print(str(sys.version_info[0])+str(sys.version_info[1]))\" |"
) || die "Could not query for python version!\n";
my $pyprefix = <P>;
chomp($pyprefix);
my $pyver = <P>;
chomp($pyver);
close(P);
# 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 $plpython = $solution->AddProject('plpython','dll','PLs','src\pl\plpython');
$plpython->AddIncludeDir($pyprefix . '\include');
$plpython->AddLibrary($pyprefix . "\\Libs\\python$pyver.lib");
$plpython->AddReference($postgres);
}
if ($solution->{options}->{tcl})
{
my $pltcl = $solution->AddProject('pltcl','dll','PLs','src\pl\tcl');
$pltcl->AddIncludeDir($solution->{options}->{tcl} . '\include');
$pltcl->AddReference($postgres);
if (-e $solution->{options}->{tcl} . '\lib\tcl85.lib')
{
$pltcl->AddLibrary($solution->{options}->{tcl} . '\lib\tcl85.lib');
}
else
{
$pltcl->AddLibrary($solution->{options}->{tcl} . '\lib\tcl84.lib');
}
}
$libpq = $solution->AddProject('libpq','dll','interfaces','src\interfaces\libpq');
$libpq->AddDefine('FRONTEND');
$libpq->AddDefine('UNSAFE_STAT_OK');
$libpq->AddIncludeDir('src\port');
$libpq->AddLibrary('wsock32.lib');
$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->ReplaceFile('src\interfaces\libpq\libpqrc.c','src\interfaces\libpq\libpq.rc');
$libpq->AddReference($libpgport);
my $pgtypes =
$solution->AddProject('libpgtypes','dll','interfaces','src\interfaces\ecpg\pgtypeslib');
$pgtypes->AddDefine('FRONTEND');
$pgtypes->AddReference($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('wsock32.lib');
$libecpg->AddLibrary($config->{'pthread'} . '\pthreadVC2.lib');
$libecpg->AddReference($libpq,$pgtypes,$libpgport);
my $libecpgcompat =
$solution->AddProject('libecpg_compat','dll','interfaces','src\interfaces\ecpg\compatlib');
$libecpgcompat->AddIncludeDir('src\interfaces\ecpg\include');
$libecpgcompat->AddIncludeDir('src\interfaces\libpq');
$libecpgcompat->UseDef('src\interfaces\ecpg\compatlib\compatlib.def');
$libecpgcompat->AddReference($pgtypes,$libecpg,$libpgport);
my $ecpg = $solution->AddProject('ecpg','exe','interfaces','src\interfaces\ecpg\preproc');
$ecpg->AddIncludeDir('src\interfaces\ecpg\include');
$ecpg->AddIncludeDir('src\interfaces\libpq');
$ecpg->AddPrefixInclude('src\interfaces\ecpg\preproc');
$ecpg->AddFiles('src\interfaces\ecpg\preproc','pgc.l','preproc.y');
$ecpg->AddDefine('MAJOR_VERSION=4');
$ecpg->AddDefine('MINOR_VERSION=2');
$ecpg->AddDefine('PATCHLEVEL=1');
$ecpg->AddDefine('ECPG_COMPILE');
$ecpg->AddReference($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->AddDefine('FRONTEND');
#$pgregress_ecpg->AddReference($libpgport);
# src/bin
my $initdb = AddSimpleFrontend('initdb');
$initdb->AddIncludeDir('src\interfaces\libpq');
$initdb->AddDefine('FRONTEND');
$initdb->AddLibrary('wsock32.lib');
$initdb->AddLibrary('ws2_32.lib');
my $pgconfig = AddSimpleFrontend('pg_config');
my $pgcontrol = AddSimpleFrontend('pg_controldata');
my $pgctl = AddSimpleFrontend('pg_ctl', 1);
my $pgreset = AddSimpleFrontend('pg_resetxlog');
my $psql = AddSimpleFrontend('psql', 1);
$psql->AddIncludeDir('src\bin\pg_dump');
$psql->AddIncludeDir('src\backend');
if ($solution->{options}->{readline})
{
$psql->AddIncludeDir($solution->{options}->{readline} . '\include');
$psql->AddLibrary($solution->{options}->{readline} . '\lib\readline.lib');
$psql->AddLibrary($solution->{options}->{readline} . '\lib\history.lib');
}
$psql->AddFile('src\bin\psql\psqlscan.l');
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->AddFile('src\bin\pg_dump\keywords.c');
$pgdump->AddFile('src\backend\parser\kwlookup.c');
my $pgdumpall = AddSimpleFrontend('pg_dump', 1);
$pgdumpall->{name} = 'pg_dumpall';
$pgdumpall->AddIncludeDir('src\backend');
$pgdumpall->AddFile('src\bin\pg_dump\pg_dumpall.c');
$pgdumpall->AddFile('src\bin\pg_dump\keywords.c');
$pgdumpall->AddFile('src\backend\parser\kwlookup.c');
my $pgrestore = AddSimpleFrontend('pg_dump', 1);
$pgrestore->{name} = 'pg_restore';
$pgrestore->AddIncludeDir('src\backend');
$pgrestore->AddFile('src\bin\pg_dump\pg_restore.c');
$pgrestore->AddFile('src\bin\pg_dump\keywords.c');
$pgrestore->AddFile('src\backend\parser\kwlookup.c');
my $zic = $solution->AddProject('zic','exe','utils');
$zic->AddFiles('src\timezone','zic.c','ialloc.c','scheck.c','localtime.c');
$zic->AddReference($libpgport);
if ($solution->{options}->{xml})
{
$contrib_extraincludes->{'pgxml'} = [
$solution->{options}->{xml} . '\include',
$solution->{options}->{xslt} . '\include',
$solution->{options}->{iconv} . '\include'
];
$contrib_extralibs->{'pgxml'} = [
$solution->{options}->{xml} . '\lib\libxml2.lib',
$solution->{options}->{xslt} . '\lib\libxslt.lib'
];
}
else
{
push @contrib_excludes,'xml2';
}
if (!$solution->{options}->{openssl})
{
push @contrib_excludes,'sslinfo';
}
if ($solution->{options}->{uuid})
{
$contrib_extraincludes->{'uuid-ossp'} = [ $solution->{options}->{uuid} . '\include' ];
$contrib_extralibs->{'uuid-ossp'} = [ $solution->{options}->{uuid} . '\lib\uuid.lib' ];
}
else
{
push @contrib_excludes,'uuid-ossp';
}
# Pgcrypto makefile too complex to parse....
my $pgcrypto = $solution->AddProject('pgcrypto','dll','crypto');
$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', 'md5.c','sha1.c','sha2.c',
'internal.c','internal-sha2.c','blf.c','rijndael.c',
'fortuna.c','random.c','pgp-mpi-internal.c','imath.c'
);
}
$pgcrypto->AddReference($postgres);
$pgcrypto->AddLibrary('wsock32.lib');
my $mf = Project::read_file('contrib/pgcrypto/Makefile');
GenerateContribSqlFiles('pgcrypto', $mf);
my $D;
opendir($D, 'contrib') || croak "Could not opendir on contrib!\n";
while (my $d = readdir($D))
{
next if ($d =~ /^\./);
next unless (-f "contrib/$d/Makefile");
next if (grep {/^$d\s*$/} @contrib_excludes);
AddContrib($d);
}
closedir($D);
$mf = Project::read_file('src\backend\utils\mb\conversion_procs\Makefile');
$mf =~ s{\\s*[\r\n]+}{}mg;
$mf =~ m{DIRS\s*=\s*(.*)$}m || die 'Could not match in conversion makefile' . "\n";
foreach my $sub (split /\s+/,$1)
{
my $mf = Project::read_file('src\backend\utils\mb\conversion_procs\\' . $sub . '\Makefile');
my $p = $solution->AddProject($sub, 'dll', 'conversion procs');
$p->AddFile('src\backend\utils\mb\conversion_procs\\' . $sub . '\\' . $sub . '.c');
if ($mf =~ m{^SRCS\s*\+=\s*(.*)\s*$}m)
{
$p->AddFile('src\backend\utils\mb\conversion_procs\\' . $sub . '\\' . $1);
}
$p->AddReference($postgres);
}
$mf = Project::read_file('src\bin\scripts\Makefile');
$mf =~ s{\\s*[\r\n]+}{}mg;
$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 eq 'keywords.c')
{
$proj->AddFile('src\backend\parser\keywords.c');
$proj->AddIncludeDir('src\backend');
}
elsif ($f eq '$(top_builddir)/src/backend/parser/keywords.c')
{
$proj->AddFile('src\backend\parser\keywords.c');
$proj->AddIncludeDir('src\backend');
}
elsif ($f eq 'kwlookup.c')
{
$proj->AddFile('src\backend\parser\kwlookup.c');
}
elsif ($f eq 'dumputils.c')
{
$proj->AddFile('src\bin\pg_dump\dumputils.c');
}
elsif ($f =~ /print\.c$/)
{ # Also catches mbprint.c
$proj->AddFile('src\bin\psql\\' . $f);
}
else
{
$proj->AddFile('src\bin\scripts\\' . $f);
}
}
$proj->AddIncludeDir('src\interfaces\libpq');
$proj->AddIncludeDir('src\bin\pg_dump');
$proj->AddIncludeDir('src\bin\psql');
$proj->AddReference($libpq,$libpgport);
$proj->AddResourceFile('src\bin\scripts','PostgreSQL Utility');
}
# Regression DLL and EXE
my $regress = $solution->AddProject('regress','dll','misc');
$regress->AddFile('src\test\regress\regress.c');
$regress->AddLibrary('wsock32.lib');
$regress->AddLibrary('ws2_32.lib');
$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->AddDefine('FRONTEND');
$pgregress->AddLibrary('wsock32.lib');
$pgregress->AddLibrary('ws2_32.lib');
$pgregress->AddReference($libpgport);
$solution->Save();
# Upgrade the files to work with Visual Studio newer than 2005
system("devenv /upgrade pgsql.sln");
}
#####################
# 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->AddDefine('FRONTEND');
$p->AddReference($libpgport);
if ($uselibpq)
{
$p->AddIncludeDir('src\interfaces\libpq');
$p->AddReference($libpq);
}
return $p;
}
# Add a simple contrib project
sub AddContrib
{
my $n = shift;
if (length($n) > 1 && (substr($n,length($n)-1) eq "\r"))
{
chop $n;
}
my $mf = Project::read_file('contrib\\' . $n . '\Makefile');
if ($mf =~ m/^MODULE_big\s*=\s*(.*)\s*$/mg)
{
my $dn = $1;
if (length($dn) > 1 && (substr($dn,length($dn)-1) eq "\r"))
{
chop $dn;
}
$mf =~ s{\\\s*[\r\n]+}{}mg;
$dn =~ s{\\\s*[\r\n]+}{}mg;
if (length($dn) > 1 && (substr($dn,length($dn)-1) eq "\r"))
{
chop $dn;
}
my $proj = $solution->AddProject($dn, 'dll', 'contrib');
$mf =~ /^OBJS\s*=\s*(.*)$/gm || croak "Could not find objects in MODULE_big for $n\n";
my $objs = $1;
while ($objs =~ /\b([\w-]+\.o)\b/g)
{
my $o = $1;
$o =~ s/\.o$/.c/;
$proj->AddFile('contrib\\' . $n . '\\' . $o);
}
$proj->AddReference($postgres);
if ($mf =~ /^SUBDIRS\s*:?=\s*(.*)\s*$/mg)
{
foreach my $d (split /\s+/, $1)
{
my $mf2 = Project::read_file('contrib\\' . $n . '\\' . $d . '\Makefile');
$mf2 =~ s{\\\s*[\r\n]+}{}mg;
$mf2 =~ /^SUBOBJS\s*=\s*(.*)\s*$/gm
|| croak "Could not find objects in MODULE_big for $n, subdir $d\n";
$objs = $1;
while ($objs =~ /\b([\w-]+\.o)\b/g)
{
my $o = $1;
$o =~ s/\.o$/.c/;
$proj->AddFile('contrib\\' . $n . '\\' . $d . '\\' . $o);
}
}
}
AdjustContribProj($proj);
}
elsif ($mf =~ m/^MODULES\s*=\s*(.*)\s*$/mg)
{
foreach my $mod (split /\s+/, $1)
{
my $proj = $solution->AddProject($mod, 'dll', 'contrib');
$proj->AddFile('contrib\\' . $n . '\\' . $mod . '.c');
$proj->AddReference($postgres);
AdjustContribProj($proj);
}
}
elsif ($mf =~ m/^PROGRAM\s*=\s*(.*)\s*$/mg)
{
my $proj = $solution->AddProject($1, 'exe', 'contrib');
$mf =~ /^OBJS\s*=\s*(.*)$/gm || croak "Could not find objects in MODULE_big for $n\n";
my $objs = $1;
while ($objs =~ /\b([\w-]+\.o)\b/g)
{
my $o = $1;
$o =~ s/\.o$/.c/;
$proj->AddFile('contrib\\' . $n . '\\' . $o);
}
AdjustContribProj($proj);
}
else
{
croak "Could not determine contrib module type for $n\n";
}
# Are there any output data files to build?
GenerateContribSqlFiles($n, $mf);
}
sub GenerateContribSqlFiles
{
my $n = shift;
my $mf = shift;
if ($mf =~ m/^DATA_built\s*=\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);
}
# Special case for contrib/spi
$l = "autoinc.sql insert_username.sql moddatetime.sql refint.sql timetravel.sql"
if ($n eq 'spi');
foreach my $d (split /\s+/, $l)
{
next unless (length($d) > 0);
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$//;
if ($mf =~ /^MODULE_big\s*=\s*(.*)$/m) { $dn = $1 }
$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);
}
}
}
}
sub AdjustContribProj
{
my $proj = shift;
my $n = $proj->{name};
if ($contrib_defines->{$n})
{
foreach my $d ($contrib_defines->{$n})
{
$proj->AddDefine($d);
}
}
if (grep {/^$n\s*$/} @contrib_uselibpq)
{
$proj->AddIncludeDir('src\interfaces\libpq');
$proj->AddReference($libpq);
}
if (grep {/^$n\s*$/} @contrib_uselibpgport)
{
$proj->AddReference($libpgport);
}
if ($contrib_extralibs->{$n})
{
foreach my $l (@{$contrib_extralibs->{$n}})
{
$proj->AddLibrary($l);
}
}
if ($contrib_extraincludes->{$n})
{
foreach my $i (@{$contrib_extraincludes->{$n}})
{
$proj->AddIncludeDir($i);
}
}
if ($contrib_extrasource->{$n})
{
$proj->AddFiles('contrib\\' . $n, @{$contrib_extrasource->{$n}});
}
$proj->AddLibrary('wsock32.lib');
$proj->AddLibrary('ws2_32.lib');
}
1;