]> granicus.if.org Git - postgresql/blob - src/tools/msvc/Install.pm
Add an "events" system to libpq, whereby applications can get callbacks that
[postgresql] / src / tools / msvc / Install.pm
1 package Install;
2
3 #
4 # Package that provides 'make install' functionality for msvc builds
5 #
6 # $PostgreSQL: pgsql/src/tools/msvc/Install.pm,v 1.31 2008/09/17 04:31:08 tgl Exp $
7 #
8 use strict;
9 use warnings;
10 use Carp;
11 use File::Basename;
12 use File::Copy;
13 use File::Find ();
14
15 use Exporter;
16 our (@ISA,@EXPORT_OK);
17 @ISA = qw(Exporter);
18 @EXPORT_OK = qw(Install);
19
20 sub lcopy
21 {
22         my $src = shift;
23         my $target = shift;
24
25         if (-f $target)
26         {
27            unlink $target || confess "Could not delete $target\n";
28         }
29
30         copy($src,$target)
31           || confess "Could not copy $src to $target\n";
32
33 }
34
35 sub Install
36 {
37     $| = 1;
38
39     my $target = shift;
40     our $config;
41     require 'config.pl';
42
43     chdir("../../..") if (-f "../../../configure");
44     chdir("../../../..") if (-f "../../../../configure");
45     my $conf = "";
46     if (-d "debug")
47     {
48         $conf = "debug";
49     }
50     if (-d "release")
51     {
52         $conf = "release";
53     }
54     die "Could not find debug or release binaries" if ($conf eq "");
55     print "Installing for $conf in $target\n";
56
57     EnsureDirectories($target, 'bin','lib','share','share/timezonesets','share/contrib','doc',
58         'doc/contrib', 'symbols', 'share/tsearch_data');
59
60     CopySolutionOutput($conf, $target);
61     lcopy($target . '/lib/libpq.dll', $target . '/bin/libpq.dll');
62         my $sample_files = [];
63         File::Find::find({wanted => 
64                                                   sub { /^.*\.sample\z/s && 
65                                                                         push(@$sample_files, $File::Find::name); 
66                                                         } 
67                                   }, 
68                                          "src" );
69     CopySetOfFiles('config files', $sample_files , $target . '/share/');
70     CopyFiles(
71         'Import libraries',
72         $target .'/lib/',
73         "$conf\\", "postgres\\postgres.lib","libpq\\libpq.lib", "libecpg\\libecpg.lib", "libpgport\\libpgport.lib"
74     );
75     CopySetOfFiles('timezone names', 
76                                    [ glob('src\timezone\tznames\*.txt') ] ,
77                                    $target . '/share/timezonesets/');
78     CopyFiles(
79         'timezone sets',
80         $target . '/share/timezonesets/',
81         'src/timezone/tznames/', 'Default','Australia','India'
82     );
83     CopySetOfFiles('BKI files', [ glob("src\\backend\\catalog\\postgres.*") ], 
84                                    $target .'/share/');
85     CopySetOfFiles('SQL files', [ glob("src\\backend\\catalog\\*.sql") ], 
86                                    $target . '/share/');
87     CopyFiles(
88         'Information schema data',
89         $target . '/share/',
90         'src/backend/catalog/', 'sql_features.txt'
91     );
92     GenerateConversionScript($target);
93     GenerateTimezoneFiles($target,$conf);
94     GenerateTsearchFiles($target);
95     CopySetOfFiles('Stopword files', 
96                                    [ glob ("src\\backend\\snowball\\stopwords\\*.stop") ], 
97                                    $target . '/share/tsearch_data/');
98     CopySetOfFiles('Dictionaries sample files', 
99                                    [ glob ("src\\backend\\tsearch\\*_sample.*" ) ], 
100                                    $target . '/share/tsearch_data/');
101     CopyContribFiles($config,$target);
102     CopyIncludeFiles($target);
103
104     GenerateNLSFiles($target,$config->{nls}) if ($config->{nls});
105
106     print "Installation complete.\n";
107 }
108
109 sub EnsureDirectories
110 {
111     my $target = shift;
112     mkdir $target unless -d ($target);
113     while (my $d = shift)
114     {
115         mkdir $target . '/' . $d unless -d ($target . '/' . $d);
116     }
117 }
118
119 sub CopyFiles
120 {
121     my $what = shift;
122     my $target = shift;
123     my $basedir = shift;
124
125     print "Copying $what";
126     while (my $f = shift)
127     {
128         print ".";
129         $f = $basedir . $f;
130         die "No file $f\n" if (!-f $f);
131         lcopy($f, $target . basename($f));
132     }
133     print "\n";
134 }
135
136 sub CopySetOfFiles
137 {
138     my $what = shift;
139     my $flist = shift;
140     my $target = shift;
141     print "Copying $what" if $what;
142     foreach (@$flist)
143     {
144         next if /regress/; # Skip temporary install in regression subdir
145         next if /ecpg.test/; # Skip temporary install in regression subdir
146         my $tgt = $target . basename($_);
147         print ".";
148         lcopy($_, $tgt) || croak "Could not copy $_: $!\n";
149     }
150     print "\n";
151 }
152
153 sub CopySolutionOutput
154 {
155     my $conf = shift;
156     my $target = shift;
157     my $rem = qr{Project\("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}"\) = "([^"]+)"};
158
159     my $sln = read_file("pgsql.sln") || croak "Could not open pgsql.sln\n";
160     print "Copying build output files...";
161     while ($sln =~ $rem)
162     {
163         my $pf = $1;
164         my $dir;
165         my $ext;
166
167         $sln =~ s/$rem//;
168
169         my $proj = read_file("$pf.vcproj") || croak "Could not open $pf.vcproj\n";
170         if ($proj !~ qr{ConfigurationType="([^"]+)"})
171         {
172             croak "Could not parse $pf.vcproj\n";
173         }
174         if ($1 == 1)
175         {
176             $dir = "bin";
177             $ext = "exe";
178         }
179         elsif ($1 == 2)
180         {
181             $dir = "lib";
182             $ext = "dll";
183         }
184         else
185         {
186
187             # Static lib, such as libpgport, only used internally during build, don't install
188             next;
189         }
190         lcopy("$conf\\$pf\\$pf.$ext","$target\\$dir\\$pf.$ext") || croak "Could not copy $pf.$ext\n";
191         lcopy("$conf\\$pf\\$pf.pdb","$target\\symbols\\$pf.pdb") || croak "Could not copy $pf.pdb\n";
192         print ".";
193     }
194     print "\n";
195 }
196
197 sub GenerateConversionScript
198 {
199     my $target = shift;
200     my $sql = "";
201     my $F;
202
203     print "Generating conversion proc script...";
204     my $mf = read_file('src/backend/utils/mb/conversion_procs/Makefile');
205     $mf =~ s{\\\s*[\r\n]+}{}mg;
206     $mf =~ /^CONVERSIONS\s*=\s*(.*)$/m
207       || die "Could not find CONVERSIONS line in conversions Makefile\n";
208     my @pieces = split /\s+/,$1;
209     while ($#pieces > 0)
210     {
211         my $name = shift @pieces;
212         my $se = shift @pieces;
213         my $de = shift @pieces;
214         my $func = shift @pieces;
215         my $obj = shift @pieces;
216         $sql .= "-- $se --> $de\n";
217         $sql .=
218 "CREATE OR REPLACE FUNCTION $func (INTEGER, INTEGER, CSTRING, INTERNAL, INTEGER) RETURNS VOID AS '\$libdir/$obj', '$func' LANGUAGE C STRICT;\n";
219         $sql .=
220 "COMMENT ON FUNCTION $func(INTEGER, INTEGER, CSTRING, INTERNAL, INTEGER) IS 'internal conversion function for $se to $de';\n";
221         $sql .= "DROP CONVERSION pg_catalog.$name;\n";
222         $sql .= "CREATE DEFAULT CONVERSION pg_catalog.$name FOR '$se' TO '$de' FROM $func;\n";
223         $sql .= "COMMENT ON CONVERSION pg_catalog.$name IS 'conversion for $se to $de';\n";
224     }
225     open($F,">$target/share/conversion_create.sql")
226       || die "Could not write to conversion_create.sql\n";
227     print $F $sql;
228     close($F);
229     print "\n";
230 }
231
232 sub GenerateTimezoneFiles
233 {
234     my $target = shift;
235     my $conf = shift;
236     my $mf = read_file("src/timezone/Makefile");
237     $mf =~ s{\\\s*[\r\n]+}{}mg;
238     $mf =~ /^TZDATA\s*:?=\s*(.*)$/m || die "Could not find TZDATA row in timezone makefile\n";
239     my @tzfiles = split /\s+/,$1;
240     unshift @tzfiles,'';
241     print "Generating timezone files...";
242     system("$conf\\zic\\zic -d \"$target/share/timezone\" " . join(" src/timezone/data/", @tzfiles));
243     print "\n";
244 }
245
246 sub GenerateTsearchFiles
247 {
248     my $target = shift;
249
250     print "Generating tsearch script...";
251     my $F;
252     my $tmpl = read_file('src/backend/snowball/snowball.sql.in');
253     my $mf = read_file('src/backend/snowball/Makefile');
254     $mf =~ s{\\\s*[\r\n]+}{}mg;
255     $mf =~ /^LANGUAGES\s*=\s*(.*)$/m
256       || die "Could not find LANGUAGES line in snowball Makefile\n";
257     my @pieces = split /\s+/,$1;
258     open($F,">$target/share/snowball_create.sql")
259       || die "Could not write snowball_create.sql";
260     print $F read_file('src/backend/snowball/snowball_func.sql.in');
261     while ($#pieces > 0)
262     {
263         my $lang = shift @pieces || last;
264         my $asclang = shift @pieces || last;
265         my $txt = $tmpl;
266         my $stop = '';
267
268         if (-s "src/backend/snowball/stopwords/$lang.stop") {
269             $stop = ", StopWords=$lang";
270         }
271
272         $txt =~ s#_LANGNAME_#${lang}#gs;
273         $txt =~ s#_DICTNAME_#${lang}_stem#gs;
274         $txt =~ s#_CFGNAME_#${lang}#gs;
275         $txt =~ s#_ASCDICTNAME_#${asclang}_stem#gs;
276         $txt =~ s#_NONASCDICTNAME_#${lang}_stem#gs;
277         $txt =~ s#_STOPWORDS_#$stop#gs;
278         print $F $txt;
279         print ".";
280     }
281     close($F);
282     print "\n";
283 }
284
285 sub CopyContribFiles
286 {
287     my $config = shift;
288     my $target = shift;
289
290     print "Copying contrib data files...";
291     my $D;
292     opendir($D, 'contrib') || croak "Could not opendir on contrib!\n";
293     while (my $d = readdir($D))
294     {
295         next if ($d =~ /^\./);
296         next unless (-f "contrib/$d/Makefile");
297         next if ($d eq "uuid-ossp"&& !defined($config->{uuid}));
298         next if ($d eq "sslinfo" && !defined($config->{openssl}));
299         next if ($d eq "xml2" && !defined($config->{xml}));
300
301         my $mf = read_file("contrib/$d/Makefile");
302         $mf =~ s{\\s*[\r\n]+}{}mg;
303         my $flist = '';
304         if ($mf =~ /^DATA_built\s*=\s*(.*)$/m) {$flist .= $1}
305         if ($mf =~ /^DATA\s*=\s*(.*)$/m) {$flist .= " $1"}
306         $flist =~ s/^\s*//; # Remove leading spaces if we had only DATA_built
307
308         if ($flist ne '')
309         {
310             $flist = ParseAndCleanRule($flist, $mf);
311
312             # Special case for contrib/spi
313             $flist = "autoinc.sql insert_username.sql moddatetime.sql refint.sql timetravel.sql"
314               if ($d eq 'spi');
315             foreach my $f (split /\s+/,$flist)
316             {
317                 lcopy('contrib/' . $d . '/' . $f,$target . '/share/contrib/' . basename($f))
318                   || croak("Could not copy file $f in contrib $d");
319                 print '.';
320             }
321         }
322
323         $flist = '';
324         if ($mf =~ /^DATA_TSEARCH\s*=\s*(.*)$/m) {$flist .= $1}
325         if ($flist ne '')
326         {
327             $flist = ParseAndCleanRule($flist, $mf);
328
329             foreach my $f (split /\s+/,$flist)
330             {
331                 lcopy('contrib/' . $d . '/' . $f,$target . '/share/tsearch_data/' . basename($f))
332                   || croak("Could not copy file $f in contrib $d");
333                 print '.';
334             }
335         }
336
337         $flist = '';
338         if ($mf =~ /^DOCS\s*=\s*(.*)$/mg) {$flist .= $1}
339         if ($flist ne '')
340         {
341             $flist = ParseAndCleanRule($flist, $mf);
342
343             # Special case for contrib/spi
344             $flist =
345 "autoinc.example insert_username.example moddatetime.example refint.example timetravel.example"
346               if ($d eq 'spi');
347             foreach my $f (split /\s+/,$flist)
348             {
349                 lcopy('contrib/' . $d . '/' . $f, $target . '/doc/contrib/' . $f)
350                   || croak("Could not copy file $f in contrib $d");
351                 print '.';
352             }
353         }
354     }
355     closedir($D);
356     print "\n";
357 }
358
359 sub ParseAndCleanRule
360 {
361     my $flist = shift;
362     my $mf = shift;
363
364     # Strip out $(addsuffix) rules
365     if (index($flist, '$(addsuffix ') >= 0)
366     {
367         my $pcount = 0;
368         my $i;
369         for ($i = index($flist, '$(addsuffix ') + 12; $i < length($flist); $i++)
370         {
371             $pcount++ if (substr($flist, $i, 1) eq '(');
372             $pcount-- if (substr($flist, $i, 1) eq ')');
373             last if ($pcount < 0);
374         }
375         $flist = substr($flist, 0, index($flist, '$(addsuffix ')) . substr($flist, $i+1);
376     }
377     return $flist;
378 }
379
380 sub CopyIncludeFiles
381 {
382     my $target = shift;
383
384     EnsureDirectories($target, 'include', 'include/libpq',
385         'include/internal', 'include/internal/libpq',
386         'include/server');
387
388     CopyFiles(
389         'Public headers',
390         $target . '/include/',
391         'src/include/', 'postgres_ext.h', 'pg_config.h', 'pg_config_os.h', 'pg_config_manual.h'
392     );
393     lcopy('src/include/libpq/libpq-fs.h', $target . '/include/libpq/')
394       || croak 'Could not copy libpq-fs.h';
395
396     CopyFiles('Libpq headers',
397               $target . '/include/', 'src/interfaces/libpq/',
398               'libpq-fe.h', 'libpq-events.h');
399     CopyFiles(
400         'Libpq internal headers',
401         $target .'/include/internal/',
402         'src/interfaces/libpq/', 'libpq-int.h', 'pqexpbuffer.h'
403     );
404
405     CopyFiles(
406         'Internal headers',
407         $target . '/include/internal/',
408         'src/include/', 'c.h', 'port.h', 'postgres_fe.h'
409     );
410     lcopy('src/include/libpq/pqcomm.h', $target . '/include/internal/libpq/')
411       || croak 'Could not copy pqcomm.h';
412
413     CopyFiles(
414         'Server headers',
415         $target . '/include/server/',
416         'src/include/', 'pg_config.h', 'pg_config_os.h'
417     );
418     CopySetOfFiles('', 
419                                    [ glob( "src\\include\\*.h" ) ], 
420                                    $target . '/include/server/');
421     my $D;
422     opendir($D, 'src/include') || croak "Could not opendir on src/include!\n";
423
424     while (my $d = readdir($D))
425     {
426         next if ($d =~ /^\./);
427         next if ($d eq 'CVS');
428         next unless (-d 'src/include/' . $d);
429
430         EnsureDirectories($target . '/include/server', $d);
431         system(
432             "xcopy /s /i /q /r /y src\\include\\$d\\*.h \"$target\\include\\server\\$d\\\"")
433           && croak("Failed to copy include directory $d\n");
434     }
435     closedir($D);
436
437     my $mf = read_file('src/interfaces/ecpg/include/Makefile');
438     $mf =~ s{\\s*[\r\n]+}{}mg;
439     $mf =~ /^ecpg_headers\s*=\s*(.*)$/m || croak "Could not find ecpg_headers line\n";
440     CopyFiles(
441         'ECPG headers',
442         $target . '/include/',
443         'src/interfaces/ecpg/include/',
444         'ecpg_config.h', split /\s+/,$1
445     );
446     $mf =~ /^informix_headers\s*=\s*(.*)$/m || croak "Could not find informix_headers line\n";
447     EnsureDirectories($target . '/include', 'informix', 'informix/esql');
448     CopyFiles(
449         'ECPG informix headers',
450         $target .'/include/informix/esql/',
451         'src/interfaces/ecpg/include/',
452         split /\s+/,$1
453     );
454 }
455
456 sub GenerateNLSFiles
457 {
458     my $target = shift;
459     my $nlspath = shift;
460
461     print "Installing NLS files...";
462     EnsureDirectories($target, "share/locale");
463         my @flist;
464         File::Find::find({wanted =>
465                                                   sub { /^nls\.mk\z/s &&
466                                                                         !push(@flist, $File::Find::name);
467                                                         }
468                                   }, "src");
469     foreach (@flist)
470     {
471         s/nls.mk/po/;
472         my $dir = $_;
473         next unless ($dir =~ /([^\/]+)\/po$/);
474         my $prgm = $1;
475         $prgm = 'postgres' if ($prgm eq 'backend');
476         foreach (glob("$dir/*.po"))
477         {
478             my $lang;
479             next unless /([^\/]+)\.po/;
480             $lang = $1;
481
482             EnsureDirectories($target, "share/locale/$lang", "share/locale/$lang/LC_MESSAGES");
483             system(
484 "\"$nlspath\\bin\\msgfmt\" -o \"$target\\share\\locale\\$lang\\LC_MESSAGES\\$prgm.mo\" $_"
485               )
486               && croak("Could not run msgfmt on $dir\\$_");
487             print ".";
488         }
489     }
490     print "\n";
491 }
492
493 sub read_file
494 {
495     my $filename = shift;
496     my $F;
497     my $t = $/;
498
499     undef $/;
500     open($F, $filename) || die "Could not open file $filename\n";
501     my $txt = <$F>;
502     close($F);
503     $/ = $t;
504
505     return $txt;
506 }
507
508 1;