]> granicus.if.org Git - postgresql/blobdiff - src/tools/msvc/Mkvcbuild.pm
Unbreak MSVC builds for pg_archivecleanup by linking with libpgport
[postgresql] / src / tools / msvc / Mkvcbuild.pm
index 9837cc77139c100964b75e29ea2ff824dcbc09c8..9ba8bd48c204c41757a9afd321fea470d9990f89 100644 (file)
@@ -1,8 +1,9 @@
 package Mkvcbuild;
+
 #
 # Package that generates build files for msvc build
 #
-# $PostgreSQL: pgsql/src/tools/msvc/Mkvcbuild.pm,v 1.3 2007/03/19 09:34:09 mha Exp $
+# $PostgreSQL: pgsql/src/tools/msvc/Mkvcbuild.pm,v 1.58 2010/06/15 12:48:36 adunstan Exp $
 #
 use Carp;
 use Win32;
@@ -10,6 +11,8 @@ use strict;
 use warnings;
 use Project;
 use Solution;
+use Cwd;
+use File::Copy;
 
 use Exporter;
 our (@ISA, @EXPORT_OK);
@@ -22,15 +25,17 @@ my $postgres;
 my $libpq;
 
 my $contrib_defines = {'refint' => 'REFINT_VERBOSE'};
-my @contrib_uselibpq = ('dblink', 'oid2name', 'pgbench', 'vacuumlo');
-my @contrib_uselibpgport = ('oid2name', 'pgbench', 'pg_standby', 'vacuumlo');
+my @contrib_uselibpq = ('dblink', 'oid2name', 'pgbench', 'pg_upgrade', 
+                                               'vacuumlo');
+my @contrib_uselibpgport = ('oid2name', 'pgbench', 'pg_standby', 
+                                                       'pg_archivecleanup', 'pg_upgrade', 'vacuumlo');
 my $contrib_extralibs = {'pgbench' => ['wsock32.lib']};
-my $contrib_extraincludes = {'tsearch2' => ['contrib/tsearch2']};
+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');
+my @contrib_excludes = ('pgcrypto','intagg');
 
 sub mkvcbuild
 {
@@ -42,11 +47,11 @@ sub mkvcbuild
     $solution = new Solution($config);
 
     our @pgportfiles = qw(
-      crypt.c fseeko.c getrusage.c inet_aton.c random.c srandom.c
-      unsetenv.c getaddrinfo.c gettimeofday.c kill.c open.c rand.c
+      chklocale.c crypt.c fseeko.c getrusage.c inet_aton.c random.c srandom.c
+      getaddrinfo.c gettimeofday.c kill.c open.c erand48.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 win32error.c);
+      getopt.c getopt_long.c dirent.c rint.c win32env.c win32error.c);
 
     $libpgport = $solution->AddProject('libpgport','lib','misc');
     $libpgport->AddDefine('FRONTEND');
@@ -58,52 +63,129 @@ sub mkvcbuild
     $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\sysv_shmem.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 ws2_32.lib');
+    $postgres->AddLibrary('wsock32.lib');
+    $postgres->AddLibrary('ws2_32.lib');
+    $postgres->AddLibrary('secur32.lib');
     $postgres->AddLibrary('wldap32.lib') if ($solution->{options}->{ldap});
     $postgres->FullExportDLL('postgres.lib');
 
+    my $snowball = $solution->AddProject('dict_snowball','dll','','src\backend\snowball');
+    $snowball->RelocateFiles(
+        'src\backend\snowball\libstemmer',
+        sub {
+            return shift !~ /dict_snowball.c$/;
+        }
+    );
+    $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','scan.l','gram.y');
+    $plpgsql->AddFiles('src\pl\plpgsql\src', 'gram.y');
     $plpgsql->AddReference($postgres);
 
     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->AddDefine('PLPERL_HAVE_UID_GID');
-        if (Solution::IsNewer('src\pl\plperl\SPI.c','src\pl\plperl\SPI.xs'))
+        foreach my $xs ('SPI.xs', 'Util.xs')
+        {
+            (my $xsc = $xs) =~ s/\.xs/.c/;
+            if (Solution::IsNewer("$plperlsrc$xsc","$plperlsrc$xs"))
+            {
+                print "Building $plperlsrc$xsc...\n";
+                system( $solution->{options}->{perl}
+                      . '/bin/perl '
+                      . $solution->{options}->{perl}
+                      . '/lib/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\SPI.c...' . "\n";
+            print 'Building src\pl\plperl\perlchunks.h ...' . "\n";
+            my $basedir = getcwd;
+            chdir 'src\pl\plperl';
             system( $solution->{options}->{perl}
                   . '/bin/perl '
-                  . $solution->{options}->{perl}
-                  . '/lib/ExtUtils/xsubpp -typemap '
-                  . $solution->{options}->{perl}
-                  . '/lib/ExtUtils/typemap src\pl\plperl\SPI.xs >src\pl\plperl\SPI.c');
-            if ((!(-f 'src\pl\plperl\SPI.c')) || -z 'src\pl\plperl\SPI.c')
+                  . '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\SPI.c'); # if zero size
-                die 'Failed to create SPI.c' . "\n";
+                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";
             }
         }
         $plperl->AddReference($postgres);
-        $plperl->AddLibrary($solution->{options}->{perl} . '\lib\CORE\perl58.lib');
+        my @perl_libs =
+          grep {/perl\d+.lib$/ }glob($solution->{options}->{perl} . '\lib\CORE\perl*.lib');
+        if (@perl_libs == 1)
+        {
+            $plperl->AddLibrary($perl_libs[0]);
+        }
+        else
+        {
+            die "could not identify perl library version";
+        }
     }
 
     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 actually works, 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($solution->{options}->{python} . '\include');
-        $solution->{options}->{python} =~ /\\Python(\d{2})/i
-          || croak "Could not determine python version from path";
-        $plpython->AddLibrary($solution->{options}->{python} . "\\Libs\\python$1.lib");
+        $plpython->AddIncludeDir($pyprefix . '\include');
+        $plpython->AddLibrary($pyprefix . "\\Libs\\python$pyver.lib");
         $plpython->AddReference($postgres);
     }
 
@@ -112,59 +194,82 @@ sub mkvcbuild
         my $pltcl = $solution->AddProject('pltcl','dll','PLs','src\pl\tcl');
         $pltcl->AddIncludeDir($solution->{options}->{tcl} . '\include');
         $pltcl->AddReference($postgres);
-        $pltcl->AddLibrary($solution->{options}->{tcl} . '\lib\tcl84.lib');
+        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 $libpqwalreceiver = $solution->AddProject('libpqwalreceiver', 'dll', '',
+        'src\backend\replication\libpqwalreceiver');
+    $libpqwalreceiver->AddIncludeDir('src\interfaces\libpq');
+    $libpqwalreceiver->AddReference($postgres,$libpq);
+
     my $pgtypes =
       $solution->AddProject('libpgtypes','dll','interfaces','src\interfaces\ecpg\pgtypeslib');
     $pgtypes->AddDefine('FRONTEND');
-    $pgtypes->AddReference($postgres,$libpgport);
+    $pgtypes->AddReference($libpgport);
+    $pgtypes->UseDef('src\interfaces\ecpg\pgtypeslib\pgtypeslib.def');
     $pgtypes->AddIncludeDir('src\interfaces\ecpg\include');
 
-    if ($config->{pthread})
-    {
-        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->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->AddReference($pgtypes,$libecpg);
-
-        my $ecpg = $solution->AddProject('ecpg','exe','interfaces','src\interfaces\ecpg\preproc');
-        $ecpg->AddIncludeDir('src\interfaces\ecpg\include');
-        $ecpg->AddIncludeDir('src\interfaces\libpq');
-        $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->AddReference($libpgport);
-    }
-    else
-    {
-        print "Not building ecpg due to lack of pthreads.\n";
-    }
+    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->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', 1);
+    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');
 
@@ -183,20 +288,30 @@ sub mkvcbuild
 
     my $psql = AddSimpleFrontend('psql', 1);
     $psql->AddIncludeDir('src\bin\pg_dump');
+    $psql->AddIncludeDir('src\backend');
     $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');
@@ -204,13 +319,13 @@ sub mkvcbuild
 
     if ($solution->{options}->{xml})
     {
-        $contrib_extraincludes->{'xml2'} = [
+        $contrib_extraincludes->{'pgxml'} = [
             $solution->{options}->{xml} . '\include',
             $solution->{options}->{xslt} . '\include',
             $solution->{options}->{iconv} . '\include'
         ];
 
-        $contrib_extralibs->{'xml2'} = [
+        $contrib_extralibs->{'pgxml'} = [
             $solution->{options}->{xml} . '\lib\libxml2.lib',
             $solution->{options}->{xslt} . '\lib\libxslt.lib'
         ];
@@ -225,6 +340,16 @@ sub mkvcbuild
         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(
@@ -249,6 +374,8 @@ sub mkvcbuild
     }
     $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";
@@ -261,9 +388,9 @@ sub mkvcbuild
     }
     closedir($D);
 
-    my $mf = Project::read_file('src\backend\utils\mb\conversion_procs\Makefile');
+    $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";
+    $mf =~ m{SUBDIRS\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');
@@ -286,25 +413,26 @@ sub mkvcbuild
         my @files = split /\s+/,$1;
         foreach my $f (@files)
         {
-            if ($f =~ /\/keywords\.o$/)
+            $f =~ s/\.o$/\.c/;
+            if ($f eq 'keywords.c')
+            {
+                $proj->AddFile('src\bin\pg_dump\keywords.c');
+            }
+            elsif ($f eq 'kwlookup.c')
             {
-                $proj->AddFile('src\backend\parser\keywords.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
             {
-                $f =~ s/\.o$/\.c/;
-                if ($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->AddFile('src\bin\scripts\\' . $f);
             }
         }
         $proj->AddIncludeDir('src\interfaces\libpq');
@@ -321,9 +449,9 @@ sub mkvcbuild
 
     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->AddReference($libpgport);
 
     $solution->Save();
@@ -341,7 +469,6 @@ sub AddSimpleFrontend
 
     my $p = $solution->AddProject($n,'exe','bin');
     $p->AddDir('src\bin\\' . $n);
-    $p->AddDefine('FRONTEND');
     $p->AddReference($libpgport);
     if ($uselibpq)
     {
@@ -357,13 +484,16 @@ sub AddContrib
     my $n = shift;
     my $mf = Project::read_file('contrib\\' . $n . '\Makefile');
 
-    if ($mf =~ /^MODULE_big/mg)
+    if ($mf =~ /^MODULE_big\s*=\s*(.*)$/mg)
     {
+        my $dn = $1;
         $mf =~ s{\\\s*[\r\n]+}{}mg;
-        my $proj = $solution->AddProject($n, 'dll', 'contrib');
+        my $proj = $solution->AddProject($dn, 'dll', 'contrib');
         $mf =~ /^OBJS\s*=\s*(.*)$/gm || croak "Could not find objects in MODULE_big for $n\n";
-        foreach my $o (split /\s+/, $1)
+        my $objs = $1;
+        while ($objs =~ /\b([\w-]+\.o)\b/g)
         {
+            my $o = $1;
             $o =~ s/\.o$/.c/;
             $proj->AddFile('contrib\\' . $n . '\\' . $o);
         }
@@ -376,15 +506,16 @@ sub AddContrib
                 $mf2 =~ s{\\\s*[\r\n]+}{}mg;
                 $mf2 =~ /^SUBOBJS\s*=\s*(.*)$/gm
                   || croak "Could not find objects in MODULE_big for $n, subdir $d\n";
-                foreach my $o (split /\s+/, $1)
+                $objs = $1;
+                while ($objs =~ /\b([\w-]+\.o)\b/g)
                 {
+                    my $o = $1;
                     $o =~ s/\.o$/.c/;
                     $proj->AddFile('contrib\\' . $n . '\\' . $d . '\\' . $o);
                 }
             }
         }
         AdjustContribProj($proj);
-        return $proj;
     }
     elsif ($mf =~ /^MODULES\s*=\s*(.*)$/mg)
     {
@@ -395,24 +526,76 @@ sub AddContrib
             $proj->AddReference($postgres);
             AdjustContribProj($proj);
         }
-        return undef;
     }
     elsif ($mf =~ /^PROGRAM\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";
-        foreach my $o (split /\s+/, $1)
+        $mf =~ s{\\\s*[\r\n]+}{}mg;
+        $mf =~ /^OBJS\s*=\s*(.*)$/gm || croak "Could not find objects in PROGRAM 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);
-        return $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 =~ /^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);
+        }
+
+        # 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)
+        {
+            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