OSDN Git Service

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