]> granicus.if.org Git - postgresql/blob - src/tools/msvc/Mkvcbuild.pm
Generate SQL files for /contrib (based on .sql.in)
[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.5 2007/03/23 09:53:33 mha Exp $
7 #
8 use Carp;
9 use Win32;
10 use strict;
11 use warnings;
12 use Project;
13 use Solution;
14
15 use Exporter;
16 our (@ISA, @EXPORT_OK);
17 @ISA = qw(Exporter);
18 @EXPORT_OK = qw(Mkvcbuild);
19
20 my $solution;
21 my $libpgport;
22 my $postgres;
23 my $libpq;
24
25 my $contrib_defines = {'refint' => 'REFINT_VERBOSE'};
26 my @contrib_uselibpq = ('dblink', 'oid2name', 'pgbench', 'vacuumlo');
27 my @contrib_uselibpgport = ('oid2name', 'pgbench', 'pg_standby', 'vacuumlo');
28 my $contrib_extralibs = {'pgbench' => ['wsock32.lib']};
29 my $contrib_extraincludes = {'tsearch2' => ['contrib/tsearch2']};
30 my $contrib_extrasource = {
31     'cube' => ['cubescan.l','cubeparse.y'],
32     'seg' => ['segscan.l','segparse.y']
33 };
34 my @contrib_excludes = ('pgcrypto');
35
36 sub mkvcbuild
37 {
38     our $config = shift;
39
40     chdir('..\..\..') if (-d '..\msvc' && -d '..\..\..\src');
41     die 'Must run from root or msvc directory' unless (-d 'src\tools\msvc' && -d 'src');
42
43     $solution = new Solution($config);
44
45     our @pgportfiles = qw(
46       crypt.c fseeko.c getrusage.c inet_aton.c random.c srandom.c
47       unsetenv.c getaddrinfo.c gettimeofday.c kill.c open.c rand.c
48       snprintf.c strlcat.c strlcpy.c copydir.c dirmod.c exec.c noblock.c path.c pipe.c
49       pgsleep.c pgstrcasecmp.c qsort.c qsort_arg.c sprompt.c thread.c
50       getopt.c getopt_long.c dirent.c rint.c win32error.c);
51
52     $libpgport = $solution->AddProject('libpgport','lib','misc');
53     $libpgport->AddDefine('FRONTEND');
54     $libpgport->AddFiles('src\port',@pgportfiles);
55
56     $postgres = $solution->AddProject('postgres','exe','','src\backend');
57     $postgres->AddIncludeDir('src\backend');
58     $postgres->AddDir('src\backend\port\win32');
59     $postgres->AddFile('src\backend\utils\fmgrtab.c');
60     $postgres->ReplaceFile('src\backend\port\dynloader.c','src\backend\port\dynloader\win32.c');
61     $postgres->ReplaceFile('src\backend\port\pg_sema.c','src\backend\port\win32_sema.c');
62     $postgres->ReplaceFile('src\backend\port\pg_shmem.c','src\backend\port\win32_shmem.c');
63     $postgres->AddFiles('src\port',@pgportfiles);
64     $postgres->AddDir('src\timezone');
65     $postgres->AddFiles('src\backend\parser','scan.l','gram.y');
66     $postgres->AddFiles('src\backend\bootstrap','bootscanner.l','bootparse.y');
67     $postgres->AddFiles('src\backend\utils\misc','guc-file.l');
68     $postgres->AddDefine('BUILDING_DLL');
69     $postgres->AddLibrary('wsock32.lib ws2_32.lib');
70     $postgres->AddLibrary('wldap32.lib') if ($solution->{options}->{ldap});
71     $postgres->FullExportDLL('postgres.lib');
72
73     my $plpgsql = $solution->AddProject('plpgsql','dll','PLs','src\pl\plpgsql\src');
74     $plpgsql->AddFiles('src\pl\plpgsql\src','scan.l','gram.y');
75     $plpgsql->AddReference($postgres);
76
77     if ($solution->{options}->{perl})
78     {
79         my $plperl = $solution->AddProject('plperl','dll','PLs','src\pl\plperl');
80         $plperl->AddIncludeDir($solution->{options}->{perl} . '/lib/CORE');
81         $plperl->AddDefine('PLPERL_HAVE_UID_GID');
82         if (Solution::IsNewer('src\pl\plperl\SPI.c','src\pl\plperl\SPI.xs'))
83         {
84             print 'Building src\pl\plperl\SPI.c...' . "\n";
85             system( $solution->{options}->{perl}
86                   . '/bin/perl '
87                   . $solution->{options}->{perl}
88                   . '/lib/ExtUtils/xsubpp -typemap '
89                   . $solution->{options}->{perl}
90                   . '/lib/ExtUtils/typemap src\pl\plperl\SPI.xs >src\pl\plperl\SPI.c');
91             if ((!(-f 'src\pl\plperl\SPI.c')) || -z 'src\pl\plperl\SPI.c')
92             {
93                 unlink('src\pl\plperl\SPI.c'); # if zero size
94                 die 'Failed to create SPI.c' . "\n";
95             }
96         }
97         $plperl->AddReference($postgres);
98         $plperl->AddLibrary($solution->{options}->{perl} . '\lib\CORE\perl58.lib');
99     }
100
101     if ($solution->{options}->{python})
102     {
103         my $plpython = $solution->AddProject('plpython','dll','PLs','src\pl\plpython');
104         $plpython->AddIncludeDir($solution->{options}->{python} . '\include');
105         $solution->{options}->{python} =~ /\\Python(\d{2})/i
106           || croak "Could not determine python version from path";
107         $plpython->AddLibrary($solution->{options}->{python} . "\\Libs\\python$1.lib");
108         $plpython->AddReference($postgres);
109     }
110
111     if ($solution->{options}->{tcl})
112     {
113         my $pltcl = $solution->AddProject('pltcl','dll','PLs','src\pl\tcl');
114         $pltcl->AddIncludeDir($solution->{options}->{tcl} . '\include');
115         $pltcl->AddReference($postgres);
116         $pltcl->AddLibrary($solution->{options}->{tcl} . '\lib\tcl84.lib');
117     }
118
119     $libpq = $solution->AddProject('libpq','dll','interfaces','src\interfaces\libpq');
120     $libpq->AddDefine('FRONTEND');
121     $libpq->AddIncludeDir('src\port');
122     $libpq->AddLibrary('wsock32.lib');
123     $libpq->AddLibrary('wldap32.lib') if ($solution->{options}->{ldap});
124     $libpq->UseDef('src\interfaces\libpq\libpqdll.def');
125     $libpq->ReplaceFile('src\interfaces\libpq\libpqrc.c','src\interfaces\libpq\libpq.rc');
126     $libpq->AddReference($libpgport);
127
128     my $pgtypes =
129       $solution->AddProject('libpgtypes','dll','interfaces','src\interfaces\ecpg\pgtypeslib');
130     $pgtypes->AddDefine('FRONTEND');
131     $pgtypes->AddReference($postgres,$libpgport);
132     $pgtypes->AddIncludeDir('src\interfaces\ecpg\include');
133
134     if ($config->{pthread})
135     {
136         my $libecpg =
137           $solution->AddProject('libecpg','dll','interfaces','src\interfaces\ecpg\ecpglib');
138         $libecpg->AddDefine('FRONTEND');
139         $libecpg->AddIncludeDir('src\interfaces\ecpg\include');
140         $libecpg->AddIncludeDir('src\interfaces\libpq');
141         $libecpg->AddIncludeDir('src\port');
142         $libecpg->AddLibrary('wsock32.lib');
143         $libecpg->AddLibrary($config->{'pthread'} . '\pthreadVC2.lib');
144         $libecpg->AddReference($libpq,$pgtypes,$libpgport);
145
146         my $libecpgcompat =
147           $solution->AddProject('libecpg_compat','dll','interfaces',
148             'src\interfaces\ecpg\compatlib');
149         $libecpgcompat->AddIncludeDir('src\interfaces\ecpg\include');
150         $libecpgcompat->AddIncludeDir('src\interfaces\libpq');
151         $libecpgcompat->AddReference($pgtypes,$libecpg);
152
153         my $ecpg = $solution->AddProject('ecpg','exe','interfaces','src\interfaces\ecpg\preproc');
154         $ecpg->AddIncludeDir('src\interfaces\ecpg\include');
155         $ecpg->AddIncludeDir('src\interfaces\libpq');
156         $ecpg->AddFiles('src\interfaces\ecpg\preproc','pgc.l','preproc.y');
157         $ecpg->AddDefine('MAJOR_VERSION=4');
158         $ecpg->AddDefine('MINOR_VERSION=2');
159         $ecpg->AddDefine('PATCHLEVEL=1');
160         $ecpg->AddReference($libpgport);
161     }
162     else
163     {
164         print "Not building ecpg due to lack of pthreads.\n";
165     }
166
167     # src/bin
168     my $initdb = AddSimpleFrontend('initdb', 1);
169
170     my $pgconfig = AddSimpleFrontend('pg_config');
171
172     my $pgcontrol = AddSimpleFrontend('pg_controldata');
173
174     my $pgctl = AddSimpleFrontend('pg_ctl', 1);
175
176     my $pgreset = AddSimpleFrontend('pg_resetxlog');
177
178     my $pgevent = $solution->AddProject('pgevent','dll','bin');
179     $pgevent->AddFiles('src\bin\pgevent','pgevent.c','pgmsgevent.rc');
180     $pgevent->AddResourceFile('src\bin\pgevent','Eventlog message formatter');
181     $pgevent->RemoveFile('src\bin\pgevent\win32ver.rc');
182     $pgevent->UseDef('src\bin\pgevent\pgevent.def');
183     $pgevent->DisableLinkerWarnings('4104');
184
185     my $psql = AddSimpleFrontend('psql', 1);
186     $psql->AddIncludeDir('src\bin\pg_dump');
187     $psql->AddFile('src\bin\psql\psqlscan.l');
188
189     my $pgdump = AddSimpleFrontend('pg_dump', 1);
190     $pgdump->AddFile('src\bin\pg_dump\pg_dump.c');
191     $pgdump->AddFile('src\bin\pg_dump\common.c');
192     $pgdump->AddFile('src\bin\pg_dump\pg_dump_sort.c');
193
194     my $pgdumpall = AddSimpleFrontend('pg_dump', 1);
195     $pgdumpall->{name} = 'pg_dumpall';
196     $pgdumpall->AddFile('src\bin\pg_dump\pg_dumpall.c');
197
198     my $pgrestore = AddSimpleFrontend('pg_dump', 1);
199     $pgrestore->{name} = 'pg_restore';
200     $pgrestore->AddFile('src\bin\pg_dump\pg_restore.c');
201
202     my $zic = $solution->AddProject('zic','exe','utils');
203     $zic->AddFiles('src\timezone','zic.c','ialloc.c','scheck.c','localtime.c');
204     $zic->AddReference($libpgport);
205
206     if ($solution->{options}->{xml})
207     {
208         $contrib_extraincludes->{'xml2'} = [
209             $solution->{options}->{xml} . '\include',
210             $solution->{options}->{xslt} . '\include',
211             $solution->{options}->{iconv} . '\include'
212         ];
213
214         $contrib_extralibs->{'xml2'} = [
215             $solution->{options}->{xml} . '\lib\libxml2.lib',
216             $solution->{options}->{xslt} . '\lib\libxslt.lib'
217         ];
218     }
219     else
220     {
221         push @contrib_excludes,'xml2';
222     }
223
224     if (!$solution->{options}->{openssl})
225     {
226         push @contrib_excludes,'sslinfo';
227     }
228
229     # Pgcrypto makefile too complex to parse....
230     my $pgcrypto = $solution->AddProject('pgcrypto','dll','crypto');
231     $pgcrypto->AddFiles(
232         'contrib\pgcrypto','pgcrypto.c','px.c','px-hmac.c',
233         'px-crypt.c','crypt-gensalt.c','crypt-blowfish.c','crypt-des.c',
234         'crypt-md5.c','mbuf.c','pgp.c','pgp-armor.c',
235         'pgp-cfb.c','pgp-compress.c','pgp-decrypt.c','pgp-encrypt.c',
236         'pgp-info.c','pgp-mpi.c','pgp-pubdec.c','pgp-pubenc.c',
237         'pgp-pubkey.c','pgp-s2k.c','pgp-pgsql.c'
238     );
239     if ($solution->{options}->{openssl})
240     {
241         $pgcrypto->AddFiles('contrib\pgcrypto', 'openssl.c','pgp-mpi-openssl.c');
242     }
243     else
244     {
245         $pgcrypto->AddFiles(
246             'contrib\pgcrypto', 'md5.c','sha1.c','sha2.c',
247             'internal.c','internal-sha2.c','blf.c','rijndael.c',
248             'fortuna.c','random.c','pgp-mpi-internal.c','imath.c'
249         );
250     }
251     $pgcrypto->AddReference($postgres);
252     $pgcrypto->AddLibrary('wsock32.lib');
253     my $mf = Project::read_file('contrib/pgcrypto/Makefile');
254     GenerateContribSqlFiles('pgcrypto', $mf);
255
256     my $D;
257     opendir($D, 'contrib') || croak "Could not opendir on contrib!\n";
258     while (my $d = readdir($D))
259     {
260         next if ($d =~ /^\./);
261         next unless (-f "contrib/$d/Makefile");
262         next if (grep {/^$d$/} @contrib_excludes);
263         AddContrib($d);
264     }
265     closedir($D);
266
267     my $mf = Project::read_file('src\backend\utils\mb\conversion_procs\Makefile');
268     $mf =~ s{\\s*[\r\n]+}{}mg;
269     $mf =~ m{DIRS\s*=\s*(.*)$}m || die 'Could not match in conversion makefile' . "\n";
270     foreach my $sub (split /\s+/,$1)
271     {
272         my $mf = Project::read_file('src\backend\utils\mb\conversion_procs\\' . $sub . '\Makefile');
273         my $p = $solution->AddProject($sub, 'dll', 'conversion procs');
274         $p->AddFile('src\backend\utils\mb\conversion_procs\\' . $sub . '\\' . $sub . '.c');
275         if ($mf =~ m{^SRCS\s*\+=\s*(.*)$}m)
276         {
277             $p->AddFile('src\backend\utils\mb\conversion_procs\\' . $sub . '\\' . $1);
278         }
279         $p->AddReference($postgres);
280     }
281
282     $mf = Project::read_file('src\bin\scripts\Makefile');
283     $mf =~ s{\\s*[\r\n]+}{}mg;
284     $mf =~ m{PROGRAMS\s*=\s*(.*)$}m || die 'Could not match in bin\scripts\Makefile' . "\n";
285     foreach my $prg (split /\s+/,$1)
286     {
287         my $proj = $solution->AddProject($prg,'exe','bin');
288         $mf =~ m{$prg\s*:\s*(.*)$}m || die 'Could not find script define for $prg' . "\n";
289         my @files = split /\s+/,$1;
290         foreach my $f (@files)
291         {
292             if ($f =~ /\/keywords\.o$/)
293             {
294                 $proj->AddFile('src\backend\parser\keywords.c');
295             }
296             else
297             {
298                 $f =~ s/\.o$/\.c/;
299                 if ($f eq 'dumputils.c')
300                 {
301                     $proj->AddFile('src\bin\pg_dump\dumputils.c');
302                 }
303                 elsif ($f =~ /print\.c$/)
304                 { # Also catches mbprint.c
305                     $proj->AddFile('src\bin\psql\\' . $f);
306                 }
307                 else
308                 {
309                     $proj->AddFile('src\bin\scripts\\' . $f);
310                 }
311             }
312         }
313         $proj->AddIncludeDir('src\interfaces\libpq');
314         $proj->AddIncludeDir('src\bin\pg_dump');
315         $proj->AddIncludeDir('src\bin\psql');
316         $proj->AddReference($libpq,$libpgport);
317         $proj->AddResourceFile('src\bin\scripts','PostgreSQL Utility');
318     }
319
320     # Regression DLL and EXE
321     my $regress = $solution->AddProject('regress','dll','misc');
322     $regress->AddFile('src\test\regress\regress.c');
323     $regress->AddReference($postgres);
324
325     my $pgregress = $solution->AddProject('pg_regress','exe','misc');
326     $pgregress->AddFile('src\test\regress\pg_regress.c');
327     $pgregress->AddIncludeDir('src\port');
328     $pgregress->AddDefine('HOST_TUPLE="i686-pc-win32vc"');
329     $pgregress->AddDefine('FRONTEND');
330     $pgregress->AddReference($libpgport);
331
332     $solution->Save();
333 }
334
335 #####################
336 # Utility functions #
337 #####################
338
339 # Add a simple frontend project (exe)
340 sub AddSimpleFrontend
341 {
342     my $n = shift;
343     my $uselibpq= shift;
344
345     my $p = $solution->AddProject($n,'exe','bin');
346     $p->AddDir('src\bin\\' . $n);
347     $p->AddDefine('FRONTEND');
348     $p->AddReference($libpgport);
349     if ($uselibpq)
350     {
351         $p->AddIncludeDir('src\interfaces\libpq');
352         $p->AddReference($libpq);
353     }
354     return $p;
355 }
356
357 # Add a simple contrib project
358 sub AddContrib
359 {
360     my $n = shift;
361     my $mf = Project::read_file('contrib\\' . $n . '\Makefile');
362
363     if ($mf =~ /^MODULE_big/mg)
364     {
365         $mf =~ s{\\\s*[\r\n]+}{}mg;
366         my $proj = $solution->AddProject($n, 'dll', 'contrib');
367         $mf =~ /^OBJS\s*=\s*(.*)$/gm || croak "Could not find objects in MODULE_big for $n\n";
368         foreach my $o (split /\s+/, $1)
369         {
370             $o =~ s/\.o$/.c/;
371             $proj->AddFile('contrib\\' . $n . '\\' . $o);
372         }
373         $proj->AddReference($postgres);
374         if ($mf =~ /^SUBDIRS\s*:?=\s*(.*)$/mg)
375         {
376             foreach my $d (split /\s+/, $1)
377             {
378                 my $mf2 = Project::read_file('contrib\\' . $n . '\\' . $d . '\Makefile');
379                 $mf2 =~ s{\\\s*[\r\n]+}{}mg;
380                 $mf2 =~ /^SUBOBJS\s*=\s*(.*)$/gm
381                   || croak "Could not find objects in MODULE_big for $n, subdir $d\n";
382                 foreach my $o (split /\s+/, $1)
383                 {
384                     $o =~ s/\.o$/.c/;
385                     $proj->AddFile('contrib\\' . $n . '\\' . $d . '\\' . $o);
386                 }
387             }
388         }
389         AdjustContribProj($proj);
390     }
391     elsif ($mf =~ /^MODULES\s*=\s*(.*)$/mg)
392     {
393         foreach my $mod (split /\s+/, $1)
394         {
395             my $proj = $solution->AddProject($mod, 'dll', 'contrib');
396             $proj->AddFile('contrib\\' . $n . '\\' . $mod . '.c');
397             $proj->AddReference($postgres);
398             AdjustContribProj($proj);
399         }
400     }
401     elsif ($mf =~ /^PROGRAM\s*=\s*(.*)$/mg)
402     {
403         my $proj = $solution->AddProject($1, 'exe', 'contrib');
404         $mf =~ /^OBJS\s*=\s*(.*)$/gm || croak "Could not find objects in MODULE_big for $n\n";
405         foreach my $o (split /\s+/, $1)
406         {
407             $o =~ s/\.o$/.c/;
408             $proj->AddFile('contrib\\' . $n . '\\' . $o);
409         }
410         AdjustContribProj($proj);
411     }
412     else
413     {
414         croak "Could not determine contrib module type for $n\n";
415     }
416
417     # Are there any output data files to build?
418     GenerateContribSqlFiles($n, $mf);
419 }
420
421 sub GenerateContribSqlFiles
422 {
423     my $n = shift;
424     my $mf = shift;
425     if ($mf =~ /^DATA_built\s*=\s*(.*)$/mg)
426     {
427         my $l = $1;
428
429         # Strip out $(addsuffix) rules
430         if (index($l, '$(addsuffix ') >= 0)
431         {
432             my $pcount = 0;
433             my $i;
434             for ($i = index($l, '$(addsuffix ') + 12; $i < length($l); $i++)
435             {
436                 $pcount++ if (substr($l, $i, 1) eq '(');
437                 $pcount-- if (substr($l, $i, 1) eq ')');
438                 last if ($pcount < 0);
439             }
440             $l = substr($l, 0, index($l, '$(addsuffix ')) . substr($l, $i+1);
441         }
442
443         # Special case for contrib/spi
444         $l = "autoinc.sql insert_username.sql moddatetime.sql refint.sql timetravel.sql"
445           if ($n eq 'spi');
446
447         foreach my $d (split /\s+/, $l)
448         {
449             my $in = "$d.in";
450             my $out = "$d";
451
452             # tsearch2 uses inconsistent naming
453             $in = "tsearch.sql.in" if ($in eq "tsearch2.sql.in");
454             $in = "untsearch.sql.in" if ($in eq "uninstall_tsearch2.sql.in");
455             if (Solution::IsNewer("contrib/$n/$out", "contrib/$n/$in"))
456             {
457                 print "Building $out from $in (contrib/$n)...\n";
458                 my $cont = Project::read_file("contrib/$n/$in");
459                 $cont =~ s/MODULE_PATHNAME/\$libdir\/$n/g;
460                 my $o;
461                 open($o,">contrib/$n/$out") || croak "Could not write to contrib/$n/$d";
462                 print $o $cont;
463                 close($o);
464             }
465         }
466     }
467 }
468
469 sub AdjustContribProj
470 {
471     my $proj = shift;
472     my $n = $proj->{name};
473
474     if ($contrib_defines->{$n})
475     {
476         foreach my $d ($contrib_defines->{$n})
477         {
478             $proj->AddDefine($d);
479         }
480     }
481     if (grep {/^$n$/} @contrib_uselibpq)
482     {
483         $proj->AddIncludeDir('src\interfaces\libpq');
484         $proj->AddReference($libpq);
485     }
486     if (grep {/^$n$/} @contrib_uselibpgport)
487     {
488         $proj->AddReference($libpgport);
489     }
490     if ($contrib_extralibs->{$n})
491     {
492         foreach my $l (@{$contrib_extralibs->{$n}})
493         {
494             $proj->AddLibrary($l);
495         }
496     }
497     if ($contrib_extraincludes->{$n})
498     {
499         foreach my $i (@{$contrib_extraincludes->{$n}})
500         {
501             $proj->AddIncludeDir($i);
502         }
503     }
504     if ($contrib_extrasource->{$n})
505     {
506         $proj->AddFiles('contrib\\' . $n, @{$contrib_extrasource->{$n}});
507     }
508 }
509
510 1;