]> granicus.if.org Git - postgresql/blob - src/tools/msvc/Mkvcbuild.pm
Unbreak MSVC builds for pg_archivecleanup by linking with libpgport
[postgresql] / src / tools / msvc / Mkvcbuild.pm
1 package Mkvcbuild;
2
3 #
4 # Package that generates build files for msvc build
5 #
6 # $PostgreSQL: pgsql/src/tools/msvc/Mkvcbuild.pm,v 1.58 2010/06/15 12:48:36 adunstan Exp $
7 #
8 use Carp;
9 use Win32;
10 use strict;
11 use warnings;
12 use Project;
13 use Solution;
14 use Cwd;
15 use File::Copy;
16
17 use Exporter;
18 our (@ISA, @EXPORT_OK);
19 @ISA = qw(Exporter);
20 @EXPORT_OK = qw(Mkvcbuild);
21
22 my $solution;
23 my $libpgport;
24 my $postgres;
25 my $libpq;
26
27 my $contrib_defines = {'refint' => 'REFINT_VERBOSE'};
28 my @contrib_uselibpq = ('dblink', 'oid2name', 'pgbench', 'pg_upgrade', 
29                                                 'vacuumlo');
30 my @contrib_uselibpgport = ('oid2name', 'pgbench', 'pg_standby', 
31                                                         'pg_archivecleanup', 'pg_upgrade', 'vacuumlo');
32 my $contrib_extralibs = {'pgbench' => ['wsock32.lib']};
33 my $contrib_extraincludes = {'tsearch2' => ['contrib/tsearch2'], 'dblink' => ['src/backend']};
34 my $contrib_extrasource = {
35     'cube' => ['cubescan.l','cubeparse.y'],
36     'seg' => ['segscan.l','segparse.y']
37 };
38 my @contrib_excludes = ('pgcrypto','intagg');
39
40 sub mkvcbuild
41 {
42     our $config = shift;
43
44     chdir('..\..\..') if (-d '..\msvc' && -d '..\..\..\src');
45     die 'Must run from root or msvc directory' unless (-d 'src\tools\msvc' && -d 'src');
46
47     $solution = new Solution($config);
48
49     our @pgportfiles = qw(
50       chklocale.c crypt.c fseeko.c getrusage.c inet_aton.c random.c srandom.c
51       getaddrinfo.c gettimeofday.c kill.c open.c erand48.c
52       snprintf.c strlcat.c strlcpy.c copydir.c dirmod.c exec.c noblock.c path.c pipe.c
53       pgsleep.c pgstrcasecmp.c qsort.c qsort_arg.c sprompt.c thread.c
54       getopt.c getopt_long.c dirent.c rint.c win32env.c win32error.c);
55
56     $libpgport = $solution->AddProject('libpgport','lib','misc');
57     $libpgport->AddDefine('FRONTEND');
58     $libpgport->AddFiles('src\port',@pgportfiles);
59
60     $postgres = $solution->AddProject('postgres','exe','','src\backend');
61     $postgres->AddIncludeDir('src\backend');
62     $postgres->AddDir('src\backend\port\win32');
63     $postgres->AddFile('src\backend\utils\fmgrtab.c');
64     $postgres->ReplaceFile('src\backend\port\dynloader.c','src\backend\port\dynloader\win32.c');
65     $postgres->ReplaceFile('src\backend\port\pg_sema.c','src\backend\port\win32_sema.c');
66     $postgres->ReplaceFile('src\backend\port\pg_shmem.c','src\backend\port\win32_shmem.c');
67     $postgres->AddFiles('src\port',@pgportfiles);
68     $postgres->AddDir('src\timezone');
69     $postgres->AddFiles('src\backend\parser','scan.l','gram.y');
70     $postgres->AddFiles('src\backend\bootstrap','bootscanner.l','bootparse.y');
71     $postgres->AddFiles('src\backend\utils\misc','guc-file.l');
72     $postgres->AddDefine('BUILDING_DLL');
73     $postgres->AddLibrary('wsock32.lib');
74     $postgres->AddLibrary('ws2_32.lib');
75     $postgres->AddLibrary('secur32.lib');
76     $postgres->AddLibrary('wldap32.lib') if ($solution->{options}->{ldap});
77     $postgres->FullExportDLL('postgres.lib');
78
79     my $snowball = $solution->AddProject('dict_snowball','dll','','src\backend\snowball');
80     $snowball->RelocateFiles(
81         'src\backend\snowball\libstemmer',
82         sub {
83             return shift !~ /dict_snowball.c$/;
84         }
85     );
86     $snowball->AddIncludeDir('src\include\snowball');
87     $snowball->AddReference($postgres);
88
89     my $plpgsql = $solution->AddProject('plpgsql','dll','PLs','src\pl\plpgsql\src');
90     $plpgsql->AddFiles('src\pl\plpgsql\src', 'gram.y');
91     $plpgsql->AddReference($postgres);
92
93     if ($solution->{options}->{perl})
94     {
95         my $plperlsrc = "src\\pl\\plperl\\";
96         my $plperl = $solution->AddProject('plperl','dll','PLs','src\pl\plperl');
97         $plperl->AddIncludeDir($solution->{options}->{perl} . '/lib/CORE');
98         $plperl->AddDefine('PLPERL_HAVE_UID_GID');
99         foreach my $xs ('SPI.xs', 'Util.xs')
100         {
101             (my $xsc = $xs) =~ s/\.xs/.c/;
102             if (Solution::IsNewer("$plperlsrc$xsc","$plperlsrc$xs"))
103             {
104                 print "Building $plperlsrc$xsc...\n";
105                 system( $solution->{options}->{perl}
106                       . '/bin/perl '
107                       . $solution->{options}->{perl}
108                       . '/lib/ExtUtils/xsubpp -typemap '
109                       . $solution->{options}->{perl}
110                       . '/lib/ExtUtils/typemap '
111                       . "$plperlsrc$xs "
112                       . ">$plperlsrc$xsc");
113                 if ((!(-f "$plperlsrc$xsc")) || -z "$plperlsrc$xsc")
114                 {
115                     unlink("$plperlsrc$xsc"); # if zero size
116                     die "Failed to create $xsc.\n";
117                 }
118             }
119         }
120         if (  Solution::IsNewer('src\pl\plperl\perlchunks.h','src\pl\plperl\plc_perlboot.pl')
121             ||Solution::IsNewer('src\pl\plperl\perlchunks.h','src\pl\plperl\plc_trusted.pl'))
122         {
123             print 'Building src\pl\plperl\perlchunks.h ...' . "\n";
124             my $basedir = getcwd;
125             chdir 'src\pl\plperl';
126             system( $solution->{options}->{perl}
127                   . '/bin/perl '
128                   . 'text2macro.pl '
129                   . '--strip="^(\#.*|\s*)$$" '
130                   . 'plc_perlboot.pl plc_trusted.pl '
131                   .     '>perlchunks.h');
132             chdir $basedir;
133             if ((!(-f 'src\pl\plperl\perlchunks.h')) || -z 'src\pl\plperl\perlchunks.h')
134             {
135                 unlink('src\pl\plperl\perlchunks.h'); # if zero size
136                 die 'Failed to create perlchunks.h' . "\n";
137             }
138         }
139         if (  Solution::IsNewer('src\pl\plperl\plperl_opmask.h','src\pl\plperl\plperl_opmask.pl'))
140         {
141             print 'Building src\pl\plperl\plperl_opmask.h ...' . "\n";
142             my $basedir = getcwd;
143             chdir 'src\pl\plperl';
144             system( $solution->{options}->{perl}
145                   . '/bin/perl '
146                   . 'plperl_opmask.pl '
147                   .     'plperl_opmask.h');
148             chdir $basedir;
149             if ((!(-f 'src\pl\plperl\plperl_opmask.h')) || -z 'src\pl\plperl\plperl_opmask.h')
150             {
151                 unlink('src\pl\plperl\plperl_opmask.h'); # if zero size
152                 die 'Failed to create plperl_opmask.h' . "\n";
153             }
154         }
155         $plperl->AddReference($postgres);
156         my @perl_libs =
157           grep {/perl\d+.lib$/ }glob($solution->{options}->{perl} . '\lib\CORE\perl*.lib');
158         if (@perl_libs == 1)
159         {
160             $plperl->AddLibrary($perl_libs[0]);
161         }
162         else
163         {
164             die "could not identify perl library version";
165         }
166     }
167
168     if ($solution->{options}->{python})
169     {
170
171         # Attempt to get python version and location. Assume python.exe in specified dir.
172         open(P,
173             $solution->{options}->{python}
174               . "\\python -c \"import sys;print(sys.prefix);print(str(sys.version_info[0])+str(sys.version_info[1]))\" |"
175         ) || die "Could not query for python version!\n";
176         my $pyprefix = <P>;
177         chomp($pyprefix);
178         my $pyver = <P>;
179         chomp($pyver);
180         close(P);
181
182   # Sometimes (always?) if python is not present, the execution actually works, but gives no data...
183         die "Failed to query python for version information\n"
184           if (!(defined($pyprefix) && defined($pyver)));
185
186         my $plpython = $solution->AddProject('plpython','dll','PLs','src\pl\plpython');
187         $plpython->AddIncludeDir($pyprefix . '\include');
188         $plpython->AddLibrary($pyprefix . "\\Libs\\python$pyver.lib");
189         $plpython->AddReference($postgres);
190     }
191
192     if ($solution->{options}->{tcl})
193     {
194         my $pltcl = $solution->AddProject('pltcl','dll','PLs','src\pl\tcl');
195         $pltcl->AddIncludeDir($solution->{options}->{tcl} . '\include');
196         $pltcl->AddReference($postgres);
197         if (-e $solution->{options}->{tcl} . '\lib\tcl85.lib')
198         {
199             $pltcl->AddLibrary($solution->{options}->{tcl} . '\lib\tcl85.lib');
200         }
201         else
202         {
203             $pltcl->AddLibrary($solution->{options}->{tcl} . '\lib\tcl84.lib');
204         }
205     }
206
207     $libpq = $solution->AddProject('libpq','dll','interfaces','src\interfaces\libpq');
208     $libpq->AddDefine('FRONTEND');
209     $libpq->AddDefine('UNSAFE_STAT_OK');
210     $libpq->AddIncludeDir('src\port');
211     $libpq->AddLibrary('wsock32.lib');
212     $libpq->AddLibrary('secur32.lib');
213     $libpq->AddLibrary('ws2_32.lib');
214     $libpq->AddLibrary('wldap32.lib') if ($solution->{options}->{ldap});
215     $libpq->UseDef('src\interfaces\libpq\libpqdll.def');
216     $libpq->ReplaceFile('src\interfaces\libpq\libpqrc.c','src\interfaces\libpq\libpq.rc');
217     $libpq->AddReference($libpgport);
218
219     my $libpqwalreceiver = $solution->AddProject('libpqwalreceiver', 'dll', '',
220         'src\backend\replication\libpqwalreceiver');
221     $libpqwalreceiver->AddIncludeDir('src\interfaces\libpq');
222     $libpqwalreceiver->AddReference($postgres,$libpq);
223
224     my $pgtypes =
225       $solution->AddProject('libpgtypes','dll','interfaces','src\interfaces\ecpg\pgtypeslib');
226     $pgtypes->AddDefine('FRONTEND');
227     $pgtypes->AddReference($libpgport);
228     $pgtypes->UseDef('src\interfaces\ecpg\pgtypeslib\pgtypeslib.def');
229     $pgtypes->AddIncludeDir('src\interfaces\ecpg\include');
230
231     my $libecpg =$solution->AddProject('libecpg','dll','interfaces','src\interfaces\ecpg\ecpglib');
232     $libecpg->AddDefine('FRONTEND');
233     $libecpg->AddIncludeDir('src\interfaces\ecpg\include');
234     $libecpg->AddIncludeDir('src\interfaces\libpq');
235     $libecpg->AddIncludeDir('src\port');
236     $libecpg->UseDef('src\interfaces\ecpg\ecpglib\ecpglib.def');
237     $libecpg->AddLibrary('wsock32.lib');
238     $libecpg->AddReference($libpq,$pgtypes,$libpgport);
239
240     my $libecpgcompat =
241       $solution->AddProject('libecpg_compat','dll','interfaces','src\interfaces\ecpg\compatlib');
242     $libecpgcompat->AddIncludeDir('src\interfaces\ecpg\include');
243     $libecpgcompat->AddIncludeDir('src\interfaces\libpq');
244     $libecpgcompat->UseDef('src\interfaces\ecpg\compatlib\compatlib.def');
245     $libecpgcompat->AddReference($pgtypes,$libecpg,$libpgport);
246
247     my $ecpg = $solution->AddProject('ecpg','exe','interfaces','src\interfaces\ecpg\preproc');
248     $ecpg->AddIncludeDir('src\interfaces\ecpg\include');
249     $ecpg->AddIncludeDir('src\interfaces\libpq');
250     $ecpg->AddPrefixInclude('src\interfaces\ecpg\preproc');
251     $ecpg->AddFiles('src\interfaces\ecpg\preproc','pgc.l','preproc.y');
252     $ecpg->AddDefine('MAJOR_VERSION=4');
253     $ecpg->AddDefine('MINOR_VERSION=2');
254     $ecpg->AddDefine('PATCHLEVEL=1');
255     $ecpg->AddDefine('ECPG_COMPILE');
256     $ecpg->AddReference($libpgport);
257
258     my $pgregress_ecpg = $solution->AddProject('pg_regress_ecpg','exe','misc');
259     $pgregress_ecpg->AddFile('src\interfaces\ecpg\test\pg_regress_ecpg.c');
260     $pgregress_ecpg->AddFile('src\test\regress\pg_regress.c');
261     $pgregress_ecpg->AddIncludeDir('src\port');
262     $pgregress_ecpg->AddIncludeDir('src\test\regress');
263     $pgregress_ecpg->AddDefine('HOST_TUPLE="i686-pc-win32vc"');
264     $pgregress_ecpg->AddDefine('FRONTEND');
265     $pgregress_ecpg->AddReference($libpgport);
266
267     # src/bin
268     my $initdb = AddSimpleFrontend('initdb');
269     $initdb->AddIncludeDir('src\interfaces\libpq');
270     $initdb->AddDefine('FRONTEND');
271     $initdb->AddLibrary('wsock32.lib');
272     $initdb->AddLibrary('ws2_32.lib');
273
274     my $pgconfig = AddSimpleFrontend('pg_config');
275
276     my $pgcontrol = AddSimpleFrontend('pg_controldata');
277
278     my $pgctl = AddSimpleFrontend('pg_ctl', 1);
279
280     my $pgreset = AddSimpleFrontend('pg_resetxlog');
281
282     my $pgevent = $solution->AddProject('pgevent','dll','bin');
283     $pgevent->AddFiles('src\bin\pgevent','pgevent.c','pgmsgevent.rc');
284     $pgevent->AddResourceFile('src\bin\pgevent','Eventlog message formatter');
285     $pgevent->RemoveFile('src\bin\pgevent\win32ver.rc');
286     $pgevent->UseDef('src\bin\pgevent\pgevent.def');
287     $pgevent->DisableLinkerWarnings('4104');
288
289     my $psql = AddSimpleFrontend('psql', 1);
290     $psql->AddIncludeDir('src\bin\pg_dump');
291     $psql->AddIncludeDir('src\backend');
292     $psql->AddFile('src\bin\psql\psqlscan.l');
293
294     my $pgdump = AddSimpleFrontend('pg_dump', 1);
295     $pgdump->AddIncludeDir('src\backend');
296     $pgdump->AddFile('src\bin\pg_dump\pg_dump.c');
297     $pgdump->AddFile('src\bin\pg_dump\common.c');
298     $pgdump->AddFile('src\bin\pg_dump\pg_dump_sort.c');
299     $pgdump->AddFile('src\bin\pg_dump\keywords.c');
300     $pgdump->AddFile('src\backend\parser\kwlookup.c');
301
302     my $pgdumpall = AddSimpleFrontend('pg_dump', 1);
303     $pgdumpall->{name} = 'pg_dumpall';
304     $pgdumpall->AddIncludeDir('src\backend');
305     $pgdumpall->AddFile('src\bin\pg_dump\pg_dumpall.c');
306     $pgdumpall->AddFile('src\bin\pg_dump\keywords.c');
307     $pgdumpall->AddFile('src\backend\parser\kwlookup.c');
308
309     my $pgrestore = AddSimpleFrontend('pg_dump', 1);
310     $pgrestore->{name} = 'pg_restore';
311     $pgrestore->AddIncludeDir('src\backend');
312     $pgrestore->AddFile('src\bin\pg_dump\pg_restore.c');
313     $pgrestore->AddFile('src\bin\pg_dump\keywords.c');
314     $pgrestore->AddFile('src\backend\parser\kwlookup.c');
315
316     my $zic = $solution->AddProject('zic','exe','utils');
317     $zic->AddFiles('src\timezone','zic.c','ialloc.c','scheck.c','localtime.c');
318     $zic->AddReference($libpgport);
319
320     if ($solution->{options}->{xml})
321     {
322         $contrib_extraincludes->{'pgxml'} = [
323             $solution->{options}->{xml} . '\include',
324             $solution->{options}->{xslt} . '\include',
325             $solution->{options}->{iconv} . '\include'
326         ];
327
328         $contrib_extralibs->{'pgxml'} = [
329             $solution->{options}->{xml} . '\lib\libxml2.lib',
330             $solution->{options}->{xslt} . '\lib\libxslt.lib'
331         ];
332     }
333     else
334     {
335         push @contrib_excludes,'xml2';
336     }
337
338     if (!$solution->{options}->{openssl})
339     {
340         push @contrib_excludes,'sslinfo';
341     }
342
343     if ($solution->{options}->{uuid})
344     {
345         $contrib_extraincludes->{'uuid-ossp'} = [ $solution->{options}->{uuid} . '\include' ];
346         $contrib_extralibs->{'uuid-ossp'} = [ $solution->{options}->{uuid} . '\lib\uuid.lib' ];
347     }
348     else
349     {
350         push @contrib_excludes,'uuid-ossp';
351     }
352
353     # Pgcrypto makefile too complex to parse....
354     my $pgcrypto = $solution->AddProject('pgcrypto','dll','crypto');
355     $pgcrypto->AddFiles(
356         'contrib\pgcrypto','pgcrypto.c','px.c','px-hmac.c',
357         'px-crypt.c','crypt-gensalt.c','crypt-blowfish.c','crypt-des.c',
358         'crypt-md5.c','mbuf.c','pgp.c','pgp-armor.c',
359         'pgp-cfb.c','pgp-compress.c','pgp-decrypt.c','pgp-encrypt.c',
360         'pgp-info.c','pgp-mpi.c','pgp-pubdec.c','pgp-pubenc.c',
361         'pgp-pubkey.c','pgp-s2k.c','pgp-pgsql.c'
362     );
363     if ($solution->{options}->{openssl})
364     {
365         $pgcrypto->AddFiles('contrib\pgcrypto', 'openssl.c','pgp-mpi-openssl.c');
366     }
367     else
368     {
369         $pgcrypto->AddFiles(
370             'contrib\pgcrypto', 'md5.c','sha1.c','sha2.c',
371             'internal.c','internal-sha2.c','blf.c','rijndael.c',
372             'fortuna.c','random.c','pgp-mpi-internal.c','imath.c'
373         );
374     }
375     $pgcrypto->AddReference($postgres);
376     $pgcrypto->AddLibrary('wsock32.lib');
377     my $mf = Project::read_file('contrib/pgcrypto/Makefile');
378     GenerateContribSqlFiles('pgcrypto', $mf);
379
380     my $D;
381     opendir($D, 'contrib') || croak "Could not opendir on contrib!\n";
382     while (my $d = readdir($D))
383     {
384         next if ($d =~ /^\./);
385         next unless (-f "contrib/$d/Makefile");
386         next if (grep {/^$d$/} @contrib_excludes);
387         AddContrib($d);
388     }
389     closedir($D);
390
391     $mf = Project::read_file('src\backend\utils\mb\conversion_procs\Makefile');
392     $mf =~ s{\\s*[\r\n]+}{}mg;
393     $mf =~ m{SUBDIRS\s*=\s*(.*)$}m || die 'Could not match in conversion makefile' . "\n";
394     foreach my $sub (split /\s+/,$1)
395     {
396         my $mf = Project::read_file('src\backend\utils\mb\conversion_procs\\' . $sub . '\Makefile');
397         my $p = $solution->AddProject($sub, 'dll', 'conversion procs');
398         $p->AddFile('src\backend\utils\mb\conversion_procs\\' . $sub . '\\' . $sub . '.c');
399         if ($mf =~ m{^SRCS\s*\+=\s*(.*)$}m)
400         {
401             $p->AddFile('src\backend\utils\mb\conversion_procs\\' . $sub . '\\' . $1);
402         }
403         $p->AddReference($postgres);
404     }
405
406     $mf = Project::read_file('src\bin\scripts\Makefile');
407     $mf =~ s{\\s*[\r\n]+}{}mg;
408     $mf =~ m{PROGRAMS\s*=\s*(.*)$}m || die 'Could not match in bin\scripts\Makefile' . "\n";
409     foreach my $prg (split /\s+/,$1)
410     {
411         my $proj = $solution->AddProject($prg,'exe','bin');
412         $mf =~ m{$prg\s*:\s*(.*)$}m || die 'Could not find script define for $prg' . "\n";
413         my @files = split /\s+/,$1;
414         foreach my $f (@files)
415         {
416             $f =~ s/\.o$/\.c/;
417             if ($f eq 'keywords.c')
418             {
419                 $proj->AddFile('src\bin\pg_dump\keywords.c');
420             }
421             elsif ($f eq 'kwlookup.c')
422             {
423                 $proj->AddFile('src\backend\parser\kwlookup.c');
424             }
425             elsif ($f eq 'dumputils.c')
426             {
427                 $proj->AddFile('src\bin\pg_dump\dumputils.c');
428             }
429             elsif ($f =~ /print\.c$/)
430             { # Also catches mbprint.c
431                 $proj->AddFile('src\bin\psql\\' . $f);
432             }
433             else
434             {
435                 $proj->AddFile('src\bin\scripts\\' . $f);
436             }
437         }
438         $proj->AddIncludeDir('src\interfaces\libpq');
439         $proj->AddIncludeDir('src\bin\pg_dump');
440         $proj->AddIncludeDir('src\bin\psql');
441         $proj->AddReference($libpq,$libpgport);
442         $proj->AddResourceFile('src\bin\scripts','PostgreSQL Utility');
443     }
444
445     # Regression DLL and EXE
446     my $regress = $solution->AddProject('regress','dll','misc');
447     $regress->AddFile('src\test\regress\regress.c');
448     $regress->AddReference($postgres);
449
450     my $pgregress = $solution->AddProject('pg_regress','exe','misc');
451     $pgregress->AddFile('src\test\regress\pg_regress.c');
452     $pgregress->AddFile('src\test\regress\pg_regress_main.c');
453     $pgregress->AddIncludeDir('src\port');
454     $pgregress->AddDefine('HOST_TUPLE="i686-pc-win32vc"');
455     $pgregress->AddReference($libpgport);
456
457     $solution->Save();
458 }
459
460 #####################
461 # Utility functions #
462 #####################
463
464 # Add a simple frontend project (exe)
465 sub AddSimpleFrontend
466 {
467     my $n = shift;
468     my $uselibpq= shift;
469
470     my $p = $solution->AddProject($n,'exe','bin');
471     $p->AddDir('src\bin\\' . $n);
472     $p->AddReference($libpgport);
473     if ($uselibpq)
474     {
475         $p->AddIncludeDir('src\interfaces\libpq');
476         $p->AddReference($libpq);
477     }
478     return $p;
479 }
480
481 # Add a simple contrib project
482 sub AddContrib
483 {
484     my $n = shift;
485     my $mf = Project::read_file('contrib\\' . $n . '\Makefile');
486
487     if ($mf =~ /^MODULE_big\s*=\s*(.*)$/mg)
488     {
489         my $dn = $1;
490         $mf =~ s{\\\s*[\r\n]+}{}mg;
491         my $proj = $solution->AddProject($dn, 'dll', 'contrib');
492         $mf =~ /^OBJS\s*=\s*(.*)$/gm || croak "Could not find objects in MODULE_big for $n\n";
493         my $objs = $1;
494         while ($objs =~ /\b([\w-]+\.o)\b/g)
495         {
496             my $o = $1;
497             $o =~ s/\.o$/.c/;
498             $proj->AddFile('contrib\\' . $n . '\\' . $o);
499         }
500         $proj->AddReference($postgres);
501         if ($mf =~ /^SUBDIRS\s*:?=\s*(.*)$/mg)
502         {
503             foreach my $d (split /\s+/, $1)
504             {
505                 my $mf2 = Project::read_file('contrib\\' . $n . '\\' . $d . '\Makefile');
506                 $mf2 =~ s{\\\s*[\r\n]+}{}mg;
507                 $mf2 =~ /^SUBOBJS\s*=\s*(.*)$/gm
508                   || croak "Could not find objects in MODULE_big for $n, subdir $d\n";
509                 $objs = $1;
510                 while ($objs =~ /\b([\w-]+\.o)\b/g)
511                 {
512                     my $o = $1;
513                     $o =~ s/\.o$/.c/;
514                     $proj->AddFile('contrib\\' . $n . '\\' . $d . '\\' . $o);
515                 }
516             }
517         }
518         AdjustContribProj($proj);
519     }
520     elsif ($mf =~ /^MODULES\s*=\s*(.*)$/mg)
521     {
522         foreach my $mod (split /\s+/, $1)
523         {
524             my $proj = $solution->AddProject($mod, 'dll', 'contrib');
525             $proj->AddFile('contrib\\' . $n . '\\' . $mod . '.c');
526             $proj->AddReference($postgres);
527             AdjustContribProj($proj);
528         }
529     }
530     elsif ($mf =~ /^PROGRAM\s*=\s*(.*)$/mg)
531     {
532         my $proj = $solution->AddProject($1, 'exe', 'contrib');
533         $mf =~ s{\\\s*[\r\n]+}{}mg;
534         $mf =~ /^OBJS\s*=\s*(.*)$/gm || croak "Could not find objects in PROGRAM for $n\n";
535         my $objs = $1;
536         while ($objs =~ /\b([\w-]+\.o)\b/g)
537         {
538             my $o = $1;
539             $o =~ s/\.o$/.c/;
540             $proj->AddFile('contrib\\' . $n . '\\' . $o);
541         }
542         AdjustContribProj($proj);
543     }
544     else
545     {
546         croak "Could not determine contrib module type for $n\n";
547     }
548
549     # Are there any output data files to build?
550     GenerateContribSqlFiles($n, $mf);
551 }
552
553 sub GenerateContribSqlFiles
554 {
555     my $n = shift;
556     my $mf = shift;
557     if ($mf =~ /^DATA_built\s*=\s*(.*)$/mg)
558     {
559         my $l = $1;
560
561         # Strip out $(addsuffix) rules
562         if (index($l, '$(addsuffix ') >= 0)
563         {
564             my $pcount = 0;
565             my $i;
566             for ($i = index($l, '$(addsuffix ') + 12; $i < length($l); $i++)
567             {
568                 $pcount++ if (substr($l, $i, 1) eq '(');
569                 $pcount-- if (substr($l, $i, 1) eq ')');
570                 last if ($pcount < 0);
571             }
572             $l = substr($l, 0, index($l, '$(addsuffix ')) . substr($l, $i+1);
573         }
574
575         # Special case for contrib/spi
576         $l = "autoinc.sql insert_username.sql moddatetime.sql refint.sql timetravel.sql"
577           if ($n eq 'spi');
578
579         foreach my $d (split /\s+/, $l)
580         {
581             my $in = "$d.in";
582             my $out = "$d";
583
584             if (Solution::IsNewer("contrib/$n/$out", "contrib/$n/$in"))
585             {
586                 print "Building $out from $in (contrib/$n)...\n";
587                 my $cont = Project::read_file("contrib/$n/$in");
588                 my $dn = $out;
589                 $dn =~ s/\.sql$//;
590                 if ($mf =~ /^MODULE_big\s*=\s*(.*)$/m) { $dn = $1 }
591                 $cont =~ s/MODULE_PATHNAME/\$libdir\/$dn/g;
592                 my $o;
593                 open($o,">contrib/$n/$out") || croak "Could not write to contrib/$n/$d";
594                 print $o $cont;
595                 close($o);
596             }
597         }
598     }
599 }
600
601 sub AdjustContribProj
602 {
603     my $proj = shift;
604     my $n = $proj->{name};
605
606     if ($contrib_defines->{$n})
607     {
608         foreach my $d ($contrib_defines->{$n})
609         {
610             $proj->AddDefine($d);
611         }
612     }
613     if (grep {/^$n$/} @contrib_uselibpq)
614     {
615         $proj->AddIncludeDir('src\interfaces\libpq');
616         $proj->AddReference($libpq);
617     }
618     if (grep {/^$n$/} @contrib_uselibpgport)
619     {
620         $proj->AddReference($libpgport);
621     }
622     if ($contrib_extralibs->{$n})
623     {
624         foreach my $l (@{$contrib_extralibs->{$n}})
625         {
626             $proj->AddLibrary($l);
627         }
628     }
629     if ($contrib_extraincludes->{$n})
630     {
631         foreach my $i (@{$contrib_extraincludes->{$n}})
632         {
633             $proj->AddIncludeDir($i);
634         }
635     }
636     if ($contrib_extrasource->{$n})
637     {
638         $proj->AddFiles('contrib\\' . $n, @{$contrib_extrasource->{$n}});
639     }
640 }
641
642 1;