Fix xslt_process() to ensure that it inserts a NULL terminator after the
[PostgreSQL.git] / src / tools / msvc / Install.pm
blobc9023760a5e91e5814496abfa3afb83bced4421e
1 package Install;
4 # Package that provides 'make install' functionality for msvc builds
6 # $PostgreSQL$
8 use strict;
9 use warnings;
10 use Carp;
11 use File::Basename;
12 use File::Copy;
13 use File::Find ();
15 use Exporter;
16 our (@ISA,@EXPORT_OK);
17 @ISA = qw(Exporter);
18 @EXPORT_OK = qw(Install);
20 sub lcopy
22 my $src = shift;
23 my $target = shift;
25 if (-f $target)
27 unlink $target || confess "Could not delete $target\n";
30 copy($src,$target)
31 || confess "Could not copy $src to $target\n";
35 sub Install
37 $| = 1;
39 my $target = shift;
40 our $config;
41 require 'config.pl';
43 chdir("../../..") if (-f "../../../configure");
44 chdir("../../../..") if (-f "../../../../configure");
45 my $conf = "";
46 if (-d "debug")
48 $conf = "debug";
50 if (-d "release")
52 $conf = "release";
54 die "Could not find debug or release binaries" if ($conf eq "");
55 my $majorver = DetermineMajorVersion();
56 print "Installing version $majorver for $conf in $target\n";
58 EnsureDirectories($target, 'bin','lib','share','share/timezonesets','share/contrib','doc',
59 'doc/contrib', 'symbols', 'share/tsearch_data');
61 CopySolutionOutput($conf, $target);
62 lcopy($target . '/lib/libpq.dll', $target . '/bin/libpq.dll');
63 my $sample_files = [];
64 File::Find::find({wanted =>
65 sub { /^.*\.sample\z/s &&
66 push(@$sample_files, $File::Find::name);
68 },
69 "src" );
70 CopySetOfFiles('config files', $sample_files , $target . '/share/');
71 CopyFiles(
72 'Import libraries',
73 $target .'/lib/',
74 "$conf\\", "postgres\\postgres.lib","libpq\\libpq.lib", "libecpg\\libecpg.lib", "libpgport\\libpgport.lib"
76 CopySetOfFiles('timezone names',
77 [ glob('src\timezone\tznames\*.txt') ] ,
78 $target . '/share/timezonesets/');
79 CopyFiles(
80 'timezone sets',
81 $target . '/share/timezonesets/',
82 'src/timezone/tznames/', 'Default','Australia','India'
84 CopySetOfFiles('BKI files', [ glob("src\\backend\\catalog\\postgres.*") ],
85 $target .'/share/');
86 CopySetOfFiles('SQL files', [ glob("src\\backend\\catalog\\*.sql") ],
87 $target . '/share/');
88 CopyFiles(
89 'Information schema data',
90 $target . '/share/',
91 'src/backend/catalog/', 'sql_features.txt'
93 GenerateConversionScript($target);
94 GenerateTimezoneFiles($target,$conf);
95 GenerateTsearchFiles($target);
96 CopySetOfFiles('Stopword files',
97 [ glob ("src\\backend\\snowball\\stopwords\\*.stop") ],
98 $target . '/share/tsearch_data/');
99 CopySetOfFiles('Dictionaries sample files',
100 [ glob ("src\\backend\\tsearch\\*_sample.*" ) ],
101 $target . '/share/tsearch_data/');
102 CopyContribFiles($config,$target);
103 CopyIncludeFiles($target);
105 GenerateNLSFiles($target,$config->{nls},$majorver) if ($config->{nls});
107 print "Installation complete.\n";
110 sub EnsureDirectories
112 my $target = shift;
113 mkdir $target unless -d ($target);
114 while (my $d = shift)
116 mkdir $target . '/' . $d unless -d ($target . '/' . $d);
120 sub CopyFiles
122 my $what = shift;
123 my $target = shift;
124 my $basedir = shift;
126 print "Copying $what";
127 while (my $f = shift)
129 print ".";
130 $f = $basedir . $f;
131 die "No file $f\n" if (!-f $f);
132 lcopy($f, $target . basename($f));
134 print "\n";
137 sub CopySetOfFiles
139 my $what = shift;
140 my $flist = shift;
141 my $target = shift;
142 print "Copying $what" if $what;
143 foreach (@$flist)
145 next if /regress/; # Skip temporary install in regression subdir
146 next if /ecpg.test/; # Skip temporary install in regression subdir
147 my $tgt = $target . basename($_);
148 print ".";
149 lcopy($_, $tgt) || croak "Could not copy $_: $!\n";
151 print "\n";
154 sub CopySolutionOutput
156 my $conf = shift;
157 my $target = shift;
158 my $rem = qr{Project\("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}"\) = "([^"]+)"};
160 my $sln = read_file("pgsql.sln") || croak "Could not open pgsql.sln\n";
161 print "Copying build output files...";
162 while ($sln =~ $rem)
164 my $pf = $1;
165 my $dir;
166 my $ext;
168 $sln =~ s/$rem//;
170 my $proj = read_file("$pf.vcproj") || croak "Could not open $pf.vcproj\n";
171 if ($proj !~ qr{ConfigurationType="([^"]+)"})
173 croak "Could not parse $pf.vcproj\n";
175 if ($1 == 1)
177 $dir = "bin";
178 $ext = "exe";
180 elsif ($1 == 2)
182 $dir = "lib";
183 $ext = "dll";
185 else
188 # Static lib, such as libpgport, only used internally during build, don't install
189 next;
191 lcopy("$conf\\$pf\\$pf.$ext","$target\\$dir\\$pf.$ext") || croak "Could not copy $pf.$ext\n";
192 lcopy("$conf\\$pf\\$pf.pdb","$target\\symbols\\$pf.pdb") || croak "Could not copy $pf.pdb\n";
193 print ".";
195 print "\n";
198 sub GenerateConversionScript
200 my $target = shift;
201 my $sql = "";
202 my $F;
204 print "Generating conversion proc script...";
205 my $mf = read_file('src/backend/utils/mb/conversion_procs/Makefile');
206 $mf =~ s{\\\s*[\r\n]+}{}mg;
207 $mf =~ /^CONVERSIONS\s*=\s*(.*)$/m
208 || die "Could not find CONVERSIONS line in conversions Makefile\n";
209 my @pieces = split /\s+/,$1;
210 while ($#pieces > 0)
212 my $name = shift @pieces;
213 my $se = shift @pieces;
214 my $de = shift @pieces;
215 my $func = shift @pieces;
216 my $obj = shift @pieces;
217 $sql .= "-- $se --> $de\n";
218 $sql .=
219 "CREATE OR REPLACE FUNCTION $func (INTEGER, INTEGER, CSTRING, INTERNAL, INTEGER) RETURNS VOID AS '\$libdir/$obj', '$func' LANGUAGE C STRICT;\n";
220 $sql .=
221 "COMMENT ON FUNCTION $func(INTEGER, INTEGER, CSTRING, INTERNAL, INTEGER) IS 'internal conversion function for $se to $de';\n";
222 $sql .= "DROP CONVERSION pg_catalog.$name;\n";
223 $sql .= "CREATE DEFAULT CONVERSION pg_catalog.$name FOR '$se' TO '$de' FROM $func;\n";
224 $sql .= "COMMENT ON CONVERSION pg_catalog.$name IS 'conversion for $se to $de';\n";
226 open($F,">$target/share/conversion_create.sql")
227 || die "Could not write to conversion_create.sql\n";
228 print $F $sql;
229 close($F);
230 print "\n";
233 sub GenerateTimezoneFiles
235 my $target = shift;
236 my $conf = shift;
237 my $mf = read_file("src/timezone/Makefile");
238 $mf =~ s{\\\s*[\r\n]+}{}mg;
239 $mf =~ /^TZDATA\s*:?=\s*(.*)$/m || die "Could not find TZDATA row in timezone makefile\n";
240 my @tzfiles = split /\s+/,$1;
241 unshift @tzfiles,'';
242 print "Generating timezone files...";
243 system("$conf\\zic\\zic -d \"$target/share/timezone\" " . join(" src/timezone/data/", @tzfiles));
244 print "\n";
247 sub GenerateTsearchFiles
249 my $target = shift;
251 print "Generating tsearch script...";
252 my $F;
253 my $tmpl = read_file('src/backend/snowball/snowball.sql.in');
254 my $mf = read_file('src/backend/snowball/Makefile');
255 $mf =~ s{\\\s*[\r\n]+}{}mg;
256 $mf =~ /^LANGUAGES\s*=\s*(.*)$/m
257 || die "Could not find LANGUAGES line in snowball Makefile\n";
258 my @pieces = split /\s+/,$1;
259 open($F,">$target/share/snowball_create.sql")
260 || die "Could not write snowball_create.sql";
261 print $F read_file('src/backend/snowball/snowball_func.sql.in');
262 while ($#pieces > 0)
264 my $lang = shift @pieces || last;
265 my $asclang = shift @pieces || last;
266 my $txt = $tmpl;
267 my $stop = '';
269 if (-s "src/backend/snowball/stopwords/$lang.stop") {
270 $stop = ", StopWords=$lang";
273 $txt =~ s#_LANGNAME_#${lang}#gs;
274 $txt =~ s#_DICTNAME_#${lang}_stem#gs;
275 $txt =~ s#_CFGNAME_#${lang}#gs;
276 $txt =~ s#_ASCDICTNAME_#${asclang}_stem#gs;
277 $txt =~ s#_NONASCDICTNAME_#${lang}_stem#gs;
278 $txt =~ s#_STOPWORDS_#$stop#gs;
279 print $F $txt;
280 print ".";
282 close($F);
283 print "\n";
286 sub CopyContribFiles
288 my $config = shift;
289 my $target = shift;
291 print "Copying contrib data files...";
292 my $D;
293 opendir($D, 'contrib') || croak "Could not opendir on contrib!\n";
294 while (my $d = readdir($D))
296 next if ($d =~ /^\./);
297 next unless (-f "contrib/$d/Makefile");
298 next if ($d eq "uuid-ossp"&& !defined($config->{uuid}));
299 next if ($d eq "sslinfo" && !defined($config->{openssl}));
300 next if ($d eq "xml2" && !defined($config->{xml}));
302 my $mf = read_file("contrib/$d/Makefile");
303 $mf =~ s{\\s*[\r\n]+}{}mg;
304 my $flist = '';
305 if ($mf =~ /^DATA_built\s*=\s*(.*)$/m) {$flist .= $1}
306 if ($mf =~ /^DATA\s*=\s*(.*)$/m) {$flist .= " $1"}
307 $flist =~ s/^\s*//; # Remove leading spaces if we had only DATA_built
309 if ($flist ne '')
311 $flist = ParseAndCleanRule($flist, $mf);
313 # Special case for contrib/spi
314 $flist = "autoinc.sql insert_username.sql moddatetime.sql refint.sql timetravel.sql"
315 if ($d eq 'spi');
316 foreach my $f (split /\s+/,$flist)
318 lcopy('contrib/' . $d . '/' . $f,$target . '/share/contrib/' . basename($f))
319 || croak("Could not copy file $f in contrib $d");
320 print '.';
324 $flist = '';
325 if ($mf =~ /^DATA_TSEARCH\s*=\s*(.*)$/m) {$flist .= $1}
326 if ($flist ne '')
328 $flist = ParseAndCleanRule($flist, $mf);
330 foreach my $f (split /\s+/,$flist)
332 lcopy('contrib/' . $d . '/' . $f,$target . '/share/tsearch_data/' . basename($f))
333 || croak("Could not copy file $f in contrib $d");
334 print '.';
338 $flist = '';
339 if ($mf =~ /^DOCS\s*=\s*(.*)$/mg) {$flist .= $1}
340 if ($flist ne '')
342 $flist = ParseAndCleanRule($flist, $mf);
344 # Special case for contrib/spi
345 $flist =
346 "autoinc.example insert_username.example moddatetime.example refint.example timetravel.example"
347 if ($d eq 'spi');
348 foreach my $f (split /\s+/,$flist)
350 lcopy('contrib/' . $d . '/' . $f, $target . '/doc/contrib/' . $f)
351 || croak("Could not copy file $f in contrib $d");
352 print '.';
356 closedir($D);
357 print "\n";
360 sub ParseAndCleanRule
362 my $flist = shift;
363 my $mf = shift;
365 # Strip out $(addsuffix) rules
366 if (index($flist, '$(addsuffix ') >= 0)
368 my $pcount = 0;
369 my $i;
370 for ($i = index($flist, '$(addsuffix ') + 12; $i < length($flist); $i++)
372 $pcount++ if (substr($flist, $i, 1) eq '(');
373 $pcount-- if (substr($flist, $i, 1) eq ')');
374 last if ($pcount < 0);
376 $flist = substr($flist, 0, index($flist, '$(addsuffix ')) . substr($flist, $i+1);
378 return $flist;
381 sub CopyIncludeFiles
383 my $target = shift;
385 EnsureDirectories($target, 'include', 'include/libpq',
386 'include/internal', 'include/internal/libpq',
387 'include/server');
389 CopyFiles(
390 'Public headers',
391 $target . '/include/',
392 'src/include/', 'postgres_ext.h', 'pg_config.h', 'pg_config_os.h', 'pg_config_manual.h'
394 lcopy('src/include/libpq/libpq-fs.h', $target . '/include/libpq/')
395 || croak 'Could not copy libpq-fs.h';
397 CopyFiles('Libpq headers',
398 $target . '/include/', 'src/interfaces/libpq/',
399 'libpq-fe.h', 'libpq-events.h');
400 CopyFiles(
401 'Libpq internal headers',
402 $target .'/include/internal/',
403 'src/interfaces/libpq/', 'libpq-int.h', 'pqexpbuffer.h'
406 CopyFiles(
407 'Internal headers',
408 $target . '/include/internal/',
409 'src/include/', 'c.h', 'port.h', 'postgres_fe.h'
411 lcopy('src/include/libpq/pqcomm.h', $target . '/include/internal/libpq/')
412 || croak 'Could not copy pqcomm.h';
414 CopyFiles(
415 'Server headers',
416 $target . '/include/server/',
417 'src/include/', 'pg_config.h', 'pg_config_os.h'
419 CopySetOfFiles('',
420 [ glob( "src\\include\\*.h" ) ],
421 $target . '/include/server/');
422 my $D;
423 opendir($D, 'src/include') || croak "Could not opendir on src/include!\n";
425 while (my $d = readdir($D))
427 next if ($d =~ /^\./);
428 next if ($d eq 'CVS');
429 next unless (-d 'src/include/' . $d);
431 EnsureDirectories($target . '/include/server', $d);
432 system(
433 "xcopy /s /i /q /r /y src\\include\\$d\\*.h \"$target\\include\\server\\$d\\\"")
434 && croak("Failed to copy include directory $d\n");
436 closedir($D);
438 my $mf = read_file('src/interfaces/ecpg/include/Makefile');
439 $mf =~ s{\\s*[\r\n]+}{}mg;
440 $mf =~ /^ecpg_headers\s*=\s*(.*)$/m || croak "Could not find ecpg_headers line\n";
441 CopyFiles(
442 'ECPG headers',
443 $target . '/include/',
444 'src/interfaces/ecpg/include/',
445 'ecpg_config.h', split /\s+/,$1
447 $mf =~ /^informix_headers\s*=\s*(.*)$/m || croak "Could not find informix_headers line\n";
448 EnsureDirectories($target . '/include', 'informix', 'informix/esql');
449 CopyFiles(
450 'ECPG informix headers',
451 $target .'/include/informix/esql/',
452 'src/interfaces/ecpg/include/',
453 split /\s+/,$1
457 sub GenerateNLSFiles
459 my $target = shift;
460 my $nlspath = shift;
461 my $majorver = shift;
463 print "Installing NLS files...";
464 EnsureDirectories($target, "share/locale");
465 my @flist;
466 File::Find::find({wanted =>
467 sub { /^nls\.mk\z/s &&
468 !push(@flist, $File::Find::name);
470 }, "src");
471 foreach (@flist)
473 my $prgm = DetermineCatalogName($_);
474 s/nls.mk/po/;
475 my $dir = $_;
476 next unless ($dir =~ /([^\/]+)\/po$/);
477 foreach (glob("$dir/*.po"))
479 my $lang;
480 next unless /([^\/]+)\.po/;
481 $lang = $1;
483 EnsureDirectories($target, "share/locale/$lang", "share/locale/$lang/LC_MESSAGES");
484 system(
485 "\"$nlspath\\bin\\msgfmt\" -o \"$target\\share\\locale\\$lang\\LC_MESSAGES\\$prgm-$majorver.mo\" $_"
487 && croak("Could not run msgfmt on $dir\\$_");
488 print ".";
491 print "\n";
494 sub DetermineMajorVersion
496 my $f = read_file('src/include/pg_config.h') || croak 'Could not open pg_config.h';
497 $f =~ /^#define\s+PG_MAJORVERSION\s+"([^"]+)"/m || croak 'Could not determine major version';
498 return $1;
501 sub DetermineCatalogName
503 my $filename = shift;
505 my $f = read_file($filename) || croak "Could not open $filename";
506 $f =~ /CATALOG_NAME\s*\:?=\s*(\S+)/m || croak "Could not determine catalog name in $filename";
507 return $1;
510 sub read_file
512 my $filename = shift;
513 my $F;
514 my $t = $/;
516 undef $/;
517 open($F, $filename) || die "Could not open file $filename\n";
518 my $txt = <$F>;
519 close($F);
520 $/ = $t;
522 return $txt;