]> granicus.if.org Git - postgresql/blob - src/tools/msvc/Solution.pm
Avoid maintaining three separate copies of the error codes list.
[postgresql] / src / tools / msvc / Solution.pm
1 package Solution;
2
3 #
4 # Package that encapsulates a Visual C++ solution file generation
5 #
6 # src/tools/msvc/Solution.pm
7 #
8 use Carp;
9 use strict;
10 use warnings;
11
12 sub new
13 {
14     my $junk = shift;
15     my $options = shift;
16     my $self = {
17         projects => {},
18         options  => $options,
19         numver   => '',
20         strver   => '',
21         vcver    => undef,
22         platform => undef,
23     };
24     bless $self;
25
26     # integer_datetimes is now the default
27     $options->{integer_datetimes} = 1
28       unless exists $options->{integer_datetimes};
29     $options->{float4byval} = 1
30       unless exists $options->{float4byval};
31     if ($options->{xml})
32     {
33         if (!($options->{xslt} && $options->{iconv}))
34         {
35             die "XML requires both XSLT and ICONV\n";
36         }
37     }
38     $options->{blocksize} = 8
39       unless $options->{blocksize}; # undef or 0 means default
40     die "Bad blocksize $options->{blocksize}"
41       unless grep {$_ == $options->{blocksize}} (1,2,4,8,16,32);
42     $options->{segsize} = 1
43       unless $options->{segsize}; # undef or 0 means default
44     # only allow segsize 1 for now, as we can't do large files yet in windows
45     die "Bad segsize $options->{segsize}"
46       unless $options->{segsize} == 1;
47     $options->{wal_blocksize} = 8
48       unless $options->{wal_blocksize}; # undef or 0 means default
49     die "Bad wal_blocksize $options->{wal_blocksize}"
50       unless grep {$_ == $options->{wal_blocksize}} (1,2,4,8,16,32,64);
51     $options->{wal_segsize} = 16
52       unless $options->{wal_segsize}; # undef or 0 means default
53     die "Bad wal_segsize $options->{wal_segsize}"
54       unless grep {$_ == $options->{wal_segsize}} (1,2,4,8,16,32,64);
55
56     $self->DetermineToolVersions();
57
58     return $self;
59 }
60
61 sub DetermineToolVersions
62 {
63     my $self = shift;
64
65     # Determine version of vcbuild command, to set proper verison of visual studio
66     open(P,"vcbuild /? |") || die "vcbuild command not found";
67     my $line = <P>;
68     close(P);
69     if ($line !~ /^Microsoft\s*\(R\) Visual C\+\+ [^-]+ - \D+(\d+)\.00\.\d+/)
70     {
71         die "Unable to determine vcbuild version from first line of output!";
72     }
73     if ($1 == 8) { $self->{vcver} = '8.00' }
74     elsif ($1 == 9) { $self->{vcver} = '9.00' }
75     else { die "Unsupported version of Visual Studio: $1" }
76     print "Detected Visual Studio version $self->{vcver}\n";
77
78     # Determine if we are in 32 or 64-bit mode. Do this by seeing if CL has
79     # 64-bit only parameters.
80     $self->{platform} = 'Win32';
81     open(P,"cl /? 2>NUL|") || die "cl command not found";
82     while (<P>)
83     {
84         if (/^\/favor:</)
85         {
86             $self->{platform} = 'x64';
87             last;
88         }
89     }
90     close(P);
91     print "Detected hardware platform: $self->{platform}\n";
92 }
93
94 # Return 1 if $oldfile is newer than $newfile, or if $newfile doesn't exist.
95 # Special case - if config.pl has changed, always return 1
96 sub IsNewer
97 {
98     my ($newfile, $oldfile) = @_;
99     if ($oldfile ne 'src\tools\msvc\config.pl' && $oldfile ne 'src\tools\msvc\config_default.pl')
100     {
101         return 1
102           if (-f 'src\tools\msvc\config.pl') && IsNewer($newfile, 'src\tools\msvc\config.pl');
103         return 1
104           if (-f 'src\tools\msvc\config_default.pl')
105           && IsNewer($newfile, 'src\tools\msvc\config_default.pl');
106     }
107     return 1 if (!(-e $newfile));
108     my @nstat = stat($newfile);
109     my @ostat = stat($oldfile);
110     return 1 if ($nstat[9] < $ostat[9]);
111     return 0;
112 }
113
114 # Copy a file, *not* preserving date. Only works for text files.
115 sub copyFile
116 {
117     my ($src, $dest) = @_;
118     open(I,$src) || croak "Could not open $src";
119     open(O,">$dest") || croak "Could not open $dest";
120     while (<I>)
121     {
122         print O;
123     }
124     close(I);
125     close(O);
126 }
127
128 sub GenerateFiles
129 {
130     my $self = shift;
131     my $bits = $self->{platform} eq 'Win32' ? 32 : 64;
132
133     # Parse configure.in to get version numbers
134     open(C,"configure.in") || confess("Could not open configure.in for reading\n");
135     while (<C>)
136     {
137         if (/^AC_INIT\(\[PostgreSQL\], \[([^\]]+)\]/)
138         {
139             $self->{strver} = $1;
140             if ($self->{strver} !~ /^(\d+)\.(\d+)(?:\.(\d+))?/)
141             {
142                 confess "Bad format of version: $self->{strver}\n";
143             }
144             $self->{numver} = sprintf("%d%02d%02d", $1, $2, $3?$3:0);
145             $self->{majorver} = sprintf("%d.%d", $1, $2);
146         }
147     }
148     close(C);
149     confess "Unable to parse configure.in for all variables!"
150       if ($self->{strver} eq '' || $self->{numver} eq '');
151
152     if (IsNewer("src\\include\\pg_config_os.h","src\\include\\port\\win32.h"))
153     {
154         print "Copying pg_config_os.h...\n";
155         copyFile("src\\include\\port\\win32.h","src\\include\\pg_config_os.h");
156     }
157
158     if (IsNewer("src\\include\\pg_config.h","src\\include\\pg_config.h.win32"))
159     {
160         print "Generating pg_config.h...\n";
161         open(I,"src\\include\\pg_config.h.win32") || confess "Could not open pg_config.h.win32\n";
162         open(O,">src\\include\\pg_config.h") || confess "Could not write to pg_config.h\n";
163         while (<I>)
164         {
165             s{PG_VERSION "[^"]+"}{PG_VERSION "$self->{strver}"};
166             s{PG_VERSION_NUM \d+}{PG_VERSION_NUM $self->{numver}};
167 s{PG_VERSION_STR "[^"]+"}{__STRINGIFY(x) #x\n#define __STRINGIFY2(z) __STRINGIFY(z)\n#define PG_VERSION_STR "PostgreSQL $self->{strver}, compiled by Visual C++ build " __STRINGIFY2(_MSC_VER) ", $bits-bit"};
168             print O;
169         }
170         print O "#define PG_MAJORVERSION \"$self->{majorver}\"\n";
171         print O "#define LOCALEDIR \"/share/locale\"\n" if ($self->{options}->{nls});
172         print O "/* defines added by config steps */\n";
173         print O "#ifndef IGNORE_CONFIGURED_SETTINGS\n";
174         print O "#define USE_ASSERT_CHECKING 1\n" if ($self->{options}->{asserts});
175         print O "#define USE_INTEGER_DATETIMES 1\n" if ($self->{options}->{integer_datetimes});
176         print O "#define USE_LDAP 1\n" if ($self->{options}->{ldap});
177         print O "#define HAVE_LIBZ 1\n" if ($self->{options}->{zlib});
178         print O "#define USE_SSL 1\n" if ($self->{options}->{openssl});
179         print O "#define ENABLE_NLS 1\n" if ($self->{options}->{nls});
180
181         print O "#define BLCKSZ ",1024 * $self->{options}->{blocksize},"\n";
182         print O "#define RELSEG_SIZE ",
183           (1024 / $self->{options}->{blocksize}) *$self->{options}->{segsize} * 1024, "\n";
184         print O "#define XLOG_BLCKSZ ",1024 * $self->{options}->{wal_blocksize},"\n";
185         print O "#define XLOG_SEG_SIZE (",$self->{options}->{wal_segsize}," * 1024 * 1024)\n";
186
187         if ($self->{options}->{float4byval})
188         {
189             print O "#define USE_FLOAT4_BYVAL 1\n";
190             print O "#define FLOAT4PASSBYVAL true\n";
191         }
192         else
193         {
194             print O "#define FLOAT4PASSBYVAL false\n";
195         }
196         if ($self->{options}->{float8byval})
197         {
198             print O "#define USE_FLOAT8_BYVAL 1\n";
199             print O "#define FLOAT8PASSBYVAL true\n";
200         }
201         else
202         {
203             print O "#define FLOAT8PASSBYVAL false\n";
204         }
205
206         if ($self->{options}->{uuid})
207         {
208             print O "#define HAVE_UUID_H\n";
209         }
210         if ($self->{options}->{xml})
211         {
212             print O "#define HAVE_LIBXML2\n";
213             print O "#define USE_LIBXML\n";
214         }
215         if ($self->{options}->{xslt})
216         {
217             print O "#define HAVE_LIBXSLT\n";
218             print O "#define USE_LIBXSLT\n";
219         }
220         if ($self->{options}->{krb5})
221         {
222             print O "#define KRB5 1\n";
223             print O "#define HAVE_KRB5_ERROR_TEXT_DATA 1\n";
224             print O "#define HAVE_KRB5_TICKET_ENC_PART2 1\n";
225             print O "#define HAVE_KRB5_FREE_UNPARSED_NAME 1\n";
226             print O "#define ENABLE_GSS 1\n";
227         }
228         if (my $port = $self->{options}->{"--with-pgport"})
229         {
230             print O "#undef DEF_PGPORT\n";
231             print O "#undef DEF_PGPORT_STR\n";
232             print O "#define DEF_PGPORT $port\n";
233             print O "#define DEF_PGPORT_STR \"$port\"\n";
234         }
235         print O "#define VAL_CONFIGURE \"" . $self->GetFakeConfigure() . "\"\n";
236         print O "#endif /* IGNORE_CONFIGURED_SETTINGS */\n";
237         close(O);
238         close(I);
239     }
240
241     $self->GenerateDefFile("src\\interfaces\\libpq\\libpqdll.def",
242         "src\\interfaces\\libpq\\exports.txt","LIBPQ");
243     $self->GenerateDefFile(
244         "src\\interfaces\\ecpg\\ecpglib\\ecpglib.def",
245         "src\\interfaces\\ecpg\\ecpglib\\exports.txt",
246         "LIBECPG"
247     );
248     $self->GenerateDefFile(
249         "src\\interfaces\\ecpg\\compatlib\\compatlib.def",
250         "src\\interfaces\\ecpg\\compatlib\\exports.txt",
251         "LIBECPG_COMPAT"
252     );
253     $self->GenerateDefFile(
254         "src\\interfaces\\ecpg\\pgtypeslib\\pgtypeslib.def",
255         "src\\interfaces\\ecpg\\pgtypeslib\\exports.txt",
256         "LIBPGTYPES"
257     );
258
259     if (IsNewer('src\backend\utils\fmgrtab.c','src\include\catalog\pg_proc.h'))
260     {
261         print "Generating fmgrtab.c and fmgroids.h...\n";
262         chdir('src\backend\utils');
263         system("perl -I ../catalog Gen_fmgrtab.pl ../../../src/include/catalog/pg_proc.h");
264         chdir('..\..\..');
265         copyFile('src\backend\utils\fmgroids.h','src\include\utils\fmgroids.h');
266     }
267
268     if (IsNewer('src\include\utils\probes.h','src\backend\utils\probes.d'))
269     {
270         print "Generating probes.h...\n";
271         system(
272 'psed -f src\backend\utils\Gen_dummy_probes.sed src\backend\utils\probes.d > src\include\utils\probes.h'
273         );
274     }
275
276     if (IsNewer('src\include\utils\errcodes.h','src\backend\utils\errcodes.txt'))
277     {
278         print "Generating errcodes.h...\n";
279         system("perl src\backend\utils\generate-errcodes.pl src\backend\utils\errcodes.txt > src\backend\utils\errcodes.h");
280         copyFile('src\backend\utils\errcodes.h','src\include\utils\errcodes.h');
281     }
282
283     if (IsNewer('src\pl\plpgsql\src\plerrcodes.h','src\backend\utils\errcodes.txt'))
284     {
285         print "Generating plerrcodes.h...\n";
286         system("perl src\pl\plpgsql\src\generate-plerrcodes.pl src\backend\utils\errcodes.txt > src\pl\plpgsql\src\plerrcodes.h");
287     }
288
289     if (IsNewer('src\interfaces\libpq\libpq.rc','src\interfaces\libpq\libpq.rc.in'))
290     {
291         print "Generating libpq.rc...\n";
292         my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
293         my $d = ($year - 100) . "$yday";
294         open(I,'<', 'src\interfaces\libpq\libpq.rc.in') || confess "Could not open libpq.rc.in";
295         open(O,'>', 'src\interfaces\libpq\libpq.rc') || confess "Could not open libpq.rc";
296         while (<I>)
297         {
298             s/(VERSION.*),0/$1,$d/;
299             print O;
300         }
301         close(I);
302         close(O);
303     }
304
305     if (IsNewer('src\bin\psql\sql_help.h','src\bin\psql\create_help.pl'))
306     {
307         print "Generating sql_help.h...\n";
308         chdir('src\bin\psql');
309         system("perl create_help.pl ../../../doc/src/sgml/ref sql_help");
310         chdir('..\..\..');
311     }
312
313     if (IsNewer('src\interfaces\ecpg\preproc\preproc.y','src\backend\parser\gram.y'))
314     {
315         print "Generating preproc.y...\n";
316         chdir('src\interfaces\ecpg\preproc');
317         system('perl parse.pl < ..\..\..\backend\parser\gram.y > preproc.y');
318         chdir('..\..\..\..');
319     }
320
321     if (
322         IsNewer(
323             'src\interfaces\ecpg\include\ecpg_config.h',
324             'src\interfaces\ecpg\include\ecpg_config.h.in'
325         )
326       )
327     {
328         print "Generating ecpg_config.h...\n";
329         open(O,'>','src\interfaces\ecpg\include\ecpg_config.h')
330           || confess "Could not open ecpg_config.h";
331         print O <<EOF;
332 #if (_MSC_VER > 1200)
333 #define HAVE_LONG_LONG_INT_64
334 #define ENABLE_THREAD_SAFETY 1
335 EOF
336         print O "#define USE_INTEGER_DATETIMES 1\n" if ($self->{options}->{integer_datetimes});
337         print O "#endif\n";
338         close(O);
339     }
340
341     unless (-f "src\\port\\pg_config_paths.h")
342     {
343         print "Generating pg_config_paths.h...\n";
344         open(O,'>', 'src\port\pg_config_paths.h') || confess "Could not open pg_config_paths.h";
345         print O  <<EOF;
346 #define PGBINDIR "/bin"
347 #define PGSHAREDIR "/share"
348 #define SYSCONFDIR "/etc"
349 #define INCLUDEDIR "/include"
350 #define PKGINCLUDEDIR "/include"
351 #define INCLUDEDIRSERVER "/include/server"
352 #define LIBDIR "/lib"
353 #define PKGLIBDIR "/lib"
354 #define LOCALEDIR "/share/locale"
355 #define DOCDIR "/doc"
356 #define HTMLDIR "/doc"
357 #define MANDIR "/man"
358 EOF
359         close(O);
360     }
361
362     my $mf = Project::read_file('src\backend\catalog\Makefile');
363     $mf =~ s{\\s*[\r\n]+}{}mg;
364     $mf =~ /^POSTGRES_BKI_SRCS\s*:?=[^,]+,(.*)\)$/gm
365       || croak "Could not find POSTGRES_BKI_SRCS in Makefile\n";
366     my @allbki = split /\s+/, $1;
367     foreach my $bki (@allbki)
368     {
369         next if $bki eq "";
370         if (IsNewer('src/backend/catalog/postgres.bki', "src/include/catalog/$bki"))
371         {
372             print "Generating postgres.bki and schemapg.h...\n";
373             chdir('src\backend\catalog');
374             my $bki_srcs = join(' ../../../src/include/catalog/', @allbki);
375             system(
376 "perl genbki.pl -I../../../src/include/catalog --set-version=$self->{majorver} $bki_srcs"
377             );
378             chdir('..\..\..');
379             copyFile('src\backend\catalog\schemapg.h', 'src\include\catalog\schemapg.h');
380             last;
381         }
382     }
383
384     open(O, ">doc/src/sgml/version.sgml") || croak "Could not write to version.sgml\n";
385     print O <<EOF;
386 <!entity version "$self->{strver}">
387 <!entity majorversion "$self->{majorver}">
388 EOF
389     close(O);
390 }
391
392 sub GenerateDefFile
393 {
394     my ($self, $deffile, $txtfile, $libname)  = @_;
395
396     if (IsNewer($deffile,$txtfile))
397     {
398         print "Generating $deffile...\n";
399         open(I,$txtfile) || confess("Could not open $txtfile\n");
400         open(O,">$deffile") || confess("Could not open $deffile\n");
401         print O "LIBRARY $libname\nEXPORTS\n";
402         while (<I>)
403         {
404             next if (/^#/);
405             next if (/^\s*$/);
406             my ($f, $o) = split;
407             print O " $f @ $o\n";
408         }
409         close(O);
410         close(I);
411     }
412 }
413
414 sub AddProject
415 {
416     my ($self, $name, $type, $folder, $initialdir) = @_;
417
418     my $proj = new Project($name, $type, $self);
419     push @{$self->{projects}->{$folder}}, $proj;
420     $proj->AddDir($initialdir) if ($initialdir);
421     if ($self->{options}->{zlib})
422     {
423         $proj->AddIncludeDir($self->{options}->{zlib} . '\include');
424         $proj->AddLibrary($self->{options}->{zlib} . '\lib\zdll.lib');
425     }
426     if ($self->{options}->{openssl})
427     {
428         $proj->AddIncludeDir($self->{options}->{openssl} . '\include');
429         $proj->AddLibrary($self->{options}->{openssl} . '\lib\VC\ssleay32.lib', 1);
430         $proj->AddLibrary($self->{options}->{openssl} . '\lib\VC\libeay32.lib', 1);
431     }
432     if ($self->{options}->{nls})
433     {
434         $proj->AddIncludeDir($self->{options}->{nls} . '\include');
435         $proj->AddLibrary($self->{options}->{nls} . '\lib\libintl.lib');
436     }
437     if ($self->{options}->{krb5})
438     {
439         $proj->AddIncludeDir($self->{options}->{krb5} . '\inc\krb5');
440         $proj->AddLibrary($self->{options}->{krb5} . '\lib\i386\krb5_32.lib');
441         $proj->AddLibrary($self->{options}->{krb5} . '\lib\i386\comerr32.lib');
442         $proj->AddLibrary($self->{options}->{krb5} . '\lib\i386\gssapi32.lib');
443     }
444     if ($self->{options}->{iconv})
445     {
446         $proj->AddIncludeDir($self->{options}->{iconv} . '\include');
447         $proj->AddLibrary($self->{options}->{iconv} . '\lib\iconv.lib');
448     }
449     if ($self->{options}->{xml})
450     {
451         $proj->AddIncludeDir($self->{options}->{xml} . '\include');
452         $proj->AddLibrary($self->{options}->{xml} . '\lib\libxml2.lib');
453     }
454     if ($self->{options}->{xslt})
455     {
456         $proj->AddIncludeDir($self->{options}->{xslt} . '\include');
457         $proj->AddLibrary($self->{options}->{xslt} . '\lib\libxslt.lib');
458     }
459     return $proj;
460 }
461
462 sub Save
463 {
464     my ($self) = @_;
465     my %flduid;
466
467     $self->GenerateFiles();
468     foreach my $fld (keys %{$self->{projects}})
469     {
470         foreach my $proj (@{$self->{projects}->{$fld}})
471         {
472             $proj->Save();
473         }
474     }
475
476     open(SLN,">pgsql.sln") || croak "Could not write to pgsql.sln\n";
477     print SLN <<EOF;
478 Microsoft Visual Studio Solution File, Format Version 9.00
479 # Visual Studio 2005
480 EOF
481
482     foreach my $fld (keys %{$self->{projects}})
483     {
484         foreach my $proj (@{$self->{projects}->{$fld}})
485         {
486             print SLN <<EOF;
487 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "$proj->{name}", "$proj->{name}.vcproj", "$proj->{guid}"
488 EndProject
489 EOF
490         }
491         if ($fld ne "")
492         {
493             $flduid{$fld} = Win32::GuidGen();
494             print SLN <<EOF;
495 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "$fld", "$fld", "$flduid{$fld}"
496 EndProject
497 EOF
498         }
499     }
500
501     print SLN <<EOF;
502 Global
503         GlobalSection(SolutionConfigurationPlatforms) = preSolution
504                 Debug|$self->{platform}= Debug|$self->{platform}
505                 Release|$self->{platform} = Release|$self->{platform}
506         EndGlobalSection
507         GlobalSection(ProjectConfigurationPlatforms) = postSolution
508 EOF
509
510     foreach my $fld (keys %{$self->{projects}})
511     {
512         foreach my $proj (@{$self->{projects}->{$fld}})
513         {
514             print SLN <<EOF;
515                 $proj->{guid}.Debug|$self->{platform}.ActiveCfg = Debug|$self->{platform}
516                 $proj->{guid}.Debug|$self->{platform}.Build.0  = Debug|$self->{platform}
517                 $proj->{guid}.Release|$self->{platform}.ActiveCfg = Release|$self->{platform}
518                 $proj->{guid}.Release|$self->{platform}.Build.0 = Release|$self->{platform}
519 EOF
520         }
521     }
522
523     print SLN <<EOF;
524         EndGlobalSection
525         GlobalSection(SolutionProperties) = preSolution
526                 HideSolutionNode = FALSE
527         EndGlobalSection
528         GlobalSection(NestedProjects) = preSolution
529 EOF
530
531     foreach my $fld (keys %{$self->{projects}})
532     {
533         next if ($fld eq "");
534         foreach my $proj (@{$self->{projects}->{$fld}})
535         {
536             print SLN "\t\t$proj->{guid} = $flduid{$fld}\n";
537         }
538     }
539
540     print SLN <<EOF;
541         EndGlobalSection
542 EndGlobal
543 EOF
544     close(SLN);
545 }
546
547 sub GetFakeConfigure
548 {
549     my $self = shift;
550
551     my $cfg = '--enable-thread-safety';
552     $cfg .= ' --enable-cassert' if ($self->{options}->{asserts});
553     $cfg .= ' --enable-integer-datetimes' if ($self->{options}->{integer_datetimes});
554     $cfg .= ' --enable-nls' if ($self->{options}->{nls});
555     $cfg .= ' --with-ldap' if ($self->{options}->{ldap});
556     $cfg .= ' --without-zlib' unless ($self->{options}->{zlib});
557     $cfg .= ' --with-openssl' if ($self->{options}->{ssl});
558     $cfg .= ' --with-ossp-uuid' if ($self->{options}->{uuid});
559     $cfg .= ' --with-libxml' if ($self->{options}->{xml});
560     $cfg .= ' --with-libxslt' if ($self->{options}->{xslt});
561     $cfg .= ' --with-krb5' if ($self->{options}->{krb5});
562     $cfg .= ' --with-tcl' if ($self->{options}->{tcl});
563     $cfg .= ' --with-perl' if ($self->{options}->{perl});
564     $cfg .= ' --with-python' if ($self->{options}->{python});
565
566     return $cfg;
567 }
568
569 1;