4 # Package that provides 'make install' functionality for msvc builds
6 # src/tools/msvc/Install.pm
16 our (@ISA,@EXPORT_OK);
18 @EXPORT_OK = qw(Install);
27 unlink $target || confess "Could not delete $target\n";
31 || confess "Could not copy $src to $target\n";
41 require "config_default.pl";
42 require "config.pl" if (-f "config.pl");
44 chdir("../../..") if (-f "../../../configure");
45 chdir("../../../..") if (-f "../../../../configure");
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";
59 EnsureDirectories($target, 'bin', 'lib', 'share', 'share/timezonesets',
60 'share/extension', 'share/contrib',
61 'doc', 'doc/extension', 'doc/contrib',
62 'symbols', 'share/tsearch_data');
64 CopySolutionOutput($conf, $target);
65 lcopy($target . '/lib/libpq.dll', $target . '/bin/libpq.dll');
66 my $sample_files = [];
71 &&push(@$sample_files, $File::Find::name);
76 CopySetOfFiles('config files', $sample_files, $target . '/share/');
80 "$conf\\", "postgres\\postgres.lib","libpq\\libpq.lib", "libecpg\\libecpg.lib",
81 "libpgport\\libpgport.lib"
85 [ glob('src\timezone\tznames\*.txt') ],
86 $target . '/share/timezonesets/'
90 $target . '/share/timezonesets/',
91 'src/timezone/tznames/', 'Default','Australia','India'
93 CopySetOfFiles('BKI files', [ glob("src\\backend\\catalog\\postgres.*") ],$target .'/share/');
94 CopySetOfFiles('SQL files', [ glob("src\\backend\\catalog\\*.sql") ],$target . '/share/');
96 'Information schema data',
98 'src/backend/catalog/', 'sql_features.txt'
100 GenerateConversionScript($target);
101 GenerateTimezoneFiles($target,$conf);
102 GenerateTsearchFiles($target);
105 [ glob("src\\backend\\snowball\\stopwords\\*.stop") ],
106 $target . '/share/tsearch_data/'
109 'Dictionaries sample files',
110 [ glob("src\\backend\\tsearch\\*_sample.*") ],
111 $target . '/share/tsearch_data/'
113 CopyContribFiles($config,$target);
114 CopyIncludeFiles($target);
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};
124 /^(.*--.*\.sql|.*\.control)\z/s
125 &&push(@$pl_extension_files, $File::Find::name);
131 'PL Extension files', $pl_extension_files,
132 $target . '/share/extension/'
135 GenerateNLSFiles($target,$config->{nls},$majorver) if ($config->{nls});
137 print "Installation complete.\n";
140 sub EnsureDirectories
143 mkdir $target unless -d ($target);
144 while (my $d = shift)
146 mkdir $target . '/' . $d unless -d ($target . '/' . $d);
156 print "Copying $what";
157 while (my $f = shift)
161 die "No file $f\n" if (!-f $f);
162 lcopy($f, $target . basename($f));
172 print "Copying $what" if $what;
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($_);
179 lcopy($_, $tgt) || croak "Could not copy $_: $!\n";
184 sub CopySolutionOutput
188 my $rem = qr{Project\("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}"\) = "([^"]+)"};
190 my $sln = read_file("pgsql.sln") || croak "Could not open pgsql.sln\n";
191 print "Copying build output files...";
200 my $proj = read_file("$pf.vcproj") || croak "Could not open $pf.vcproj\n";
201 if ($proj !~ qr{ConfigurationType="([^"]+)"})
203 croak "Could not parse $pf.vcproj\n";
218 # Static lib, such as libpgport, only used internally during build, don't install
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";
230 sub GenerateConversionScript
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;
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";
251 "CREATE OR REPLACE FUNCTION $func (INTEGER, INTEGER, CSTRING, INTERNAL, INTEGER) RETURNS VOID AS '\$libdir/$obj', '$func' LANGUAGE C STRICT;\n";
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";
258 open($F,">$target/share/conversion_create.sql")
259 || die "Could not write to conversion_create.sql\n";
265 sub GenerateTimezoneFiles
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;
274 print "Generating timezone files...";
276 "$conf\\zic\\zic -d \"$target/share/timezone\" " . join(" src/timezone/data/", @tzfiles));
280 sub GenerateTsearchFiles
284 print "Generating tsearch script...";
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');
298 my $lang = shift @pieces || last;
299 my $asclang = shift @pieces || last;
303 if (-s "src/backend/snowball/stopwords/$lang.stop")
305 $stop = ", StopWords=$lang";
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;
326 print "Copying contrib data files...";
328 opendir($D, 'contrib') || croak "Could not opendir on contrib!\n";
329 while (my $d = readdir($D))
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");
338 my $mf = read_file("contrib/$d/Makefile");
339 $mf =~ s{\\s*[\r\n]+}{}mg;
341 # Note: we currently don't support setting MODULEDIR in the makefile
342 my $moduledir = 'contrib';
345 if ($mf =~ /^EXTENSION\s*=\s*(.*)$/m) {$flist .= $1}
348 $moduledir = 'extension';
349 $flist = ParseAndCleanRule($flist, $mf);
351 foreach my $f (split /\s+/,$flist)
353 lcopy('contrib/' . $d . '/' . $f . '.control',
354 $target . '/share/extension/' . $f . '.control')
355 || croak("Could not copy file $f.control in contrib $d");
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
367 $flist = ParseAndCleanRule($flist, $mf);
369 foreach my $f (split /\s+/,$flist)
371 lcopy('contrib/' . $d . '/' . $f,
372 $target . '/share/' . $moduledir . '/' . basename($f))
373 || croak("Could not copy file $f in contrib $d");
379 if ($mf =~ /^DATA_TSEARCH\s*=\s*(.*)$/m) {$flist .= $1}
382 $flist = ParseAndCleanRule($flist, $mf);
384 foreach my $f (split /\s+/,$flist)
386 lcopy('contrib/' . $d . '/' . $f,
387 $target . '/share/tsearch_data/' . basename($f))
388 || croak("Could not copy file $f in contrib $d");
394 if ($mf =~ /^DOCS\s*=\s*(.*)$/mg) {$flist .= $1}
397 $flist = ParseAndCleanRule($flist, $mf);
399 # Special case for contrib/spi
401 "autoinc.example insert_username.example moddatetime.example refint.example timetravel.example"
403 foreach my $f (split /\s+/,$flist)
405 lcopy('contrib/' . $d . '/' . $f,
406 $target . '/doc/' . $moduledir . '/' . $f)
407 || croak("Could not copy file $f in contrib $d");
416 sub ParseAndCleanRule
421 # Strip out $(addsuffix) rules
422 if (index($flist, '$(addsuffix ') >= 0)
426 for ($i = index($flist, '$(addsuffix ') + 12; $i < length($flist); $i++)
428 $pcount++ if (substr($flist, $i, 1) eq '(');
429 $pcount-- if (substr($flist, $i, 1) eq ')');
430 last if ($pcount < 0);
432 $flist = substr($flist, 0, index($flist, '$(addsuffix ')) . substr($flist, $i+1);
441 EnsureDirectories($target, 'include', 'include/libpq','include/internal',
442 'include/internal/libpq','include/server', 'include/server/parser');
446 $target . '/include/',
447 'src/include/', 'postgres_ext.h', 'pg_config.h', 'pg_config_os.h', 'pg_config_manual.h'
449 lcopy('src/include/libpq/libpq-fs.h', $target . '/include/libpq/')
450 || croak 'Could not copy libpq-fs.h';
454 $target . '/include/',
455 'src/interfaces/libpq/','libpq-fe.h', 'libpq-events.h'
458 'Libpq internal headers',
459 $target .'/include/internal/',
460 'src/interfaces/libpq/', 'libpq-int.h', 'pqexpbuffer.h'
465 $target . '/include/internal/',
466 'src/include/', 'c.h', 'port.h', 'postgres_fe.h'
468 lcopy('src/include/libpq/pqcomm.h', $target . '/include/internal/libpq/')
469 || croak 'Could not copy pqcomm.h';
473 $target . '/include/server/',
474 'src/include/', 'pg_config.h', 'pg_config_os.h'
476 CopyFiles('Grammar header', $target . '/include/server/parser/',
477 'src/backend/parser/', 'gram.h');
478 CopySetOfFiles('',[ glob("src\\include\\*.h") ],$target . '/include/server/');
480 opendir($D, 'src/include') || croak "Could not opendir on src/include!\n";
482 # some xcopy progs don't like mixed slash style paths
483 (my $ctarget = $target) =~ s!/!\\!g;
484 while (my $d = readdir($D))
486 next if ($d =~ /^\./);
487 next if ($d eq '.git');
488 next if ($d eq 'CVS');
489 next unless (-d "src/include/$d");
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");
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";
502 $target . '/include/',
503 'src/interfaces/ecpg/include/',
504 'ecpg_config.h', split /\s+/,$1
506 $mf =~ /^informix_headers\s*=\s*(.*)$/m || croak "Could not find informix_headers line\n";
507 EnsureDirectories($target . '/include', 'informix', 'informix/esql');
509 'ECPG informix headers',
510 $target .'/include/informix/esql/',
511 'src/interfaces/ecpg/include/',
520 my $majorver = shift;
522 print "Installing NLS files...";
523 EnsureDirectories($target, "share/locale");
529 &&!push(@flist, $File::Find::name);
536 my $prgm = DetermineCatalogName($_);
539 next unless ($dir =~ /([^\/]+)\/po$/);
540 foreach (glob("$dir/*.po"))
543 next unless /([^\/]+)\.po/;
546 EnsureDirectories($target, "share/locale/$lang", "share/locale/$lang/LC_MESSAGES");
548 "\"$nlspath\\bin\\msgfmt\" -o \"$target\\share\\locale\\$lang\\LC_MESSAGES\\$prgm-$majorver.mo\" $_"
549 )&& croak("Could not run msgfmt on $dir\\$_");
556 sub DetermineMajorVersion
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';
563 sub DetermineCatalogName
565 my $filename = shift;
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";
574 my $filename = shift;
579 open($F, $filename) || die "Could not open file $filename\n";