package Install;
+#
+# Package that provides 'make install' functionality for msvc builds
+#
+# src/tools/msvc/Install.pm
+#
use strict;
use warnings;
use Carp;
use File::Basename;
use File::Copy;
+use File::Find ();
use Exporter;
our (@ISA,@EXPORT_OK);
@ISA = qw(Exporter);
@EXPORT_OK = qw(Install);
+sub lcopy
+{
+ my $src = shift;
+ my $target = shift;
+
+ if (-f $target)
+ {
+ unlink $target || confess "Could not delete $target\n";
+ }
+
+ copy($src,$target)
+ || confess "Could not copy $src to $target\n";
+
+}
+
sub Install
{
$| = 1;
my $target = shift;
+ our $config;
+ require "config_default.pl";
+ require "config.pl" if (-f "config.pl");
chdir("../../..") if (-f "../../../configure");
+ chdir("../../../..") if (-f "../../../../configure");
my $conf = "";
if (-d "debug")
{
$conf = "release";
}
die "Could not find debug or release binaries" if ($conf eq "");
- print "Installing for $conf\n";
+ my $majorver = DetermineMajorVersion();
+ print "Installing version $majorver for $conf in $target\n";
- EnsureDirectories($target, 'bin','lib','share','share/timezonesets');
+ EnsureDirectories($target, 'bin','lib','share','share/timezonesets','share/contrib','doc',
+ 'doc/contrib', 'symbols', 'share/tsearch_data');
CopySolutionOutput($conf, $target);
- copy($target . '/lib/libpq.dll', $target . '/bin/libpq.dll');
- CopySetOfFiles('config files', "*.sample", $target . '/share/');
- CopySetOfFiles('timezone names', 'src\timezone\tznames\*.txt',$target . '/share/timezonesets/');
+ lcopy($target . '/lib/libpq.dll', $target . '/bin/libpq.dll');
+ my $sample_files = [];
+ File::Find::find(
+ {
+ wanted =>sub {
+ /^.*\.sample\z/s
+ &&push(@$sample_files, $File::Find::name);
+ }
+ },
+ "src"
+ );
+ CopySetOfFiles('config files', $sample_files, $target . '/share/');
+ CopyFiles(
+ 'Import libraries',
+ $target .'/lib/',
+ "$conf\\", "postgres\\postgres.lib","libpq\\libpq.lib", "libecpg\\libecpg.lib",
+ "libpgport\\libpgport.lib"
+ );
+ CopySetOfFiles(
+ 'timezone names',
+ [ glob('src\timezone\tznames\*.txt') ],
+ $target . '/share/timezonesets/'
+ );
CopyFiles(
'timezone sets',
$target . '/share/timezonesets/',
'src/timezone/tznames/', 'Default','Australia','India'
);
- CopySetOfFiles('BKI files', "src\\backend\\catalog\\postgres.*", $target .'/share/');
- CopySetOfFiles('SQL files', "src\\backend\\catalog\\*.sql", $target . '/share/');
+ CopySetOfFiles('BKI files', [ glob("src\\backend\\catalog\\postgres.*") ],$target .'/share/');
+ CopySetOfFiles('SQL files', [ glob("src\\backend\\catalog\\*.sql") ],$target . '/share/');
CopyFiles(
'Information schema data',
$target . '/share/',
);
GenerateConversionScript($target);
GenerateTimezoneFiles($target,$conf);
+ GenerateTsearchFiles($target);
+ CopySetOfFiles(
+ 'Stopword files',
+ [ glob("src\\backend\\snowball\\stopwords\\*.stop") ],
+ $target . '/share/tsearch_data/'
+ );
+ CopySetOfFiles(
+ 'Dictionaries sample files',
+ [ glob("src\\backend\\tsearch\\*_sample.*") ],
+ $target . '/share/tsearch_data/'
+ );
+ CopyContribFiles($config,$target);
+ CopyIncludeFiles($target);
+
+ GenerateNLSFiles($target,$config->{nls},$majorver) if ($config->{nls});
+
+ print "Installation complete.\n";
}
sub EnsureDirectories
print ".";
$f = $basedir . $f;
die "No file $f\n" if (!-f $f);
- copy($f, $target . basename($f))
- || croak "Could not copy $f to $target". basename($f). " to $target". basename($f) . "\n";
+ lcopy($f, $target . basename($f));
}
print "\n";
}
sub CopySetOfFiles
{
my $what = shift;
- my $spec = shift;
+ my $flist = shift;
my $target = shift;
- my $D;
-
- print "Copying $what";
- open($D, "dir /b /s $spec |") || croak "Could not list $spec\n";
- while (<$D>)
+ print "Copying $what" if $what;
+ foreach (@$flist)
{
- chomp;
next if /regress/; # Skip temporary install in regression subdir
+ next if /ecpg.test/; # Skip temporary install in regression subdir
my $tgt = $target . basename($_);
print ".";
- copy($_, $tgt) || croak "Could not copy $_: $!\n";
+ lcopy($_, $tgt) || croak "Could not copy $_: $!\n";
}
- close($D);
print "\n";
}
# Static lib, such as libpgport, only used internally during build, don't install
next;
}
- copy("$conf\\$pf\\$pf.$ext","$target\\$dir\\$pf.$ext") || croak "Could not copy $pf.$ext\n";
+ lcopy("$conf\\$pf\\$pf.$ext","$target\\$dir\\$pf.$ext")
+ || croak "Could not copy $pf.$ext\n";
+ lcopy("$conf\\$pf\\$pf.pdb","$target\\symbols\\$pf.pdb")
+ || croak "Could not copy $pf.pdb\n";
print ".";
}
print "\n";
$sql .= "-- $se --> $de\n";
$sql .=
"CREATE OR REPLACE FUNCTION $func (INTEGER, INTEGER, CSTRING, INTERNAL, INTEGER) RETURNS VOID AS '\$libdir/$obj', '$func' LANGUAGE C STRICT;\n";
+ $sql .=
+"COMMENT ON FUNCTION $func(INTEGER, INTEGER, CSTRING, INTERNAL, INTEGER) IS 'internal conversion function for $se to $de';\n";
$sql .= "DROP CONVERSION pg_catalog.$name;\n";
$sql .= "CREATE DEFAULT CONVERSION pg_catalog.$name FOR '$se' TO '$de' FROM $func;\n";
+ $sql .= "COMMENT ON CONVERSION pg_catalog.$name IS 'conversion for $se to $de';\n";
}
open($F,">$target/share/conversion_create.sql")
|| die "Could not write to conversion_create.sql\n";
my @tzfiles = split /\s+/,$1;
unshift @tzfiles,'';
print "Generating timezone files...";
- system("$conf\\zic\\zic -d $target/share/timezone " . join(" src/timezone/data/", @tzfiles));
+ system(
+ "$conf\\zic\\zic -d \"$target/share/timezone\" " . join(" src/timezone/data/", @tzfiles));
print "\n";
}
+sub GenerateTsearchFiles
+{
+ my $target = shift;
+
+ print "Generating tsearch script...";
+ my $F;
+ my $tmpl = read_file('src/backend/snowball/snowball.sql.in');
+ my $mf = read_file('src/backend/snowball/Makefile');
+ $mf =~ s{\\\s*[\r\n]+}{}mg;
+ $mf =~ /^LANGUAGES\s*=\s*(.*)$/m
+ || die "Could not find LANGUAGES line in snowball Makefile\n";
+ my @pieces = split /\s+/,$1;
+ open($F,">$target/share/snowball_create.sql")
+ || die "Could not write snowball_create.sql";
+ print $F read_file('src/backend/snowball/snowball_func.sql.in');
+
+ while ($#pieces > 0)
+ {
+ my $lang = shift @pieces || last;
+ my $asclang = shift @pieces || last;
+ my $txt = $tmpl;
+ my $stop = '';
+
+ if (-s "src/backend/snowball/stopwords/$lang.stop")
+ {
+ $stop = ", StopWords=$lang";
+ }
+
+ $txt =~ s#_LANGNAME_#${lang}#gs;
+ $txt =~ s#_DICTNAME_#${lang}_stem#gs;
+ $txt =~ s#_CFGNAME_#${lang}#gs;
+ $txt =~ s#_ASCDICTNAME_#${asclang}_stem#gs;
+ $txt =~ s#_NONASCDICTNAME_#${lang}_stem#gs;
+ $txt =~ s#_STOPWORDS_#$stop#gs;
+ print $F $txt;
+ print ".";
+ }
+ close($F);
+ print "\n";
+}
+
+sub CopyContribFiles
+{
+ my $config = shift;
+ my $target = shift;
+
+ print "Copying contrib data files...";
+ my $D;
+ opendir($D, 'contrib') || croak "Could not opendir on contrib!\n";
+ while (my $d = readdir($D))
+ {
+ next if ($d =~ /^\./);
+ next unless (-f "contrib/$d/Makefile");
+ next if ($d eq "uuid-ossp"&& !defined($config->{uuid}));
+ next if ($d eq "sslinfo" && !defined($config->{openssl}));
+ next if ($d eq "xml2" && !defined($config->{xml}));
+
+ my $mf = read_file("contrib/$d/Makefile");
+ $mf =~ s{\\s*[\r\n]+}{}mg;
+ my $flist = '';
+ if ($mf =~ /^DATA_built\s*=\s*(.*)$/m) {$flist .= $1}
+ if ($mf =~ /^DATA\s*=\s*(.*)$/m) {$flist .= " $1"}
+ $flist =~ s/^\s*//; # Remove leading spaces if we had only DATA_built
+
+ if ($flist ne '')
+ {
+ $flist = ParseAndCleanRule($flist, $mf);
+
+ # Special case for contrib/spi
+ $flist = "autoinc.sql insert_username.sql moddatetime.sql refint.sql timetravel.sql"
+ if ($d eq 'spi');
+ foreach my $f (split /\s+/,$flist)
+ {
+ lcopy('contrib/' . $d . '/' . $f,$target . '/share/contrib/' . basename($f))
+ || croak("Could not copy file $f in contrib $d");
+ print '.';
+ }
+ }
+
+ $flist = '';
+ if ($mf =~ /^DATA_TSEARCH\s*=\s*(.*)$/m) {$flist .= $1}
+ if ($flist ne '')
+ {
+ $flist = ParseAndCleanRule($flist, $mf);
+
+ foreach my $f (split /\s+/,$flist)
+ {
+ lcopy('contrib/' . $d . '/' . $f,$target . '/share/tsearch_data/' . basename($f))
+ || croak("Could not copy file $f in contrib $d");
+ print '.';
+ }
+ }
+
+ $flist = '';
+ if ($mf =~ /^DOCS\s*=\s*(.*)$/mg) {$flist .= $1}
+ if ($flist ne '')
+ {
+ $flist = ParseAndCleanRule($flist, $mf);
+
+ # Special case for contrib/spi
+ $flist =
+"autoinc.example insert_username.example moddatetime.example refint.example timetravel.example"
+ if ($d eq 'spi');
+ foreach my $f (split /\s+/,$flist)
+ {
+ lcopy('contrib/' . $d . '/' . $f, $target . '/doc/contrib/' . $f)
+ || croak("Could not copy file $f in contrib $d");
+ print '.';
+ }
+ }
+ }
+ closedir($D);
+ print "\n";
+}
+
+sub ParseAndCleanRule
+{
+ my $flist = shift;
+ my $mf = shift;
+
+ # Strip out $(addsuffix) rules
+ if (index($flist, '$(addsuffix ') >= 0)
+ {
+ my $pcount = 0;
+ my $i;
+ for ($i = index($flist, '$(addsuffix ') + 12; $i < length($flist); $i++)
+ {
+ $pcount++ if (substr($flist, $i, 1) eq '(');
+ $pcount-- if (substr($flist, $i, 1) eq ')');
+ last if ($pcount < 0);
+ }
+ $flist = substr($flist, 0, index($flist, '$(addsuffix ')) . substr($flist, $i+1);
+ }
+ return $flist;
+}
+
+sub CopyIncludeFiles
+{
+ my $target = shift;
+
+ EnsureDirectories($target, 'include', 'include/libpq','include/internal',
+ 'include/internal/libpq','include/server');
+
+ CopyFiles(
+ 'Public headers',
+ $target . '/include/',
+ 'src/include/', 'postgres_ext.h', 'pg_config.h', 'pg_config_os.h', 'pg_config_manual.h'
+ );
+ lcopy('src/include/libpq/libpq-fs.h', $target . '/include/libpq/')
+ || croak 'Could not copy libpq-fs.h';
+
+ CopyFiles(
+ 'Libpq headers',
+ $target . '/include/',
+ 'src/interfaces/libpq/','libpq-fe.h', 'libpq-events.h'
+ );
+ CopyFiles(
+ 'Libpq internal headers',
+ $target .'/include/internal/',
+ 'src/interfaces/libpq/', 'libpq-int.h', 'pqexpbuffer.h'
+ );
+
+ CopyFiles(
+ 'Internal headers',
+ $target . '/include/internal/',
+ 'src/include/', 'c.h', 'port.h', 'postgres_fe.h'
+ );
+ lcopy('src/include/libpq/pqcomm.h', $target . '/include/internal/libpq/')
+ || croak 'Could not copy pqcomm.h';
+
+ CopyFiles(
+ 'Server headers',
+ $target . '/include/server/',
+ 'src/include/', 'pg_config.h', 'pg_config_os.h'
+ );
+ CopySetOfFiles('',[ glob("src\\include\\*.h") ],$target . '/include/server/');
+ my $D;
+ opendir($D, 'src/include') || croak "Could not opendir on src/include!\n";
+
+ while (my $d = readdir($D))
+ {
+ next if ($d =~ /^\./);
+ next if ($d eq 'CVS');
+ next unless (-d 'src/include/' . $d);
+
+ EnsureDirectories($target . '/include/server', $d);
+ system("xcopy /s /i /q /r /y src\\include\\$d\\*.h \"$target\\include\\server\\$d\\\"")
+ && croak("Failed to copy include directory $d\n");
+ }
+ closedir($D);
+
+ my $mf = read_file('src/interfaces/ecpg/include/Makefile');
+ $mf =~ s{\\s*[\r\n]+}{}mg;
+ $mf =~ /^ecpg_headers\s*=\s*(.*)$/m || croak "Could not find ecpg_headers line\n";
+ CopyFiles(
+ 'ECPG headers',
+ $target . '/include/',
+ 'src/interfaces/ecpg/include/',
+ 'ecpg_config.h', split /\s+/,$1
+ );
+ $mf =~ /^informix_headers\s*=\s*(.*)$/m || croak "Could not find informix_headers line\n";
+ EnsureDirectories($target . '/include', 'informix', 'informix/esql');
+ CopyFiles(
+ 'ECPG informix headers',
+ $target .'/include/informix/esql/',
+ 'src/interfaces/ecpg/include/',
+ split /\s+/,$1
+ );
+}
+
+sub GenerateNLSFiles
+{
+ my $target = shift;
+ my $nlspath = shift;
+ my $majorver = shift;
+
+ print "Installing NLS files...";
+ EnsureDirectories($target, "share/locale");
+ my @flist;
+ File::Find::find(
+ {
+ wanted =>sub {
+ /^nls\.mk\z/s
+ &&!push(@flist, $File::Find::name);
+ }
+ },
+ "src"
+ );
+ foreach (@flist)
+ {
+ my $prgm = DetermineCatalogName($_);
+ s/nls.mk/po/;
+ my $dir = $_;
+ next unless ($dir =~ /([^\/]+)\/po$/);
+ foreach (glob("$dir/*.po"))
+ {
+ my $lang;
+ next unless /([^\/]+)\.po/;
+ $lang = $1;
+
+ EnsureDirectories($target, "share/locale/$lang", "share/locale/$lang/LC_MESSAGES");
+ system(
+"\"$nlspath\\bin\\msgfmt\" -o \"$target\\share\\locale\\$lang\\LC_MESSAGES\\$prgm-$majorver.mo\" $_"
+ )&& croak("Could not run msgfmt on $dir\\$_");
+ print ".";
+ }
+ }
+ print "\n";
+}
+
+sub DetermineMajorVersion
+{
+ my $f = read_file('src/include/pg_config.h') || croak 'Could not open pg_config.h';
+ $f =~ /^#define\s+PG_MAJORVERSION\s+"([^"]+)"/m || croak 'Could not determine major version';
+ return $1;
+}
+
+sub DetermineCatalogName
+{
+ my $filename = shift;
+
+ my $f = read_file($filename) || croak "Could not open $filename";
+ $f =~ /CATALOG_NAME\s*\:?=\s*(\S+)/m || croak "Could not determine catalog name in $filename";
+ return $1;
+}
+
sub read_file
{
my $filename = shift;